151 lines
6.1 KiB
PHP
151 lines
6.1 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Recon;
|
||
|
||
use App\Exceptions\RepositoryException;
|
||
use App\Models\BillModel;
|
||
use App\Models\PaymentModel;
|
||
use App\Models\StoreModel;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Modules\AnnoRoute\Attribute\GetRoute;
|
||
use Modules\AnnoRoute\Attribute\PutRoute;
|
||
use Modules\AnnoRoute\Attribute\RequestAttribute;
|
||
use Modules\Common\Http\Controllers\BaseController;
|
||
use Throwable;
|
||
|
||
/**
|
||
* 支付记录(小程序合并付款提交汇款凭证;后台审核:通过后关联账单批量置已支付,拒绝释放账单)
|
||
*/
|
||
#[RequestAttribute('/recon/payment', 'recon.payment')]
|
||
class PaymentController extends BaseController
|
||
{
|
||
protected array $searchField = [
|
||
'store_id' => '=',
|
||
'status' => '=',
|
||
'pay_type' => '=',
|
||
'pay_method' => '=',
|
||
'payment_no' => 'like',
|
||
];
|
||
|
||
/** 支付记录列表(待审核优先) */
|
||
#[GetRoute(authorize: 'query')]
|
||
public function query(Request $request): JsonResponse
|
||
{
|
||
$params = $request->all();
|
||
$pageSize = $params['pageSize'] ?? 10;
|
||
$data = $this->buildSearch($params, PaymentModel::query()
|
||
->with(['store:id,name', 'auditor:id,nickname'])
|
||
->withCount('bills'))
|
||
->orderBy('status')
|
||
->orderBy('id', 'desc')
|
||
->paginate($pageSize)
|
||
->toArray();
|
||
return $this->success($data);
|
||
}
|
||
|
||
/** 支付记录详情:支付信息 + 凭证图片 + 合并付款的账单 */
|
||
#[GetRoute(route: '/{id}', authorize: 'query', where: ['id' => '[0-9]+'])]
|
||
public function detail(int $id): JsonResponse
|
||
{
|
||
$payment = PaymentModel::with(['store:id,name,contact,phone', 'auditor:id,nickname'])->find($id);
|
||
if (empty($payment)) {
|
||
throw new RepositoryException('支付记录不存在');
|
||
}
|
||
|
||
$bills = $payment->bills()
|
||
->orderBy('id')
|
||
->get(['id', 'bill_no', 'bill_date', 'product_amount', 'delivery_fee', 'added_amount', 'after_sale', 'total_amount', 'status'])
|
||
->toArray();
|
||
|
||
$data = $payment->toArray();
|
||
$data['voucher_urls'] = $payment->voucherUrls();
|
||
|
||
return $this->success([
|
||
'payment' => $data,
|
||
'bills' => $bills,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 审核支付记录:通过 → 关联账单全部置已支付;拒绝 → 释放账单(可重新发起付款)
|
||
* @throws Throwable
|
||
*/
|
||
#[PutRoute(route: '/{id}/audit', authorize: 'audit', where: ['id' => '[0-9]+'])]
|
||
public function audit(int $id, Request $request): JsonResponse
|
||
{
|
||
$data = $request->validate([
|
||
'result' => 'required|string|in:pass,reject',
|
||
'audit_remark' => 'nullable|string|max:255|required_if:result,reject',
|
||
], [
|
||
'result.required' => '请选择审核结果',
|
||
'result.in' => '审核结果不正确',
|
||
'audit_remark.required_if' => '拒绝时请填写原因',
|
||
'audit_remark.max' => '审核备注超过最大长度',
|
||
]);
|
||
|
||
return DB::transaction(function () use ($id, $data, $request) {
|
||
$payment = PaymentModel::query()->lockForUpdate()->find($id);
|
||
if (empty($payment)) {
|
||
throw new RepositoryException('支付记录不存在');
|
||
}
|
||
if ($payment->status !== PaymentModel::STATUS_PENDING) {
|
||
throw new RepositoryException('该支付记录已审核,请勿重复操作');
|
||
}
|
||
|
||
$bills = $payment->bills()->lockForUpdate()->get();
|
||
$auditorId = (int) $request->user()->id;
|
||
$now = now();
|
||
|
||
if ($data['result'] === 'pass') {
|
||
// 任一账单已通过其他方式收款(如线下登记)则整批中止,避免重复收款
|
||
$paid = $bills->where('status', BillModel::STATUS_PAID);
|
||
if ($paid->isNotEmpty()) {
|
||
throw new RepositoryException(
|
||
'账单 ' . $paid->pluck('bill_no')->implode('、') . ' 已收款,请核实后再审核'
|
||
);
|
||
}
|
||
|
||
$methodName = PaymentModel::METHOD_NAMES[$payment->pay_method] ?? '线上支付';
|
||
BillModel::query()->whereIn('id', $bills->pluck('id'))->update([
|
||
'status' => BillModel::STATUS_PAID,
|
||
'paid_at' => $now,
|
||
'paid_operator_id' => $auditorId,
|
||
'pay_remark' => $methodName . '(支付单号 ' . $payment->payment_no . ')',
|
||
]);
|
||
|
||
// 按门店累加总采购金额(只统计商品金额,不含配送费/附加金额)
|
||
foreach ($bills->groupBy('store_id')->sortKeys() as $storeId => $storeBills) {
|
||
$amount = '0';
|
||
foreach ($storeBills as $storeBill) {
|
||
$amount = bcadd($amount, (string) $storeBill->product_amount, 2);
|
||
}
|
||
$store = StoreModel::query()->lockForUpdate()->find((int) $storeId);
|
||
if ($store !== null) {
|
||
$store->total_purchase_amount = bcadd((string) $store->total_purchase_amount, $amount, 2);
|
||
$store->save();
|
||
}
|
||
}
|
||
$payment->status = PaymentModel::STATUS_APPROVED;
|
||
} else {
|
||
// 拒绝:释放账单,门店可重新发起付款
|
||
BillModel::query()->whereIn('id', $bills->pluck('id'))->update(['payment_id' => 0]);
|
||
$payment->status = PaymentModel::STATUS_REJECTED;
|
||
}
|
||
|
||
$payment->audited_at = $now;
|
||
$payment->auditor_id = $auditorId;
|
||
$payment->audit_remark = (string) ($data['audit_remark'] ?? '');
|
||
$payment->save();
|
||
|
||
return $this->success(
|
||
[],
|
||
$payment->status === PaymentModel::STATUS_APPROVED
|
||
? '审核通过,' . $bills->count() . ' 张账单已置为已支付'
|
||
: '已拒绝,账单已释放可重新付款'
|
||
);
|
||
});
|
||
}
|
||
}
|