where('parent_code', $params['parent_code']); } if (!empty($params['keyword'])) { $query->where(function ($q) use ($params) { $q->where('title', 'like', '%' . $params['keyword'] . '%') ->orWhere('code', 'like', '%' . $params['keyword'] . '%'); }); } $query->orderBy('id'); $pageSize = $params['page_size'] ?? 20; $list = $query->paginate($pageSize); return [ 'list' => $list->items(), 'total' => $list->total(), 'page' => $list->currentPage(), 'page_size' => $list->perPage(), ]; } public function getTree(): array { return $this->buildTree(City::orderBy('id')->get()); } public function getChildren(string $parentCode): array { return City::where('parent_code', $parentCode) ->orderBy('id') ->get() ->toArray(); } public function getByCode(string $code): ?City { return City::where('code', $code)->first(); } public function getById(int $id): ?City { return City::find($id); } public function create(array $data): City { Validator::make($data, [ 'title' => 'required|string|max:100', 'code' => 'required|string|max:50|unique:system_city,code', 'parent_code' => 'sometimes|nullable|exists:system_city,code', ])->validate(); $city = City::create($data); $this->clearCache(); return $city; } public function update(int $id, array $data): City { $city = City::findOrFail($id); Validator::make($data, [ 'title' => 'sometimes|required|string|max:100', 'code' => 'sometimes|required|string|max:50|unique:system_city,code,' . $id, 'parent_code' => 'sometimes|nullable|exists:system_city,code', ])->validate(); // 防止循环引用 if (isset($data['parent_code']) && $data['parent_code'] === $city->code) { throw new \Exception('不能将城市设置为自己的子级'); } $city->update($data); $this->clearCache(); return $city; } public function delete(int $id): bool { $city = City::findOrFail($id); if ($city->children()->exists()) { throw new \Exception('该城市下有子级数据,不能删除'); } $city->delete(); $this->clearCache(); return true; } public function batchDelete(array $ids): bool { City::whereIn('id', $ids)->delete(); $this->clearCache(); return true; } /** * 获取所有顶级城市(省/直辖市/自治区) */ public function getTopLevel(): array { return City::whereNull('parent_code') ->orderBy('id') ->get() ->toArray(); } /** * 根据父级编码获取城市 */ public function getByParentCode(string $parentCode): array { return City::where('parent_code', $parentCode) ->orderBy('id') ->get() ->toArray(); } /** * 获取城市的完整路径(从顶级到当前) */ public function getPath(string $code): array { $path = []; $city = $this->getByCode($code); while ($city) { array_unshift($path, $city); $city = $city->parent; } return $path; } /** * 获取城市的所有子孙 */ public function getDescendants(string $code): array { $descendants = []; $this->collectDescendants($code, $descendants); return $descendants; } private function collectDescendants(string $parentCode, array &$result): void { $children = City::where('parent_code', $parentCode)->get(); foreach ($children as $child) { $result[] = $child; $this->collectDescendants($child->code, $result); } } private function buildTree($cities, string $parentCode = ''): array { $tree = []; foreach ($cities as $city) { if ($city->parent_code == $parentCode) { $children = $this->buildTree($cities, $city->code); if (!empty($children)) { $city['children'] = $children; } $tree[] = $city; } } return $tree; } private function clearCache(): void { Cache::forget('system:cities:tree'); } public function getCachedTree(): array { $cacheKey = 'system:cities:tree'; $tree = Cache::get($cacheKey); if ($tree === null) { $tree = $this->getTree(); Cache::put($cacheKey, $tree, 3600); } return $tree; } /** * 获取省份(兼容旧接口) */ public function getProvinces(): array { return $this->getTopLevel(); } /** * 获取城市(兼容旧接口) */ public function getCities(int $provinceId): array { // 由于新结构使用code关联,这里需要根据id找到对应的code $province = City::find($provinceId); if (!$province) { return []; } return $this->getByParentCode($province->code); } /** * 获取区县(兼容旧接口) */ public function getDistricts(int $cityId): array { $city = City::find($cityId); if (!$city) { return []; } return $this->getByParentCode($city->code); } }