ensurePasswordColumn(); return api_success([ 'cards' => [ [ 'title' => '用户总量', 'value' => (int)Db::name('users')->count(), 'desc' => '当前数据库中的用户数量', ], [ 'title' => '正常用户', 'value' => (int)Db::name('users')->where('status', 'enabled')->count(), 'desc' => '当前可正常使用系统的用户数量', ], [ 'title' => '地址数量', 'value' => (int)Db::name('user_addresses')->count(), 'desc' => '用户维护的寄送与收货地址总数', ], [ 'title' => '消息总量', 'value' => (int)Db::name('user_messages')->count(), 'desc' => '已发送给用户的站内消息数量', ], ], ]); } public function index(Request $request) { $this->ensurePasswordColumn(); $keyword = trim((string)$request->input('keyword', '')); $status = trim((string)$request->input('status', '')); $query = Db::name('users') ->alias('u') ->leftJoin('user_addresses a', 'a.user_id = u.id AND a.is_default = 1') ->field([ 'u.id', 'u.nickname', 'u.mobile', 'u.password', 'u.status', 'u.created_at', 'u.updated_at', 'a.province', 'a.city', 'a.district', 'a.detail_address', ]) ->order('u.id', 'desc'); if ($keyword !== '') { $query->where(function ($builder) use ($keyword) { $builder->whereLike('u.nickname', "%{$keyword}%") ->whereOrLike('u.mobile', "%{$keyword}%"); }); } if ($status !== '') { $query->where('u.status', $status); } $rows = $query->select()->toArray(); $list = array_map(function (array $item) { $userId = (int)$item['id']; return [ 'id' => $userId, 'nickname' => $item['nickname'] ?: '未命名用户', 'mobile' => $item['mobile'] ?: '', 'status' => $item['status'], 'status_text' => $this->userStatusText($item['status']), 'password_set' => ((string)($item['password'] ?? '')) !== '', 'default_address' => trim(sprintf( '%s%s%s%s', $item['province'] ?? '', $item['city'] ?? '', $item['district'] ?? '', $item['detail_address'] ?? '' )), 'order_count' => (int)Db::name('orders')->where('user_id', $userId)->count(), 'message_count' => (int)Db::name('user_messages')->where('user_id', $userId)->count(), 'ticket_count' => (int)Db::name('tickets')->where('user_id', $userId)->count(), 'created_at' => $item['created_at'], 'updated_at' => $item['updated_at'], ]; }, $rows); return api_success(['list' => $list]); } public function detail(Request $request) { $this->ensurePasswordColumn(); $id = (int)$request->input('id', 0); if ($id <= 0) { return api_error('用户 ID 不能为空', 422); } $user = Db::name('users')->where('id', $id)->find(); if (!$user) { return api_error('用户不存在', 404); } $addresses = Db::name('user_addresses') ->where('user_id', $id) ->order('is_default', 'desc') ->order('id', 'desc') ->select() ->toArray(); $recentOrders = Db::name('orders') ->where('user_id', $id) ->order('id', 'desc') ->limit(5) ->select() ->toArray(); $recentMessages = Db::name('user_messages') ->where('user_id', $id) ->order('id', 'desc') ->limit(5) ->select() ->toArray(); return api_success([ 'user_info' => [ 'id' => (int)$user['id'], 'nickname' => $user['nickname'] ?: '未命名用户', 'mobile' => $user['mobile'] ?: '', 'status' => $user['status'], 'status_text' => $this->userStatusText($user['status']), 'password_set' => ((string)($user['password'] ?? '')) !== '', 'created_at' => $user['created_at'], 'updated_at' => $user['updated_at'], ], 'addresses' => array_map(fn (array $item) => [ 'consignee' => $item['consignee'], 'mobile' => $item['mobile'], 'full_address' => trim(sprintf('%s%s%s%s', $item['province'], $item['city'], $item['district'], $item['detail_address'])), 'is_default' => (bool)$item['is_default'], ], $addresses), 'recent_orders' => array_map(fn (array $item) => [ 'order_no' => $item['order_no'], 'display_status' => $item['display_status'], 'pay_amount' => (float)$item['pay_amount'], 'created_at' => $item['created_at'], ], $recentOrders), 'recent_messages' => array_map(fn (array $item) => [ 'title' => $item['title'], 'content' => $item['content'], 'is_read' => (bool)$item['is_read'], 'created_at' => $item['created_at'], ], $recentMessages), ]); } public function save(Request $request) { $this->ensurePasswordColumn(); $id = (int)$request->input('id', 0); $nickname = trim((string)$request->input('nickname', '')); $mobile = trim((string)$request->input('mobile', '')); $status = trim((string)$request->input('status', 'enabled')); $password = trim((string)$request->input('password', '')); if ($nickname === '' || $mobile === '') { return api_error('昵称和手机号不能为空', 422); } $now = date('Y-m-d H:i:s'); $payload = [ 'nickname' => $nickname, 'mobile' => $mobile, 'status' => $status !== '' ? $status : 'enabled', 'updated_at' => $now, ]; if ($password !== '') { $payload['password'] = password_hash($password, PASSWORD_BCRYPT); } if ($id > 0) { $user = Db::name('users')->where('id', $id)->find(); if (!$user) { return api_error('用户不存在', 404); } $exists = Db::name('users') ->where('mobile', $mobile) ->where('id', '<>', $id) ->find(); if ($exists) { return api_error('手机号已存在', 422); } Db::name('users')->where('id', $id)->update($payload); return api_success(['id' => $id], '用户已更新'); } $exists = Db::name('users')->where('mobile', $mobile)->find(); if ($exists) { return api_error('手机号已存在', 422); } $payload['avatar'] = ''; $payload['password'] = $payload['password'] ?? ''; $payload['last_login_at'] = null; $payload['created_at'] = $now; $newId = (int)Db::name('users')->insertGetId($payload); return api_success(['id' => $newId], '用户已创建'); } private function userStatusText(string $status): string { return match ($status) { 'enabled' => '正常', 'disabled' => '已停用', default => $status, }; } private function ensurePasswordColumn(): void { $column = Db::query("SHOW COLUMNS FROM users LIKE 'password'"); if ($column) { return; } Db::execute("ALTER TABLE users ADD COLUMN password VARCHAR(255) NOT NULL DEFAULT '' AFTER mobile"); } }