第一次更新,完成了初版调整
This commit is contained in:
@@ -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();
|
||||
// 验证当前客户端状态
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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'])]);
|
||||
|
||||
@@ -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=..×tamp=..&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.'】');
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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('上游库存同步已停用(企油通无油券库存概念)');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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('上游库存同步已停用,请直接在「合作商」中维护可下单额度');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -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('小程序油品链路已停用');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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('已重新推送加油结果');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user