This commit is contained in:
gaofeng
2026-06-17 09:23:18 +08:00
commit 81ade70944
1638 changed files with 213697 additions and 0 deletions

12
.env Normal file
View File

@@ -0,0 +1,12 @@
APP_DEBUG = true
TCP_LOG_URL="121.40.192.142"
TCP_LOG_PORT=18001
PROJECT_NAME="miniapi"
DEFAULT_LANG = zh-cn
SITE_DOMAIN = www.evus.cn
SITE_DOMAIN_BJ = www.evus.com.cn
WECHAT_MINI_APPID = your_jinan_mini_appid
WECHAT_MINI_SECRET = your_jinan_mini_secret
WECHAT_MINI_APPID_BJ = wx72d43069c530ddb7
WECHAT_MINI_SECRET_BJ = 1369ccf68a82005b3b6e94f5f95c6489

17
.example.env Normal file
View File

@@ -0,0 +1,17 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = test
DB_USER = username
DB_PASS = password
DB_PORT = 3306
DB_CHARSET = utf8
DEFAULT_LANG = zh-cn
SITE_DOMAIN = www.evus.cn
SITE_DOMAIN_BJ = www.evus.com.cn
WECHAT_MINI_APPID = your_jinan_mini_appid
WECHAT_MINI_SECRET = your_jinan_mini_secret
WECHAT_MINI_APPID_BJ = your_beijing_mini_appid
WECHAT_MINI_SECRET_BJ = your_beijing_mini_secret

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/.idea
/doc
/runtime
/data

0
.htaccess Normal file
View File

42
.travis.yml Normal file
View File

@@ -0,0 +1,42 @@
sudo: false
language: php
branches:
only:
- stable
cache:
directories:
- $HOME/.composer/cache
before_install:
- composer self-update
install:
- composer install --no-dev --no-interaction --ignore-platform-reqs
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
- composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
- composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
script:
- php think unit
deploy:
provider: releases
api_key:
secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
file:
- ThinkPHP_Core.zip
- ThinkPHP_Full.zip
skip_cleanup: true
on:
tags: true

32
LICENSE.txt Normal file
View File

@@ -0,0 +1,32 @@
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
版权所有Copyright © 2006-2025 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
Apache Licence是著名的非盈利开源组织Apache采用的协议。
该协议和BSD类似鼓励代码共享和尊重原作者的著作权
允许代码修改,再作为开源或商业软件发布。需要满足
的条件:
1 需要给代码的用户一份Apache Licence
2 如果你修改了代码,需要在被修改的文件中说明;
3 在延伸的代码中(修改和有源代码衍生的代码中)需要
带有原来代码中的协议,商标,专利声明和其他原来作者规
定需要包含的说明;
4 如果再发布的产品中包含一个Notice文件则在Notice文
件中需要带有本协议内容。你可以在Notice中增加自己的
许可但不可以表现为对Apache Licence构成更改。
具体的协议参考http://www.apache.org/licenses/LICENSE-2.0
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

77
README.md Normal file
View File

