Merge branch 'dev'

This commit is contained in:
zpc 2025-04-25 11:13:59 +08:00
commit 58fd8b1605
23 changed files with 1084 additions and 298 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ runtime/*
vendor/*
404.html
public/.well-known/*
public/ueditor/*
public/ueditor/*
public/pay_log/*

View File

@ -35,7 +35,11 @@
<label class="layui-form-label">卡等级图标</label>
<div class="layui-input-inline">
<input type="hidden" name="imgurl" value="{$info.imgurl}" lay-verify="required" placeholder="请上传图片" autocomplete="off" class="layui-input" >
{if $info.imgurl}
<img style="float:left;width:120px;" class="" id="imgurl" src="{:imageUrl($info.imgurl)}">
{else/}
<img style="float:left;width:120px;" class="" id="imgurl" src="/static/image/upload.jpg">
{/if}
</div>
</div>
{if $info.special_imgurl}

View File

@ -87,7 +87,7 @@
{if isset($merchants) && is_array($merchants)}
{foreach $merchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="miniprograms[{$index}][merchants][]" value="{$merchant.id}" title="{$merchant.name}" lay-skin="primary" {if isset($miniprogram.merchants) && in_array($merchant.id, $miniprogram.merchants)}checked{/if}>
<input type="checkbox" name="miniprograms[{$index}][merchants][]" value="{$merchant.mch_id}" title="{$merchant.name}" lay-skin="primary" {if isset($miniprogram.merchants) && in_array($merchant.mch_id, $miniprogram.merchants)}checked{/if}>
</div>
{/foreach}
{else}
@ -155,7 +155,7 @@
{if isset($merchants) && is_array($merchants)}
{foreach $merchants as $merchant_id => $merchant}
<div class="merchant-item-mini">
<input type="checkbox" name="miniprograms[0][merchants][]" value="{$merchant.id}" title="{$merchant.name}" lay-skin="primary">
<input type="checkbox" name="miniprograms[0][merchants][]" value="{$merchant.mch_id}" title="{$merchant.name}" lay-skin="primary">
</div>
{/foreach}
{else}

View File

@ -26,7 +26,7 @@ class Config extends Base
return $this->renderSuccess('获取成功', [
'good_type' => $goodsTypeList,
'app_setting' => $app_setting,
'version' => '106'
'version' => '107'
]);
}

View File

@ -59,22 +59,15 @@ class Goods extends Base
$type_str = request()->param('type', -1);
$user_id = $this->getUserId();
$page = request()->param('page', 1);
// 获取Redis实例并检查缓存
$redis = (new \app\common\server\RedisHelper())->getRedis();
$cache_key = "goods_list_{$type_str}_{$user_id}_{$page}";
$cache_data = $redis->get($cache_key);
$new_data = [
'data' => [],
'last_page' => 0,
];
return $this->renderSuccess('请求成功', $new_data);
// 如果缓存存在,直接返回缓存数据
if ($cache_data) {
$cached_data = json_decode($cache_data, true);
// 在返回缓存数据前,更新参与次数
if (!empty($cached_data['data'])) {
$redis = (new \app\common\server\RedisHelper())->getRedis();
@ -83,24 +76,23 @@ class Goods extends Base
$goods_id = $item['id'];
$cacheKey = "order_goods_count:{$goods_id}";
$join_count = $redis->get($cacheKey);
// 如果参与次数缓存存在,更新数据
if ($join_count !== false) {
$item['join_count'] = intval($join_count);
}
}
}
return $this->renderSuccess('请求成功', $cached_data);
}
$whe = [];
$whe[] = ['status', '=', 1];
$whe[] = ['show_is', '=', 0];
$paginate = 15;
// 1一番赏 2无限赏 3擂台赏 4抽卡机 5积分赏 6全局赏 7福利盲盒 8领主赏 9连击赏 10 商品赏
// 使用映射数组简化类型条件判断
$typeMapping = [
1 => ['type' => 1],
@ -117,12 +109,12 @@ class Goods extends Base
15 => ['type' => 15],
16 => ['type' => 16],
];
if (isset($typeMapping[$type_str])) {
if (isset($typeMapping[$type_str]['paginate'])) {
$paginate = $typeMapping[$type_str]['paginate'];
}
if (isset($typeMapping[$type_str]['type']) && $typeMapping[$type_str]['type'] === 'in') {
$whe[] = ['type', 'in', $typeMapping[$type_str]['value']];
} else {
@ -136,12 +128,13 @@ class Goods extends Base
//充值金额
$whe[] = ['unlock_amount', '=', 0];
} else {
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
if ($isTest == "1") {
$order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('order_zhe_total');
} else {
$order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('price');
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
// $order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('order_zhe_total');
// } else {
// $order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('price');
// }
$order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('price');
// $order_money = Order::where('status', '=', 1)->where('user_id', '=', $user_id)->sum('price');
$userInfo = User::where('id', '=', $user_id)->field('istest')->find();
if ($userInfo && $userInfo['istest'] > 0) {
@ -165,39 +158,39 @@ class Goods extends Base
// 获取所有商品项
$goodsItems = $goods->items();
// 如果没有商品,直接返回
if (empty($goodsItems)) {
$new_data = [
'data' => [],
'last_page' => $goods->lastPage(),
];
// 缓存空结果,避免重复查询
$redis->set($cache_key, json_encode($new_data), 10);
return $this->renderSuccess('请求成功', $new_data);
}
// 获取所有商品ID和类型
$goodsIds = [];
$goodsTypes = [];
$type10GoodsIds = [];
foreach ($goodsItems as $item) {
$goodsIds[] = $item['id'];
$goodsTypes[$item['id']] = $item['type'];
// 记录type=10的商品ID
if ($item['type'] == 10) {
$type10GoodsIds[] = $item['id'];
}
}
// 批量查询所有商品的参与次数
// 移除外部查询使用Redis缓存在循环中查询每个商品的参与次数
$redis = (new \app\common\server\RedisHelper())->getRedis();
// 批量查询type=10的商品库存
$goodslistMap = [];
if (!empty($type10GoodsIds)) {
@ -208,7 +201,7 @@ class Goods extends Base
->group('goods_id')
->select()
->toArray();
foreach ($goodslists as $goodslist) {
$goodslistMap[$goodslist['goods_id']] = [
'stock' => intval($goodslist['stock']),
@ -220,22 +213,22 @@ class Goods extends Base
// 处理所有商品数据
foreach ($goodsItems as &$item) {
$item['imgurl'] = imageUrl($item['imgurl']);
// 计算剩余库存
$item['sale_stock'] = $item['stock'] - $item['sale_stock'];
// 处理type=10的商品
if ($item['type'] == 10 && isset($goodslistMap[$item['id']])) {
$goodslistData = $goodslistMap[$item['id']];
$item['sale_stock'] = $goodslistData['surplus_stock'];
$item['stock'] = $goodslistData['stock'];
}
// 使用Redis缓存获取参与次数
$goods_id = $item['id'];
$cacheKey = "order_goods_count:{$goods_id}";
$join_count = $redis->get($cacheKey);
// 缓存未命中,从数据库获取并缓存
if ($join_count === false) {
$join_count = OrderList::field('id')
@ -244,17 +237,17 @@ class Goods extends Base
->where('shang_id', 'between', self::$shang_count_id)
->where('order_type', '=', $item['type'])
->count();
// 设置缓存5分钟过期
$redis->set($cacheKey, $join_count, 300);
} else {
// 转为整数
$join_count = intval($join_count);
}
// 设置参与次数
$item['join_count'] = $join_count;
// 设置需要抽奖的数量
$item['need_draw_num'] = $item['type'] == 7 ? 1 : 0;
@ -263,7 +256,7 @@ class Goods extends Base
$item['type_text'] = $goods_types_map[$item['type']];
}
}
// 将处理后的数据设置回分页对象
$goods->setCollection(new Collection($goodsItems));
@ -271,10 +264,10 @@ class Goods extends Base
'data' => $goods->items(),
'last_page' => $goods->lastPage(),
];
// 将结果缓存到Redis设置过期时间为30秒30秒
$redis->set($cache_key, json_encode($new_data), 10);
return $this->renderSuccess('请求成功', $new_data);
}
@ -441,7 +434,7 @@ class Goods extends Base
$redis = (new \app\common\server\RedisHelper())->getRedis();
$cacheKey = "order_goods_count:{$goods_id}";
$join_count = $redis->get($cacheKey);
// 缓存未命中,从数据库获取并缓存
if ($join_count === false) {
$join_count = OrderList::field('id')
@ -450,7 +443,7 @@ class Goods extends Base
->where('shang_id', 'between', self::$shang_count_id)
->where('order_type', '=', $goods['type'])
->count();
// 设置缓存5分钟过期
$redis->set($cacheKey, $join_count, 300);
} else {
@ -816,7 +809,7 @@ class Goods extends Base
if (empty($user['mobile'])) {
return $this->renderError('请先绑定手机号', [], -9);
}
return $this->renderError('正在维护中', []);
$prize_num = request()->param('prize_num/d', 0); #抽几发
$goods_id = request()->param('goods_id/d', 0); #盒子ID
$num = request()->param('goods_num/d', 0); #第几箱
@ -846,9 +839,9 @@ class Goods extends Base
if (RegInt($num)) {
return $this->renderError("箱号选择错误");
}
if ($goods['type'] == 10) {
return $this->renderError("商城未开放");
}
// if ($goods['type'] == 10) {
// return $this->renderError("商城未开放");
// }
// 使用PaymentCalculator验证抽奖限制限购、消费门槛等
$paymentCalculator = new \app\common\service\PaymentCalculator();
@ -1070,6 +1063,28 @@ class Goods extends Base
'ad_id' => $ad_id,
'click_id' => $user['click_id']
]);
if ($goods['quanju_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
// ->where('addtime', '>=', time() - 86400)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
if ($goods['daily_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->where('addtime', '>=', time() - 86400)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
#微信支付金额大于0
if ($paymentResult['price'] > 0) {
$body = '购买盒子' . $goods['title'];
@ -1091,27 +1106,14 @@ class Goods extends Base
$attach = 'order_qts';
}
if ($goods['quanju_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
$order_info = Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('num', '=', $num)
->where('status', '=', 0)
->where('addtime', '>=', time() - 86400)
->find();
if ($order_info) {
$order_info->status = 2;
$order_info->save();
}
}
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
if ($isTest == "1") {
Db::rollback();
#删除redis
$redis->del($redis_key);
return $this->renderError("支付未开放");
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
// if ($isTest == "1") {
// Db::rollback();
// #删除redis
// $redis->del($redis_key);
// return $this->renderError("支付未开放");
// }
$payRes = (new Pay())->wxCreateOrder($order_num, $paymentResult['price'], $user['openid'], $body, $attach);
if ($payRes['status'] == 1) {
#结果集
@ -1290,18 +1292,18 @@ class Goods extends Base
'addtime' => time(),
]);
}
// 清除相关缓存
if ($res) {
$redis = (new \app\common\server\RedisHelper())->getRedis();
// 清除商品详情缓存
$redis->del("goods_detail_{$goods_id}_{$user['id']}");
// 清除无限抽盒子缓存(如果有)
$redis->del("infinite_goodsdetail_{$goods_id}_{$user['id']}");
return $this->renderSuccess("操作成功");
} else {
return $this->renderError("操作失败");

View File

@ -41,7 +41,7 @@ class Index extends Base
// // unset($tuijian[$key]);
// continue;
// }
// $tuijian_list[] = $advert_value;
$tuijian_list[] = $advert_value;
}
}
$tuijian = $tuijian_list;

View File

@ -507,7 +507,7 @@ class Infinite extends Base
if (empty($user['mobile'])) {
return $this->renderError('请先绑定手机号', [], -9);
}
return $this->renderError('正在维护中', []);
$ad_id = request()->header('adid');
$goods_id = request()->param('goods_id/d', 0); #盒子ID
$prize_num = request()->param('prize_num/d', 0); #抽几发
@ -667,6 +667,27 @@ class Infinite extends Base
'ad_id' => $ad_id,
'click_id' => $user['click_id']
]);
if ($goods['quanju_xiangou'] > 0) {
// 查看有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
if ($goods['daily_xiangou'] > 0) {
// 查看一天内有没有未支付的订单,未支付的订单要先作废
Order::where('goods_id', '=', $goods_id)
->where('user_id', '=', $user['id'])
->where('status', '=', 0)
->where('addtime', '>=', time() - 86400)
->where('order_num', '!=', $order_num)
->update(['status' => 2]);
}
#微信支付金额大于0
if ($paymentResult['price'] > 0) {
@ -675,13 +696,13 @@ class Infinite extends Base
if ($goods['type'] == 16) {
$attach = 'order_fbs';
}
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
if ($isTest == "1") {
Db::rollback();
#删除redis
$redis->del($redis_key);
return $this->renderError("支付未开放");
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
// if ($isTest == "1") {
// Db::rollback();
// #删除redis
// $redis->del($redis_key);
// return $this->renderError("支付未开放");
// }
$payRes = (new Pay())->wxCreateOrder($order_num, $paymentResult['price'], $user['openid'], $body, $attach);
if ($payRes['status'] == 1) {
#结果集

View File

@ -337,24 +337,24 @@ class Login extends Base
if ($uid) {
User::where('id', $user_id)->update(['uid' => $uid]);
}
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
if ($isTest == "1") {
$userCount = ProfitMoney::where('user_id', $user_id)
->where('type', 8)
->where('content', '=', '内测免费送')
->count();
if ($userCount == 0) {
// 使用Redis锁防止重复获取
$redis = (new RedisHelper())->getRedis();
$lockKey = 'user:beta_reward:' . $user_id;
if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
$res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
$logMessages[] = '赠送钻石: 50000';
// 释放锁
$redis->del($lockKey);
}
}
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
// $userCount = ProfitMoney::where('user_id', $user_id)
// ->where('type', 8)
// ->where('content', '=', '内测免费送')
// ->count();
// if ($userCount == 0) {
// // 使用Redis锁防止重复获取
// $redis = (new RedisHelper())->getRedis();
// $lockKey = 'user:beta_reward:' . $user_id;
// if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
// $res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
// $logMessages[] = '赠送钻石: 50000';
// // 释放锁
// $redis->del($lockKey);
// }
// }
// }
$time = time();
#token字符串
$token_num = getRandStr(10);
@ -787,27 +787,27 @@ class Login extends Base
$location = "";// $ip_province . $ip_city;
}
$user_id = $user['id'];
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
if ($isTest == "1") {
// 使用Redis锁防止重复获取
$redis = (new RedisHelper())->getRedis();
$lockKey = 'user:beta_reward:' . $user_id;
if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
$userCount = ProfitMoney::where('user_id', $user_id)
->where('type', 8)
->where('content', '=', '内测免费送')
->count();
if ($userCount == 0) {
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
// // 使用Redis锁防止重复获取
// $redis = (new RedisHelper())->getRedis();
// $lockKey = 'user:beta_reward:' . $user_id;
// if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
// $userCount = ProfitMoney::where('user_id', $user_id)
// ->where('type', 8)
// ->where('content', '=', '内测免费送')
// ->count();
// if ($userCount == 0) {
try {
$res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
} finally {
// 释放锁
$redis->del($lockKey);
}
}
}
}
// try {
// $res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
// } finally {
// // 释放锁
// $redis->del($lockKey);
// }
// }
// }
// }
// 记录登录日志
UserLoginLog::recordLogin(
@ -990,24 +990,24 @@ class Login extends Base
}
// 如果是测试环境,给新用户赠送钻石
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
if ($isTest == "1") {
$userCount = ProfitMoney::where('user_id', $user_id)
->where('type', 8)
->where('content', '=', '内测免费送')
->count();
if ($userCount == 0) {
// 使用Redis锁防止重复获取
$redis = (new RedisHelper())->getRedis();
$lockKey = 'user:beta_reward:' . $user_id;
if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
$res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
$logMessages[] = '赠送钻石: 50000';
// 释放锁
$redis->del($lockKey);
}
}
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
// $userCount = ProfitMoney::where('user_id', $user_id)
// ->where('type', 8)
// ->where('content', '=', '内测免费送')
// ->count();
// if ($userCount == 0) {
// // 使用Redis锁防止重复获取
// $redis = (new RedisHelper())->getRedis();
// $lockKey = 'user:beta_reward:' . $user_id;
// if ($redis->set($lockKey, 1, ['nx', 'ex' => 60])) {
// $res[] = User::changeMoney($user_id, 50000, 8, '内测免费送');
// $logMessages[] = '赠送钻石: 50000';
// // 释放锁
// $redis->del($lockKey);
// }
// }
// }
// 生成账号token
$time = time();

View File

@ -470,8 +470,7 @@ class Notify extends Base
*/
public function CallbackSuccess()
{
$html = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
die($html);
exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');
}
/**
@ -489,7 +488,7 @@ class Notify extends Base
->where(['goods_id' => $goods_id])
->where(['num' => $num])
->where(['status' => 0])
->where('order_type', 'in', [1, 3, 5, 6, 11, 15])
->where('order_type', 'in', [1, 3, 5, 6, 11, 15, 10])
->find();
if ($order) {
@ -1289,7 +1288,7 @@ class Notify extends Base
if ($goodslist) {
$res[] = $this->infinite_drawprize_box($goodslist, $prize_num, $order_id, $user_id, $goods_id, $order_type, $infinite_goods);
// 更新Redis中盒子的热度值增加本次抽奖次数
$this->updateGoodsHeat($goods_id, $prize_num);
@ -1435,7 +1434,7 @@ class Notify extends Base
$res[] = $this->infinite_drawprize_box($goodslist_1, 1, $order_id, $user_id, $goods_id, $order_type, $infinite_goods);
}
}
return $res;
}
@ -2010,7 +2009,7 @@ class Notify extends Base
#开奖==================================================
$res[] = $this->draw_drawprize($order);
#开奖==================================================
// 更新Redis中盒子的热度值
$this->updateGoodsHeat($goods_id, $order['prize_num']);
} else {
@ -2123,7 +2122,7 @@ class Notify extends Base
#开奖==================================================
$res[] = $this->draw_drawprize($order);
#开奖==================================================
// 更新Redis中盒子的热度值
$this->updateGoodsHeat($goods_id, $order['prize_num']);
} else {
@ -2140,7 +2139,9 @@ class Notify extends Base
$access_token = $wxServer->get_access_token();
$open_id = Db::name('user')->where('id', $user_id)->value('openid');
$wxServer->post_order($open_id, $access_token, $order_num);
$pay = new \app\api\controller\Pay();
$pay->post_order($open_id, $access_token, $order_num);
// $wxServer->post_order($open_id, $access_token, $order_num);
}
/**
@ -2214,10 +2215,10 @@ class Notify extends Base
// 获取Redis实例
$redis = (new \app\common\server\RedisHelper())->getRedis();
$cacheKey = "order_goods_count:{$goods_id}";
// 获取当前热度值
$current_heat = $redis->get($cacheKey);
// 如果缓存不存在,先从数据库获取
if ($current_heat === false) {
$current_heat = \app\common\model\OrderList::where('goods_id', '=', $goods_id)
@ -2228,13 +2229,13 @@ class Notify extends Base
// 转为整数
$current_heat = intval($current_heat);
}
// 计算新的热度值
$new_heat = $current_heat + $prize_num;
// 更新Redis缓存设置5分钟过期时间
$redis->set($cacheKey, $new_heat, 300);
return true;
} catch (\Exception $e) {
// 发生异常时记录日志
@ -2242,4 +2243,306 @@ class Notify extends Base
return false;
}
}
/**
* 新的支付结果通知处理方法(通过路径参数接收)
* @param string|null $payment_type 支付方式
* @param string|null $order_type 赏品类型(order_yfs, order_lts等)
* @param int|null $user_id 用户ID
* @param string|null $order_num 订单号
* @param int|null $timestamp 支付时间戳
* @param string|null $sign 加密验签
* @return void
*/
public function order_notify_new($payment_type = null, $order_type = null, $user_id = null, $order_num = null, $timestamp = null, $sign = null)
{
// 记录接收到的参数
writelog('pay_notify_log', json_encode([
'method' => 'order_notify_new',
'payment_type' => $payment_type,
'order_type' => $order_type,
'user_id' => $user_id,
'order_num' => $order_num,
'timestamp' => $timestamp,
'sign' => $sign,
'time' => date('Y-m-d H:i:s')
]));
// 验证所有必要参数是否存在
if (empty($payment_type) || empty($order_type) || empty($user_id) || empty($order_num) || empty($timestamp) || empty($sign)) {
writelog('pay_notify_error', "缺少必要参数: payment_type={$payment_type}, order_type={$order_type}, user_id={$user_id}, order_num={$order_num}, timestamp={$timestamp}, sign={$sign}");
$this->CallbackSuccess(); // 返回成功避免重复通知
return;
}
// 验证时间戳允许6小时的误差
if (abs(time() - intval($timestamp)) > 21600) {
writelog('pay_notify_error', "时间戳验证失败超出6小时的有效期: " . $timestamp);
$this->CallbackSuccess();
return;
}
// 获取Redis实例
$redis = (new \app\common\server\RedisHelper())->getRedis();
$lockKey = "pay_notify_lock:{$order_num}";
// 尝试获取锁过期时间为5分钟
if (!$redis->setnx($lockKey, time())) {
// 如果锁已存在,检查是否已经超时
$lockTime = $redis->get($lockKey);
if ($lockTime && time() - $lockTime < 300) {
// 锁未超时,说明订单正在处理中,直接返回成功
writelog('pay_notify_info', "订单 {$order_num} 正在处理中,跳过重复请求");
$this->CallbackSuccess();
return;
}
// 锁已超时,可以强制获取
$redis->set($lockKey, time(), 300);
} else {
// 成功获取锁,设置过期时间
$redis->expire($lockKey, 300);
}
try {
// 从订单通知表中获取随机字符串
$orderNotify = \app\common\model\OrderNotify::getByOrderNo($order_num);
if (!$orderNotify) {
writelog('pay_notify_error', "未找到订单通知记录: " . $order_num);
$this->CallbackSuccess();
return;
}
$nonce_str = $orderNotify['nonce_str'];
if (empty($nonce_str)) {
writelog('pay_notify_error', "订单 {$order_num} 没有关联的随机字符串");
$this->CallbackSuccess();
return;
}
// 验证签名
$data = [
'payment_type' => $payment_type,
'order_type' => $order_type,
'user_id' => $user_id,
'order_num' => $order_num,
'timestamp' => $timestamp,
'nonce_str' => $nonce_str // 使用从数据库中获取的随机字符串
];
if (!verifyPayNotifySign($data, $sign)) {
writelog('pay_notify_error', "签名验证失败: " . json_encode($data));
$this->CallbackSuccess(); // 签名验证失败,但仍返回成功,避免重复通知
return;
}
$price = 0;
// 开始处理支付
Db::startTrans();
try {
$res = [];
// 更新订单通知状态为处理中
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 1, '回调处理中');
if ($order_type == 'order_list_send') {
// 背包发货处理逻辑
$orderInfo = OrderListSend::where('send_num', '=', $order_num)
->where('status', '=', 0)
->find();
if (!$orderInfo) {
writelog('pay_notify_error', "未找到发货订单或状态错误: " . $order_num);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '未找到发货订单或状态错误');
Db::rollback();
$this->CallbackSuccess();
return;
}
$user_id = $orderInfo['user_id'];
$freight = $orderInfo['freight'];
$res[] = $this->reward_order_handle($user_id, $orderInfo['id']);
// 记录微信支付
$res[] = ProfitPay::insert([
'user_id' => $user_id,
'order_num' => $order_num,
'change_money' => $freight,
'content' => '背包发货',
'pay_type' => $payment_type == 'alipay' ? 2 : 1,
'addtime' => time(),
]);
// 微信官方公众号发货通知
if ($payment_type == "wxpay") {
$this->wx_gf_fahuo($user_id, $order_num);
}
} else {
// 普通盒子订单处理逻辑
$orderInfo = Order::where('order_num', '=', $order_num)
->find();
if (!$orderInfo) {
writelog('pay_notify_error', "未找到订单: " . $order_num);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '未找到订单');
Db::rollback();
$this->CallbackSuccess();
return;
}
// 检查订单状态
if ($orderInfo['status'] != 0) {
writelog('pay_notify_error', "订单状态不正确: " . $order_num . ", 状态: " . $orderInfo['status']);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '订单状态不正确');
Db::rollback();
$this->CallbackSuccess();
return;
}
// 验证用户ID是否一致
if ($orderInfo['user_id'] != $user_id) {
writelog('pay_notify_error', "用户ID不匹配: 通知中的用户ID={$user_id}, 订单中的用户ID={$orderInfo['user_id']}");
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '用户ID不匹配');
Db::rollback();
$this->CallbackSuccess();
return;
}
$price = $orderInfo['price'];
// 根据订单类型执行不同的处理逻辑
if (in_array($order_type, ['order_wxs', 'order_fbs'])) {
// 无限赏处理
$res[] = $this->infinite_drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id'], $orderInfo['num']);
} elseif (in_array($order_type, ['order_yfs', 'order_lts', 'order_zzs', 'order_flw', 'order_scs'])) {
// 一番赏、擂台赏等处理
$res[] = $this->drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id'], $orderInfo['num']);
} elseif ($order_type == 'order_ckj') {
// 抽卡机处理
$res[] = $this->cardextractor_drawprize_notice($user_id, $orderInfo['id'], $orderInfo['goods_id']);
} else {
writelog('pay_notify_error', "不支持的订单类型: " . $order_type);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '不支持的订单类型');
Db::rollback();
$this->CallbackSuccess();
return;
}
// 微信官方公众号发货通知
if ($payment_type == "wxpay") {
$this->wx_gf_fahuo($user_id, $orderInfo['order_num']);
}
// 记录支付信息
$res[] = Db::name('profit_pay')->insert([
'user_id' => $user_id,
'order_num' => $order_num,
'change_money' => $price,
'content' => '购买盒子' . $orderInfo['goods_title'],
'pay_type' => $payment_type == 'alipay' ? 2 : 1,
'addtime' => time(),
]);
}
if (resCheck($res)) {
Db::commit();
writelog('pay_notify_success', "支付成功处理完成: " . $order_num);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 1, '支付处理成功');
} else {
Db::rollback();
writelog('pay_notify_error', "支付处理失败,可能存在业务逻辑错误: " . $order_num);
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '支付处理失败,业务逻辑错误');
}
} catch (\Exception $e) {
Db::rollback();
writelog('pay_notify_error', "支付处理异常: " . $e->getMessage() . ", 订单号: " . $order_num);
try {
// 标记订单为卡单状态
if ($order_type == 'order_list_send') {
OrderListSend::where(['send_num' => $order_num])->update(['kd_is' => 1]);
} else {
Order::where(['order_num' => $order_num])->update(['kd_is' => 1]);
}
\app\common\model\OrderNotify::updateStatus($orderNotify['id'], 2, '支付处理异常: ' . $e->getMessage());
} catch (\Exception $ex) {
writelog('pay_notify_error', "更新卡单状态异常: " . $ex->getMessage());
}
}
// 无论处理结果如何,都释放锁并返回成功,避免重复通知
$redis->del($lockKey);
$this->CallbackSuccess();
} catch (\Exception $e) {
// 处理外层异常(如锁获取失败等)
writelog('pay_notify_error', "处理订单异常: " . $e->getMessage() . ", 订单号: " . $order_num);
$this->CallbackSuccess();
}
}
public function test_notify_url()
{
// 测试生成支付通知URL
$payment_type = 'wxpay';
$order_type = 'order_wxs';
$user_id = 123;
$order_num = 'MH_' . date('YmdHis') . mt_rand(1000, 9999);
$nonce_str = getRandStr(16);
try {
// 生成URL
$notify_url = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_num, $nonce_str);
// 解析URL以提取参数和签名
$url_parts = parse_url($notify_url);
$path_parts = explode('/', trim($url_parts['path'], '/'));
// 路径格式应该是: notify/payment_type/order_type/user_id/order_num/timestamp/sign
$extracted_payment_type = $path_parts[1] ?? '';
$extracted_order_type = $path_parts[2] ?? '';
$extracted_user_id = $path_parts[3] ?? '';
$extracted_order_num = $path_parts[4] ?? '';
$extracted_timestamp = $path_parts[5] ?? '';
$extracted_sign = $path_parts[6] ?? '';
// 重新构建验证数据
$verify_data = [
'payment_type' => $extracted_payment_type,
'order_type' => $extracted_order_type,
'user_id' => $extracted_user_id,
'order_num' => $extracted_order_num,
'timestamp' => $extracted_timestamp,
'nonce_str' => $nonce_str
];
// 验证签名
$is_valid = verifyPayNotifySign($verify_data, $extracted_sign);
// 返回测试结果
return json([
'original' => [
'payment_type' => $payment_type,
'order_type' => $order_type,
'user_id' => $user_id,
'order_num' => $order_num,
'nonce_str' => $nonce_str
],
'generated_url' => $notify_url,
'extracted' => [
'payment_type' => $extracted_payment_type,
'order_type' => $extracted_order_type,
'user_id' => $extracted_user_id,
'order_num' => $extracted_order_num,
'timestamp' => $extracted_timestamp,
'sign' => $extracted_sign
],
'is_valid' => $is_valid,
'message' => '支付通知URL测试成功'
]);
} catch (\Exception $e) {
return json([
'error' => $e->getMessage(),
'message' => '生成支付通知URL失败请确保提供了所有必要参数'
]);
}
}
}

