[107], 'k-battery' => [100, 102], 'bluetooth' => [104], 'power-bank' => [1, 101], 'password' => [103], 'password-bluetooth' => [105], ]; private const DEVICE_NAMES = [ 1 => '充电底座', 100 => 'K霸宝', 101 => '充电宝', 102 => 'K霸宝 Blue', 103 => '密码线充', 104 => '蓝牙线充', 105 => '密码蓝牙线充', 107 => '车载充电器', ]; private const STATUSES = [ 0 => ['code' => 'pending', 'name' => '待支付'], 1 => ['code' => 'using', 'name' => '使用中'], 2 => ['code' => 'done', 'name' => '已完成'], 3 => ['code' => 'timeout', 'name' => '超时完成'], 4 => ['code' => 'rent_failed', 'name' => '租借失败'], 5 => ['code' => 'deduct_failed', 'name' => '扣款失败'], 6 => ['code' => 'refund', 'name' => '申请退款'], ]; public function __construct() { $this->model = new RentalOrder(); } public function indexList(array $filters): array { return $this->listOrders($filters); } /** * 当前账号下级代理管理的订单分页列表。 */ public function subordinateIndexList(array $filters): array { return $this->listOrders($filters, true); } private function listOrders(array $filters, bool $subordinate = false): array { $page = max(1, (int) request()->input('page', 1)); $limit = min(100, max(1, (int) request()->input('limit', 20))); $result = $this->buildQuery($filters, $subordinate) ->order('o.rent_at', 'desc') ->order('o.id', 'desc') ->paginate(['page' => $page, 'list_rows' => $limit]) ->toArray(); $result['data'] = array_map(fn (array $item) => $this->formatOrder($item), $result['data'] ?? []); $result['summary'] = $this->summary($filters, $subordinate); return $result; } public function readOrder(int $id): array { return $this->readScopedOrder($id); } /** * 获取下级代理管理的订单详情。 */ public function readSubordinateOrder(int $id): array { return $this->readScopedOrder($id, true); } private function readScopedOrder(int $id, bool $subordinate = false): array { if ($id <= 0) { throw new ApiException('订单参数错误'); } $order = $this->buildQuery([], $subordinate)->where('o.id', $id)->find(); if (empty($order)) { throw new ApiException('订单不存在或无权查看'); } return $this->formatOrder($order, true); } private function buildQuery(array $filters, bool $subordinate = false) { $query = Db::table('sa_rental_order')->alias('o') ->leftJoin('sa_device d', 'd.id = o.device_id AND d.delete_time IS NULL') ->leftJoin('sa_merchant rent', 'rent.id = o.rent_merchant_id AND rent.delete_time IS NULL') ->leftJoin('sa_merchant back', 'back.id = o.return_merchant_id AND back.delete_time IS NULL') ->leftJoin('sa_system_user manager', 'manager.id = o.manage_agent_id AND manager.delete_time IS NULL') ->whereNull('o.delete_time') ->field(implode(',', [ 'o.id,o.order_sn,o.sub_order_sn,o.transaction_id,o.merchant_order_no,o.device_id,o.device_type,o.source,o.pay_type', 'o.user_id,o.user_name,o.user_phone,o.rent_merchant_id,o.return_merchant_id,o.rent_at,o.return_at,o.status', 'o.paid_amount,o.income_amount,o.refund_amount,o.package_description,o.remark,o.manage_agent_id,o.create_time', 'd.goods_sn AS device_sn,d.device_code AS device_code,d.goods_name AS device_name', 'rent.name AS rent_merchant_name,back.name AS return_merchant_name', 'manager.realname AS manager_realname,manager.username AS manager_username,manager.phone AS manager_phone', ])); if ($subordinate) { $managerIds = $this->subordinateManagerIds(); if ($managerIds === []) { $query->where('o.id', 0); } else { $query->whereIn('o.manage_agent_id', $managerIds); } } elseif ($this->currentAdminId() !== 1) { $query->where('o.manage_agent_id', $this->currentAdminId()); } $category = trim((string) ($filters['category'] ?? 'all')); if ($category !== 'all' && isset(self::DEVICE_TYPES[$category])) { $query->whereIn('o.device_type', self::DEVICE_TYPES[$category]); } $status = trim((string) ($filters['status'] ?? 'all')); if ($status === 'today') { $query->whereTime('o.rent_at', 'today'); } elseif ($status !== 'all') { $statusValue = array_search($status, array_column(self::STATUSES, 'code'), true); if ($statusValue !== false) { $query->where('o.status', (int) $statusValue); } } $this->like($query, 'o.order_sn', $filters['order_no'] ?? ''); $this->like($query, 'o.user_phone', $filters['phone'] ?? ''); $this->like($query, 'o.user_id', $filters['user_id'] ?? ''); $this->like($query, 'rent.name', $filters['merchant'] ?? ''); $this->like($query, 'd.goods_sn', $filters['device_sn'] ?? ''); $this->like($query, 'o.transaction_id', $filters['transaction_id'] ?? ''); $agentId = (int) ($filters['agent_id'] ?? 0); if ($agentId > 0) { $query->where('o.manage_agent_id', $agentId); } $source = trim((string) ($filters['source'] ?? '')); if (in_array($source, ['1', '2'], true)) { $query->where('o.source', (int) $source); } $payType = trim((string) ($filters['pay_type'] ?? '')); if (in_array($payType, ['deposit', 'free'], true)) { $query->where('o.pay_type', $payType); } $startAt = trim((string) ($filters['start_at'] ?? '')); $endAt = trim((string) ($filters['end_at'] ?? '')); if ($startAt !== '') { $query->where('o.rent_at', '>=', $startAt . ' 00:00:00'); } if ($endAt !== '') { $query->where('o.rent_at', '<=', $endAt . ' 23:59:59'); } return $query; } private function summary(array $filters, bool $subordinate = false): array { $result = ['all' => 0, 'today' => 0]; foreach (self::STATUSES as $status) { $result[$status['code']] = 0; } $rows = $this->buildQuery(array_merge($filters, ['status' => 'all']), $subordinate) ->field('o.status,COUNT(*) AS total')->group('o.status')->select()->toArray(); foreach ($rows as $row) { $status = self::STATUSES[(int) $row['status']] ?? null; if ($status) { $result[$status['code']] = (int) $row['total']; $result['all'] += (int) $row['total']; } } $result['today'] = $this->buildQuery(array_merge($filters, ['status' => 'today']), $subordinate)->count(); return $result; } private function formatOrder(array $order, bool $detail = false): array { $status = self::STATUSES[(int) ($order['status'] ?? 0)] ?? self::STATUSES[0]; $source = (int) ($order['source'] ?? 0); $payType = (string) ($order['pay_type'] ?? 'deposit'); return [ 'id' => (int) $order['id'], 'order_sn' => (string) $order['order_sn'], 'sub_order_sn' => (string) ($order['sub_order_sn'] ?? ''), 'transaction_id' => (string) ($order['transaction_id'] ?? ''), 'merchant_order_no' => (string) ($order['merchant_order_no'] ?? ''), 'device_type' => (int) ($order['device_type'] ?? 0), 'device_type_name' => self::DEVICE_NAMES[(int) ($order['device_type'] ?? 0)] ?? '未知设备', 'device_name' => (string) ($order['device_name'] ?? ''), 'device_sn' => (string) ($order['device_sn'] ?? ''), 'device_code' => (string) ($order['device_code'] ?? ''), 'source' => $source, 'source_name' => $source === 1 ? '微信' : ($source === 2 ? '支付宝' : '未知'), 'pay_type' => $payType, 'pay_type_name' => $payType === 'free' ? '免押金' : '押金', 'user_id' => (int) ($order['user_id'] ?? 0), 'user_name' => (string) ($order['user_name'] ?? ''), 'user_phone' => (string) ($order['user_phone'] ?? ''), 'rent_merchant' => (string) ($order['rent_merchant_name'] ?? ''), 'return_merchant' => (string) ($order['return_merchant_name'] ?? ''), 'rent_at' => (string) ($order['rent_at'] ?? ''), 'return_at' => (string) ($order['return_at'] ?? ''), 'status' => $status['code'], 'status_name' => $status['name'], 'paid_amount' => (float) ($order['paid_amount'] ?? 0), 'income_amount' => (float) ($order['income_amount'] ?? 0), 'refund_amount' => (float) ($order['refund_amount'] ?? 0), 'package_description' => (string) ($order['package_description'] ?? ''), 'remark' => (string) ($order['remark'] ?? ''), 'manage_agent_id' => (int) ($order['manage_agent_id'] ?? 0), 'create_time' => (string) ($order['create_time'] ?? ''), 'manager_name' => (string) (($order['manager_realname'] ?? '') ?: ($order['manager_username'] ?? '未分配')), 'manager_phone' => (string) ($order['manager_phone'] ?? ''), 'detail' => $detail, ]; } private function currentAdminId(): int { return (int) ($this->adminInfo['id'] ?? 0); } /** @return int[] */ private function subordinateManagerIds(): array { $currentId = $this->currentAdminId(); if ($currentId <= 0) { return []; } if ($currentId === 1) { return array_map('intval', SystemUser::where('id', '<>', 1) ->whereNull('delete_time')->column('id')); } $dept = $this->adminInfo['deptList'] ?? []; if (empty($dept['id']) || !isset($dept['level'])) { return []; } $prefix = (string) $dept['level'] . (int) $dept['id'] . ','; $deptIds = Db::table('sa_system_dept')->whereNull('delete_time') ->whereLike('level', $prefix . '%')->column('id'); if ($deptIds === []) { return []; } return array_map('intval', SystemUser::whereIn('dept_id', $deptIds) ->whereNull('delete_time')->column('id')); } private function like($query, string $field, mixed $value): void { $value = trim((string) $value); if ($value !== '') { $query->whereLike($field, '%' . $value . '%'); } } }