# spring_weather **Repository Path**: renchunlin/spring_weather ## Basic Information - **Project Name**: spring_weather - **Description**: No description available - **Primary Language**: Dart - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-04 - **Last Updated**: 2026-08-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring_weather Flutter 天气应用(早期阶段)。目前已完成网络层封装和 Weatherstack 天气 API 客户端,业务页面待开发。 ## 技术栈 - **Flutter**(见下方「双 SDK 说明」) - **dio** — HTTP 请求 - **oktoast** — Toast 提示 - **web_socket_channel** — WebSocket 长连接 ## 目录结构 ``` lib/ ├── http/ # 网络层封装(Dio + WebSocket) │ ├── dio_instance.dart │ ├── base_model.dart │ ├── interceptor/ # Token / 打印日志 / 响应拦截器 │ └── socket/ # WebSocket 单例与消息模型 └── weatherstack/ # Weatherstack 天气 API 客户端 ├── weatherstack_client.dart # 客户端、异常、单位/语言枚举 ├── weatherstack_config.dart # API Key、主机、限流间隔配置 └── weatherstack_model.dart # 响应模型 ``` ## 网络层(lib/http) 基于 Dio 的单例封装: - `DioInstance.instance()` 获取单例,使用前必须调用 `initDio(baseUrl: ...)`。 - 拦截器顺序:`TokenInterceptor` → `PrintLogInterceptor` → `RspInterceptor`。 - `RspInterceptor` 统一处理响应:HTTP 200 且业务 `code == 200` 时提取 `content`;`code == 210` 视为需登录;其余业务错误 toast 提示。 - `socket/` 提供 WebSocket 长连接单例(服务端 `code 333` 心跳回发、`code 666` 业务消息)。 ## Weatherstack 天气接口(lib/weatherstack) 基于独立 Dio 实例,不经过 `lib/http` 的拦截器链(Weatherstack 响应不是后端约定的 `{content, code, message, success}` 结构)。 ### 免费版可用 / 不可用接口(实测) | 接口 | 状态 | 说明 | | --- | --- | --- | | `current` 当前天气 | ✅ 可用 | 响应内嵌空气质量、天文、时区 | | `forecast` 天气预报 | ✅ 可用 | 逐日 + 逐小时,免费版建议 1-3 天 | | 经纬度查询 | ✅ 可用 | `query` 传 `'39.90,116.40'` | | `units=f` 华氏度 | ✅ 可用 | `WsUnit.fahrenheit` | | `historical` 历史天气 | ❌ 错误 603 | 套餐不支持 | | `autocomplete` 地点搜索 | ❌ 错误 105 | 套餐不支持 | | 多语言 `language=zh` | ❌ 错误 105 | 多语言是付费功能,免费版被锁 | > `timezone`、`astronomy` 不是独立接口,其数据直接内嵌在 `current` / `forecast` 响应中。 ### 语言参数实测结论(`language=zh` 为什么传不进去) `language` 参数传不进去**不是写法问题,是套餐限制**:语言本地化是 weatherstack 的付费功能,免费版调用一律被拒。 | 传入参数 | 返回 | 错误码 | | --- | --- | --- | | `language=zh` / `de` / `es` / `fr` / `zh-TW` | Access Restricted - your plan does not support this API Function | `105 function_access_restricted` | | `language=en` / `EN` | invalid language code | `605 invalid_language` | | 不传 `language` | ✅ 正常返回,默认英文 | — | 两个反直觉的点: 1. **主因**:任何真实语言代码(zh/de/es/fr 等)在免费版都会被拒,错误 105。要中文只能升级付费套餐。 2. **附加怪癖**:官方支持的语言代码表里**没有 `en`**,所以显式传 `language=en` 反而会被当成非法代码(错误 605)。英文靠「不传参数」实现,而不是传 `en`。 因此免费版能拿到的天气描述只有英文(默认),客户端不传 `language` 参数即可(已删除 `WsLanguage` 枚举)。若需要中文天气文案,需换用支持中文的免费天气 API(如 Open-Meteo / 和风天气)。 ### 用法示例 ```dart import 'package:spring_weather/weatherstack/weatherstack_client.dart'; final ws = WeatherstackClient.instance(); // 当前天气(含空气质量、天文、时区) final resp = await ws.current(query: 'Beijing'); print(resp.current?.temperature); // 34 print(resp.current?.weatherDescriptions); // [Partly cloudy] print(resp.current?.feelslike); // 体感温度 print(resp.current?.airQuality?.pm25); // 空气 PM2.5 print(resp.current?.astro?.sunrise); // 日出时间 print(resp.location?.timezoneId); // Asia/Shanghai // 天气预报(免费版:current + 单日预报,不支持 forecast_days) final fc = await ws.forecast(query: 'Beijing'); for (final day in fc.forecast?.forecastday ?? []) { print('${day.date}: ${day.mintemp}~${day.maxtemp}°C'); } ``` 错误处理(免费版不可用接口 / 限流会抛异常): ```dart try { final resp = await ws.current(query: 'Beijing'); } on WeatherstackException catch (e) { print('${e.code} ${e.type} ${e.info}'); // 如 603 historical_queries_not_supported_on_plan } on DioException catch (e) { print('网络异常: $e'); } ``` ### 内置处理逻辑 - **限流保护**:免费版约 1 次/秒(`x-rate-limit: 1`),客户端内置串行队列 + 1 秒间隔,并发调用自动排队,避免触发错误 106 `rate_limit_reached`。 - **HTTPS 回退 HTTP**:默认走 HTTPS,连接失败(如无 VPN 环境 SSL 被阻断)自动回退 HTTP 重试一次。可通过构造参数 `useHttps` / `retryWithHttp` 控制。 - **业务错误转异常**:接口不可用或套餐不支持时抛 `WeatherstackException`(含 `code` / `type` / `info`)。 ### 配置 API Key 与主机、限流间隔集中在 `weatherstack_config.dart`: ```dart WeatherstackConfig.apiKey; // 免费版 Key WeatherstackConfig.host; // api.weatherstack.com WeatherstackConfig.requestInterval; // 限流间隔,默认 1 秒 ``` - 单例:`WeatherstackClient.instance()`;需要自定义配置时可 `WeatherstackClient(apiKey: ..., useHttps: ...)`。 - 单位枚举:`WsUnit.metric` / `WsUnit.fahrenheit`。 ## 常用命令 ```bash flutter pub get # 安装依赖 flutter run # 运行应用 flutter test # 运行全部测试 flutter analyze # 静态分析 ``` ## 注意事项 - **双 Flutter SDK**:PATH 中是鸿蒙定制版 3.27.5,实际构建请使用 3.32 稳定版 `C:\flutter_windows_3.32.0-stable\flutter`。 - **网络环境**:位于中国大陆,Android 构建依赖国内镜像。不要改动 `android/` 下已配置的镜像源(腾讯 gradle、阿里云 maven)。 - **Android 明文 HTTP**:Android 9+ 默认禁止明文 HTTP,若走 HTTP 回退需在 `AndroidManifest.xml` 的 `` 加 `android:usesCleartextTraffic="true"`。 - **API Key 明文**:`weatherstack_config.dart` 中明文存放,仓库公开前请改为从环境变量 / 安全存储读取。