View File

@ -23,39 +23,23 @@ class Pay extends Base
{
// 获取系统微信设置
$wechat_setting = getConfig('wechat_setting');
$wechatofficialaccount_setting = getConfig('wechatofficialaccount_setting');
if ($this->ish5()) {
// 公众号配置不变
$config = getConfig('wechatofficialaccount');
// 如果系统设置中存在微信公众号配置,则优先使用
if (!empty($wechatofficialaccount_setting) && !empty($wechatofficialaccount_setting['appid']) && !empty($wechatofficialaccount_setting['appSecret'])) {
$this->appid = $wechatofficialaccount_setting['appid'];
// 初始使用默认配置,具体支付时会根据订单号重新获取商户信息
$wxpayConfig = WxPayHelper::getWxPayConfig();
if (!empty($wxpayConfig['merchant'])) {
$this->appid = $wxpayConfig['appid'];
$this->merchant = $wxpayConfig['merchant']['mch_id'];
$this->secretKey = $wxpayConfig['merchant']['keys'];
} else {
// 如果没有获取到商户信息,则使用旧方式
$config = getConfig('weixinpay');
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
$this->appid = $wechat_setting['appid'];
} else {
// 公众号使用自己的appid
$this->appid = $config['appid'];
}
$this->merchant = $config['mch_id'];
$this->secretKey = $config['keys'];
} else {
// 初始使用默认配置,具体支付时会根据订单号重新获取商户信息
$wxpayConfig = WxPayHelper::getWxPayConfig();
if (!empty($wxpayConfig['merchant'])) {
$this->appid = $wxpayConfig['appid'];
$this->merchant = $wxpayConfig['merchant']['mch_id'];
$this->secretKey = $wxpayConfig['merchant']['keys'];
} else {
// 如果没有获取到商户信息,则使用旧方式
$config = getConfig('weixinpay');
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
$this->appid = $wechat_setting['appid'];
} else {
$this->appid = $config['appid'];
}
$this->merchant = $config['mch_id'];
$this->secretKey = $config['keys'];
}
}
$this->noticeurl = request()->domain() . '/api/notify/order_notify';#订单回调URL
@ -70,12 +54,12 @@ class Pay extends Base
*/
protected function setMerchantByOrderNum($order_num)
{
if (!$this->ish5() && strpos($order_num, 'MH_') === 0) {
if (!$this->ish5() && (strpos($order_num, 'MH_') === 0 || strpos($order_num, 'FH_') === 0)) {
// 提取订单中的商户前缀和小程序前缀
$prefixInfo = WxPayHelper::extractOrderPrefix($order_num);
$merchant_prefix = null;
$miniprogram_prefix = null;
// 新格式返回为数组,包含商户前缀和可能的小程序前缀
if (is_array($prefixInfo)) {
$merchant_prefix = $prefixInfo['merchant_prefix'] ?? null;
@ -84,13 +68,13 @@ class Pay extends Base
// 兼容旧格式,直接作为商户前缀
$merchant_prefix = $prefixInfo;
}
// 优先根据小程序前缀获取配置
$wxpayConfig = null;
if (!empty($miniprogram_prefix)) {
// 通过小程序前缀获取小程序配置
$miniprogramConfig = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByOrderPrefix($miniprogram_prefix);
if (!empty($miniprogramConfig)) {
// 使用小程序关联的商户配置
if (!empty($merchant_prefix)) {
@ -104,17 +88,17 @@ class Pay extends Base
}
}
}
// 如果没有通过小程序前缀获取到配置,则回退到商户前缀
if (empty($wxpayConfig) && !empty($merchant_prefix)) {
$wxpayConfig = WxPayHelper::getFixedWxPayConfig($merchant_prefix);
}
// 如果前两种方式都没有获取到配置,则使用默认配置
if (empty($wxpayConfig)) {
return false;
}
if (!empty($wxpayConfig['merchant'])) {
// 更新当前实例的商户信息
$this->appid = $wxpayConfig['appid'];
@ -138,7 +122,7 @@ class Pay extends Base
{
// 确保设置正确的商户配置
$this->setMerchantByOrderNum($order_num);
if ($type == 1) {
$order = OrderModel::getInfo(['order_num' => $order_num]);
if ($order['status'] != 1) {
@ -232,7 +216,7 @@ class Pay extends Base
{
// 根据订单号设置正确的商户配置
$this->setMerchantByOrderNum($order_num);
$openidx = $openid;
if ($this->ish5()) {
$user = User::getInfo(['openid' => $openidx]);
@ -306,7 +290,7 @@ class Pay extends Base
{
// 根据订单号设置正确的商户配置
$this->setMerchantByOrderNum($order_no);
$openidx = $openid;
if ($this->ish5()) {
$user = User::getInfo(['openid' => $openidx]);
@ -315,11 +299,38 @@ class Pay extends Base
}
}
$body = mb_substr($body, 0, 30);
$notifyUrl = $this->noticeurl;
// 使用新的动态路由生成通知URL
$user = User::where('openid', $openidx)->find();
$payment_type = 'wxpay';
$order_type = $attach;
$user_id = $user ? $user['id'] : 0;
// 支付使用的随机数
$nonce_str = $this->genRandomString();
// 回调使用的随机数(与支付随机数分离)
$callback_nonce_str = $this->genRandomString();
// 生成新的支付通知URL
$notifyUrl = generatePayNotifyUrl($payment_type, $order_type, $user_id, $order_no, $callback_nonce_str);
$is_test = $user['istest'];
// if ($is_test == 2) {
// $price = 0.01;
// }
// 将通知URL和随机字符串保存到order_notify表中
Db::name('order_notify')->insert([
'order_no' => $order_no,
'notify_url' => $notifyUrl,
'nonce_str' => $callback_nonce_str,
'pay_time' => date('Y-m-d H:i:s'),
'pay_amount' => $price,
'status' => 0,
'retry_count' => 0,
'create_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s')
]);
$params['appid'] = $this->appid;
$params['mch_id'] = $this->merchant;
$params['nonce_str'] = $this->genRandomString();
$params['nonce_str'] = $nonce_str;
$params['body'] = $body;
$params['attach'] = $attach;
$params['out_trade_no'] = $order_no;
@ -327,7 +338,7 @@ class Pay extends Base
$params['total_fee'] = round($price * 100, 2);
$params['spbill_create_ip'] = $this->get_client_ip();
$params['trade_type'] = 'JSAPI';
$params['openid'] = $openidx ;
$params['openid'] = $openidx;
$params['sign'] = $this->MakeSign($params);
$xml = $this->data_to_xml($params);
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
@ -347,6 +358,97 @@ class Pay extends Base
}
}
public function post_order($openid, $access_token, $order_num, $title = '订单发货')
{
$msg = "本单购买商品已发放至[小程序盒柜]";
if (strpos($order_num, 'FH_') === 0) {
$msg = "本单购买的商品正在打包,请联系客服获取物流信息";
}
// 根据订单号设置正确的商户配置
$this->setMerchantByOrderNum($order_num);
$date = new \DateTime();
// $this->appid
//订单发货时间
$formattedDate = $date->format('Y-m-d\TH:i:s');
$request_url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" . $access_token;
$param = '{
"order_key": {
"order_number_type": 1,
"mchid":"' . $this->merchant . '",
"out_trade_no":"' . $order_num . '"
},
"logistics_type": 4,
"delivery_mode": 1,
"shipping_list": [
{
"item_desc": "'.$msg.'"
}
],
"upload_time": "' . $formattedDate . '+08:00",
"payer": {
"openid":"' . $openid . '"
}
}';
// 记录请求参数日志
writelog('post_order_log', json_encode([
'method' => 'post_order',
'order_num' => $order_num,
'openid' => $openid,
'merchant' => $this->merchant,
'request_url' => $request_url,
'param' => $param,
'time' => date('Y-m-d H:i:s')
]));
$res = curlPost($request_url, $param);
$res = json_decode($res, true);
// 记录响应结果日志
writelog('post_order_log', json_encode([
'method' => 'post_order_response',
'order_num' => $order_num,
'response' => $res,
'time' => date('Y-m-d H:i:s')
]));
if ($res['errcode'] == 0 && $res['errmsg'] == 'ok') {
return 1;
} else {
// 发货失败将订单信息存入Redis等待定时任务重试
$redis = (new \app\common\server\RedisHelper())->getRedis();
$key = 'post_order:' . $order_num;
// 存储发货失败的订单信息
$orderData = [
'openid' => $openid,
'appid' => $this->appid,
// 不存储access_token因为可能过期
'order_num' => $order_num,
'title' => $title,
'merchant' => $this->merchant,
'error_code' => $res['errcode'] ?? 'unknown',
'error_msg' => $res['errmsg'] ?? 'unknown',
'retry_count' => 0,
'last_retry_time' => time(),
'create_time' => time()
];
// 存入Redis设置过期时间为3天
$redis->set($key, json_encode($orderData));
$redis->expire($key, 3 * 24 * 3600);
// 记录存入Redis的日志
writelog('post_order_log', json_encode([
'method' => 'post_order_redis_save',
'order_num' => $order_num,
'redis_key' => $key,
'data' => $orderData,
'time' => date('Y-m-d H:i:s')
]));
return 2;
}
}
/**
* 生成签名

View File

@ -119,11 +119,12 @@ class Sign extends Base
if (!$is_sign) {
$signDays_1 = $signDays + 1;
}
$sign_multiple = \app\common\helper\ConfigHelper::getSystemTestKey("sign_multiple");
$sign_multiple = intval($sign_multiple);
if($sign_multiple<=0){
$sign_multiple = 1;
}
$sign_multiple =1;
// $sign_multipl= \app\common\helper\ConfigHelper::getSystemTestKey("sign_multiple");
// $sign_multiple = intval($sign_multiple);
// if($sign_multiple<=0){
// $sign_multiple = 1;
// }
// 处理配置关联的奖励信息
$nowDay = date('j');

View File

@ -708,10 +708,10 @@ class Warehouse extends Base
if ($type != 1 && $type != 2) {
return $this->renderError("请求参数错误");
}
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
if ($isTest == "1") {
return $this->renderError("发货未开放");
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("disable_wechat_pay");
// if ($isTest == "1") {
// return $this->renderError("发货未开放");
// }
$recovery_info = request()->param('recovery_info', '');
if (empty($recovery_info)) {
return $this->renderError("请选择兑换的赏品");

View File

@ -156,6 +156,11 @@ Route::any('seckill_order_detail', 'Seckill/seckill_order_detail');
Route::any('seckill_order_confirm', 'Seckill/seckill_order_confirm');
Route::any('seckill_order_logistics', 'Seckill/seckill_order_logistics');
// 添加新的支付通知动态路由(所有参数必选)
Route::any('notify/<payment_type>/<order_type>/<user_id>/<order_num>/<timestamp>/<sign>', 'Notify/order_notify_new');
//https://testapi.zfunbox.cn/notify/wxpay/order_wxs/21623/MH_DEAMH202504245256999793456/1745435988/6154CF0189BBDF73C7AD0E5246B5397B
// 添加测试路由
Route::any('test_notify_url', 'Notify/test_notify_url');
//generate_urllink
Route::any('generate_urllink', 'Index/generate_urllink');

View File

@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
use app\common\server\RedisHelper;
/**
* 重试微信发货失败的订单
*/
class PostOrderRetry extends Command
{
//php think PostOrderRetry
protected function configure()
{
// 指令配置
$this->setName('PostOrderRetry')
->setDescription('重试微信发货失败的订单');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('开始处理发货失败的订单...');
// 获取Redis实例
$redis = (new RedisHelper())->getRedis();
// 获取所有发货失败的订单键
$failedOrderKeys = $redis->keys('post_order:*');
if (empty($failedOrderKeys)) {
$output->writeln('没有发现发货失败的订单,任务结束');
return;
}
$output->writeln('发现' . count($failedOrderKeys) . '个发货失败的订单');
// 重试限制
$maxRetryCount = 10; // 最大重试次数
$retryInterval = 10; // 重试间隔(秒)
$nowTime = time();
// 处理每个失败的订单
foreach ($failedOrderKeys as $key) {
// 获取订单数据
$orderDataJson = $redis->get($key);
if (empty($orderDataJson)) {
continue;
}
$orderData = json_decode($orderDataJson, true);
if (!is_array($orderData)) {
// 数据格式错误,删除此键
$redis->del($key);
continue;
}
// 检查重试次数和时间间隔
if ($orderData['retry_count'] >= $maxRetryCount) {
// 超过最大重试次数,记录日志并删除
writelog('post_order_retry_log', json_encode([
'method' => 'post_order_retry_max_reached',
'order_num' => $orderData['order_num'],
'retry_count' => $orderData['retry_count'],
'time' => date('Y-m-d H:i:s')
]));
$redis->del($key);
$output->writeln("订单 {$orderData['order_num']} 超过最大重试次数({$maxRetryCount}),已移除");
continue;
}
// 检查是否达到重试间隔
if (($nowTime - $orderData['last_retry_time']) < $retryInterval) {
$output->writeln("订单 {$orderData['order_num']} 未达到重试间隔,跳过");
continue;
}
// 尝试重新发货
try {
$access_token = "";
$appid = $orderData['appid'];
//获取对应的小程序
$app = \app\common\helper\MiniprogramHelper::getMiniprogramConfigByAppid($appid);
$wx_secret = $app['app_secret'];
// 获取access_token - 优先使用微信服务器类获取
$wxServer = new \app\common\server\Wx($this->app);
$access_token = $wxServer->get_access_appid_token($appid, $wx_secret);
if (empty($access_token)) {
throw new \Exception("无法获取有效的access_token");
}
// 调用发货接口
$payController = new \app\api\controller\Pay();
$retryResult = $payController->post_order(
$orderData['openid'],
$access_token,
$orderData['order_num'],
$orderData['title'] ?? '订单发货'
);
// 记录重试日志
writelog('post_order_retry_log', json_encode([
'method' => 'post_order_retry_attempt',
'order_num' => $orderData['order_num'],
'retry_count' => $orderData['retry_count'] + 1,
'result' => $retryResult,
'time' => date('Y-m-d H:i:s')
]));
// 处理重试结果
if ($retryResult == 1) {
// 发货成功删除Redis中的记录
$redis->del($key);
$output->writeln("订单 {$orderData['order_num']} 重试发货成功,已从重试队列移除");
} else {
// 发货失败,更新重试信息
$orderData['retry_count'] += 1;
$orderData['last_retry_time'] = $nowTime;
$redis->set($key, json_encode($orderData));
$output->writeln("订单 {$orderData['order_num']}{$orderData['retry_count']} 次重试发货失败,将在下次重试");
}
} catch (\Exception $e) {
// 记录异常日志
writelog('post_order_retry_log', json_encode([
'method' => 'post_order_retry_exception',
'order_num' => $orderData['order_num'],
'error' => $e->getMessage(),
'time' => date('Y-m-d H:i:s')
]));
// 更新重试信息
$orderData['retry_count'] += 1;
$orderData['last_retry_time'] = $nowTime;
$orderData['last_error'] = $e->getMessage();
$redis->set($key, json_encode($orderData));
$output->writeln("订单 {$orderData['order_num']} 重试时发生异常:" . $e->getMessage());
}
}
$output->writeln('发货失败订单处理完成!');
}
}

View File

@ -153,21 +153,21 @@ if (!function_exists('getConfig')) {
// // 生成缓存键
// $redis = (new \app\common\server\RedisHelper())->getRedis();
// $cache_key = "config:{$type}";
// // 尝试从缓存获取数据
// $cached_data = $redis->get($cache_key);
// if ($cached_data) {
// return json_decode($cached_data, true);
// }
$content = Db::name('config')->where(['key' => $type])->value('value');
$config = json_decode($content, true);
// 将数据存入缓存设置10分钟过期时间
// if ($config) {
// $redis->set($cache_key, json_encode($config), 600);
// }
return $config;
}
}
@ -287,7 +287,7 @@ if (!function_exists('create_order_no')) {
function create_order_no($pre = 'NO_', $tale = 'order', $field = 'order_num')
{
// 检查是否包含多商户前缀标记,处理商户随机选择并获取前缀
if (strpos($pre, 'MH_') !== false) {
if (strpos($pre, 'MH_') !== false || strpos($pre, 'FH_') !== false) {
// 引入微信支付帮助类
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
@ -295,7 +295,7 @@ if (!function_exists('create_order_no')) {
if (!empty($wxpayConfig['merchant']) && !empty($wxpayConfig['merchant']['order_prefix'])) {
$merchant_prefix = $wxpayConfig['merchant']['order_prefix'];
// 在MH_后面添加商户前缀而不是替换MH_
$pre = 'MH_' . $merchant_prefix;
$pre = $pre . $merchant_prefix;
}
// 获取当前域名的微信小程序配置
@ -701,9 +701,30 @@ if (!function_exists('time_jian')) {
}
}
if (!function_exists('order_callback')) {
/**
* 订单回调方法,设置通用的订单方法
* @param mixed $order_no
* @param mixed $notifyUrl
* @param mixed $callback_nonce_str
* @return void
*/
function order_callback($order_no, $notifyUrl, $callback_nonce_str)
{
// 将通知URL和随机字符串保存到订单表中
Db::name('order')->where('order_num', $order_no)->update([
'nonce_str' => $callback_nonce_str,
'order_notify' => $notifyUrl
]);
}
}
// 订单支付日志
function writelog($filename, $content)
{
try {
$date = date('Y-m-d');
$file = "./pay_log/" . $date;
if (!is_dir($file)) {
@ -714,6 +735,9 @@ function writelog($filename, $content)
$open = fopen($file, "a");
fwrite($open, $content);
fclose($open);
} catch (\Throwable $th) {
}
}
@ -814,4 +838,91 @@ if (!function_exists('removeTrailingZeros')) {
}
return $numStr;
}
}
/**
* 生成支付通知URL
* @param string $payment_type 支付方式(wxpay, alipay等)
* @param string $order_type 赏品类型(order_yfs, order_lts等)
* @param int $user_id 用户ID
* @param string $order_num 订单号
* @param string $nonce_str 随机字符串,用于签名
* @return string 完整的回调URL
*/
function generatePayNotifyUrl($payment_type = null, $order_type = null, $user_id = null, $order_num = null, $nonce_str = null)
{
// 验证所有必须参数
if (empty($payment_type) || empty($order_type) || empty($user_id) || empty($order_num)) {
throw new \Exception("支付通知URL的所有参数都是必选的");
}
// 如果没有传入随机字符串,则生成一个
if (empty($nonce_str)) {
$nonce_str = getRandStr(16, false);
}
$timestamp = time();
$data = [
'payment_type' => $payment_type,
'order_type' => $order_type,
'user_id' => $user_id,
'order_num' => $order_num,
'timestamp' => $timestamp,
'nonce_str' => $nonce_str
];
// 生成签名
$sign = generatePayNotifySign($data);
// 返回完整URL所有参数都是必选的
return request()->domain() . "/api/notify/{$payment_type}/{$order_type}/{$user_id}/{$order_num}/{$timestamp}/{$sign}";
}
/**
* 生成支付通知URL的签名
* @param array $data 签名数据
* @return string 签名字符串
*/
function generatePayNotifySign($data)
{
// 移除空值
foreach ($data as $key => $value) {
if (empty($value)) {
unset($data[$key]);
}
}
// 按键名排序
ksort($data);
// 拼接字符串
$stringA = '';
foreach ($data as $key => $value) {
$stringA .= "{$key}={$value}&";
}
// 获取密钥
$wxpay_config = getConfig('wxpay');
$key = $wxpay_config['key'] ?? 'l044imysi1vsmobyrkkfnaniu5bhiupd'; // 默认密钥
// 加上密钥
$stringA .= "key=" . $key;
// MD5加密并转大写
return strtoupper(md5($stringA));
}
/**
* 验证支付通知URL的签名
* @param array $data 待验证的数据
* @param string $sign 传入的签名
* @return bool 验证是否通过
*/
function verifyPayNotifySign($data, $sign)
{
// 生成签名
$newSign = generatePayNotifySign($data);
// 比较签名是否一致
return $sign === $newSign;
}

View File

@ -294,19 +294,19 @@ class ConfigHelper
return self::$systemTest;
}
// 实例化Redis助手
$redis = new RedisHelper();
// // 实例化Redis助手
// $redis = new RedisHelper();
// 设置Redis键名
$redisKey = 'config:system_test';
// // 设置Redis键名
// $redisKey = 'config:system_test';
// 尝试从Redis获取
$cachedValue = $redis->get($redisKey);
if ($cachedValue !== false) {
// 缓存结果到静态属性
self::$systemTest = json_decode($cachedValue, true);
return self::$systemTest;
}
// // 尝试从Redis获取
// $cachedValue = $redis->get($redisKey);
// if ($cachedValue !== false) {
// // 缓存结果到静态属性
// self::$systemTest = json_decode($cachedValue, true);
// return self::$systemTest;
// }
// Redis中不存在从数据库获取
$config = Db::name('config')
@ -317,7 +317,7 @@ class ConfigHelper
$configArray = json_decode($config, true) ?: [];
// 存入Redis过期时间为1小时3600秒
$redis->set($redisKey, json_encode($configArray), 3600);
// $redis->set($redisKey, json_encode($configArray), 3600);
// 缓存结果到静态属性
self::$systemTest = $configArray;
@ -342,7 +342,7 @@ class ConfigHelper
return "0";
}
if ($key == "disable_wechat_pay") {
return "1";
return "0";
}
return $system_test['enable_test'];

View File

@ -117,6 +117,33 @@ class MiniprogramHelper
return self::getCompatibleConfig();
}
/**
* 根据appid获取小程序配置
*
* @param string $appid 小程序appid
* @return array 小程序配置
*/
public static function getMiniprogramConfigByAppid($appid)
{
// 加载所有小程序配置
self::loadMiniprogramConfigs();
// 如果没有配置,返回空数组
if (empty(self::$cache) || empty(self::$cache['miniprograms'])) {
return self::getCompatibleConfig();
}
// 遍历所有小程序配置匹配appid
foreach (self::$cache['miniprograms'] as $miniprogram) {
if (isset($miniprogram['appid']) && $miniprogram['appid'] === $appid) {
return $miniprogram;
}
}
// 如果没有匹配的appid返回默认小程序配置
return self::getDefaultMiniprogramConfig();
}
/**
* 获取与商户ID关联的小程序配置
*
@ -215,4 +242,6 @@ class MiniprogramHelper
{
self::$cache = null;
}
}

View File

@ -65,7 +65,7 @@ class WxPayHelper
// 过滤出关联的商户
$associatedMerchants = [];
foreach ($weixinpay_setting['merchants'] as $m) {
if (in_array($m['id'] ?? '', $miniprogram['merchants'])) {
if (in_array($m['mch_id'] ?? '', $miniprogram['merchants'])) {
$associatedMerchants[] = $m;
}
}
@ -177,7 +177,7 @@ class WxPayHelper
*/
public static function extractOrderPrefix($order_no)
{
if (strpos($order_no, 'MH_') === 0) {
if (strpos($order_no, 'MH_') === 0||strpos($order_no, 'FH_') === 0) {
// 尝试提取MH_后的字符作为商户前缀
// 商户前缀长度为3如果有小程序前缀则总长度为53+2
$totalPrefix = substr($order_no, 3, 5);

View File

@ -0,0 +1,97 @@
<?php
namespace app\common\model;
use app\common\model\Base;
use think\Model;
class OrderNotify extends Base
{
// 设置当前模型对应的完整数据表名称
protected $table = 'order_notify';
// 设置主键
protected $pk = 'id';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
/**
* 获取列表
*/
public static function getList($where = [], $field = '*', $order = '', $pageSize = "15")
{
$list = self::where($where)
->field($field)
->order($order)
->paginate(['list_rows' => $pageSize, 'query' => request()->param()]);
$page = $list->render();
$data['list'] = $list->toArray()['data'];
$data['count'] = $list->total();
$data['last_page'] = $list->toArray()['last_page'];
$data['page'] = $page;
return $data;
}
/**
* 获取列表 不分页
*/
public static function getAllList($where = [], $field = '*', $order = '', $limit = '0')
{
$data = self::where($where)
->field($field)
->order($order)
->limit($limit)
->select();
return $data;
}
/**
* 获取单条数据
*/
public static function getInfo($where = [], $field = '*')
{
$data = self::where($where)
->field($field)
->find();
return $data;
}
/**
* 根据订单号获取回调信息
*/
public static function getByOrderNo($orderNo, $field = '*')
{
return self::where('order_no', $orderNo)
->field($field)
->find();
}
/**
* 更新回调状态
*/
public static function updateStatus($id, $status, $remark = null)
{
$data = [
'status' => $status,
'last_notify_time' => date('Y-m-d H:i:s')
];
if (!is_null($remark)) {
$data['remark'] = $remark;
}
return self::update($data, ['id' => $id]);
}
/**
* 增加重试次数
*/
public static function increaseRetryCount($id)
{
return self::where('id', $id)
->inc('retry_count')
->update([
'last_notify_time' => date('Y-m-d H:i:s')
]);
}
}

View File

@ -100,13 +100,13 @@ class UserSign extends Base
*/
protected static function processSignRewards($user_id, $type, $day)
{
$sign_multiple = ConfigHelper::getSystemTestKey("sign_multiple");
if (!$sign_multiple) {
$sign_multiple = 0;
} else {
$sign_multiple = intval($sign_multiple);
}
$sign_multiple=1;
// $sign_multiple = ConfigHelper::getSystemTestKey("sign_multiple");
// if (!$sign_multiple) {
// $sign_multiple = 0;
// } else {
// $sign_multiple = intval($sign_multiple);
// }
if ($sign_multiple <= 0) {
$sign_multiple = 1;
}

View File

@ -16,30 +16,13 @@ class Wx extends MyController
{
// 使用新的MiniprogramHelper获取当前域名对应的小程序配置
$miniprogram = \app\common\helper\MiniprogramHelper::getMiniprogramConfig();
// 设置appid和secret
if (!empty($miniprogram) && !empty($miniprogram['appid'])) {
self::$wx_appid = $miniprogram['appid'];
self::$wx_secret = $miniprogram['app_secret'];
} else {
// 旧的兼容代码,逐步过渡,最终应该删除
$wechat_setting = getConfig("wechat_setting");
// 获取微信支付配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
// 如果系统设置中存在微信配置,则优先使用
if (!empty($wechat_setting) && !empty($wechat_setting['appid']) && !empty($wechat_setting['appSecret'])) {
self::$wx_appid = $wechat_setting['appid'];
self::$wx_secret = $wechat_setting['appSecret'];
} else {
self::$wx_appid = $wxpayConfig['appid'];
// 从旧配置或商户配置中获取appSecret
$weixinpay = getConfig("weixinpay");
self::$wx_secret = !empty($weixinpay['appSecret']) ? $weixinpay['appSecret'] : '';
}
}
// 获取对应的商户信息
// 如果小程序配置了关联商户,则使用关联商户中的第一个
if (!empty($miniprogram) && !empty($miniprogram['merchants']) && is_array($miniprogram['merchants']) && count($miniprogram['merchants']) > 0) {
@ -55,7 +38,7 @@ class Wx extends MyController
}
}
}
// 如果没有找到关联商户或没有配置,则使用默认商户配置
$wxpayConfig = \app\common\helper\WxPayHelper::getWxPayConfig();
$merchant = $wxpayConfig['merchant'];
@ -212,9 +195,19 @@ class Wx extends MyController
* 获取access_token
*/
public function get_access_token()
{
return $this->get_access_appid_token(self::$wx_appid, self::$wx_secret);
}
/**
* 获取access_token
*/
public function get_access_appid_token($appid, $wx_secret)
{
$redis = (new \app\common\server\RedisHelper())->getRedis();
$redis_key = 'wx_access_token:' . self::$wx_appid;
$redis_key = 'wx_access_token:' . $appid;
$access_token_info = $redis->get($redis_key);
if ($access_token_info) {
@ -224,7 +217,7 @@ class Wx extends MyController
}
}
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . self::$wx_appid . "&secret=" . self::$wx_secret;
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appid . "&secret=" . $wx_secret;
$res_access_token = $this->get_curl_data($url);
if (isset($res_access_token['errcode'])) {
return $this->renderError('获取失败');
@ -244,7 +237,6 @@ class Wx extends MyController
return $access_token;
}
/**
* @param $url 请求链接
*/
@ -318,41 +310,7 @@ class Wx extends MyController
$return_content = curl_exec($ch);
return $return_content;
}
public function post_order($openid, $access_token, $order_num, $title = '订单发货')
{
$date = new \DateTime();
//2023-08-07T17:16:31
//2025-03-04T21:06:59
// 格式化时间为 yyyy-MM-dd HH:mm:ss
$formattedDate = $date->format('Y-m-d\TH:i:s');
$request_url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" . $access_token;
$param = '{
"order_key": {
"order_number_type": 1,
"mchid":"' . self::$mch . '",
"out_trade_no":"' . $order_num . '"
},
"logistics_type": 3,
"delivery_mode": 1,
"shipping_list": [
{
"item_desc": "本单购买商品已发放至[小程序盒柜]"
}
],
"upload_time": "' . $formattedDate . '+08:00",
"payer": {
"openid":"' . $openid . '"
}
}';
$res = curlPost($request_url, $param);
$res = json_decode($res, true);
if ($res['errcode'] == 0 && $res['errmsg'] == 'ok') {
return 1;
} else {
return 2;
}
}
public function post_order_one($openid, $access_token, $order_num, $title = '订单发货')

View File

@ -155,7 +155,7 @@ class PaymentCalculator
} else {
// 支付模式下余额不足无法抵扣
$use_money = 0;
return ['status' => 0, 'msg' => '金额不足'];
return ['status' => 0, 'msg' => '钻石不足'];
}
}
}
@ -181,7 +181,7 @@ class PaymentCalculator
} else {
// 支付模式下货币不足无法抵扣
$use_integral = 0;
return ['status' => 0, 'msg' => '金额不足'];
return ['status' => 0, 'msg' => 'UU币不足'];
}
}
}
@ -207,7 +207,7 @@ class PaymentCalculator
} else {
// 支付模式下货币2不足无法抵扣
$use_money2 = 0;
return ['status' => 0, 'msg' => '金额不足'];
return ['status' => 0, 'msg' => '达达卷不足'];
}
}
}
@ -283,13 +283,14 @@ class PaymentCalculator
}
} else {
// 常规消费验证
$isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
$user_price = 0;
if ($isTest == "1") {
$user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('order_zhe_total');
} else {
$user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('price');
}
// $isTest = \app\common\helper\ConfigHelper::getSystemTestKey("enable_test");
// if ($isTest == "1") {
// $user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('order_zhe_total');
// } else {
// $user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('price');
// }
$user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('price');
if ($user_price < $choujiang_xianzhi) {
if ($user['istest'] > 0) {
$user_price = Order::where('user_id', '=', $user_id)->where('status', '=', 1)->sum('order_zhe_total');

View File

@ -9,5 +9,6 @@ return [
'CreateOffshelfLogTable' => 'app\command\CreateOffshelfLogTable',#创建盒子自动下架日志表
'FlwOpen' => 'app\command\FlwOpen',#福利屋开奖
'UpdateGoodsHeat' => 'app\command\UpdateGoodsHeat',#更新盒子热度值到Redis
'PostOrderRetry'=>'app\command\PostOrderRetry'
],
];