第一次更新,完成了初版调整

This commit is contained in:
2026-09-07 22:05:51 +08:00
parent b5a26a8848
commit 4d6a7e7aac
198 changed files with 6142 additions and 3856 deletions
+1 -1
View File
@@ -1 +1 @@
APP_DEBUG = false␍␍[APP]␍DEFAULT_TIMEZONE = Asia/Shanghai␍␍[DATABASE]␍TYPE = mysql␍HOSTNAME = 127.0.0.1␍DATABASE = oil␍USERNAME = user␍PASSWORD = qQ19961012␍HOSTPORT = 3306␍CHARSET = utf8␍DEBUG = true␍␍[CACHE]␍DRIVER = file␍␍[REDIS]␍HOSTNAME = 127.0.0.1␍HOSTPORT = 6379␍PASSWORD =␍SELECT = 0␍␍[LANG]␍default_lang = zh-cn␍
APP_DEBUG = false␍␍[APP]␍DEFAULT_TIMEZONE = Asia/Shanghai␍␍[DATABASE]␍TYPE = mysql␍HOSTNAME = 127.0.0.1␍DATABASE = oil␍USERNAME = oil␍PASSWORD = AxSZEJDp5nePyfMa␍HOSTPORT = 3306␍CHARSET = utf8␍DEBUG = true␍␍[CACHE]␍DRIVER = file␍␍[REDIS]␍HOSTNAME = 127.0.0.1␍HOSTPORT = 6379␍PASSWORD =␍SELECT = 0␍␍[LANG]␍default_lang = zh-cn␍␍; ===== 企油通对接配置(值不要加引号;RSA 密钥压成一行 base64,去掉 BEGIN/END 与换行)=====␍[QYT]␍; 接口域名,不含结尾斜杠,向企油通对接人索取(测试/正式不同)␍BASE_URL = http://dev.pbpayapi.worknoya.work␍; 平台分配的 appId(业务接口用)␍APP_ID = 37257338026e7a377165454689773882␍; 我方 RSA 私钥(一行 base64,无头尾无换行)␍PRIVATE_KEY = MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpi86NYP5sVj2FcLvSk3o6mmWAgEW4TiuMtzDqld1QjaKAVaEbbGN93ZvSaafawPeBMI5qi04NvFHnPlGuQiGxJ5ddzaE4P5/xAsd2NnUFY30o71n3Tq30jYQdblTa7xr9nAhZiBrd2CtKWfvnoU/RxsJORUhiJhKtTVjNQ6N8UICDyUVzlhIqKtishMLYDSIhRnONtrQuKnXDPAoaTxbxEHLHXbic7WPwtOUNln/7oKuLdGJyKn2ikFv1YBNzwVpaLRsu+zF617qE5HWVf5GtEs/IuD+p1t4p6Dnm0Bq1fChkqz3pQejiyuNzhZt3zJZWCz3aPOxKLXBhfm+V29yZAgMBAAECggEABRJmGOucYNBJodrH+5omz2b7/1aW/1bzTY/TWkkmaDj8cftdyOy6ozLEbQx1FYGtRcOWlxBYwudtyc2e8ugOivakvA0hNAYPOk37eolRgdpCjb1vQwsrAARVve2lgzEg7RJEW61LJHTJgr1jYD/+m8dm26GN80WxTFbif6100Ayp++qxP7/EAwsD+Nl9wNo1itSHIiwzt4ohCWgxoSgVwkoUdMVM3qERzBXzjQAzK/A8DuJDyNayu74YVBrxXGRbzeeOippu7d2dY6bHwyDah7ki5IW9MRmMH/d24sayb+kYYKXI9d+G9jqXlL9ZASb4/OPQ2s7WPftDQhIvpXJdwQKBgQDUmkdl+viy13PtmV00AfzDaOeOTOknzfW7xjylnYR63pI8dnMLTN+RIXYgWMbehrLjMwxhWjekOPKA9HZ2g1wI7sxYsHWBiq6StYOcS7RlGayyvFstbbUR3X5Ff7y/3+BmYzrzZKepwJ8bCLCoMaRPw4HiGVdNm0qOfmQAeuopMQKBgQDMJ5MWc9jAbvs1hb4JZ6oTPY1SMfA7QTas3EHq8CFCgvFJEtu1F2QD4w+L2G0m0zgVHDuFKR88mFIs+yGzfEhNy05ro2oAExW7MuwwEeQHWcq1m+r7iQMEchMCJbXEZLf9lUxNb3NKVG/W3gMDAkJfh+uFln/DAf/GOeFXttqP6QKBgBvI8e3VB+2vlLoHohOq8tLDAggp6wrFswjQVpq6ZFb+gZQfUte2Oz2nZt3BXHQ2nGhvAn4yoTT4MFFEIbtJXZzZQKGSpjjA3CHWURX7Gt9fpJ9TWIMuZEyqatdAY3WcF6Kp80ld7T+VKfYJg7Tnj/bQsUfQUgWVdv/6aq1ZYwLxAoGAVo80+yEVNsoXcuMI5x3v64Z+g+oujEEjOHmO7VjxES1JtozrYMXRHyDHt6iE5yBJOBInXldIKj3/IZk8+cfpNCrOTi8UQ4HUBfIaWGtNfpxgqU25fPrjr5/aVV+9yM5XgIortu1ZOZvBa53oMv9jOvP7NUHRW9JeKyLhW3ppAvkCgYEAlOC6viG8DSarMSMRL4I5vm4tpO0lwuU0bGeAEy+fTme+Ww3Hkg2Bgz/e3+jfkYbwAkkR24XO4qJJeNF11Uq4ghA/1dCRrfGkB0XK7bNrsXsgyZeaU8Ku1vLG/6LVNPgSuoZTYyQAcSaQP+Ipze3Sz49nUD6Jsdjt2zlaeMCCQVs=␍; 企油通平台公钥(一行 base64,用于验签其响应与回调)␍PLATFORM_PUBLIC_KEY = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqYvOjWD+bFY9hXC70pN6OpplgIBFuE4rjLcw6pXdUI2igFWhG2xjfd2b0mmn2sD3gTCOaotODbxR5z5RrkIhsSeXXc2hOD+f8QLHdjZ1BWN9KO9Z906t9I2EHW5U2u8a/ZwIWYga3dgrSln756FP0cbCTkVIYiYSrU1YzUOjfFCAg8lFc5YSKirYrITC2A0iIUZzjba0Lip1wzwKGk8W8RByx124nO1j8LTlDZZ/+6Cri3Ricip9opBb9WATc8FaWi0bLvsxete6hOR1lX+RrRLPyLg/qdbeKeg55tAatXwoZKs96UHo4srjc4Wbd8yWVgs92jzsSi1wYX5vldvcmQIDAQAB␍; 我方统一社会信用代码 / 营业执照编码␍BUSINESS_LICENSE = 91450103MAE2729EXJ (1-1)␍; 机构号:支付宝企业码通道 7.7 必填,由企油通分配␍INSTITUTION_ID = ␍; 全平台唯一编号前缀(batch_no/waybill_no/waybill_no_code),正式 HM、测试建议 HMTEST␍NO_PREFIX = HM␍; 7.15 H5 免登落地页地址(测试:http://dev.paytaskpage.worknoya.vip/#/)␍H5_BASE_URL = https://paytaskpage.worknoya.vip/#/␍; 7.15 H5 免登用的 appId(可能与业务 appId 不同,需线下确认)␍H5_APP_ID = 37257338026e7a377165454689773882␍; 我方 7.10 消费回调地址(仅作记录,实际需在企油通后台配置)␍CONSUME_NOTIFY_URL = http://47.104.73.7/qytconsume.php␍; 业务接口签名字段拼接方式,待对接人确认后可改␍SIGN_MODE = ksort_md5_sha1rsa␍; 是否记录详细请求/响应日志到 runtime/storelog␍LOG = true␍
@@ -32,9 +32,6 @@ class Controller extends BaseController
{
// 当前商城ID
protected $storeId;
protected $latitude;
protected $longitude;
protected $province;
/**
* API基类初始化
@@ -44,12 +41,6 @@ class Controller extends BaseController
{
// 当前商城id
$this->storeId = $this->getStoreId();
$this->latitude = input('get.latitude');
$this->longitude = input('get.longitude');
$res = $this->httpCurl('https://apis.map.qq.com/ws/geocoder/v1/',['location'=>$this->latitude.','.$this->longitude,'key'=>'ETFBZ-F4XCW-YOQRI-RWBQM-BAZIQ-2VFS5']);
if(isset($res['status']) && $res['status'] == 0){
$this->province = $res['result']['address_component']['province'];
}
// 验证当前商城状态
$this->checkStore();
// 验证当前客户端状态
+72 -189
View File
@@ -15,13 +15,9 @@ namespace app\api\controller;
use app\api\controller\Controller;
use app\api\model\partners\Order as OrderModel;
use app\common\model\Partners as PartnersModel;
use app\common\model\PartnersGoods as PartnersGoodsModel;
use app\common\model\Goods as GoodsModel;
use app\api\model\GoodsSku as GoodsSkuModel;
use app\common\crypto\RsaUtil;
use app\common\service\goods\source\Factory as StockFactory;
use app\common\service\IdingjuOil;
use app\store\model\Company as CompanyModel;
use app\common\service\PartnersBalance;
use app\common\service\QytOil;
use app\store\model\order\QrCode as QrCodeModel;
use cores\exception\BaseException;
use think\facade\Request;
@@ -83,10 +79,12 @@ class OilCard extends Controller
// 业务处理
$data = $result['data'];
$orgNo = $data['orgNo'] ?? '';
$goodsNo = $data['goodsNo'] ?? '';
$amount = $data['amount'] ?? '';
$mobile = $data['mobile'] ?? '';
$saleOrderNo = $data['saleOrderNo'] ?? '';
$oilCardType = $data['oilCardType'] ?? '';
// 企油通:司机姓名(下单平台必传,用于运单油费下发)
$driverName = trim((string)($data['name'] ?? ''));
// 请求参数orgNo与请求头appKey不存在对应关系
if($partners['orgNo'] != $orgNo){
return $this->oilRenderError('12','0014');
@@ -94,78 +92,80 @@ class OilCard extends Controller
if(!preg_match('/^1[3-9]\d{9}$/', $mobile)) {
return $this->oilRenderError('12','0013', '', '手机号格式不正确');
}
// 销售平台订单号重复
$order = OrderModel::detail(['partners_id' => $partners['partners_id'], 'partners_order_no' => $saleOrderNo]);
if($order){
return $this->oilRenderError('12','2023');
if($driverName === ''){
return $this->oilRenderError('12','0001', '', '缺少必填请求参数 name(司机姓名)');
}
// 未找到卡券对应的信息
$goods = GoodsModel::detail(['goods_no' => $goodsNo, 'oilCardType' => $oilCardType]);
if(!$goods){
return $this->oilRenderError('12','2047');
// 金额:由下单平台直接传入(单位:元,最多两位小数)
if($amount === '' || !preg_match('/^\d{1,8}(\.\d{1,2})?$/', (string)$amount) || (float)$amount <= 0){
return $this->oilRenderError('12','0013', '', 'amount 金额格式不正确(单位:元,最多两位小数且大于0)');
}
// 判断商品库存
$detail = GoodsSkuModel::detail($goods['goods_id'], '0');
if (1 > $detail['stock_num']) {
return $this->oilRenderError('12','0013', '', "很抱歉,商品 [{$goods['goods_name']}] 平台库存不足");
$amount = round((float)$amount, 2);
// 销售平台订单号幂等:已存在则返回原订单结果(发放失败态除外)
$exist = OrderModel::detail(['partners_id' => $partners['partners_id'], 'partners_order_no' => $saleOrderNo]);
if($exist){
if((int)$exist['oil_status_zsy'] === 3){
return $this->oilRenderError('12','2023','','该销售订单号已提交且发放失败,请使用新订单号');
}
return $this->oilRenderSuccess([
'oilCardNo' => $exist['oilCardNo'],
'amount' => $exist['total_price'],
]);
}
$partnersGoods = PartnersGoodsModel::detail($partners['partners_id'], $goods['goods_id']);
if(!$partnersGoods || $partnersGoods['stock_num'] <= 0){
return $this->oilRenderError('01','2010');
// 额度管控:先原子扣减合作商余额(余额不足直接拒单,防并发超扣)
$balanceLogId = PartnersBalance::deduct((int)$partners['partners_id'], (float)$amount, $saleOrderNo, 0, (int)$this->storeId);
if ($balanceLogId === false) {
return $this->oilRenderError('01','2028', '', '合作商余额不足,请先充值');
}
$OrderModel = new OrderModel();
$oilCardNo = 'OIL' . self::createOrderNo();
if($OrderModel->saveOrder([
'province' => $goods['province'],
'mobile' => $mobile,
'oilCardType' => $oilCardType,
'oilCardNo' => $oilCardNo,
'partners_order_no' => $saleOrderNo,
'partners_id' => $partners['partners_id'],
'goods_id' => $goods['goods_id'],
'goods_name' => $goods['goods_name'],
'goods_no' => $goods['goods_no'],
'goods_price' => $goods['goods_price_min'],
'total_num' => 1,
'total_price' => $goods['goods_price_min'],
'total_pay_price' => $goods['goods_price_min'],
'store_id' => $this->storeId,
])){
// 更新商品库存 (针对下单减库存的商品)
$this->updateGoodsStockNum([
[
'deduct_stock_type' => 10,
'goods_id' => $goods['goods_id'],
'goods_sku_id' => $detail['id'],
'total_num' => 1,
]
try {
$created = $OrderModel->saveOrder([
'mobile' => $mobile,
'driver_name' => $driverName,
'oilCardType' => $oilCardType,
'oilCardNo' => $oilCardNo,
'partners_order_no' => $saleOrderNo,
'partners_id' => $partners['partners_id'],
'goods_price' => $amount,
'total_num' => 1,
'total_price' => $amount,
'total_pay_price' => $amount,
'store_id' => $this->storeId,
]);
} catch (\Throwable $e) {
// 建单失败 -> 回补刚扣减的余额
PartnersBalance::refund((int)$partners['partners_id'], (float)$amount, $saleOrderNo, 0, (int)$this->storeId);
// 并发下 (partners_id, partners_order_no) 唯一键冲突 -> 幂等返回已存在订单
if (strpos($e->getMessage(), 'Duplicate') !== false) {
$again = OrderModel::detail(['partners_id' => $partners['partners_id'], 'partners_order_no' => $saleOrderNo]);
if ($again) {
return $this->oilRenderSuccess(['oilCardNo' => $again['oilCardNo'], 'amount' => $again['total_price']]);
}
}
$this->saveLog('share saveOrder 异常:' . $e->getMessage());
return $this->oilRenderError('99', '9999');
}
if($created){
// 回填流水的订单ID(建单前尚不知 order_id)
PartnersBalance::bindOrder($balanceLogId, (int)$OrderModel->order_id);
// 存入队列处理
$queueService = new QueueService();
$queueService->push('partners_queue', json_encode(['order_id'=>$OrderModel->order_id],320));
return $this->oilRenderSuccess([
'oilCardNo' => $oilCardNo,
'amount' => $goods['goods_price_min'],
'amount' => $amount,
]);
}
// 未建单成功 -> 回补余额
PartnersBalance::refund((int)$partners['partners_id'], (float)$amount, $saleOrderNo, 0, (int)$this->storeId);
return $this->oilRenderError('99', '9999');
} else {
// 错误处理
return $this->oilRenderError($result['code'], $result['errCode'], '', $result['errMessage'] ?? '');
}
}
/**
* 更新商品库存 (针对下单减库存的商品)
* @param $order
* @return void
*/
private function updateGoodsStockNum($goodsList): void
{
StockFactory::getFactory(10)->updateGoodsStock($goodsList);
}
public static function createOrderNo(): string
{
// 获取当前时间戳到毫秒(13位)
@@ -177,36 +177,6 @@ class OilCard extends Controller
return (string)($time . $rand); // 拼接成纯数字字符串
}
/**
* @param $goodsNo
* @return string
*/
public function getCompany($goodsNo): string
{
switch ($goodsNo) {
case 'djgdface100':
case 'djgdface200':
case 'djgdface300':
return 'umQZhIHKofyA';
case 'jmgxyyface100':
case 'jmgxyyface200':
case 'jmgxyyface300':
case 'jmgzface100':
case 'jmgzface200':
case 'jmgzface300':
case 'jmfjface100':
case 'jmfjface200':
case 'jmfjface300':
return 'HpWqXIgYvTBg';
case 'face003':
return 'NKJBgaaRNdiL'; // 测试参数
default:
return '';
}
}
/**
* 付款二维码申请
* @return Json
@@ -266,105 +236,22 @@ class OilCard extends Controller
if(!$order) {
return $this->oilRenderError('12','0013', '', '未找到对应订单');
}
$company = CompanyModel::detail(['orgNo' => $this->getCompany($order['goods_no'])]);
if (!$company) {
return $this->oilRenderError('12','0013', '', '没有可用参数:【' . $order['goods_no'] . '】');
// 企油通:生成司机免登加油 H5 地址(7.15)。出参 qrCode 结构不变,值由二维码链接改为 H5 URL。
$h5Url = (new QytOil())->h5Url($order['mobile']);
if(!$h5Url){
return $this->oilRenderError('12','0013', '', '生成加油链接失败');
}
$IdingjuOil = new IdingjuOil();
// 对接平台接口
$res = $IdingjuOil->payQrCodeRequest(['order_no'=>$oilCardNo], $company);
if(isset($res['code']) && $res['code'] == '1' && isset($res['data']['useQrCodeUrl'])){
// 插入支付码记录
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$order['oilCardNo'],
'qrCode'=>$res['data']['useQrCodeUrl'],
'amount'=>$order['goods_price'],
'order_id'=>$order['order_id'],
'user_id'=>0,
'store_id'=>$order['store_id'],
]);
return $this->oilRenderSuccess([
'qrCode'=>$res['data']['useQrCodeUrl']
]);
}else{
return $this->oilRenderError('12','0013', '', $res['message'] ?? '');
}
} else {
// 错误处理
return $this->oilRenderError($result['code'], $result['errCode'], '', $result['errMessage'] ?? '');
}
}
/**
* 油券库存查询
* @return Json
* @throws BaseException
* @throws \think\db\exception\DbException
*/
public function stock()
{
// 获取所有 HTTP 请求头
$headers = Request::header();
// 获取 JSON 格式的请求体
$jsonData = Request::instance()->getContent();
$data = json_decode($jsonData, true);
$appKey = $headers['appkey'] ?? '';
$requestId = $headers['requestid'] ?? '';
$method = $headers['method'] ?? '';
// 请求头METHOD参数不正确
if($method != 'syb.oil.oilCard.stockList'){
return $this->oilRenderError('12', '0005');
}
try {
Db::name('request_log')->insert([
'request_id' => $requestId,
'app_key' => $appKey,
'create_time' => time()
]);
} catch (\think\Exception $e) {
// 判断是否唯一索引冲突
if (strpos($e->getMessage(), 'Duplicate entry') !== false) {
return $this->oilRenderError('12', '0012'); // 请求流水号重复
}
// 其他数据库异常
return $this->oilRenderError('99', '9999'); // 未知错误
}
$partners = PartnersModel::detail(['appKey' => $appKey]);
if(!$partners){
return $this->oilRenderError('02','0004');
}
$crypto = new RsaUtil($partners);
$result = $crypto->processResponse(['header'=>$headers,'encryptData'=>$data]);
if ($result['code'] === '00') {
// 业务处理
$data = $result['data'];
$orgNo = $data['orgNo'] ?? '';
$goodsNo = $data['goodsNo'] ?? '';
// 请求参数orgNo与请求头appKey不存在对应关系
if($partners['orgNo'] != $orgNo){
return $this->oilRenderError('12','0014');
}
// 未找到卡券对应的信息
$goods = GoodsModel::detail(['goods_no' => $goodsNo]);
if(!$goods){
return $this->oilRenderError('12','2047');
}
$partnersGoods = PartnersGoodsModel::detail($partners['partners_id'], $goods['goods_id']);
if(!$partnersGoods){
return $this->oilRenderError('01','2010');
}
$orderModel = new OrderModel();
$sucNum = $orderModel->getCount(['partners_id' => $partners['partners_id'], 'goods_id' => $goods['goods_id']], 'all');
$useNum = $orderModel->getCount(['partners_id' => $partners['partners_id'], 'goods_id' => $goods['goods_id']], 'used');
return $this->oilRenderSuccess([
'goodsNo'=>$goods['goods_no'], // 商品编号
'canOrderNum'=>$partnersGoods['stock_num'], // 剩余可下单数量
'unUseNum'=>floatval($sucNum) - floatval($useNum), // 未使用商品数量
'useNum'=>$useNum, // 已使用商品数量
// 插入申请记录(留痕)
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$order['oilCardNo'],
'qrCode'=>$h5Url,
'amount'=>$order['goods_price'],
'order_id'=>$order['order_id'],
'user_id'=>0,
'store_id'=>$order['store_id'],
]);
return $this->oilRenderSuccess(['qrCode'=>$h5Url]);
} else {
// 错误处理
return $this->oilRenderError($result['code'], $result['errCode'], '', $result['errMessage'] ?? '');
@@ -478,7 +365,6 @@ class OilCard extends Controller
'shareNo' => $item['shareNo'],
'mobile' => $item['mobile'],
'oilCardNo' => $item['oilCardNo'],
'productName' => $item['goods_name'],
'num' => $item['total_num'],
'amount' => $item['total_price'],
'createTime' => $item['create_time'],
@@ -561,13 +447,10 @@ class OilCard extends Controller
$detail = OrderModel::detail(['oilCardNo' => $data['oilCardNo']]);
// 组装返回格式
$result = [
'province' => $detail['province'],
'mobile' => $detail['mobile'],
'oilCardNo' => $detail['oilCardNo'],
'shareNo' => $detail['shareNo'],
'saleOrderNo' => $detail['partners_order_no'],
'goods_name' => $detail['goods_name'],
'goods_no' => $detail['goods_no'],
'total_price' => $detail['total_price'],
'oil_status_zsy' => $detail['oil_status_zsy'],
'oil_status' => $detail['oil_status'],
+6 -71
View File
@@ -309,36 +309,9 @@ class Order extends Controller
*/
public function qrCode(): Json
{
$IdingjuOil = new IdingjuOil();
$postData = $this->request->param();
$postData['order_goods_id'] = $this->request->param('orderGoodsId');
$orderGoods = OrderGoodsModel::detail($postData['order_goods_id']);
if($orderGoods['use_status'] != 0){
return $this->renderError('油券已核销');
}else {
$company = CompanyModel::detail($orderGoods['company_id']);
// 对接平台接口
$res = $IdingjuOil->payQrCodeRequest(['order_no'=>$orderGoods['oilCardNo']], $company);
if($res['code'] == '1' && isset($res['data']['useQrCodeUrl'])){
// 插入支付码记录
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$orderGoods['oilCardNo'],
'qrCode'=>$res['data']['useQrCodeUrl'],
'amount'=>$orderGoods['goods_price'],
'order_id'=>$orderGoods['order_id'],
'user_id'=>$orderGoods['user_id'],
'store_id'=>$orderGoods['store_id'],
]);
$res = [
'qrCode'=>$res['data']['useQrCodeUrl']
];
return $this->renderSuccess($res, '提交成功');
}else{
return $this->renderError('平台创建用户失败【'.($res['errMessage'] ?? $res['message']).'】');
}
}
// 企油通改造:小程序油品发放/核销链路已停用(上游定聚已下线)。
// 保留接口避免小程序端报错,统一返回提示;如需恢复请改走 QytOil。
return $this->renderError('该功能已停用');
}
/**
@@ -348,47 +321,9 @@ class Order extends Controller
*/
public function qrCodeImg(): Json
{
// $res = [
// 'base64'=> 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAIAAAAP3aGbAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGwElEQVR4nO3cwY4bRxAFQdPY//9l+roQ4IULVoGVmoizwO0ZUok+vdf7/f4LoODvTx8A4L8SLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CAjK/tP/B6vbb/xKO83+/Rv99+/9PzTE3Pf+39PM3278ENC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsICM9T2sqe09nWuu7VVNz3Pt/NuunWfbtb0wNywgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMs7tYU1d2+up7yVdO//0+62ff9u19zPlhgVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhARn4Pi5/V96SunYfPcsMCMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIMMe1h9uuid1bT/r2nn4LDcsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjLye1j2j36va/tWdX6fv5cbFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZ5/awnraXtG36Pqf7Tdc+f9u18zyNGxaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGa/pPhF8t72HBd+5YQEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQ8fXpA/xf23tM08/fdu382+e59vnbrp3/2nncsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIWN/DuranM3Vtj+navtX25197n9d+n0/jhgVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAxuvavs+1faLt/ampa9/X1LX3OWVv67PcsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIWN/Detoe0PbzPu19TtXfz/ZeWP335oYFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQMbXpw/wq2t7QNuu7Q1t87w/u7ZPd40bFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZ63tY9T2jqWvPe21f7Nr7mdret7q2n3Xt+3LDAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCDjtb13s73H9DTX9ommru1zXTvP1LX/X9vP64YFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQMbXpw/wq/re09T2nlF972nq2j7U097/NjcsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjLO7WFNXds/eto+0dT0/Vzbk5qeZ/v3sH2ea8/rhgVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhARn4Pi5/V97nqe1LX1J/XDQvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADHtYf7hr+0fT80xNz3/teZ92/ik3LCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIy8ntY2/s7ddP3c23/aOra+et7Vde+XzcsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjLO7WFN933gu2v7Tdf2uaaund8NC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsICM17X9HYB/44YFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWEDGP+OBJg1/1F6rAAAAAElFTkSuQmCC'
//// 'base64'=> ''
// ];
// return $this->renderSuccess($res, '提交成功');
$IdingjuOil = new IdingjuOil();
$postData = $this->request->param();
$postData['order_goods_id'] = $this->request->param('orderGoodsId');
$orderGoods = OrderGoodsModel::detail($postData['order_goods_id']);
if($orderGoods['use_status'] != 0){
return $this->renderError('油券已核销');
}else {
/*$res = [
'base64'=> $this->getBase64($orderGoods['qrCode'])
];
return $this->renderSuccess($res, '提交成功');*/
$company = CompanyModel::detail($orderGoods['company_id']);
// 对接平台接口
$res = $IdingjuOil->payQrCodeRequest(['order_no'=>$orderGoods['oilCardNo']], $company);
//$res = $IdingjuOil->payQrCodeRequest(['oilCardNo'=>$orderGoods['oilCardNo'],'shareNo'=>$orderGoods['shareNo'],'mobile'=>$orderGoods['mobile'],'oilCardType'=>$orderGoods['oilCardType']], $company);
//if($res['code'] == '00' && isset($res['data']['qrCode'])){
if($res['code'] == '1' && isset($res['data']['useQrCodeUrl'])){
// 插入支付码记录
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$orderGoods['oilCardNo'],
'qrCode'=>$res['data']['useQrCodeUrl'],
'amount'=>$orderGoods['goods_price'],
'order_id'=>$orderGoods['order_id'],
'user_id'=>$orderGoods['user_id'],
'store_id'=>$orderGoods['store_id'],
]);
$res = [
'base64'=> $this->getBase64($res['data']['useQrCodeUrl'])
];
return $this->renderSuccess($res, '提交成功');
}else{
return $this->renderError('平台创建用户失败【'.($res['errMessage'] ?? $res['message']).'】');
}
}
// 企油通改造:小程序油品发放/核销链路已停用(上游定聚已下线)。
// 保留接口避免小程序端报错,统一返回提示;如需恢复请改走 QytOil。
return $this->renderError('该功能已停用');
}
public function getBase64($url){
@@ -0,0 +1,56 @@
<?php
// +----------------------------------------------------------------------
// | 企油通 · 回调接收
// +----------------------------------------------------------------------
// | 入口:public/qytconsume.php -> /qytNotify/consume
// | 对应企油通 7.10「运单加油明细推送(异步)」,我方须返回 {"code":"100","msg":"Success"}
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\api\controller;
use app\common\crypto\Qyt as QytCrypto;
use app\common\service\QueueService;
use app\common\service\QytOil;
use think\facade\Request;
class QytNotify extends Controller
{
/**
* 7.10 运单加油明细推送
* @return \think\response\Json
*/
public function consume()
{
$raw = Request::instance()->getContent();
(new QytOil())->saveLog('7.10 消费回调原文:' . $raw);
$body = json_decode($raw, true);
// 兼容:外层信封 {appId,timestamp,sign,params(JSON字符串)} 或 直接推 body
$params = $body;
if (is_array($body) && isset($body['params'])) {
$params = is_string($body['params']) ? json_decode($body['params'], true) : $body['params'];
}
// TODO 验签(规则待对接人确认)
// if (is_array($body) && !empty($body['sign'])) {
// $crypto = new QytCrypto();
// if (!$crypto->verify($params, $body['sign'])) {
// return json(['code' => '99', 'msg' => '验签失败']);
// }
// }
// 无明细也回 100,避免企油通重复推送
if (is_array($params) && !empty($params['order_list'])) {
(new QueueService())->push('callback_queue', json_encode([
'header' => ['method' => 'qyt_consume'],
'res' => $params,
], 320));
} else {
(new QytOil())->saveLog('7.10 消费回调无 order_list,已忽略');
}
return json(['code' => '100', 'msg' => 'Success']);
}
}
+2 -40
View File
@@ -32,47 +32,9 @@ class Station extends Controller
*/
public function list(): Json
{
$SybOil = new SybOil();
$company = CompanyModel::detail('2');
$param = $this->request->param();
$province = Db::name('area')->where(array(
['name', '=', $param['province']],
['level', '=', 1],
))->find();
$city = Db::name('area')->where(array(
['name', '=', $param['city']],
['pid', '=', $province['code']],
['level', '=', 2],
))->find();
$goods = Db::name('goods')->where(array(
['oilCardType', '=', 'zsy_ticket'],
['province', '=', $param['province']],
['status', '=', 10],
))->order('stock_total', 'desc')
->find();
// 企油通改造:本功能依赖已下线的上游(智慧油客/定聚),现已停用。
// 企油通无油站查询能力,返回空列表,避免小程序端报错。
$list = [];
if($goods) {
// 对接平台接口
$res = $SybOil->baseGasStationList(['goodsNo' => $goods['goods_no'], 'longitude' => $param['longitude'], 'latitude' => $param['latitude'], 'province' => $province['code'], 'city' => $city['code']], $company);
if (isset($res['data']['zsyResp']['count']) && $res['data']['zsyResp']['count'] > 0) {
$lists = $res['data']['zsyResp']['lists'];
foreach ($lists as $k => $array) {
$list[] = [
'id' => $k + 1,
'name' => $array['name'],
'brand' => $array['brand'] == 1 ? '中国石油' : '中国BP',
'latitude' => $array['lat'],
'longitude' => $array['lng'],
'address' => '',
'distance' => $array['nearBy'],
'width' => 30,
'height' => 30,
'iconPath' => '/static/marker.png',
'stationCode' => $array['stationCode'],
];
}
}
}
return $this->renderSuccess(compact('list'));
}
}
@@ -1,27 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\api\model;
use app\common\model\PartnersGoods as PartnersGoodsModel;
use app\common\library\helper;
use cores\exception\BaseException;
/**
* 用户优惠券模型
* Class PartnersGoods
* @package app\api\model
*/
class PartnersGoods extends PartnersGoodsModel
{
}
+2 -9
View File
@@ -13,7 +13,6 @@ declare (strict_types=1);
namespace app\api\model\partners;
use app\common\model\partners\Order as OrderModel;
use app\api\model\PartnersGoods as PartnersGoodsModel;
use app\common\library\helper;
use cores\exception\BaseException;
@@ -32,14 +31,8 @@ class Order extends OrderModel
*/
public function saveOrder(array $data)
{
// 事务处理
$this->transaction(function () use ($data) {
// 写入订单记录
$this->save($data);
// 修改合作商商品库存
PartnersGoodsModel::setIncUseNum(['partners_id' => $data['partners_id'], 'goods_id' => $data['goods_id']], 1);
PartnersGoodsModel::setDecStockNum(['partners_id' => $data['partners_id'], 'goods_id' => $data['goods_id']], 1);
});
// 企油通改造:下单改为按金额(amount)直传,不再挂商品与合作商商品库存
$this->save($data);
return true;
}
@@ -255,6 +255,11 @@ class PaySuccess extends BaseService
$IdingjuOil = new IdingjuOil();
foreach ($order->goods as $goods) {
/* @var GoodsModel $goods */
// 企油通改造:小程序油品发放链路已停用,支付成功后不再触发上游分油(保留优惠券发放)。
// 如需恢复小程序侧发放,改走 QytOil 并补齐司机三要素来源。
if ($goods['oilCardType'] == 'zsy_ticket') {
continue;
}
if($goods['oilCardType'] == 'zsy_ticket') {
$data['amount'] = $goods['total_price']; // 金额
$company = CompanyModel::detail(['orgNo' => $this->getCompany($goods['goods_no'])]);
+182
View File
@@ -0,0 +1,182 @@
<?php
// +----------------------------------------------------------------------
// | 企油通 · 签名 / 验签 / 请求信封
// +----------------------------------------------------------------------
// | 第二阶段 A-1 骨架。标 【TODO 待对接人确认】 处需按企油通实际规则微调。
// |
// | 三种签名场景(见《企油通接口对接文档.md》§2.1 / §2.4):
// | 1. 业务信封:外层 { appId, timestamp, sign, params(JSON字符串) }
// | 2. 7.15 H5 免登地址:ksort → http_build_query → urldecode → 大写MD5 → SHA1withRSA → base64
// | 3. 7.16 / 7.17:必须真实 RSA 验签调用(本质同 1,但企油通侧强校验)
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\crypto;
class Qyt
{
/** @var string 我方私钥资源 */
protected $privateKey;
/** @var string|false 企油通平台公钥资源 */
protected $platformPublicKey;
/** @var array config('qyt') */
protected $conf;
public function __construct(array $conf = null)
{
$this->conf = $conf ?: (array) config('qyt');
$this->privateKey = $this->loadPrivateKey($this->conf['private_key'] ?? '');
$this->platformPublicKey = $this->loadPublicKey($this->conf['platform_public_key'] ?? '');
}
// ================================================================
// 1. 业务信封
// ================================================================
/**
* 构建业务请求信封
* @param array $bizParams 业务参数(会被 json_encode 成字符串放进 params)
* @return array{appId:string,timestamp:int,sign:string,params:string}
*/
public function envelope(array $bizParams): array
{
$appId = (string) ($this->conf['app_id'] ?? '');
$timestamp = time();
// ⚠️ params 必须是 JSON 字符串(转义),不是对象
$paramsJson = json_encode($bizParams, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$sign = $this->bizSign([
'appId' => $appId,
'timestamp' => $timestamp,
'params' => $paramsJson,
]);
return [
'appId' => $appId,
'timestamp' => $timestamp,
'sign' => $sign,
'params' => $paramsJson,
];
}
/**
* 业务信封签名
* 【TODO 待对接人确认】业务接口的具体拼签字段与顺序,文档 §2.4 只给了 7.15 的算法,
* 业务接口写的是「密钥、公私钥与具体拼签字段请向对接人员索取」。
* 当前默认实现:对参与字段 ksort 后 http_build_query → urldecode → 大写MD5 → SHA1withRSA → base64
* (与 7.15 同族算法,落地时按实际规则替换本方法即可,调用方无需改动)
*
* @param array $fields 参与签名的字段(appId / timestamp / params)
*/
public function bizSign(array $fields): string
{
$mode = $this->conf['sign_mode'] ?? 'ksort_md5_sha1rsa';
switch ($mode) {
// 预留:'raw_concat_sha256rsa' 等其它模式
case 'ksort_md5_sha1rsa':
default:
return $this->md5Sha1RsaSign($fields);
}
}
// ================================================================
// 2. 7.15 H5 免登地址
// ================================================================
/**
* 生成司机免登加油 H5 跳转地址(7.15)
* @param string $mobile 司机手机号
* @param string $creditcode 企业营业执照号;传空则不带(不参与签名)
* @return string 完整可跳转 URL
*/
public function h5Url(string $mobile, string $creditcode = ''): string
{
$base = rtrim((string) ($this->conf['h5_base_url'] ?? ''), '/');
$appId = (string) ($this->conf['h5_app_id'] ?: $this->conf['app_id']);
$timestamp = date('YmdHis');
$params = [
'appId' => $appId,
'mobile' => $mobile,
'timestamp' => $timestamp,
];
if ($creditcode !== '') {
$params['creditcode'] = $creditcode; // 传了必须一并入签
}
$params['sign'] = $this->md5Sha1RsaSign($params);
// 文档示例形如 https://paytaskpage.worknoya.vip/#/?appId=..&mobile=..&timestamp=..&sign=..#/
return $base . '/?' . http_build_query($params) . '#/';
}
// ================================================================
// 3. 验签(企油通响应 / 回调)
// ================================================================
/**
* 验签企油通返回 / 回调报文
* @param array $data 业务数据(通用响应里的 data,或回调 params)
* @param string $sign 报文里的 sign
* 【TODO 待对接人确认】验签串的构造(对 data 的 json 形态 / 是否含外层字段)
*/
public function verify($data, string $sign): bool
{
if ($sign === '' || $this->platformPublicKey === false) {
return false;
}
$payload = is_string($data) ? $data : json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$ok = openssl_verify($payload, base64_decode($sign), $this->platformPublicKey, OPENSSL_ALGO_SHA256);
return $ok === 1;
}
// ================================================================
// 内部:算法与密钥
// ================================================================
/**
* 同族签名算法:ksort → http_build_query → urldecode → 大写MD5 → SHA1withRSA → base64
*/
protected function md5Sha1RsaSign(array $params): string
{
ksort($params);
$raw = urldecode(http_build_query($params));
$md5Upper = strtoupper(md5($raw));
$signature = '';
openssl_sign($md5Upper, $signature, $this->privateKey, OPENSSL_ALGO_SHA1);
return base64_encode($signature);
}
/**
* 载入私钥:支持 PEM 全文 / 去头尾的单行 base64
* @return \OpenSSLAsymmetricKey|resource|string
*/
protected function loadPrivateKey(string $key)
{
if ($key === '') {
return '';
}
if (strpos($key, '-----BEGIN') === false) {
$key = "-----BEGIN PRIVATE KEY-----\n" . chunk_split(trim($key), 64, "\n") . "-----END PRIVATE KEY-----\n";
}
$res = openssl_pkey_get_private($key);
return $res === false ? '' : $res;
}
/**
* 载入公钥:支持 PEM 全文 / 去头尾的单行 base64
* @return \OpenSSLAsymmetricKey|resource|false
*/
protected function loadPublicKey(string $key)
{
if ($key === '') {
return false;
}
if (strpos($key, '-----BEGIN') === false) {
$key = "-----BEGIN PUBLIC KEY-----\n" . chunk_split(trim($key), 64, "\n") . "-----END PUBLIC KEY-----\n";
}
return openssl_pkey_get_public($key);
}
}
@@ -1,85 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\model;
use cores\BaseModel;
use app\common\library\helper;
/**
* 合作商关联商品券模型
* Class PartnersGoods
* @package app\common\model
*/
class PartnersGoods extends BaseModel
{
// 定义表名
protected $name = 'partners_goods';
// 定义主键
protected $pk = 'id';
/**
* 关联模型:商品
* @return \think\model\relation\HasOne
*/
public function goods(): \think\model\relation\HasOne
{
return $this->hasOne('Goods', 'id', 'goods_id');
}
/**
* 获取商品信息详情
* @param int $partnersId
* @param int $goodsId
* @return array|null|static
*/
public static function detail(int $partnersId, int $goodsId)
{
return static::get(['partners_id' => $partnersId, 'goods_id' => $goodsId]);
}
/**
* 获取合作商商品列表
* @param int $partnersId 合作商ID
* @return \think\Collection
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public static function getGoodsList(int $partnersId): \think\Collection
{
return (new static)->where('partners_id', '=', $partnersId)->order(['id' => 'asc'])->select();
}
/**
* 累计合作商使用数量
* @param int|array $where
* @param int $num
* @return mixed
*/
public static function setIncUseNum($where, int $num)
{
return (new static)->setInc($where, 'use_num', $num);
}
/**
* 减少合作商库存数量
* @param int|array $where
* @param int $num
* @return mixed
*/
public static function setDecStockNum($where, int $num)
{
return (new static)->setDec($where, 'stock_num', $num);
}
}
@@ -1,54 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\model\partners;
use cores\BaseModel;
/**
* 合作商库存变动记录
* Class StockLog
* @package app\common\model\partners
*/
class StockLog extends BaseModel
{
// 定义表名
protected $name = 'partners_stock_log';
// 定义主键
protected $pk = 'id';
protected $updateTime = false;
/**
* 关联合作商记录表
* @return \think\model\relation\BelongsTo
*/
public function partners()
{
$module = self::getCalledModule();
return $this->belongsTo("app\\{$module}\\model\\Partners");
}
/**
* 新增记录
* @param array $data
*/
public static function add(array $data)
{
$model = new static;
$model->save(array_merge([
'store_id' => $model::$storeId
], $data));
}
}
@@ -0,0 +1,105 @@
<?php
// +----------------------------------------------------------------------
// | 合作商(下单平台)余额管控
// +----------------------------------------------------------------------
// | 企油通改造后,下单按金额直传,不再有商品/库存额度概念,
// | 改由 yoshop_partners.balance 承担下单额度管控:
// | 下单时原子扣减(余额不足直接拒单),发放失败/撤单时原路回补。
// | 扣减用 UPDATE ... WHERE balance >= amount,天然防并发超扣。
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\service;
use app\common\enum\partners\balanceLog\Scene as SceneEnum;
use think\facade\Db;
class PartnersBalance
{
/**
* 原子扣减合作商余额(余额不足返回 false,不做任何变更)
* @param int $partnersId 合作商ID
* @param float $amount 扣减金额(元)
* @param string $orderNo 业务单号(写入流水描述)
* @param int $orderId 关联订单ID
* @param int $storeId 商城ID
* @return int|false 成功返回流水ID(供建单后回填 order_id),余额不足返回 false
*/
public static function deduct(int $partnersId, float $amount, string $orderNo, int $orderId = 0, int $storeId = 0)
{
if ($amount <= 0) {
return false;
}
$affected = Db::name('partners')
->where('partners_id', '=', $partnersId)
->where('balance', '>=', $amount)
->update(['balance' => Db::raw('`balance` - ' . self::money($amount))]);
if (!$affected) {
return false;
}
return self::log(SceneEnum::CONSUME, $partnersId, -$amount, $orderNo, $orderId, $storeId);
}
/**
* 回填流水的关联订单ID
*/
public static function bindOrder($logId, int $orderId): void
{
if ($logId && $orderId) {
Db::name('partners_balance_log')->where('log_id', '=', (int)$logId)->update(['order_id' => $orderId]);
}
}
/**
* 回补合作商余额(发放失败 / 撤单)
*/
public static function refund(int $partnersId, float $amount, string $orderNo, int $orderId = 0, int $storeId = 0): void
{
if ($amount <= 0) {
return;
}
Db::name('partners')
->where('partners_id', '=', $partnersId)
->update(['balance' => Db::raw('`balance` + ' . self::money($amount))]);
self::log(SceneEnum::REFUND, $partnersId, $amount, $orderNo, $orderId, $storeId);
}
/**
* 查询当前余额
*/
public static function balance(int $partnersId): float
{
return (float) Db::name('partners')->where('partners_id', '=', $partnersId)->value('balance');
}
/**
* 写余额变动流水(失败不影响主流程),返回流水ID
* @return int
*/
protected static function log(int $scene, int $partnersId, float $money, string $orderNo, int $orderId, int $storeId): int
{
try {
return (int) Db::name('partners_balance_log')->insertGetId([
'partners_id' => $partnersId,
'scene' => $scene,
'money' => round($money, 2),
'describe' => vsprintf(SceneEnum::data()[$scene]['describe'], [$orderNo]),
'remark' => '',
'order_id' => $orderId,
'store_id' => $storeId,
'create_time' => time(),
]);
} catch (\Throwable $e) {
log_record(['name' => '合作商余额流水写入失败', 'partners_id' => $partnersId, 'msg' => $e->getMessage()]);
return 0;
}
}
/**
* 金额安全格式化(仅允许数字与小数点,防拼接注入)
*/
protected static function money(float $amount): string
{
return number_format(round($amount, 2), 2, '.', '');
}
}
@@ -44,19 +44,21 @@ class QueueService
/**
* 检查延迟队列,将到期任务迁移到普通队列
* 并发安全:先 zrem,只有「移除成功(返回>0)」的进程才把任务放进就绪队列,
* 避免多个消费者同时迁移导致同一任务被投递多次。
* @param string $queueName
*/
public function migrateDelayedToReady($queueName)
{
$now = time();
// 获取到期任务
$tasks = $this->redis->zrangebyscore($queueName . ':delayed', 0, $now);
if (!empty($tasks)) {
foreach ($tasks as $task) {
// 放回普通队列
if (empty($tasks)) {
return;
}
foreach ($tasks as $task) {
// 先抢占式移除,再入队
if ($this->redis->zrem($queueName . ':delayed', $task) > 0) {
$this->push($queueName, $task);
// 从延迟队列中移除
$this->redis->zrem($queueName . ':delayed', $task);
}
}
}
+189
View File
@@ -0,0 +1,189 @@
<?php
// +----------------------------------------------------------------------
// | 企油通 · 上游接口客户端(支付宝企业码通道)
// +----------------------------------------------------------------------
// | 本次实际对接仅 3 个接口:
// | 7.7 运单油费下发 createAndSendQuota —— 本类 createAndSendQuota()
// | 7.15 H5 跳转地址 (本地签名生成 URL) —— 本类 h5Url()
// | 7.10 运单加油明细推送(异步回调) —— app\api\controller\QytNotify
// | 通道固定为支付宝企业码(passageway=1),司机手机号 + 姓名必填。
// | 金额:对外「元」入参,7.7 内部换算为「分」。
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\service;
use app\common\crypto\Qyt as QytCrypto;
class QytOil extends BaseService
{
// 支付宝企业码通道
const PASSAGEWAY_ALIPAY = 1;
/** @var array config('qyt') */
protected $conf;
/** @var QytCrypto */
protected $crypto;
protected function initialize()
{
$this->conf = (array) config('qyt');
$this->crypto = new QytCrypto($this->conf);
}
// ================================================================
// 7.7 运单油费下发(一张订单 = 1 batch / 1 waybill / 1 employee)
// ================================================================
/**
* @param array $data ['batch_no','waybill_no','waybill_no_code','mobile','name','amount'(元)]
* @return array 归一化 ['code'=>'00'|其它, 'data'=>[], 'message'=>'', 'dup_batch'=>bool]
*/
public function createAndSendQuota(array $data): array
{
$params = [
'batch_no' => $data['batch_no'],
'business_license' => $this->conf['business_license'],
'passageway' => (string) self::PASSAGEWAY_ALIPAY,
'institution_id' => $this->conf['institution_id'],
'waybill_list' => [[
'waybill_no' => $data['waybill_no'],
'employee_list' => [[
'mobile' => $data['mobile'],
'name' => $data['name'],
'quota_amount' => self::yuanToFen($data['amount']), // 分
'voucher_name' => $this->conf['voucher_name'],
'effective_start' => '', // 空=当日生效
'effective_end' => '', // 空=长期有效
'waybill_no_code' => $data['waybill_no_code'],
]],
]],
];
return $this->post('/enterprise/api/zhyk/createAndSendQuota', $params);
}
// ================================================================
// 7.15 H5 免登加油地址(本地签名生成 URL,不发起接口请求)
// ================================================================
public function h5Url(string $mobile): string
{
$creditcode = !empty($this->conf['h5_with_creditcode']) ? (string) $this->conf['business_license'] : '';
return $this->crypto->h5Url($mobile, $creditcode);
}
// ================================================================
// 内部:统一请求
// ================================================================
/**
* @return array ['code' => '00'成功 | 其它, 'data' => array, 'message' => string, 'dup_batch' => bool, 'raw' => array]
*/
protected function post(string $path, array $bizParams): array
{
$url = rtrim((string) $this->conf['base_url'], '/') . $path;
$body = $this->crypto->envelope($bizParams);
$this->saveLog("[请求] {$path} " . json_encode(['url' => $url, 'params' => $bizParams], JSON_UNESCAPED_UNICODE));
$resp = $this->curlPost($url, json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$this->saveLog("[响应] {$path} " . $resp);
$json = json_decode($resp, true);
if (!is_array($json)) {
return ['code' => 'ERR', 'data' => [], 'message' => '响应解析失败', 'dup_batch' => false, 'raw' => ['body' => $resp]];
}
// 通用响应结构:{ status: ok|error, data, message, serialNo, sign }
$ok = ($json['status'] ?? '') === 'ok';
// 【TODO 待对接人确认】是否所有响应都带 sign、验签串如何构造
// if ($ok && !empty($json['sign'])) { $ok = $this->crypto->verify($json['data'] ?? [], $json['sign']); }
$message = (string) ($json['message'] ?? '');
// 幂等:batch_no 已存在 —— 视为「已受理」,供 7.7 重试判定
$dupBatch = false;
if (!$ok && $message !== '') {
foreach ((array) (config('qyt.dup_batch_hints') ?: ['已存在', '重复', 'duplicate', 'exist']) as $hint) {
if (mb_stripos($message, $hint) !== false) {
$dupBatch = true;
break;
}
}
}
return [
'code' => $ok ? '00' : 'ERR',
'data' => $json['data'] ?? [],
'message' => $message,
'dup_batch' => $dupBatch,
'raw' => $json,
];
}
protected function curlPost(string $url, string $jsonBody): string
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $jsonBody,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => (int) ($this->conf['timeout'] ?? 15),
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Charset: UTF-8'],
]);
$resp = curl_exec($ch);
if ($resp === false) {
$err = curl_error($ch);
curl_close($ch);
return json_encode(['status' => 'error', 'message' => 'cURL: ' . $err], JSON_UNESCAPED_UNICODE);
}
curl_close($ch);
return (string) $resp;
}
// ================================================================
// 工具
// ================================================================
/** 元 → 分 */
public static function yuanToFen($yuan): int
{
return (int) bcmul((string) $yuan, '100', 0);
}
/** 分 → 元(字符串,保留 2 位) */
public static function fenToYuan($fen): string
{
return bcdiv((string) (int) $fen, '100', 2);
}
/**
* 生成企油通全平台唯一编号(加企业前缀),确定性派生 -> 重试保持一致
* @param string $type B=batch / W=waybill / C=waybill_no_code
* @param string $seed 订单号 / order_id 等业务种子
*/
public static function genNo(string $type, string $seed): string
{
$prefix = (string) (config('qyt.no_prefix') ?: 'HM');
return $prefix . $type . $seed;
}
/** 记录日志到 runtime/storelog */
public function saveLog($data): void
{
if (empty($this->conf['log'])) {
return;
}
$dir = runtime_path('storelog/') . date('Ym') . '/' . date('Ymd');
is_dir($dir) || @mkdir($dir, 0777, true);
@file_put_contents(
$dir . '/qyt_' . date('YmdH') . '.txt',
date('Y-m-d H:i:s ') . (is_string($data) ? $data : json_encode($data, JSON_UNESCAPED_UNICODE)) . "\r\n",
FILE_APPEND
);
}
}
@@ -330,6 +330,70 @@ class CallbackQueue extends Command
// $SybOil->saveLogStore('重新加入队列:【'.$message.'】');
}
break;
case "qyt_consume":
// 企油通 7.10 运单加油明细推送(一次可能多条;企油通可能重推 -> 全流程幂等)
foreach ((array)($res['order_list'] ?? []) as $c) {
$code = $c['waybill_no_code'] ?? '';
$order = Db::name('partners_order')->where('waybill_no_code', $code)->find();
if (!$order) {
$SybOil->saveLogStore('企油通消费回调未匹配订单:' . json_encode($c, 320));
continue;
}
$payTime = !empty($c['pay_time']) ? (strtotime($c['pay_time']) ?: time()) : time();
// 落消费流水(uk_consume 去重:重复回调直接忽略)
try {
Db::name('qyt_consume')->insert([
'order_id' => $order['order_id'],
'waybill_no' => $c['waybill_no'] ?? '',
'waybill_no_code' => $code,
'order_code' => $c['order_code'] ?? '',
'station_code' => $c['station_code'] ?? '',
'station_name' => $c['station_name'] ?? '',
'oil_name' => $c['oil_name'] ?? '',
'litter' => $c['litter'] ?? '',
'oil_price' => (int)($c['oil_price'] ?? 0),
'actual_oil_price' => (int)($c['actual_oil_price'] ?? 0),
'pay_fee' => (int)($c['pay_fee'] ?? 0),
'consume_amount' => (int)($c['consume_amount'] ?? 0),
'pay_time' => $payTime,
'raw' => json_encode($c, 320),
'store_id' => $order['store_id'],
'create_time' => time(),
]);
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'Duplicate') === false) {
throw $e;
}
$SybOil->saveLogStore('企油通消费回调重复,忽略流水:' . $code . '/' . ($c['order_code'] ?? ''));
}
// 条件跃迁 use_status 0 -> 1(要求已发放),仅胜出者通知下单平台
$affected = Db::name('partners_order')->where('order_id', $order['order_id'])
->where('use_status', 0)
->where('oil_status', 1)
->update([
'use_status' => 1,
'use_time' => time(),
'use_text' => '加油',
'qyt_order_code' => $c['order_code'] ?? '',
'shareNo' => $c['order_code'] ?: $order['shareNo'],
'station_name' => $c['station_name'] ?? '',
'oil_name' => $c['oil_name'] ?? '',
'litter' => $c['litter'] ?? '',
'pay_fee' => (int)($c['pay_fee'] ?? 0),
'consume_time' => $payTime,
'update_time' => time(),
]);
if ($affected) {
$queueService = new QueueService();
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'optType' => 'add_oil',
'retry_count' => 0,
], 320));
$SybOil->saveLogStore('企油通消费回调处理成功:' . json_encode($c, 320));
}
}
break;
default:
$SybOil->saveLogStore('队列数据处理异常,未知:【'.$message.'】');
}
+163 -91
View File
@@ -1,36 +1,54 @@
<?php
// +----------------------------------------------------------------------
// | 下单平台订单 -> 企油通「运单油费下发」(7.7)
// +----------------------------------------------------------------------
// | OilCard::share 创建 yoshop_partners_order 后 push 'partners_queue',本命令消费:
// | 1. 每个 order_id 用 RedisLock 串行化,锁内重读订单状态
// | 2. 生成企油通全平台唯一编号(确定性派生,可安全重试)
// | 3. 调 7.7 createAndSendQuota;支付宝企业码通道下「受理成功即完成发放」
// | 4. 发放成功 -> oil_status=1 / oil_status_zsy=2,投 notify_queue 回调下单平台
// |
// | 并发/原子性:
// | · oil_status_zsy 只在 7.7 结果确定后离开 0;空闲时 reconcile() 补投丢失的队列消息
// | · 状态跃迁为条件 UPDATE(... WHERE oil_status_zsy=0),affected=1 才回调,天然幂等
// | · batch_no 确定性派生 -> 崩溃重试用同一值,依赖企油通按 batch_no 幂等去重
// | 启动:php think partners:queue(可多进程;RedisLock 保证同一订单串行)
// +----------------------------------------------------------------------
namespace app\console\command;
use app\common\service\IdingjuOil;
use app\common\lock\RedisLock;
use app\common\model\partners\Order as OrderModel;
use app\common\service\PartnersBalance;
use app\common\service\QueueService;
use app\store\model\Company as CompanyModel;
use app\common\service\QytOil;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\common\model\partners\Order as OrderModel;
use think\facade\Db;
class PartnersQueue extends Command
{
/** @var QueueService */
protected $queueService;
/** @var int 上次兜底巡检时间 */
protected $lastReconcile = 0;
protected function configure()
{
$this->setName('partners:queue')
->setDescription('Process messages from the Redis queue');
->setDescription('下单平台订单 -> 企油通运单油费下发(7.7)');
}
protected function execute(Input $input, Output $output)
{
$this->queueService = new QueueService();
while (true) {
$message = $this->queueService->pop('partners_queue');
if ($message) {
// 处理消息
$this->processMessage($message);
} else {
// 暂停一段时间再检查队列
$this->reconcile();
sleep(1);
}
}
@@ -38,103 +56,157 @@ class PartnersQueue extends Command
protected function processMessage($message)
{
$IdingjuOil = new IdingjuOil();
$IdingjuOil->saveLogStore('partners_queue队列数据处理:【'.$message.'】');
// 拿到队列数据处理
$data = json_decode($message, true);
// 检查解码是否成功
if ($data === false) {
$IdingjuOil->saveLogStore('partners_queue队列数据解码失败:' . json_last_error_msg());
$qyt = new QytOil();
$data = is_array($message) ? $message : json_decode($message, true);
$orderId = $data['order_id'] ?? null;
if (!$orderId) {
$qyt->saveLog('partners_queue 消息缺少 order_id:' . json_encode($message, 320));
return;
}
$order = OrderModel::detail($data['order_id']);
if (!$order) {
$IdingjuOil->saveLogStore('未找到对应订单:【' . $data['order_id'] . '】');
// 每个订单串行处理;锁 60s 自动过期,崩溃不会死锁
$lock = new RedisLock('qyt:send:' . $orderId, 60);
if (!$lock->acquireLock()) {
$qyt->saveLog("partners_queue 订单处理中,跳过 order={$orderId}");
return;
}
// 此处可以根据业务逻辑处理接收到的数据
try {
if($order['order_status'] == 10 && $order['oil_status'] == 0 && $order['oilCardType'] == 'zsy_ticket'){
$data['amount'] = $order['total_price']; // 金额
$company = CompanyModel::detail(['orgNo' => $this->getCompany($order['goods_no'])]);
if (!$company) {
$IdingjuOil->saveLogStore('没有可用参数:【' . $order['goods_no'] . '】');
return;
}
// 对接平台接口
$res = $IdingjuOil->oilOilCardShare([
'goodsNo'=>$order['goods_no'],
'amount'=>$data['amount'],
'userNo'=>'',
'mobile'=>$order['mobile'],
'saleOrderNo'=>$order['oilCardNo'],
'oilCardType'=>$order['oilCardType'],
'notify_url'=>config('app.base_url') . 'partnersoil.php',
'use_notify_url'=>config('app.base_url') . 'partnersoiluse.php'
], $company);
if ($res['code'] != 1) {
$IdingjuOil->saveLogStore(($res['errMessage'] ?? $res['message']));
return;
}
$item['shareNo'] = $res['data']['trade_no'] ?? '';
$item['qrCode'] = $res['data']['useUrl'] ?? '';
$item['oil_status_zsy'] = $res['data']['state'] ?? 0;
$item['oil_time'] = time();
$item['oil_status'] = $item['oil_status_zsy'] == 2 ? 1 : 0; // 已发券 为1
$resUpdate = $order->save($item);
if (!$resUpdate) {
$IdingjuOil->saveLogStore("订单更新失败,order_id: {$data['order_id']}");
}
if($item['oil_status_zsy'] == 2) {
// 存入队列处理
$queueService = new QueueService();
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'type' => 'add',
'optType' => 'share_oil',
'retry_count' => 0,
], 320));
}
$order = OrderModel::detail($orderId);
if (!$order) {
$qyt->saveLog('partners_queue 未找到订单:' . $orderId);
return;
}
} catch (\Exception $e) {
$IdingjuOil->saveLogStore(
'partners_queue队列处理异常:' . $e->getMessage() .
' 在文件:' . $e->getFile() .
' 第:' . $e->getLine() .
' 行。堆栈跟踪:' . $e->getTraceAsString()
);
if ($order['order_status'] != 10 || $order['oil_status'] != 0 || (int) $order['oil_status_zsy'] !== 0) {
$qyt->saveLog("partners_queue 订单状态不满足下发条件 order={$orderId}");
return;
}
// 1. 确定性派生编号(含企业前缀),重试时保持一致
$seed = $order['partners_order_no'] ?: (string) $order['order_id'];
$batchNo = QytOil::genNo('B', $seed);
$waybillNo = QytOil::genNo('W', $seed);
$waybillNoCode = QytOil::genNo('C', $seed);
$name = trim((string) $order['driver_name']);
if ($name === '' || $order['mobile'] === '') {
$failed = Db::name('partners_order')->where('order_id', $orderId)->where('oil_status_zsy', 0)
->update(['oil_status_zsy' => 3, 'oil_text' => '缺司机姓名/手机号,无法发放', 'update_time' => time()]);
$failed && $this->refundBalance($order);
return;
}
// 2. 先落编号(状态不动,仍为 0)
Db::name('partners_order')->where('order_id', $orderId)->update([
'batch_no' => $batchNo,
'waybill_no' => $waybillNo,
'waybill_no_code' => $waybillNoCode,
'oil_text' => '发放中',
'update_time' => time(),
]);
// 3. 7.7 运单油费下发
$res = $qyt->createAndSendQuota([
'batch_no' => $batchNo,
'waybill_no' => $waybillNo,
'waybill_no_code' => $waybillNoCode,
'mobile' => $order['mobile'],
'name' => $name,
'amount' => $order['total_price'],
]);
// 失败(且非 batch 已存在)
if ($res['code'] !== '00' && empty($res['dup_batch'])) {
$failed = Db::name('partners_order')->where('order_id', $orderId)->where('oil_status_zsy', 0)->update([
'oil_status_zsy' => 3,
'oil_text' => '下发失败:' . $res['message'],
'update_time' => time(),
]);
$failed && $this->refundBalance($order);
$qyt->saveLog("7.7 下发失败 order={$orderId}:" . $res['message']);
return;
}
// data.status: 1 成功 / 2 失败(dup_batch 视为已受理成功)
$status = empty($res['dup_batch']) ? (string) ($res['data']['status'] ?? '1') : '1';
if ($status === '2') {
$failed = Db::name('partners_order')->where('order_id', $orderId)->where('oil_status_zsy', 0)->update([
'oil_status_zsy' => 3,
'oil_text' => $res['data']['msg'] ?? '下发失败',
'update_time' => time(),
]);
$failed && $this->refundBalance($order);
return;
}
// 4. 受理成功 = 发放成功(支付宝企业码通道);条件跃迁 0 -> 2,胜出者回调下单平台
$affected = Db::name('partners_order')->where('order_id', $orderId)->where('oil_status_zsy', 0)->update([
'oil_status' => 1,
'oil_status_zsy' => 2,
'oil_time' => time(),
'shareNo' => $waybillNoCode,
'oil_text' => $res['data']['msg'] ?? '发放成功',
'update_time' => time(),
]);
if ($affected) {
$this->queueService->push('notify_queue', json_encode([
'order_id' => $orderId,
'type' => 'add',
'optType' => 'share_oil',
'retry_count' => 0,
], 320));
$qyt->saveLog("7.7 发放成功 order={$orderId} batch_no={$batchNo}"
. (empty($res['dup_batch']) ? '' : '(batch 已存在,按已发放处理)'));
}
} catch (\Throwable $e) {
// 不改状态(仍为 0),交由 reconcile 兜底重投
$qyt->saveLog('partners_queue 异常 order=' . $orderId . ':' . $e->getMessage()
. ' @ ' . $e->getFile() . ':' . $e->getLine());
} finally {
$lock->releaseLock();
}
}
/**
* @param $goodsNo
* @return string
* 发放失败 -> 回补合作商余额(仅在状态跃迁胜出时调用,天然幂等)
*/
public function getCompany($goodsNo): string
protected function refundBalance($order)
{
switch ($goodsNo) {
case 'djgdface100':
case 'djgdface200':
case 'djgdface300':
return 'umQZhIHKofyA';
case 'jmgxyyface100':
case 'jmgxyyface200':
case 'jmgxyyface300':
PartnersBalance::refund(
(int) $order['partners_id'],
(float) $order['total_price'],
(string) $order['partners_order_no'],
(int) $order['order_id'],
(int) $order['store_id']
);
(new QytOil())->saveLog("发放失败已回补合作商余额 order={$order['order_id']} amount={$order['total_price']}");
}
case 'jmgzface100':
case 'jmgzface200':
case 'jmgzface300':
case 'jmfjface100':
case 'jmfjface200':
case 'jmfjface300':
return 'HpWqXIgYvTBg';
case 'face003':
return 'NKJBgaaRNdiL'; // 测试参数
default:
return '';
/**
* 兜底巡检(限频 60s):补投丢失的 partners_queue 消息。
* 因下发为条件跃迁,重复投递安全。
*/
protected function reconcile()
{
if (time() - $this->lastReconcile < 60) {
return;
}
$this->lastReconcile = time();
$now = time();
try {
$ids = Db::name('partners_order')
->where('order_status', 10)
->where('oil_status_zsy', 0)
->where('oilCardType', 'zsy_ticket')
->where('create_time', '<', $now - 120)
->where('create_time', '>', $now - 86400)
->limit(200)->column('order_id');
foreach ($ids as $oid) {
$this->queueService->push('partners_queue', json_encode(['order_id' => $oid], 320));
}
if ($ids) {
(new QytOil())->saveLog('partners_queue reconcile 补投 ' . count($ids) . ' 单');
}
} catch (\Throwable $e) {
(new QytOil())->saveLog('partners_queue reconcile 异常:' . $e->getMessage());
}
}
}
+2 -42
View File
@@ -224,48 +224,8 @@ class Company extends Controller
*/
public function stock(): Json
{
$arr = $this->postData();
// 商品详情
foreach ((array)$arr as $company_id) {
$company = CompanyModel::detail($company_id);
$IdingjuOil = new IdingjuOil();
$goodsNo = $this->getGoodsNo($company['orgNo']);
$goodsList = GoodsModel::getAll([['goods_no','in',$goodsNo]]);
$canOrderNum = $unUseNum = $useNum = 0;
foreach ($goodsList as $array){
// 对接平台接口
$res = $IdingjuOil->stockList(['goodsNo' => $array['goods_no']], $company);
// 更新记录
if ($res['code'] == '1') {
/*$res['data']['canOrderNum'] = $res['data']['canOrderNum']?:0;
$res['data']['unUseNum'] = $res['data']['unUseNum']?:0;
$res['data']['useNum'] = $res['data']['useNum']?:0;
$canOrderNum += $res['data']['canOrderNum'];
$unUseNum += $res['data']['unUseNum'];
$useNum += $res['data']['useNum'];
Db::name('goods')
->where('goods_id', $array['goods_id'])
->update(['stock_total' => $res['data']['canOrderNum'], 'unUseNum' => $res['data']['unUseNum'], 'useNum' => $res['data']['useNum']]);
Db::name('goods_sku')
->where('goods_id', $array['goods_id'])
->update(['stock_num' => $res['data']['canOrderNum'], 'unUseNum' => $res['data']['unUseNum'], 'useNum' => $res['data']['useNum']]);
*/
$res['data']['quantity'] = $res['data']['quantity']?:0;
$canOrderNum += $res['data']['quantity'];
Db::name('goods')
->where('goods_id', $array['goods_id'])
->update(['stock_total' => $res['data']['quantity']]);
Db::name('goods_sku')
->where('goods_id', $array['goods_id'])
->update(['stock_num' => $res['data']['quantity']]);
}
}
$company->save(['canOrderNum'=>$canOrderNum,'unUseNum'=>$unUseNum,'useNum'=>$useNum]);
}
return $this->renderSuccess($arr);
// 企油通改造:本功能依赖已下线的上游(智慧油客/定聚),现已停用。
return $this->renderError('上游库存同步已停用(企油通无油券库存概念)');
}
}
+2 -11
View File
@@ -183,17 +183,8 @@ class Goods extends Controller
*/
public function onReloadStock(int $goodsId): Json
{
// 商品详情
$model = GoodsModel::detail($goodsId);
$company = CompanyModel::detail(['status'=>10]);
$SybOil = new SybOil();
// 对接平台接口
$res = $SybOil->stockList(['goodsNo' => $model['goods_no']], $company);
// 更新记录
if ($res['code'] == '00') {
return $this->renderSuccess($res['data']);
}
return $this->renderError($model->getError() ?: $res['errMessage']);
// 企油通改造:上游库存同步已停用(企油通无油券库存概念,库存改为平台内部维护)。
return $this->renderError('上游库存同步已停用,请直接在「合作商」中维护可下单额度');
}
/**
+6 -126
View File
@@ -153,47 +153,8 @@ class Order extends Controller
*/
public function cancelShare(): Json
{
$postData = $this->postForm();
$postData['order_goods_id'] = $this->request->param('orderGoodsId');
$order_goods_id = $postData['order_goods_id'];
$password = $postData['password'];
//验证密码
$user = UserModel::detail($this->store['user']['store_user_id']);
if(!password_verify($password, $user['password'])){
return $this->renderError('登录密码错误');
}
$model = OrderGoodsModel::detail($order_goods_id);
if ($model['oil_status'] != 1) {
return $this->renderError('该订单状态不是已分油状态');
}
if ($model['use_status'] != 0) {
return $this->renderError('油卡已核销,无法撤销');
}
$order = OrderModel::detail($model['order_id']);
$company = CompanyModel::detail($model['company_id']);
// 对接平台接口,撤销分油
$res = $this->oilOilCardCancelShare(['shareNo'=>$model['shareNo'],'amount'=>$model['goods_price'],'saleOrderNo'=>$order['order_no'],'type'=>'recovery','oilCardType'=>$model['oilCardType']], $company);
if($res['code'] == '00'){
if ($model->cancelShare($postData)) {
// 更新商户余额(增加)
MerchantModel::setIncBalance((int)$model['merchant_id'], (float)$model['goods_price']);
BalanceLogModel::add(SceneEnum::REFUND, [
'merchant_id' => (int)$model['merchant_id'],
'money' => (float)$model['goods_price'],
], ['saleOrderNo' => $order['order_no']]);
// 更新公司余额(增加)
CompanyModel::setIncBalance((int)$model['company_id'], (float)$model['goods_price']);
CompanyBalanceLogModel::add(SceneEnum::REFUND, [
'company_id' => (int)$model['company_id'],
'money' => (float)$model['goods_price'],
], ['saleOrderNo' => $order['order_no']]);
return $this->renderSuccess('操作成功');
}
return $this->renderError($model->getError() ?: '操作失败');
}else{
return $this->renderError('平台撤销分油失败【'.($res['errMessage'] ?? $res['message']).'】');
}
// 企油通改造:本功能依赖已下线的上游(智慧油客/定聚),现已停用。
return $this->renderError('小程序油品链路已停用');
}
/**
@@ -203,36 +164,8 @@ class Order extends Controller
*/
public function qrCode(): Json
{
$postData = $this->postForm();
$postData['order_goods_id'] = $this->request->param('orderGoodsId');
$orderGoods = OrderGoodsModel::detail($postData['order_goods_id']);
if($orderGoods['use_status'] != 0){
return $this->renderError('油券已核销');
}else {
$company = CompanyModel::detail($orderGoods['company_id']);
$IdingjuOil = new IdingjuOil();
// 对接平台接口
$res = $IdingjuOil->payQrCodeRequest(['order_no'=>$orderGoods['oilCardNo']], $company);
if($res['code'] == '1' && isset($res['data']['useQrCodeUrl'])){
// 插入支付码记录
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$orderGoods['oilCardNo'],
'qrCode'=>$res['data']['useQrCodeUrl'],
'amount'=>$orderGoods['goods_price'],
'order_id'=>$orderGoods['order_id'],
'user_id'=>$orderGoods['user_id'],
'store_id'=>$orderGoods['store_id'],
]);
$res = [
'qrCode'=>$res['data']['useQrCodeUrl']
];
return $this->renderSuccess($res, '提交成功');
}else{
return $this->renderError($res['errMessage'] ?? $res['message']);
}
}
// 企油通改造:本功能依赖已下线的上游(智慧油客/定聚),现已停用。
return $this->renderError('小程序油品链路已停用');
}
/**
@@ -317,60 +250,7 @@ class Order extends Controller
*/
public function share(): Json
{
$IdingjuOil = new IdingjuOil();
$postData = $this->request->param();
$postData['order_goods_id'] = $this->request->param('orderGoodsId');
$goods = OrderGoodsModel::detail($postData['order_goods_id']);
if($goods['oil_status'] != 0){
return $this->renderError('已分油');
}else {
if($goods['oilCardType']) {
$data['amount'] = $goods['total_price']; // 金额
$company = CompanyModel::detail(['orgNo' => $this->getCompany($goods['goods_no'])]);
if (!$company) {
return $this->renderError('已分油');
}
try {
// 查找一个未被调用的用户
$account = $this->processUserOrders($company['company_id']);
} catch (\Throwable $e) {
return $this->renderError($e->getMessage());
}
// 查询公司余额
/*if ($data['amount'] > $company['balance']) {
$this->error = '公司余额不足';
return false;
}*/
// 对接平台接口
$res = $IdingjuOil->oilOilCardShare([
'goodsNo'=>$goods['goods_no'],
'amount'=>$data['amount'],
'userNo'=>$account['userNo'],
'mobile'=>$account['mobile'],
'saleOrderNo'=>$goods['oilCardNo'],
'oilCardType'=>$goods['oilCardType']
], $company);
//$res = json_decode('{"code":"00","message":"请求成功","errCode":null,"errMessage":null,"data":{"oilCardNo":null,"amount":100,"balance":null,"shareNo":"CDV2408021928160001"}}', true);
if ($res['code'] != 1) {
return $this->renderError($res['errMessage'] ?? $res['message']);
}
$IdingjuOil->saveLogStore("分油接口结果".json_encode($res,320));
$item['account_id'] = $account['account_id'];
$item['userNo'] = $account['userNo'];
$item['mobile'] = $account['mobile'];
$item['company_id'] = $company['company_id'];
$item['shareNo'] = $res['data']['trade_no'] ?? '';
$item['qrCode'] = $res['data']['useUrl'] ?? '';
$item['oil_status_zsy'] = $res['data']['state'] ?? 0;
$item['oil_time'] = time();
$item['oil_status'] = $item['oil_status_zsy'] == 2 ? 1 : 0; // 已发券 为1
$r = $goods->save($item);
$IdingjuOil->saveLogStore("保存分油结果".json_encode($r,320));
return $this->renderSuccess('分油成功');
}
}
// 企油通改造:本功能依赖已下线的上游(智慧油客/定聚),现已停用。
return $this->renderError('小程序油品链路已停用');
}
}
@@ -5,7 +5,6 @@ namespace app\store\controller;
use app\store\controller\Controller;
use app\store\model\Partners as PartnersModel;
use app\store\model\PartnersGoods as PartnersGoodsModel;
use app\store\model\partners\Order as OrderModel;
use app\store\model\store\User;
use think\response\Json;
@@ -94,13 +93,6 @@ class Partners extends Controller
public function detail(int $partnersId): Json
{
$partnersInfo = PartnersModel::detail($partnersId);
// 商品列表
$partnersInfo['goodsList'] = PartnersGoodsModel::getGoodsList($partnersInfo['partners_id']);
// 给每个元素加上 allot_num
foreach ($partnersInfo['goodsList'] as &$item) {
$item['allot_num'] = 0; // 默认值,可以改成其他逻辑
}
unset($item); // 解除引用,避免后续误操作
return $this->renderSuccess(compact('partnersInfo'));
}
+4 -108
View File
@@ -64,40 +64,8 @@ class User extends Controller
*/
public function add()
{
$model = new UserModel;
$data = $this->postForm();
if (empty($data['company_id'])) {
return $this->renderError('请填写分油公司');
}
if (empty($data['merchant_id'])) {
return $this->renderError('请填写商户');
}
if (empty($data['userType'])) {
return $this->renderError('请填写用户角色');
}
if (empty($data['userName'])) {
return $this->renderError('请填写用户姓名');
}
if (empty($data['mobile'])) {
return $this->renderError('请填写用户手机号');
}
if (empty($data['userIdCard'])) {
return $this->renderError('请填写用户身份证号');
}
$company = CompanyModel::detail($data['company_id']);
// 对接平台接口
$res = $this->userOptAdd(['mobile'=>$data['mobile'],'userName'=>$data['userName'],'userIdCard'=>$data['userIdCard'],'userType'=>$data['userType']], $company);
if($res['code'] == '00' && isset($res['data']['userNo'])){
$data['userNo'] = $res['data']['userNo'];
}else{
return $this->renderError('平台创建用户失败【'.($res['errMessage'] ?? $res['message']).'】');
}
// 新增记录
if ($model->add($data,$this->store['user'])) {
return $this->renderSuccess('添加成功');
}
return $this->renderError($model->getError() ?: '添加失败');
// 企油通改造:分油用户池已停用(司机信息改由下单平台随单传入),不再向上游注册用户。
return $this->renderError('分油用户池已停用');
}
/**
@@ -201,80 +169,8 @@ class User extends Controller
*/
public function oil(int $userId, string $target)
{
$data = $this->postForm();
$user = UserModel::detail($userId);
$data['amount'] = $data['amount']??0;
$data['goodsNo'] = $data['goodsNo']??'';
// 用户详情
$model = new OrderModel();
if (empty($data['oilCardType'])) {
return $this->renderError('请填写卡券类型');
}
if ($data['oilCardType'] == 'card' && empty($data['amount'])) {
return $this->renderError('请填写分油金额');
}
if ($data['oilCardType'] != 'card' && empty($data['goods_id'])) {
return $this->renderError('请选择油券');
}
if($data['goods_id']) {
$goods = GoodsModel::detail($data['goods_id']);
$data['goodsNo'] = $goods['goods_no'];
$data['amount'] = $goods['goods_price_max']; // 金额
}
// 查询用户余额
if($data['amount'] > $user['balance']){
return $this->renderError('用户余额不足');
}
$saleOrderNo = OrderService::createOrderNo();
try {
// 查找一个未被调用的用户
$account = $this->processUserOrders();
} catch (Exception $e) {
return $this->renderError($e->getMessage());
}
$company = CompanyModel::detail($account['company_id']);
// 查询公司余额
if($data['amount'] > $company['balance']){
return $this->renderError('公司余额不足');
}
$SybOil = new SybOil();
// 对接平台接口
$res = $SybOil->oilOilCardShare(['goodsNo'=>$data['goodsNo'],'amount'=>$data['amount'],'userNo'=>$account['userNo'],'mobile'=>$account['mobile'],'saleOrderNo'=>$saleOrderNo,'oilCardType'=>$data['oilCardType']], $company);
//$res = json_decode('{"code":"00","message":"请求成功","errCode":null,"errMessage":null,"data":{"oilCardNo":null,"amount":100,"balance":null,"shareNo":"CDV2407271355570001"}}',true);
if($res['code'] == '00'){
$data['saleOrderNo'] = $saleOrderNo;
$data['oilCardNo'] = $data['oilCardType'] == 'zsy_ticket' ? $res['data']['shareNo'] : $res['data']['oilCardNo'];
$data['amount'] = $res['data']['amount'];
$data['shareNo'] = $res['data']['shareNo'] ?? '';
$data['company_id'] = $account['company_id'];
$data['account_id'] = $account['account_id'];
$data['userNo'] = $account['userNo'];
$data['mobile'] = $account['mobile'];
}else{
return $this->renderError('平台分油失败【'.($res['errMessage'] ?? $res['message']).'】');
}
$data['user_id'] = $user['user_id'];
$data['store_id'] = $user['store_id'];
$data['payType'] = 10;
if ($model->addOil($data)) {
// 更新用户余额
UserModel::setDecBalance((int)$user['user_id'], (float)$data['amount']);
BalanceLogModel::add(SceneEnum::CONSUME, [
'user_id' => (int)$user['user_id'],
'money' => -(float)$data['amount'],
], ['saleOrderNo' => $saleOrderNo]);
// 累积用户总消费金额
UserModel::setIncPayMoney($user['user_id'], (float)$data['amount']);
// 更新公司余额
CompanyModel::setDecBalance((int)$user['company_id'], (float)$data['amount']);
CompanyBalanceLogModel::add(SceneEnum::CONSUME, [
'company_id' => (int)$user['company_id'],
'money' => -(float)$data['amount'],
], ['saleOrderNo' => $saleOrderNo]);
return $this->renderSuccess('操作成功');
}
return $this->renderError($model->getError() ?: '操作失败');
// 企油通改造:后台给小程序用户手动分油依赖已下线的上游(智慧油客)+ 分油用户池,现已停用。
return $this->renderError('小程序油品链路已停用');
}
/**
+126 -188
View File
@@ -1,34 +1,31 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// | 后台 · 下单平台(合作商)订单管理
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// | 企油通改造后:
// | list() 订单列表(含企油通批次号/运单号/发放态/加油明细)
// | detail() 订单详情 + 加油消费流水
// | share() 重新发放:把订单打回未提交态并重投 partners_queue,由 7.7 下发
// | qrCode() 获取司机免登加油地址(7.15),出参 qrCode 为 H5 URL
// | oilCallback() / useCallback() 重推「发放/加油」结果给下单平台
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\store\controller\partners;
use app\store\controller\Controller;
use app\store\model\partners\Order as OrderModel;
use app\store\model\Company as CompanyModel;
use app\common\service\IdingjuOil;
use app\store\model\order\QrCode as QrCodeModel;
use app\common\service\PartnersBalance;
use app\common\service\QueueService;
use app\common\service\QytOil;
use app\store\controller\Controller;
use app\store\model\order\QrCode as QrCodeModel;
use app\store\model\partners\Order as OrderModel;
use think\facade\Db;
use think\response\Json;
/**
* 余额明细
* Class Order
* @package app\store\controller\user
*/
class Order extends Controller
{
/**
* 订单明细
* 订单列表
* @return mixed
*/
public function list()
@@ -39,207 +36,148 @@ class Order extends Controller
}
/**
* 获取支付二维码
* 订单详情(含企油通加油消费流水)
* @return Json
*/
public function detail(): Json
{
$orderId = (int) $this->request->param('orderId');
$order = OrderModel::detail($orderId);
if (!$order) {
return $this->renderError('订单不存在');
}
$consume = Db::name('qyt_consume')
->where('order_id', $orderId)
->order('id', 'desc')
->select()
->toArray();
return $this->renderSuccess(['order' => $order, 'consume' => $consume]);
}
/**
* 获取司机免登加油地址(企油通 7.15)
* @return Json
* @throws \think\db\exception\DbException
*/
public function qrCode(): Json
{
$postData = $this->postForm();
$postData['order_id'] = $this->request->param('orderId');
$order = OrderModel::detail($postData['order_id']);
if($order['use_status'] != 0){
return $this->renderError('油券已核销');
}else {
$company = CompanyModel::detail(['orgNo' => $this->getCompany($order['goods_no'])]);
$IdingjuOil = new IdingjuOil();
// 对接平台接口
$res = $IdingjuOil->payQrCodeRequest(['order_no'=>$order['oilCardNo']], $company);
if($res['code'] == '1' && isset($res['data']['useQrCodeUrl'])){
// 插入支付码记录
$QrCodeModel = new QrCodeModel();
$QrCodeModel->add([
'oilCardNo'=>$order['oilCardNo'],
'qrCode'=>$res['data']['useQrCodeUrl'],
'amount'=>$order['goods_price'],
'order_id'=>$order['order_id'],
'user_id'=>0,
'store_id'=>$order['store_id'],
]);
$res = [
'qrCode'=>$res['data']['useQrCodeUrl']
];
return $this->renderSuccess($res, '提交成功');
}else{
return $this->renderError($res['errMessage'] ?? $res['message']);
}
$orderId = (int) $this->request->param('orderId');
$order = OrderModel::detail($orderId);
if (!$order) {
return $this->renderError('订单不存在');
}
if ($order['use_status'] != 0) {
return $this->renderError('该单已加油核销');
}
if ($order['oil_status'] != 1) {
return $this->renderError('该单尚未发放成功,无法获取加油地址');
}
$h5Url = (new QytOil())->h5Url((string) $order['mobile']);
if (!$h5Url) {
return $this->renderError('生成加油地址失败,请检查企油通配置');
}
// 留痕
(new QrCodeModel())->add([
'oilCardNo' => $order['oilCardNo'],
'qrCode' => $h5Url,
'amount' => $order['goods_price'],
'order_id' => $order['order_id'],
'user_id' => 0,
'store_id' => $order['store_id'],
]);
return $this->renderSuccess(['qrCode' => $h5Url], '获取成功');
}
/**
* @param $goodsNo
* @return string
*/
public function getCompany($goodsNo): string
{
switch ($goodsNo) {
case 'djgdface100':
case 'djgdface200':
case 'djgdface300':
return 'umQZhIHKofyA';
case 'jmgxyyface100':
case 'jmgxyyface200':
case 'jmgxyyface300':
case 'jmgzface100':
case 'jmgzface200':
case 'jmgzface300':
case 'jmfjface100':
case 'jmfjface200':
case 'jmfjface300':
return 'HpWqXIgYvTBg';
case 'face003':
return 'NKJBgaaRNdiL'; // 测试参数
default:
return '';
}
}
/**
* 手动分油
* 重新发放(把订单打回未提交态并重投队列,由 partners:queue 调用企油通 7.7)
* @return Json
* @throws \think\db\exception\DbException
*/
public function share(): Json
{
$IdingjuOil = new IdingjuOil();
$postData = $this->request->param();
$postData['order_id'] = $this->request->param('orderId');
$order = OrderModel::detail($postData['order_id']);
if($order['oil_status'] != 0){
return $this->renderError('已分油');
}else {
if($order['oilCardType']) {
$data['amount'] = $order['total_price']; // 金额
$company = CompanyModel::detail(['orgNo' => $this->getCompany($order['goods_no'])]);
if (!$company) {
return $this->renderError('已分油');
}
// 对接平台接口
$res = $IdingjuOil->oilOilCardShare([
'goodsNo'=>$order['goods_no'],
'amount'=>$data['amount'],
'userNo'=>0,
'mobile'=>$order['mobile'],
'saleOrderNo'=>$order['oilCardNo'],
'oilCardType'=>$order['oilCardType']
], $company);
if ($res['code'] != 1) {
return $this->renderError($res['errMessage'] ?? $res['message']);
}
$IdingjuOil->saveLogStore("分油接口结果".json_encode($res,320));
// $item['company_id'] = $company['company_id'];
$item['shareNo'] = $res['data']['trade_no'] ?? '';
$item['qrCode'] = $res['data']['useUrl'] ?? '';
$item['oil_status_zsy'] = $res['data']['state'] ?? 0;
$item['oil_time'] = time();
$item['oil_status'] = $item['oil_status_zsy'] == 2 ? 1 : 0; // 已发券 为1
$r = $order->save($item);
$IdingjuOil->saveLogStore("保存分油结果".json_encode($r,320));
return $this->renderSuccess('分油成功');
}
$orderId = (int) $this->request->param('orderId');
$order = OrderModel::detail($orderId);
if (!$order) {
return $this->renderError('订单不存在');
}
if ($order['oil_status'] == 1) {
return $this->renderError('该单已发放成功,无需重发');
}
if ($order['use_status'] != 0) {
return $this->renderError('该单已加油核销,不可重发');
}
// 发放失败时余额已回补,重发需重新扣减合作商余额
$logId = PartnersBalance::deduct(
(int) $order['partners_id'],
(float) $order['total_price'],
(string) $order['partners_order_no'],
$orderId,
(int) $order['store_id']
);
if ($logId === false) {
return $this->renderError('合作商余额不足,请先充值后再重发');
}
// 仅允许从「发放失败(3)」重置为「未提交(0)」,避免与正在处理的单冲突
$reset = Db::name('partners_order')->where('order_id', $orderId)
->where('oil_status', 0)
->where('oil_status_zsy', 3)
->update(['oil_status_zsy' => 0, 'oil_text' => '后台重新发放', 'update_time' => time()]);
if (!$reset) {
// 状态已被其它进程改变 -> 回补刚扣的余额
PartnersBalance::refund(
(int) $order['partners_id'],
(float) $order['total_price'],
(string) $order['partners_order_no'],
$orderId,
(int) $order['store_id']
);
return $this->renderError('当前状态不允许重发(仅「发放失败」的订单可重发)');
}
(new QueueService())->push('partners_queue', json_encode(['order_id' => $orderId], 320));
return $this->renderSuccess('已提交重新发放,请稍后刷新查看结果');
}
/**
* 分油回调
* 重推「发放结果」给下单平台
* @return Json
* @throws \think\db\exception\DbException
*/
public function oilCallback(): Json
{
/*
$model = new OrderModel;
$list = $model->getAll([
['partners_id', '=', 4],
['oil_status', '=', 1],
['oil_callback', '=', 0],
])->toArray();
$queueService = new QueueService();
foreach ($list as $order){
// 存入队列处理
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'type' => 'add',
'optType' => 'share_oil',
'retry_count' => 0,
], 320));
$orderId = (int) $this->request->param('orderId');
$order = OrderModel::detail($orderId);
if (!$order) {
return $this->renderError('订单不存在');
}
return $this->renderSuccess('回调成功');*/
$postData = $this->request->param();
$postData['order_id'] = $this->request->param('orderId');
$order = OrderModel::detail($postData['order_id']);
if($order['oil_status'] == 0){
return $this->renderError('未分油');
}else {
// 存入队列处理
$queueService = new QueueService();
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'type' => 'add',
'optType' => 'share_oil',
'retry_count' => 0,
], 320));
return $this->renderSuccess('回调成功');
if ($order['oil_status'] != 1) {
return $this->renderError('该单未发放成功,无可回调内容');
}
(new QueueService())->push('notify_queue', json_encode([
'order_id' => $orderId,
'type' => 'add',
'optType' => 'share_oil',
'retry_count' => 0,
], 320));
return $this->renderSuccess('已重新推送发放结果');
}
/**
* 使用回调
* 重推「加油核销」结果给下单平台
* @return Json
* @throws \think\db\exception\DbException
*/
public function useCallback(): Json
{
/*
$model = new OrderModel;
$list = $model->getAll([
['partners_id', '=', 4],
['use_status', '=', 1],
['use_callback', '=', 0],
])->toArray();
$queueService = new QueueService();
foreach ($list as $order){
// 存入队列处理
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'optType' => 'add_oil',
'retry_count' => 0,
], 320));
$orderId = (int) $this->request->param('orderId');
$order = OrderModel::detail($orderId);
if (!$order) {
return $this->renderError('订单不存在');
}
return $this->renderSuccess('回调成功');*/
$postData = $this->request->param();
$postData['order_id'] = $this->request->param('orderId');
$order = OrderModel::detail($postData['order_id']);
if($order['use_status'] == 0){
return $this->renderError('未使用');
}else {
// 通知对接公司
$queueService = new QueueService();
$queueService->push('notify_queue', json_encode([
'order_id' => $order['order_id'],
'optType' => 'add_oil',
'retry_count' => 0,
], 320));
return $this->renderSuccess('回调成功');
if ($order['use_status'] != 1) {
return $this->renderError('该单未加油核销,无可回调内容');
}
(new QueueService())->push('notify_queue', json_encode([
'order_id' => $orderId,
'type' => 'sub',
'optType' => 'add_oil',
'retry_count' => 0,
], 320));
return $this->renderSuccess('已重新推送加油结果');
}
}
-14
View File
@@ -8,10 +8,8 @@ use think\Session;
use app\common\model\Partners as PartnersModel;
use app\store\model\Order as OrderModel;
use app\store\model\store\User as UserModel;
use app\store\model\PartnersGoods as PartnersGoodsModel;
use app\store\model\partners\BalanceLog as BalanceLogModel;
use app\store\model\partners\StockLog as StockLogModel;
use app\store\service\store\User as StoreUserService;
use app\common\enum\partners\balanceLog\Scene as SceneEnum;
use app\common\library\helper;
@@ -51,8 +49,6 @@ class Partners extends PartnersModel
$this->error = '请填写商户名称';
return false;
}
// 检查商品数据
$goodsList = PartnersGoodsModel::checkGoodsData($data['goodsList']);
$this->startTrans();
try {
$data['orgNo'] = $this->createNo();
@@ -75,10 +71,6 @@ class Partners extends PartnersModel
$userModel->save($newData);
// 新增角色关系记录
UserRole::increased((int)$userModel['store_user_id'], [10014]);
// 新增商品关系记录
PartnersGoodsModel::increased((int)$this['partners_id'], $goodsList);
// 新增合作商商品库存变动记录
StockLogModel::increased((int)$this['partners_id'], $goodsList);
$this->commit();
return true;
} catch (\Exception $e) {
@@ -146,16 +138,10 @@ class Partners extends PartnersModel
$this->error = '请填写商户名称';
return false;
}
// 检查商品数据
$goodsList = PartnersGoodsModel::checkGoodsData($data['goodsList']);
$this->startTrans();
try {
// 更新管理员记录
$this->save($data);
// 更新商品关系记录
PartnersGoodsModel::updates((int)$this['partners_id'], $goodsList);
// 新增合作商商品库存变动记录
StockLogModel::increased((int)$this['partners_id'], $goodsList);
$this->commit();
return true;
} catch (\Exception $e) {
@@ -1,85 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\store\model;
use app\common\library\helper;
use app\common\model\PartnersGoods as PartnersGoodsModel;
use cores\exception\BaseException;
/**
* 合作商商品关系模型
* Class PartnersGoods
* @package app\store\model
*/
class PartnersGoods extends PartnersGoodsModel
{
/**
* 验证商品是否合法
* @param array $goodsList
* @throws BaseException
*/
public static function checkGoodsData(array $goodsList)
{
$values = helper::getArrayColumn($goodsList, 'goods_id');
if (count($goodsList) != count(array_unique($values))) {
throwError('很抱歉,不能存在重复的商品 请检查您的选择');
}
foreach ($goodsList as &$item) {
if($item['allot_num'] < 0 && abs($item['allot_num']) > $item['stock_num']){
throwError('很抱歉,分配数量减少不能低于库存数量 请检查您的输入');
}elseif($item['allot_num'] > 100){
throwError('很抱歉,分配数量不能大于100 请检查您的输入');
}
$item['before_num'] = $item['stock_num'];
$item['stock_num'] += $item['allot_num'];
$item['after_num'] = $item['stock_num'];
$item['change_type'] = $item['allot_num'] < 0 ? 2 : 1;
}
unset($item);
return $goodsList;
}
/**
* 批量写入合作商与商品关系记录
* @param int $partnersId
* @param array $goodsList
* @return array|false
*/
public static function increased(int $partnersId, array $goodsList)
{
$dataset = [];
foreach ($goodsList as $item) {
$dataset[] = [
'partners_id' => $partnersId,
'goods_id' => $item['goods_id'],
'stock_num' => $item['stock_num'],
'store_id' => self::$storeId
];
}
return (new static)->addAll($dataset);
}
/**
* 批量更新合作商和商品关系记录
* @param int $partnersId
* @param array $goodsList
* @return array|false
*/
public static function updates(int $partnersId, array $goodsList)
{
// 删除所有的记录
static::deleteAll(['partners_id' => $partnersId]);
// 批量新增记录
return static::increased($partnersId, $goodsList);
}
}
+20 -11
View File
@@ -31,13 +31,13 @@ class Order extends OrderModel
// 设置查询条件
$filter = $this->getFilter($param);
// 先查询总金额
$totalPayPrice = $this->with(['goods.images.file', 'partners'])
$totalPayPrice = $this->with(['partners'])
->alias('order')
->field('order.*')
->where($filter)
->sum('order.total_pay_price');
// 获取列表数据
$list = $this->with(['goods.images.file', 'partners'])
$list = $this->with(['partners'])
->alias('order')
->field('order.*')
->where($filter)
@@ -60,23 +60,32 @@ class Order extends OrderModel
// 设置默认的检索数据
$params = $this->setQueryDefaultValue($param, [
'partners_id' => 0, // 合作商ID
'search' => '', // 用户昵称
'goods_no' => '', // 商品编码
'oil_status' => -1, // 分油状态
'use_status' => -1, // 使用状态
'search' => '', // 司机手机号
'driver_name' => '', // 司机姓名
'orderNo' => '', // 单号:油卡编号/下单平台单号/批次号/运单号
'oil_status' => -1, // 分油(发放)状态
'oil_status_zsy' => -1, // 企油通发放态 0未提交 2成功 3失败
'use_status' => -1, // 加油核销状态
'betweenTime' => [] // 起止时间
]);
// 检索查询条件
$filter = [];
// 合作商ID
$params['partners_id'] > 0 && $filter[] = ['order.partners_id', '=', $params['partners_id']];
// 用户昵称
// 司机手机号
!empty($params['search']) && $filter[] = ['order.mobile', 'like', "%{$params['search']}%"];
// 商品编码
!empty($params['goods_no']) && $filter[] = ['order.goods_no', 'like', "%{$params['goods_no']}%"];
// 分油状态
// 司机姓名
!empty($params['driver_name']) && $filter[] = ['order.driver_name', 'like', "%{$params['driver_name']}%"];
// 单号模糊检索(油卡编号 / 下单平台销售单号 / 企油通批次号 / 运单号)
if (!empty($params['orderNo'])) {
$kw = $params['orderNo'];
$filter[] = ['order.oilCardNo|order.partners_order_no|order.batch_no|order.waybill_no', 'like', "%{$kw}%"];
}
// 分油(发放)状态
$params['oil_status'] > -1 && $filter[] = ['oil_status', '=', (int)$params['oil_status']];
// 使用状态
// 企油通发放态
$params['oil_status_zsy'] > -1 && $filter[] = ['oil_status_zsy', '=', (int)$params['oil_status_zsy']];
// 加油核销状态
$params['use_status'] > -1 && $filter[] = ['use_status', '=', (int)$params['use_status']];
// 起止时间
if (!empty($params['betweenTime'])) {
@@ -1,95 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\store\model\partners;
use app\common\model\partners\StockLog as StockLogModel;
/**
* 合作商商品库存变动明细模型
* Class StockLog
* @package app\store\model\partners
*/
class StockLog extends StockLogModel
{
/**
* 获取合作商商品库存变动明细列表
* @param array $param
* @return \think\Paginator
*/
public function getList(array $param = [])
{
// 设置查询条件
$filter = $this->getFilter($param);
// 获取列表数据
return $this->alias('log')
->field('log.*,partners.name')
->where($filter)
->join('partners', 'partners.partners_id = log.partners_id')
->order(['log.create_time' => 'desc'])
->paginate(15);
}
/**
* 设置查询条件
* @param array $param
* @return array
*/
private function getFilter(array $param): array
{
// 设置默认的检索数据
$params = $this->setQueryDefaultValue($param, [
'partners_id' => 0, // 合作商ID
'search' => '', // 用户昵称
'betweenTime' => [] // 起止时间
]);
// 检索查询条件
$filter = [];
// 用户ID
$params['partners_id'] > 0 && $filter[] = ['log.partners_id', '=', $params['partners_id']];
// 用户昵称
!empty($params['search']) && $filter[] = ['partners.name', 'like', "%{$params['search']}%"];
// 起止时间
if (!empty($params['betweenTime'])) {
$times = between_time($params['betweenTime']);
$filter[] = ['log.create_time', '>=', $times['start_time']];
$filter[] = ['log.create_time', '<', $times['end_time'] + 86400];
}
return $filter;
}
/**
* 批量写入合作商与商品库存变动记录
* @param int $partnersId
* @param array $goodsList
* @return array|false
*/
public static function increased(int $partnersId, array $goodsList)
{
$dataset = [];
foreach ($goodsList as $item) {
if(!empty($item['allot_num'])) {
$dataset[] = [
'partners_id' => $partnersId,
'goods_id' => $item['goods_id'],
'before_num' => $item['before_num'],
'after_num' => $item['after_num'],
'change_type' => $item['change_type'],
'change_num' => $item['allot_num'],
'store_id' => self::$storeId
];
}
}
return (new static)->addAll($dataset);
}
}
+2 -2
View File
@@ -7,9 +7,9 @@ $config = [
// 数据库名
'database' => 'oil',
// 用户名
'username' => 'root',
'username' => 'oil',
// 密码
'password' => 'qQ19961012',
'password' => 'AxSZEJDp5nePyfMa',
// 端口号
'hostport' => '3306',
// 数据库编码默认采用utf8
+75
View File
@@ -0,0 +1,75 @@
<?php
// +----------------------------------------------------------------------
// | 企油通对接配置(支付宝企业码通道)
// +----------------------------------------------------------------------
// | 本次仅对接 3 个接口:7.7 运单油费下发 / 7.15 H5 跳转地址 / 7.10 加油明细推送(异步回调)
// | 标 ★ 的值需向企油通对接人索取后填入;敏感值建议用 .env 覆盖,不提交版本库
// +----------------------------------------------------------------------
return [
// ============ 接口环境 ============
// ★ 接口域名(测试 / 正式不同),不含结尾斜杠,例如 https://xxx.worknoya.vip
'base_url' => env('qyt.base_url', ''),
// ★ 平台分配的 appId(业务接口用)
'app_id' => env('qyt.app_id', ''),
// ★ 我方(慧明)RSA 私钥:PEM 全文,或去头尾的 base64 单行均可
'private_key' => env('qyt.private_key', ''),
// ★ 企油通平台公钥:PEM 或 base64(验签企油通的响应 / 回调)
'platform_public_key' => env('qyt.platform_public_key', ''),
// 请求超时(秒)
'timeout' => 15,
// ============ 企业(单企业,线下已开户,固定支付宝企业码通道) ============
// ★ 营业执照编码 / 统一社会信用代码
'business_license' => env('qyt.business_license', ''),
// ★ 机构号:支付宝企业码通道 7.7 必填,待企油通系统分配
'institution_id' => env('qyt.institution_id', ''),
// ============ 编号 / 业务参数 ============
// 全平台唯一编号前缀(batch_no / waybill_no / waybill_no_code 加前缀防撞号)
// 建议:企业标识 + 环境标识,如 HM / HMTEST
'no_prefix' => env('qyt.no_prefix', 'HM'),
// 7.7 凭证名称(voucher_name)
'voucher_name' => '运单卡',
// ============ 回调 / H5 ============
// ★ 我方对外回调地址(配置到企油通后台,用于 7.10 加油消费推送)
// 形如 https://你的域名/qytconsume.php
'consume_notify_url' => env('qyt.consume_notify_url', ''),
// 7.15 H5 免登落地页地址(测试 / 正式不同)
// 测试:http://dev.paytaskpage.worknoya.vip/#/ 正式:https://paytaskpage.worknoya.vip/#/
'h5_base_url' => env('qyt.h5_base_url', 'https://paytaskpage.worknoya.vip/#/'),
// ★ 7.15 H5 免登使用的 appId(可用企业级通道 appId 或商户级 appId,线下确认)
'h5_app_id' => env('qyt.h5_app_id', ''),
// 7.15 是否把 creditcode(营业执照号)带入链接并入签(true 则本次登录固定到该企业)
'h5_with_creditcode' => true,
// ============ 杂项 ============
// 是否记录详细请求 / 响应日志到 runtime/storelog
'log' => (bool) env('qyt.log', true),
// ★ 业务接口拼签字段顺序 —— 待对接人确认(见 Qyt::bizSign 的 TODO)
'sign_mode' => env('qyt.sign_mode', 'ksort_md5_sha1rsa'),
// 7.7 重试幂等:企油通返回 message 命中以下任一关键字时,判定 batch_no 已存在 -> 按「已受理」处理
'dup_batch_hints' => ['已存在', '重复', 'duplicate', 'exist'],
];
+124
View File
@@ -0,0 +1,124 @@
-- ============================================================
-- 开发/联调环境 数据库初始化清理脚本
-- 生成:2026-09-06
-- 作用:清空业务与测试流水,保留系统运行所需的账号、权限、字典、设置
-- ⚠️ 执行前务必备份:mysqldump -uroot -p oil > backup.sql
-- ============================================================
SET FOREIGN_KEY_CHECKS = 0;
-- ------------------------------------------------------------
-- 1. 企油通 / 合作商 业务流水
-- 注意:yoshop_partners(下游接口账户:orgNo/appKey/RSA密钥/回调地址)予以保留
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_partners_order`;
TRUNCATE TABLE `yoshop_qyt_consume`;
TRUNCATE TABLE `yoshop_partners_balance_log`;
TRUNCATE TABLE `yoshop_partners_goods`;
TRUNCATE TABLE `yoshop_partners_stock_log`;
-- ------------------------------------------------------------
-- 2. 商城订单与售后
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_order`;
TRUNCATE TABLE `yoshop_order_goods`;
TRUNCATE TABLE `yoshop_order_address`;
TRUNCATE TABLE `yoshop_order_qrcode`;
TRUNCATE TABLE `yoshop_order_record`;
TRUNCATE TABLE `yoshop_order_refund`;
TRUNCATE TABLE `yoshop_order_refund_address`;
TRUNCATE TABLE `yoshop_order_refund_image`;
TRUNCATE TABLE `yoshop_order_export`;
-- ------------------------------------------------------------
-- 3. 接口请求日志 / 已停用模块(分油公司、分油用户池)
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_request_log`;
TRUNCATE TABLE `yoshop_account`;
TRUNCATE TABLE `yoshop_company`;
TRUNCATE TABLE `yoshop_company_balance_log`;
-- ------------------------------------------------------------
-- 4. 购物车 / 结算 / 兑换卡 / 充值
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_cart`;
TRUNCATE TABLE `yoshop_checkout`;
TRUNCATE TABLE `yoshop_card`;
TRUNCATE TABLE `yoshop_card_log`;
TRUNCATE TABLE `yoshop_card_print`;
TRUNCATE TABLE `yoshop_recharge_order`;
TRUNCATE TABLE `yoshop_recharge_order_plan`;
TRUNCATE TABLE `yoshop_recharge_plan`;
-- ------------------------------------------------------------
-- 5. 商户 / 代理商体系(支付聚合链路)
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_merchant`;
TRUNCATE TABLE `yoshop_merchant_balance_log`;
TRUNCATE TABLE `yoshop_merchant_order`;
-- ------------------------------------------------------------
-- 6. 小程序用户及其附属数据(保留 yoshop_user_grade 等级配置)
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_user`;
TRUNCATE TABLE `yoshop_user_oauth`;
TRUNCATE TABLE `yoshop_user_address`;
TRUNCATE TABLE `yoshop_user_balance_log`;
TRUNCATE TABLE `yoshop_user_coupon`;
TRUNCATE TABLE `yoshop_user_grade_log`;
TRUNCATE TABLE `yoshop_user_points_log`;
TRUNCATE TABLE `yoshop_user_commission_log`;
-- ------------------------------------------------------------
-- 7. 商城基础数据(商品 / 分类 / 优惠券 / 装修页 / 文件库)
-- 注意:图片物理文件仍在磁盘,仅清库记录
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_goods`;
TRUNCATE TABLE `yoshop_goods_sku`;
TRUNCATE TABLE `yoshop_goods_image`;
TRUNCATE TABLE `yoshop_goods_category_rel`;
TRUNCATE TABLE `yoshop_goods_service_rel`;
TRUNCATE TABLE `yoshop_goods_spec_rel`;
TRUNCATE TABLE `yoshop_goods_coupon`;
TRUNCATE TABLE `yoshop_category`;
TRUNCATE TABLE `yoshop_coupon`;
TRUNCATE TABLE `yoshop_page`;
TRUNCATE TABLE `yoshop_upload_file`;
-- ------------------------------------------------------------
-- 8. 其余业务表
-- ------------------------------------------------------------
TRUNCATE TABLE `yoshop_notify`;
TRUNCATE TABLE `yoshop_code`;
TRUNCATE TABLE `yoshop_comment`;
TRUNCATE TABLE `yoshop_comment_image`;
-- ------------------------------------------------------------
-- 9. 后台账号:只保留超级管理员 admin(store_user_id=10001)
-- ------------------------------------------------------------
DELETE FROM `yoshop_store_user` WHERE `store_user_id` <> 10001;
DELETE FROM `yoshop_store_user_role` WHERE `store_user_id` <> 10001;
-- ------------------------------------------------------------
-- 10. 恢复 yoshop 惯用的自增起始值(10001)
-- ------------------------------------------------------------
ALTER TABLE `yoshop_cart` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_category` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_coupon` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_goods` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_goods_category_rel` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_goods_image` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_goods_service_rel` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_goods_sku` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_merchant_order` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_order` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_order_goods` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_page` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_upload_file` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_user` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_user_address` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_user_balance_log` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_user_coupon` AUTO_INCREMENT = 10001;
ALTER TABLE `yoshop_user_oauth` AUTO_INCREMENT = 10001;
SET FOREIGN_KEY_CHECKS = 1;
+33
View File
@@ -0,0 +1,33 @@
; ============================================================
; 企油通配置 —— 追加到 server-tp6/.env 文件末尾
; ------------------------------------------------------------
; ⚠️ ThinkPHP 用 parse_ini_file(..., INI_SCANNER_RAW) 解析 .env:
; 值【不要加引号】,加了引号会连引号一起被读进去。
; ⚠️ RSA 密钥请去掉 -----BEGIN/END----- 头尾与所有换行,压成【一行 base64】。
; Qyt.php 会自动补回 PEM 头尾(见 loadPrivateKey / loadPublicKey)。
; ============================================================
[QYT]
; 接口域名,不含结尾斜杠(测试/正式不同,向企油通对接人索取)
BASE_URL = https://xxx.worknoya.vip
; 平台分配的 appId(业务接口用)
APP_ID =
; 我方 RSA 私钥(一行 base64,无头尾无换行)
PRIVATE_KEY =
; 企油通平台公钥(一行 base64,用于验签其响应/回调)
PLATFORM_PUBLIC_KEY =
; 我方企业营业执照编码 / 统一社会信用代码
BUSINESS_LICENSE =
; 机构号:支付宝企业码通道 7.7 必填,由企油通分配
INSTITUTION_ID =
; 全平台唯一编号前缀(batch_no / waybill_no / waybill_no_code 用,防与其他接入方撞号)
; 建议区分环境,如正式 HM、测试 HMTEST
NO_PREFIX = HM
; 7.15 H5 免登落地页地址(测试:http://dev.paytaskpage.worknoya.vip/#/)
H5_BASE_URL = https://paytaskpage.worknoya.vip/#/
; 7.15 H5 免登用的 appId(可能与业务 appId 不同,需线下确认)
H5_APP_ID =
; 我方 7.10 回调地址(此项仅作记录,实际需在企油通后台配置)
CONSUME_NOTIFY_URL = https://www.hmzkw.com/qytconsume.php
; 是否记录详细请求/响应日志到 runtime/storelog
LOG = true
+48
View File
@@ -0,0 +1,48 @@
SET NAMES utf8mb4;
DELETE FROM `yoshop_partners` WHERE `appKey` = 'qyttest0000000000000000000001';
INSERT INTO `yoshop_partners`
(`name`,`address`,`contact_name`,`contact_tel`,`orgNo`,`appKey`,`privateKey`,`publicKey`,`callback`,`balance`,`store_id`,`store_user_id`,`update_time`,`create_time`)
VALUES
('企油通联调测试商','测试地址','测试联系人','13800000000','S88888888','qyttest0000000000000000000001',
'-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDUOwdCCPSgPpR+
WsYXVz/2FQ35ybQWJK14ytDk4ITv+fQMEnMbQ8D+4cREXIydE/NOzgn+hDfHbFDf
91wti0P+1C+74wBA5xZlbhae8ZAB1hc5gmSJqE++LalfrmOIg5SDfQhtiaf4htEo
RdxEsupS964izLoXeUgIE9lY87JCqluU1thvbk601sPi2q8WnfIKN8vnSvu31YRr
86Hc4soyIdf1WZSoU4couE2QmOFm9jr55Obp+USfJg7Rjoce9ZOFj0lX3gkHqGOz
l2toxfzyZp+KqlhQLWYLmiLM3Z3G2Gz118MGjaan7zIW15Feuae1v3eNaqF7Zq6C
zDZckiwvAgMBAAECggEAWOOUSaMomkk7tqRcXYBMqMvuu5UwmSmpgnMoBQTIiR5e
igPcNCovllGG+77rogquGG6RnwlPt3X1dajL8cNnqj7C82XDQGvJZJg0X0P7tdku
hwbHByT4qUXEr++BfrblqCDhO8exE5Wb2stP03JAVJmqVKb8tkI02jU+DlSbb/HY
9G//rx1Wkbwmk1jYBu5pSuQWsQaZyijeb8JfLzGYkHyV6mrBFyFrWHoH7fmC3M6R
sLxdsj/KUIYyNHCTmRC7TG2MFQGyTRUb7Z73WgI4OzAD934sM93AMeDWtkp0Ar2r
lm5xjojrpi63IiKhR66+Tzws8bV94e99k3UcftxDQQKBgQDut5RE/srMWvv584oY
TKvVrJwm6C47QpfjY98F745cV7IYGF2ZXOcS7uavRjbI0ct3vKTJk3dfrQ9pqgIN
vrLIIxWFv+xgIzTtbpUPpd4Y6dUIZYRS2Di03WlJnlHvnq8iZbQ2cK6T3nLETVa5
sK/ab5vEIuCyEv5DF4ulmGtMYQKBgQDjmIsuRCuX7BhSSC6ZpRMPEDuTyczKAEgd
m4qzSM4YFkhVGD9LdLwbMndTZk1iNie1UkpN52zzidx/ZTUfcewJem/C6re9cMM/
xytUWz89TUYerkXpPx8K7CG3KG9v64rx3j8/pvsY82oBnJu3n0Pb0sbfpv0G0Cax
GMzv+GHCjwKBgBPHVhuZWMgxyareCNUWlS4xDQcUObBjRh5rCfF99bek9HaWnjq0
vdiNvsNK+AMPjuIu2SByfXOJ7Tr+AQmOgVWfworAah9VC2+Yt/UB8zLxPNtjPeF3
VMt3hN+6pGNMLIqXDqVsFKrfxXaKSeM8xnU8xhUsJp3agQKiEsn3pPxBAoGAJ0Gg
gBJyEno+l7KrlwTKDMcX/53HHPB/f9vcSKA+LBlr0IGmKTWxXF+QBWqX3mDI6JWR
C27nc/pLdg3W492agOgFbvFdT+erRczsxFgYyFITzibxGBIzIIcqKZVn4z4VcTKM
ZkY++UbC+hQxJ7Lo2Xm5o8oByrMMeW91qc6FzjECgYAwQX10LEqlWJDogvZP3MN9
AUrlZUGBYnjMu8vbiFY9n7DW+2gzbhL/AqzkXSgjzee+r/71aolT2gGvXPO7S290
9Ep8SF9gz1yVaYMlyj0hedF7jNkVGBRWRo0BtUqaFhthOsiD0M+/bYq0D5YCkCTZ
QOwUIF9JWN+l5JNRws+jGA==
-----END PRIVATE KEY-----
',
'-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1DsHQgj0oD6UflrGF1c/
9hUN+cm0FiSteMrQ5OCE7/n0DBJzG0PA/uHERFyMnRPzTs4J/oQ3x2xQ3/dcLYtD
/tQvu+MAQOcWZW4WnvGQAdYXOYJkiahPvi2pX65jiIOUg30IbYmn+IbRKEXcRLLq
UveuIsy6F3lICBPZWPOyQqpblNbYb25OtNbD4tqvFp3yCjfL50r7t9WEa/Oh3OLK
MiHX9VmUqFOHKLhNkJjhZvY6+eTm6flEnyYO0Y6HHvWThY9JV94JB6hjs5draMX8
8mafiqpYUC1mC5oizN2dxths9dfDBo2mp+8yFteRXrmntb93jWqhe2augsw2XJIs
LwIDAQAB
-----END PUBLIC KEY-----
',
'http://127.0.0.1/qyt_test.php?act=notify_receiver',
100000.00, 10001, 10001, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
SELECT partners_id, name, orgNo, appKey, balance, callback FROM `yoshop_partners`;
+67
View File
@@ -0,0 +1,67 @@
; ============================================================
; 企油通对接 · Supervisor 守护进程配置
; ------------------------------------------------------------
; 三个常驻队列消费者,缺一不可:
; partners:queue 下单 -> 企油通 7.7 运单油费下发(含丢单兜底巡检 reconcile)
; callback:queue 企油通 7.10 加油消费回调入队后的落库与状态更新
; notify:queue 我方 -> 下单平台 结果回调(含 60s/5min/30min 退避重试)
;
; 【使用前必改】
; 1) command 里的 PHP 路径:宝塔用 `ls /www/server/php/` 看版本,
; 如 PHP 7.4 -> /www/server/php/74/bin/php
; 2) directory:站点根目录(think 文件所在目录,不是 public)
; 3) user:宝塔站点默认运行用户一般是 www
; 4) stdout_logfile 目录需先存在:mkdir -p /www/wwwlogs/supervisor
;
; 【进程数说明】
; partners:queue 可多进程(同一订单由 RedisLock 串行,安全)
; callback:queue / notify:queue 建议保持单进程
; ============================================================
[program:qyt-partners-queue]
command=/www/server/php/74/bin/php think partners:queue
directory=/www/wwwroot/www.hmzkw.com
user=www
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
startsecs=3
startretries=99
stopwaitsecs=10
redirect_stderr=true
stdout_logfile=/www/wwwlogs/supervisor/qyt-partners-queue.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
[program:qyt-callback-queue]
command=/www/server/php/74/bin/php think callback:queue
directory=/www/wwwroot/www.hmzkw.com
user=www
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
startsecs=3
startretries=99
stopwaitsecs=10
redirect_stderr=true
stdout_logfile=/www/wwwlogs/supervisor/qyt-callback-queue.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
[program:qyt-notify-queue]
command=/www/server/php/74/bin/php think notify:queue
directory=/www/wwwroot/www.hmzkw.com
user=www
numprocs=1
process_name=%(program_name)s_%(process_num)02d
autostart=true
autorestart=true
startsecs=3
startretries=99
stopwaitsecs=10
redirect_stderr=true
stdout_logfile=/www/wwwlogs/supervisor/qyt-notify-queue.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
-10
View File
@@ -1,10 +0,0 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
</IfModule>
View File
+432
View File
@@ -0,0 +1,432 @@
<?php
/**
* ============================================================
* 慧明宝入站接口 · 联调测试脚本(模拟「下单平台」)
* ------------------------------------------------------------
* 放在 public/ 下,浏览器访问:http://你的域名/qyt_test.php
* 也可命令行:php qyt_test.php share
*
* 它做四件事:
* 1) 从数据库读取合作商(appKey / RSA 私钥 / orgNo)
* 2) 按「慧明宝」规范组装 RSA+AES 信封并调用我方入站接口
* 3) 展示明文请求 / 待签串 / 密文报文 / 服务端响应
* 4) act=notify_receiver 作为回调接收端,解密+验签我方推送的
* syb.notify.oilCard.change,并返回 {"code":"00"}
*
* ⚠️ 仅用于联调,正式环境请删除本文件。
* ============================================================
*/
const TEST_APPKEY = 'qyttest0000000000000000000001'; // 测试合作商 appKey
const API_VERSION = '1.0.0';
date_default_timezone_set('Asia/Shanghai');
$ROOT = dirname(__DIR__);
// ------------------------------------------------------------
// 基础工具
// ------------------------------------------------------------
function envConf($root)
{
$file = $root . '/.env';
if (!is_file($file)) {
throw new Exception('.env 不存在:' . $file);
}
$ini = parse_ini_file($file, true, INI_SCANNER_RAW) ?: [];
$db = isset($ini['DATABASE']) ? $ini['DATABASE'] : [];
return [
'host' => isset($db['HOSTNAME']) ? $db['HOSTNAME'] : '127.0.0.1',
'port' => isset($db['HOSTPORT']) ? $db['HOSTPORT'] : '3306',
'name' => isset($db['DATABASE']) ? $db['DATABASE'] : 'oil',
'user' => isset($db['USERNAME']) ? $db['USERNAME'] : 'root',
'pass' => isset($db['PASSWORD']) ? $db['PASSWORD'] : '',
'char' => isset($db['CHARSET']) ? $db['CHARSET'] : 'utf8',
];
}
function db($root)
{
static $pdo = null;
if ($pdo === null) {
$c = envConf($root);
$dsn = 'mysql:host=' . $c['host'] . ';port=' . $c['port'] . ';dbname=' . $c['name'] . ';charset=' . $c['char'];
$pdo = new PDO($dsn, $c['user'], $c['pass'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
}
/** 读取合作商(下单平台账户) */
function getPartner($root, $appKey)
{
$st = db($root)->prepare('SELECT * FROM yoshop_partners WHERE appKey = ? LIMIT 1');
$st->execute([$appKey]);
$row = $st->fetch();
if (!$row) {
throw new Exception('未找到合作商,appKey=' . $appKey . '(请先执行 deploy/seed_test_partner.sql)');
}
return $row;
}
/** 平台公钥:加密 AES 密钥 / 验签我方回调 */
function platformPublicKey($root)
{
$p = $root . '/rsa/public_key.pem';
if (!is_file($p)) {
throw new Exception('平台公钥不存在:' . $p);
}
return file_get_contents($p);
}
function uniqueRequestId($appKey)
{
return $appKey . strtoupper(bin2hex(random_bytes(12)));
}
/**
* 组装请求信封(口径对齐 app\common\crypto\RsaUtil::processResponse)
* 待签串:http_build_query([version, appKey, method, requestId, body])
* body :json_encode($body, JSON_UNESCAPED_UNICODE)
* 签名 :SHA256withRSA,用【合作商私钥】
* AES :AES-256-ECB,随机 32 字节密钥;密钥用【平台公钥】RSA 加密
*/
function buildEnvelope($partner, $platformPub, $method, array $body)
{
$bodyJson = json_encode($body, JSON_UNESCAPED_UNICODE);
$requestId = uniqueRequestId($partner['appKey']);
$signData = http_build_query([
'version' => API_VERSION,
'appKey' => $partner['appKey'],
'method' => $method,
'requestId' => $requestId,
'body' => $bodyJson,
]);
$pkey = openssl_pkey_get_private($partner['privateKey']);
if ($pkey === false) {
throw new Exception('合作商私钥无效:' . openssl_error_string());
}
$signature = '';
openssl_sign($signData, $signature, $pkey, OPENSSL_ALGO_SHA256);
// AES-256-ECB 加密 body(与服务端 encryptData/decryptData 完全一致)
$aesKey = openssl_random_pseudo_bytes(32);
$encBody = base64_encode(openssl_encrypt($bodyJson, 'AES-256-ECB', $aesKey, OPENSSL_PKCS1_PADDING));
// 平台公钥加密 AES 密钥
$pub = openssl_pkey_get_public($platformPub);
if ($pub === false) {
throw new Exception('平台公钥无效:' . openssl_error_string());
}
$encKey = '';
openssl_public_encrypt($aesKey, $encKey, $pub, OPENSSL_PKCS1_PADDING);
return [
'headers' => [
'version' => API_VERSION,
'appKey' => $partner['appKey'],
'method' => $method,
'requestId' => $requestId,
'sign' => base64_encode($signature),
],
'body' => ['key' => base64_encode($encKey), 'data' => $encBody],
'plain' => $body,
'signData' => $signData,
];
}
function httpPost($url, array $headers, array $jsonBody)
{
$h = ['Content-Type: application/json', 'Charset: UTF-8'];
foreach ($headers as $k => $v) {
$h[] = $k . ': ' . $v;
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($jsonBody, JSON_UNESCAPED_UNICODE),
CURLOPT_HTTPHEADER => $h,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
]);
$resp = curl_exec($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['http' => $code, 'body' => $resp, 'error' => $err];
}
/** 入站接口地址:public/partners.php/<控制器>/<方法> */
function apiUrl($path)
{
if (!empty($_GET['api'])) {
return rtrim($_GET['api'], '/') . '/partners.php' . $path;
}
if (PHP_SAPI === 'cli') {
return 'http://127.0.0.1/partners.php' . $path;
}
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
return $scheme . '://' . $_SERVER['HTTP_HOST'] . '/partners.php' . $path;
}
// ------------------------------------------------------------
// 回调接收端(模拟下单平台的 callback 地址)
// 我方 NotifyQueue 用 RsaUtil::prepareRequest 推送,注意它的待签串是
// 「原样拼接」,与入站的 http_build_query 不同。
// ------------------------------------------------------------
function notifyReceiver($root)
{
$raw = file_get_contents('php://input');
$headers = [];
foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) {
$headers[strtolower(str_replace('_', '', substr($k, 5)))] = $v;
}
}
$log = $root . '/runtime/qyt_test_notify.log';
@file_put_contents($log, date('Y-m-d H:i:s') . " 收到回调\nheaders=" . json_encode($headers, 320) . "\nbody=" . $raw . "\n", FILE_APPEND);
try {
$data = json_decode($raw, true);
$appKey = isset($headers['appkey']) ? $headers['appkey'] : TEST_APPKEY;
$partner = getPartner($root, $appKey);
// 1) 合作商私钥解出 AES 密钥
$pkey = openssl_pkey_get_private($partner['privateKey']);
$aesKey = '';
openssl_private_decrypt(base64_decode($data['key']), $aesKey, $pkey, OPENSSL_PKCS1_PADDING);
// 2) 解密 body
$plain = openssl_decrypt(base64_decode($data['data']), 'AES-256-ECB', $aesKey, OPENSSL_PKCS1_PADDING);
$body = json_decode($plain, true);
// 3) 平台公钥验签
$signData = 'version=' . API_VERSION
. '&appKey=' . $partner['appKey']
. '&method=' . (isset($headers['method']) ? $headers['method'] : '')
. '&requestId=' . (isset($headers['requestid']) ? $headers['requestid'] : '')
. '&body=' . json_encode($body);
$sign = isset($headers['sign']) ? $headers['sign'] : '';
$ok = openssl_verify($signData, base64_decode($sign), openssl_pkey_get_public(platformPublicKey($root)), OPENSSL_ALGO_SHA256);
@file_put_contents($log, '解密结果=' . json_encode($body, 320) . ' 验签=' . ($ok === 1 ? 'OK' : 'FAIL') . "\n\n", FILE_APPEND);
header('Content-Type: application/json');
echo json_encode(['code' => '00', 'message' => '请求成功'], JSON_UNESCAPED_UNICODE);
} catch (Throwable $e) {
@file_put_contents($log, '异常:' . $e->getMessage() . "\n\n", FILE_APPEND);
header('Content-Type: application/json');
echo json_encode(['code' => '99', 'message' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
}
exit;
}
// ------------------------------------------------------------
// 各接口默认入参
// ------------------------------------------------------------
function defaultBody($act, $orgNo)
{
$saleOrderNo = 'TEST' . date('YmdHis') . mt_rand(100, 999);
$oilCardNo = isset($_GET['oilCardNo']) ? $_GET['oilCardNo'] : '';
$mobile = isset($_GET['mobile']) ? $_GET['mobile'] : '13800000001';
if ($act === 'share') {
return [
'method' => 'syb.oil.oilCard.share',
'path' => '/oilCard/share',
'body' => [
'orgNo' => $orgNo,
'amount' => isset($_GET['amount']) ? $_GET['amount'] : '100.00',
'mobile' => $mobile,
'name' => isset($_GET['name']) ? $_GET['name'] : '张三',
'saleOrderNo' => $saleOrderNo,
'oilCardType' => 'zsy_ticket',
],
];
}
if ($act === 'qrcode') {
return [
'method' => 'syb.pay.qrCode.request',
'path' => '/oilCard/getPayQrCode',
'body' => ['orgNo' => $orgNo, 'oilCardNo' => $oilCardNo, 'mobile' => $mobile],
];
}
if ($act === 'status') {
return [
'method' => 'syb.oil.oilCard.orderStatus',
'path' => '/oilCard/orderStatus',
'body' => ['orgNo' => $orgNo, 'oilCardNo' => $oilCardNo],
];
}
if ($act === 'sharelist') {
return [
'method' => 'syb.oil.oilCard.shareList',
'path' => '/oilCard/shareList',
'body' => [
'orgNo' => $orgNo,
'mobile' => $mobile,
'beginDate' => date('Ymd', strtotime('-7 day')),
'endDate' => date('Ymd'),
'pageLimit' => '10',
'pageNum' => '1',
],
];
}
return null;
}
// ------------------------------------------------------------
// 主流程
// ------------------------------------------------------------
$act = isset($_GET['act']) ? $_GET['act'] : (isset($argv[1]) ? $argv[1] : '');
$fatal = '';
$result = null;
$partner = null;
try {
if ($act === 'notify_receiver') {
notifyReceiver($ROOT);
}
$partner = getPartner($ROOT, TEST_APPKEY);
$platformPub = platformPublicKey($ROOT);
if (in_array($act, ['share', 'qrcode', 'status', 'sharelist'], true)) {
$def = defaultBody($act, $partner['orgNo']);
$env = buildEnvelope($partner, $platformPub, $def['method'], $def['body']);
$url = apiUrl($def['path']);
$rsp = httpPost($url, $env['headers'], $env['body']);
$result = ['def' => $def, 'env' => $env, 'url' => $url, 'rsp' => $rsp];
}
} catch (Throwable $e) {
$fatal = $e->getMessage();
}
// ---------- 命令行输出 ----------
if (PHP_SAPI === 'cli') {
if ($fatal !== '') {
echo '错误:' . $fatal . "\n";
exit(1);
}
if (!$result) {
echo "用法:php qyt_test.php [share|qrcode|status|sharelist]\n";
exit;
}
echo '接口:' . $result['def']['method'] . "\n";
echo 'URL :' . $result['url'] . "\n";
echo '明文:' . json_encode($result['env']['plain'], 320) . "\n";
echo 'HTTP:' . $result['rsp']['http'] . "\n";
echo '响应:' . $result['rsp']['body'] . "\n";
if ($result['rsp']['error'] !== '') {
echo 'cURL:' . $result['rsp']['error'] . "\n";
}
exit;
}
$rspArr = $result ? json_decode($result['rsp']['body'], true) : null;
$okFlag = is_array($rspArr) && isset($rspArr['code']) && $rspArr['code'] === '00';
$newOil = is_array($rspArr) && isset($rspArr['data']['oilCardNo']) ? $rspArr['data']['oilCardNo'] : '';
$curOil = isset($_GET['oilCardNo']) ? $_GET['oilCardNo'] : '';
?><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>慧明宝入站接口 · 联调测试</title>
<style>
body{font:14px/1.7 -apple-system,"Microsoft YaHei",sans-serif;margin:0;background:#f5f6f8;color:#222}
.wrap{max-width:1000px;margin:0 auto;padding:24px}
h1{font-size:20px;margin:0 0 4px}
.sub{color:#888;font-size:13px;margin-bottom:18px}
.card{background:#fff;border:1px solid #e6e8eb;border-radius:8px;padding:16px 18px;margin-bottom:16px}
.card h2{font-size:15px;margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid #eee}
.btns a{display:inline-block;margin:0 8px 8px 0;padding:7px 16px;background:#1890ff;color:#fff;
border-radius:4px;text-decoration:none;font-size:13px}
.btns a.g{background:#52c41a}.btns a.o{background:#faad14}.btns a.p{background:#722ed1}
table{border-collapse:collapse;width:100%;font-size:13px}
th,td{border:1px solid #eee;padding:6px 10px;text-align:left;vertical-align:top;word-break:break-all}
th{background:#fafafa;width:150px;font-weight:600;color:#555}
pre{background:#282c34;color:#abb2bf;padding:12px;border-radius:6px;overflow:auto;
font-size:12px;line-height:1.6;margin:0;white-space:pre-wrap;word-break:break-all}
.ok{color:#52c41a;font-weight:600}.err{color:#ff4d4f;font-weight:600}
.tip{background:#fffbe6;border:1px solid #ffe58f;padding:10px 14px;border-radius:6px;font-size:13px;margin-bottom:16px}
.lbl{margin:12px 0 6px;color:#555}
</style>
</head>
<body><div class="wrap">
<h1>慧明宝入站接口 · 联调测试</h1>
<div class="sub">模拟「下单平台」调用我方 API(RSA + AES 信封)。仅供联调,正式环境请删除本文件。</div>
<?php if ($fatal !== ''): ?>
<div class="card"><h2>初始化失败</h2><p class="err"><?php echo htmlspecialchars($fatal); ?></p></div>
<?php else: ?>
<div class="card">
<h2>当前合作商</h2>
<table>
<tr><th>partners_id</th><td><?php echo (int)$partner['partners_id']; ?></td></tr>
<tr><th>名称</th><td><?php echo htmlspecialchars($partner['name']); ?></td></tr>
<tr><th>orgNo</th><td><?php echo htmlspecialchars($partner['orgNo']); ?></td></tr>
<tr><th>appKey</th><td><?php echo htmlspecialchars($partner['appKey']); ?></td></tr>
<tr><th>余额</th><td><b><?php echo htmlspecialchars($partner['balance']); ?></b> 元 (下单会扣减,不足返回 2028)</td></tr>
<tr><th>回调地址</th><td><?php echo htmlspecialchars($partner['callback']); ?></td></tr>
</table>
</div>
<div class="tip">
接口地址默认取当前域名;如需指定,链接后加 <code>&amp;api=http://你的域名</code>。<br>
<b>下单前提</b>:三个队列进程已启动(partners:queue / callback:queue / notify:queue)、Redis 可用、合作商余额充足。
</div>
<div class="card">
<h2>发起测试</h2>
<div class="btns">
<a class="g" href="?act=share">① 下单分油 (share)</a>
<a class="o" href="?act=qrcode&amp;oilCardNo=<?php echo urlencode($curOil); ?>">② 获取加油链接 (qrCode)</a>
<a class="p" href="?act=status&amp;oilCardNo=<?php echo urlencode($curOil); ?>">③ 订单状态 (orderStatus)</a>
<a href="?act=sharelist">④ 分油记录 (shareList)</a>
</div>
<div class="sub" style="margin:6px 0 0">
②③ 需要 oilCardNo:先点①拿到后会自动带上。<br>
① 可自定义参数:<code>?act=share&amp;amount=50.00&amp;mobile=13800000002&amp;name=李四</code>
</div>
</div>
<?php if ($result): ?>
<div class="card">
<h2>请求 · <?php echo htmlspecialchars($result['def']['method']); ?></h2>
<table>
<tr><th>URL</th><td><?php echo htmlspecialchars($result['url']); ?></td></tr>
<tr><th>requestId</th><td><?php echo htmlspecialchars($result['env']['headers']['requestId']); ?></td></tr>
</table>
<p class="lbl">明文 body</p>
<pre><?php echo htmlspecialchars(json_encode($result['env']['plain'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); ?></pre>
<p class="lbl">待签串(http_build_query)</p>
<pre><?php echo htmlspecialchars($result['env']['signData']); ?></pre>
<p class="lbl">实际发送(密文信封)</p>
<pre><?php echo htmlspecialchars(json_encode($result['env']['body'], JSON_PRETTY_PRINT)); ?></pre>
</div>
<div class="card">
<h2>响应 <?php echo $okFlag ? '<span class="ok">成功</span>' : '<span class="err">失败</span>'; ?></h2>
<table>
<tr><th>HTTP 状态</th><td><?php echo (int)$result['rsp']['http']; ?></td></tr>
<?php if ($result['rsp']['error'] !== ''): ?>
<tr><th>cURL 错误</th><td class="err"><?php echo htmlspecialchars($result['rsp']['error']); ?></td></tr>
<?php endif; ?>
</table>
<p class="lbl">响应体</p>
<pre><?php echo htmlspecialchars(is_array($rspArr) ? json_encode($rspArr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $result['rsp']['body']); ?></pre>
<?php if ($newOil !== ''): ?>
<div class="btns" style="margin-top:14px">
<a class="o" href="?act=qrcode&amp;oilCardNo=<?php echo urlencode($newOil); ?>">用 <?php echo htmlspecialchars($newOil); ?> 取加油链接</a>
<a class="p" href="?act=status&amp;oilCardNo=<?php echo urlencode($newOil); ?>">查该单状态</a>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div></body></html>
+23
View File
@@ -0,0 +1,23 @@
<?php
// [ 企油通 7.10 加油消费回调入口 ]
namespace think;
// 检测PHP环境
if (version_compare(PHP_VERSION, '7.1.0', '<')) die('require PHP > 7.1.0 !');
// 加载核心文件
require __DIR__ . '/../vendor/autoload.php';
// 执行HTTP应用并响应
$http = (new App())->http;
// 手动指定路由
$_SERVER['PATH_INFO'] = '/qytNotify/consume';
// 绑定 api 应用
$response = $http->name('api')->run();
$response->send();
$http->end($response);
+3 -2
View File
@@ -5,6 +5,7 @@ window.publicConfig = {
// 例如: https://www.你的域名.com/index.php?s=/store
// BASE_API: '../index.php?s=/store'
// BASE_API: 'http://139.129.21.253/index.php?s=/store'
BASE_API: 'https://www.hmzkw.com/index.php?s=/store',
BASE_DOMAIN: 'https://www.hmzkw.com/'
// BASE_DOMAIN: 'http://139.129.21.253/'
BASE_API: 'http://47.104.73.7/index.php?s=/store',
BASE_DOMAIN: 'http:/47.104.73.7/'
}
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
.ant-modal-body[data-v-547be636]{padding-top:0!important}.ant-form-item[data-v-547be636]{margin-bottom:15px}.ant-tabs-nav .ant-tabs-tab[data-v-547be636]{padding:10px 16px}.ant-card-body[data-v-c8e4e012]{padding:22px 29px 25px}.tab-list[data-v-c8e4e012]{margin-right:20px}.ant-card-body[data-v-78d16cc2]{padding:22px 29px 25px}.tab-list[data-v-78d16cc2]{margin-right:20px}
@@ -0,0 +1 @@
.platform-icon{font-size:16px}.platform-icon .icon{margin-right:5px}.platform-icon .icon:last-child{margin-right:0}.platform-icon .H5-WEIXIN,.platform-icon .MP-WEIXIN{color:#04be02}.platform-icon .H5{color:#e44c27}.user-info[data-v-3d0bc671]{width:160px;height:55px;line-height:1.3}.user-info .in-left[data-v-3d0bc671]{float:left;margin-right:10px}.user-info .in-left img[data-v-3d0bc671]{width:40px;height:40px;border-radius:50%}.user-info .in-right[data-v-3d0bc671]{float:left;width:100px;height:100%}.user-info .in-right .user-name[data-v-3d0bc671]{margin-bottom:2px}.goods-info[data-v-6d71ff35]{width:270px;line-height:1.3;white-space:normal}.goods-info .in-left[data-v-6d71ff35]{float:left;margin-right:8px}.goods-info .in-left img[data-v-6d71ff35]{width:60px;height:60px}.goods-info .in-right[data-v-6d71ff35]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;float:left;height:60px}.goods-info .in-right .title[data-v-6d71ff35]{font-size:13px;margin-bottom:5px}.goods-info .in-right .subtitle[data-v-6d71ff35]{font-size:12px;color:#7b7b7b}.goods-info .goods-props[data-v-6d71ff35]{color:#8a8a8a;font-size:12px;overflow:hidden}.goods-info .goods-props-item[data-v-6d71ff35]{display:inline-block;margin-right:4px}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
.ant-form-item[data-v-1e3b16a8]{margin-bottom:15px}[data-v-1e3b16a8] .ant-form-item-control{padding-left:10px}[data-v-1e3b16a8] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-1e3b16a8]{margin-top:50px!important}.ant-form-item[data-v-42865d94]{margin-bottom:15px}[data-v-42865d94] .ant-form-item-control{padding-left:10px}[data-v-42865d94] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-42865d94]{margin-top:60px!important}.ant-form-item[data-v-0d8ee9ca]{margin-bottom:24px}[data-v-0d8ee9ca] .ant-form-item-control{padding-left:10px}[data-v-0d8ee9ca] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-0d8ee9ca]{margin-top:60px!important}
@@ -0,0 +1 @@
.ant-card-body[data-v-650748d8],.ant-card-body[data-v-46700646]{padding:22px 29px 25px}.flex-box[data-v-46700646]{display:-webkit-box;display:-ms-flexbox;display:flex}.button-group[data-v-46700646]{margin-right:10px}.form-total[data-v-46700646]{color:#1890ff;margin-right:10px;font-size:20px;font-weight:500}.ant-card-body[data-v-1b1d1568]{padding:22px 29px 25px}.tab-list[data-v-1b1d1568]{margin-right:20px}.ant-card-body[data-v-614fa1f7],.ant-card-body[data-v-669bb9b6],.ant-card-body[data-v-2466693d],.ant-card-body[data-v-e7f9db82]{padding:22px 29px 25px}.tab-list[data-v-614fa1f7]{margin-right:20px}.ant-card-body[data-v-58bb637f]{padding:22px 29px 25px}.tab-list[data-v-58bb637f]{margin-right:20px}.preview-box[data-v-58bb637f]{width:120px;height:80px;line-height:80px}.preview-box img[data-v-58bb637f]{display:block;max-width:100%;max-height:100%}[data-v-74b71be6] .ant-form-item-control{padding-left:10px}[data-v-74b71be6] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-74b71be6]{margin-top:50px!important}.table-rules .operation{text-align:right}.table-rules .operation a{font-size:13px;margin-left:6px}.table-rules .content{color:#505050;white-space:normal}.table-rules .content .city-name{font-size:12.5px;color:#7b7b7b}.ant-checkbox-wrapper+.ant-checkbox-wrapper[data-v-4c243eb9]{margin-left:0}.ant-checkbox-wrapper[data-v-4c243eb9]{margin-right:14px;margin-bottom:18px}[data-v-4c243eb9] .ant-form-item-control{padding-left:10px}[data-v-4c243eb9] .ant-form-item-control .ant-form-item-control{padding-left:0}
@@ -0,0 +1 @@
[data-v-6bcd977c]{margin:0;padding:0;-webkit-tap-highlight-color:transparent}[data-v-6bcd977c],[data-v-6bcd977c]:after,[data-v-6bcd977c]:before{-webkit-box-sizing:border-box;box-sizing:border-box}address[data-v-6bcd977c],applet[data-v-6bcd977c],area[data-v-6bcd977c],article[data-v-6bcd977c],aside[data-v-6bcd977c],blockquote[data-v-6bcd977c],fieldset[data-v-6bcd977c],figcaption[data-v-6bcd977c],figure[data-v-6bcd977c],footer[data-v-6bcd977c],header[data-v-6bcd977c],map[data-v-6bcd977c],section[data-v-6bcd977c],summary[data-v-6bcd977c]{display:block}body[data-v-6bcd977c]{font:12px/1.5 Microsoft YaHei,tahoma,arial,Hiragino Sans GB,\\5b8b\4f53,sans-serif;background:#fff}.container[data-v-6bcd977c]{text-align:center;max-width:640px}.img[data-v-6bcd977c]{width:60px;vertical-align:middle}.box-title[data-v-6bcd977c]{line-height:30px}.box-p[data-v-6bcd977c]{line-height:35px;text-align:left;height:40px}.text-val[data-v-6bcd977c]{display:inline-block;width:240px;border:1px solid grey;height:29px;line-height:25px;padding:0 5px;background-color:#ededed}.copy[data-v-6bcd977c]{margin-left:10px;color:#000;font-size:13px;border:1px solid;padding:5px;background-color:#e9e9e9}.sure-but[data-v-6bcd977c]{display:inline-block;width:100%;height:45px;line-height:45px;background-color:#4caf50;color:#fff;text-align:center;margin-bottom:60px;margin-top:20px;border-radius:5px;font-size:21px}.text-input[data-v-6bcd977c]{display:inline-block;width:176px;border:1px solid grey;height:29px;line-height:25px;padding:0 5px}.tick-text[data-v-6bcd977c]{line-height:30px}.tick-text-no[data-v-6bcd977c]{height:60px;line-height:60px;font-weight:600}.explain[data-v-6bcd977c]{color:red;font-weight:600;width:90%;margin:0 auto;text-align:left;font-size:16px}.explain-text[data-v-6bcd977c]{text-align:left;width:90%;margin:0 auto;height:36px}.explain-blue[data-v-6bcd977c]{color:#fff;font-weight:unset;width:80%;font-size:19px;background-color:#40a9ff;border-radius:5px}.orange[data-v-6bcd977c]{color:#ff5200;font-weight:600}.text-box-top[data-v-6bcd977c]{width:90%;margin:0 auto}.red[data-v-6bcd977c]{color:red}.title[data-v-6bcd977c]{height:75px;line-height:75px}.title-text[data-v-6bcd977c]{font-size:40px;vertical-align:middle;margin-left:15px}.money[data-v-6bcd977c]{font-size:40px;color:#ff5200;margin-right:8px}.qrcode[data-v-6bcd977c]{width:70%}.qrcode-div[data-v-6bcd977c]{border-top:1px solid #ebebeb;border-bottom:1px solid #ebebeb;margin:20px 0 20px 0}.black[data-v-6bcd977c]{color:#000!important;font-size:13px}.center[data-v-6bcd977c]{text-align:center;margin:15px auto!important}.min[data-v-6bcd977c],.sec[data-v-6bcd977c]{color:red}.ali-img[data-v-6bcd977c]{width:170px;vertical-align:middle}.ali-explain[data-v-6bcd977c]{font-weight:600;width:90%;margin:0 auto;font-size:15px}.ali-title[data-v-6bcd977c]{height:125px;line-height:125px}.ali-a[data-v-6bcd977c]{color:#fff;display:inline-block;width:100%}.ali-time[data-v-6bcd977c]{color:#000;font-size:14px}.ali-explain-trip[data-v-6bcd977c]{margin-top:27%!important}.down img[data-v-6bcd977c]{width:270px!important}.box[data-v-6bcd977c]{margin-top:10%}.c-red[data-v-6bcd977c]{color:#c7171e}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
.card-title[data-v-0fc1832e]{font-size:14px;font-weight:700;margin-bottom:15px}.statistics[data-v-0fc1832e]{line-height:1}.statistics .ant-card .ant-card-body[data-v-0fc1832e]{height:100%;padding:30px 40px}.statistics .col-left[data-v-0fc1832e]{margin-right:25px;font-size:24px;color:#4799ff}.statistics .col-left .icon-body[data-v-0fc1832e]{width:58px;height:58px;border-radius:50%;background-color:#eff6ff}.statistics .col-right[data-v-0fc1832e]{margin-top:-5px}.statistics .col-right .keynote[data-v-0fc1832e]{font-size:26px}.pending[data-v-0fc1832e]{text-align:center;padding:15px 0}.pending .item[data-v-0fc1832e]{display:inline-block;padding:0 20px}.pending .item a[data-v-0fc1832e]{color:unset}.pending .item a[data-v-0fc1832e]:hover{color:#1890ff}.pending .name[data-v-0fc1832e]{font-weight:700;margin-bottom:10px;font-size:13.6px}.pending .value[data-v-0fc1832e]{font-weight:700;color:red;font-size:16px}.functions .item[data-v-0fc1832e]{display:block;padding:13px 20px;cursor:pointer}.functions .item[data-v-0fc1832e]:hover{color:#1890ff}.functions .name[data-v-0fc1832e]{font-size:13px;margin-left:10px}.functions .icon[data-v-0fc1832e]{width:28px;height:28px;color:#fff;font-size:13px;border-radius:5px}.overview a[data-v-0fc1832e]{color:unset}.overview a[data-v-0fc1832e]:hover{color:#1890ff}.overview .item[data-v-0fc1832e]{padding:13px 20px}.overview .col-left[data-v-0fc1832e]{color:#4799ff;font-size:28px;margin-right:35px}.overview .col-left .icon-body[data-v-0fc1832e]{width:58px;height:58px;border-radius:50%;background-color:#eff6ff}.overview .col-right .name[data-v-0fc1832e]{font-size:13px;color:#666}.overview .col-right .value[data-v-0fc1832e]{color:#000;font-size:24px}.overview .col-right .yesterday[data-v-0fc1832e]{display:none;font-size:12px;color:#999}
@@ -0,0 +1 @@
.ant-form-item .ant-form-item[data-v-0a4749e4],.ant-form-item .ant-form-item[data-v-65eaa220],.ant-form-item .ant-form-item[data-v-270e929b]{margin-bottom:0}[data-v-65eaa220] .ant-form-item-control{padding-left:10px}[data-v-65eaa220] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-form-item .ant-form-item[data-v-3a1c101e]{margin-bottom:0}[data-v-3a1c101e] .ant-form-item-control{padding-left:10px}[data-v-3a1c101e] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-form-item .ant-form-item[data-v-e0c5fd3e]{margin-bottom:0}[data-v-e0c5fd3e] .ant-form-item-control{padding-left:10px}[data-v-e0c5fd3e] .ant-form-item-control .ant-form-item-control{padding-left:0}
@@ -0,0 +1 @@
.pagination[data-v-57882a87]{margin-top:16px}.pagination .ant-pagination[data-v-57882a87]{float:right}.ant-table table[data-v-57882a87]{border:none;border-collapse:collapse}.ant-table .ant-table-thead>tr[data-v-57882a87]{border:1px solid #e8e8e8}.ant-table tr.order-empty[data-v-57882a87]{height:15px;border:1px solid #fff}.ant-table tr.order-empty td[data-v-57882a87]{padding:0;border-right:none;border-left:none;background:none!important}.ant-table-thead>tr>th[data-v-57882a87]{border-right:none;border-bottom:none;padding:12px 12px;font-weight:700}.ant-table-tbody>tr>td[data-v-57882a87]{border-right:1px solid #e8e8e8;border-left:1px solid #e8e8e8;padding:12px 12px}.form-total[data-v-57882a87]{color:#1890ff;margin-right:10px;font-size:20px;font-weight:500}
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
.header[data-v-644f46f3]{margin-bottom:40px;text-align:center}.header .logo[data-v-644f46f3]{height:50px;vertical-align:top;border-style:none;margin-bottom:20px}.header .title[data-v-644f46f3]{font-size:20px;color:#6c7293;font-family:Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;margin:0}.user-layout-login label[data-v-644f46f3]{font-size:14px}.user-layout-login[data-v-644f46f3] .login-input .ant-input{border:none;background:rgba(235,237,242,.4);border-radius:5px;font-size:14px}.user-layout-login[data-v-644f46f3] .ant-form-explain{margin-top:5px;color:rgba(0,0,0,.45)}.user-layout-login .login-button[data-v-644f46f3]{padding:0 15px;font-size:16px;height:40px;width:100%;margin-top:20px;-webkit-box-shadow:0 4px 16px 0 rgba(34,185,255,.15);box-shadow:0 4px 16px 0 rgba(34,185,255,.15)}.user-layout-login .login-button[data-v-644f46f3]:hover{-webkit-box-shadow:0 9px 16px 0 rgba(34,185,255,.25)!important;box-shadow:0 9px 16px 0 rgba(34,185,255,.25)!important}.user-layout-login .ant-alert-error[data-v-644f46f3]{margin-bottom:24px;background-color:#fffbfb}
@@ -0,0 +1 @@
[data-v-f551b524] .ant-form-item-control{padding-left:10px}[data-v-f551b524] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-f551b524]{margin-top:60px!important}.ant-input-number[data-v-f551b524]{width:160px}.input-text_right[data-v-f551b524]{margin-left:10px}.radio-item[data-v-f551b524]{margin-bottom:16px}[data-v-01d8132a] .ant-form-item-control{padding-left:10px}[data-v-01d8132a] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-01d8132a]{margin-top:60px!important}.ant-input-number[data-v-01d8132a]{width:160px}.input-text_right[data-v-01d8132a]{margin-left:10px}.radio-item[data-v-01d8132a]{margin-bottom:16px}[data-v-dd6519b2] .ant-form-item-control{padding-left:10px}[data-v-dd6519b2] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-form-item[data-v-12cd9802]{margin-bottom:10px}[data-v-12cd9802] .ant-form-item-control{padding-left:10px}[data-v-12cd9802] .ant-form-item-control .ant-form-item-control{padding-left:0}.ant-divider[data-v-12cd9802]{margin-top:50px!important}
@@ -0,0 +1 @@
[data-v-6bc7e79e] .ant-form-item-control{padding-left:10px}[data-v-6bc7e79e] .ant-form-item-control .ant-form-item-control{padding-left:0}
@@ -0,0 +1 @@
.ant-card-body[data-v-50d1f642]{padding:22px 29px 25px}.ant-form-item{margin-bottom:15px}.ant-modal-body[data-v-eba90c32]{padding-top:0!important}.ant-form-item[data-v-eba90c32]{margin-bottom:15px}.ant-tabs-nav .ant-tabs-tab[data-v-eba90c32]{padding:10px 16px}.ant-card-body[data-v-7d67caec]{padding:22px 29px 25px}
+1 -1
View File
@@ -1 +1 @@
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>商家管理中心</title><style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="css/card.cd6f5842.css" rel="prefetch"><link href="css/card~company~content~goods~manage~market~order~page~store~user.27053c45.css" rel="prefetch"><link href="css/card~company~content~goods~manage~market~page~store~user.cfde183d.css" rel="prefetch"><link href="css/client.6506b2fb.css" rel="prefetch"><link href="css/company.6f636102.css" rel="prefetch"><link href="css/content.697364a3.css" rel="prefetch"><link href="css/exception.009ecf1b.css" rel="prefetch"><link href="css/goods.b28ced63.css" rel="prefetch"><link href="css/index.6dd91373.css" rel="prefetch"><link href="css/market.b57c2023.css" rel="prefetch"><link href="css/order.a11cc175.css" rel="prefetch"><link href="css/page.ede54ce6.css" rel="prefetch"><link href="css/passport.72f81382.css" rel="prefetch"><link href="css/setting.9f0456cb.css" rel="prefetch"><link href="css/store.594f0437.css" rel="prefetch"><link href="css/user.c6b42a5d.css" rel="prefetch"><link href="js/card.30673901.js" rel="prefetch"><link href="js/card~company~content~goods~manage~market~order~page~store~user.fd33b8f3.js" rel="prefetch"><link href="js/card~company~content~goods~manage~market~page~store~user.c18e4132.js" rel="prefetch"><link href="js/client.7d864e43.js" rel="prefetch"><link href="js/company.e998455c.js" rel="prefetch"><link href="js/content.9cb26642.js" rel="prefetch"><link href="js/exception.d3b6b1e8.js" rel="prefetch"><link href="js/goods.7b837414.js" rel="prefetch"><link href="js/goods~order.d9a20aa1.js" rel="prefetch"><link href="js/index.aad2d5ff.js" rel="prefetch"><link href="js/lang-zh-CN.cdf43def.js" rel="prefetch"><link href="js/manage.5aeb0557.js" rel="prefetch"><link href="js/market.a2ecd67a.js" rel="prefetch"><link href="js/order.c97bad04.js" rel="prefetch"><link href="js/page.df9f19ad.js" rel="prefetch"><link href="js/passport.d55f3da4.js" rel="prefetch"><link href="js/setting.b630cfa1.js" rel="prefetch"><link href="js/store.c2940b18.js" rel="prefetch"><link href="js/user.9b5a3592.js" rel="prefetch"><link href="css/app.1c3e5c41.css" rel="preload" as="style"><link href="css/chunk-vendors.d2070efc.css" rel="preload" as="style"><link href="js/app.cb8e9a51.js" rel="preload" as="script"><link href="js/chunk-vendors.d8af00dc.js" rel="preload" as="script"><link href="css/chunk-vendors.d2070efc.css" rel="stylesheet"><link href="css/app.1c3e5c41.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div id="loading-mask"><div class="loading-wrapper"><span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span></div></div></div><script src="config.js"></script><script src="js/chunk-vendors.d8af00dc.js"></script><script src="js/app.cb8e9a51.js"></script></body></html>
<!DOCTYPE html><html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>商家管理中心</title><style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style><link href="css/card.83fb7f11.css" rel="prefetch"><link href="css/card~content~goods~manage~market~order~page~store~user.bd86e290.css" rel="prefetch"><link href="css/card~content~goods~manage~market~page~store~user.06d71c34.css" rel="prefetch"><link href="css/client.6fbe4ba8.css" rel="prefetch"><link href="css/content.c53a1699.css" rel="prefetch"><link href="css/exception.37940980.css" rel="prefetch"><link href="css/goods.f0b216a1.css" rel="prefetch"><link href="css/index.ff363719.css" rel="prefetch"><link href="css/market.cc3678af.css" rel="prefetch"><link href="css/order.6124dbc9.css" rel="prefetch"><link href="css/page.2d2d2056.css" rel="prefetch"><link href="css/passport.68f7ba8b.css" rel="prefetch"><link href="css/setting.cf99a4da.css" rel="prefetch"><link href="css/store.3d002fc4.css" rel="prefetch"><link href="css/user.21b3449e.css" rel="prefetch"><link href="js/card.c3d0e7b1.js" rel="prefetch"><link href="js/card~content~goods~manage~market~order~page~store~user.58f5f8c7.js" rel="prefetch"><link href="js/card~content~goods~manage~market~page~store~user.a13c007a.js" rel="prefetch"><link href="js/client.58c03cfb.js" rel="prefetch"><link href="js/content.fd60d4aa.js" rel="prefetch"><link href="js/exception.d3b6b1e8.js" rel="prefetch"><link href="js/goods.3499bb6d.js" rel="prefetch"><link href="js/goods~order.d9a20aa1.js" rel="prefetch"><link href="js/index.874a9e0d.js" rel="prefetch"><link href="js/lang-zh-CN.303b0ef5.js" rel="prefetch"><link href="js/manage.3485dc4a.js" rel="prefetch"><link href="js/market.db25da7c.js" rel="prefetch"><link href="js/order.bc3eac19.js" rel="prefetch"><link href="js/page.24dbd519.js" rel="prefetch"><link href="js/passport.4036de85.js" rel="prefetch"><link href="js/setting.6f66a0e7.js" rel="prefetch"><link href="js/store.e6d3d371.js" rel="prefetch"><link href="js/user.dde70d3c.js" rel="prefetch"><link href="css/app.ce663112.css" rel="preload" as="style"><link href="css/chunk-vendors.43940245.css" rel="preload" as="style"><link href="js/app.09bba8d4.js" rel="preload" as="script"><link href="js/chunk-vendors.d8af00dc.js" rel="preload" as="script"><link href="css/chunk-vendors.43940245.css" rel="stylesheet"><link href="css/app.ce663112.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div id="loading-mask"><div class="loading-wrapper"><span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span></div></div></div><script src="config.js"></script><script src="js/chunk-vendors.d8af00dc.js"></script><script src="js/app.09bba8d4.js"></script></body></html>
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["lang-zh-CN"],{2807:function(e,a,o){"use strict";o.r(a);var t=o("5530"),n=o("eb60"),r=n["a"],l=o("5c3a"),c=o.n(l),i={antLocale:r,momentName:"zh-cn",momentLocale:c.a},d={message:"-","menu.home":"主页","menu.dashboard":"仪表盘","menu.dashboard.analysis":"分析页","menu.dashboard.monitor":"监控页","menu.dashboard.workplace":"工作台"};a["default"]=Object(t["a"])(Object(t["a"])({},i),d)},eb60:function(e,a,o){"use strict";var t=o("3579"),n=o("41b2"),r=o.n(n),l={today:"今天",now:"此刻",backToToday:"返回今天",ok:"确定",timeSelect:"选择时间",dateSelect:"选择日期",weekSelect:"选择周",clear:"清除",month:"月",year:"年",previousMonth:"上个月 (翻页上键)",nextMonth:"下个月 (翻页下键)",monthSelect:"选择月份",yearSelect:"选择年份",decadeSelect:"选择年代",yearFormat:"YYYY年",dayFormat:"D日",dateFormat:"YYYY年M月D日",dateTimeFormat:"YYYY年M月D日 HH时mm分ss秒",previousYear:"上一年 (Control键加左方向键)",nextYear:"下一年 (Control键加右方向键)",previousDecade:"上一年代",nextDecade:"下一年代",previousCentury:"上一世纪",nextCentury:"下一世纪"},c={placeholder:"请选择时间"},i=c,d={lang:r()({placeholder:"请选择日期",rangePlaceholder:["开始日期","结束日期"]},l),timePickerLocale:r()({},i)};d.lang.ok="确 定";var m=d,s=m;a["a"]={locale:"zh-cn",Pagination:t["a"],DatePicker:m,TimePicker:i,Calendar:s,global:{placeholder:"请选择"},Table:{filterTitle:"筛选",filterConfirm:"确定",filterReset:"重置",selectAll:"全选当页",selectInvert:"反选当页",sortTitle:"排序",expand:"展开行",collapse:"关闭行"},Modal:{okText:"确定",cancelText:"取消",justOkText:"知道了"},Popconfirm:{cancelText:"取消",okText:"确定"},Transfer:{searchPlaceholder:"请输入搜索内容",itemUnit:"项",itemsUnit:"项"},Upload:{uploading:"文件上传中",removeFile:"删除文件",uploadError:"上传错误",previewFile:"预览文件",downloadFile:"下载文件"},Empty:{description:"暂无数据"},Icon:{icon:"图标"},Text:{edit:"编辑",copy:"复制",copied:"复制成功",expand:"展开"},PageHeader:{back:"返回"}}}}]);
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["passport"],{"2cbd":function(t,e,r){},"3a10":function(t,e,r){"use strict";r.d(e,"b",(function(){return o})),r.d(e,"c",(function(){return n})),r.d(e,"a",(function(){return c})),r.d(e,"d",(function(){return l}));var s=r("b775"),i={list:"/order/list",detail:"/order/detail",rebate:"/order/rebate"},a={detail:"/passport/detail",bindUid:"/passport/bindUid",clickNum:"/passport/clickNum"};function o(t){return Object(s["b"])({url:i.list,method:"get",params:t})}function n(t){return Object(s["b"])({url:a.detail,method:"get",params:t})}function c(t){return Object(s["b"])({url:a.clickNum,method:"get",params:t})}function l(t){return Object(s["b"])({url:i.rebate,method:"post",data:t})}},"9b19":function(t,e,r){t.exports=r.p+"assets/logo.a3adccf1.svg"},b011:function(t,e,r){"use strict";r("2cbd")},cedc:function(t,e,r){"use strict";r.r(e);var s=function(){var t=this,e=t._self._c;return e("div",{staticClass:"main"},[t._m(0),e("a-form",{ref:"formLogin",staticClass:"user-layout-login",attrs:{id:"formLogin",form:t.form},on:{submit:t.handleSubmit}},[t.isLoginError?e("a-alert",{attrs:{type:"error",showIcon:"",message:t.loginErrorMsg}}):t._e(),e("a-form-item",[e("a-input",{directives:[{name:"decorator",rawName:"v-decorator",value:["username",{rules:[{required:!0,message:"您还没有输入用户名"}],validateTrigger:"change"}],expression:"[\n 'username',\n {rules: [{ required: true, message: '您还没有输入用户名' }], validateTrigger: 'change'}\n ]"}],staticClass:"login-input",attrs:{size:"large",type:"text",placeholder:"请输入用户名"}},[e("a-icon",{style:{color:"rgba(0,0,0,.25)"},attrs:{slot:"prefix",type:"user"},slot:"prefix"})],1)],1),e("a-form-item",[e("a-input",{directives:[{name:"decorator",rawName:"v-decorator",value:["password",{rules:[{required:!0,message:"您还没有输入用户密码"}],validateTrigger:"blur"}],expression:"[\n 'password',\n {rules: [{ required: true, message: '您还没有输入用户密码' }], validateTrigger: 'blur'}\n ]"}],staticClass:"login-input",attrs:{size:"large",type:"password",autocomplete:"false",placeholder:"请输入用户密码"}},[e("a-icon",{style:{color:"rgba(0,0,0,.25)"},attrs:{slot:"prefix",type:"lock"},slot:"prefix"})],1)],1),e("a-form-item",{staticStyle:{"margin-top":"24px"}},[e("a-button",{staticClass:"login-button",attrs:{size:"large",type:"primary",htmlType:"submit",loading:t.state.loginBtn,disabled:t.state.loginBtn}},[t._v("确定")])],1)],1)],1)},i=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"header"},[e("div",{staticClass:"logo"},[e("img",{staticClass:"logo",attrs:{src:r("9b19"),alt:"logo"}})]),e("p",{staticClass:"title"},[t._v("商城系统登录")])])}],a=r("5530"),o=(r("d3b7"),r("2f62")),n=r("ca00"),c=(r("3a10"),{data:function(){return{isLoginError:!1,loginErrorMsg:"登录失败",form:this.$form.createForm(this),state:{loginBtn:!1}}},created:function(){},methods:Object(a["a"])(Object(a["a"])({},Object(o["b"])(["Login"])),{},{handleSubmit:function(t){var e=this;t.preventDefault();var r=this.form.validateFields,s=this.state,i=this.Login;s.loginBtn=!0,r(["username","password"],{force:!0},(function(t,r){if(t)setTimeout((function(){s.loginBtn=!1}),100);else{var o=Object(a["a"])({},r);i(o).then((function(t){return e.loginSuccess(t)})).catch((function(t){return e.loginFailed(t)})).finally((function(){s.loginBtn=!1}))}}))},loginSuccess:function(t){var e=this;this.isLoginError=!1,this.$message.success(t.message,1.5),setTimeout((function(){e.$router.push({path:"/"}),e.$notification.success({message:"欢迎",description:"".concat(Object(n["h"])(),",欢迎回来")})}),1e3)},loginFailed:function(t){this.isLoginError=!0,this.loginErrorMsg=t.message}})}),l=c,u=(r("b011"),r("2877")),d=Object(u["a"])(l,s,i,!1,null,"644f46f3",null);e["default"]=d.exports}}]);
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.

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