Files
oil/server-tp6/app/api/controller/Controller.php
T

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;
}
}