Files
oil/server-tp6/app/api/controller/Code.php
T
2026-09-01 22:15:50 +08:00

397 lines
17 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\api\model\Code as CodeModel;
use app\api\model\Notify as NotifyModel;
use app\api\model\Shop as ShopModel;
use app\api\model\Order as OrderModel;
use think\cache\driver\Redis;
use cores\exception\BaseException;
use think\facade\Cache;
use think\response\Json;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Cookie\CookieJar;
/**
* 产码控制器
* Class Code
* @package app\api\controller
*/
class Code extends Controller
{
/**
* 查询支付状态
* @throws \app\common\exception\BaseException
* @throws \think\exception\DbException
*/
public function payStatus()
{
$data = $this->request->get();
$zone = isset($data['zone'])?$data['zone']:1; //订单分区
$this->saveLog("进入支付查询");
//开始
$model = new CodeModel();
$orderModel = new OrderModel();
$NotifyModel = new NotifyModel();
$array = $model->getList($zone);
$this->saveLog("进入支付查询,分区:".$zone.",订单数量:".count($array));
//获取IP组
$iPGroup = $this->getIPGroup();
foreach ($array as $k=>$val) {
$payTotal = $orderModel->getZoneOrderTotal([['code_id', '=', $val['code_id']],['order_status', '=', 20]]);
$waitPayTotal = $orderModel->getZoneOrderTotal([['code_id', '=', $val['code_id']],['order_status', '=', 10]]);
$modelOr = OrderModel::detail([['code_id', '=', $val['code_id']],['order_status', '=', 10]]);
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
if (isset($modelOr['order_id']) && !empty($modelOr['order_id']) && $payTotal <= 0 && $waitPayTotal == 1) {
if(isset($iPGroup[0]['ip'])) {
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $iPGroup[0]['ip'], $iPGroup[0]['port'], $text);
}else {
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
}
if ($res !== false) { //支付成功
$pass = $modelOr['callback'] == 1 ? $NotifyModel->add($modelOr) : true;
if ($pass) {
$model->update([
'order_id' => $modelOr['order_id'],
'pay_status' => 1,
],['code_id'=>$val['code_id']]);
$modelOr->save([
'pay_status' => 20,
'pay_time' => time(),
'order_status' => 20,
]);
return $this->renderSuccess('操作成功');
} else {
return $this->renderError($NotifyModel->getError() ?: '操作失败');
}
}
}
}
$this->saveLog("支付查询结束,分区:".$zone.",订单数量:".count($array));
}
/**
* 查询失效状态
* @throws \app\common\exception\BaseException
* @throws \think\exception\DbException
*/
public function lapseStatus()
{
//连接本地的 Redis 服务
// 获取分布式锁
$lock_key = 'lapseStatus_lock';
$max_wait_time = 10; // 最大等待时间(秒)
$retry_interval = 100; // 重试间隔时间(毫秒)
$lock_ttl = 600; // 锁定时间(秒)
$lock_count = 0; // 锁的重试次数
// 获取分布式锁
$redis = Cache::store('redis');
$lock = false;
$wait_time = 0;
$lock_value = '';
while ($wait_time <= $max_wait_time) {
$lock_value = uniqid(); // 生成唯一的锁值
if ($redis->set($lock_key, $lock_value, 'EX', $lock_ttl, 'NX')) {
$lock = true;
break;
} else {
// 获取锁失败,等待一段时间后重试
$lock_count++;
usleep($retry_interval * 1000);
$wait_time += ($retry_interval / 1000);
if ($lock_count > 40) {
// 重试次数超过限制,抛出异常
$this->saveLog("失效查询重试次数超过限制");
}
}
}
if($lock) {
try {
// TODO: 需要加锁的业务逻辑
// 字符串操作
$conn = new \think\cache\driver\Redis(config('cache.stores.redis'));
$codesIp = $conn->get('codesIp');
$codesPort = $conn->get('codesPort');
$codeNum = $conn->get('codeNum');
if (empty($codesIp) || empty($codeNum) || $codeNum <= 0) {
//获取IP组
$iPGroup = $this->getIPGroup();
$codesIp = isset($iPGroup[0]['ip']) ? $iPGroup[0]['ip'] : '';
$codesPort = isset($iPGroup[0]['port']) ? $iPGroup[0]['port'] : '';
$codeNum = 20;
$conn->set('codesIp', $codesIp);
$conn->set('codesPort', $codesPort);
}
$modelOr = new OrderModel();
$model = new CodeModel();
$this->saveLog("失效查询开始");
//非失效码的状态更新
$nonFailure = $model->getNonFailure();
foreach ($nonFailure as $val) {
$codeM = CodeModel::detail($val['code_id']);
$create_time = is_numeric($val['create_time']) ? $val['create_time'] : strtotime($val['create_time']);
//支付码失效
if ($create_time + $val['expiration_time'] < time()) {
$codeM->save([
'status' => 3,
]);
//分配时间过长未支付,订单超时,支付码释放
} elseif ($val['allot_time'] + 300 < time()) {
$text = $val['passageway'] == 'ZFBH5' ? '订单已支付' : '该订单已完成支付';
if (!empty($codesIp)) {
$conn->set('codeNum', $codeNum - 1);
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
} else {
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
}
$status = 1;
$pay_status = 0;
if ($res !== false) { //已支付 更改状态
$status = 3;
$pay_status = 1;
} else {
//重新写入队列左边(为的是后进先出,失败的一般代表比较前面的码,所以放在左边)
$code_queue = ['code_id' => (int)$val['code_id'], 'expire_time' => $create_time + (float)$val['expiration_time']]; // 过期时间
//先清楚队列再加入,避免重复
Cache::store('redis')->lRem($val['passageway'] . '_' . ($val['money'] * 1), json_encode($code_queue, 320), 1);
Cache::store('redis')->lPush($val['passageway'] . '_' . ($val['money'] * 1), json_encode($code_queue, 320));
$this->saveLog("回码:".json_encode($code_queue, 320));
}
$codeM->save([
'allot_time' => 0,
'back_time' => time(),
'status' => $status,
'pay_status' => $pay_status,
]);
}
if ($val['code_id']) {
$modelOr->update([
'code_id' => 0,
'order_status' => 30,
'old_code_id' => $val['code_id']
], ['code_id' => $val['code_id'],
'order_status' => 10]);
}
}
//处理未匹配上码的订单为超时
$modelOr->update([
'order_status' => 30,
], [['code_id', '=', 0],
['order_status', '=', 10], ['create_time', '<', time() - 120]]);
$this->saveLog("失效查询结束");
} finally {
// 释放锁
$lock_value_in_redis = $redis->get($lock_key);
if ($lock_value_in_redis === $lock_value) { // 如果当前锁的值和刚才设置的值相等,则说明当前线程持有该锁,可以进行释放
$redis->del($lock_key); // 删除锁
}
}
}
//未使用超过调起次数的支付码
/*$modelNew = new CodeModel();
$modelNew->setStatusLose([],['status' => array('=', 1),
'num' => array('>=', 100)]);*/
}
public function getIPGroup(): array
{
$html = @file_get_contents("http://http.tiqu.letecs.com/getip3?num=1&type=2&pro=&city=0&yys=0&port=1&time=1&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1&regions=");
$result = json_decode($html,true);
$ips = is_array($result['data'])?$result['data']:[];
return $ips;
}
public function test(){
$data = $this->request->get();
$queue_name = $data['passageway'].'_'.$data['money'];
$queue_length = Cache::store('redis')->lLen($queue_name);
var_dump($queue_length);
// 获取队列中的所有值
$queue_values = Cache::store('redis')->lRange($queue_name, 0, $queue_length - 1);
var_dump($queue_values);
die();
/*return $this->renderSuccess([], '提交成功');
$html = @file_get_contents("http://http.tiqu.letecs.com/getip3?num=1&type=2&pro=&city=0&yys=0&port=1&pack=301270&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1&regions=");
$result = json_decode($html,true);
$ips = $result['data'];
var_dump(date("Y-m-d H:i:s"));*/
$data = $this->request->get();
$order_id = $data['order_id'];
var_dump($this->checkQueue($order_id));die();
$val = OrderModel::detail($order_id);
$codesIp = '';
$codesPort = '';
//获取IP组
$iPGroup = $this->getIPGroup();
var_dump($iPGroup);
$codesIp = isset($iPGroup[0]['ip'])?$iPGroup[0]['ip']:'';
$codesPort = isset($iPGroup[0]['port'])?$iPGroup[0]['port']:'';
/*$aa = str_replace("&", "%26", $val['search_url']);
$bb = str_replace("amp;", "", $aa);
$stream_opts = [
"ssl" => [
"verify_peer" => false,
"verify_peer_name" => false,
]
];
$html = @file_get_contents(urldecode($bb), false, stream_context_create($stream_opts));
$res = strpos($html, '该订单已完成支付');*/
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
/*if(!empty($codesIp)) {
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
}else {
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $val['passageway'], $text);
}*/
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $val['passageway'], $codesIp, $codesPort, $text);
//$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']));
var_dump($res);
var_dump(date("Y-m-d H:i:s"));
}
/**
* 插入队列判断当前是否可执行
* @param $code_id
* @return
*/
public function checkQueue($code_id)
{
$message = Cache::store('redis')->lPop("queue:$code_id");
// 如果队列为空,则写入队列
if ($message === null || $message === false) {
// 消息内容
$message = 1;
// 监控客户的消息 Hash
Cache::store('redis')->watch("code:$code_id");
// 开始事务
Cache::store('redis')->multi();
// 将消息添加到队列末尾
Cache::store('redis')->rPush("queue:$code_id", $message);
// 提交事务
$result = Cache::store('redis')->exec();
if ($result !== null && $result !== false) {
return true;
}
}
return false;
}
public function checkStatus($url,$passageway,$text='该订单已完成支付'){
if(in_array($passageway,['WX','SNZFBH5'])){
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT,10);
curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt ($ch, CURLOPT_MAXREDIRS, 3);
$content = curl_exec($ch);
}else {
$publicPath = app()->getRootPath() . 'public/';
$escaped_url = escapeshellarg($url);
$command = "node {$publicPath}myscript.js {$escaped_url} 2>&1";
$content = shell_exec($command);
}
//$res = strpos((string)$content,$text);
return (string)$content;
}
public function checkStatusProxy($targetUrl,$passageway,$ip,$port,$text='该订单已完成支付'){
if(in_array($passageway,['WX','SNZFBH5'])){
// 代理服务器
$proxyServer = "http://" . $ip . ":" . $port;
// 隧道身份信息
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
// 设置代理服务器
//curl_setopt($ch, CURLOPT_PROXYTYPE, 0); //http
curl_setopt($ch, CURLOPT_PROXYTYPE, 5); //sock5
curl_setopt($ch, CURLOPT_PROXY, $proxyServer);
// 设置隧道验证信息
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727;)");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
}else{
$publicPath = app()->getRootPath() . 'public/';
$escaped_url = escapeshellarg($targetUrl);
//$command = "node {$publicPath}myscript_proxy.js {$escaped_url} {$ip} {$port} 2>&1";
$command = "node {$publicPath}myscript.js {$escaped_url} 2>&1";
$result = shell_exec($command);
}
$res = strpos((string)$result,$text);
return $res;
}
public function jysx(){
$res = $this->checkStatus('https://t-h5.einrkv.com/qAvYzu2Uraqq/ak7X8TC8Vq6TCkRvtlHzJZE2%252FeIBI0HjIVvQc0IIwXhcng%252FC2I3D0bicsYRpOpUqmtASUp6v7MiNCtPpPPV%2BKgeNvxF%2BQ%252FEXaPT886oB785uqOkFAlj6KDk8hEzwLQ4pjMuZ1xnPeU5fljvX84bwZPx9J%2Bb7nMmsvF2yTahh6FU%3D', 'zfb');
var_dump($res);
return;
$this->saveLog("码校验查询开始");
//开始
$model = new CodeModel();
//连接本地的 Redis 服务
$conn = new \Redis(); // 实例化
$conn->connect('127.0.0.1','6379');
$codepage = $conn->get('codepagejy');
$codepage = empty($codepage)?1:$codepage;
$array = $model->getSx($codepage);
$this->saveLog("进入码校验查询,页码:".$codepage.",码数:".count($array));
//获取IP组
$iPGroup = $this->getIPGroup();
foreach ($array as $k=>$val) {
$text = $val['passageway'] == 'ZFBH5'?'订单已支付':'该订单已完成支付';
if(isset($iPGroup[0]['ip'])) {
$res = $this->checkStatusProxy(htmlspecialchars_decode($val['search_url']), $iPGroup[0]['ip'], $iPGroup[0]['port'], $text);
}else {
$res = $this->checkStatus(htmlspecialchars_decode($val['search_url']), $text);
}
if ($res !== false) { //支付成功
$model->update([
'status' => 3,
],['code_id'=>$val['code_id']]);
}
}
if(count($array) < 20){
$conn->set('codepagejy',1);
}else{
$conn->set('codepagejy',$codepage+1);
}
$this->saveLog("码校验查询结束");
}
}