@@ -0,0 +1,77 @@
![](https://www.thinkphp.cn/uploads/images/20230630/300c856765af4d8ae758c503185f8739.png)
ThinkPHP 8
===============
## 特性
* 基于PHP`8.0+`重构
* 升级`PSR`依赖
* 依赖`think-orm`3.0+版本
* 全新的`think-dumper`服务,支持远程调试
* 支持`6.0`/`6.1`无缝升级
> ThinkPHP8的运行环境要求PHP8.0+
现在开始,你可以使用官方提供的[ThinkChat](https://chat.topthink.com/)让你在学习ThinkPHP的旅途中享受私人AI助理服务
![](https://www.topthink.com/uploads/assistant/20230630/4d1a3f0ad2958b49bb8189b7ef824cb0.png)
ThinkPHP生态服务由[顶想云](https://www.topthink.com)TOPThink Cloud提供为生态提供专业的开发者服务和价值之选。
## 文档
[完全开发手册](https://doc.thinkphp.cn)
## 赞助
全新的[赞助计划](https://www.thinkphp.cn/sponsor)可以让你通过我们的网站、手册、欢迎页及GIT仓库获得巨大曝光同时提升企业的品牌声誉也更好保障ThinkPHP的可持续发展。
[![](https://www.thinkphp.cn/sponsor/special.svg)](https://www.thinkphp.cn/sponsor/special)
[![](https://www.thinkphp.cn/sponsor.svg)](https://www.thinkphp.cn/sponsor)
## 安装
~~~
composer create-project topthink/think tp
~~~
启动服务
~~~
cd tp
php think run
~~~
然后就可以在浏览器中访问
~~~
http://localhost:8000
~~~
如果需要更新框架使用
~~~
composer update topthink/framework
~~~
## 命名规范
`ThinkPHP`遵循PSR-2命名规范和PSR-4自动加载规范。
## 参与开发
直接提交PR或者Issue即可
## 版权信息
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2024 by ThinkPHP (http://thinkphp.cn) All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
更多细节参阅 [LICENSE.txt](LICENSE.txt)

1
app/.htaccess Normal file
View File

@@ -0,0 +1 @@
deny from all

22
app/AppService.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

94
app/BaseController.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
[$validate, $scene] = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}

58
app/ExceptionHandle.php Normal file
View File

@@ -0,0 +1,58 @@
<?php
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 其他错误交给系统处理
return parent::render($request, $e);
}
}

8
app/Request.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

2
app/common.php Normal file
View File

@@ -0,0 +1,2 @@
<?php
// 应用公共文件

17
app/event.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

1058
app/lang/en-us.php Normal file

File diff suppressed because it is too large Load Diff

911
app/lang/zh-cn.php Normal file
View File

@@ -0,0 +1,911 @@
<?php
return [
'site' => [
'title' => 'EVUS中文网_evus美国签证登记_evus登记步骤',
'description' => 'evus,evus登记步骤,evus系统登记入口,evus是什么,evus美国签证登记,evus官网中文',
'keywords' => 'EVUS中文网为您提供申请登记新的签证更新电子系统(EVUS)。提供evus官网查询、更新、evus系统登记入口、evus登记步骤等相关信息。',
'footer_1' => '声明:本网站是经工商注册、工信部备案的专业服务站点,本网站致力于协助个人或者企业获得他们得旅行许可或短时间的入境停留。登记不成功全额退费,登记成功服务费用不退。纠纷解决,适用本网站所属机构注册地仲裁委员会管辖',
'footer_2' => 'ITS INTERNATIONAL TRAVEL SERVICE COMPANY Copyright © 2014-2025 All rights reserved',
],
'menu_home' => '首页',
'menu_apply' => 'EVUS登记',
'menu_search' => '查询EVUS状态',
'menu_us_apply' => '美签申请',
'menu_esta_apply' => 'ESTA申请',
'menu_help' => '使用帮助',
'menu_user' => '个人中心',
'menu_news' => '新闻资讯',
'menu_company' => '关于我们',
'user_my_order' => '我的订单',
'user_invoice' => '自助开票',
'user_multiple_invoice' => '合并开票',
'user_logout' => '退出',
'order_paid' => '已支付',
'order_unpaid' => '未支付',
'index_title' => 'eVUS中文办理登记',
'index_desc' => '点击登记按钮,轻松完成办理',
'index_button' => 'eVUS登记',
'index_step1' => '填写旅行信息',
'index_step2' => '3-5分钟信息录入',
'index_step3' => '支付服务费',
'index_step4' => '3-15分钟内递交',
'index_step5' => '查询登记结果',
'index_attention_tips' => '请注意',
'index_attention_1' => '根据美国海关和边境保护局CBP规定自2025年9月30日起eVUS登记将收取30美元官方费用。',
'index_attention_2' => '本网站为独立运营的商务服务平台,凭借自主研发的智能系统,带来三大核心服务:智能填表系统可高效简化表单填写流程,精准 OCR 识别能快速提取图文信息,智能 AI 翻译则支持多语种精准转换。网站经相关机构注册、登记和备案后上线运营,专注为用户提供专业有偿服务,不隶属于任何政府部门。',
'index_attention_3' => '登记成功,所有费用不予退还;若登记未成功,官方登记费无法退回,但我们承诺全额退还服务费。',
'index_price_1_1' => 'eVUS快速登记',
'index_price_1_2' => '精准OCR识别',
'index_price_1_3' => '智能AI翻译',
'index_price_1_4' => '6小时内递交',
'index_price_1_5' => '包含30.75美金官方收费',
'index_price_1_6' => '6小时内递交',
'index_price_2_1' => 'eVUS专业登记',
'index_price_2_2' => '精准OCR识别',
'index_price_2_3' => '智能AI翻译',
'index_price_2_4' => '15分钟内递交',
'index_service' => '我们的服务',
'index_advantage' => '我们的优势',
'index_advantage_1_title' => '7×24小时急速响应',
'index_advantage_1_content_1' => '提供机场加急服务,专为应对各类临时出行需求而设,为赶时间的旅客开辟高效登记服务',
'index_advantage_1_content_2' => '旅客抵达机场后,无需繁琐排队,仅需 5-7 分钟即可递交',
'index_advantage_1_content_3' => '简化流程,助您快速通关,彻底规避误机风险,让紧急出行也能从容无忧',
'index_advantage_2_title' => '智能填表系统',
'index_advantage_2_content_1' => '我们推出简化中文表单填写服务全程仅需5分钟即可完成核心信息录入大幅降低语言壁垒带来的操作困扰',
'index_advantage_2_content_2' => '专业团队开展二次审核重点核查信息逻辑、格式规范及关键内容匹配度从源头减少登记失误显著降低因信息错漏导致的不成功风险较用户自行英文操作30-40分钟提速8倍',
'evus_pic' => '/new/assets/images/evus.jpg',
'evusearch_pic' => '/new/assets/images/evusearch.jpg',
'esta_pic' => '/new/assets/images/esta.jpg',
'evus' => [
'other_country_acquired_other_required' => '请填写其他',
'other_country_acquired_other_label' => '其他',
'add' => '添加',
'delete' => '删除',
'title' => '旅行证件信息',
'passport_number_label' => '护照号码',
'passport_placeholder' => '输入或拍照上传',
'passport_required' => '请填写护照号码',
'passport_upload_title' => '拍照/上传有效护照照片',
'passport_tooltip' => '护照号码通常为G、E、EA、EB、EC等开头的9位字符公务护照以及外交护照例外。请仔细区分数字1与字母I以及数字0与字母O。',
'passport_hint' => '依照您旅游时将使用的护照来输入护照号。',
'passport_country_label' => '护照国家',
'select_country_required' => '请选择一个国家',
'select_country_prompt' => '请选择一个国家',
'china' => '中国',
'country_tooltip' => '', // Can add tooltip text if needed
'country_hint' => '目前只有中华人民共和国的公民国民必须登记 EVUS。',
'pinyin' => '拼音',
'last_name_label' => '姓氏',
'last_name_placeholder' => '请填写拼音姓',
'last_name_required' => '请填写拼音姓',
'last_name_tooltip' => '请勿在此栏输入名字。复姓请按照护照信息为准。若您没有姓氏,请在姓氏栏位填 "UNKNOWN"',
'last_name_hint' => '请参考护照上的信息用英文字母填写您的姓氏',
'first_name_label' => '名字',
'first_name_placeholder' => '请填写拼音名',
'first_name_required' => '请填写拼音名',
'first_name_tooltip' => '请参考护照上的信息填写您的名字,请勿在此栏输入姓氏',
'first_name_hint' => '依照您的护照正确输入您的名字',
'birth_date_label' => '出生日期',
'birth_date_placeholder' => '请选择出生日期',
'birth_date_required' => '请选择出生日期',
'birth_date_tooltip' => '请参考您护照上的信息填写您的出生日期,请确保出生日期正确',
'birth_city_label' => '出生城市',
'birth_city_placeholder' => '请填写出生城市',
'birth_city_required' => '请填写出生城市',
'birth_city_tooltip' => '请参考您护照上的信息填写您的出生地点。',
'birth_country_label' => '出生国家',
'birth_country_tooltip' => '请从下拉式菜单选择您护照上出生地栏位显示的国家名称',
'country_required' => '请选择一个国家',
'gender_label' => '性别',
'male' => '男性',
'female' => '女性',
'passport_issue_date_label' => '护照签发时间',
'passport_issue_placeholder' => '请选择护照签发日期',
'passport_issue_required' => '请选择护照签发日期',
'passport_issue_tooltip' => '请参照护照上的签发日期填写',
'passport_expiry_label' => '护照有效期',
'passport_expiry_placeholder' => '请选择护照有效期',
'passport_expiry_required' => '请选择护照有效期',
'passport_expiry_tooltip' => '请参照护照上的有效期填写',
'ten_year_visa_label' => '您持有十年效期的美国访客签证吗B1、B2 或 B1/B2 类)?',
'ten_year_visa_hint' => '请确认是否持有美国十年效期商务/旅游B1、B2、B1/B2类型签证',
'ten_year_visa_warning' => '要在 EVUS 登记,您必须持有 10 年效期的美国访客签证B1、 B2 或 B1/B2 类)。若希望到美国旅游,但未持有这类的签证,您必须到美国大使馆申请',
'visa_number_label' => '美国B1/B2签证号码',
'visa_number_placeholder' => '输入或拍照上传',
'visa_number_required' => '请填写签证号码',
'visa_number_tooltip' => '请参考您的美国签证贴纸输入右下角8位红色字符的签证号码',
'upload_visa_title' => '拍照/上传有效护照照片',
'visa_in_passport_label' => '您旅行时所使用的护照是否含有您的美国签证?',
'visa_in_passport_hint' => '如果你的签证在旧护照上面,请选择"否",并填写旧护照信息',
'yes' => '是',
'no' => '否',
'old_passport_number_label' => '含美国签证的旧护照号码',
'enter_or_upload' => '输入或拍照上传',
'passport_number_required' => '请填写护照号码',
'upload_passport_photo' => '拍照/上传有效护照照片',
'old_passport_tooltip' => '请输入包含美国签证的旧护照号码',
'passport_country_tooltip' => '请选择旧护照的签发国家',
'next_step' => '下一步',
'other_citizenship_title' => '其他公民身份、国籍、护照',
'other_nationality_label' => '您现在是否拥有其他国籍?',
'other_nationality_tooltip' => '请确认是否拥有除中国以外的他国国籍',
'other_citizenship_current_label' => '其他国家公民当前',
'other_citizenship_current_tooltip' => '选择您的其他国籍的国家。如果您以上回答"是",该国家必须完成您的申请',
'how_acquired_citizenship_label' => '你如何从这个国家获得公民/国籍',
'how_acquired_citizenship_tooltip' => '选择你如何获得国籍/从这个国家的国籍',
'past_nationality_label' => '您曾经是否拥有其他国籍?',
'past_nationality_tooltip' => '在此问题上回答"是"代表着现在已无此国籍',
'past_citizenship_label' => '其他国家公民过去',
'past_citizenship_tooltip' => '请从下拉式菜单选择您护照上出生地栏位显示的国家名称',
'other_passport_label' => '您曾经是否拥有其他国家的护照或居民身份证?',
'other_passport_tooltip' => '请确认是否拥有除中国以外的他国护照。若您有此材料但已遗失或不记得号码,可填写"UNKNOWN"',
'issuing_country_label' => '发行国',
'issuing_country_tooltip' => '选择国家的公民身份,因为它出现在您的文档。完成应用程序需签发国的文档。',
'document_type_label' => '文档类型',
'document_type_tooltip' => '请选择另一个国家颁布的旅行文件类型',
'document_number_label' => '文档编号',
'document_number_placeholder' => '字母及数字(如无法提供请填写000000)',
'document_number_tooltip' => '输入您的旅行证件上显示的文件编号。旅行证件可能包含数字和/或字符。请仔细区分数字零和字母O.',
'expiry_year_label' => '到期年份',
'expiry_year_placeholder' => '请选择到期年份',
'expiry_year_tooltip' => '请即使它已过期,说明颁发的另一个国家,你的旅行证件到期年份。',
'social_media_label' => '是否有社交媒体?',
'social_media_tooltip' => '请选择是否有社交媒体',
'facebook_label' => '脸书(facebook)',
'facebook_placeholder' => '请填写脸书用户名',
'facebook_tooltip' => '如有请填写facebook账号名称。',
'linkedin_label' => '领英链接(linkedin)',
'linkedin_placeholder' => '请填写领英链接',
'linkedin_tooltip' => '如有请填写linkedin链接。',
'twitter_label' => '推特(Twitter)',
'twitter_placeholder' => '请填写推特用户名',
'twitter_tooltip' => '如有请填写Twitter账号名称。',
'instagram_label' => '照片墙(Instagram)',
'instagram_placeholder' => '请填写照片墙用户名',
'instagram_tooltip' => '如有,请填写照片墙用户名。',
'platform_label' => '提供者/平台',
'platform_tooltip' => '如有,请请选择提供者/平台。',
'identifier_label' => '社交媒体标识符',
'identifier_placeholder' => '请填写社交媒体标识符',
'identifier_tooltip' => '如有,请填写社交媒体标识符。',
'alias_label' => '是否有曾用名?',
'alias_tooltip' => '是否有曾用名',
'alias_last_name_label' => '曾用 姓氏',
'alias_last_name_placeholder' => '请填写曾用 姓氏',
'alias_last_name_tooltip' => '请填写您曾用名的姓氏',
'alias_last_name_hint' => '请填写您曾用名的姓氏,请勿在此栏输入名字。',
'alias_first_name_label' => '曾用 名字',
'alias_first_name_placeholder' => '请填写曾用 名字',
'alias_first_name_tooltip' => '请填写您曾用名的名字',
'alias_first_name_hint' => '请填写您曾用名的名字,请勿在此栏输入名字。',
'select_option_required' => '请选择',
'select_option_prompt' => '请选择',
'select_document_type_required' => '请选择文档类型',
'select_document_type_prompt' => '请选择文档类型',
'select_platform_required' => '请选择提供者/平台',
'select_platform_prompt' => '请选择提供者/平台',
'by_birth' => '经由出生',
'by_parents' => '通过父母',
'naturalization' => '归化',
'other' => '其他',
'passport_number' => '护照号',
'permanent_residence_id' => '永久居留证件号码(绿卡号)',
'address_contact_title' => '住址及联系方式',
'id_card_instructions' => '输入号码或点击【相机】识别您的有效<span style="color: #7F1313">身份证正面照</span>信息;拍照上传方式,可自动识别导入您的信息。',
'id_number_label' => '身份证号码',
'id_number_required' => '请填写身份证号码',
'upload_photo_title' => '拍照/上传有效照片',
'id_number_tooltip' => '请输入18位的身份证号码。如果没有身份证号码请填写UNKNOWN。',
'email_label' => '电子邮箱(选填)',
'email_placeholder' => '建议填写用于收入eVUS批准信',
'email_required' => '请填写电子邮箱',
'email_tooltip' => '您将通过邮件方式收到您的申请确认函。如有任何状态更新也将通过邮件通知您。',
'current_country_label' => '现居住国家',
'current_country_tooltip' => '请选择您当前居住的国家',
'current_province_label' => '现居住省份/州',
'current_province_placeholder' => '填写居住省份/州',
'current_province_required' => '填写居住省份/州',
'current_province_tooltip' => '请填写您当前居住的省份或州',
'current_city_label' => '现居住城市',
'current_city_placeholder' => '填写现居住城市',
'current_city_required' => '填写现居住城市',
'current_city_tooltip' => '请填写您当前居住的城市',
'current_address_label' => '现居住地址',
'current_address_placeholder' => '填写现居住地址',
'current_address_required' => '填写现居住地址',
'current_address_tooltip' => '请填写您当前居住的详细地址',
'phone_label' => '联系电话',
'phone_placeholder' => '请输入手机号码',
'phone_tooltip' => '请填写有效的联系电话号码',
'parents_info_title' => '父母信息',
'father_surname_label' => '父亲的姓氏',
'father_surname_placeholder' => '填写父亲的姓氏',
'father_surname_required' => '填写父亲的姓氏',
'father_surname_tooltip' => '请输入申请人父亲的姓氏',
'father_name_label' => '父亲的名字',
'father_name_placeholder' => '填写父亲的名字',
'father_name_required' => '填写父亲的名字',
'father_name_tooltip' => '请输入申请人父亲的名字',
'mother_surname_label' => '母亲的姓氏',
'mother_surname_placeholder' => '填写母亲的姓氏',
'mother_surname_required' => '填写母亲的姓氏',
'mother_surname_tooltip' => '请输入申请人母亲的姓氏',
'mother_name_label' => '母亲的名字',
'mother_name_placeholder' => '填写母亲的名字',
'mother_name_required' => '填写母亲的名字',
'mother_name_tooltip' => '请输入申请人母亲的名字',
'employment_info_title' => '工作信息',
'has_employer_label' => '您现在或之前是否有过工作单位或雇主',
'has_employer_tooltip' => '如填"是"请填写工作单位信息',
'employer_country_label' => '工作单位所在国家',
'employer_country_tooltip' => '请在列表中选择工作单位所在国家',
'employer_state_label' => '工作单位所在省份/州',
'employer_state_placeholder' => '请填写工作单位所在省份/州',
'employer_state_required' => '请填写工作单位所在省份/州',
'employer_state_tooltip' => '建议使用英文或拼音输入工作单位所在省份。若不清楚可填写"UNKNOWN"',
'employer_city_label' => '工作单位所在城市',
'employer_city_placeholder' => '请填写工作单位所在城市',
'employer_city_required' => '请填写工作单位所在城市,请勿填写省份',
'employer_city_tooltip' => '建议使用英文或拼音输入工作单位所在城市。若不清楚可填写"UNKNOWN"',
'employer_address_label' => '工作单位地址',
'employer_address_placeholder' => '请填写工作单位地址',
'employer_address_required' => '请填写工作单位地址',
'employer_address_tooltip' => '建议使用英文或拼音输入工作单位的地址,不必在此栏填写省份或城市。若不清楚可填写"UNKNOWN"',
'employer_name_label' => '工作单位名称',
'employer_name_placeholder' => '请填写工作单位名称',
'employer_name_required' => '填写工作单位名称限30字',
'employer_name_tooltip' => '请使用英文或拼音输入工作单位的名称。若没有工作单位,您也可以填写自雇、家庭主妇、等信息形容您的工作状况',
'travel_info_title' => '旅游信息',
'transit_purpose_label' => '您到美国的目的,是为了过境到另一国家吗?',
'transit_purpose_tooltip' => '请选择您前往美国是否只是为了过境而不停留。选否可选择填写在美联系人。',
'us_address_section_title' => '在美国的地址(选填)',
'us_contact_name_label' => '将在美国到访的酒店名称 / 商业上往来的友人 / 亲友全名(选填)',
'us_contact_name_placeholder' => '不得含有数字和特殊字符。如无法提供请留空',
'us_contact_name_required' => '不得含有数字和特殊字符。如无法提供请留空',
'us_contact_name_tooltip' => '在美国的联系人可以是友人、亲戚或生意上的伙伴。若您在美国没有联系人,请输入您住宿地点的名称、地址和电话号码(例如酒店名称)。您也可输入"UNKNOWN"。',
'us_state_label' => '所在州(选填)',
'select_state_required' => '请选择所在州',
'select_state_prompt' => '请选择所在州',
'us_state_tooltip' => '请在列表中选择您在美国的联系人所在的州。若不清楚可选择"未知"',
'us_city_label' => '所在城市(选填)',
'us_city_placeholder' => '填写联系人所在城市,不必填写省份',
'us_city_required' => '填写联系人所在城市,不必填写省份',
'us_city_tooltip' => '请输入您在美国的联系人所在城市。若不清楚可填写"UNKNOWN"。',
'us_address_label' => '地址(选填)',
'us_address_placeholder' => '填写联系人的地址街道及号码,不必填写省份或城市',
'us_address_required' => '填写联系人的地址街道及号码,不必填写省份或城市',
'us_address_tooltip' => '请输入您在美国的联系人的地址街道及号码,不必在此栏填写省份或城市。若不清楚可填写"UNKNOWN"。',
'us_phone_label' => '电话号码(选填)',
'select_phone_prefix_prompt' => '请选择手机号前缀',
'us_phone_placeholder' => '请输入手机号码',
'us_phone_tooltip' => '请输入您在美国的联系人的电话号码。若不清楚可填写"0"。',
'emergency_contact_title' => '紧急联系人(选填)',
'emergency_last_name_label' => '姓氏(选填)',
'emergency_last_name_placeholder' => '填写紧急联系人姓氏',
'emergency_last_name_required' => '填写紧急联系人姓氏',
'emergency_last_name_tooltip' => '请输入您的紧急联系人姓氏。紧急联系人可以是您在美国或其他国家的联系人,如家属、好友或商业伙伴。若没有紧急联系人,请留空',
'emergency_first_name_label' => '名字(选填)',
'emergency_first_name_placeholder' => '填写紧急联系人名字',
'emergency_first_name_required' => '填写紧急联系人名字',
'emergency_first_name_tooltip' => '请输入您的紧急联系人名字。紧急联系人名字可以是您在美国或其他国家的联系人,如家属、好友或商业伙伴。若没有紧急联系人,请留空',
'emergency_email_label' => '电子邮箱(选填)',
'emergency_email_placeholder' => '填写紧急联系人电子邮箱',
'emergency_email_required' => '填写紧急联系人电子邮箱',
'emergency_email_tooltip' => '请输入您的紧急联系人的电子邮箱。若没有紧急联系人,请留空',
'emergency_phone_label' => '电话号码(选填)',
'emergency_phone_placeholder' => '填写紧急联系人电话号码',
'emergency_phone_tooltip' => '请输入您的紧急联系人的电话号码。若没有紧急联系人请输入0',
'eligibility_question' => '资格问题',
'warning_cannot_continue' => '您确定要对此问题回答「是」吗?如果您的答案为「是」,您将不能继续在线申请。',
'q_a' => '您目前是否患有以下任何疾病《公共健康法》第361条b款所列传染病霍乱、白喉、肺结核、天花、黄热病、病毒性出血热包括埃博拉、拉沙热、马尔堡病、克里米亚-刚果出血热)、可传染他人且可能致命的严重急性呼吸道疾病?',
'q_b' => '您是否曾因严重破坏财产或严重伤害他人或政府机关而被逮捕或定罪?',
'q_c' => '您是否曾违反过与非法药物的持有、使用或分发相关的法律?',
'q_d' => '您是否曾经或试图从事恐怖活动、间谍活动、破坏活动或种族灭绝?',
'q_e' => '您是否曾为获取签证或协助他人获取签证、或进入美国而欺诈或伪造自己或他人的信息?',
'q_f' => '您目前是否在美国寻求工作,或是否曾在未获许可的情况下在美国工作?',
'q_g' => '您是否曾被拒签美国签证或被拒绝入境美国,或在入境口岸撤回过申请?',
'q_h' => '您是否曾逾期居留美国或违反过美国签证的规定?',
'q_i' => '自2011年3月1日以来您是否曾去过或居住在伊朗、伊拉克、利比亚、朝鲜、索马里、苏丹、叙利亚或也门',
'reject_date' => '拒签日期',
'reject_address' => '拒签地点',
'vist_country' => '访问国家',
'vist_from_date' => '开始日期',
'vist_end_date' => '结束日期',
'vist_country_des' => '访问主要原因',
'IRN' => '伊朗',
'IRQ' => '伊拉克',
'LBY' => '利比亚',
'PRK' => '朝鲜',
'SOM' => '索马里',
'SDN' => '苏丹',
'SYR' => '叙利亚',
'YEM' => '也门',
'reason_1' => '旅游',
'reason_2' => '探亲',
'reason_3' => '工作',
'reason_4' => '商务',
'reason_5' => '学习',
'reason_6' => '新闻报道',
'reason_7' => '会议',
'reason_8' => '文化交流',
'reason_9' => '宗教活动',
'reason_10' => '医疗',
'reason_11' => '过境',
'reason_12' => '外交/公务',
'reason_13' => '其他',
'declaration_title' => '申请人声明',
'declaration_check1' => '本人声明在此申请中提供真实丶完整和正确的信息。',
'declaration_check2' => '我已阅读并理解',
'choose_package' => '选择套餐',
'package_a' => 'A套餐:398元/人民币 Vip快速登记(资料完整15分钟反馈信息)',
'package_b' => 'B套餐:98元/人民币 安逸登记(资料完整6小时内反馈信息)',
'submit_now' => '立即提交',
'termsCheckbox' => '
<div style="padding: 20px;">
<h5>条款与条件</h5>
<p>
签证更新电子系统EVUS与执法部门 的数据库进行比对。所有持10年有效期访客签证的旅客B1, B2 或 B1/B2,
如果需要以短期访客身份入境美国都必须在赴美前在EVUS上登记。
如果您的EVUS登记获得批准就意味着您可以前往美国。但这并不意味着您可以入境美国。抵达美国入境口岸时您将接受United States
Customs and Border Protection官员的检查。 入境口岸的官员会根据美国移民法的规定决定您是否可以入境。
由您或指定的第三方代理人所提供的所有信息必须是真实和正确的。如果您在填写信息时由您或您的代理人有意提交任何虚假、虚构或伪造的声明或陈述造成EVUS更新失败或者美国签证被注销责任由您本人承担。
信息填写完成后,我们根据你所选择的套餐时效反馈信息,提交后信息将不能再更改。
</p>
<p> 请表明您已阅读并了解上述所提供的信息</p>
<h5>隐私政策</h5>
<p>
<p>
本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。
您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。
<h5>1. 适用范围</h5>
<p>(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息;
(b)
在您使用本应用网络服务或访问本应用平台网页时本应用自动接收并记录的您的浏览器和计算机上的信息包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据
(c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。
您了解并同意,以下信息不适用本隐私权政策:
(a) 您在使用本应用平台提供的搜索服务时输入的关键字信息;
(b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;
(c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。</p>
<h5>2. 信息使用</h5>
<p>
(a)本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。
(b)
本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。
(c)
为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)</p>
<h5>3. 信息披露</h5>
<p>在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:
(a) 经您事先同意,向第三方披露;
(b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;
(c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;
(d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露;
(e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;
(f)
在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。
(g) 其它本应用根据法律、法规或者网站政策认为合适的披露。</p>
<h5>4. 信息存储和交换</h5>
<p>
本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。</p>
<h5>5. Cookie的使用</h5>
<p>(a) 在您未拒绝接受cookies的情况下本应用会在您的计算机上设定或取用cookies
以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务包括推广服务。
(b)
您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。
(c) 通过本应用所设cookies所取得的有关信息将适用本政策。</p>
<h5>6. 信息安全</h5>
<p>(a)
本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。
(b) 在使用本应用网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对</p>
<h5>7.本隐私政策的更改</h5>
<p>
(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。
(b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。
方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。</p>
</div>
',
'terms_privacy_title' => '条款与条件及隐私政策',
'close' => '关闭'
],
'lookup' => [
'hot_price' => '热门套餐',
'status_check_title' => '查询EVUS状态',
'passport_number_label' => '护照号码',
'enter_or_upload' => '输入或拍照上传',
'passport_number_required' => '请填写护照号码',
'upload_passport_photo' => '拍照/上传有效护照照片',
'passport_number_tooltip' => '护照号码通常为G、E、EA、EB、EC等开头的9位字符公务护照以及外交护照例外。请仔细区分数字1与字母I以及数字0与字母O。',
'visa_number_label' => '美国B1/B2签证号码',
'visa_number_required' => '请填写签证号码',
'visa_number_tooltip' => '请参考您的美国签证贴纸输入右下角8位红色字符的签证号码。',
'last_name_label' => '姓氏',
'pinyin' => '拼音',
'last_name_placeholder' => '请填写拼音姓',
'last_name_required' => '请填写拼音姓',
'last_name_tooltip' => '请勿在此栏输入名字。复姓请按照护照信息为准。若您没有姓氏,请在姓氏栏位填 "UNKNOWN"。',
'last_name_hint' => '请参考护照上的信息用英文字母填写您的姓氏',
'first_name_label' => '名字',
'first_name_placeholder' => '请填写拼音名',
'first_name_required' => '请填写拼音名',
'first_name_tooltip' => '请参考护照上的信息填写您的名字,请勿在此栏输入姓氏。',
'first_name_hint' => '依照您的护照正确输入您的名字',
'birth_date_label' => '出生日期',
'birth_date_placeholder' => '请选择出生日期',
'birth_date_required' => '请选择出生日期',
'birth_date_tooltip' => '请参考您护照上的信息填写您的出生日期,请确保出生日期正确',
'citizenship_label' => '公民身份国家',
'select_country_required' => '请选择国家',
'select_country_prompt' => '请选择国家',
'citizenship_tooltip' => '请从下拉式菜单选择您护照上出生地栏位显示的国家名称。',
'terms_acceptance' => '我已阅读并理解条款与条件及隐私政策。',
'check_status_button' => '查询状态',
'status_registered' => '已登记',
'registered_description1' => '“已登记”的EVUS状态意味着EVUS的要求得到了满足得以前往美国 1从登记批准之日起两年内有效期或 2直到主要的护照到期“主要的护照”是用于登记的护照是有效的并在登记申请时尚未过期或 3直到签证过期以先到者为准。这样并不保证入境美国。美国联邦Customs and Border Protection 的官员在美国的入境口岸将做最终决定。',
'registered_description2' => '要进入您的EVUS登记申请会要求您提供您的登记申请号码护照号码签证号码和出生日期。如果有必要您可以更新一份成功的登记内以下信息1在美地址和 2电子邮件地址。如果您需要更改或更新表格上的任何其他信息您必须申请新的旅行授权。',
'registered_description3' => '您已成功提交了以下所列',
'status_pending' => '审批中',
'pending_description' => '您的信息正在登记中,请耐心等候!',
'status_unsuccessful' => '登记不成功',
'unsuccessful_description1' => '您的EVUS登记不成功并且您目前没有得到授权前往美国。',
'unsuccessful_description2' => 'EVUS登记申请失败的常见原因包括您没有完成所有必填的资料; 您没有提供正确的信息; 您没有用有效的护照或美国签证申请登记,或者其他原因。',
'unsuccessful_description3' => '如需帮助请拨打EVUS帮助中心400-0878-958 。帮助中心每天24小時每周7天提供以帮助您与您的EVUS招生。',
'unsuccessful_description4' => '您已成功提交了以下所列的登记申请。为您的个人记录,请印列此页。',
'table_name' => '姓名',
'table_birth_date' => '出生日期',
'table_enrollment_number' => '登记申请号码',
'table_passport_number' => '护照号码',
'table_visa_number' => 'B1/B2签证的签证号码',
'table_status' => '状态',
'table_expiry' => '到期',
'table_view' => '查看',
'visa_services' => '美国签证服务',
'package1_title' => '套餐一',
'package1_price' => '¥198',
'package1_feature1' => 'DS-160 英文翻译填写',
'package2_title' => '套餐二',
'package2_price' => '¥1998',
'package2_feature1' => 'DS-160 英文翻译填写',
'package2_feature2' => '即时预约面签',
'package2_feature3' => '包含签证费用',
'package3_title' => '套餐三(限时特惠)',
'package3_price' => '¥2098<s style="font-size: 14px">¥2250</s>',
'package3_feature1' => 'DS-160 英文翻译填写',
'package3_feature2' => '即时预约',
'package3_feature3' => '面签辅导',
'package3_feature4' => '包含签证费用',
'package4_title' => '套餐四',
'package4_price' => '¥2099',
'package4_feature1' => '免面谈代传递(签证续签)',
'package4_feature2' => 'DS-160 英文翻译填写',
'package4_feature3' => '即时预约代传递',
'package4_feature4' => '包含签证费用',
'package4_feature5' => '(赴美目的说明)',
],
'esta' => [
'notice' => '注意您正在申请ESTA如果您拥有中国护照的请点击申请',
'personal_details_title' => '个人详细信息',
'passport_photo_label' => '护照照片',
'upload_instructions' => '点击上传,或将文件拖拽到此处',
'upload_success_alt' => '上传成功后渲染',
'passport_photo_required' => '请上传护照照片',
'passport_example_label' => '护照示例',
'personal_photo_label' => '个人照片',
'personal_photo_required' => '请上传个人照片',
'personal_photo_example_label' => '个人照示例',
'last_name_label' => '姓氏',
'pinyin' => '拼音',
'last_name_placeholder' => '请填写拼音姓',
'last_name_required' => '请填写拼音姓',
'last_name_tooltip' => '请勿在此栏输入名字。复姓请按照护照信息为准。若您没有姓氏,请在姓氏栏位填 "UNKNOWN"。',
'last_name_hint' => '请参考护照上的信息用英文字母填写您的姓氏',
'first_name_label' => '名字',
'first_name_placeholder' => '请填写拼音名',
'first_name_required' => '请填写拼音名',
'first_name_tooltip' => '请参考护照上的信息填写您的名字,请勿在此栏输入姓氏。',
'first_name_hint' => '依照您的护照正确输入您的名字',
'birth_date_label' => '出生日期',
'birth_date_placeholder' => '请选择出生日期',
'birth_date_required' => '请选择出生日期',
'birth_date_tooltip' => '请参考您护照上的信息填写您的出生日期,请确保出生日期正确',
'birth_city_label' => '出生城市',
'birth_city_placeholder' => '请填写出生城市',
'birth_city_required' => '请填写出生城市',
'birth_city_tooltip' => '请参考您护照上的信息填写您的出生地点。',
'birth_country_label' => '出生国家',
'select_country_required' => '请选择一个国家',
'select_country_prompt' => '请选择一个国家',
'birth_country_tooltip' => '请从下拉式菜单选择您护照上出生地栏位显示的国家名称。',
'gender_label' => '性别',
'male' => '男性',
'female' => '女性',
'id_number_label' => 'ID证件号码',
'id_number_placeholder' => '请填写ID证件号码',
'id_number_required' => '请填写ID证件号码',
'id_number_tooltip' => '请输入身份证号码。如果没有身份证号码,请填写"UNKNOWN"',
'former_name_label' => '曾用名(选填)',
'former_name_placeholder' => '请填写曾用名',
'former_name_required' => '请填写曾用名',
'next_step' => '下一步',
'travel_documents_title' => '旅行文件详细信息',
'passport_country_label' => '护照国家',
'passport_number_label' => '护照号码',
'passport_number_placeholder' => '请填写护照号码',
'passport_number_required' => '请填写护照号码',
'passport_number_tooltip' => '护照号码通常为G、E、EA、EB、EC等开头的9位字符公务护照以及外交护照例外。请仔细区分数字1与字母I以及数字0与字母O。',
'passport_issue_date_label' => '护照签发时间',
'passport_issue_date_placeholder' => '请选择护照签发日期',
'passport_issue_date_required' => '请选择护照签发日期',
'passport_issue_date_tooltip' => '请参照护照上的签发日期填写',
'passport_expiry_label' => '护照有效期',
'passport_expiry_placeholder' => '请选择护照有效期',
'passport_expiry_required' => '请选择护照有效期',
'passport_expiry_tooltip' => '请参照护照上的有效期填写',
'visa_number_label' => '填写签证号码:(如果有)',
'visa_number_placeholder' => '请填写签证号码',
'visa_number_required' => '请填写签证号码',
'other_citizenship_title' => '其他公民身份、国籍、护照',
'current_other_nationality_label' => '您现在是否拥有其他国籍?',
'current_other_nationality_tooltip' => '请确认是否拥有除中国以外的他国国籍',
'other_citizenship_current_label' => '其他国家公民当前',
'other_citizenship_current_tooltip' => '选择您的其他国籍的国家。如果您以上回答"是",该国家必须完成您的申请',
'how_acquired_citizenship_label' => '你如何从这个国家获得公民/国籍',
'how_acquired_citizenship_tooltip' => '选择你如何获得国籍/从这个国家的国籍',
'past_other_nationality_label' => '您曾经是否拥有其他国籍?',
'past_other_nationality_tooltip' => '在此问题上回答"是"代表着现在已无此国籍',
'past_nationality_start_date_label' => '曾经拥有其他国籍的开始日期',
'past_nationality_end_date_label' => '曾经拥有其他国籍的结束日期',
'past_citizenship_label' => '其他国家公民过去',
'past_citizenship_tooltip' => '请从下拉式菜单选择您护照上出生地栏位显示的国家名称',
'other_passport_label' => '您曾经是否拥有其他国家的护照或居民身份证?',
'other_passport_tooltip' => '请确认是否拥有除中国以外的他国护照。若您有此材料但已遗失或不记得号码,可填写"UNKNOWN"',
'issuing_country_label' => '发行国',
'issuing_country_tooltip' => '选择国家的公民身份,因为它出现在您的文档。完成应用程序需签发国的文档',
'document_type_label' => '文档类型',
'document_type_tooltip' => '请选择另一个国家颁布的旅行文件类型',
'document_number_label' => '文档编号(选填)',
'document_number_placeholder' => '字母及数字(如无法提供请填写000000)',
'document_number_required' => '请填写文档编号',
'document_number_tooltip' => '输入您的旅行证件上显示的文件编号。旅行证件可能包含数字和/或字符。请仔细区分数字零和字母O',
'expiry_year_label' => '到期年份',
'expiry_year_placeholder' => '请选择到期年份',
'expiry_year_required' => '请选择到期年份',
'expiry_year_tooltip' => '请即使它已过期,说明颁发的另一个国家,你的旅行证件到期年份',
'select_option_required' => '请选择',
'select_option_prompt' => '请选择',
'select_document_type_required' => '请选择文档类型',
'select_document_type_prompt' => '请选择文档类型',
'select_date_required' => '请选择日期',
'select_date_prompt' => '请选择日期',
'passport_number' => '护照号',
'national_id' => '身份证号码',
'by_birth' => '经由出生',
'by_parents' => '通过父母',
'naturalization' => '归化',
'other' => '其他',
'yes' => '是',
'no' => '否',
'contact_information_title' => '联系方式',
'email_label' => '电子邮箱(选填)',
'email_placeholder' => '建议填写用于接收ESTA批准信',
'email_required' => '请填写电子邮箱',
'email_tooltip' => '您将通过邮件方式收到您的申请确认函。如有任何状态更新也将通过邮件通知您。',
'confirm_email_label' => '确认电子邮箱(选填)',
'confirm_email_placeholder' => '建议填写用于接收ESTA批准信',
'confirm_email_required' => '请填写电子邮箱',
'confirm_email_tooltip' => '您将通过邮件方式收到您的申请确认函。如有任何状态更新也将通过邮件通知您。',
'phone_number_label' => '联系电话',
'phone_number_placeholder' => '请输入手机号码',
'select_phone_prefix_prompt' => '请选择手机号前缀',
'address_details_title' => '详细地址',
'current_country_label' => '现居住国家',
'current_province_label' => '现居住省份/州',
'current_province_placeholder' => '填写居住省份/州',
'current_province_required' => '填写居住省份/州',
'current_city_label' => '现居住城市',
'current_city_placeholder' => '填写现居住城市',
'current_city_required' => '填写现居住城市',
'current_address_label' => '现居住地址',
'current_address_placeholder' => '填写现居住地址',
'current_address_required' => '填写现居住地址',
'parents_info_title' => '父母信息',
'father_surname_label' => '父亲的姓氏',
'father_surname_placeholder' => '填写父亲的姓氏',
'father_surname_required' => '填写父亲的姓氏',
'father_surname_tooltip' => '请输入申请人父亲的姓氏',
'father_name_label' => '父亲的名字',
'father_name_placeholder' => '填写父亲的名字',
'father_name_required' => '填写父亲的名字',
'father_name_tooltip' => '请输入申请人父亲的名字',
'mother_surname_label' => '母亲的姓氏',
'mother_surname_placeholder' => '填写母亲的姓氏',
'mother_surname_required' => '填写母亲的姓氏',
'mother_surname_tooltip' => '请输入申请人母亲的姓氏',
'mother_name_label' => '母亲的名字',
'mother_name_placeholder' => '填写母亲的名字',
'mother_name_required' => '填写母亲的名字',
'mother_name_tooltip' => '请输入申请人母亲的名字',
'employment_info_title' => '工作信息',
'has_employer_label' => '您现在或之前是否有过工作单位或雇主',
'has_employer_tooltip' => '如填"是"请填写工作单位信息',
'employer_country_label' => '工作单位所在国家',
'employer_country_tooltip' => '请在列表中选择工作单位所在国家',
'employer_state_label' => '工作单位所在省份/州',
'employer_state_placeholder' => '填写居住省份/州',
'employer_state_required' => '填写居住省份/州',
'employer_state_tooltip' => '建议使用英文或拼音输入工作单位所在省份。若不清楚可填写"UNKNOWN"',
'employer_city_label' => '工作单位所在城市',
'employer_city_placeholder' => '填写现居住城市',
'employer_city_required' => '填写现居住城市,请勿填写省份',
'employer_city_tooltip' => '建议使用英文或拼音输入工作单位所在城市。若不清楚可填写"UNKNOWN"',
'employer_address_label' => '工作单位地址',
'employer_address_placeholder' => '填写现居住地址',
'employer_address_required' => '填写现居住地址',
'employer_address_tooltip' => '建议使用英文或拼音输入工作单位的地址,不必在此栏填写省份或城市。若不清楚可填写"UNKNOWN"',
'employer_name_label' => '工作单位名称',
'employer_name_placeholder' => '填写工作单位名称',
'employer_name_required' => '填写工作单位名称限30字',
'employer_name_tooltip' => '请使用英文或拼音输入工作单位的名称。若没有工作单位,您也可以填写自雇、家庭主妇、等信息形容您的工作状况',
'travel_info_title' => '旅游信息',
'transit_purpose_label' => '您到美国的目的,是为了过境到另一国家吗?',
'transit_purpose_tooltip' => '请选择您前往美国是否只是为了过境而不停留。选否可选择填写在美联系人',
'us_address_section_title' => '在美国的地址(选填)',
'us_contact_name_label' => '将在美国到访的酒店名称 / 商业上往来的友人 / 亲友全名(选填)',
'us_contact_name_placeholder' => '不得含有数字和特殊字符。如无法提供请留空',
'us_contact_name_required' => '不得含有数字和特殊字符。如无法提供请留空',
'us_contact_name_tooltip' => '在美国的联系人可以是友人、亲戚或生意上的伙伴。若您在美国没有联系人,请输入您住宿地点的名称、地址和电话号码(例如酒店名称)。您也可输入"UNKNOWN"',
'us_state_label' => '所在州(选填)',
'select_state_required' => '请选择所在州',
'select_state_prompt' => '请选择所在州',
'us_state_tooltip' => '请在列表中选择您在美国的联系人所在的州。若不清楚可选择"未知"',
'us_city_label' => '所在城市(选填)',
'us_city_placeholder' => '填写联系人所在城市,不必填写省份',
'us_city_required' => '填写联系人所在城市,不必填写省份',
'us_city_tooltip' => '请输入您在美国的联系人所在城市。若不清楚可填写"UNKNOWN"',
'us_address_label' => '地址(选填)',
'us_address_placeholder' => '填写联系人的地址街道及号码,不必填写省份或城市',
'us_address_required' => '填写联系人的地址街道及号码,不必填写省份或城市',
'us_address_tooltip' => '请输入您在美国的联系人的地址街道及号码,不必在此栏填写省份或城市。若不清楚可填写"UNKNOWN"',
'us_phone_label' => '电话号码(选填)',
'us_phone_placeholder' => '请输入手机号码',
'us_phone_tooltip' => '请输入您在美国的联系人的电话号码。若不清楚可填写"0"',
'emergency_contact_title' => '紧急联系人(选填)',
'emergency_last_name_label' => '姓氏(选填)',
'emergency_last_name_placeholder' => '填写紧急联系人姓氏',
'emergency_last_name_required' => '填写紧急联系人姓氏',
'emergency_last_name_tooltip' => '请输入您的紧急联系人姓氏。紧急联系人可以是您在美国或其他国家的联系人,如家属、好友或商业伙伴。若没有紧急联系人,请留空',
'emergency_first_name_label' => '名字(选填)',
'emergency_first_name_placeholder' => '填写紧急联系人名字',
'emergency_first_name_required' => '填写紧急联系人名字',
'emergency_first_name_tooltip' => '请输入您的紧急联系人名字。紧急联系人名字可以是您在美国或其他国家的联系人,如家属、好友或商业伙伴。若没有紧急联系人,请留空',
'emergency_email_label' => '电子邮箱(选填)',
'emergency_email_placeholder' => '填写紧急联系人电子邮箱',
'emergency_email_required' => '填写紧急联系人电子邮箱',
'emergency_email_tooltip' => '请输入您的紧急联系人的电子邮箱。若没有紧急联系人,请留空',
'emergency_phone_label' => '电话号码(选填)',
'emergency_phone_placeholder' => '填写紧急联系人电话号码',
'emergency_phone_tooltip' => '请输入您的紧急联系人的电话号码。若没有紧急联系人请输入0',
'applicant_declaration_title' => '申请人声明',
'declaration_statement' => '本人声明在此申请中提供真实、完整和正确的信息。',
'terms_acceptance' => '我已阅读并理解条款与条件及隐私政策。',
'select_package_title' => '选择套餐',
'package_a_option' => 'A套餐:958元/人民币 Vip快速登记(资料完整6小时内通过)',
'package_b_option' => 'B套餐:495元/人民币 安逸登记(资料完整48小时内通过)',
'submit_application_button' => '立即提交',
'eligibility_questions_title' => '资格问题',
'question_a_label' => '您目前是否患有任何下列疾病(《公共健康法案》第 361 号 b 项)?霍乱、白喉、肺结核、结核、天花、黄热病、病毒性出血热,包括埃博拉病毒、拉萨热、马尔堡病、克里米亚-刚果出血热、可传染他人且可能致死的严重急性呼吸道疾病',
'question_b_label' => '您是否曾因严重破坏财产或伤及他人或政府机关,而被逮或被判罪?',
'question_c_label' => '您是否曾违反任何涉及持有、使用或分发非法药物的法律?',
'question_d_label' => '您是否试图或曾经从事恐怖活动、间谍、破坏活动或种族灭绝?',
'question_e_label' => '您是否曾为了取得签证、协助他人取得签证、或入境美国,犯下欺诈或伪造个人或他人的资料?',
'question_f_label' => '您目前是否在美国寻求工作或是您是否曾未经美国政府许可在美国工作?',
'question_g_label' => '您是否曾使用目前或先前的护照申请美国签证被拒?或者您是否曾被拒绝入境美国、或在美国入境口岸时,申请遭到驳回?',
'question_h_label' => '您是否曾在美国停留超过美国政府允许的入境期间?',
'question_i_label' => '您是否曾在 2011 年三月 1 日当日或其后到过伊拉克、叙利亚、伊朗或苏丹',
'eligibility_warning' => '您确定要对此问题回答「是」吗?如果您的答案为「是」,您就不能继续在线申请了。',
],
'pay' => [
'success_message' => '订单提交成功,请尽快付款,订单号',
'payment_amount' => '应付金额',
'order_success_message' => '您的订单已经提交成功,订单号为:',
'notice_1' => '1、您填写的信息必须和您的真实信息一致如提交的信息与事实不符合由此造成的后果本中心概不负责。',
'notice_3' => '2、工作时间周一至周日09:00 - 22:00 (节假日正常工作),在非工作时间段提交的申请表,系统自动延伸到下一工作日,敬请谅解。',
'notice_4' => '3、提交后我们会在规定的时间内翻译审核校对资料信息请注意查收邮件如收件箱里没有则很可能被隔离到了垃圾箱里。',
],
'jump' => [
'auto_redirect' => '秒后自动跳转',
'redirect_now' => '立即跳转'
],
'js' => [
'daxie' => '请填写大写字母',
'zhongwen' => '请填写填写中文',
'len40' => '请控制在100字以内',
'required_field' => '此项为必填项',
'min_length' => '至少需要%d个字符',
'max_length' => '最多允许%d个字符',
'ppt_min' => '至少需要9个字符',
'ppt_max' => '最多允许9个字符',
'ppt_format' => '护照格式应为字母开头 + 至少6位数字或字母',
'visa_min' => '至少需要8个字符',
'visa_max' => '最多允许8个字符',
'visa_format' => '签证格式不正确',
'default_reqtext' => '必填项不能为空',
'checkbox_checked' => '必须选择一个选项',
'checkbox_required' => '请勾选声明',
'english_only' => '只能输入英文字母',
'success_message' => '上传成功',
'fail_message' => '上传失败:',
'error_message' => '上传出错',
'upload_image_only' => '文件必须为图片',
'browser_not_supported' => '你的浏览器不支持上传',
'visa_unrecognized' => '无法识别,请手动填写签证号',
'manual_entry_required' => '无法识别,请手动填写',
'upload_retry' => '请重新上传',
'status_enrolled' => '已登记',
'status_pending' => '审批中',
'status_unsuccessful' => '登记不成功',
'status_revoked' => 'DOS已撤销您的签证',
'click_to_view' => '点击查看'
],
'controller' => [
'success' => '操作成功',
'fail' => '操作失败',
'price_error' => '价格错误',
'server_error' => '服务器错误',
'no_registration_info' => '未检索到登记信息',
'registration_expired' => '您正在搜索的登记申请已过期或取消',
'registration_success' => '登记成功',
'registration_failed' => '登记不成功',
'registration_pending' => '登记中',
'visa_revoked' => '已撤销您的签证',
'missing_order_sn' => '缺少订单号,请重新填写',
'order_not_exist' => '订单号不存在,请重新填写',
'payment_success' => '订单已支付成功',
'parameter_error' => '参数错误',
'payment_not_completed' => '订单未支付',
'order_not_exist_retry' => '订单号不存在,请重新支付',
'payment_not_completed_retry' => '您还未支付,请重新支付',
],
'payment' => [
'select_payment_method' => '选择支付方式',
'wechat_pay' => '微信支付',
'alipay' => '支付宝',
'checkout' => 'checkout',
'success' => '支付成功',
'fail' => '支付失败'
],
'foot' => [
'user_agreement' => '条款与条件',
'privacy_policy' => '隐私政策',
'disclaimer' => '免责声明',
'refund_policy' => '退款与支付条款',
'cookie_policy' => 'Cookies 使用声明',
'user_agreement_info' => '<div style="padding: 20px;"> <p>
签证更新电子系统EVUS与执法部门 的数据库进行比对。所有持10年有效期访客签证的旅客B1, B2 或 B1/B2,
如果需要以短期访客身份入境美国都必须在赴美前在EVUS上登记。
如果您的EVUS登记获得批准就意味着您可以前往美国。但这并不意味着您可以入境美国。抵达美国入境口岸时您将接受United States
Customs and Border Protection官员的检查。 入境口岸的官员会根据美国移民法的规定决定您是否可以入境。
由您或指定的第三方代理人所提供的所有信息必须是真实和正确的。如果您在填写信息时由您或您的代理人有意提交任何虚假、虚构或伪造的声明或陈述造成EVUS更新失败或者美国签证被注销责任由您本人承担。
信息填写完成后,我们根据你所选择的套餐时效反馈信息,提交后信息将不能再更改。
</p>
<p> 请表明您已阅读并了解上述所提供的信息</p></div>',
'privacy_policy_info' => ' <div style="padding: 20px;"><p>
本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。注意:为更好的提供您所要求的产品和服务上文所述对“第三方”的限制不包括本公司的关联公司(母公司,子公司,控股、被控股公司等),“第三方”是指本司及本司关联公司之外的主体,即下文:信息披露款项中(b)款之规定。
</p>
<h3>1. 适用范围</h3>
<p>(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息; (b) 在您使用本应用网络服务或访问本应用平台网页时本应用自动接收并记录的您的浏览器和计算机上的信息包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据 (c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。 您了解并同意,以下信息不适用本隐私权政策: (a) 您在使用本应用平台提供的搜索服务时输入的关键字信息; (b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情; (c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。
</p>
<h3>2. 信息使用</h3>
<p>
(a)本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。 (b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。 (c) 为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)
</p>
<h3>3. 信息披露</h3>
<p>在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息: (a) 经您事先同意,向第三方披露; (b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息; (c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露; (d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露; (e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷; (f) 在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。 (g) 其它本应用根据法律、法规或者网站政策认为合适的披露。
</p>
<h3>4. 信息存储和交换</h3>
<p>
本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。
</p>
<h3>5. Cookie的使用</h3>
<p>(a) 在您未拒绝接受cookies的情况下本应用会在您的计算机上设定或取用cookies 以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务包括推广服务。 (b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。 (c) 通过本应用所设cookies所取得的有关信息将适用本政策。
</p>
<h3>6. 信息安全</h3>
<p>(a) 本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。 (b) 在使用本应用网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易
</p>
<h3>7.本隐私政策的更改</h3>
<p>
(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。 (b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。 方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。
</p>
</div>',
'disclaimer_info' => '<div style="padding: 20px;">
<div class="disclaimer-item">
<h4>1. 非官方性质声明</h4>
<p>本网站并非美国政府官方网站也不隶属于美国海关与边境保护局CBP。我们是独立的服务提供商专门协助申请人完成EVUS登记流程。</p>
</div>
<div class="disclaimer-item">
<h4>2. 服务范围说明</h4>
<p>ITS INTERNATIONAL TRAVEL SERVICE COMPANY 仅提供 EVUS 表格填写协助和相关技术支持服务。我们不对最终审批结果作出任何保证或承诺,因为审批权完全在于美国政府相关部门。</p>
</div>
<div class="disclaimer-item">
<h4>3. 审批权限声明</h4>
<p>EVUS申请是否获批完全由美国政府相关部门决定我们无法影响或加快审批进程。我们的服务仅限于协助您正确填写和提交申请表格。</p>
</div>
<div class="note">
<p>使用本服务即表示您已阅读、理解并同意上述免责声明。</p>
</div>
</div>
</div>',
'refund_policy_info' => '<div style="padding: 20px;"> <h4>1. 支付方式</h4> <p>(a) 本公司接受信用卡、借记卡、PayPal等多种支付方式</p> <p>(b) 所有支付交易均通过安全加密处理,保障您的资金安全</p><h4>2. 退款与支付</h4>
<p>(a) 在提交前用户可申请退款退款将在7-14个工作日内处理</p>
<p>(b) 一旦申请提交至EVUS系统费用不予退还因服务已实际提供</p>
<p>(c) 如遇重复扣款等系统问题,经核实后将全额退款</p>
<h4>3. 服务失败</h4>
<p>(a) 因用户提供的信息不准确或不完整导致申请失败,本公司不退款</p>
<p>(b) 因美国政府系统问题导致的申请失败,可提供相应证明申请部分退款</p>
<p>(c) 因本公司技术问题导致申请失败,将全额退款或重新提交申请</p></div>
',
'cookie_policy_info' => ' <div style="padding: 20px;">
<div class="cookie-notice">
<h3>Cookie使用说明</h3>
<p>本网站由 ITS INTERNATIONAL TRAVEL SERVICE COMPANY 运营,使用 Cookies 技术以提升用户体验。</p>
<h4>我们使用Cookies的主要目的</h4>
<ul>
<li>记住您的登录状态和偏好设置</li>
<li>分析网站访问量和用户行为模式</li>
<li>优化网站性能和改进服务质量</li>
<li>提供个性化的内容推荐</li>
</ul>
<h4>Cookie管理</h4>
<p>您可以通过浏览器设置限制或禁用Cookies。请注意如果禁用Cookies部分网站功能可能无法正常使用包括但不限于</p>
<ul>
<li>无法保持登录状态,需要每次访问时重新登录</li>
<li>个性化设置无法保存</li>
<li>某些服务可能完全无法使用</li>
</ul>
<p>继续使用本网站即表示您同意我们按照本说明使用Cookies技术。</p>
</div>
</div>'
],
];

View File

@@ -0,0 +1,81 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace app\log\driver;
use think\App;
use think\contract\LogHandlerInterface;
/**
* 本地化调试输出到文件
*/
class Monolog implements LogHandlerInterface
{
/**
* 配置参数
* @var array
*/
protected $config = [
'time_format' => 'c',
'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
];
/**
* 日志写入接口
* @access public
* @param array $log 日志信息
* @return bool
*/
public function save(array $log): bool
{
$info = [];
// 日志信息封装
$time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
foreach ($log as $type => $val) {
$message = [];
foreach ($val as $msg) {
$message[] = json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) ;
}
$info[$type] = $message;
}
if ($info) {
return $this->write($info);
}
return true;
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @return bool
*/
protected function write(array $message): bool
{
$tcp_log_url = env('TCP_LOG_URL');
$tcp_log_port = intval(env('TCP_LOG_PORT'));
$project = env('PROJECT_NAME');
$socket = @fsockopen($tcp_log_url,$tcp_log_port ,$errno, $errstr, 1);
if ($socket) {
fwrite($socket, json_encode(['message' => $message, 'project' => $project], $this->config['json_options']));
fclose($socket);
}
//
return true;
}
}

11
app/middleware.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
\think\middleware\LoadLangPack::class,
// Session初始化
\think\middleware\SessionInit::class,
\app\middleware\LogRecorder::class,
];

View File

@@ -0,0 +1,41 @@
<?php
declare (strict_types = 1);
namespace app\middleware;
use think\facade\Log;
class LogRecorder
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
// 记录请求信息
$requestInfo = [
'time' => date('Y-m-d H:i:s'),
'method' => $request->method(),
'uri' => $request->url(),
'params' => $request->param(),
'headers' => $request->header(),
'ip' => $request->ip(),
];
Log::write($requestInfo, 'request');
// 继续处理请求
$response = $next($request);
// 记录响应信息
$responseInfo = [
'status' => $response->getCode(),
'data' => $response->getContent(),
];
Log::write($responseInfo, 'response');
return $response;
}
}

413
app/miniapi/common.php Normal file
View File

@@ -0,0 +1,413 @@
<?php
const GET_WEB_CONFIG_URL = 'https://uniuser.jzvisa.com/home/Company/index/model/evus';
const CREATE_EVUS_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/applyevus';
const UPDATE_EVUS_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/updateevus';
const CREATE_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/do_apply';
const NEWS_LIST = 'https://uniuser.jzvisa.com/home/news/lists';
const NEWS_DETAIL = 'https://uniuser.jzvisa.com/home/news/detail';
const YZM_URL = 'https://uniuser.jzvisa.com/home/sms/sendCode';
const GET_ORDER_URL = 'https://uniuser.jzvisa.com/home/order/orderList';
const INCOICETYPE = '1';
const MODEL = 'msg';
const MINIAPI_TOKEN_KEY = '20241021';
const MINIAPI_TOKEN_EXPIRE = 604800;
const BUCKET = 'jnevus';
const COUNTRY = 'EVUS登记';
const SOURCE = 'cn';
const PAY_MARK = 'evuscn';
const OSS_URL = 'https://files.jzvisa.com/' . BUCKET . '/';
/*esta 配置*/
const ESTA_COUNTRY = 'ESTA登记';
const PAY_ESTA_MARK = 'estacn';
const ESTA_MODEL = 'esta';
const ESTA_INCOICETYPE = '6';
// 公共函数
function formDataToJson($fields, $postData, $kew_key, $checkField)
{
if (!isset($postData[$checkField]) || $postData[$checkField] != 1) {
foreach ($fields as $field) {
unset($postData[$field]);
}
return $postData;
}
$data = [];
foreach ($postData[$fields[0]] as $index => $value) {
$entry = [];
foreach ($fields as $field) {
$entry[$field] = isset($postData[$field][$index]) ? $postData[$field][$index] : null;
}
$data[] = $entry;
}
$postData[$kew_key] = json_encode($data, JSON_UNESCAPED_UNICODE);
foreach ($fields as $field) {
unset($postData[$field]);
}
return $postData;
}
function find_json($json_name, $field, $field_value)
{
$data = file_get_contents($json_name);
$data_arr = json_decode($data, true);
$found = array_filter($data_arr, function ($item) use ($field, $field_value) {
return $item[$field] === $field_value;
});
// 输出结果
if ($found) {
$found = array_values($found); // 获取过滤后的第一个元素
return $found;
} else {
return false;
}
}
function getNameById($json_name, $id)
{
$data = file_get_contents('./json/' . $json_name . '.json');
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['id'] === $id) {
return $dataItem['name'];
}
}
}
}
return null;
}
function getIdByName($json_name, $id)
{
$data = file_get_contents('./json/' . $json_name . '.json');
$data_arr = json_decode($data, true);
foreach ($data_arr as $item) {
if (array_key_exists('data', $item['data'])) {
foreach ($item['data']['data'] as $dataItem) {
if ($dataItem['name'] === $id) {
return $dataItem['id'];
}
}
}
}
return null;
}
function isMobile()
{
$_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i',
strtolower($_SERVER['HTTP_USER_AGENT']))) {
return true;
}
if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']),
'application/vnd.wap.xhtml+xml') !== false)) {
return true;
}
if (isset($_SERVER['HTTP_X_WAP_PROFILE'])) {
return true;
}
if (isset($_SERVER['HTTP_PROFILE'])) {
return true;
}
$mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
$mobile_agents = array(
'w3c ',
'acs-',
'alav',
'alca',
'amoi',
'audi',
'avan',
'benq',
'bird',
'blac',
'blaz',
'brew',
'cell',
'cldc',
'cmd-',
'dang',
'doco',
'eric',
'hipt',
'inno',
'ipaq',
'java',
'jigs',
'kddi',
'keji',
'leno',
'lg-c',
'lg-d',
'lg-g',
'lge-',
'maui',
'maxo',
'midp',
'mits',
'mmef',
'mobi',
'mot-',
'moto',
'mwbp',
'nec-',
'newt',
'noki',
'oper',
'palm',
'pana',
'pant',
'phil',
'play',
'port',
'prox',
'qwap',
'sage',
'sams',
'sany',
'sch-',
'sec-',
'send',
'seri',
'sgh-',
'shar',
'sie-',
'siem',
'smal',
'smar',
'sony',
'sph-',
'symb',
't-mo',
'teli',
'tim-',
'tosh',
'tsm-',
'upg1',
'upsi',
'vk-v',
'voda',
'wap-',
'wapa',
'wapi',
'wapp',
'wapr',
'webc',
'winw',
'winw',
'xda',
'xda-'
);
if (in_array($mobile_ua, $mobile_agents)) {
return true;
}
if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false) {
return true;
}
// Pre-final check to reset everything if the user is on Windows
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false) {
return false;
}
// But WP7 is also Windows, with a slightly different characteristic
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false) {
return true;
}
return false;
}
function getOrderNumber()
{
return date('YmdHis') . mt_rand(10, 99);
}
function cmf_get_current_user_id()
{
$sessionUserId = session('user.id');
if (empty($sessionUserId)) {
return 0;
}
return $sessionUserId;
}
function getIP()
{
$realip = '';
$unknown = 'unknown';
if (isset($_SERVER)){
if(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_FOR'], $unknown)){
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
foreach($arr as $ip){
$ip = trim($ip);
if ($ip != 'unknown'){
$realip = $ip;
break;
}
}
}else if(isset($_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP']) && strcasecmp($_SERVER['HTTP_CLIENT_IP'], $unknown)){
$realip = $_SERVER['HTTP_CLIENT_IP'];
}else if(isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']) && strcasecmp($_SERVER['REMOTE_ADDR'], $unknown)){
$realip = $_SERVER['REMOTE_ADDR'];
}else{
$realip = $unknown;
}
}else{
if(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), $unknown)){
$realip = getenv("HTTP_X_FORWARDED_FOR");
}else if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), $unknown)){
$realip = getenv("HTTP_CLIENT_IP");
}else if(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), $unknown)){
$realip = getenv("REMOTE_ADDR");
}else{
$realip = $unknown;
}
}
$realip = preg_match("/[\d\.]{7,15}/", $realip, $matches) ? $matches[0] : $unknown;
return $realip;
}
function build_ocr_signature(string $appId, string $secret, ?int $timestamp = null): array
{
$timestamp = $timestamp ?? time();
$signString = $appId . "\n" . (string)$timestamp;
$signature = hash_hmac('sha256', $signString, $secret);
return [
'app_id' => $appId,
'timestamp' => (string)$timestamp,
'signature' => $signature,
'base_url' => config('app.OCR_BASE_URL'),
];
}
// 这是系统自动生成的公共文件
function arrayToJson($params, $values)
{
$datas = [];
$count = count($values[0]);
$valcount = count($params);
for ($i = 0; $i < $count; $i++) {
for ($j = 0; $j < $valcount; $j++) {
$data[$params[$j]] = $values[$j][$i];
}
$datas[] = $data;
}
if (empty($datas)) {
return '';
}
return json_encode($datas);
}
function getCate($val)
{
$cates = explode('-', $val);
if (count($cates) == 1) {
if ($val == 'CNMI') {
return 'CW/E2C';
}
} else {
preg_match_all('/[A-Z]+/', $val, $matches);
return $matches[0][0];
}
}
//非常给力的authcode加密函数,Discuz!经典代码(带详解)
function authcode($string, $operation = 'DECODE', $key = '20241021', $expiry = 0)
{
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
$ckey_length = 4;
// 密匙
$key = md5($key ? $key : $GLOBALS['discuz_auth_key']);
// 密匙a会参与加解密
$keya = md5(substr($key, 0, 16));
// 密匙b会用来做数据完整性验证
$keyb = md5(substr($key, 16, 16));
// 密匙c用于变化生成的密文
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
// 参与运算的密匙
$cryptkey = $keya . md5($keya . $keyc);
$key_length = strlen($cryptkey);
// 明文前10位用来保存时间戳解密时验证数据有效性10到26位用来保存$keyb(密匙b)
//解密时会通过这个密匙验证数据完整性
// 如果是解码的话,会从第$ckey_length位开始因为密文前$ckey_length位保存 动态密匙,以保证解密正确
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
// 产生密匙簿
for ($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
// 核心加解密部分
for ($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
// 从密匙簿得出密匙进行异或,再转成字符
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if ($operation == 'DECODE') {
// 验证数据有效性,请看未加密明文的格式
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
} else {
// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
// 因为加密后的密文可能是一些特殊字符复制过程可能会丢失所以用base64编码
return $keyc . str_replace('=', '', base64_encode($result));
}
}
function getChineseCode($code)
{
$url = "http://code.mcdvisa.com/save.php?action=getcode&i=0&w={$code}";
$data = file_get_contents($url);
if (empty($data)){
return [
'code'=>0,
'data'=>[]
];
}
// 创建 DOMDocument 对象并加载 HTML
$dom = new DOMDocument();
libxml_use_internal_errors(true); // 抑制 HTML 解析错误
$dom->loadHTML($data);
libxml_clear_errors();
// 获取所有的 <li> 元素
$lis = $dom->getElementsByTagName('li');
$pattern = '/-?\d+(?:\.\d+)?/';
$datas=[];
foreach ($lis as $li) {
$text = $li->textContent;
preg_match_all($pattern, $text, $matches);
$datas[] = $matches[0][0];
}
return [
'code'=>200,
'data'=>$datas
];
}

View File

@@ -0,0 +1,134 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
use app\service\MiniProgramWechatService;
use think\exception\ValidateException;
use think\facade\Session;
class Auth extends Base
{
public function sendSms()
{
$data = $this->postData();
if ($error = $this->requireFields($data, ['mobile'])) {
return $error;
}
$code = mt_rand(1001, 9999);
$response = Httpcurl::request(YZM_URL, 'post', [
'mobile' => $data['mobile'],
'code' => $code,
]);
$result = $this->decodeResponse($response, 'miniapi send sms');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return $this->fail('发送验证码失败');
}
Session::set($data['mobile'] . '_code', $code);
Session::set($data['mobile'] . '_' . $code, time());
return $this->ok([], '发送成功');
}
public function login()
{
$data = $this->postData();
try {
$this->validate($data, [
'mobile' => 'require',
'code' => 'require',
], [
'mobile.require' => '手机号不能为空',
'code.require' => '验证码不能为空',
]);
} catch (ValidateException $e) {
return $this->fail($e->getError());
}
$sms = Session::get($data['mobile'] . '_code');
if (empty($sms) || strval($sms) !== strval($data['code'])) {
return $this->fail('验证码不正确');
}
$codeTime = Session::get($data['mobile'] . '_' . $data['code']);
if (empty($codeTime) || (time() - intval($codeTime)) > 300) {
Session::delete($data['mobile'] . '_code');
Session::delete($data['mobile'] . '_' . $data['code']);
return $this->fail('验证码超时,请重新发送');
}
$openid = '';
if (!empty($data['login_code'])) {
$openid = $this->wechatService()->openidByLoginCode((string)$data['login_code']);
}
return $this->loginByMobile((string)$data['mobile'], $openid);
}
public function wxPhoneLogin()
{
$data = $this->postData();
if ($error = $this->requireFields($data, ['phone_code', 'login_code'])) {
return $error;
}
$wechat = $this->wechatService();
$openid = $wechat->openidByLoginCode((string)$data['login_code'], true);
if ($openid === '') {
return $this->fail('微信登录失败,请使用验证码登录');
}
$mobile = $wechat->mobileByPhoneCode((string)$data['phone_code']);
if ($mobile === '') {
return $this->fail('微信手机号获取失败,请使用验证码登录');
}
return $this->loginByMobile($mobile, $openid);
}
public function me()
{
$userId = $this->currentUserId();
if ($userId <= 0) {
return $this->ok(['isLogin' => false]);
}
return $this->ok([
'isLogin' => true,
'user' => [
'id' => $userId,
],
]);
}
private function loginByMobile(string $mobile, string $openid = '')
{
$response = Httpcurl::request(config('app.user_url'), 'post', [
'model' => MODEL,
'mobile' => $mobile,
]);
$result = $this->decodeResponse($response, 'miniapi login');
if (empty($result) || intval($result['code'] ?? 0) !== 1 || empty($result['data']['id'])) {
return $this->fail('账号不存在');
}
$user = $result['data'];
return $this->ok([
'token' => authcode((string)$user['id'], 'ENCODE', MINIAPI_TOKEN_KEY, MINIAPI_TOKEN_EXPIRE),
'expires_in' => MINIAPI_TOKEN_EXPIRE,
'openid' => $openid,
'user' => [
'id' => intval($user['id']),
'mobile' => $user['mobile'] ?? $mobile,
'nickname' => $user['nickname'] ?? '微信用户',
],
], '登录成功');
}
private function wechatService(): MiniProgramWechatService
{
return new MiniProgramWechatService($this->currentMiniAppCode(), $this->currentMiniApp());
}
}

View File

@@ -0,0 +1,283 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
use app\BaseController;
use app\miniapi\service\Context\MiniAppContext;
use think\exception\HttpResponseException;
use think\facade\Config;
use think\facade\Log;
class Base extends BaseController
{
protected $miniAppCode = '';
protected $miniApp = [];
protected function initialize()
{
parent::initialize();
$this->initializeMiniAppContext();
$this->loadSiteConfig();
}
protected function ok($data = [], string $msg = 'ok')
{
return json([
'code' => 1,
'msg' => $msg,
'data' => $data,
]);
}
protected function fail(string $msg = '操作失败', int $code = 0, $data = [])
{
$httpStatus = in_array($code, [401, 403], true) ? $code : 200;
return json([
'code' => $code,
'msg' => $msg,
'data' => $data,
], $httpStatus);
}
protected function postData(): array
{
$data = $this->request->post();
if (!empty($data)) {
return $data;
}
$raw = $this->request->getInput();
$json = json_decode($raw, true);
return is_array($json) ? $json : [];
}
protected function requireFields(array $data, array $fields)
{
foreach ($fields as $field) {
if (!isset($data[$field]) || $data[$field] === '') {
return $this->fail('缺少参数:' . $field);
}
}
return null;
}
protected function decodeResponse($response, string $scene): array
{
if (!is_array($response) || !isset($response[0]) || !empty($response[3])) {
Log::error($scene . ' request failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
return [];
}
$result = json_decode($response[0], true);
if (!is_array($result)) {
Log::error($scene . ' response decode failed: ' . $response[0]);
return [];
}
return $result;
}
protected function currentUserId(): int
{
$token = $this->currentToken();
if ($token === '') {
return 0;
}
$userId = authcode($token, 'DECODE', MINIAPI_TOKEN_KEY);
return is_numeric($userId) ? intval($userId) : 0;
}
protected function requireLogin()
{
$userId = $this->currentUserId();
if ($userId <= 0) {
return $this->fail('请先登录', 401);
}
return $userId;
}
protected function currentMiniAppCode(): string
{
return $this->miniAppCode;
}
protected function currentMiniApp(): array
{
return $this->miniApp;
}
protected function currentCompanyCode(): string
{
return (string)($this->miniApp['company_code'] ?? '');
}
protected function currentMiniAppContext(string $businessCode = 'evus'): MiniAppContext
{
$context = (array)Config::get('app.miniapi_context', []);
return new MiniAppContext([
'mini_app_code' => $this->currentMiniAppCode(),
'company_code' => $this->currentCompanyCode(),
'business_code' => $businessCode,
'site_domain' => (string)($context['site_domain'] ?? ''),
'mini_app' => $this->currentMiniApp(),
'site' => (array)Config::get('app.miniapi_site', []),
]);
}
private function currentToken(): string
{
$token = trim((string)$this->request->header('Authorization', ''));
if (stripos($token, 'Bearer ') === 0) {
$token = trim(substr($token, 7));
}
if ($token === '') {
$token = trim((string)$this->request->param('token', ''));
}
return $token;
}
private function loadSiteConfig(): void
{
$configFile = $this->siteConfigFile();
if (!file_exists($configFile)) {
$legacyConfig = $this->legacySiteConfig();
if (!empty($legacyConfig)) {
$config = $legacyConfig;
file_put_contents($configFile, json_encode($config, JSON_UNESCAPED_UNICODE));
} else {
$domain = $this->siteConfigDomain();
$response = Httpcurl::request(GET_WEB_CONFIG_URL . '/domain/' . $domain, 'get');
$config = $this->decodeResponse($response, 'site config');
if (empty($config) || intval($config['code'] ?? 0) !== 1) {
return;
}
file_put_contents($configFile, json_encode($config, JSON_UNESCAPED_UNICODE));
}
} else {
$config = json_decode(file_get_contents($configFile), true);
}
if (empty($config['data']) || !is_array($config['data'])) {
return;
}
$data = $config['data'];
Config::set(['EVUS_GOODS' => $data['EVUS_GOODS'] ?? []], 'app');
Config::set(['ESTA_GOODS' => $data['ESTA_GOODS'] ?? []], 'app');
Config::set(['pay_url' => $data['pay']['pay_url'] ?? ''], 'app');
Config::set(['order_url' => $data['order_url'] ?? ''], 'app');
Config::set(['user_url' => $data['user_url'] ?? ''], 'app');
Config::set(['get_evus_detail_url' => $data['get_evus_detail_url'] ?? ''], 'app');
Config::set(['get_order_detail_url' => $data['get_order_detail_url'] ?? ''], 'app');
Config::set(['download_evus_url' => $data['download_evus_url'] ?? ''], 'app');
Config::set(['update_order_url' => $data['update_order_url'] ?? ''], 'app');
Config::set(['LOOKUP_URL' => $data['evus_look_url'] ?? ''], 'app');
Config::set(['OCR_BASE_URL' => $data['ocr_base_url'] ?? ''], 'app');
Config::set(['OCR_APP_ID' => $data['ocr_app_id'] ?? ''], 'app');
Config::set(['OCR_SECRET' => $data['ocr_secret'] ?? ''], 'app');
Config::set(['miniapi_site' => [
'company_name' => $data['company_name'] ?? '',
'principals' => $data['principals'] ?? '',
'phone' => $data['phone'] ?? '',
'brief' => $data['brief'] ?? '',
'footer' => $data['footer'] ?? '',
'logo' => $data['logo'] ?? '',
'logo_icon' => $data['logo_icon'] ?? '',
]], 'app');
}
private function initializeMiniAppContext(): void
{
$apps = Config::get('miniapp.apps', []);
$headerName = (string)Config::get('miniapp.header_name', 'X-Mini-App-Code');
$defaultCode = (string)Config::get('miniapp.default_code', '');
$code = trim((string)$this->request->header($headerName, ''));
if ($code === '') {
$code = trim((string)$this->request->param('mini_app_code', ''));
}
$hasExplicitCode = $code !== '';
if ($code === '') {
$code = $defaultCode;
}
if ($code !== '' && (!isset($apps[$code]) || intval($apps[$code]['status'] ?? 1) !== 1)) {
Log::warning('miniapp config not found or disabled: ' . $code);
if ($hasExplicitCode) {
throw new HttpResponseException($this->fail('mini app config not found', 403));
}
$code = $defaultCode;
}
$this->miniAppCode = $code;
$this->miniApp = $apps[$code] ?? [];
if (!empty($this->miniApp)) {
$this->miniApp['code'] = $code;
}
Config::set(['miniapi_context' => [
'mini_app_code' => $this->miniAppCode,
'company_code' => $this->currentCompanyCode(),
'site_domain' => $this->siteConfigDomain(),
]], 'app');
}
private function siteConfigFile(): string
{
$configDir = app()->getRootPath() . 'data/config/';
if (!is_dir($configDir)) {
mkdir($configDir, 0755, true);
}
return $configDir . $this->siteConfigCacheKey() . '.json';
}
private function legacySiteConfig(): array
{
$defaultCode = (string)Config::get('miniapp.default_code', '');
if ($this->miniAppCode !== $defaultCode) {
return [];
}
$legacyFile = app()->getRootPath() . 'data/config.json';
if (!file_exists($legacyFile)) {
return [];
}
$config = json_decode(file_get_contents($legacyFile), true);
return is_array($config) ? $config : [];
}
private function siteConfigDomain(): string
{
$domain = trim((string)($this->miniApp['site_domain'] ?? ''));
if ($domain === '') {
$domain = trim((string)Config::get('app.site_domain', ''));
}
if ($domain === '') {
return $this->request->host();
}
if (stripos($domain, 'http://') === 0 || stripos($domain, 'https://') === 0) {
$host = parse_url($domain, PHP_URL_HOST);
return $host ?: $this->request->host();
}
return trim(explode('/', $domain)[0]);
}
private function siteConfigCacheKey(): string
{
$key = $this->miniAppCode ?: $this->siteConfigDomain();
$key = preg_replace('/[^a-zA-Z0-9_.-]/', '_', $key);
return $key ?: 'default';
}
}

View File

@@ -0,0 +1,107 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
use app\miniapi\service\Context\MiniAppContext;
use app\miniapi\service\Payment\MiniPayService;
use app\miniapi\service\Product\ProductService;
class Esta extends Base
{
public function apply()
{
$userId = $this->requireLogin();
if (!is_int($userId)) {
return $userId;
}
$data = $this->postData();
if (empty($data['passport_img'])) {
return $this->fail('请上传护照照片');
}
if (empty($data['person_img'])) {
return $this->fail('请上传个人照片');
}
if ($error = $this->requireFields($data, ['total_price'])) {
return $error;
}
$context = $this->currentMiniAppContext('esta');
$product = (new ProductService())->findByValue($context, $data['total_price']);
if (empty($product)) {
return $this->fail('价格错误');
}
$totalPrice = $product['value'];
$orderSn = getOrderNumber();
$payload = $this->buildOrderPayload($data, $orderSn, $product, $userId);
$response = Httpcurl::request(CREATE_ORDER_URL, 'post', $payload);
$result = $this->decodeResponse($response, 'miniapi esta apply');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return $this->fail($result['msg'] ?? '提交失败,请联系管理员');
}
if (!empty($data['openid'])) {
$pay = $this->requestMiniPay($context, $orderSn, $totalPrice, $data['openid']);
if ($pay['code'] !== 1) {
return $this->fail($pay['msg']);
}
return $this->ok([
'order_sn' => $orderSn,
'pay' => $pay['data'],
], '提交成功');
}
return $this->ok([
'order_sn' => $orderSn,
'pay' => null,
], '提交成功');
}
private function buildOrderPayload(array $data, string $orderSn, array $product, int $userId): array
{
$payload = $data;
unset($payload['token']);
$totalPrice = (string)$product['value'];
$payload['user_id'] = $userId;
$payload['order_sn'] = $orderSn;
$payload['source'] = 'qlwxmini';
$payload['is_mobile'] = 1;
$payload['total_price'] = $totalPrice;
$payload['origin_price'] = $totalPrice;
$payload['product_value'] = $totalPrice;
$payload['product_name'] = (string)($product['name'] ?? '');
$payload['product_desc'] = (string)($product['desc'] ?? '');
$payload['ip'] = getIP();
$payload['coupon_code'] = $payload['coupon_code'] ?? '';
$payload['model'] = ESTA_MODEL;
$payload['token'] = md5(ESTA_MODEL);
$payload['other_country_acquired_other'] = $payload['other_country_acquired_other'] ?? '';
foreach ([
'birth_date' => 'birth_date',
'passport_expedition_date' => 'passport_expedition_date',
'passport_expiration_date' => 'passport_expiration_date',
'other_country_date' => 'other_country_date',
'other_country_edate' => 'other_country_edate',
] as $field => $prefix) {
$date = date_parse($payload[$field] ?? '');
$payload[$prefix . '_day'] = (string)($date['day'] ?: '');
$payload[$prefix . '_month'] = (string)($date['month'] ?: '');
$payload[$prefix . '_year'] = (string)($date['year'] ?: '');
}
unset($payload['other_country_date'], $payload['other_country_edate']);
return $payload;
}
private function requestMiniPay(MiniAppContext $context, string $orderSn, $money, string $openid): array
{
return (new MiniPayService())->request($context, $orderSn, $money, $openid, 'miniapi esta mini pay');
}
}

View File

@@ -0,0 +1,139 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
use app\miniapi\service\Context\MiniAppContext;
use app\miniapi\service\Payment\MiniPayService;
use app\miniapi\service\Product\ProductService;
class Evus extends Base
{
public function lookup()
{
$data = $this->postData();
if ($error = $this->requireFields($data, [
'passport_number',
'visa_foil_number',
'last_name',
'first_name',
'birth_date',
'country_birth',
])) {
return $error;
}
$birth = date_parse($data['birth_date']);
$params = [
'passportNum' => $data['passport_number'],
'b1b2Num' => $data['visa_foil_number'],
'surname' => $data['last_name'],
'givenname' => $data['first_name'],
'year_birthday' => (string)($birth['year'] ?: ''),
'month_birthday' => (string)($birth['month'] ?: ''),
'day_birthday' => (string)($birth['day'] ?: ''),
'country_birth' => $data['country_birth'],
];
$response = Httpcurl::request(config('app.LOOKUP_URL'), 'post', json_encode($params, JSON_UNESCAPED_UNICODE), [
'Content-Type: application/json',
]);
$result = $this->decodeResponse($response, 'miniapi evus lookup');
if (empty($result)) {
return $this->fail('服务异常,请稍后重试');
}
return $this->ok($result);
}
public function apply()
{
$userId = $this->requireLogin();
if (!is_int($userId)) {
return $userId;
}
$data = $this->postData();
if ($error = $this->requireFields($data, ['total_price'])) {
return $error;
}
$context = $this->currentMiniAppContext('evus');
$product = (new ProductService())->findByValue($context, $data['total_price']);
if (empty($product)) {
return $this->fail('价格错误');
}
$totalPrice = $product['value'];
$orderSn = getOrderNumber();
$payload = $this->buildOrderPayload($data, $orderSn, $product, $userId);
$response = Httpcurl::request(CREATE_EVUS_ORDER_URL, 'post', $payload);
$result = $this->decodeResponse($response, 'miniapi evus apply');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return $this->fail($result['msg'] ?? '提交失败,请联系管理员');
}
if (!empty($data['openid'])) {
$pay = $this->requestMiniPay($context, $orderSn, $totalPrice, $data['openid']);
if ($pay['code'] !== 1) {
return $this->fail($pay['msg']);
}
return $this->ok([
'order_sn' => $orderSn,
'pay' => $pay['data'],
], '提交成功');
}
return $this->ok([
'order_sn' => $orderSn,
'pay' => null,
], '提交成功');
}
private function buildOrderPayload(array $data, string $orderSn, array $product, int $userId): array
{
$payload = $data;
unset($payload['token']);
$totalPrice = (string)$product['value'];
$payload['user_id'] = $userId;
$payload['order_sn'] = $orderSn;
$payload['source'] = 'qlwxmini';
$payload['is_mobile'] = 1;
$payload['total_price'] = $totalPrice;
$payload['origin_price'] = $totalPrice;
$payload['product_value'] = $totalPrice;
$payload['product_name'] = (string)($product['name'] ?? '');
$payload['product_desc'] = (string)($product['desc'] ?? '');
$payload['ip'] = getIP();
$payload['coupon_code'] = $payload['coupon_code'] ?? '';
$payload['model'] = MODEL;
$payload['token'] = md5(MODEL);
foreach ([
'birth_date' => 'birth_date',
'passport_expedition_date' => 'passport_expedition_date',
'passport_expiration_date' => 'passport_expiration_date',
'us_visa_passport_issue_date' => 'us_visa_passport_issue_date',
'us_visa_passport_expiration_date' => 'us_visa_passport_expiration_date',
'us_visa_birth_date' => 'us_visa_birth_date',
] as $field => $prefix) {
$date = date_parse($payload[$field] ?? '');
$payload[$prefix . '_day'] = (string)($date['day'] ?: '');
$payload[$prefix . '_month'] = (string)($date['month'] ?: '');
$payload[$prefix . '_year'] = (string)($date['year'] ?: '');
}
if (isset($payload['vist_country']) && is_array($payload['vist_country'])) {
$payload = formDataToJson(['vist_country', 'vist_from_date', 'vist_end_date', 'vist_country_des'], $payload, 'vistitem', 'question_i');
}
$payload['other_country_acquired_other'] = $payload['other_country_acquired_other'] ?? '';
return $payload;
}
private function requestMiniPay(MiniAppContext $context, string $orderSn, $money, string $openid): array
{
return (new MiniPayService())->request($context, $orderSn, $money, $openid, 'miniapi evus mini pay');
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
class Help extends Base
{
public function faqs()
{
return $this->ok([
[
'question' => '什么是签证更新电子系统EVUS',
'answer' => '签证更新电子系统是持有十年有效 B1/B2、B1 或 B2 访问者签证的中国公民使用的个人基本信息在线定期更新系统。除有效签证外,相关旅客还需要完成 EVUS 登记,才能获得赴美旅行许可。'
],
[
'question' => 'EVUS登记从哪一天开始强制施行',
'answer' => '自 2016 年 11 月 29 日起,持中华人民共和国护照并持有最长有效期十年 B1/B2、B1 或 B2 签证的个人,必须持有有效的 EVUS 登记才能赴美旅行。没有有效登记的旅客将不能获得登机牌或经由陆地入境口入境。'
],
[
'question' => 'B1/B2签证有效期有什么要求',
'answer' => '绝大多数 B1/B2 签证有效期为 10 年,所有十年 B1/B2 签证在前往美国前都需要完成 EVUS 更新。极个别低于 10 年有效期的 B1/B2 签证,通常不需要进行 EVUS 登记。'
],
[
'question' => '护照有效期和换发护照会影响EVUS吗',
'answer' => '16 岁以上护照有效期通常为 10 年16 岁以下通常为 5 年。护照有效期低于 6 个月时禁止前往美国。如果护照过期或有效期不足,需要换发新护照;换发时应保留含有效美国签证的旧护照。'
],
[
'question' => '美国B1/B2签证EVUS有效期是多久',
'answer' => '如果十年 B1/B2 签证有效且护照有效期大于 2 年 6 个月EVUS 通常有效期为 2 年。如果护照有效期少于 2 年 6 个月EVUS 有效期通常按护照实际有效期减去 6 个月计算。'
],
[
'question' => 'EVUS失效或个人信息变更后需要更新吗',
'answer' => 'EVUS 过期并不必然导致美国签证失效,可以在前往美国前重新登记。若住址、工作信息、赴美地址、联系方式等个人信息发生变化,建议及时更新 EVUS。'
],
[
'question' => 'EVUS登记需要准备什么',
'answer' => '旅客需要准备含有最长有效期十年 B1/B2、B1 或 B2 签证的中华人民共和国护照,并确保可以连接互联网。持十年有效期签证的未成年人,也需要持有效个人护照并在赴美前申请 EVUS。'
],
[
'question' => 'EVUS表格上会填写哪些问题',
'answer' => '每次登记都需要提供姓名、出生日期、紧急联系人、护照信息、个人和就业信息,并回答旅行资格相关问题。朋友、亲属、专业旅游从业人员或第三方可以代为提交,但旅客本人应对信息真实性和准确性负责。'
],
[
'question' => '进入美国是否需要登记EVUS',
'answer' => '是的。自 2016 年 11 月起需要登记。该要求用于定期更新旅客个人信息,有助于加快中国旅客入境美国。'
],
[
'question' => '其他国家是否也有EVUS要求',
'answer' => 'EVUS 是一项针对特定旅客的信息更新要求。美国政府曾预期未来可能将类似要求适用于其他国家。'
],
[
'question' => '刚拿到十年B1/B2签证还需要申请新签证吗',
'answer' => '不需要,原签证仍然有效。但如果在 2016 年 11 月以后前往美国,需要先完成 EVUS 登记。EVUS 登记和申请签证是两个不同的程序。'
],
]);
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use app\miniapi\service\Legal\LegalDocumentService;
use app\miniapi\service\Product\ProductService;
use think\facade\Config;
class Index extends Base
{
public function home()
{
$products = new ProductService();
return $this->ok([
'site' => Config::get('app.miniapi_site', []),
'packages' => $products->list($this->currentMiniAppContext('evus')),
'quickEntries' => [
['key' => 'apply', 'title' => '开始登记', 'path' => '/pages/apply/index'],
['key' => 'query', 'title' => '状态查询', 'path' => '/pages/query/index'],
['key' => 'orders', 'title' => '我的订单', 'path' => '/pages/orders/index'],
['key' => 'help', 'title' => '帮助中心', 'path' => '/pages/help/index'],
],
]);
}
public function packages()
{
$businessCode = strtolower(trim((string)$this->request->param('business', 'evus'))) ?: 'evus';
return $this->ok((new ProductService())->list($this->currentMiniAppContext($businessCode)));
}
public function about()
{
$site = Config::get('app.miniapi_site', []);
$companyName = trim((string)($site['company_name'] ?? '')) ?: 'EVUS登记助手';
$brief = $this->plainText((string)($site['brief'] ?? ''));
return $this->ok([
'title' => '关于我们',
'company_name' => $companyName,
'content' => $brief ?: $companyName . '提供 EVUS 登记资料提交、订单查询和进度管理服务。',
'phone' => (string)($site['phone'] ?? ''),
'notice' => '我们不是美国政府官方网站,登记结果以官方系统为准。',
'logo' => (string)($site['logo'] ?? ''),
'logo_icon' => (string)($site['logo_icon'] ?? ''),
]);
}
public function legal()
{
$type = strtolower(trim((string)$this->request->param('type', 'terms')));
$businessCode = strtolower(trim((string)$this->request->param('business', 'evus'))) ?: 'evus';
$document = (new LegalDocumentService())->get($this->currentMiniAppContext($businessCode), $type);
if (empty($document)) {
return $this->fail('legal type not found', 404);
}
return $this->ok($document);
}
public function countries()
{
$file = app()->getRootPath() . 'public/json/country.json';
if (!file_exists($file)) {
return $this->ok([]);
}
$countries = json_decode(file_get_contents($file), true);
return $this->ok(is_array($countries) ? $countries : []);
}
private function plainText(string $html): string
{
$html = preg_replace('/<\s*(h[1-6]|div|section|li)\b[^>]*>/i', "\n", $html);
$html = preg_replace('/<\s*\/\s*(h[1-6]|div|section|li)\s*>/i', "\n", (string)$html);
$text = preg_replace('/<\s*br\s*\/?\s*>/i', "\n", $html);
$text = preg_replace('/<\s*\/p\s*>/i', "\n", $text);
$text = strip_tags((string)$text);
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$text = preg_replace("/[ \t\r]+/", ' ', $text);
$text = preg_replace("/\n{3,}/", "\n\n", $text);
return trim((string)$text);
}
}

View File

@@ -0,0 +1,79 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
class News extends Base
{
public function lists()
{
$page = max(1, intval($this->request->param('page', 1)));
$size = max(1, intval($this->request->param('size', 10)));
$response = Httpcurl::request(NEWS_LIST, 'post', [
'model' => 'article',
'page' => $page,
'size' => $size,
]);
$result = $this->decodeResponse($response, 'miniapi news list');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return $this->ok(['list' => [], 'count' => 0, 'page' => $page, 'size' => $size]);
}
return $this->ok([
'list' => $this->normalizeList($result['data'] ?? []),
'count' => intval($result['count'] ?? count($result['data'] ?? [])),
'page' => $page,
'size' => $size,
]);
}
public function detail()
{
$id = intval($this->request->param('id', 0));
if ($id <= 0) {
return $this->fail('参数错误');
}
$response = Httpcurl::request(NEWS_DETAIL, 'post', [
'model' => 'article',
'id' => $id,
]);
$result = $this->decodeResponse($response, 'miniapi news detail');
if (empty($result) || intval($result['code'] ?? 0) !== 1 || empty($result['data'])) {
return $this->fail('新闻详情获取失败');
}
return $this->ok($this->normalizeItem($result['data']));
}
private function normalizeList($list): array
{
if (!is_array($list)) {
return [];
}
$data = [];
foreach ($list as $item) {
$data[] = $this->normalizeItem($item);
}
return $data;
}
private function normalizeItem($item): array
{
if (!is_array($item)) {
return [];
}
return [
'id' => intval($item['id'] ?? 0),
'title' => $item['title'] ?? '',
'content' => $item['contents'] ?? ($item['content'] ?? ''),
'create_time' => $item['create_time'] ?? '',
];
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . 'common.php';
class Ocr extends Base
{
private const ENDPOINTS = [
'passport' => '/ocr/passport/base64',
'visa' => '/ocr/visa-number/base64',
'identification' => '/ocr/idcard/base64',
];
public function signature()
{
$type = strtolower(trim((string)$this->request->param('type', 'passport')));
if (!isset(self::ENDPOINTS[$type])) {
return $this->fail('不支持的识别类型');
}
$appId = (string)(config('app.OCR_APP_ID') ?? '');
$secret = (string)(config('app.OCR_SECRET') ?? '');
$baseUrl = (string)(config('app.OCR_BASE_URL') ?? '');
if ($appId === '' || $secret === '' || $baseUrl === '') {
return $this->fail('OCR服务配置未完成');
}
$signature = build_ocr_signature($appId, $secret);
$signature['endpoint'] = self::ENDPOINTS[$type];
$signature['origin'] = $this->resolveOrigin();
$signature['expires_in'] = 300;
$signature['auto_rotate'] = true;
$signature['return_debug'] = false;
return $this->ok($signature);
}
private function resolveOrigin(): string
{
$origin = trim((string)(config('app.OCR_ORIGIN') ?? config('ocr.origin', '')));
if ($origin === '') {
$context = (array)(config('app.miniapi_context') ?? []);
$origin = trim((string)($context['site_domain'] ?? ''));
}
if ($origin === '') {
return '';
}
if (!preg_match('/^https?:\/\//i', $origin)) {
$origin = 'https://' . $origin;
}
$parts = parse_url($origin);
if (!is_array($parts) || empty($parts['host'])) {
return '';
}
$scheme = strtolower((string)($parts['scheme'] ?? 'https'));
if (!in_array($scheme, ['http', 'https'], true)) {
return '';
}
$port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
return $scheme . '://' . $parts['host'] . $port;
}
}

View File

@@ -0,0 +1,193 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use api\Httpcurl;
use app\miniapi\service\Payment\MiniPayService;
class Order extends Base
{
public function lists()
{
$userId = $this->requireLogin();
if (!is_int($userId)) {
return $userId;
}
$payStatus = (string)$this->request->param('pay_status', 'pay');
if (!in_array($payStatus, ['pay', 'nopay'], true)) {
return $this->fail('参数错误');
}
$business = $this->businessCode((string)$this->request->param('business', 'evus'));
$model = $this->businessModel($business);
$invoiceType = $this->businessInvoiceType($business);
$response = Httpcurl::request(GET_ORDER_URL, 'post', [
'model' => $model,
'user_id' => $userId,
'invoiceType' => $invoiceType,
'pay_status' => $payStatus,
]);
$result = $this->decodeResponse($response, 'miniapi order list');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return $this->ok([]);
}
$orders = array_map(static function ($item) use ($business) {
if (is_array($item)) {
$item['business'] = $business;
$item['business_text'] = $business === 'esta' ? 'ESTA' : 'EVUS';
}
return $item;
}, (array)($result['data'] ?? []));
return $this->ok($orders);
}
public function detail()
{
$userId = $this->requireLogin();
if (!is_int($userId)) {
return $userId;
}
$orderSn = (string)$this->request->param('order_sn', '');
if ($orderSn === '') {
return $this->fail('参数错误');
}
$business = $this->businessCode((string)$this->request->param('business', 'evus'));
$data = $this->fetchOrderDetail($orderSn, $business, $userId);
if (isset($data['error'])) {
return $this->fail($data['error'], intval($data['code'] ?? 0));
}
return $this->ok($data);
}
public function pay()
{
$userId = $this->requireLogin();
if (!is_int($userId)) {
return $userId;
}
$data = $this->postData();
$orderSn = trim((string)($data['order_sn'] ?? ''));
$business = $this->businessCode((string)($data['business'] ?? 'evus'));
$openid = trim((string)($data['openid'] ?? ''));
if ($orderSn === '' || $openid === '') {
return $this->fail('参数错误');
}
$detail = $this->fetchOrderDetail($orderSn, $business, $userId);
if (isset($detail['error'])) {
return $this->fail($detail['error'], intval($detail['code'] ?? 0));
}
$money = $this->payMoney($detail);
if ($money === '') {
return $this->fail('订单金额异常');
}
$pay = (new MiniPayService())->request(
$this->currentMiniAppContext($business),
$orderSn,
$money,
$openid,
'miniapi order mini pay'
);
if ($pay['code'] !== 1) {
return $this->fail($pay['msg']);
}
return $this->ok([
'order_sn' => $orderSn,
'pay' => $pay['data'],
], 'ok');
}
private function fetchOrderDetail(string $orderSn, string $business, int $userId): array
{
$model = $this->businessModel($business);
$detailUrl = $business === 'esta' ? config('app.get_order_detail_url') : config('app.get_evus_detail_url');
if (empty($detailUrl)) {
return ['error' => '订单详情接口未配置'];
}
$response = Httpcurl::request($detailUrl, 'post', [
'order_sn' => $orderSn,
'model' => $model,
'user_id' => $userId,
]);
$result = $this->decodeResponse($response, 'miniapi order detail');
if (empty($result) || intval($result['code'] ?? 0) !== 1) {
return ['error' => $result['msg'] ?? '订单不存在'];
}
$data = $result['data'] ?? [];
$orderUserId = intval($data['user_id'] ?? $data['order']['user_id'] ?? 0);
if ($orderUserId > 0 && $orderUserId !== $userId) {
return ['error' => '无权访问该订单', 'code' => 403];
}
if (is_array($data)) {
$data['business'] = $business;
$data['business_text'] = $business === 'esta' ? 'ESTA' : 'EVUS';
}
return is_array($data) ? $data : [];
}
private function businessCode(string $business): string
{
$business = strtolower(trim($business));
return $business === 'esta' ? 'esta' : 'evus';
}
private function businessModel(string $business): string
{
return $business === 'esta' ? ESTA_MODEL : MODEL;
}
private function businessInvoiceType(string $business): string
{
return $business === 'esta' ? ESTA_INCOICETYPE : INCOICETYPE;
}
private function payMoney(array $detail): string
{
$scopes = [$detail];
if (isset($detail['order']) && is_array($detail['order'])) {
$scopes[] = $detail['order'];
}
foreach ($scopes as $scope) {
foreach (['total_price', 'money', 'pay_money', 'order_amount', 'amount', 'product_value', 'origin_price'] as $field) {
$money = $this->cleanMoney($scope[$field] ?? '');
if ($money !== '') {
return $money;
}
}
}
return '';
}
private function cleanMoney($value): string
{
if (is_int($value) || is_float($value)) {
return (string)$value;
}
$value = trim((string)$value);
if ($value === '') {
return '';
}
$value = str_replace([',', '¥', '¥', '元', ' '], '', $value);
return preg_match('/^\d+(\.\d+)?$/', $value) ? $value : '';
}
}

View File

@@ -0,0 +1,74 @@
<?php
declare (strict_types=1);
namespace app\miniapi\controller;
use app\service\SwooleService;
class Upload extends Base
{
public function image()
{
$file = request()->file('file');
if (!$file) {
return $this->fail('请上传图片');
}
$path = $file->getPathname();
if (!is_file($path)) {
return $this->fail('上传文件读取失败');
}
$size = filesize($path);
if ($size === false || $size <= 0) {
return $this->fail('上传文件为空');
}
if ($size > 10 * 1024 * 1024) {
return $this->fail('图片不能超过10MB');
}
$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->fail('请上传图片文件');
}
$mime = function_exists('mime_content_type') ? (string)mime_content_type($path) : '';
if ($mime !== '' && strpos($mime, 'image/') !== 0) {
return $this->fail('请上传图片文件');
}
$content = file_get_contents($path);
if ($content === false || $content === '') {
return $this->fail('上传文件读取失败');
}
$fileName = date('YmdHis') . uniqid('', true) . '.' . $ext;
try {
$client = SwooleService::getInstance();
$sent = $client->sendTask([
'type' => 'upload_ali',
'data' => [
'Bucket' => BUCKET,
'Key' => $fileName,
'file_content' => base64_encode($content),
],
]);
$client->close();
if (!$sent) {
return $this->fail('上传失败,请重试');
}
$url = OSS_URL . $fileName;
return $this->ok([
'path' => $url,
'url' => $url,
], '上传成功');
} catch (\Throwable $e) {
return $this->fail('上传失败,请重试');
}
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare (strict_types=1);
namespace app\miniapi\service\Context;
class MiniAppContext
{
private $miniAppCode;
private $companyCode;
private $businessCode;
private $siteDomain;
private $miniApp;
private $site;
public function __construct(array $data)
{
$this->miniAppCode = (string)($data['mini_app_code'] ?? '');
$this->companyCode = (string)($data['company_code'] ?? '');
$this->businessCode = (string)($data['business_code'] ?? '');
$this->siteDomain = (string)($data['site_domain'] ?? '');
$this->miniApp = is_array($data['mini_app'] ?? null) ? $data['mini_app'] : [];
$this->site = is_array($data['site'] ?? null) ? $data['site'] : [];
}
public function miniAppCode(): string
{
return $this->miniAppCode;
}
public function companyCode(): string
{
return $this->companyCode;
}
public function businessCode(): string
{
return $this->businessCode;
}
public function siteDomain(): string
{
return $this->siteDomain;
}
public function miniApp(): array
{
return $this->miniApp;
}
public function site(): array
{
return $this->site;
}
public function toArray(): array
{
return [
'mini_app_code' => $this->miniAppCode,
'company_code' => $this->companyCode,
'business_code' => $this->businessCode,
'site_domain' => $this->siteDomain,
'mini_app' => $this->miniApp,
'site' => $this->site,
];
}
}

View File

@@ -0,0 +1,144 @@
<?php
declare (strict_types=1);
namespace app\miniapi\service\Legal;
use app\miniapi\service\Context\MiniAppContext;
use think\facade\Config;
class LegalDocumentService
{
public function get(MiniAppContext $context, string $type): array
{
$type = strtolower(trim($type));
$businessCode = $context->businessCode() ?: (string)Config::get('mini_legal.default_business_code', 'evus');
$business = (array)Config::get('mini_legal.businesses.' . $businessCode, []);
$companyCode = $this->resolveCompanyCode($business, $context);
$document = $this->resolveDocument($business, $context, $companyCode, $type);
if (empty($document)) {
return [];
}
$company = $this->resolveCompany($business, $context, $companyCode);
$title = $this->resolveText($document, 'title', 'title_key', $type === 'privacy' ? '隐私政策' : '条款与条件');
$body = $this->resolveText($document, 'content', 'content_key', '');
$content = $this->plainText($this->buildContent($document, $company, $body));
return [
'type' => $type,
'business_code' => $businessCode,
'mini_app_code' => $context->miniAppCode(),
'company_code' => $companyCode,
'company_name' => $company['company_name'],
'site_domain' => $company['site_domain'],
'title' => $title,
'content' => $content,
];
}
private function resolveDocument(array $business, MiniAppContext $context, string $companyCode, string $type): array
{
$document = (array)($business['documents'][$type] ?? []);
if (empty($document)) {
return [];
}
$company = (array)($business['companies'][$companyCode] ?? []);
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
$companyDocument = (array)($company['documents'][$type] ?? []);
$appDocument = (array)($app['documents'][$type] ?? []);
return array_replace_recursive($document, $companyDocument, $appDocument);
}
private function resolveCompany(array $business, MiniAppContext $context, string $companyCode): array
{
$site = $context->site();
$company = (array)($business['companies'][$companyCode] ?? []);
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
$company = array_replace_recursive($company, (array)($app['company'] ?? []));
return [
'company_name' => trim((string)($company['company_name'] ?? '')) ?: trim((string)($site['company_name'] ?? '')) ?: 'EVUS登记服务',
'site_domain' => trim((string)($company['site_domain'] ?? '')) ?: $context->siteDomain(),
'phone' => trim((string)($company['phone'] ?? '')) ?: trim((string)($site['phone'] ?? '')),
];
}
private function resolveCompanyCode(array $business, MiniAppContext $context): string
{
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
return trim((string)($app['company_code'] ?? '')) ?: $context->companyCode();
}
private function resolveText(array $document, string $literalKey, string $langKey, string $fallback): string
{
$literal = trim((string)($document[$literalKey] ?? ''));
if ($literal !== '') {
return $literal;
}
$key = trim((string)($document[$langKey] ?? ''));
if ($key === '') {
return $fallback;
}
$value = (string)lang($key);
return trim($value) !== '' ? $value : $fallback;
}
private function buildContent(array $document, array $company, string $body): string
{
$companyName = $company['company_name'] ?: 'EVUS登记服务';
$siteUrl = $this->siteUrl((string)($company['site_domain'] ?? ''));
$phone = $company['phone'] ?: '请通过小程序客服入口联系我们';
$intro = $this->replaceTokens((string)($document['intro_template'] ?? ''), $company, $siteUrl, $phone);
$subjectLabel = trim((string)($document['subject_label'] ?? '服务主体'));
$prefix = [
$intro,
$subjectLabel . '' . $companyName,
$siteUrl ? '适用网站:' . $siteUrl : '',
'联系方式:' . $phone,
];
$prefix = array_values(array_filter($prefix, static function ($line) {
return trim((string)$line) !== '';
}));
return implode("\n", $prefix) . "\n\n" . $body;
}
private function replaceTokens(string $text, array $company, string $siteUrl, string $phone): string
{
return strtr($text, [
'{company_name}' => (string)($company['company_name'] ?? ''),
'{site_url}' => $siteUrl,
'{phone}' => $phone,
]);
}
private function siteUrl(string $siteDomain): string
{
$siteDomain = trim($siteDomain);
if ($siteDomain === '') {
return '';
}
return preg_match('/^https?:\/\//i', $siteDomain) ? rtrim($siteDomain, '/') : 'https://' . rtrim($siteDomain, '/');
}
private function plainText(string $html): string
{
$html = preg_replace('/<\s*(h[1-6]|div|section|li)\b[^>]*>/i', "\n", $html);
$html = preg_replace('/<\s*\/\s*(h[1-6]|div|section|li)\s*>/i', "\n", (string)$html);
$text = preg_replace('/<\s*br\s*\/?\s*>/i', "\n", (string)$html);
$text = preg_replace('/<\s*\/p\s*>/i', "\n", (string)$text);
$text = strip_tags((string)$text);
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$text = preg_replace("/[ \t\r]+/", ' ', (string)$text);
$text = preg_replace("/\n{3,}/", "\n\n", (string)$text);
return trim((string)$text);
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare (strict_types=1);
namespace app\miniapi\service\Payment;
use api\Httpcurl;
use app\miniapi\service\Context\MiniAppContext;
use think\facade\Log;
class MiniPayService
{
public function request(MiniAppContext $context, string $orderSn, $money, string $openid, string $scene = 'miniapi mini pay'): array
{
$payConfig = (new PaymentChannelService())->miniPay($context);
if ($payConfig['url'] === '') {
return [
'code' => 0,
'msg' => '支付配置未完成',
'data' => [],
];
}
$response = Httpcurl::request($payConfig['url'], 'post', [
'order_sn' => $orderSn,
'body' => $payConfig['body'],
'money' => $money,
'openid' => $openid,
'attach' => $payConfig['attach'],
]);
$result = $this->decodeResponse($response, $scene);
if (empty($result) || intval($result['code'] ?? 0) !== 200) {
return [
'code' => 0,
'msg' => $result['msg'] ?? '支付参数获取失败',
'data' => [],
];
}
if (isset($result['data']['package'])) {
$result['data']['package'] = urlencode($result['data']['package']);
}
return [
'code' => 1,
'msg' => 'ok',
'data' => $result['data'],
];
}
private function decodeResponse($response, string $scene): array
{
if (!is_array($response) || !isset($response[0]) || !empty($response[3])) {
Log::error($scene . ' request failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
return [];
}
$result = json_decode($response[0], true);
if (!is_array($result)) {
Log::error($scene . ' response decode failed: ' . $response[0]);
return [];
}
return $result;
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare (strict_types=1);
namespace app\miniapi\service\Payment;
use app\miniapi\service\Context\MiniAppContext;
use think\facade\Config;
class PaymentChannelService
{
public function miniPay(MiniAppContext $context): array
{
$businessCode = $context->businessCode() ?: (string)Config::get('mini_payment.default_business_code', 'evus');
$business = (array)Config::get('mini_payment.businesses.' . $businessCode, []);
$companyCode = $this->resolveCompanyCode($business, $context);
$config = (array)($business['mini_pay'] ?? []);
$company = (array)($business['companies'][$companyCode] ?? []);
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
$config = array_replace_recursive($config, (array)($company['mini_pay'] ?? []), (array)($app['mini_pay'] ?? []));
return [
'url' => trim((string)($config['url'] ?? '')),
'body' => trim((string)($config['body'] ?? 'EVUS登记服务费')) ?: 'EVUS登记服务费',
'attach' => trim((string)($config['attach'] ?? 'msg')) ?: 'msg',
];
}
private function resolveCompanyCode(array $business, MiniAppContext $context): string
{
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
return trim((string)($app['company_code'] ?? '')) ?: $context->companyCode();
}
}

View File

@@ -0,0 +1,94 @@
<?php
declare (strict_types=1);
namespace app\miniapi\service\Product;
use app\miniapi\service\Context\MiniAppContext;
use think\facade\Config;
class ProductService
{
public function list(MiniAppContext $context): array
{
$businessCode = $context->businessCode() ?: (string)Config::get('mini_product.default_business_code', 'evus');
$business = (array)Config::get('mini_product.businesses.' . $businessCode, []);
$products = $this->resolveConfiguredProducts($business, $context);
if (empty($products)) {
$legacyKey = (string)($business['legacy_config_key'] ?? '');
$products = $legacyKey !== '' ? (array)Config::get('app.' . $legacyKey, []) : [];
}
return $this->normalizeProducts($products);
}
public function values(MiniAppContext $context): array
{
$products = $this->list($context);
return array_values(array_filter(array_map(static function ($item) {
return isset($item['value']) ? (string)$item['value'] : '';
}, $products), static function ($value) {
return $value !== '';
}));
}
public function isValidValue(MiniAppContext $context, $value): bool
{
$values = $this->values($context);
return empty($values) || in_array((string)$value, $values, true);
}
public function findByValue(MiniAppContext $context, $value): array
{
$value = (string)$value;
foreach ($this->list($context) as $product) {
if ((string)($product['value'] ?? '') === $value) {
return $product;
}
}
return [];
}
private function resolveConfiguredProducts(array $business, MiniAppContext $context): array
{
$companyCode = $this->resolveCompanyCode($business, $context);
$company = (array)($business['companies'][$companyCode] ?? []);
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
$products = (array)($company['products'] ?? []);
if (!empty($app['products']) && is_array($app['products'])) {
$products = (array)$app['products'];
}
return $products;
}
private function resolveCompanyCode(array $business, MiniAppContext $context): string
{
$app = (array)($business['apps'][$context->miniAppCode()] ?? []);
return trim((string)($app['company_code'] ?? '')) ?: $context->companyCode();
}
private function normalizeProducts(array $products): array
{
return array_values(array_filter(array_map(function ($item) {
if (!is_array($item)) {
return null;
}
$value = isset($item['value']) ? (string)$item['value'] : '';
if ($value === '') {
return null;
}
return [
'value' => $value,
'desc' => (string)($item['desc'] ?? ''),
'is_checked' => (string)($item['is_checked'] ?? '0'),
'name' => (string)($item['name'] ?? ''),
];
}, $products)));
}
}

9
app/provider.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

9
app/service.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

View File

@@ -0,0 +1,126 @@
<?php
namespace app\service;
use Aws\S3\S3Client;
use think\facade\Log;
use think\File;
class AwsUploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param array $extraFields 额外传递的字段
* @return array 包含上传结果的数组
*/
public function uploadAndForward( File $file, string $fileField,array $extraFields = []): array
{
$mimeType = $file->getMime();
$s3config = config('filesystem.disks.s3');
$s3 = new S3Client($s3config);
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 根据文件类型设置 Content-Type
if (in_array($mimeType, config('app.imageMimeTypes'))) {
$contentType = $mimeType; // 使用图片的实际 MIME 类型
} else {
$contentType = 'application/octet-stream'; // 非图片文件使用默认类型
}
$checkBucket = $this->checkBucket($s3, $extraFields['Bucket']);
if (!$checkBucket) {
return $this->createErrorResult('Bucket error');
}
$s3->putObject([
'Bucket' => $extraFields['Bucket'],
'Key' => $extraFields['Key'],
'Body' => fopen($file->getRealPath(), 'r'),
'ACL' => 'public-read',
'ContentType' => $contentType, // 设置检测到的 Content-Type
]);
return [
'code' => 1,
'data' =>[
'savename'=>$s3config['endpoint'].'/'.$extraFields['Bucket'].'/'.$extraFields['Key']
] ,
];
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
public function checkBucket($s3, $bucket)
{
try {
$buckets = $s3->listBuckets()->get('Buckets');
$buckets_arr = array_column($buckets, 'Name');
if (!in_array($bucket, $buckets_arr)) {
$s3->createBucket([
'Bucket' => $bucket,
]);
$policy = [
'Version' => '2012-10-17',
'Statement' => [
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:GetBucketLocation',
's3:ListBucket',
's3:ListBucketMultipartUploads',
],
'Resource' => "arn:aws:s3:::$bucket",
],
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:DeleteObject',
's3:GetObject',
's3:ListMultipartUploadParts',
's3:PutObject',
's3:AbortMultipartUpload',
],
'Resource' => "arn:aws:s3:::$bucket/*",
],
],
];
$s3->putBucketPolicy([
'Bucket' => $bucket,
'Policy' => json_encode($policy),
]);
}
return true;
} catch (\Exception $e) {
Log::info('checkBucket error:' . $e->getMessage());
return false;
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace app\service;
use Curl\Curl;
class BaiduOcrService
{
private $url = 'https://uniuser.evus.cn/home/upload/';
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function passport($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$curl->setOpt(CURLOPT_SSL_VERIFYHOST, 0);
$curl->post($this->url.'passport', [
'image' => $fileData,
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (!$curl->response->code){
return [
'code' => 0,
'msg' => '解析失败',
];
}
return [
'code' => 1,
'data' => $curl->response->data,
];
}
}
/**
*
*身份证识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function identification($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
$curl = new Curl();
$curl->post($this->url.'identification', [
'image' => $fileData,
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (!$curl->response->code){
return [
'code' => 0,
'msg' => '解析失败',
];
}
return [
'code' => 1,
'data' => $curl->response->data,
];
}
}
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array 包含压缩后图片信息的数组
*/
function visa($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
$curl = new Curl();
$curl->post($this->url.'visa', [
'image' => $fileData,
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (!$curl->response->code){
return [
'code' => 0,
'msg' => '解析失败',
];
}
return [
'code' => 1,
'data' => $curl->response->data,
];
}
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace app\service;
/**
* 压缩二进制流图片
*/
class CompressImgService
{
/**
* 压缩通过表单上传的图片
*
* @param string $fileData 图片的二进制数据
* @param int $quality 压缩质量0-100仅适用于JPEG和WebP
* @param string|null $targetFormat 目标格式('jpeg', 'png', 'webp',默认使用原格式)
* @return array 包含压缩后图片信息的数组
*/
function compress($fileData, $quality = 80, $targetFormat = null)
{
// 读取上传的二进制数据
$imageData = file_get_contents($fileData);
if ($imageData === false) {
return [
'code' => 0,
'message' => '无法读取上传的文件',
];
}
// 获取图片信息
$imageInfo = getimagesizefromstring($imageData);
if (!$imageInfo) {
return [
'code' => 0,
'message' => '无法解析图片信息',
];
}
// 确定原始和目标格式
$originalMimeType = $imageInfo['mime'];
$originalExtension = image_type_to_extension($imageInfo[2], false);
if ($targetFormat === null) {
$targetFormat = $originalExtension;
}
// 创建图片资源
switch (strtolower($originalExtension)) {
case 'jpeg':
case 'jpg':
$image = imagecreatefromstring($imageData);
break;
case 'png':
$image = imagecreatefromstring($imageData);
break;
case 'webp':
$image = imagecreatefromstring($imageData);
break;
default:
return [
'code' => 0,
'message' => '不支持的图片格式',
];
}
if (!$image) {
return [
'code' => 0,
'message' => '无法创建图片资源',
];
}
// 压缩并转换图片
ob_start();
switch (strtolower($targetFormat)) {
case 'jpeg':
case 'jpg':
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
break;
case 'png':
$pngQuality = 9 - floor($quality / 11);
imagepng($image, null, $pngQuality);
$compressedMimeType = 'image/png';
break;
case 'webp':
if (function_exists('imagewebp')) {
imagewebp($image, null, $quality);
$compressedMimeType = 'image/webp';
} else {
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
}
break;
default:
imagedestroy($image);
ob_end_clean();
return [
'code' => 0,
'message' => "不支持的目标格式: {$targetFormat}",
];
}
$compressedData = ob_get_clean();
imagedestroy($image);
// 返回结果
return [
'code' => 1,
'compressed_data' => $compressedData,
'base64' => 'data:' . $compressedMimeType . ';base64,' . base64_encode($compressedData),
];
}
public function compressImage($filePath, $fileSize)
{
// 获取图片信息
$imageInfo = getimagesize($filePath);
if (!$imageInfo) {
return false;
}
$mime = $imageInfo['mime'];
// 根据MIME类型创建图像资源
switch ($mime) {
case 'image/jpeg':
$image = imagecreatefromjpeg($filePath);
break;
case 'image/png':
$image = imagecreatefrompng($filePath);
break;
case 'image/gif':
$image = imagecreatefromgif($filePath);
break;
default:
return false;
}
if (!$image) {
return false;
}
// 计算压缩质量(根据文件大小动态调整)
$originalQuality = 85; // 初始质量
$targetSize = 2.5 * 1024 * 1024; // 目标大小2.5MB
// 如果文件很大,降低初始质量
if ($fileSize > 5 * 1024 * 1024) {
$quality = 75;
} elseif ($fileSize > 10 * 1024 * 1024) {
$quality = 65;
} else {
$quality = $originalQuality;
}
// 创建临时文件
$tempFile = tempnam(sys_get_temp_dir(), 'compressed_');
// 保存压缩后的图片
switch ($mime) {
case 'image/jpeg':
imagejpeg($image, $tempFile, $quality);
break;
case 'image/png':
// PNG使用压缩级别0-9需要转换
$pngQuality = 9 - round(($quality / 100) * 9);
imagepng($image, $tempFile, $pngQuality);
break;
case 'image/gif':
imagegif($image, $tempFile);
break;
}
// 释放内存
imagedestroy($image);
// 检查压缩后文件大小如果仍然大于3MB继续压缩
$compressedSize = filesize($tempFile);
if ($compressedSize > 3 * 1024 * 1024) {
// 递归压缩直到满足要求
return $this->compressImage($tempFile, $compressedSize);
}
return $tempFile;
}
}

View File

@@ -0,0 +1,127 @@
<?php
declare (strict_types=1);
namespace app\service;
use api\Httpcurl;
use think\facade\Cache;
use think\facade\Log;
class MiniProgramWechatService
{
private string $miniAppCode;
private array $miniApp;
public function __construct(string $miniAppCode, array $miniApp)
{
$this->miniAppCode = $miniAppCode;
$this->miniApp = $miniApp;
}
public function openidByLoginCode(string $loginCode, bool $required = false): string
{
$config = $this->wechatConfig();
if ($loginCode === '' || $config['appid'] === '' || $config['secret'] === '') {
return '';
}
$response = Httpcurl::request('https://api.weixin.qq.com/sns/jscode2session', 'get', [
'appid' => $config['appid'],
'secret' => $config['secret'],
'js_code' => $loginCode,
'grant_type' => 'authorization_code',
], [], 8);
$result = $this->decodeWechatResponse($response, 'miniapi wx code2session');
if (!empty($result['openid'])) {
return (string)$result['openid'];
}
if ($required) {
Log::warning('miniapi wx openid missing: ' . json_encode($result, JSON_UNESCAPED_UNICODE));
}
return '';
}
public function mobileByPhoneCode(string $phoneCode): string
{
$accessToken = $this->wechatAccessToken();
if ($phoneCode === '' || $accessToken === '') {
return '';
}
$response = Httpcurl::request(
'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . urlencode($accessToken),
'post',
json_encode(['code' => $phoneCode], JSON_UNESCAPED_UNICODE),
['Content-Type: application/json'],
8
);
$result = $this->decodeWechatResponse($response, 'miniapi wx get phone number');
$phoneInfo = $result['phone_info'] ?? [];
return (string)($phoneInfo['purePhoneNumber'] ?? $phoneInfo['phoneNumber'] ?? '');
}
private function wechatAccessToken(): string
{
$config = $this->wechatConfig();
if ($config['appid'] === '' || $config['secret'] === '') {
Log::warning('miniapi wx config missing: ' . $this->miniAppCode);
return '';
}
$cacheKey = 'miniapp_access_token_' . $this->miniAppCode;
$cached = Cache::get($cacheKey);
if (!empty($cached)) {
return (string)$cached;
}
$response = Httpcurl::request('https://api.weixin.qq.com/cgi-bin/token', 'get', [
'grant_type' => 'client_credential',
'appid' => $config['appid'],
'secret' => $config['secret'],
], [], 8);
$result = $this->decodeWechatResponse($response, 'miniapi wx access token');
if (empty($result['access_token'])) {
return '';
}
$ttl = max(60, intval($result['expires_in'] ?? 7200) - 300);
Cache::set($cacheKey, $result['access_token'], $ttl);
return (string)$result['access_token'];
}
private function wechatConfig(): array
{
return [
'appid' => trim((string)($this->miniApp['wechat_appid'] ?? '')),
'secret' => trim((string)($this->miniApp['wechat_secret'] ?? '')),
];
}
private function decodeWechatResponse($response, string $scene): array
{
$result = $this->decodeResponse($response, $scene);
if (isset($result['errcode']) && intval($result['errcode']) !== 0) {
Log::warning($scene . ' failed: ' . json_encode($result, JSON_UNESCAPED_UNICODE));
return [];
}
return $result;
}
private function decodeResponse($response, string $scene): array
{
if (!is_array($response) || !isset($response[0]) || !empty($response[3])) {
Log::error($scene . ' request failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
return [];
}
$result = json_decode($response[0], true);
if (!is_array($result)) {
Log::error($scene . ' response decode failed: ' . $response[0]);
return [];
}
return $result;
}
}

301
app/service/OssService.php Normal file
View File

@@ -0,0 +1,301 @@
<?php
namespace app\service;
use AlibabaCloud\Oss\V2 as Oss;
use Exception;
final class OssService
{
private Oss\Client $client;
private string $bucket;
private string $region;
private ?string $endpoint;
/**
* @param array{
* region: string,
* bucket: string,
* endpoint?: string,
* access_key_id?: string,
* access_key_secret?: string,
* session_token?: string
* } $config
*/
public function __construct(array $config)
{
$this->region = $config['region'] ?? '';
$this->bucket = $config['bucket'] ?? '';
$this->endpoint = $config['endpoint'] ?? null;
if ($this->region === '' || $this->bucket === '') {
throw new Exception('OSS config missing: region/bucket');
}
if (!empty($config['access_key_id']) && !empty($config['access_key_secret'])) {
$credentialsProvider = new FixedCredentialsProvider(
(string)$config['access_key_id'],
(string)$config['access_key_secret'],
$config['session_token'] ?? null
);
} else {
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();
}
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider);
$cfg->setRegion($this->region);
if ($this->endpoint) {
$cfg->setEndpoint($this->endpoint);
if ($this->shouldUseCname($this->endpoint)) {
$cfg->setUseCname(true);
}
}
$this->client = new Oss\Client($cfg);
$this->assertCredentialsVisible($config);
}
public function upload(string $source, string $key, ?string $bucket = null, array $options = []): array
{
if ($this->isBase64DataUri($source)) {
return $this->uploadFromBase64($source, $key, $bucket, $options);
}
if ($this->isUrl($source)) {
return $this->uploadFromUrl($source, $key, $bucket, $options);
}
return $this->uploadFromFile($source, $key, $bucket, $options);
}
public function uploadFromBase64(string $base64, string $key, ?string $bucket = null, array $options = []): array
{
$contentType = null;
$payload = $base64;
if (preg_match('/^data:([a-zA-Z0-9.+\\/-]+);base64,(.*)$/s', $base64, $m)) {
$contentType = strtolower((string)$m[1]);
$payload = (string)$m[2];
}
$payload = preg_replace('/\\s+/', '', $payload ?? '');
if ($payload === '') {
throw new Exception('Base64 payload is empty');
}
$binary = base64_decode($payload, true);
if ($binary === false) {
throw new Exception('Invalid base64 payload');
}
if ($contentType && empty($options['contentType'])) {
$options['contentType'] = $contentType;
}
return $this->uploadFromBinary($binary, $key, $bucket, $options);
}
public function uploadFromBinary(string $binary, string $key, ?string $bucket = null, array $options = []): array
{
if ($binary === '') {
throw new Exception('Binary payload is empty');
}
$stream = fopen('php://temp', 'wb+');
if ($stream === false) {
throw new Exception('Unable to create temporary stream for binary upload');
}
fwrite($stream, $binary);
rewind($stream);
return $this->putObjectFromResource($stream, $key, $bucket, $options);
}
public function uploadFromFile(string $filePath, string $key, ?string $bucket = null, array $options = []): array
{
if (!is_file($filePath)) {
throw new Exception("Local file not found: {$filePath}");
}
$fp = fopen($filePath, 'rb');
if ($fp === false) {
throw new Exception("Unable to open local file: {$filePath}");
}
return $this->putObjectFromResource($fp, $key, $bucket, $options);
}
public function uploadFromUrl(string $url, string $key, ?string $bucket = null, array $options = []): array
{
$fp = @fopen($url, 'rb');
if ($fp !== false) {
return $this->putObjectFromResource($fp, $key, $bucket, $options);
}
$tmp = tmpfile();
if ($tmp === false) {
throw new Exception('Unable to create tmpfile() for remote download');
}
$this->downloadToStreamByCurl($url, $tmp);
rewind($tmp);
return $this->putObjectFromResource($tmp, $key, $bucket, $options);
}
public function buildObjectUrl(string $key, ?string $bucket = null, bool $useHttps = true): string
{
$bucket = $bucket ?: $this->bucket;
$key = ltrim($key, '/');
$endpoint = $this->endpoint ?: sprintf('oss-%s.aliyuncs.com', $this->region);
$host = $this->extractEndpointHost($endpoint);
$scheme = $useHttps ? 'https' : 'http';
$encodedKey = implode('/', array_map('rawurlencode', explode('/', $key)));
if ($this->shouldUseCname($endpoint)) {
return sprintf('%s://%s/%s', $scheme, $host, $encodedKey);
}
return sprintf('%s://%s.%s/%s', $scheme, $bucket, $host, $encodedKey);
}
private function putObjectFromResource($resource, string $key, ?string $bucket = null, array $options = []): array
{
$bucket = $bucket ?: $this->bucket;
$key = ltrim($key, '/');
$metadata = $options['metadata'] ?? [];
$contentType = $options['contentType'] ?? null;
$acl = $options['acl'] ?? null;
$body = Oss\Utils::streamFor($resource);
$request = new Oss\Models\PutObjectRequest(
bucket: $bucket,
key: $key,
metadata: $metadata ?: null,
contentType: $contentType,
objectAcl: $acl
);
$request->body = $body;
$result = $this->client->putObject($request);
return [
'statusCode' => $result->statusCode,
'requestId' => $result->requestId,
'etag' => $result->etag,
'bucket' => $bucket,
'key' => $key,
];
}
private function downloadToStreamByCurl(string $url, $destStream): void
{
if (!function_exists('curl_init')) {
throw new Exception('cURL extension is required when allow_url_fopen is disabled.');
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_FILE => $destStream,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 60,
CURLOPT_FAILONERROR => true,
CURLOPT_USERAGENT => 'OssService/1.0',
]);
$ok = curl_exec($ch);
if ($ok !== true) {
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
throw new Exception("Failed to download remote file. HTTP={$code}, error={$err}");
}
curl_close($ch);
}
private function isUrl(string $value): bool
{
return (bool)filter_var($value, FILTER_VALIDATE_URL);
}
private function isBase64DataUri(string $value): bool
{
return (bool)preg_match('/^data:[a-zA-Z0-9.+\\/-]+;base64,/i', $value);
}
private function assertCredentialsVisible(array $config): void
{
if (!empty($config['access_key_id']) && !empty($config['access_key_secret'])) {
return;
}
$ak = getenv('OSS_ACCESS_KEY_ID');
$sk = getenv('OSS_ACCESS_KEY_SECRET');
if (empty($ak) || empty($sk)) {
throw new Exception(
"OSS credentials missing in PHP process env. " .
"getenv('OSS_ACCESS_KEY_ID')/getenv('OSS_ACCESS_KEY_SECRET') is empty. " .
"Fix: make sure the PHP runtime process can see env vars, or pass access_key_id/access_key_secret into OssService config."
);
}
}
private function shouldUseCname(string $endpoint): bool
{
$host = strtolower($this->extractEndpointHost($endpoint));
if ($host === '' || str_contains($host, 'aliyuncs.com') || str_contains($host, '.oss-')) {
return false;
}
return true;
}
private function extractEndpointHost(string $endpoint): string
{
$normalized = preg_match('#^https?://#i', $endpoint) ? $endpoint : 'https://' . ltrim($endpoint, '/');
return (string)(parse_url($normalized, PHP_URL_HOST) ?: '');
}
public function presignGetObjectUrl(string $key, ?string $bucket = null, int $expireSeconds = 900): array
{
$bucket = $bucket ?: $this->bucket;
$key = ltrim($key, '/');
$request = new Oss\Models\GetObjectRequest(bucket: $bucket, key: $key);
$result = $this->client->presign($request, [
'expires' => new \DateInterval("PT{$expireSeconds}S"),
]);
return [
'url' => $result->url,
'expire' => $expireSeconds,
'bucket' => $bucket,
'key' => $key,
];
}
}
final class FixedCredentialsProvider implements Oss\Credentials\CredentialsProvider
{
private string $ak;
private string $sk;
private ?string $token;
public function __construct(string $ak, string $sk, ?string $token = null)
{
$this->ak = $ak;
$this->sk = $sk;
$this->token = $token;
}
public function getCredentials(): Oss\Credentials\Credentials
{
return new Oss\Credentials\Credentials($this->ak, $this->sk, $this->token);
}
}

View File

@@ -0,0 +1,203 @@
<?php
namespace app\service;
use Throwable;
class OssUploadService
{
public function uploadBase64(string $fileName, string $base64File, array $extraFields = []): array
{
$fileContent = base64_decode($base64File, true);
if ($fileContent === false || $fileContent === '') {
return $this->createErrorResult('文件解析错误');
}
return $this->uploadBinaryContent($fileName, $fileContent, $extraFields);
}
public function uploadBinary(string $fileName, string $fileContent, array $extraFields = []): array
{
if ($fileContent === '') {
return $this->createErrorResult('Binary payload is empty');
}
return $this->uploadBinaryContent($fileName, $fileContent, $extraFields);
}
private function uploadBinaryContent(string $fileName, string $fileContent, array $extraFields = []): array
{
$uploadConfig = config('filesystem.upload_conf') ?: [];
$maxSize = (int)($uploadConfig['fileSize'] ?? 10 * 1024 * 1024);
if (strlen($fileContent) > $maxSize) {
return $this->createErrorResult('File size exceeds limit');
}
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$allowedExts = $uploadConfig['fileExt'] ?? [];
if ($ext === '' || (!empty($allowedExts) && !in_array($ext, $allowedExts, true))) {
return $this->createErrorResult('File extension not allowed');
}
$bucket = (string)config('oss.bucket');
$folder = (string)($extraFields['Folder'] ?? ($extraFields['Bucket'] ?? ''));
$key = $extraFields['Key'] ?? basename($fileName);
$objectKey = $this->buildObjectKey($folder, (string)$key);
if ($bucket === '' || $objectKey === '') {
return $this->createErrorResult('Bucket or key missing');
}
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = (string)$finfo->buffer($fileContent);
try {
$service = new OssService(config('oss'));
$result = $service->uploadFromBinary($fileContent, $objectKey, $bucket, [
'contentType' => $this->resolveContentType($mimeType, $ext),
]);
$result['url'] = $service->buildObjectUrl($objectKey, $bucket);
$result['folder'] = $folder;
return [
'code' => 1,
'msg' => 'upload ok',
'data' => $result,
];
} catch (Throwable $e) {
$fallback = $this->saveToLocalFallback($fileContent, $bucket, $objectKey);
return $this->createErrorResult(
'OSS upload failed: ' . $e->getMessage(),
[
'bucket' => $bucket,
'key' => $objectKey,
'folder' => $folder,
'fallback_saved' => $fallback['saved'],
'fallback_path' => $fallback['path'],
'fallback_error' => $fallback['error'],
]
);
}
}
private function buildObjectKey(string $folder, string $key): string
{
$folder = trim(str_replace('\\', '/', $folder), '/');
$key = trim(str_replace('\\', '/', $key), '/');
if ($folder === '') {
return $key;
}
if ($key === '') {
return $folder;
}
if (str_starts_with($key . '/', $folder . '/')) {
return $key;
}
return $folder . '/' . $key;
}
private function saveToLocalFallback(string $fileContent, string $bucket, string $key): array
{
$baseDir = $this->resolveFallbackBaseDir();
$safeBucket = $this->sanitizePathSegment($bucket);
$safeKey = $this->sanitizeObjectKey($key);
$fullPath = $baseDir . DIRECTORY_SEPARATOR . $safeBucket . DIRECTORY_SEPARATOR . $safeKey;
$directory = dirname($fullPath);
try {
if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
throw new \RuntimeException('Unable to create fallback directory');
}
if (file_put_contents($fullPath, $fileContent) === false) {
throw new \RuntimeException('Unable to write fallback file');
}
return [
'saved' => true,
'path' => $fullPath,
'error' => null,
];
} catch (Throwable $e) {
return [
'saved' => false,
'path' => $fullPath,
'error' => $e->getMessage(),
];
}
}
private function resolveFallbackBaseDir(): string
{
$uploadConfig = config('filesystem.upload_conf') ?: [];
$configured = (string)($uploadConfig['fallbackDir'] ?? '');
if ($configured !== '') {
return rtrim($configured, "\\/");
}
$localRoot = (string)(config('filesystem.disks.local.root') ?? '');
if ($localRoot !== '') {
return rtrim($localRoot, "\\/") . DIRECTORY_SEPARATOR . 'oss-fallback';
}
return app()->getRuntimePath() . 'storage' . DIRECTORY_SEPARATOR . 'oss-fallback';
}
private function sanitizeObjectKey(string $key): string
{
$normalized = str_replace('\\', '/', $key);
$segments = array_filter(explode('/', $normalized), static function (string $segment): bool {
return $segment !== '' && $segment !== '.' && $segment !== '..';
});
if ($segments === []) {
return 'unnamed-file';
}
$safeSegments = array_map([$this, 'sanitizePathSegment'], $segments);
return implode(DIRECTORY_SEPARATOR, $safeSegments);
}
private function sanitizePathSegment(string $segment): string
{
$cleaned = preg_replace('/[^A-Za-z0-9._-]/', '_', $segment) ?? '';
return $cleaned !== '' ? $cleaned : 'unknown';
}
private function resolveContentType(string $mimeType, string $ext): string
{
if ($mimeType !== '' && $mimeType !== 'application/octet-stream') {
return $mimeType;
}
return match ($ext) {
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
'bmp' => 'image/bmp',
'svg' => 'image/svg+xml',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
private function createErrorResult(string $message, array $data = []): array
{
$result = [
'code' => 0,
'msg' => $message,
];
if ($data !== []) {
$result['data'] = $data;
}
return $result;
}
}

View File

@@ -0,0 +1,233 @@
<?php
namespace app\service;
use Throwable;
class PassportOcrService
{
private const PASSPORT_ENDPOINT = '/ocr/passport/base64';
private const IDCARD_ENDPOINT = '/ocr/idcard/base64';
private const VISA_ENDPOINT = '/ocr/visa-number/base64';
public function passport($fileData): array
{
return $this->request(self::PASSPORT_ENDPOINT, $fileData);
}
public function identification($fileData): array
{
return $this->request(self::IDCARD_ENDPOINT, $fileData);
}
public function visa($fileData): array
{
return $this->request(self::VISA_ENDPOINT, $fileData);
}
private function request(string $endpoint, $fileData): array
{
if (!is_string($fileData) || $fileData === '') {
return $this->error('Unable to read uploaded file');
}
$config = $this->loadConfig();
if ($config['base_url'] === '' || $config['app_id'] === '' || $config['secret'] === '') {
return $this->error('OCR config missing: app.OCR_BASE_URL, app.OCR_APP_ID and app.OCR_SECRET are required');
}
try {
$payload = $this->buildPayload($fileData, $config);
} catch (Throwable $e) {
return $this->error('Unable to build OCR payload: ' . $e->getMessage());
}
$timestamp = (string) time();
$signature = hash_hmac('sha256', $config['app_id'] . "\n" . $timestamp, $config['secret']);
$headers = [
'Content-Type: application/json',
'Accept: application/json',
'X-App-Id: ' . $config['app_id'],
'X-Timestamp: ' . $timestamp,
'X-Signature: ' . $signature,
];
if ($config['origin'] !== '') {
$headers[] = 'Origin: ' . $config['origin'];
}
$ch = curl_init($config['base_url'] . $endpoint);
if ($ch === false) {
return $this->error('Unable to initialize cURL');
}
try {
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_TIMEOUT => $config['timeout'],
CURLOPT_CONNECTTIMEOUT => $config['connect_timeout'],
]);
if (!$config['verify_ssl']) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
return $this->error('OCR request failed: ' . curl_error($ch));
}
$decoded = json_decode((string) $response, true);
if (!is_array($decoded)) {
return $this->error('OCR response is not valid JSON', [
'http_code' => $httpCode,
'response' => substr((string) $response, 0, 300),
]);
}
return $decoded;
} finally {
curl_close($ch);
}
}
private function buildPayload(string $fileData, array $config): array
{
$mimeType = $this->detectMimeType($fileData);
$extension = $this->extensionForMimeType($mimeType);
return [
'filename' => 'upload.' . $extension,
'content_type' => $mimeType,
'image_base64' => 'data:' . $mimeType . ';base64,' . base64_encode($fileData),
'auto_rotate' => $config['auto_rotate'],
'return_debug' => $config['return_debug'],
];
}
private function loadConfig(): array
{
$config = config('ocr') ?: [];
return [
'base_url' => rtrim((string) (config('app.OCR_BASE_URL') ?? ''), '/'),
'app_id' => (string) (config('app.OCR_APP_ID') ?? ''),
'secret' => (string) (config('app.OCR_SECRET') ?? ''),
'origin' => $this->resolveOrigin((string) (config('app.OCR_ORIGIN') ?? ($config['origin'] ?? ''))),
'timeout' => max(1, (int) ($config['timeout'] ?? 60)),
'connect_timeout' => max(1, (int) ($config['connect_timeout'] ?? 10)),
'auto_rotate' => $this->boolValue($config['auto_rotate'] ?? true),
'return_debug' => $this->boolValue($config['return_debug'] ?? false),
'verify_ssl' => $this->boolValue($config['verify_ssl'] ?? true),
];
}
private function resolveOrigin(string $configuredOrigin): string
{
$configuredOrigin = trim($configuredOrigin);
if ($configuredOrigin !== '') {
return rtrim($configuredOrigin, '/');
}
$requestOrigin = trim((string) ($_SERVER['HTTP_ORIGIN'] ?? ''));
if ($this->isValidOrigin($requestOrigin)) {
return rtrim($requestOrigin, '/');
}
$host = trim((string) ($_SERVER['HTTP_HOST'] ?? ''));
if ($host === '' || !preg_match('/^[A-Za-z0-9.-]+(?::\d+)?$/', $host)) {
return '';
}
return $this->currentScheme() . '://' . $host;
}
private function isValidOrigin(string $origin): bool
{
if ($origin === '') {
return false;
}
$parts = parse_url($origin);
if (!is_array($parts)) {
return false;
}
$scheme = strtolower((string) ($parts['scheme'] ?? ''));
return in_array($scheme, ['http', 'https'], true)
&& !empty($parts['host'])
&& empty($parts['path'])
&& empty($parts['query'])
&& empty($parts['fragment']);
}
private function currentScheme(): string
{
$forwardedProto = trim((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''));
if ($forwardedProto !== '') {
$forwardedProto = strtolower(trim(explode(',', $forwardedProto)[0]));
if (in_array($forwardedProto, ['http', 'https'], true)) {
return $forwardedProto;
}
}
$requestScheme = strtolower((string) ($_SERVER['REQUEST_SCHEME'] ?? ''));
if (in_array($requestScheme, ['http', 'https'], true)) {
return $requestScheme;
}
$https = strtolower((string) ($_SERVER['HTTPS'] ?? ''));
return $https !== '' && $https !== 'off' ? 'https' : 'http';
}
private function detectMimeType(string $fileData): string
{
if (!class_exists(\finfo::class)) {
return 'image/jpeg';
}
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($fileData);
if (is_string($mimeType) && $mimeType !== '' && $mimeType !== 'application/octet-stream') {
return $mimeType;
}
return 'image/jpeg';
}
private function extensionForMimeType(string $mimeType): string
{
return match ($mimeType) {
'image/png' => 'png',
'image/gif' => 'gif',
'image/webp' => 'webp',
'image/bmp', 'image/x-ms-bmp' => 'bmp',
default => 'jpg',
};
}
private function boolValue($value): bool
{
$parsed = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
return $parsed ?? (bool) $value;
}
private function error(string $message, array $data = []): array
{
$result = [
'code' => 0,
'msg' => $message,
];
if ($data !== []) {
$result['data'] = $data;
}
return $result;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace app\service;
use Swoole\Client;
class SwooleService
{
private static ?self $instance = null;
private string $host = 'ossup.jzvisa.com';
private int $port = 19501;
private float $timeout = 3.0;
// 私有化构造函数,保留原来的单例调用方式
private function __construct()
{
}
// 获取单例
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 发送任务:短连接模式,每次连接、发送、关闭
public function sendTask(array $task): bool
{
$client = new Client(SWOOLE_SOCK_TCP);
try {
if (!$client->connect($this->host, $this->port, $this->timeout)) {
throw new \RuntimeException(
"Cannot connect to Swoole Server {$this->host}:{$this->port}, error: {$client->errCode}"
);
}
$data = json_encode($task, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($data === false) {
throw new \RuntimeException('JSON encode failed: ' . json_last_error_msg());
}
// 你的服务端使用 \r\n 作为 package_eof所以这里必须加 \r\n
$sendResult = $client->send($data . "\r\n");
return $sendResult !== false;
} finally {
if ($client->isConnected()) {
$client->close();
}
}
}
// 保留 close 方法,避免旧代码调用时报错
public function close(): void
{
self::$instance = null;
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace app\service;
use think\Exception;
class UploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param string $targetUrl 目标服务器URL
* @param array $extraFields 额外传递的字段
* @param array $headers 额外的请求头
* @return array 包含上传结果的数组
*/
public function uploadAndForward(string $fileField, string $targetUrl, array $extraFields = [], array $headers = []): array
{
// 获取上传的文件
$file = request()->file($fileField);
if (!$file) {
return $this->createErrorResult('未上传文件或上传失败');
}
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 使用cURL转发到目标服务器
$result = $this->forwardToTargetServer($file, $targetUrl, $extraFields, $headers);
return $result;
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
/**
* 使用cURL将文件转发到目标服务器
*
* @param \think\File $file 文件对象
* @param string $targetUrl 目标URL
* @param array $extraFields 额外字段
* @param array $headers 请求头
* @return array 包含响应信息的数组
*/
private function forwardToTargetServer(\think\File $file, string $targetUrl, array $extraFields = [], array $headers = []): array
{
$ch = curl_init();
try {
// 设置基本选项
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300); // 5分钟超时
// 构建上传数据
$postData = [
'file' => new \CURLFile(
$file->getPathname(),
$file->getOriginalMime(),
$file->getOriginalName()
)
];
// 添加额外字段
$postData = array_merge($postData, $extraFields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
// 设置请求头
$httpHeaders = [];
foreach ($headers as $headerName => $headerValue) {
$httpHeaders[] = "$headerName: $headerValue";
}
if (!empty($httpHeaders)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
}
// 执行请求
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlInfo = curl_getinfo($ch);
// 检查错误
if ($response === false) {
throw new Exception('cURL错误: ' . curl_error($ch));
}
return [
'code' => 1,
'http_code' => $httpCode,
'response' => $response,
];
} catch (\Exception $e) {
return [
'code' => 0,
'error' => $e->getMessage()
];
} finally {
curl_close($ch);
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}

53
composer.json Normal file
View File

@@ -0,0 +1,53 @@
{
"name": "topthink/think",
"description": "the new thinkphp framework",
"type": "project",
"keywords": [
"framework",
"thinkphp",
"ORM"
],
"homepage": "https://www.thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
},
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"require": {
"php": ">=8.0.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0|^4.0",
"topthink/think-multi-app": "^1.1",
"topthink/think-view": "^2.0",
"topthink/think-filesystem": "^3.0",
"php-curl-class/php-curl-class": "^12.0",
"alibabacloud/oss-v2": "^0.4.0"
},
"require-dev": {
"topthink/think-dumper": "^1.0",
"topthink/think-trace": "^1.0"
},
"autoload": {
"psr-4": {
"app\\": "app"
},
"psr-0": {
"": "extend/"
}
},
"config": {
"preferred-install": "dist"
},
"scripts": {
"post-autoload-dump": [
"@php think service:discover",
"@php think vendor:publish"
]
}
}

1794
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

31
config/app.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
return [
// 应用的命名空间
'app_namespace' => '',
// 是否启用路由
'with_route' => true,
// 默认应用
'default_app' => 'miniapi',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
'site_domain' => env('SITE_DOMAIN', ''),
// 应用映射(自动多应用模式有效)
'app_map' => [],
// 域名绑定(自动多应用模式有效)
'domain_bind' => [],
// 禁止URL访问的应用列表自动多应用模式有效
'deny_app_list' => [],
// 异常页面的模板文件
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg' => false,
];

29
config/cache.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
// 默认缓存驱动
'default' => 'file',
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => '',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
],
// 更多的缓存连接
],
];

6
config/citic.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
return [
// 中信 EVUS 套餐合法价格,单位按现有业务字段保持不变。
'evus_prices' => ['49900', '74500'],
];

9
config/console.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
// 指令定义
'commands' => [
],
];

20
config/cookie.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
return [
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
];

63
config/database.php Normal file
View File

@@ -0,0 +1,63 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => env('DB_DRIVER', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
// true为自动识别类型 false关闭
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 时间字段配置 配置格式create_time,update_time
'datetime_field' => '',
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('DB_TYPE', 'mysql'),
// 服务器地址
'hostname' => env('DB_HOST', '127.0.0.1'),
// 数据库名
'database' => env('DB_NAME', ''),
// 用户名
'username' => env('DB_USER', 'root'),
// 密码
'password' => env('DB_PASS', ''),
// 端口
'hostport' => env('DB_PORT', '3306'),
// 数据库连接参数
'params' => [],
// 数据库编码
'charset' => env('DB_CHARSET', 'utf8mb4'),
// 数据库表前缀
'prefix' => env('DB_PREFIX', 'zy_'),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('APP_DEBUG', true),
// 开启字段缓存
'fields_cache' => false,
],
// 更多的数据库配置信息
],
];

47
config/filesystem.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
return [
// 默认磁盘
'default' => 'local',
// 磁盘列表
'disks' => [
'local' => [
'type' => 'local',
'root' => app()->getRuntimePath() . 'storage',
],
'public' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'public/storage',
// 磁盘路径对应的外部URL路径
'url' => '/storage',
// 可见性
'visibility' => 'public',
],
's3'=>[
'version' => 'latest',
'region' => 'us-east-1',
'endpoint' => 'https://oss.jzvisa.com',
'use_path_style_endpoint' => true,
'credentials' => [
'key' => 'aq72GoQAniHsTgXMmC9Z',
'secret' => '4ucvm7yvoGV9bAimLSHNXIWC7NkaZjLKOHEZeHUe',
],
],
],
'upload_conf' => [
'fileSize'=>'10485760',
'fileExt'=>['jpg','png','gif','pdf','doc','docx'],
'fallbackDir'=>app()->getRuntimePath() . 'storage/oss-fallback',
],
'imageMimeTypes' => [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/svg+xml',
'image/bmp',
],
];

29
config/lang.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 多语言设置
// +----------------------------------------------------------------------
return [
// 默认语言
'default_lang' => env('DEFAULT_LANG', 'zh-cn'),
// 自动侦测浏览器语言
'auto_detect_browser' => true,
// 允许的语言列表
'allow_lang_list' => [],
// 多语言自动侦测变量名
'detect_var' => 'lang',
// 是否使用Cookie记录
'use_cookie' => true,
// 多语言cookie变量
'cookie_var' => 'think_var',
// 多语言header变量
'header_var' => 'think-lang',
// 扩展语言包
'extend_list' => [],
// Accept-Language转义为对应语言包名称
'accept_language' => [
'zh-hans-cn' => 'zh-cn',
],
// 是否支持语言分组
'allow_group' => true,
];

50
config/log.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => 'file',
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
'monolog' => [
// 驱动类型
'type' => '\\app\\log\\driver\\Monolog',
// 日志保存目录
],
],
];

8
config/middleware.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
// 中间件配置
return [
// 别名或分组
'alias' => [],
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
'priority' => [],
];

43
config/mini_legal.php Normal file
View File

@@ -0,0 +1,43 @@
<?php
return [
'default_business_code' => 'evus',
'businesses' => [
'evus' => [
'documents' => [
'terms' => [
'title_key' => 'foot.user_agreement',
'content_key' => 'foot.user_agreement_info',
'intro_template' => '本条款与条件适用于{company_name}提供的EVUS登记小程序服务。',
'subject_label' => '服务主体',
],
'privacy' => [
'title_key' => 'foot.privacy_policy',
'content_key' => 'foot.privacy_policy_info',
'intro_template' => '本隐私政策适用于{company_name}提供的EVUS登记小程序服务。',
'subject_label' => '个人信息处理者',
],
],
'companies' => [
'jn' => [
'company_name' => '济南经纬九州出入境服务有限公司',
'site_domain' => 'www.evus.cn',
'phone' => '',
],
'bj' => [
'company_name' => '北京签律出入境服务有限公司',
'site_domain' => 'www.evus.com.cn',
'phone' => '',
],
],
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
],
'evus_bj_mp' => [
'company_code' => 'bj',
],
],
],
],
];

57
config/mini_payment.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
return [
'default_business_code' => 'evus',
'businesses' => [
'evus' => [
'mini_pay' => [
'url' => 'https://pay.qlevisa.com/home/pay/miniwxevuspay',
'body' => 'EVUS登记服务费',
'attach' => 'msg',
],
'companies' => [
'jn' => [
'mini_pay' => [],
],
'bj' => [
'mini_pay' => [],
],
],
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
'mini_pay' => [],
],
'evus_bj_mp' => [
'company_code' => 'bj',
'mini_pay' => [],
],
],
],
'esta' => [
'mini_pay' => [
'url' => 'https://pay.qlevisa.com/home/pay/miniwxevuspay',
'body' => 'ESTA登记签证服务费',
'attach' => 'esta',
],
'companies' => [
'jn' => [
'mini_pay' => [],
],
'bj' => [
'mini_pay' => [],
],
],
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
'mini_pay' => [],
],
'evus_bj_mp' => [
'company_code' => 'bj',
'mini_pay' => [],
],
],
],
],
];

49
config/mini_product.php Normal file
View File

@@ -0,0 +1,49 @@
<?php
return [
'default_business_code' => 'evus',
'businesses' => [
'evus' => [
'legacy_config_key' => 'EVUS_GOODS',
'companies' => [
'jn' => [
'products' => [],
],
'bj' => [
'products' => [],
],
],
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
'products' => [],
],
'evus_bj_mp' => [
'company_code' => 'bj',
'products' => [],
],
],
],
'esta' => [
'legacy_config_key' => 'ESTA_GOODS',
'companies' => [
'jn' => [
'products' => [],
],
'bj' => [
'products' => [],
],
],
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
'products' => [],
],
'evus_bj_mp' => [
'company_code' => 'bj',
'products' => [],
],
],
],
],
];

24
config/miniapp.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
return [
'header_name' => 'X-Mini-App-Code',
'default_code' => 'evus_jn_mp',
'apps' => [
'evus_jn_mp' => [
'company_code' => 'jn',
'company_name' => 'jinan',
'site_domain' => env('SITE_DOMAIN', 'www.evus.cn'),
'wechat_appid' => env('WECHAT_MINI_APPID', 'wx91fe9316ef97178c'),
'wechat_secret' => env('WECHAT_MINI_SECRET', ''),
'status' => 1,
],
'evus_bj_mp' => [
'company_code' => 'bj',
'company_name' => 'beijing',
'site_domain' => env('SITE_DOMAIN_BJ', 'www.evus.com.cn'),
'wechat_appid' => env('WECHAT_MINI_APPID_BJ', ''),
'wechat_secret' => env('WECHAT_MINI_SECRET_BJ', ''),
'status' => 1,
],
],
];

10
config/ocr.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
return [
'origin' => env('OCR_ORIGIN', ''),
'timeout' => (int) env('OCR_TIMEOUT', 60),
'connect_timeout' => (int) env('OCR_CONNECT_TIMEOUT', 10),
'auto_rotate' => env('OCR_AUTO_ROTATE', true),
'return_debug' => env('OCR_RETURN_DEBUG', false),
'verify_ssl' => env('OCR_VERIFY_SSL', true),
];

13
config/oss.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
'region' => 'cn-hangzhou',
'endpoint' => 'oss-cn-hangzhou.aliyuncs.com',
'bucket' => 'filesoa',
'access_key_id' => 'LTAI5tBscvVZHW7P9F7NT1K2',
'access_key_secret' => 'NIVu6LYDaYRJBrexqMpqAFe2d52CwB',
];

51
config/route.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// pathinfo分隔符
'pathinfo_depr' => '/',
// 是否开启路由延迟解析
'url_lazy_route' => false,
// 是否强制使用路由
'url_route_must' => false,
// 是否区分大小写
'url_case_sensitive' => false,
// 合并路由规则
'route_rule_merge' => false,
// 路由是否完全匹配
'route_complete_match' => false,
// 去除斜杠
'remove_slash' => false,
// 默认的路由变量规则
'default_route_pattern' => '[\w\.]+',
// URL伪静态后缀
'url_html_suffix' => 'html',
// 访问控制器层名称
'controller_layer' => 'controller',
// 空控制器名
'empty_controller' => 'Error',
// 是否使用控制器后缀
'controller_suffix' => false,
// 默认模块名(开启自动多模块有效)
'default_module' => 'index',
// 默认控制器名
'default_controller' => 'Index',
// 默认操作名
'default_action' => 'index',
// 操作方法后缀
'action_suffix' => '',
// 非路由变量是否使用普通参数方式用于URL生成
'url_common_param' => true,
// 操作方法的参数绑定方式 route get param
'action_bind_param' => 'get',
// 请求缓存规则 true为自动规则
'request_cache_key' => true,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则
'request_cache_except' => [],
// 请求缓存的Tag
'request_cache_tag' => '',
];

19
config/session.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
return [
// session name
'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// 驱动方式 支持file cache
'type' => 'file',
// 存储连接标识 当type使用cache的时候有效
'store' => null,
// 过期时间
'expire' => 1440,
// 前缀
'prefix' => '',
];

10
config/trace.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
// +----------------------------------------------------------------------
// | Trace设置 开启调试模式后有效
// +----------------------------------------------------------------------
return [
// 内置Html和Console两种方式 支持扩展
'type' => 'Html',
// 读取的日志通道名
'channel' => '',
];

25
config/view.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
return [
// 模板引擎类型使用Think
'type' => 'Think',
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
'auto_rule' => 1,
// 模板目录名
'view_dir_name' => 'view',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DIRECTORY_SEPARATOR,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
];

125
extend/api/Httpcurl.php Normal file
View File

@@ -0,0 +1,125 @@
<?php
/**
* HttpCurl Curl模拟Http工具类 v2.0
*
* @author gaoming13 <gaoming13@yeah.net>
* @link https://github.com/gaoming13/HttpCurl
* @link http://me.diary8.com/
*/
namespace api;
class Httpcurl
{
/**
* 模拟POST与GET请求
*
* Examples:
* ```
* HttpCurl::request('http://example.com/', 'post', array(
* 'user_uid' => 'root',
* 'user_pwd' => '123456'
* ));
*
* HttpCurl::request('http://example.com/', 'post', '{"name": "peter"}');
*
* HttpCurl::request('http://example.com/', 'post', array(
* 'file1' => '@/data/sky.jpg',
* 'file2' => '@/data/bird.jpg'
* ));
*
* // windows
* HttpCurl::request('http://example.com/', 'post', array(
* 'file1' => '@G:\wamp\www\data\1.jpg',
* 'file2' => '@G:\wamp\www\data\2.jpg'
* ));
*
* HttpCurl::request('http://example.com/', 'get');
*
* HttpCurl::request('http://example.com/?a=123', 'get', array('b'=>456));
* ```
*
* @param string $url [请求地址]
* @param string $type [请求方式 post or get]
* @param bool|string|array $data [传递的参数]
* @param array $header [可选:请求头] eg: ['Content-Type:application/json']
* @param int $timeout [可选:超时时间]
*
* @return array($body, $header, $status, $errno, $error)
* - $body string [响应正文]
* - $header string [响应头]
* - $status array [响应状态]
* - $errno int [错误码]
* - $error string [错误描述]
*/
public static function request($url, $type, $data = false, $header = [], $timeout = 0)
{
$cl = curl_init();
// 兼容HTTPS
if (stripos($url, 'https://') !== FALSE) {
curl_setopt($cl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($cl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($cl, CURLOPT_SSLVERSION, 1);
}
// 设置返回内容做变量存储
curl_setopt($cl, CURLOPT_RETURNTRANSFER, 1);
// 设置需要返回Header
curl_setopt($cl, CURLOPT_HEADER, true);
// 设置请求头
if (count($header) > 0) {
curl_setopt($cl, CURLOPT_HTTPHEADER, $header);
}
// 设置需要返回Body
curl_setopt($cl, CURLOPT_NOBODY, 0);
// 设置超时时间
if ($timeout > 0) {
curl_setopt($cl, CURLOPT_TIMEOUT, $timeout);
}
// POST/GET参数处理
$type = strtoupper($type);
if ($type == 'POST') {
curl_setopt($cl, CURLOPT_POST, true);
// convert @ prefixed file names to CurlFile class
// since @ prefix is deprecated as of PHP 5.6
if (class_exists('\CURLFile') && is_array($data)) {
foreach ($data as $k => $v) {
if (is_string($v) && strpos($v, '@') === 0) {
$v = ltrim($v, '@');
$data[$k] = new \CURLFile($v);
}
}
}
curl_setopt($cl, CURLOPT_POSTFIELDS, $data);
}
if ($type == 'GET' && is_array($data)) {
if (stripos($url, "?") === FALSE) {
$url .= '?';
}
$url .= http_build_query($data);
}
curl_setopt($cl, CURLOPT_URL, $url);
// 读取获取内容
$response = curl_exec($cl);
// 读取状态
$status = curl_getinfo($cl);
// 读取错误号
$errno = curl_errno($cl);
// 读取错误详情
$error = curl_error($cl);
// 关闭Curl
curl_close($cl);
if ($errno == 0 && isset($status['http_code'])) {
$header = substr($response, 0, $status['header_size']);
$body = substr($response, $status['header_size']);
return array($body, $header, $status, 0, '');
} else {
return array('', '', $status, $errno, $error);
}
}
}

337
extend/api/Verify.php Normal file
View File

@@ -0,0 +1,337 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------
namespace api;
class Verify {
protected $config = array(
'seKey' => 'ThinkPHP.CN', // 验证码加密密钥
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', // 验证码字符集合
'expire' => 1800, // 验证码过期时间s
'useZh' => false, // 使用中文验证码
'zhSet' => '们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书术状厂须离再目海交权且儿青才证低越际八试规斯近注办布门铁需走议县兵固除般引齿千胜细影济白格效置推空配刀叶率述今选养德话查差半敌始片施响收华觉备名红续均药标记难存测士身紧液派准斤角降维板许破述技消底床田势端感往神便贺村构照容非搞亚磨族火段算适讲按值美态黄易彪服早班麦削信排台声该击素张密害侯草何树肥继右属市严径螺检左页抗苏显苦英快称坏移约巴材省黑武培著河帝仅针怎植京助升王眼她抓含苗副杂普谈围食射源例致酸旧却充足短划剂宣环落首尺波承粉践府鱼随考刻靠够满夫失包住促枝局菌杆周护岩师举曲春元超负砂封换太模贫减阳扬江析亩木言球朝医校古呢稻宋听唯输滑站另卫字鼓刚写刘微略范供阿块某功套友限项余倒卷创律雨让骨远帮初皮播优占死毒圈伟季训控激找叫云互跟裂粮粒母练塞钢顶策双留误础吸阻故寸盾晚丝女散焊功株亲院冷彻弹错散商视艺灭版烈零室轻血倍缺厘泵察绝富城冲喷壤简否柱李望盘磁雄似困巩益洲脱投送奴侧润盖挥距触星松送获兴独官混纪依未突架宽冬章湿偏纹吃执阀矿寨责熟稳夺硬价努翻奇甲预职评读背协损棉侵灰虽矛厚罗泥辟告卵箱掌氧恩爱停曾溶营终纲孟钱待尽俄缩沙退陈讨奋械载胞幼哪剥迫旋征槽倒握担仍呀鲜吧卡粗介钻逐弱脚怕盐末阴丰雾冠丙街莱贝辐肠付吉渗瑞惊顿挤秒悬姆烂森糖圣凹陶词迟蚕亿矩康遵牧遭幅园腔订香肉弟屋敏恢忘编印蜂急拿扩伤飞露核缘游振操央伍域甚迅辉异序免纸夜乡久隶缸夹念兰映沟乙吗儒杀汽磷艰晶插埃燃欢铁补咱芽永瓦倾阵碳演威附牙芽永瓦斜灌欧献顺猪洋腐请透司危括脉宜笑若尾束壮暴企菜穗楚汉愈绿拖牛份染既秋遍锻玉夏疗尖殖井费州访吹荣铜沿替滚客召旱悟刺脑措贯藏敢令隙炉壳硫煤迎铸粘探临薄旬善福纵择礼愿伏残雷延烟句纯渐耕跑泽慢栽鲁赤繁境潮横掉锥希池败船假亮谓托伙哲怀割摆贡呈劲财仪沉炼麻罪祖息车穿货销齐鼠抽画饲龙库守筑房歌寒喜哥洗蚀废纳腹乎录镜妇恶脂庄擦险赞钟摇典柄辩竹谷卖乱虚桥奥伯赶垂途额壁网截野遗静谋弄挂课镇妄盛耐援扎虑键归符庆聚绕摩忙舞遇索顾胶羊湖钉仁音迹碎伸灯避泛亡答勇频皇柳哈揭甘诺概宪浓岛袭谁洪谢炮浇斑讯懂灵蛋闭孩释乳巨徒私银伊景坦累匀霉杜乐勒隔弯绩招绍胡呼痛峰零柴簧午跳居尚丁秦稍追梁折耗碱殊岗挖氏刃剧堆赫荷胸衡勤膜篇登驻案刊秧缓凸役剪川雪链渔啦脸户洛孢勃盟买杨宗焦赛旗滤硅炭股坐蒸凝竟陷枪黎救冒暗洞犯筒您宋弧爆谬涂味津臂障褐陆啊健尊豆拔莫抵桑坡缝警挑污冰柬嘴啥饭塑寄赵喊垫丹渡耳刨虎笔稀昆浪萨茶滴浅拥穴覆伦娘吨浸袖珠雌妈紫戏塔锤震岁貌洁剖牢锋疑霸闪埔猛诉刷狠忽灾闹乔唐漏闻沈熔氯荒茎男凡抢像浆旁玻亦忠唱蒙予纷捕锁尤乘乌智淡允叛畜俘摸锈扫毕璃宝芯爷鉴秘净蒋钙肩腾枯抛轨堂拌爸循诱祝励肯酒绳穷塘燥泡袋朗喂铝软渠颗惯贸粪综墙趋彼届墨碍启逆卸航衣孙龄岭骗休借', // 中文验证码字符串
'useImgBg' => false, // 使用背景图片
'fontSize' => 25, // 验证码字体大小(px)
'useCurve' => true, // 是否画混淆曲线
'useNoise' => true, // 是否添加杂点
'imageH' => 0, // 验证码图片高度
'imageW' => 0, // 验证码图片宽度
'length' => 5, // 验证码位数
'fontttf' => '', // 验证码字体,不设置随机获取
'bg' => array(243, 251, 254), // 背景颜色
'reset' => true, // 验证成功后是否重置
);
private $_image = NULL; // 验证码图片实例
private $_color = NULL; // 验证码字体颜色
/**
* 架构方法 设置参数
* @access public
* @param array $config 配置参数
*/
public function __construct($config=array()){
$this->config = array_merge($this->config, $config);
}
/**
* 使用 $this->name 获取配置
* @access public
* @param string $name 配置名称
* @return multitype 配置值
*/
public function __get($name) {
return $this->config[$name] ?? null;
}
/**
* 设置验证码配置
* @access public
* @param string $name 配置名称
* @param string $value 配置值
* @return void
*/
public function __set($name,$value){
if(isset($this->config[$name])) {
$this->config[$name] = $value;
}
}
/**
* 检查配置
* @access public
* @param string $name 配置名称
* @return bool
*/
public function __isset($name){
return isset($this->config[$name]);
}
/**
* 验证验证码是否正确
* @access public
* @param string $code 用户验证码
* @param string $id 验证码标识
* @return bool 用户验证码是否正确
*/
public function check($code, $id = '') {
$code = $this->normalizeCode($code);
$key = $this->authcode($this->seKey).$id;
// 验证码不能为空
$secode = session($key);
if(empty($code) || empty($secode) || !is_array($secode) || empty($secode['verify_code']) || empty($secode['verify_time'])) {
return false;
}
// session 过期
if(time() - $secode['verify_time'] > $this->expire) {
session($key, null);
return false;
}
if($this->authcode($code) == $secode['verify_code']) {
$this->reset && session($key, null);
return true;
}
return false;
}
/**
* 输出验证码并把验证码的值保存的session中
* 验证码保存到session的格式为 array('verify_code' => '验证码值', 'verify_time' => '验证码创建时间');
* @access public
* @param string $id 要生成验证码的标识
* @return void
*/
public function entry($id = '') {
// 图片宽(px)
$this->imageW || $this->imageW = max(1, (int) ceil($this->length * $this->fontSize * 1.5 + $this->length * $this->fontSize / 2));
// 图片高(px)
$this->imageH || $this->imageH = max(1, (int) ceil($this->fontSize * 2.5));
// 建立一幅 $this->imageW x $this->imageH 的图像
$this->_image = imagecreate((int) $this->imageW, (int) $this->imageH);
if (!$this->_image) {
throw new \RuntimeException('Verify image create failed.');
}
// 设置背景
imagecolorallocate($this->_image, $this->bg[0], $this->bg[1], $this->bg[2]);
// 验证码字体随机颜色
$this->_color = imagecolorallocate($this->_image, mt_rand(1,150), mt_rand(1,150), mt_rand(1,150));
// 验证码使用随机字体
$ttfPath = dirname(__FILE__) . '/Verify/' . ($this->useZh ? 'zhttfs' : 'ttfs') . '/';
if(empty($this->fontttf)){
$ttfs = is_dir($ttfPath) ? glob($ttfPath . '*.ttf') : array();
if (empty($ttfs)) {
throw new \RuntimeException('Verify font file not found.');
}
$this->fontttf = $ttfs[array_rand($ttfs)];
} else {
$this->fontttf = $ttfPath . $this->fontttf;
}
if (!is_file($this->fontttf)) {
throw new \RuntimeException('Verify font file not found.');
}
if($this->useImgBg) {
$this->_background();
}
if ($this->useNoise) {
// 绘杂点
$this->_writeNoise();
}
if ($this->useCurve) {
// 绘干扰线
$this->_writeCurve();
}
// 绘验证码
$code = array(); // 验证码
$codeNX = 0; // 验证码第N个字符的左边距
if($this->useZh){ // 中文验证码
for ($i = 0; $i<$this->length; $i++) {
$code[$i] = iconv_substr($this->zhSet,floor(mt_rand(0,mb_strlen($this->zhSet,'utf-8')-1)),1,'utf-8');
imagettftext($this->_image, (int) $this->fontSize, mt_rand(-40, 40), (int) round($this->fontSize * ($i + 1) * 1.5), (int) round($this->fontSize + mt_rand(10, 20)), $this->_color, $this->fontttf, $code[$i]);
}
}else{
for ($i = 0; $i<$this->length; $i++) {
$code[$i] = $this->codeSet[mt_rand(0, strlen($this->codeSet)-1)];
$codeNX += $this->randInt($this->fontSize * 1.2, $this->fontSize * 1.6);
imagettftext($this->_image, (int) $this->fontSize, mt_rand(-40, 40), (int) $codeNX, (int) round($this->fontSize * 1.6), $this->_color, $this->fontttf, $code[$i]);
}
}
// 保存验证码
$key = $this->authcode($this->seKey);
$code = $this->authcode($this->normalizeCode(implode('', $code)));
$secode = array();
$secode['verify_code'] = $code; // 把校验码保存到session
$secode['verify_time'] = time(); // 验证码创建时间
session($key.$id, $secode);
header('Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header("content-type: image/png");
// 输出图像
imagepng($this->_image);
imagedestroy($this->_image);
}
/**
* 画一条由两条连在一起构成的随机正弦函数曲线作干扰线(你可以改成更帅的曲线函数)
*
* 高中的数学公式咋都忘了涅,写出来
* 正弦型函数解析式y=Asin(ωx+φ)+b
* 各常数值对函数图像的影响:
* A决定峰值即纵向拉伸压缩的倍数
* b表示波形在Y轴的位置关系或纵向移动距离上加下减
* φ决定波形与X轴位置关系或横向移动距离左加右减
* ω决定周期最小正周期T=2π/∣ω∣)
*
*/
private function _writeCurve() {
$px = $py = 0;
// 曲线前部分
$A = $this->randInt(1, $this->imageH / 2); // 振幅
$b = $this->randInt(-$this->imageH / 4, $this->imageH / 4); // Y轴方向偏移量
$f = $this->randInt(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
$T = $this->randInt($this->imageH, $this->imageW * 2); // 周期
$w = (2* M_PI)/$T;
$px1 = 0; // 曲线横坐标起始位置
$px2 = $this->randInt($this->imageW / 2, $this->imageW * 0.8); // 曲线横坐标结束位置
for ($px=$px1; $px<=$px2; $px = $px + 1) {
if ($w!=0) {
$py = $A * sin($w*$px + $f)+ $b + $this->imageH/2; // y = Asin(ωx+φ) + b
$i = (int) ($this->fontSize/5);
while ($i > 0) {
imagesetpixel($this->_image, $px + $i , (int) round($py) + $i, $this->_color); // 这里(while)循环画像素点比imagettftext和imagestring用字体大小一次画出不用这while循环性能要好很多
$i--;
}
}
}
// 曲线后部分
$A = $this->randInt(1, $this->imageH / 2); // 振幅
$f = $this->randInt(-$this->imageH / 4, $this->imageH / 4); // X轴方向偏移量
$T = $this->randInt($this->imageH, $this->imageW * 2); // 周期
$w = (2* M_PI)/$T;
$b = $py - $A * sin($w*$px + $f) - $this->imageH/2;
$px1 = $px2;
$px2 = (int) $this->imageW;
for ($px=$px1; $px<=$px2; $px=$px+ 1) {
if ($w!=0) {
$py = $A * sin($w*$px + $f)+ $b + $this->imageH/2; // y = Asin(ωx+φ) + b
$i = (int) ($this->fontSize/5);
while ($i > 0) {
imagesetpixel($this->_image, $px + $i, (int) round($py) + $i, $this->_color);
$i--;
}
}
}
}
/**
* 画杂点
* 往图片上写不同颜色的字母或数字
*/
private function _writeNoise() {
$codeSet = '2345678abcdefhijkmnpqrstuvwxyz';
for($i = 0; $i < 10; $i++){
//杂点颜色
$noiseColor = imagecolorallocate($this->_image, mt_rand(150,225), mt_rand(150,225), mt_rand(150,225));
for($j = 0; $j < 5; $j++) {
// 绘杂点
imagestring($this->_image, 5, mt_rand(-10, (int) $this->imageW), mt_rand(-10, (int) $this->imageH), $codeSet[mt_rand(0, 29)], $noiseColor);
}
}
}
/**
* 绘制背景图片
* 注:如果验证码输出图片比较大,将占用比较多的系统资源
*/
private function _background() {
$path = dirname(__FILE__).'/Verify/bgs/';
if (!is_dir($path)) {
return;
}
$dir = dir($path);
if (!$dir) {
return;
}
$bgs = array();
while (false !== ($file = $dir->read())) {
if($file[0] != '.' && substr($file, -4) == '.jpg') {
$bgs[] = $path . $file;
}
}
$dir->close();
if (empty($bgs)) {
return;
}
$gb = $bgs[array_rand($bgs)];
$size = @getimagesize($gb);
if (!$size) {
return;
}
list($width, $height) = $size;
// Resample
$bgImage = @imagecreatefromjpeg($gb);
if (!$bgImage) {
return;
}
@imagecopyresampled($this->_image, $bgImage, 0, 0, 0, 0, (int) $this->imageW, (int) $this->imageH, (int) $width, (int) $height);
@imagedestroy($bgImage);
}
private function randInt($min, $max) {
$min = (int) round($min);
$max = (int) round($max);
if ($min > $max) {
list($min, $max) = array($max, $min);
}
return mt_rand($min, $max);
}
private function normalizeCode($code) {
$code = trim((string) $code);
if (function_exists('mb_convert_kana')) {
$code = mb_convert_kana($code, 'as', 'UTF-8');
}
$normalized = preg_replace('/\s+/u', '', $code);
if (is_string($normalized)) {
$code = $normalized;
}
return strtoupper($code);
}
/* 加密验证码 */
private function authcode($str){
$key = substr(md5($this->seKey), 5, 8);
$str = substr(md5($str), 8, 10);
return md5($key . $str);
}
}

BIN
extend/api/Verify/bgs/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
extend/api/Verify/bgs/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
extend/api/Verify/bgs/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
extend/api/Verify/bgs/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
extend/api/Verify/bgs/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
extend/api/Verify/bgs/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
extend/api/Verify/bgs/7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
extend/api/Verify/bgs/8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

8
nginx.htaccess Normal file
View File

@@ -0,0 +1,8 @@
location ~* (runtime|application)/{
return 403;
}
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=$1 last; break;
}
}

0
public/.htaccess Normal file
View File

25
public/index.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\App;
// [ 应用入口文件 ]
require __DIR__ . '/../vendor/autoload.php';
// 执行HTTP应用并响应
$http = (new App())->http;
$response = $http->run();
$response->send();
$http->end($response);

978
public/json/country.json Normal file
View File

@@ -0,0 +1,978 @@
[
{
"value": "BT",
"text": "不丹"
},
{
"value": "TL",
"text": "东帝汶"
},
{
"value": "AS",
"text": "东萨摩亚"
},
{
"value": "CN",
"text": "中国"
},
{
"value": "CF",
"text": "中非共和国"
},
{
"value": "DK",
"text": "丹麦"
},
{
"value": "UA",
"text": "乌克兰"
},
{
"value": "UZ",
"text": "乌兹别克斯坦"
},
{
"value": "UG",
"text": "乌干达"
},
{
"value": "UY",
"text": "乌拉圭"
},
{
"value": "TD",
"text": "乍得"
},
{
"value": "YE",
"text": "也门"
},
{
"value": "AM",
"text": "亚美尼亚"
},
{
"value": "IL",
"text": "以色列"
},
{
"value": "IQ",
"text": "伊拉克"
},
{
"value": "IR",
"text": "伊朗,伊斯兰共和国"
},
{
"value": "BZ",
"text": "伯利兹"
},
{
"value": "CV",
"text": "佛得角"
},
{
"value": "RU",
"text": "俄罗斯联邦"
},
{
"value": "BG",
"text": "保加利亚"
},
{
"value": "HR",
"text": "克罗地亚"
},
{
"value": "GU",
"text": "关岛"
},
{
"value": "GM",
"text": "冈比亚"
},
{
"value": "IS",
"text": "冰岛"
},
{
"value": "GN",
"text": "几内亚"
},
{
"value": "GW",
"text": "几内亚比绍"
},
{
"value": "LI",
"text": "列支敦士登"
},
{
"value": "CG",
"text": "刚果"
},
{
"value": "CD",
"text": "刚果民主共和国"
},
{
"value": "LY",
"text": "利比亚"
},
{
"value": "LR",
"text": "利比里亚"
},
{
"value": "CA",
"text": "加拿大"
},
{
"value": "GH",
"text": "加纳"
},
{
"value": "GA",
"text": "加蓬"
},
{
"value": "HU",
"text": "匈牙利"
},
{
"value": "MP",
"text": "北马里亚纳群岛"
},
{
"value": "GS",
"text": "南乔治亚岛和南桑威奇群岛"
},
{
"value": "AQ",
"text": "南极洲"
},
{
"value": "SS",
"text": "南苏丹"
},
{
"value": "ZA",
"text": "南非"
},
{
"value": "BQ",
"text": "博内尔岛;圣尤斯特歇斯和萨巴"
},
{
"value": "BW",
"text": "博茨瓦纳"
},
{
"value": "QA",
"text": "卡塔尔"
},
{
"value": "RW",
"text": "卢旺达"
},
{
"value": "LU",
"text": "卢森堡"
},
{
"value": "ID",
"text": "印尼"
},
{
"value": "IN",
"text": "印度"
},
{
"value": "GT",
"text": "危地马拉"
},
{
"value": "EC",
"text": "厄瓜多尔"
},
{
"value": "ER",
"text": "厄立特里亚"
},
{
"value": "CU",
"text": "古巴"
},
{
"value": "TW",
"text": "中国台湾"
},
{
"value": "KG",
"text": "吉尔吉斯斯坦"
},
{
"value": "DJ",
"text": "吉布提"
},
{
"value": "KZ",
"text": "哈萨克斯坦"
},
{
"value": "CO",
"text": "哥伦比亚"
},
{
"value": "CR",
"text": "哥斯达黎加"
},
{
"value": "CM",
"text": "喀麦隆"
},
{
"value": "TV",
"text": "图瓦卢"
},
{
"value": "TM",
"text": "土库曼斯坦"
},
{
"value": "TR",
"text": "土耳其"
},
{
"value": "LC",
"text": "圣卢西亚"
},
{
"value": "KN",
"text": "圣基茨和尼维斯"
},
{
"value": "ST",
"text": "圣多美和普林西比"
},
{
"value": "BL",
"text": "圣巴泰勒米"
},
{
"value": "VC",
"text": "圣文森特和格林纳丁斯"
},
{
"value": "SH",
"text": "圣海伦娜"
},
{
"value": "PM",
"text": "圣皮埃尔和密克隆"
},
{
"value": "CX",
"text": "圣诞岛"
},
{
"value": "SX",
"text": "圣马丁 (荷兰领地)"
},
{
"value": "MF",
"text": "圣马丁岛"
},
{
"value": "SM",
"text": "圣马利诺"
},
{
"value": "GY",
"text": "圭亚那"
},
{
"value": "TZ",
"text": "坦桑尼亚联合共和国"
},
{
"value": "EG",
"text": "埃及"
},
{
"value": "ET",
"text": "埃塞俄比亚"
},
{
"value": "KI",
"text": "基里巴斯"
},
{
"value": "TJ",
"text": "塔吉克斯坦"
},
{
"value": "SN",
"text": "塞内加尔"
},
{
"value": "RS",
"text": "塞尔维亚"
},
{
"value": "SL",
"text": "塞拉利昂"
},
{
"value": "CY",
"text": "塞浦路斯共和国"
},
{
"value": "SC",
"text": "塞舌尔"
},
{
"value": "MX",
"text": "墨西哥"
},
{
"value": "TG",
"text": "多哥"
},
{
"value": "DM",
"text": "多米尼克"
},
{
"value": "DO",
"text": "多米尼克共和国"
},
{
"value": "KR",
"text": "大韩民国"
},
{
"value": "AX",
"text": "奥兰群岛"
},
{
"value": "AT",
"text": "奥地利"
},
{
"value": "VE",
"text": "委内瑞拉"
},
{
"value": "BD",
"text": "孟加拉"
},
{
"value": "AO",
"text": "安哥拉"
},
{
"value": "AI",
"text": "安圭拉"
},
{
"value": "AG",
"text": "安提瓜和巴布达"
},
{
"value": "AD",
"text": "安道尔"
},
{
"value": "FM",
"text": "密克罗尼西亚联邦国"
},
{
"value": "NI",
"text": "尼加拉瓜"
},
{
"value": "NG",
"text": "尼日利亚"
},
{
"value": "NE",
"text": "尼日尔"
},
{
"value": "NP",
"text": "尼泊尔"
},
{
"value": "PS",
"text": "巴勒斯坦领土"
},
{
"value": "BS",
"text": "巴哈马"
},
{
"value": "PK",
"text": "巴基斯坦"
},
{
"value": "BB",
"text": "巴巴多斯"
},
{
"value": "PG",
"text": "巴布亚新几内亚"
},
{
"value": "PY",
"text": "巴拉圭"
},
{
"value": "PA",
"text": "巴拿马"
},
{
"value": "BH",
"text": "巴林"
},
{
"value": "BR",
"text": "巴西"
},
{
"value": "BF",
"text": "布吉纳法索"
},
{
"value": "BI",
"text": "布隆迪"
},
{
"value": "GR",
"text": "希腊"
},
{
"value": "PW",
"text": "帕劳"
},
{
"value": "CK",
"text": "库克群岛"
},
{
"value": "CW",
"text": "库拉索岛"
},
{
"value": "KY",
"text": "开曼群岛"
},
{
"value": "DE",
"text": "德国"
},
{
"value": "IT",
"text": "意大利"
},
{
"value": "SB",
"text": "所罗门群岛"
},
{
"value": "TK",
"text": "托克劳"
},
{
"value": "LV",
"text": "拉托维亚"
},
{
"value": "NO",
"text": "挪威"
},
{
"value": "CZ",
"text": "捷克共和国"
},
{
"value": "MD",
"text": "摩尔多瓦共和国"
},
{
"value": "MA",
"text": "摩洛哥"
},
{
"value": "MC",
"text": "摩纳哥"
},
{
"value": "BN",
"text": "文莱达鲁萨兰国"
},
{
"value": "FJ",
"text": "斐济"
},
{
"value": "SZ",
"text": "斯威士兰"
},
{
"value": "SK",
"text": "斯洛伐克"
},
{
"value": "SI",
"text": "斯洛文尼亚"
},
{
"value": "SJ",
"text": "斯瓦尔巴和扬马延岛"
},
{
"value": "LK",
"text": "斯里兰卡"
},
{
"value": "SG",
"text": "新加坡"
},
{
"value": "NC",
"text": "新喀里多尼亚"
},
{
"value": "NZ",
"text": "新西兰"
},
{
"value": "JP",
"text": "日本"
},
{
"value": "CL",
"text": "智利"
},
{
"value": "KP",
"text": "朝鲜民主人民共和国"
},
{
"value": "KH",
"text": "柬埔寨"
},
{
"value": "GG",
"text": "根西岛"
},
{
"value": "GD",
"text": "格林纳达"
},
{
"value": "GL",
"text": "格陵兰岛"
},
{
"value": "GE",
"text": "格鲁吉亚"
},
{
"value": "VA",
"text": "梵蒂冈"
},
{
"value": "BE",
"text": "比利时"
},
{
"value": "MR",
"text": "毛里塔尼亚"
},
{
"value": "MU",
"text": "毛里求斯"
},
{
"value": "TO",
"text": "汤加"
},
{
"value": "SA",
"text": "沙特阿拉伯"
},
{
"value": "FR",
"text": "法国"
},
{
"value": "GF",
"text": "法属圭亚那"
},
{
"value": "PF",
"text": "法属波利尼西亚"
},
{
"value": "FO",
"text": "法罗群岛"
},
{
"value": "PL",
"text": "波兰"
},
{
"value": "PR",
"text": "波多黎各"
},
{
"value": "BA",
"text": "波斯尼亚和黑塞哥维那"
},
{
"value": "TH",
"text": "泰国"
},
{
"value": "JE",
"text": "泽西岛"
},
{
"value": "ZW",
"text": "津巴布韦"
},
{
"value": "HN",
"text": "洪都拉斯"
},
{
"value": "HT",
"text": "海地"
},
{
"value": "AU",
"text": "澳大利亚"
},
{
"value": "MO",
"text": "中国澳门"
},
{
"value": "IE",
"text": "爱尔兰"
},
{
"value": "EE",
"text": "爱沙尼亚"
},
{
"value": "JM",
"text": "牙买加"
},
{
"value": "TC",
"text": "特克斯和凯科斯群岛"
},
{
"value": "TT",
"text": "特立尼达和多巴哥"
},
{
"value": "BO",
"text": "玻利维亚"
},
{
"value": "NR",
"text": "瑙鲁"
},
{
"value": "SE",
"text": "瑞典"
},
{
"value": "CH",
"text": "瑞士"
},
{
"value": "GP",
"text": "瓜德罗普岛"
},
{
"value": "WF",
"text": "瓦利斯和富图纳群岛"
},
{
"value": "VU",
"text": "瓦努阿图"
},
{
"value": "RE",
"text": "留尼汪"
},
{
"value": "BY",
"text": "白俄罗斯"
},
{
"value": "BM",
"text": "百慕大"
},
{
"value": "PN",
"text": "皮特凯恩群岛"
},
{
"value": "GI",
"text": "直布罗陀"
},
{
"value": "FK",
"text": "福克兰群岛 (马尔维纳斯群岛)"
},
{
"value": "KW",
"text": "科威特"
},
{
"value": "KM",
"text": "科摩罗"
},
{
"value": "CI",
"text": "科特迪瓦"
},
{
"value": "CC",
"text": "科科斯 (基林) 群岛"
},
{
"value": "PE",
"text": "秘鲁"
},
{
"value": "TN",
"text": "突尼西亚"
},
{
"value": "LT",
"text": "立陶宛"
},
{
"value": "SO",
"text": "索马里"
},
{
"value": "JO",
"text": "约旦"
},
{
"value": "NA",
"text": "纳米比亚"
},
{
"value": "NU",
"text": "纽埃"
},
{
"value": "MM",
"text": "缅甸"
},
{
"value": "RO",
"text": "罗马尼亚"
},
{
"value": "US",
"text": "美国"
},
{
"value": "VI",
"text": "美属维京群岛"
},
{
"value": "LA",
"text": "老挝人民民主共和国"
},
{
"value": "KE",
"text": "肯尼亚"
},
{
"value": "FI",
"text": "芬兰"
},
{
"value": "SD",
"text": "苏丹"
},
{
"value": "SR",
"text": "苏里南"
},
{
"value": "GB",
"text": "英国"
},
{
"value": "IO",
"text": "英属印度洋领地"
},
{
"value": "VG",
"text": "英属维京群岛"
},
{
"value": "NL",
"text": "荷兰"
},
{
"value": "MZ",
"text": "莫桑比克"
},
{
"value": "LS",
"text": "莱索托"
},
{
"value": "PH",
"text": "菲律宾"
},
{
"value": "SV",
"text": "萨尔瓦多"
},
{
"value": "WS",
"text": "萨摩亚"
},
{
"value": "PT",
"text": "葡萄牙"
},
{
"value": "MN",
"text": "蒙古"
},
{
"value": "MS",
"text": "蒙特塞拉特"
},
{
"value": "ES",
"text": "西班牙"
},
{
"value": "NF",
"text": "诺福克岛"
},
{
"value": "BJ",
"text": "贝宁"
},
{
"value": "ZM",
"text": "赞比亚"
},
{
"value": "GQ",
"text": "赤道几内亚"
},
{
"value": "VN",
"text": "越南"
},
{
"value": "AZ",
"text": "阿塞拜疆"
},
{
"value": "AF",
"text": "阿富汗"
},
{
"value": "DZ",
"text": "阿尔及利亚"
},
{
"value": "AL",
"text": "阿尔巴尼亚"
},
{
"value": "SY",
"text": "阿拉伯叙利亚共和国"
},
{
"value": "OM",
"text": "阿曼"
},
{
"value": "AR",
"text": "阿根廷"
},
{
"value": "AE",
"text": "阿联酋"
},
{
"value": "AW",
"text": "阿鲁巴"
},
{
"value": "HK",
"text": "中国香港"
},
{
"value": "MK",
"text": "马其顿"
},
{
"value": "MV",
"text": "马尔代夫"
},
{
"value": "IM",
"text": "马恩岛"
},
{
"value": "MW",
"text": "马拉维"
},
{
"value": "MQ",
"text": "马提尼克岛"
},
{
"value": "MY",
"text": "马来西亚"
},
{
"value": "YT",
"text": "马约特岛"
},
{
"value": "MH",
"text": "马绍尔群岛"
},
{
"value": "MT",
"text": "马耳他"
},
{
"value": "MG",
"text": "马达加斯加"
},
{
"value": "ML",
"text": "马里"
},
{
"value": "LB",
"text": "黎巴嫩"
},
{
"value": "ME",
"text": "黑山"
}
]

978
public/json/country_en.json Normal file
View File

@@ -0,0 +1,978 @@
[
{
"value": "BT",
"text": "Bhutan"
},
{
"value": "TL",
"text": "East Timor"
},
{
"value": "AS",
"text": "American Samoa"
},
{
"value": "CN",
"text": "China"
},
{
"value": "CF",
"text": "CentralAfricanRepublic"
},
{
"value": "DK",
"text": "Denmark"
},
{
"value": "UA",
"text": "Ukraine"
},
{
"value": "UZ",
"text": "Uzbekistan"
},
{
"value": "UG",
"text": "Uganda"
},
{
"value": "UY",
"text": "Uruguay"
},
{
"value": "TD",
"text": "Chad"
},
{
"value": "YE",
"text": "Yemen"
},
{
"value": "AM",
"text": "Armenia"
},
{
"value": "IL",
"text": "Israel"
},
{
"value": "IQ",
"text": "Iraq"
},
{
"value": "IR",
"text": "Iran, Islamic Republic of"
},
{
"value": "BZ",
"text": "Belize"
},
{
"value": "CV",
"text": "Cape Verde"
},
{
"value": "RU",
"text": "Russian Federation"
},
{
"value": "BG",
"text": "Bulgaria"
},
{
"value": "HR",
"text": "Croatia"
},
{
"value": "GU",
"text": "Guam"
},
{
"value": "GM",
"text": "Gambia"
},
{
"value": "IS",
"text": "Iceland"
},
{
"value": "GN",
"text": "Guinea"
},
{
"value": "GW",
"text": "Guinea-Bissau"
},
{
"value": "LI",
"text": "Liechtenstein"
},
{
"value": "CG",
"text": "Congo"
},
{
"value": "CD",
"text": "Democratic Republic of the Congo"
},
{
"value": "LY",
"text": "Libya"
},
{
"value": "LR",
"text": "Liberia"
},
{
"value": "CA",
"text": "Canada"
},
{
"value": "GH",
"text": "Ghana"
},
{
"value": "GA",
"text": "Gabon"
},
{
"value": "HU",
"text": "Hungary"
},
{
"value": "MP",
"text": "Northern Mariana Islands"
},
{
"value": "GS",
"text": "South Georgia and the South Sandwich Islands"
},
{
"value": "AQ",
"text": "Antarctica"
},
{
"value": "SS",
"text": "South Sudan"
},
{
"value": "ZA",
"text": "South Africa"
},
{
"value": "BQ",
"text": "Bonaire, Sint Eustatius and Saba"
},
{
"value": "BW",
"text": "Botswana"
},
{
"value": "QA",
"text": "Qatar"
},
{
"value": "RW",
"text": "Rwanda"
},
{
"value": "LU",
"text": "Luxembourg"
},
{
"value": "ID",
"text": "Indonesia"
},
{
"value": "IN",
"text": "India"
},
{
"value": "GT",
"text": "Guatemala"
},
{
"value": "EC",
"text": "Ecuador"
},
{
"value": "ER",
"text": "Eritrea"
},
{
"value": "CU",
"text": "Cuba"
},
{
"value": "TW",
"text": "Taiwan, China"
},
{
"value": "KG",
"text": "Kyrgyzstan"
},
{
"value": "DJ",
"text": "Djibouti"
},
{
"value": "KZ",
"text": "Kazakhstan"
},
{
"value": "CO",
"text": "Colombia"
},
{
"value": "CR",
"text": "Costa Rica"
},
{
"value": "CM",
"text": "Cameroon"
},
{
"value": "TV",
"text": "Tuvalu"
},
{
"value": "TM",
"text": "Turkmenistan"
},
{
"value": "TR",
"text": "Turkey"
},
{
"value": "LC",
"text": "Saint Lucia"
},
{
"value": "KN",
"text": "Saint Kitts and Nevis"
},
{
"value": "ST",
"text": "Sao Tome and Principe"
},
{
"value": "BL",
"text": "Saint Barthélemy"
},
{
"value": "VC",
"text": "Saint Vincent and the Grenadines"
},
{
"value": "SH",
"text": "Saint Helena"
},
{
"value": "PM",
"text": "Saint Pierre and Miquelon"
},
{
"value": "CX",
"text": "Christmas Island"
},
{
"value": "SX",
"text": "Sint Maarten (Dutch part)"
},
{
"value": "MF",
"text": "Saint Martin (French part)"
},
{
"value": "SM",
"text": "San Marino"
},
{
"value": "GY",
"text": "Guyana"
},
{
"value": "TZ",
"text": "United Republic of Tanzania"
},
{
"value": "EG",
"text": "Egypt"
},
{
"value": "ET",
"text": "Ethiopia"
},
{
"value": "KI",
"text": "Kiribati"
},
{
"value": "TJ",
"text": "Tajikistan"
},
{
"value": "SN",
"text": "Senegal"
},
{
"value": "RS",
"text": "Serbia"
},
{
"value": "SL",
"text": "Sierra Leone"
},
{
"value": "CY",
"text": "Cyprus"
},
{
"value": "SC",
"text": "Seychelles"
},
{
"value": "MX",
"text": "Mexico"
},
{
"value": "TG",
"text": "Togo"
},
{
"value": "DM",
"text": "Dominica"
},
{
"value": "DO",
"text": "Dominican Republic"
},
{
"value": "KR",
"text": "South Korea"
},
{
"value": "AX",
"text": "Åland Islands"
},
{
"value": "AT",
"text": "Austria"
},
{
"value": "VE",
"text": "Venezuela"
},
{
"value": "BD",
"text": "Bangladesh"
},
{
"value": "AO",
"text": "Angola"
},
{
"value": "AI",
"text": "Anguilla"
},
{
"value": "AG",
"text": "Antigua and Barbuda"
},
{
"value": "AD",
"text": "Andorra"
},
{
"value": "FM",
"text": "Federated States of Micronesia"
},
{
"value": "NI",
"text": "Nicaragua"
},
{
"value": "NG",
"text": "Nigeria"
},
{
"value": "NE",
"text": "Niger"
},
{
"value": "NP",
"text": "Nepal"
},
{
"value": "PS",
"text": "Palestinian Territory"
},
{
"value": "BS",
"text": "Bahamas"
},
{
"value": "PK",
"text": "Pakistan"
},
{
"value": "BB",
"text": "Barbados"
},
{
"value": "PG",
"text": "Papua New Guinea"
},
{
"value": "PY",
"text": "Paraguay"
},
{
"value": "PA",
"text": "Panama"
},
{
"value": "BH",
"text": "Bahrain"
},
{
"value": "BR",
"text": "Brazil"
},
{
"value": "BF",
"text": "Burkina Faso"
},
{
"value": "BI",
"text": "Burundi"
},
{
"value": "GR",
"text": "Greece"
},
{
"value": "PW",
"text": "Palau"
},
{
"value": "CK",
"text": "Cook Islands"
},
{
"value": "CW",
"text": "Curaçao"
},
{
"value": "KY",
"text": "Cayman Islands"
},
{
"value": "DE",
"text": "Germany"
},
{
"value": "IT",
"text": "Italy"
},
{
"value": "SB",
"text": "Solomon Islands"
},
{
"value": "TK",
"text": "Tokelau"
},
{
"value": "LV",
"text": "Latvia"
},
{
"value": "NO",
"text": "Norway"
},
{
"value": "CZ",
"text": "Czech Republic"
},
{
"value": "MD",
"text": "Republic of Moldova"
},
{
"value": "MA",
"text": "Morocco"
},
{
"value": "MC",
"text": "Monaco"
},
{
"value": "BN",
"text": "Brunei Darussalam"
},
{
"value": "FJ",
"text": "Fiji"
},
{
"value": "SZ",
"text": "Eswatini"
},
{
"value": "SK",
"text": "Slovakia"
},
{
"value": "SI",
"text": "Slovenia"
},
{
"value": "SJ",
"text": "Svalbard and Jan Mayen"
},
{
"value": "LK",
"text": "Sri Lanka"
},
{
"value": "SG",
"text": "Singapore"
},
{
"value": "NC",
"text": "New Caledonia"
},
{
"value": "NZ",
"text": "New Zealand"
},
{
"value": "JP",
"text": "Japan"
},
{
"value": "CL",
"text": "Chile"
},
{
"value": "KP",
"text": "North Korea"
},
{
"value": "KH",
"text": "Cambodia"
},
{
"value": "GG",
"text": "Guernsey"
},
{
"value": "GD",
"text": "Grenada"
},
{
"value": "GL",
"text": "Greenland"
},
{
"value": "GE",
"text": "Georgia"
},
{
"value": "VA",
"text": "Vatican"
},
{
"value": "BE",
"text": "Belgium"
},
{
"value": "MR",
"text": "Mauritania"
},
{
"value": "MU",
"text": "Mauritius"
},
{
"value": "TO",
"text": "Tonga"
},
{
"value": "SA",
"text": "Saudi Arabia"
},
{
"value": "FR",
"text": "France"
},
{
"value": "GF",
"text": "French Guiana"
},
{
"value": "PF",
"text": "French Polynesia"
},
{
"value": "FO",
"text": "Faroe Islands"
},
{
"value": "PL",
"text": "Poland"
},
{
"value": "PR",
"text": "Puerto Rico"
},
{
"value": "BA",
"text": "Bosnia and Herzegovina"
},
{
"value": "TH",
"text": "Thailand"
},
{
"value": "JE",
"text": "Jersey"
},
{
"value": "ZW",
"text": "Zimbabwe"
},
{
"value": "HN",
"text": "Honduras"
},
{
"value": "HT",
"text": "Haiti"
},
{
"value": "AU",
"text": "Australia"
},
{
"value": "MO",
"text": "Macao, China"
},
{
"value": "IE",
"text": "Ireland"
},
{
"value": "EE",
"text": "Estonia"
},
{
"value": "JM",
"text": "Jamaica"
},
{
"value": "TC",
"text": "Turks and Caicos Islands"
},
{
"value": "TT",
"text": "Trinidad and Tobago"
},
{
"value": "BO",
"text": "Bolivia"
},
{
"value": "NR",
"text": "Nauru"
},
{
"value": "SE",
"text": "Sweden"
},
{
"value": "CH",
"text": "Switzerland"
},
{
"value": "GP",
"text": "Guadeloupe"
},
{
"value": "WF",
"text": "Wallis and Futuna"
},
{
"value": "VU",
"text": "Vanuatu"
},
{
"value": "RE",
"text": "Réunion"
},
{
"value": "BY",
"text": "Belarus"
},
{
"value": "BM",
"text": "Bermuda"
},
{
"value": "PN",
"text": "Pitcairn Islands"
},
{
"value": "GI",
"text": "Gibraltar"
},
{
"value": "FK",
"text": "Falkland Islands (Malvinas)"
},
{
"value": "KW",
"text": "Kuwait"
},
{
"value": "KM",
"text": "Comoros"
},
{
"value": "CI",
"text": "Côte d'Ivoire"
},
{
"value": "CC",
"text": "Cocos (Keeling) Islands"
},
{
"value": "PE",
"text": "Peru"
},
{
"value": "TN",
"text": "Tunisia"
},
{
"value": "LT",
"text": "Lithuania"
},
{
"value": "SO",
"text": "Somalia"
},
{
"value": "JO",
"text": "Jordan"
},
{
"value": "NA",
"text": "Namibia"
},
{
"value": "NU",
"text": "Niue"
},
{
"value": "MM",
"text": "Myanmar"
},
{
"value": "RO",
"text": "Romania"
},
{
"value": "US",
"text": "United States"
},
{
"value": "VI",
"text": "U.S. Virgin Islands"
},
{
"value": "LA",
"text": "Lao People's Democratic Republic"
},
{
"value": "KE",
"text": "Kenya"
},
{
"value": "FI",
"text": "Finland"
},
{
"value": "SD",
"text": "Sudan"
},
{
"value": "SR",
"text": "Suriname"
},
{
"value": "GB",
"text": "United Kingdom"
},
{
"value": "IO",
"text": "British Indian Ocean Territory"
},
{
"value": "VG",
"text": "British Virgin Islands"
},
{
"value": "NL",
"text": "Netherlands"
},
{
"value": "MZ",
"text": "Mozambique"
},
{
"value": "LS",
"text": "Lesotho"
},
{
"value": "PH",
"text": "Philippines"
},
{
"value": "SV",
"text": "El Salvador"
},
{
"value": "WS",
"text": "Samoa"
},
{
"value": "PT",
"text": "Portugal"
},
{
"value": "MN",
"text": "Mongolia"
},
{
"value": "MS",
"text": "Montserrat"
},
{
"value": "ES",
"text": "Spain"
},
{
"value": "NF",
"text": "Norfolk Island"
},
{
"value": "BJ",
"text": "Benin"
},
{
"value": "ZM",
"text": "Zambia"
},
{
"value": "GQ",
"text": "Equatorial Guinea"
},
{
"value": "VN",
"text": "Viet Nam"
},
{
"value": "AZ",
"text": "Azerbaijan"
},
{
"value": "AF",
"text": "Afghanistan"
},
{
"value": "DZ",
"text": "Algeria"
},
{
"value": "AL",
"text": "Albania"
},
{
"value": "SY",
"text": "Syrian Arab Republic"
},
{
"value": "OM",
"text": "Oman"
},
{
"value": "AR",
"text": "Argentina"
},
{
"value": "AE",
"text": "United Arab Emirates"
},
{
"value": "AW",
"text": "Aruba"
},
{
"value": "HK",
"text": "Hong Kong, China"
},
{
"value": "MK",
"text": "North Macedonia"
},
{
"value": "MV",
"text": "Maldives"
},
{
"value": "IM",
"text": "Isle of Man"
},
{
"value": "MW",
"text": "Malawi"
},
{
"value": "MQ",
"text": "Martinique"
},
{
"value": "MY",
"text": "Malaysia"
},
{
"value": "YT",
"text": "Mayotte"
},
{
"value": "MH",
"text": "Marshall Islands"
},
{
"value": "MT",
"text": "Malta"
},
{
"value": "MG",
"text": "Madagascar"
},
{
"value": "ML",
"text": "Mali"
},
{
"value": "LB",
"text": "Lebanon"
},
{
"value": "ME",
"text": "Montenegro"
}
]

View File

@@ -0,0 +1,978 @@
[
{
"value": "CN:86",
"text": "中国 (+86)"
},
{
"value": "US:1",
"text": "美国 (+1)"
},
{
"value": "CA:1",
"text": "加拿大 (+1)"
},
{
"value": "AU:61",
"text": "澳大利亚 (+61)"
},
{
"value": "JP:81",
"text": "日本 (+81)"
},
{
"value": "MX:52",
"text": "墨西哥 (+52)"
},
{
"value": "GB:44",
"text": "英国 (+44)"
},
{
"value": "DE:49",
"text": "德国 (+49)"
},
{
"value": "FR:33",
"text": "法国 (+33)"
},
{
"value": "AF:93",
"text": "阿富汗 (+93)"
},
{
"value": "AX:358",
"text": "奥兰群岛 (+358)"
},
{
"value": "AL:355",
"text": "阿尔巴尼亚 (+355)"
},
{
"value": "DZ:213",
"text": "阿尔及利亚 (+213)"
},
{
"value": "AS:1",
"text": "东萨摩亚 (+1)"
},
{
"value": "AD:376",
"text": "安道尔 (+376)"
},
{
"value": "AO:244",
"text": "安哥拉 (+244)"
},
{
"value": "AI:1",
"text": "安圭拉 (+1)"
},
{
"value": "AQ:672",
"text": "南极洲 (+672)"
},
{
"value": "AG:1",
"text": "安提瓜和巴布达 (+1)"
},
{
"value": "AR:54",
"text": "阿根廷 (+54)"
},
{
"value": "AM:374",
"text": "亚美尼亚 (+374)"
},
{
"value": "AW:297",
"text": "阿鲁巴 (+297)"
},
{
"value": "AT:43",
"text": "奥地利 (+43)"
},
{
"value": "AZ:994",
"text": "阿塞拜疆 (+994)"
},
{
"value": "BS:1",
"text": "巴哈马 (+1)"
},
{
"value": "BH:973",
"text": "巴林 (+973)"
},
{
"value": "BD:880",
"text": "孟加拉 (+880)"
},
{
"value": "BB:1",
"text": "巴巴多斯 (+1)"
},
{
"value": "BY:375",
"text": "白俄罗斯 (+375)"
},
{
"value": "BE:32",
"text": "比利时 (+32)"
},
{
"value": "BZ:501",
"text": "伯利兹 (+501)"
},
{
"value": "BJ:229",
"text": "贝宁 (+229)"
},
{
"value": "BM:1",
"text": "百慕大 (+1)"
},
{
"value": "BT:975",
"text": "不丹 (+975)"
},
{
"value": "BO:591",
"text": "玻利维亚 (+591)"
},
{
"value": "BQ:599",
"text": "博内尔岛;圣尤斯特歇斯和萨巴 (+599)"
},
{
"value": "BA:387",
"text": "波斯尼亚和黑塞哥维那 (+387)"
},
{
"value": "BW:267",
"text": "博茨瓦纳 (+267)"
},
{
"value": "BR:55",
"text": "巴西 (+55)"
},
{
"value": "IO:246",
"text": "英属印度洋领地 (+246)"
},
{
"value": "BN:673",
"text": "文莱达鲁萨兰国 (+673)"
},
{
"value": "BG:359",
"text": "保加利亚 (+359)"
},
{
"value": "BF:226",
"text": "布吉纳法索 (+226)"
},
{
"value": "BI:257",
"text": "布隆迪 (+257)"
},
{
"value": "KH:855",
"text": "柬埔寨 (+855)"
},
{
"value": "CM:237",
"text": "喀麦隆 (+237)"
},
{
"value": "CV:238",
"text": "佛得角 (+238)"
},
{
"value": "KY:1",
"text": "开曼群岛 (+1)"
},
{
"value": "CF:236",
"text": "中非共和国 (+236)"
},
{
"value": "TD:235",
"text": "乍得 (+235)"
},
{
"value": "CL:56",
"text": "智利 (+56)"
},
{
"value": "CX:61",
"text": "圣诞岛 (+61)"
},
{
"value": "CC:61",
"text": "科科斯 (基林) 群岛 (+61)"
},
{
"value": "CO:57",
"text": "哥伦比亚 (+57)"
},
{
"value": "KM:269",
"text": "科摩罗 (+269)"
},
{
"value": "CG:242",
"text": "刚果 (+243)"
},
{
"value": "CD:243",
"text": "刚果民主共和国 (+243)"
},
{
"value": "CK:682",
"text": "库克群岛 (+682)"
},
{
"value": "CR:506",
"text": "哥斯达黎加 (+506)"
},
{
"value": "CI:225",
"text": "科特迪瓦 (+225)"
},
{
"value": "HR:385",
"text": "克罗地亚 (+385)"
},
{
"value": "CU:53",
"text": "古巴 (+53)"
},
{
"value": "CW:599",
"text": "库拉索岛 (+599)"
},
{
"value": "CZ:420",
"text": "捷克共和国 (+420)"
},
{
"value": "DK:45",
"text": "丹麦 (+45)"
},
{
"value": "DJ:253",
"text": "吉布提 (+253)"
},
{
"value": "DM:1",
"text": "多米尼克 (+1)"
},
{
"value": "DO:1",
"text": "多米尼克共和国 (+1)"
},
{
"value": "EC:593",
"text": "厄瓜多尔 (+593)"
},
{
"value": "EG:20",
"text": "埃及 (+20)"
},
{
"value": "SV:503",
"text": "萨尔瓦多 (+503)"
},
{
"value": "GQ:240",
"text": "赤道几内亚 (+240)"
},
{
"value": "ER:291",
"text": "厄立特里亚 (+291)"
},
{
"value": "EE:372",
"text": "爱沙尼亚 (+372)"
},
{
"value": "ET:251",
"text": "埃塞俄比亚 (+251)"
},
{
"value": "FK:500",
"text": "福克兰群岛 (马尔维纳斯群岛) (+500)"
},
{
"value": "FO:298",
"text": "法罗群岛 (+298)"
},
{
"value": "FJ:679",
"text": "斐济 (+679)"
},
{
"value": "FI:358",
"text": "芬兰 (+358)"
},
{
"value": "GY:592",
"text": "法属圭亚那 (+592)"
},
{
"value": "PF:689",
"text": "法属波利尼西亚 (+689)"
},
{
"value": "GA:241",
"text": "加蓬 (+241)"
},
{
"value": "GM:220",
"text": "冈比亚 (+220)"
},
{
"value": "GE:995",
"text": "格鲁吉亚 (+995)"
},
{
"value": "GH:233",
"text": "加纳 (+233)"
},
{
"value": "GI:350",
"text": "直布罗陀 (+350)"
},
{
"value": "GR:30",
"text": "希腊 (+30)"
},
{
"value": "GL:299",
"text": "格陵兰岛 (+299)"
},
{
"value": "GD:1",
"text": "格林纳达 (+1)"
},
{
"value": "GP:590",
"text": "瓜德罗普岛 (+590)"
},
{
"value": "GU:1",
"text": "关岛 (+1)"
},
{
"value": "GT:502",
"text": "危地马拉 (+502)"
},
{
"value": "GG:44",
"text": "根西岛 (+44)"
},
{
"value": "GN:224",
"text": "几内亚 (+224)"
},
{
"value": "GW:245",
"text": "几内亚比绍 (+245)"
},
{
"value": "GY:592",
"text": "圭亚那 (+592)"
},
{
"value": "HT:509",
"text": "海地 (+509)"
},
{
"value": "HN:504",
"text": "洪都拉斯 (+504)"
},
{
"value": "HK:852",
"text": "中国香港 (+852)"
},
{
"value": "HU:36",
"text": "匈牙利 (+36)"
},
{
"value": "IS:354",
"text": "冰岛 (+354)"
},
{
"value": "IN:91",
"text": "印度 (+91)"
},
{
"value": "ID:62",
"text": "印尼 (+62)"
},
{
"value": "IR:98",
"text": "伊朗,伊斯兰共和国 (+98)"
},
{
"value": "IQ:964",
"text": "伊拉克 (+964)"
},
{
"value": "IE:353",
"text": "爱尔兰 (+353)"
},
{
"value": "IM:44",
"text": "马恩岛 (+44)"
},
{
"value": "IL:972",
"text": "以色列 (+972)"
},
{
"value": "IT:39",
"text": "意大利 (+39)"
},
{
"value": "JM:1",
"text": "牙买加 (+1)"
},
{
"value": "JE:44",
"text": "泽西岛 (+44)"
},
{
"value": "JO:962",
"text": "约旦 (+962)"
},
{
"value": "KZ:7",
"text": "哈萨克斯坦 (+7)"
},
{
"value": "KE:254",
"text": "肯尼亚 (+254)"
},
{
"value": "KI:686",
"text": "基里巴斯 (+686)"
},
{
"value": "KP:850",
"text": "朝鲜民主人民共和国 (+850)"
},
{
"value": "KR:82",
"text": "大韩民国 (+82)"
},
{
"value": "KW:965",
"text": "科威特 (+965)"
},
{
"value": "KG:996",
"text": "吉尔吉斯斯坦 (+996)"
},
{
"value": "LA:856",
"text": "老挝人民民主共和国 (+856)"
},
{
"value": "LV:371",
"text": "拉托维亚 (+371)"
},
{
"value": "LB:961",
"text": "黎巴嫩 (+961)"
},
{
"value": "LS:266",
"text": "莱索托 (+266)"
},
{
"value": "LR:231",
"text": "利比里亚 (+231)"
},
{
"value": "LY:218",
"text": "利比亚 (+218)"
},
{
"value": "LI:423",
"text": "列支敦士登 (+423)"
},
{
"value": "LT:370",
"text": "立陶宛 (+370)"
},
{
"value": "LU:352",
"text": "卢森堡 (+352)"
},
{
"value": "MO:853",
"text": "中国澳门 (+853)"
},
{
"value": "MK:389",
"text": "马其顿 (+389)"
},
{
"value": "MG:261",
"text": "马达加斯加 (+261)"
},
{
"value": "MW:265",
"text": "马拉维 (+265)"
},
{
"value": "MY:60",
"text": "马来西亚 (+60)"
},
{
"value": "MV:960",
"text": "马尔代夫 (+960)"
},
{
"value": "ML:223",
"text": "马里 (+223)"
},
{
"value": "MT:356",
"text": "马耳他 (+356)"
},
{
"value": "MH:692",
"text": "马绍尔群岛 (+692)"
},
{
"value": "MQ:596",
"text": "马提尼克岛 (+596)"
},
{
"value": "MR:222",
"text": "毛里塔尼亚 (+222)"
},
{
"value": "MU:230",
"text": "毛里求斯 (+230)"
},
{
"value": "YT:262",
"text": "马约特岛 (+262)"
},
{
"value": "FM:691",
"text": "密克罗尼西亚联邦国 (+691)"
},
{
"value": "MD:373",
"text": "摩尔多瓦共和国 (+373)"
},
{
"value": "MC:377",
"text": "摩纳哥 (+377)"
},
{
"value": "MN:976",
"text": "蒙古 (+976)"
},
{
"value": "ME:382",
"text": "黑山 (+382)"
},
{
"value": "MS:1",
"text": "蒙特塞拉特 (+1)"
},
{
"value": "MA:212",
"text": "摩洛哥 (+212)"
},
{
"value": "MZ:258",
"text": "莫桑比克 (+258)"
},
{
"value": "MM:95",
"text": "缅甸 (+95)"
},
{
"value": "NA:264",
"text": "纳米比亚 (+264)"
},
{
"value": "NR:674",
"text": "瑙鲁 (+674)"
},
{
"value": "NP:977",
"text": "尼泊尔 (+977)"
},
{
"value": "NL:31",
"text": "荷兰 (+31)"
},
{
"value": "NC:687",
"text": "新喀里多尼亚 (+687)"
},
{
"value": "NZ:64",
"text": "新西兰 (+64)"
},
{
"value": "NI:505",
"text": "尼加拉瓜 (+505)"
},
{
"value": "NE:227",
"text": "尼日尔 (+227)"
},
{
"value": "NG:234",
"text": "尼日利亚 (+234)"
},
{
"value": "NU:683",
"text": "纽埃 (+683)"
},
{
"value": "NF:672",
"text": "诺福克岛 (+672)"
},
{
"value": "MP:1",
"text": "北马里亚纳群岛 (+1)"
},
{
"value": "NO:47",
"text": "挪威 (+47)"
},
{
"value": "OM:968",
"text": "阿曼 (+968)"
},
{
"value": "PK:92",
"text": "巴基斯坦 (+92)"
},
{
"value": "PW:680",
"text": "帕劳 (+680)"
},
{
"value": "PS:970",
"text": "巴勒斯坦领土 (+970)"
},
{
"value": "PA:507",
"text": "巴拿马 (+507)"
},
{
"value": "PG:675",
"text": "巴布亚新几内亚 (+675)"
},
{
"value": "PY:595",
"text": "巴拉圭 (+595)"
},
{
"value": "PE:51",
"text": "秘鲁 (+51)"
},
{
"value": "PH:63",
"text": "菲律宾 (+63)"
},
{
"value": "PN:64",
"text": "皮特凯恩群岛 (+64)"
},
{
"value": "PL:48",
"text": "波兰 (+48)"
},
{
"value": "PT:351",
"text": "葡萄牙 (+351)"
},
{
"value": "PR:1",
"text": "波多黎各 (+1)"
},
{
"value": "QA:974",
"text": "卡塔尔 (+974)"
},
{
"value": "CY:357",
"text": "塞浦路斯共和国 (+357)"
},
{
"value": "TF:262",
"text": "留尼汪 (+262)"
},
{
"value": "RO:40",
"text": "罗马尼亚 (+40)"
},
{
"value": "RU:7",
"text": "俄罗斯联邦 (+7)"
},
{
"value": "RW:250",
"text": "卢旺达 (+250)"
},
{
"value": "BL:590",
"text": "圣巴泰勒米 (+590)"
},
{
"value": "SH:290",
"text": "圣海伦娜 (+290)"
},
{
"value": "KN:1",
"text": "圣基茨和尼维斯 (+1)"
},
{
"value": "LC:1",
"text": "圣卢西亚 (+1)"
},
{
"value": "MF:590",
"text": "圣马丁岛 (+590)"
},
{
"value": "PM:508",
"text": "圣皮埃尔和密克隆 (+508)"
},
{
"value": "VC:1",
"text": "圣文森特和格林纳丁斯 (+1)"
},
{
"value": "WS:685",
"text": "萨摩亚 (+685)"
},
{
"value": "SM:378",
"text": "圣马利诺 (+378)"
},
{
"value": "ST:239",
"text": "圣多美和普林西比 (+239)"
},
{
"value": "SA:966",
"text": "沙特阿拉伯 (+966)"
},
{
"value": "SN:221",
"text": "塞内加尔 (+221)"
},
{
"value": "RS:381",
"text": "塞尔维亚 (+381)"
},
{
"value": "SC:248",
"text": "塞舌尔 (+248)"
},
{
"value": "SL:232",
"text": "塞拉利昂 (+232)"
},
{
"value": "SG:65",
"text": "新加坡 (+65)"
},
{
"value": "MF:590",
"text": "圣马丁 (荷兰领地) (+590)"
},
{
"value": "SK:421",
"text": "斯洛伐克 (+421)"
},
{
"value": "SI:386",
"text": "斯洛文尼亚 (+386)"
},
{
"value": "SB:677",
"text": "所罗门群岛 (+677)"
},
{
"value": "SO:252",
"text": "索马里 (+252)"
},
{
"value": "ZA:27",
"text": "南非 (+27)"
},
{
"value": "GS:500",
"text": "南乔治亚岛和南桑威奇群岛 (+500)"
},
{
"value": "SS:211",
"text": "南苏丹 (+211)"
},
{
"value": "ES:34",
"text": "西班牙 (+34)"
},
{
"value": "LK:94",
"text": "斯里兰卡 (+94)"
},
{
"value": "SD:249",
"text": "苏丹 (+249)"
},
{
"value": "SR:597",
"text": "苏里南 (+597)"
},
{
"value": "SJ:47",
"text": "斯瓦尔巴和扬马延岛 (+47)"
},
{
"value": "SZ:268",
"text": "斯威士兰 (+268)"
},
{
"value": "SE:46",
"text": "瑞典 (+46)"
},
{
"value": "CH:41",
"text": "瑞士 (+41)"
},
{
"value": "SY:963",
"text": "阿拉伯叙利亚共和国 (+963)"
},
{
"value": "TW:886",
"text": "中国台湾 (+886)"
},
{
"value": "TJ:992",
"text": "塔吉克斯坦 (+992)"
},
{
"value": "TZ:255",
"text": "坦桑尼亚联合共和国 (+255)"
},
{
"value": "TH:66",
"text": "泰国 (+66)"
},
{
"value": "TL:670",
"text": "东帝汶 (+670)"
},
{
"value": "TG:228",
"text": "多哥 (+228)"
},
{
"value": "TK:690",
"text": "托克劳 (+690)"
},
{
"value": "TO:676",
"text": "汤加 (+676)"
},
{
"value": "TT:1",
"text": "特立尼达和多巴哥 (+1)"
},
{
"value": "TN:216",
"text": "突尼西亚 (+216)"
},
{
"value": "TR:90",
"text": "土耳其 (+90)"
},
{
"value": "TM:993",
"text": "土库曼斯坦 (+993)"
},
{
"value": "TC:1",
"text": "特克斯和凯科斯群岛 (+1)"
},
{
"value": "TV:688",
"text": "图瓦卢 (+688)"
},
{
"value": "UG:256",
"text": "乌干达 (+256)"
},
{
"value": "UA:380",
"text": "乌克兰 (+380)"
},
{
"value": "AE:971",
"text": "阿联酋 (+971)"
},
{
"value": "UY:598",
"text": "乌拉圭 (+598)"
},
{
"value": "UZ:998",
"text": "乌兹别克斯坦 (+998)"
},
{
"value": "VU:678",
"text": "瓦努阿图 (+678)"
},
{
"value": "VA:379",
"text": "梵蒂冈 (+379)"
},
{
"value": "VE:58",
"text": "委内瑞拉 (+58)"
},
{
"value": "VN:84",
"text": "越南 (+84)"
},
{
"value": "VG:1",
"text": "英属维京群岛 (+1)"
},
{
"value": "VI:1",
"text": "美属维京群岛 (+1)"
},
{
"value": "WF:681",
"text": "瓦利斯和富图纳群岛 (+681)"
},
{
"value": "YE:967",
"text": "也门 (+967)"
},
{
"value": "ZM:260",
"text": "赞比亚 (+260)"
},
{
"value": "ZW:263",
"text": "津巴布韦 (+263)"
}
]

View File

@@ -0,0 +1,979 @@
[
{
"value": "CN:86",
"text": "China (+86)"
},
{
"value": "US:1",
"text": "United States (+1)"
},
{
"value": "CA:1",
"text": "Canada (+1)"
},
{
"value": "AU:61",
"text": "Australia (+61)"
},
{
"value": "JP:81",
"text": "Japan (+81)"
},
{
"value": "MX:52",
"text": "Mexico (+52)"
},
{
"value": "GB:44",
"text": "United Kingdom (+44)"
},
{
"value": "DE:49",
"text": "Germany (+49)"
},
{
"value": "FR:33",
"text": "France (+33)"
},
{
"value": "AF:93",
"text": "Afghanistan (+93)"
},
{
"value": "AX:358",
"text": "Åland Islands (+358)"
},
{
"value": "AL:355",
"text": "Albania (+355)"
},
{
"value": "DZ:213",
"text": "Algeria (+213)"
},
{
"value": "AS:1",
"text": "American Samoa (+1)"
},
{
"value": "AD:376",
"text": "Andorra (+376)"
},
{
"value": "AO:244",
"text": "Angola (+244)"
},
{
"value": "AI:1",
"text": "Anguilla (+1)"
},
{
"value": "AQ:672",
"text": "Antarctica (+672)"
},
{
"value": "AG:1",
"text": "Antigua and Barbuda (+1)"
},
{
"value": "AR:54",
"text": "Argentina (+54)"
},
{
"value": "AM:374",
"text": "Armenia (+374)"
},
{
"value": "AW:297",
"text": "Aruba (+297)"
},
{
"value": "AT:43",
"text": "Austria (+43)"
},
{
"value": "AZ:994",
"text": "Azerbaijan (+994)"
},
{
"value": "BS:1",
"text": "Bahamas (+1)"
},
{
"value": "BH:973",
"text": "Bahrain (+973)"
},
{
"value": "BD:880",
"text": "Bangladesh (+880)"
},
{
"value": "BB:1",
"text": "Barbados (+1)"
},
{
"value": "BY:375",
"text": "Belarus (+375)"
},
{
"value": "BE:32",
"text": "Belgium (+32)"
},
{
"value": "BZ:501",
"text": "Belize (+501)"
},
{
"value": "BJ:229",
"text": "Benin (+229)"
},
{
"value": "BM:1",
"text": "Bermuda (+1)"
},
{
"value": "BT:975",
"text": "Bhutan (+975)"
},
{
"value": "BO:591",
"text": "Bolivia (+591)"
},
{
"value": "BQ:599",
"text": "Bonaire, Sint Eustatius and Saba (+599)"
},
{
"value": "BA:387",
"text": "Bosnia and Herzegovina (+387)"
},
{
"value": "BW:267",
"text": "Botswana (+267)"
},
{
"value": "BR:55",
"text": "Brazil (+55)"
},
{
"value": "IO:246",
"text": "British Indian Ocean Territory (+246)"
},
{
"value": "BN:673",
"text": "Brunei Darussalam (+673)"
},
{
"value": "BG:359",
"text": "Bulgaria (+359)"
},
{
"value": "BF:226",
"text": "Burkina Faso (+226)"
},
{
"value": "BI:257",
"text": "Burundi (+257)"
},
{
"value": "KH:855",
"text": "Cambodia (+855)"
},
{
"value": "CM:237",
"text": "Cameroon (+237)"
},
{
"value": "CV:238",
"text": "Cape Verde (+238)"
},
{
"value": "KY:1",
"text": "Cayman Islands (+1)"
},
{
"value": "CF:236",
"text": "Central African Republic (+236)"
},
{
"value": "TD:235",
"text": "Chad (+235)"
},
{
"value": "CL:56",
"text": "Chile (+56)"
},
{
"value": "CX:61",
"text": "Christmas Island (+61)"
},
{
"value": "CC:61",
"text": "Cocos (Keeling) Islands (+61)"
},
{
"value": "CO:57",
"text": "Colombia (+57)"
},
{
"value": "KM:269",
"text": "Comoros (+269)"
},
{
"value": "CG:242",
"text": "Congo (+242)"
},
{
"value": "CD:243",
"text": "Democratic Republic of the Congo (+243)"
},
{
"value": "CK:682",
"text": "Cook Islands (+682)"
},
{
"value": "CR:506",
"text": "Costa Rica (+506)"
},
{
"value": "CI:225",
"text": "Côte d'Ivoire (+225)"
},
{
"value": "HR:385",
"text": "Croatia (+385)"
},
{
"value": "CU:53",
"text": "Cuba (+53)"
},
{
"value": "CW:599",
"text": "Curaçao (+599)"
},
{
"value": "CZ:420",
"text": "Czech Republic (+420)"
},
{
"value": "DK:45",
"text": "Denmark (+45)"
},
{
"value": "DJ:253",
"text": "Djibouti (+253)"
},
{
"value": "DM:1",
"text": "Dominica (+1)"
},
{
"value": "DO:1",
"text": "Dominican Republic (+1)"
},
{
"value": "EC:593",
"text": "Ecuador (+593)"
},
{
"value": "EG:20",
"text": "Egypt (+20)"
},
{
"value": "SV:503",
"text": "El Salvador (+503)"
},
{
"value": "GQ:240",
"text": "Equatorial Guinea (+240)"
},
{
"value": "ER:291",
"text": "Eritrea (+291)"
},
{
"value": "EE:372",
"text": "Estonia (+372)"
},
{
"value": "ET:251",
"text": "Ethiopia (+251)"
},
{
"value": "FK:500",
"text": "Falkland Islands (Malvinas) (+500)"
},
{
"value": "FO:298",
"text": "Faroe Islands (+298)"
},
{
"value": "FJ:679",
"text": "Fiji (+679)"
},
{
"value": "FI:358",
"text": "Finland (+358)"
},
{
"value": "GF:594",
"text": "French Guiana (+594)"
},
{
"value": "PF:689",
"text": "French Polynesia (+689)"
},
{
"value": "GA:241",
"text": "Gabon (+241)"
},
{
"value": "GM:220",
"text": "Gambia (+220)"
},
{
"value": "GE:995",
"text": "Georgia (+995)"
},
{
"value": "GH:233",
"text": "Ghana (+233)"
},
{
"value": "GI:350",
"text": "Gibraltar (+350)"
},
{
"value": "GR:30",
"text": "Greece (+30)"
},
{
"value": "GL:299",
"text": "Greenland (+299)"
},
{
"value": "GD:1",
"text": "Grenada (+1)"
},
{
"value": "GP:590",
"text": "Guadeloupe (+590)"
},
{
"value": "GU:1",
"text": "Guam (+1)"
},
{
"value": "GT:502",
"text": "Guatemala (+502)"
},
{
"value": "GG:44",
"text": "Guernsey (+44)"
},
{
"value": "GN:224",
"text": "Guinea (+224)"
},
{
"value": "GW:245",
"text": "Guinea-Bissau (+245)"
},
{
"value": "GY:592",
"text": "Guyana (+592)"
},
{
"value": "HT:509",
"text": "Haiti (+509)"
},
{
"value": "HN:504",
"text": "Honduras (+504)"
},
{
"value": "HK:852",
"text": "Hong Kong, China (+852)"
},
{
"value": "HU:36",
"text": "Hungary (+36)"
},
{
"value": "IS:354",
"text": "Iceland (+354)"
},
{
"value": "IN:91",
"text": "India (+91)"
},
{
"value": "ID:62",
"text": "Indonesia (+62)"
},
{
"value": "IR:98",
"text": "Iran, Islamic Republic of (+98)"
},
{
"value": "IQ:964",
"text": "Iraq (+964)"
},
{
"value": "IE:353",
"text": "Ireland (+353)"
},
{
"value": "IM:44",
"text": "Isle of Man (+44)"
},
{
"value": "IL:972",
"text": "Israel (+972)"
},
{
"value": "IT:39",
"text": "Italy (+39)"
},
{
"value": "JM:1",
"text": "Jamaica (+1)"
},
{
"value": "JE:44",
"text": "Jersey (+44)"
},
{
"value": "JO:962",
"text": "Jordan (+962)"
},
{
"value": "KZ:7",
"text": "Kazakhstan (+7)"
},
{
"value": "KE:254",
"text": "Kenya (+254)"
},
{
"value": "KI:686",
"text": "Kiribati (+686)"
},
{
"value": "KP:850",
"text": "North Korea (+850)"
},
{
"value": "KR:82",
"text": "South Korea (+82)"
},
{
"value": "KW:965",
"text": "Kuwait (+965)"
},
{
"value": "KG:996",
"text": "Kyrgyzstan (+996)"
},
{
"value": "LA:856",
"text": "Lao People's Democratic Republic (+856)"
},
{
"value": "LV:371",
"text": "Latvia (+371)"
},
{
"value": "LB:961",
"text": "Lebanon (+961)"
},
{
"value": "LS:266",
"text": "Lesotho (+266)"
},
{
"value": "LR:231",
"text": "Liberia (+231)"
},
{
"value": "LY:218",
"text": "Libya (+218)"
},
{
"value": "LI:423",
"text": "Liechtenstein (+423)"
},
{
"value": "LT:370",
"text": "Lithuania (+370)"
},
{
"value": "LU:352",
"text": "Luxembourg (+352)"
},
{
"value": "MO:853",
"text": "Macao, China (+853)"
},
{
"value": "MK:389",
"text": "North Macedonia (+389)"
},
{
"value": "MG:261",
"text": "Madagascar (+261)"
},
{
"value": "MW:265",
"text": "Malawi (+265)"
},
{
"value": "MY:60",
"text": "Malaysia (+60)"
},
{
"value": "MV:960",
"text": "Maldives (+960)"
},
{
"value": "ML:223",
"text": "Mali (+223)"
},
{
"value": "MT:356",
"text": "Malta (+356)"
},
{
"value": "MH:692",
"text": "Marshall Islands (+692)"
},
{
"value": "MQ:596",
"text": "Martinique (+596)"
},
{
"value": "MR:222",
"text": "Mauritania (+222)"
},
{
"value": "MU:230",
"text": "Mauritius (+230)"
},
{
"value": "YT:262",
"text": "Mayotte (+262)"
},
{
"value": "FM:691",
"text": "Federated States of Micronesia (+691)"
},
{
"value": "MD:373",
"text": "Republic of Moldova (+373)"
},
{
"value": "MC:377",
"text": "Monaco (+377)"
},
{
"value": "MN:976",
"text": "Mongolia (+976)"
},
{
"value": "ME:382",
"text": "Montenegro (+382)"
},
{
"value": "MS:1",
"text": "Montserrat (+1)"
},
{
"value": "MA:212",
"text": "Morocco (+212)"
},
{
"value": "MZ:258",
"text": "Mozambique (+258)"
},
{
"value": "MM:95",
"text": "Myanmar (+95)"
},
{
"value": "NA:264",
"text": "Namibia (+264)"
},
{
"value": "NR:674",
"text": "Nauru (+674)"
},
{
"value": "NP:977",
"text": "Nepal (+977)"
},
{
"value": "NL:31",
"text": "Netherlands (+31)"
},
{
"value": "NC:687",
"text": "New Caledonia (+687)"
},
{
"value": "NZ:64",
"text": "New Zealand (+64)"
},
{
"value": "NI:505",
"text": "Nicaragua (+505)"
},
{
"value": "NE:227",
"text": "Niger (+227)"
},
{
"value": "NG:234",
"text": "Nigeria (+234)"
},
{
"value": "NU:683",
"text": "Niue (+683)"
},
{
"value": "NF:672",
"text": "Norfolk Island (+672)"
},
{
"value": "MP:1",
"text": "Northern Mariana Islands (+1)"
},
{
"value": "NO:47",
"text": "Norway (+47)"
},
{
"value": "OM:968",
"text": "Oman (+968)"
},
{
"value": "PK:92",
"text": "Pakistan (+92)"
},
{
"value": "PW:680",
"text": "Palau (+680)"
},
{
"value": "PS:970",
"text": "Palestinian Territory (+970)"
},
{
"value": "PA:507",
"text": "Panama (+507)"
},
{
"value": "PG:675",
"text": "Papua New Guinea (+675)"
},
{
"value": "PY:595",
"text": "Paraguay (+595)"
},
{
"value": "PE:51",
"text": "Peru (+51)"
},
{
"value": "PH:63",
"text": "Philippines (+63)"
},
{
"value": "PN:64",
"text": "Pitcairn Islands (+64)"
},
{
"value": "PL:48",
"text": "Poland (+48)"
},
{
"value": "PT:351",
"text": "Portugal (+351)"
},
{
"value": "PR:1",
"text": "Puerto Rico (+1)"
},
{
"value": "QA:974",
"text": "Qatar (+974)"
},
{
"value": "CY:357",
"text": "Cyprus (+357)"
},
{
"value": "RE:262",
"text": "Réunion (+262)"
},
{
"value": "RO:40",
"text": "Romania (+40)"
},
{
"value": "RU:7",
"text": "Russian Federation (+7)"
},
{
"value": "RW:250",
"text": "Rwanda (+250)"
},
{
"value": "BL:590",
"text": "Saint Barthélemy (+590)"
},
{
"value": "SH:290",
"text": "Saint Helena (+290)"
},
{
"value": "KN:1",
"text": "Saint Kitts and Nevis (+1)"
},
{
"value": "LC:1",
"text": "Saint Lucia (+1)"
},
{
"value": "MF:590",
"text": "Saint Martin (French part) (+590)"
},
{
"value": "PM:508",
"text": "Saint Pierre and Miquelon (+508)"
},
{
"value": "VC:1",
"text": "Saint Vincent and the Grenadines (+1)"
},
{
"value": "WS:685",
"text": "Samoa (+685)"
},
{
"value": "SM:378",
"text": "San Marino (+378)"
},
{
"value": "ST:239",
"text": "Sao Tome and Principe (+239)"
},
{
"value": "SA:966",
"text": "Saudi Arabia (+966)"
},
{
"value": "SN:221",
"text": "Senegal (+221)"
},
{
"value": "RS:381",
"text": "Serbia (+381)"
},
{
"value": "SC:248",
"text": "Seychelles (+248)"
},
{
"value": "SL:232",
"text": "Sierra Leone (+232)"
},
{
"value": "SG:65",
"text": "Singapore (+65)"
},
{
"value": "SX:599",
"text": "Sint Maarten (Dutch part) (+599)"
},
{
"value": "SK:421",
"text": "Slovakia (+421)"
},
{
"value": "SI:386",
"text": "Slovenia (+386)"
},
{
"value": "SB:677",
"text": "Solomon Islands (+677)"
},
{
"value": "SO:252",
"text": "Somalia (+252)"
},
{
"value": "ZA:27",
"text": "South Africa (+27)"
},
{
"value": "GS:500",
"text": "South Georgia and the South Sandwich Islands (+500)"
},
{
"value": "SS:211",
"text": "South Sudan (+211)"
},
{
"value": "ES:34",
"text": "Spain (+34)"
},
{
"value": "LK:94",
"text": "Sri Lanka (+94)"
},
{
"value": "SD:249",
"text": "Sudan (+249)"
},
{
"value": "SR:597",
"text": "Suriname (+597)"
},
{
"value": "SJ:47",
"text": "Svalbard and Jan Mayen (+47)"
},
{
"value": "SZ:268",
"text": "Eswatini (+268)"
},
{
"value": "SE:46",
"text": "Sweden (+46)"
},
{
"value": "CH:41",
"text": "Switzerland (+41)"
},
{
"value": "SY:963",
"text": "Syrian Arab Republic (+963)"
},
{
"value": "TW:886",
"text": "Taiwan, China (+886)"
},
{
"value": "TJ:992",
"text": "Tajikistan (+992)"
},
{
"value": "TZ:255",
"text": "United Republic of Tanzania (+255)"
},
{
"value": "TH:66",
"text": "Thailand (+66)"
},
{
"value": "TL:670",
"text": "East Timor (+670)"
},
{
"value": "TG:228",
"text": "Togo (+228)"
},
{
"value": "TK:690",
"text": "Tokelau (+690)"
},
{
"value": "TO:676",
"text": "Tonga (+676)"
},
{
"value": "TT:1",
"text": "Trinidad and Tobago (+1)"
},
{
"value": "TN:216",
"text": "Tunisia (+216)"
},
{
"value": "TR:90",
"text": "Turkey (+90)"
},
{
"value": "TM:993",
"text": "Turkmenistan (+993)"
},
{
"value": "TC:1",
"text": "Turks and Caicos Islands (+1)"
},
{
"value": "TV:688",
"text": "Tuvalu (+688)"
},
{
"value": "UG:256",
"text": "Uganda (+256)"
},
{
"value": "UA:380",
"text": "Ukraine (+380)"
},
{
"value": "AE:971",
"text": "United Arab Emirates (+971)"
},
{
"value": "UY:598",
"text": "Uruguay (+598)"
},
{
"value": "UZ:998",
"text": "Uzbekistan (+998)"
},
{
"value": "VU:678",
"text": "Vanuatu (+678)"
},
{
"value": "VA:379",
"text": "Vatican (+379)"
},
{
"value": "VE:58",
"text": "Venezuela (+58)"
},
{
"value": "VN:84",
"text": "Viet Nam (+84)"
},
{
"value": "VG:1",
"text": "British Virgin Islands (+1)"
},
{
"value": "VI:1",
"text": "U.S. Virgin Islands (+1)"
},
{
"value": "WF:681",
"text": "Wallis and Futuna (+681)"
},
{
"value": "YE:967",
"text": "Yemen (+967)"
},
{
"value": "ZM:260",
"text": "Zambia (+260)"
},
{
"value": "ZW:263",
"text": "Zimbabwe (+263)"
}
]

234
public/json/us.json Normal file
View File

@@ -0,0 +1,234 @@
[
{
"value": "AL",
"text": "阿拉巴马州(ALABAMA)"
},
{
"value": "AK",
"text": "阿拉斯加(ALASKA)"
},
{
"value": "AS",
"text": "美属萨摩亚(AMERICAN SAMOA)"
},
{
"value": "AZ",
"text": "亚利桑那州(ARIZONA)"
},
{
"value": "AR",
"text": "阿肯色州(ARKANSAS)"
},
{
"value": "CA",
"text": "加利福尼亚州(CALIFORNIA)"
},
{
"value": "CO",
"text": "科罗拉多州(COLORADO)"
},
{
"value": "CT",
"text": "康涅狄格州(CONNECTICUT)"
},
{
"value": "DE",
"text": "特拉华州(DELAWARE)"
},
{
"value": "DC",
"text": "哥伦比亚特区(DISTRICT OF COLUMBIA)"
},
{
"value": "FL",
"text": "佛罗里达(FLORIDA)"
},
{
"value": "GA",
"text": "格鲁吉亚(GEORGIA)"
},
{
"value": "GU",
"text": "关岛(GUAM)"
},
{
"value": "HI",
"text": "夏威夷(HAWAII)"
},
{
"value": "ID",
"text": "爱达荷州(IDAHO)"
},
{
"value": "IL",
"text": "伊利诺伊州(ILLINOIS)"
},
{
"value": "IN",
"text": "印地安那(INDIANA)"
},
{
"value": "IA",
"text": "爱荷华州(IOWA)"
},
{
"value": "KS",
"text": "堪萨斯州(KANSAS)"
},
{
"value": "KY",
"text": "肯塔基州(KENTUCKY)"
},
{
"value": "LA",
"text": "路易斯安那州(LOUISIANA)"
},
{
"value": "ME",
"text": "缅因州(MAINE)"
},
{
"value": "MD",
"text": "马里兰州(MARYLAND)"
},
{
"value": "MA",
"text": "马萨诸塞州(MASSACHUSETTS)"
},
{
"value": "MI",
"text": "密歇根州(MICHIGAN)"
},
{
"value": "MN",
"text": "明尼苏达州(MINNESOTA)"
},
{
"value": "MS",
"text": "密西西比州(MISSISSIPPI)"
},
{
"value": "MO",
"text": "密苏里州(MISSOURI)"
},
{
"value": "MT",
"text": "蒙大拿州(MONTANA)"
},
{
"value": "NE",
"text": "内布拉斯加州(NEBRASKA)"
},
{
"value": "NV",
"text": "内华达州(NEVADA)"
},
{
"value": "NH",
"text": "新汉普郡(NEW HAMPSHIRE)"
},
{
"value": "NJ",
"text": "新泽西州(NEW JERSEY)"
},
{
"value": "NM",
"text": "新墨西哥(NEW MEXICO)"
},
{
"value": "NY",
"text": "纽约(NEW YORK)"
},
{
"value": "NC",
"text": "北卡罗莱纳州(NORTH CAROLINA)"
},
{
"value": "ND",
"text": "北达科塔州(NORTH DAKOTA)"
},
{
"value": "MP",
"text": "北马里亚纳群岛(NORTHERN MARIANA ISLANDS)"
},
{
"value": "OH",
"text": "俄亥俄州(OHIO)"
},
{
"value": "OK",
"text": "奥克拉荷马(OKLAHOMA)"
},
{
"value": "OR",
"text": "俄勒冈州(OREGON)"
},
{
"value": "PA",
"text": "宾夕法尼亚州(PENNSYLVANIA)"
},
{
"value": "PR",
"text": "波多黎各(PUERTO RICO)"
},
{
"value": "RI",
"text": "罗德岛(RHODE ISLAND)"
},
{
"value": "SC",
"text": "南卡罗莱纳州(SOUTH CAROLINA)"
},
{
"value": "SD",
"text": "南达科他州(SOUTH DAKOTA)"
},
{
"value": "TN",
"text": "田纳西州(TENNESSEE)"
},
{
"value": "TX",
"text": "德克萨斯州(TEXAS)"
},
{
"value": "UM",
"text": "美国本土外小岛屿(UNITED STATES MINOR OUTLYING ISLANDS)"
},
{
"value": "UT",
"text": "犹他州(UTAH)"
},
{
"value": "VT",
"text": "佛蒙特州(VERMONT)"
},
{
"value": "VI",
"text": "美属维尔京群岛(VIRGIN ISLANDS, U.S.)"
},
{
"value": "VA",
"text": "弗吉尼亚州(VIRGINIA)"
},
{
"value": "WA",
"text": "华盛顿(WASHINGTON)"
},
{
"value": "WV",
"text": "西弗吉尼亚州(WEST VIRGINIA)"
},
{
"value": "WI",
"text": "威斯康星州(WISCONSIN)"
},
{
"value": "WY",
"text": "怀俄明州(WYOMING)"
},
{
"value": "XX",
"text": "未知(UNKNOWN)"
}
]

234
public/json/us_en.json Normal file
View File

@@ -0,0 +1,234 @@
[
{
"value": "AL",
"text": "Alabama"
},
{
"value": "AK",
"text": "Alaska"
},
{
"value": "AS",
"text": "American Samoa"
},
{
"value": "AZ",
"text": "Arizona"
},
{
"value": "AR",
"text": "Arkansas"
},
{
"value": "CA",
"text": "California"
},
{
"value": "CO",
"text": "Colorado"
},
{
"value": "CT",
"text": "Connecticut"
},
{
"value": "DE",
"text": "Delaware"
},
{
"value": "DC",
"text": "District of Columbia"
},
{
"value": "FL",
"text": "Florida"
},
{
"value": "GA",
"text": "Georgia"
},
{
"value": "GU",
"text": "Guam"
},
{
"value": "HI",
"text": "Hawaii"
},
{
"value": "ID",
"text": "Idaho"
},
{
"value": "IL",
"text": "Illinois"
},
{
"value": "IN",
"text": "Indiana"
},
{
"value": "IA",
"text": "Iowa"
},
{
"value": "KS",
"text": "Kansas"
},
{
"value": "KY",
"text": "Kentucky"
},
{
"value": "LA",
"text": "Louisiana"
},
{
"value": "ME",
"text": "Maine"
},
{
"value": "MD",
"text": "Maryland"
},
{
"value": "MA",
"text": "Massachusetts"
},
{
"value": "MI",
"text": "Michigan"
},
{
"value": "MN",
"text": "Minnesota"
},
{
"value": "MS",
"text": "Mississippi"
},
{
"value": "MO",
"text": "Missouri"
},
{
"value": "MT",
"text": "Montana"
},
{
"value": "NE",
"text": "Nebraska"
},
{
"value": "NV",
"text": "Nevada"
},
{
"value": "NH",
"text": "New Hampshire"
},
{
"value": "NJ",
"text": "New Jersey"
},
{
"value": "NM",
"text": "New Mexico"
},
{
"value": "NY",
"text": "New York"
},
{
"value": "NC",
"text": "North Carolina"
},
{
"value": "ND",
"text": "North Dakota"
},
{
"value": "MP",
"text": "Northern Mariana Islands"
},
{
"value": "OH",
"text": "Ohio"
},
{
"value": "OK",
"text": "Oklahoma"
},
{
"value": "OR",
"text": "Oregon"
},
{
"value": "PA",
"text": "Pennsylvania"
},
{
"value": "PR",
"text": "Puerto Rico"
},
{
"value": "RI",
"text": "Rhode Island"
},
{
"value": "SC",
"text": "South Carolina"
},
{
"value": "SD",
"text": "South Dakota"
},
{
"value": "TN",
"text": "Tennessee"
},
{
"value": "TX",
"text": "Texas"
},
{
"value": "UM",
"text": "United States Minor Outlying Islands"
},
{
"value": "UT",
"text": "Utah"
},
{
"value": "VT",
"text": "Vermont"
},
{
"value": "VI",
"text": "U.S. Virgin Islands"
},
{
"value": "VA",
"text": "Virginia"
},
{
"value": "WA",
"text": "Washington"
},
{
"value": "WV",
"text": "West Virginia"
},
{
"value": "WI",
"text": "Wisconsin"
},
{
"value": "WY",
"text": "Wyoming"
},
{
"value": "XX",
"text": "Unknown"
}
]

8
public/nginx.htaccess Normal file
View File

@@ -0,0 +1,8 @@
location ~* (runtime|application)/{
return 403;
}
location / {
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=$1 last; break;
}
}

2
public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

19
public/router.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
return false;
} else {
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
require __DIR__ . "/index.php";
}

41
route/app.php Normal file
View File

@@ -0,0 +1,41 @@
<?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/detail', 'Order/detail');
Route::post('order/pay', 'Order/pay');
Route::get('ocr/signature', 'Ocr/signature');
Route::post('upload/image', 'Upload/image');
Route::post('evus/lookup', 'Evus/lookup');
Route::post('evus/apply', 'Evus/apply');
Route::post('esta/apply', 'Esta/apply');
})->namespace('app\\miniapi\\controller');

