'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->shouldApplyUserAdminScope()) { $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> $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() || $this->hasGlobalReadScope()) { 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); } }