Files
webman-buildadmin/app/admin/controller/order/DepositOrder.php
zhenhui 1b8d947f97 0.使用模拟数据进行充值和提现
1.优化提现接口/api/finance/withdrawCreate
2.优化充值接口/api/finance/depositCreate
2026-05-20 15:57:19 +08:00

357 lines
11 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
namespace app\admin\controller\order;
use app\common\controller\Backend;
use app\common\library\finance\DepositSettlement;
use app\common\library\finance\MockPay;
use RuntimeException;
use support\think\Db;
use support\Response;
use Throwable;
use Webman\Http\Request as WebmanRequest;
/**
* 充值订单
*
* 模拟支付流程:用户确认支付后 status=3待审核管理员 approve 后由 DepositSettlement 入账。
* 编辑入口用于查看详情;审核通过/驳回走 approve/reject。
*/
class DepositOrder extends Backend
{
protected ?object $model = null;
protected bool $modelValidate = true;
protected bool $modelSceneValidate = true;
protected string|array $quickSearchField = ['id', 'order_no', 'pay_channel', 'remark', 'deposit_tier_id', 'idempotency_key'];
protected string|array $defaultSortField = ['id' => 'desc'];
protected string|array $orderGuarantee = ['id' => 'desc'];
protected array $withJoinTable = ['user', 'channel', 'reviewAdmin'];
protected function initController(WebmanRequest $request): ?Response
{
$this->model = new \app\common\model\DepositOrder();
return null;
}
protected function _index(): Response
{
if ($this->request && $this->request->get('select')) {
return $this->select($this->request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
$table = strtolower($this->model->getTable());
$mainShort = $alias[$table] ?? '';
if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
$where[] = ['user.admin_id', 'in', $this->scopedAdminIds()];
}
$this->appendDepositOrderIndexWhere($where, $mainShort);
$res = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->with($this->withJoinTable)
->visible([
'user' => ['username', 'phone'],
'channel' => ['name'],
'reviewAdmin' => ['username'],
])
->alias($alias)
->where($where)
->order($order)
->paginate($limit);
return $this->success('', [
'list' => $res->items(),
'total' => $res->total(),
'remark' => get_route_remark(),
]);
}
/**
* 子类可追加列表过滤条件(例如仅展示已注册充值渠道的订单)
*
* @param list<array<mixed>> $where
*/
protected function appendDepositOrderIndexWhere(array &$where, string $mainShort): void
{
}
/**
* GET 时返回关联信息,便于前端详情弹窗直接渲染 user.username / channel.name
* POST 一律拒绝,保证充值订单的金额/状态只能由结算服务变更。
*/
protected function _edit(): Response
{
$pk = $this->model->getPk();
$id = $this->request ? ($this->request->post($pk) ?? $this->request->get($pk)) : null;
if ($id === null || $id === '') {
return $this->error(__('Parameter error'));
}
if ($this->request && $this->request->method() === 'POST') {
return $this->error(__('Please use approve/reject buttons to complete the review'));
}
$row = $this->loadWithRelations(intval(strval($id)));
if (!$row) {
return $this->error(__('Record not found'));
}
if (!$this->checkChannelScoped($row)) {
return $this->error(__('You have no permission'));
}
return $this->success('', ['row' => $row]);
}
private function loadWithRelations(int $id): ?array
{
$row = $this->model
->withJoin($this->withJoinTable, $this->withJoinType)
->with($this->withJoinTable)
->visible([
'user' => ['username', 'phone', 'admin_id'],
'channel' => ['name'],
'reviewAdmin' => ['username'],
])
->where($this->model->getTable() . '.id', $id)
->find();
if (!$row) {
return null;
}
return $row->toArray();
}
private function checkChannelScoped(array $row): bool
{
if (!$this->auth || $this->auth->isSuperAdmin()) {
return true;
}
$userRow = $row['user'] ?? null;
if (!is_array($userRow)) {
return false;
}
$adminIdRaw = $userRow['admin_id'] ?? null;
if ($adminIdRaw === null || $adminIdRaw === '') {
return false;
}
if (!is_numeric(strval($adminIdRaw))) {
return false;
}
return in_array(intval(strval($adminIdRaw)), $this->scopedAdminIds(), true);
}
/**
* 当前管理员可见的管理员ID集合本人 + 下级角色组内管理员)
*
* @return int[]
*/
private function scopedAdminIds(): array
{
if (!$this->auth) {
return [0];
}
if ($this->auth->isSuperAdmin()) {
return [];
}
$groupIds = $this->auth->getAdminChildGroups();
$adminIds = $groupIds ? $this->auth->getGroupAdmins($groupIds) : [];
$adminIds[] = $this->auth->id;
$adminIds = array_map(static fn($id) => intval(strval($id)), $adminIds);
$adminIds = array_values(array_unique(array_filter($adminIds, static fn($id) => $id > 0)));
return $adminIds === [] ? [0] : $adminIds;
}
/**
* 审核通过将待审核订单结算入账status 3 -> 1
*/
public function approve(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$id = $this->intParam($request->post('id'));
if ($id <= 0) {
return $this->error(__('Parameter error'));
}
$scoped = $this->loadWithRelations($id);
if (!$scoped) {
return $this->error(__('Record not found'));
}
if (!$this->checkChannelScoped($scoped)) {
return $this->error(__('You have no permission'));
}
$order = Db::name('deposit_order')->where('id', $id)->find();
if (!$order) {
return $this->error(__('Record not found'));
}
$currentStatus = $this->intParam($order['status'] ?? 0);
if ($currentStatus !== MockPay::DEPOSIT_STATUS_PENDING_REVIEW) {
return $this->error(__('This order has already been reviewed'));
}
$now = time();
$adminId = $this->intParam($this->auth->id ?? 0);
$adminName = $this->adminDisplayName();
$extraRemark = '管理员(' . $adminName . ')审核通过并入账';
try {
DepositSettlement::settle(
$id,
DepositSettlement::SOURCE_ADMIN_APPROVE,
'admin approve mock deposit',
$adminId > 0 ? $adminId : null,
$extraRemark
);
} catch (RuntimeException $e) {
return $this->error($e->getMessage());
} catch (Throwable $e) {
return $this->error($e->getMessage());
}
$patch = [
'update_time' => $now,
];
if ($this->depositOrderHasColumn('review_admin_id')) {
$patch['review_admin_id'] = $adminId > 0 ? $adminId : null;
}
if ($this->depositOrderHasColumn('review_time')) {
$patch['review_time'] = $now;
}
Db::name('deposit_order')->where('id', $id)->where('status', 1)->update($patch);
return $this->success(__('Approved'));
}
/**
* 审核驳回必须填写备注reject_reason
*/
public function reject(WebmanRequest $request): Response
{
$response = $this->initializeBackend($request);
if ($response !== null) {
return $response;
}
if ($request->method() !== 'POST') {
return $this->error(__('Parameter error'));
}
$id = $this->intParam($request->post('id'));
if ($id <= 0) {
return $this->error(__('Parameter error'));
}
$remarkRaw = $request->post('remark');
$remark = is_string($remarkRaw) ? trim($remarkRaw) : '';
if ($remark === '') {
return $this->error(__('Please provide reject reason'));
}
$scoped = $this->loadWithRelations($id);
if (!$scoped) {
return $this->error(__('Record not found'));
}
if (!$this->checkChannelScoped($scoped)) {
return $this->error(__('You have no permission'));
}
$order = Db::name('deposit_order')->where('id', $id)->find();
if (!$order) {
return $this->error(__('Record not found'));
}
$currentStatus = $this->intParam($order['status'] ?? 0);
if ($currentStatus !== MockPay::DEPOSIT_STATUS_PENDING_REVIEW) {
return $this->error(__('This order has already been reviewed'));
}
$now = time();
$adminId = $this->intParam($this->auth->id ?? 0);
$adminName = $this->adminDisplayName();
$rejectReason = mb_substr($remark, 0, 255);
$baseRemark = is_string($order['remark'] ?? null) ? trim($order['remark']) : '';
$note = '管理员(' . $adminName . ')驳回:' . $rejectReason;
$combined = $baseRemark === '' ? $note : mb_substr($baseRemark . ' | ' . $note, 0, 255);
$update = [
'status' => 2,
'remark' => $combined,
'update_time' => $now,
];
if ($this->depositOrderHasColumn('reject_reason')) {
$update['reject_reason'] = $rejectReason;
}
if ($this->depositOrderHasColumn('review_admin_id')) {
$update['review_admin_id'] = $adminId > 0 ? $adminId : null;
}
if ($this->depositOrderHasColumn('review_time')) {
$update['review_time'] = $now;
}
$affected = Db::name('deposit_order')
->where('id', $id)
->where('status', MockPay::DEPOSIT_STATUS_PENDING_REVIEW)
->update($update);
if (!is_numeric($affected) || intval($affected) <= 0) {
return $this->error(__('This order has already been reviewed'));
}
return $this->success(__('Rejected'));
}
private function depositOrderHasColumn(string $column): bool
{
static $cache = [];
if (array_key_exists($column, $cache)) {
return $cache[$column];
}
try {
$rows = Db::query('SHOW COLUMNS FROM `deposit_order` LIKE ?', [$column]);
$cache[$column] = is_array($rows) && $rows !== [];
} catch (Throwable $e) {
$cache[$column] = false;
}
return $cache[$column];
}
private function intParam($raw): int
{
if ($raw === null || $raw === '') {
return 0;
}
if (is_numeric(strval($raw))) {
return intval(strval($raw));
}
return 0;
}
private function adminDisplayName(): string
{
if (!$this->auth) {
return 'admin';
}
$username = $this->auth->username ?? '';
if (is_string($username) && trim($username) !== '') {
return trim($username);
}
return 'admin#' . strval($this->auth->id ?? 0);
}
}