11
think Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env php
<?php
use think\App;
// 命令行入口文件
// 加载基础文件
require __DIR__ . '/vendor/autoload.php';
// 应用初始化
(new App())->console->run();

136
thinkphp/lang/zh-cn.php Normal file
View File

@@ -0,0 +1,136 @@
<?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>
// +----------------------------------------------------------------------
// 核心中文语言包
return [
// 系统错误提示
'Undefined variable' => '未定义变量',
'Undefined index' => '未定义数组索引',
'Undefined offset' => '未定义数组下标',
'Parse error' => '语法解析错误',
'Type error' => '类型错误',
'Fatal error' => '致命错误',
'syntax error' => '语法错误',
// 框架核心错误提示
'dispatch type not support' => '不支持的调度类型',
'method param miss' => '方法参数错误',
'method not exists' => '方法不存在',
'module not exists' => '模块不存在',
'controller not exists' => '控制器不存在',
'class not exists' => '类不存在',
'property not exists' => '类的属性不存在',
'template not exists' => '模板文件不存在',
'illegal controller name' => '非法的控制器名称',
'illegal action name' => '非法的操作名称',
'url suffix deny' => '禁止的URL后缀访问',
'Route Not Found' => '当前访问路由未定义',
'Undefined db type' => '未定义数据库类型',
'variable type error' => '变量类型错误',
'PSR-4 error' => 'PSR-4 规范错误',
'not support total' => '简洁模式下不能获取数据总数',
'not support last' => '简洁模式下不能获取最后一页',
'error session handler' => '错误的SESSION处理器类',
'not allow php tag' => '模板不允许使用PHP语法',
'not support' => '不支持',
'redisd master' => 'Redisd 主服务器错误',
'redisd slave' => 'Redisd 从服务器错误',
'must run at sae' => '必须在SAE运行',
'memcache init error' => '未开通Memcache服务请在SAE管理平台初始化Memcache服务',
'KVDB init error' => '没有初始化KVDB请在SAE管理平台初始化KVDB服务',
'fields not exists' => '数据表字段不存在',
'where express error' => '查询表达式错误',
'not support data' => '不支持的数据表达式',
'no data to update' => '没有任何数据需要更新',
'miss data to insert' => '缺少需要写入的数据',
'miss complex primary data' => '缺少复合主键数据',
'miss update condition' => '缺少更新条件',
'model data Not Found' => '模型数据不存在',
'table data not Found' => '表数据不存在',
'delete without condition' => '没有条件不会执行删除操作',
'miss relation data' => '缺少关联表数据',
'tag attr must' => '模板标签属性必须',
'tag error' => '模板标签错误',
'cache write error' => '缓存写入失败',
'sae mc write error' => 'SAE mc 写入错误',
'route name not exists' => '路由标识不存在(或参数不够)',
'invalid request' => '非法请求',
'bind attr has exists' => '模型的属性已经存在',
'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法',
'closure not support cache(true)' => '使用闭包查询不支持cache(true)请指定缓存Key',
// 上传错误信息
'unknown upload error' => '未知上传错误!',
'file write error' => '文件写入失败!',
'upload temp dir not found' => '找不到临时文件夹!',
'no file to uploaded' => '没有文件被上传!',
'only the portion of file is uploaded' => '文件只有部分被上传!',
'upload File size exceeds the maximum value' => '上传文件大小超过了最大值!',
'upload write error' => '文件上传保存错误!',
'has the same filename: {:filename}' => '存在同名文件:{:filename}',
'upload illegal files' => '非法上传文件',
'illegal image files' => '非法图片文件',
'extensions to upload is not allowed' => '上传文件后缀不允许',
'mimetype to upload is not allowed' => '上传文件MIME类型不允许',
'filesize not match' => '上传文件大小不符!',
'directory {:path} creation failed' => '目录 {:path} 创建失败!',
// Validate Error Message
':attribute require' => ':attribute不能为空',
':attribute must be numeric' => ':attribute必须是数字',
':attribute must be integer' => ':attribute必须是整数',
':attribute must be float' => ':attribute必须是浮点数',
':attribute must be bool' => ':attribute必须是布尔值',
':attribute not a valid email address' => ':attribute格式不符',
':attribute not a valid mobile' => ':attribute格式不符',
':attribute must be a array' => ':attribute必须是数组',
':attribute must be yes,on or 1' => ':attribute必须是yes、on或者1',
':attribute not a valid datetime' => ':attribute不是一个有效的日期或时间格式',
':attribute not a valid file' => ':attribute不是有效的上传文件',
':attribute not a valid image' => ':attribute不是有效的图像文件',
':attribute must be alpha' => ':attribute只能是字母',
':attribute must be alpha-numeric' => ':attribute只能是字母和数字',
':attribute must be alpha-numeric, dash, underscore' => ':attribute只能是字母、数字和下划线_及破折号-',
':attribute not a valid domain or ip' => ':attribute不是有效的域名或者IP',
':attribute must be chinese' => ':attribute只能是汉字',
':attribute must be chinese or alpha' => ':attribute只能是汉字、字母',
':attribute must be chinese,alpha-numeric' => ':attribute只能是汉字、字母和数字',
':attribute must be chinese,alpha-numeric,underscore, dash' => ':attribute只能是汉字、字母、数字和下划线_及破折号-',
':attribute not a valid url' => ':attribute不是有效的URL地址',
':attribute not a valid ip' => ':attribute不是有效的IP地址',
':attribute must be dateFormat of :rule' => ':attribute必须使用日期格式 :rule',
':attribute must be in :rule' => ':attribute必须在 :rule 范围内',
':attribute be notin :rule' => ':attribute不能在 :rule 范围内',
':attribute must between :1 - :2' => ':attribute只能在 :1 - :2 之间',
':attribute not between :1 - :2' => ':attribute不能在 :1 - :2 之间',
'size of :attribute must be :rule' => ':attribute长度不符合要求 :rule',
'max size of :attribute must be :rule' => ':attribute长度不能超过 :rule',
'min size of :attribute must be :rule' => ':attribute长度不能小于 :rule',
':attribute cannot be less than :rule' => ':attribute日期不能小于 :rule',
':attribute cannot exceed :rule' => ':attribute日期不能超过 :rule',
':attribute not within :rule' => '不在有效期内 :rule',
'access IP is not allowed' => '不允许的IP访问',
'access IP denied' => '禁止的IP访问',
':attribute out of accord with :2' => ':attribute和确认字段:2不一致',
':attribute cannot be same with :2' => ':attribute和比较字段:2不能相同',
':attribute must greater than or equal :rule' => ':attribute必须大于等于 :rule',
':attribute must greater than :rule' => ':attribute必须大于 :rule',
':attribute must less than or equal :rule' => ':attribute必须小于等于 :rule',
':attribute must less than :rule' => ':attribute必须小于 :rule',
':attribute must equal :rule' => ':attribute必须等于 :rule',
':attribute has exists' => ':attribute已存在',
':attribute not conform to the rules' => ':attribute不符合指定规则',
'invalid Request method' => '无效的请求类型',
'invalid token' => '令牌数据无效',
'not conform to the rules' => '规则错误',
];

View File

@@ -0,0 +1,37 @@
name: main
on: [push, pull_request]
jobs:
phpunit:
name: PHPUnit
runs-on: ${{ matrix.operating-system }}
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
steps:
- name: Install SASL library on macOS
if: matrix.operating-system == 'macos-latest' && matrix.php-version >= 8.5
run: brew update && brew install cyrus-sasl
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: bz2, curl, fileinfo, mbstring, exif, openssl, zip
coverage: xdebug
- name: Checkout
uses: actions/checkout@v5
- name: Install dependencies
run: composer install --ansi --prefer-dist --no-interaction --no-progress
- name: PHPUnit Migrate Configuration
if: ${{ matrix.php-version != '7.4' && matrix.php-version != '8.0'}}
run: vendor/bin/phpunit --migrate-configuration
- name: Run PHPUnit
run: vendor/bin/phpunit --testsuite UnitTest --fail-on-warning --fail-on-incomplete --fail-on-risky

Some files were not shown because too many files have changed in this diff Show More