472 lines
15 KiB
PHP
472 lines
15 KiB
PHP
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
// +----------------------------------------------------------------------
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types=1);
|
|
|
|
namespace app\api\controller;
|
|
|
|
use app\common\crypto\Crypto;
|
|
use think\response\Json;
|
|
use cores\BaseController;
|
|
use app\api\model\User as UserModel;
|
|
use app\api\model\Store as StoreModel;
|
|
use app\api\model\Merchant as MerchantModel;
|
|
use app\api\service\User as UserService;
|
|
use cores\exception\BaseException;
|
|
|
|
use app\common\service\SybOil;
|
|
|
|
/**
|
|
* API控制器基类
|
|
* Class Controller
|
|
* @package app\store\controller
|
|
*/
|
|
class Controller extends BaseController
|
|
{
|
|
// 当前商城ID
|
|
protected $storeId;
|
|
|
|
/**
|
|
* API基类初始化
|
|
* @throws BaseException
|
|
*/
|
|
public function initialize()
|
|
{
|
|
// 当前商城id
|
|
$this->storeId = $this->getStoreId();
|
|
// 验证当前商城状态
|
|
$this->checkStore();
|
|
// 验证当前客户端状态
|
|
$this->checkClient();
|
|
}
|
|
|
|
/**
|
|
* 获取当前商城ID
|
|
* @return int|null
|
|
* @throws BaseException
|
|
*/
|
|
protected function getStoreId(): ?int
|
|
{
|
|
$storeId = getStoreId(); // app/api/common.php
|
|
empty($storeId) && throwError('缺少必要的参数:storeId');
|
|
return $storeId;
|
|
}
|
|
|
|
/**
|
|
* 验证当前商城状态
|
|
* @return void
|
|
* @throws BaseException
|
|
*/
|
|
private function checkStore(): void
|
|
{
|
|
// 获取当前商城信息
|
|
$store = StoreModel::detail($this->storeId);
|
|
if (empty($store)) {
|
|
throwError('当前商城信息不存在');
|
|
}
|
|
if ($store['is_recycle'] || $store['is_delete']) {
|
|
throwError('当前商城已删除');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证当前客户端是否允许访问
|
|
* @throws BaseException
|
|
*/
|
|
private function checkClient()
|
|
{
|
|
$client = getPlatform();
|
|
$settingClass = [
|
|
'H5' => [\app\api\model\h5\Setting::class, 'checkStatus', 'H5端']
|
|
];
|
|
if (!isset($settingClass[$client])) {
|
|
return;
|
|
}
|
|
$status = call_user_func([$settingClass[$client][0], $settingClass[$client][1]]);
|
|
$status === false && throwError('很抱歉,当前' . $settingClass[$client][2] . '端暂不支持访问');
|
|
}
|
|
|
|
/**
|
|
* 获取当前用户信息
|
|
* @param bool $isForce 强制验证登录
|
|
* @return UserModel|bool|null
|
|
* @throws BaseException
|
|
*/
|
|
protected function getLoginUser(bool $isForce = true)
|
|
{
|
|
return UserService::getCurrentLoginUser($isForce);
|
|
}
|
|
|
|
/**
|
|
* 返回封装后的 API 数据到客户端
|
|
* @param string|null $code 状态码
|
|
* @param string $message
|
|
* @param string|null $errCode
|
|
* @param string|null $errMessage
|
|
* @param array $data
|
|
* @return Json
|
|
*/
|
|
protected function oilRenderJson(string $code = null, string $message = '', string $errCode = null, string $errMessage = null, array $data = []): Json
|
|
{
|
|
return json(compact('code', 'message', 'errCode', 'errMessage', 'data'));
|
|
}
|
|
|
|
/**
|
|
* 返回操作成功json
|
|
* @param array|string $data
|
|
* @param string $code
|
|
* @param string $message
|
|
* @return Json
|
|
*/
|
|
protected function oilRenderSuccess($data = [], string $code = '', string $message = ''): Json
|
|
{
|
|
if(empty($code)){
|
|
$code = '00';
|
|
}
|
|
if(empty($message)){
|
|
$message = config('api_status.response_code')[$code] ?? '';
|
|
}
|
|
if (is_string($data)) {
|
|
$message = $data;
|
|
$data = [];
|
|
}
|
|
return $this->oilRenderJson($code, $message, null, null, $data);
|
|
}
|
|
|
|
/**
|
|
* 返回操作失败json
|
|
* @param string $code
|
|
* @param string $errCode
|
|
* @param string $message
|
|
* @param string $errMessage
|
|
* @return Json
|
|
*/
|
|
protected function oilRenderError(string $code, string $errCode, string $message = '', string $errMessage = ''): Json
|
|
{
|
|
if(empty($message)){
|
|
$message = config('api_status.response_code')[$code] ?? '';
|
|
}
|
|
if(empty($errMessage)){
|
|
$errMessage = config('api_status.error_code')[$errCode] ?? '';
|
|
}
|
|
return $this->oilRenderJson($code, $message, $errCode, $errMessage);
|
|
}
|
|
|
|
/**
|
|
* 返回封装后的 API 数据到客户端
|
|
* @param int|null $status 状态码
|
|
* @param string $message
|
|
* @param array $data
|
|
* @return Json
|
|
*/
|
|
protected function renderJson(int $status = null, string $message = '', array $data = []): Json
|
|
{
|
|
return json(compact('status', 'message', 'data'));
|
|
}
|
|
|
|
/**
|
|
* 返回操作成功json
|
|
* @param array|string $data
|
|
* @param string $message
|
|
* @return Json
|
|
*/
|
|
protected function renderSuccess($data = [], string $message = 'success'): Json
|
|
{
|
|
if (is_string($data)) {
|
|
$message = $data;
|
|
$data = [];
|
|
}
|
|
return $this->renderJson(config('status.success'), $message, $data);
|
|
}
|
|
|
|
/**
|
|
* 返回操作失败json
|
|
* @param string $message
|
|
* @param array $data
|
|
* @return Json
|
|
*/
|
|
protected function renderError(string $message = 'error', array $data = []): Json
|
|
{
|
|
return $this->renderJson(config('status.error'), $message, $data);
|
|
}
|
|
|
|
/**
|
|
* 获取post数据 (数组)
|
|
* @param $key
|
|
* @return mixed
|
|
*/
|
|
protected function postData($key = null)
|
|
{
|
|
return $this->request->post(is_null($key) ? '' : $key . '/a');
|
|
}
|
|
|
|
/**
|
|
* 获取post数据 (数组)
|
|
* @param string $key
|
|
* @return mixed
|
|
*/
|
|
protected function postForm(string $key = 'form')
|
|
{
|
|
return $this->postData($key);
|
|
}
|
|
|
|
//创建sign
|
|
public function makeSign($data,$appKey) {
|
|
ksort($data);
|
|
$string = $this->toUrlParams($data);
|
|
$string = $string . "&key=" . $appKey;
|
|
$string = md5($string);
|
|
$result = strtoupper($string);
|
|
return $result;
|
|
}
|
|
|
|
//检验sign是否正确
|
|
public function verifySign($data) {
|
|
//check sign
|
|
if (!isset($data['sign']) || !$data['sign']) {
|
|
return ['code'=>1,'msg'=>'发送的数据签名不存在'];
|
|
}
|
|
|
|
//check sign
|
|
if (!isset($data['appid']) || !$data['appid']) {
|
|
return ['code'=>1,'msg'=>'发送的应用参数1不存在'];
|
|
}
|
|
|
|
//check timestamp
|
|
if (!isset($data['timestamp']) || !$data['timestamp']) {
|
|
return ['code'=>1,'msg'=>'缺少时间戳参数'];
|
|
}
|
|
|
|
// 验证请求, 10分钟失效
|
|
if (time() - $data['timestamp'] > 600) {
|
|
return ['code'=>1,'msg'=>'验证超时, 请重新发送请求'];
|
|
}
|
|
$merchant = MerchantModel::detail(['appId'=>$data['appid']]);
|
|
if (empty($merchant['merchant_id'])) {
|
|
return ['code'=>1,'msg'=>'appid错误'];
|
|
}
|
|
$clientSign = $data['sign'];
|
|
unset($data['sign']);
|
|
$serverSign = $this->makeSign($data,$merchant['appKey']);
|
|
if ($clientSign == $serverSign) {
|
|
return ['code'=>0,'msg'=>'签名验证通过','merchant_id'=>$merchant['merchant_id'],'callback'=>$merchant['callback']];
|
|
} else {
|
|
return ['code'=>1,'msg'=>'签名不合法'];
|
|
}
|
|
}
|
|
|
|
//生成url字符串
|
|
private function toUrlParams($values){
|
|
$buff = "";
|
|
foreach ($values as $k => $v)
|
|
{
|
|
//&& $v != ""
|
|
if($k != "sign" && !is_array($v)){
|
|
$buff .= $k . "=" . $v . "&";
|
|
}
|
|
}
|
|
$buff = trim($buff, "&");
|
|
return $buff;
|
|
}
|
|
|
|
/**
|
|
* http请求
|
|
*
|
|
* @param $url //请求地址
|
|
* @param $params //链接后拼接的参数数组
|
|
* @param $method //get/post
|
|
* @param $header //请求头数组
|
|
* @return array
|
|
*/
|
|
function httpCurl($url, $params, $method = 'GET', $header = array(), $multi = false): array
|
|
{
|
|
date_default_timezone_set('PRC'); //设置默认时区
|
|
$opts = array( //请求参数
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_RETURNTRANSFER => 1, //将curl_exec()获取的信息以文件流的形式返回
|
|
CURLOPT_SSL_VERIFYPEER => false, //不对认证证书来源的检查
|
|
CURLOPT_SSL_VERIFYHOST => false, //不从证书中检查SSL加密算法是否存在
|
|
CURLOPT_HTTPHEADER => $header, //Content-Type类型
|
|
CURLOPT_COOKIESESSION => true,
|
|
CURLOPT_FOLLOWLOCATION => 1, // 使用自动跳转
|
|
CURLOPT_COOKIE => session_name() . '=' . session_id(),
|
|
);
|
|
|
|
/* 根据请求类型设置特定参数 */
|
|
switch (strtoupper($method)) {
|
|
case 'GET':
|
|
// 链接后拼接参数 如xxx.com?key = value & key2=value
|
|
$opts[CURLOPT_URL] = $url . '?' . http_build_query($params);//将数组变成key=value&key2=value
|
|
break;
|
|
case 'POST': //判断是否传输文件
|
|
$params = $multi ? $params : http_build_query($params); //同上
|
|
$opts[CURLOPT_URL] = $url;
|
|
$opts[CURLOPT_POST] = 1;
|
|
$opts[CURLOPT_POSTFIELDS] = $params;
|
|
break;
|
|
default:
|
|
throw new BaseException(['message' => '不支持的请求方式!']);
|
|
}
|
|
|
|
/* 初始化并执行curl请求 */
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, $opts); // 数组形式设置URL和其他参数
|
|
$data = curl_exec($ch); // URL抓取并把它传递给浏览器
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($error){
|
|
$data = ['code'=>2,'message'=>$error];
|
|
}else {
|
|
$data = json_decode($data, true);
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
public function sendRequest($company, $url, $businessParams = [], $appendHeaders = [], $file = null)
|
|
{
|
|
$crypto = new Crypto($company);
|
|
// 签名并组合header,并加密数据
|
|
$this->saveLog(json_encode($businessParams,320));
|
|
$requestData = $crypto->prepareRequest($appendHeaders, $businessParams);
|
|
$businessParams = $requestData['encryptData'];
|
|
// 构建默认的公共参数头
|
|
$headers = [];
|
|
foreach ($requestData['header'] as $k=>$val){
|
|
$headers[] = $k.': '.$val;
|
|
}
|
|
$this->saveLog(json_encode($requestData['header'],320));
|
|
$this->saveLog(json_encode($businessParams,320));
|
|
// 使用 cURL 发送请求
|
|
$ch = curl_init();
|
|
|
|
// 设置请求 URL
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
|
|
// 设置请求方式为 POST
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
|
|
// 返回结果而不是输出
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
// 包含响应头在输出中
|
|
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
|
|
$headers[] = 'Charset: UTF-8';
|
|
if ($file) {
|
|
// 如果有文件上传,设置 multipart/form-data 表单
|
|
$filePath = $file->getPathname();
|
|
$fileName = $file->getOriginalName();
|
|
$fileMime = $file->getMimeType();
|
|
$postFields = [
|
|
'file' => new \CURLFile($filePath, $fileMime, $fileName),
|
|
'businessParams' => json_encode($businessParams, JSON_UNESCAPED_UNICODE),
|
|
];
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
|
|
} else {
|
|
// 如果没有文件上传,直接发送业务参数的 JSON 数据
|
|
$postFields = json_encode($businessParams, JSON_UNESCAPED_UNICODE);
|
|
$headers[] = 'Content-Type: application/json';
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
|
|
}
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
|
|
// 执行请求
|
|
$response = curl_exec($ch);
|
|
|
|
// 检查是否有错误
|
|
if (curl_errno($ch)) {
|
|
$error_msg = curl_error($ch);
|
|
curl_close($ch);
|
|
return ['status' => '01', 'message' => $error_msg];
|
|
}
|
|
|
|
// 获取响应头大小
|
|
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
|
|
// 分离响应头和响应体
|
|
$headerString = substr($response, 0, $header_size);
|
|
$bodyString = substr($response, $header_size);
|
|
|
|
// 关闭 cURL 句柄
|
|
curl_close($ch);
|
|
|
|
// 解析响应头为数组
|
|
$headerArray = [];
|
|
foreach (explode("\r\n", $headerString) as $header) {
|
|
if (trim($header) !== '' && strpos($header, ':') !== false) {
|
|
list($key, $value) = explode(': ', $header, 2);
|
|
$headerArray[strtolower(trim($key))] = trim($value);
|
|
}
|
|
}
|
|
$res = $crypto->processResponse(['header'=>$headerArray,'encryptData'=>json_decode($bodyString, true)]);
|
|
if(!$res){
|
|
return ['status' => '01', 'message' => '解密失败'];
|
|
}
|
|
// 返回结果
|
|
return $res;
|
|
}
|
|
|
|
|
|
/**
|
|
* xml/json数据的POST请求
|
|
*
|
|
* @param $url //请求地址
|
|
* @param $xmlData //xml 或 json数据
|
|
*/
|
|
public function SendData($url,$xmlData)
|
|
{
|
|
$curl = curl_init();
|
|
$param[CURLOPT_URL] = $url;
|
|
$param[CURLOPT_HEADER] = false;
|
|
$param[CURLOPT_RETURNTRANSFER] = true;
|
|
$param[CURLOPT_FOLLOWLOCATION] = true;
|
|
$param[CURLOPT_POST] = true;
|
|
$param[CURLOPT_POSTFIELDS] = $xmlData;
|
|
$param[CURLOPT_SSL_VERIFYPEER] = false;
|
|
$param[CURLOPT_SSL_VERIFYHOST] = false;
|
|
curl_setopt_array($curl,$param); //传参数
|
|
$data = curl_exec($curl); //执行命令
|
|
curl_close($curl);
|
|
return $data;
|
|
}
|
|
|
|
// 循环创建目录
|
|
|
|
public function mk_dir($dir, $mode = 0777)
|
|
{
|
|
if (is_dir($dir) || @mkdir($dir,$mode)) return true;
|
|
if (!$this->mk_dir(dirname($dir),$mode)) return false;
|
|
return @mkdir($dir,$mode);
|
|
}
|
|
|
|
/** 记录日志
|
|
* @param $data
|
|
*/
|
|
public function saveLog($data){
|
|
$SybOil = new SybOil();
|
|
$SybOil->saveLogStore($data);
|
|
}
|
|
/** 插入订单号
|
|
* @param $order_no
|
|
*/
|
|
public function insertNo($order_no){
|
|
return $order_no;
|
|
//return (int)$order_no-100000;
|
|
}
|
|
/** 获取订单号
|
|
* @param $order_no
|
|
*/
|
|
public function getOrderNo($order_no){
|
|
return $order_no;
|
|
//return (int)$order_no+100000;
|
|
}
|
|
}
|