319 lines
13 KiB
PHP
319 lines
13 KiB
PHP
<?php
|
||
// +----------------------------------------------------------------------
|
||
// | 后台 · 下单平台(合作商)订单管理
|
||
// +----------------------------------------------------------------------
|
||
// | 企油通改造后:
|
||
// | 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\common\service\PartnersBalance;
|
||
use app\common\service\QueueService;
|
||
use app\common\model\qyt\Enterprise as EnterpriseModel;
|
||
use app\common\service\QytInvoice;
|
||
use app\common\service\QytOil;
|
||
use app\common\service\QytRefund;
|
||
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 extends Controller
|
||
{
|
||
/**
|
||
* 订单列表
|
||
* @return mixed
|
||
*/
|
||
public function list()
|
||
{
|
||
$model = new OrderModel;
|
||
$list = $model->getList($this->request->param());
|
||
return $this->renderSuccess($list);
|
||
}
|
||
|
||
/**
|
||
* 订单详情(含企油通加油消费流水)
|
||
* @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
|
||
*/
|
||
public function qrCode(): Json
|
||
{
|
||
$orderId = (int) $this->request->param('orderId');
|
||
$order = OrderModel::detail($orderId);
|
||
if (!$order) {
|
||
return $this->renderError('订单不存在');
|
||
}
|
||
// 一单可以分多次加油:没加完的单继续给加油地址
|
||
if ((int) $order['refund_status'] === 2) {
|
||
return $this->renderError('该单已退款,不能再加油');
|
||
}
|
||
if ((float) $order['used_amount'] + 0.005 >= (float) $order['total_price']) {
|
||
return $this->renderError('该单金额已全部加油');
|
||
}
|
||
if ($order['oil_status'] != 1) {
|
||
return $this->renderError('该单尚未发放成功,无法获取加油地址');
|
||
}
|
||
// 免登链接要带本单企业的 creditcode(营业执照号),把登录固定到该企业
|
||
$enterprise = EnterpriseModel::detail((int) $order['qyt_enterprise_id']);
|
||
if (!$enterprise) {
|
||
return $this->renderError('本单绑定的企油通企业不存在,无法生成加油地址');
|
||
}
|
||
if ($enterprise['status'] !== 'approved') {
|
||
return $this->renderError('企业「' . $enterprise['company_name'] . '」尚未通过企油通审核,无法生成加油地址');
|
||
}
|
||
$h5Url = QytOil::forEnterprise($enterprise)->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], '获取成功');
|
||
}
|
||
|
||
/**
|
||
* 重新发放(把订单打回未提交态并重投队列,由 partners:queue 调用企油通 7.7)
|
||
* @return Json
|
||
*/
|
||
public function share(): Json
|
||
{
|
||
$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)」,避免与「发放中(1)」的单冲突。
|
||
// 同时清空编号并把重发次数 +1:企油通按 batch_no 幂等,沿用老批次的话
|
||
// 7.8 会一直返回那条旧的失败记录,重发永远不可能成功。
|
||
$reset = Db::name('partners_order')->where('order_id', $orderId)
|
||
->where('oil_status', 0)
|
||
->where('oil_status_zsy', 3)
|
||
->update([
|
||
'oil_status_zsy' => 0,
|
||
'batch_no' => '',
|
||
'waybill_no' => '',
|
||
'waybill_no_code' => '',
|
||
'qyt_retry_num' => Db::raw('qyt_retry_num + 1'),
|
||
'qyt_query_time' => 0,
|
||
'qyt_query_num' => 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
|
||
*/
|
||
public function oilCallback(): Json
|
||
{
|
||
$orderId = (int) $this->request->param('orderId');
|
||
$order = OrderModel::detail($orderId);
|
||
if (!$order) {
|
||
return $this->renderError('订单不存在');
|
||
}
|
||
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
|
||
*/
|
||
public function useCallback(): Json
|
||
{
|
||
$orderId = (int) $this->request->param('orderId');
|
||
$order = OrderModel::detail($orderId);
|
||
if (!$order) {
|
||
return $this->renderError('订单不存在');
|
||
}
|
||
if ($order['use_status'] != 1) {
|
||
return $this->renderError('该单未加油核销,无可回调内容');
|
||
}
|
||
// 每笔加油单独通知:优先补推还没通知成功的;都通知过了就全部重推一遍(下游按 shareNo 去重)
|
||
$ids = Db::name('qyt_consume')->where('order_id', $orderId)->where('notify_status', 0)->order('id')->column('id');
|
||
$all = !$ids;
|
||
$all && $ids = Db::name('qyt_consume')->where('order_id', $orderId)->order('id')->column('id');
|
||
$queue = new QueueService();
|
||
if (!$ids) {
|
||
// 没有消费流水(改造前的历史单),按旧口径推一次
|
||
$queue->push('notify_queue', json_encode(['order_id' => $orderId, 'optType' => 'add_oil', 'retry_count' => 0], 320));
|
||
return $this->renderSuccess('已重新推送加油结果');
|
||
}
|
||
foreach ($ids as $consumeId) {
|
||
$queue->push('notify_queue', json_encode([
|
||
'order_id' => $orderId,
|
||
'optType' => 'add_oil',
|
||
'consume_id' => (int) $consumeId,
|
||
'retry_count' => 0,
|
||
], 320));
|
||
}
|
||
return $this->renderSuccess(($all ? '已重新推送全部 ' : '已补推未通知的 ') . count($ids) . ' 笔加油结果');
|
||
}
|
||
|
||
// ================================================================
|
||
// 退款(整单全额:7.17 查凭证 + 7.16 调低额度)
|
||
// ================================================================
|
||
|
||
/**
|
||
* 发起退款
|
||
*/
|
||
public function refund(): Json
|
||
{
|
||
$orderId = (int) $this->request->param('orderId');
|
||
$reason = trim((string) $this->request->param('reason', ''));
|
||
if (!Db::name('partners_order')->where('order_id', $orderId)->count()) {
|
||
return $this->renderError('订单不存在');
|
||
}
|
||
$res = QytRefund::apply($orderId, 'admin', $this->operator(), $reason);
|
||
return $res['ok'] ? $this->renderSuccess($res['data'], $res['message']) : $this->renderError($res['message']);
|
||
}
|
||
|
||
/**
|
||
* 确认「待人工确认」的退款:向企油通取证(7.13 流水 + 7.17 凭证)
|
||
*/
|
||
public function refundConfirm(): Json
|
||
{
|
||
$res = QytRefund::confirm((int) $this->request->param('orderId'));
|
||
return $this->decided($res) ? $this->renderSuccess($res['data'], $res['message']) : $this->renderError($res['message']);
|
||
}
|
||
|
||
/**
|
||
* 人工判定「待人工确认」的退款
|
||
* result = success / failed,remark 必填(判定依据)
|
||
*/
|
||
public function refundResolve(): Json
|
||
{
|
||
$result = (string) $this->request->param('result');
|
||
if (!in_array($result, ['success', 'failed'], true)) {
|
||
return $this->renderError('判定结果不合法');
|
||
}
|
||
$res = QytRefund::resolve(
|
||
(int) $this->request->param('orderId'),
|
||
$result === 'success',
|
||
$this->operator(),
|
||
(string) $this->request->param('remark', '')
|
||
);
|
||
return $this->decided($res) ? $this->renderSuccess($res['data'], $res['message']) : $this->renderError($res['message']);
|
||
}
|
||
|
||
/**
|
||
* 订单的退款流水
|
||
*/
|
||
public function refundLog(): Json
|
||
{
|
||
$list = Db::name('qyt_refund')
|
||
->where('order_id', (int) $this->request->param('orderId'))
|
||
->field('refund_id,refund_no,source,operator,reason,amount,quota_id,total_before,available_before,total_after,available_after,status,message,create_time,update_time')
|
||
->order('refund_id', 'desc')
|
||
->select()->toArray();
|
||
foreach ($list as &$row) {
|
||
$row['status_text'] = QytRefund::STATUS_TEXT[(int) $row['status']] ?? '';
|
||
$row['create_time_text'] = date('Y-m-d H:i:s', (int) $row['create_time']);
|
||
}
|
||
return $this->renderSuccess(compact('list'));
|
||
}
|
||
|
||
// ================================================================
|
||
// 发票(7.14,按「企业 + 消费月份 + 司机」关联)
|
||
// ================================================================
|
||
|
||
public function invoice(): Json
|
||
{
|
||
$order = Db::name('partners_order')->where('order_id', (int) $this->request->param('orderId'))->find();
|
||
if (!$order) {
|
||
return $this->renderError('订单不存在');
|
||
}
|
||
if ((int) $order['use_status'] !== 1) {
|
||
return $this->renderError('该订单还没有加油记录,暂无发票');
|
||
}
|
||
$res = QytInvoice::forOrder($order, (bool) $this->request->param('refresh', 0));
|
||
return $res['ok'] ? $this->renderSuccess($res, $res['message']) : $this->renderError($res['message']);
|
||
}
|
||
|
||
/**
|
||
* 取证 / 人工判定是否得出了结论:退款成功,或明确判定为「未生效」都算请求成功,
|
||
* 由返回的 refundStatus 区分;状态不符、取证失败、仍无法判定才算失败
|
||
*/
|
||
private function decided(array $res): bool
|
||
{
|
||
return $res['ok']
|
||
|| ($res['status'] === QytRefund::FAILED && $res['reason'] === QytRefund::REASON_UPSTREAM);
|
||
}
|
||
|
||
/** 当前后台操作人 */
|
||
private function operator(): string
|
||
{
|
||
return (string) ($this->store['user']['user_name'] ?? 'admin');
|
||
}
|
||
}
|