Files
oil/server-tp6/app/store/controller/partners/Order.php
T
vipnub fafd3e2c16 1、合作商余额查询
2、分油账户司机可用额度
2026-10-20 23:21:10 +08:00

309 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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\MallOil;
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('该单已加油核销,不可重发');
}
// 发放失败时额度已原路退回,重发需重新扣减:
// 合作商单扣合作商余额,小程序单扣下单用户余额
if (!MallOil::take($order, (float) $order['total_price'])) {
return $this->renderError(MallOil::notEnoughText($order));
}
// 仅允许从「发放失败(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) {
// 状态已被其它进程改变 -> 回补刚扣的额度
MallOil::giveBack($order, (float) $order['total_price'], '重发撤销');
return $this->renderError('当前状态不允许重发(仅「发放失败」的订单可重发)');
}
(new QueueService())->push('partners_queue', json_encode(['order_id' => $orderId], 320));
// 小程序单把「重新发放」状态同步回订单商品行,用户端立刻能看到
MallOil::syncGoods($order);
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');
}
}