import type { WithAbortSignal } from "#Source/abort/index.ts" import { generalFetch } from "#Source/request/index.ts" import type { Lang } from "../base.ts" import type { AirQualityIndex, AirQualityMetadata, AirQualityPollutant } from "./shared.ts" /** * @description 调用和风天气空气质量每日预报接口时使用的参数。 * * @example * ;``` * const options: AirDailyForecastOptions = { * baseUrl: "https://abcxyz.qweatherapi.com", * jwt: "eyJhbGciOiJFZERTQSJ9.payload.signature", * latitude: 39.9, * longitude: 116.4, * localTime: true, * lang: "zh-hans", * timeout: 5_000, * } * ``` */ export interface AirDailyForecastOptions extends WithAbortSignal { /** * @description 和风天气 API Base URL,例如 `https://abcxyz.qweatherapi.com`。 */ baseUrl: string /** * @description 用于 `Authorization: Bearer` 请求头的 JWT。 */ jwt: string /** * @description 查询位置的纬度,十进制格式,最多支持小数点后两位。 */ latitude: number | string /** * @description 查询位置的经度,十进制格式,最多支持小数点后两位。 */ longitude: number | string /** * @description 是否返回查询地点的本地时间,`false` 或省略时返回 UTC 时间。 */ localTime?: boolean | undefined /** * @description 返回结果的多语言代码。 */ lang?: Lang | undefined /** * @description 请求超时时间,单位为毫秒。 */ timeout?: number | undefined } /** * @description 空气质量每日预报响应中的单日数据。 * * @example * ;``` * const day: AirDailyForecastResultDay = { * forecastStartTime: "2023-02-14T23:00Z", * forecastEndTime: "2023-02-15T23:00Z", * indexes: [ * { * code: "qaqi", * name: "QAQI", * aqi: 1, * aqiDisplay: "1.0", * level: "1", * category: "Excellent", * color: { * red: 195, * green: 217, * blue: 78, * alpha: 1, * }, * primaryPollutant: { * code: "pm2p5", * name: "PM 2.5", * fullName: "Fine particulate matter (<2.5µm)", * }, * health: { * effect: "No health implications.", * advice: { * generalPopulation: "Enjoy your outdoor activities.", * sensitivePopulation: "Enjoy your outdoor activities.", * }, * }, * }, * ], * pollutants: [ * { * code: "pm2p5", * name: "PM 2.5", * fullName: "Fine particulate matter (<2.5µm)", * concentration: { * value: 11.88, * unit: "μg/m3", * }, * subIndexes: [ * { * code: "qaqi", * aqi: 1, * aqiDisplay: "1", * }, * ], * }, * ], * } * ``` */ export interface AirDailyForecastResultDay { /** * @description 当前预报数据的开始时间。 */ forecastStartTime: string /** * @description 当前预报数据的结束时间。 */ forecastEndTime: string /** * @description 当前日期的空气质量指数列表。 */ indexes: AirQualityIndex[] /** * @description 当前日期的污染物浓度数据列表。 */ pollutants: AirQualityPollutant[] } /** * @description 和风天气空气质量每日预报接口的响应结果。 * * @example * ;``` * const result: AirDailyForecastResult = { * metadata: { * tag: "4b78230843e636a6f910631d94878da73aa980a66abfcf53d35f9c06493a292d", * }, * days: [ * { * forecastStartTime: "2023-02-14T23:00Z", * forecastEndTime: "2023-02-15T23:00Z", * indexes: [ * { * code: "qaqi", * name: "QAQI", * aqi: 1, * aqiDisplay: "1.0", * level: "1", * category: "Excellent", * color: { * red: 195, * green: 217, * blue: 78, * alpha: 1, * }, * primaryPollutant: { * code: "pm2p5", * name: "PM 2.5", * fullName: "Fine particulate matter (<2.5µm)", * }, * health: { * effect: "No health implications.", * advice: { * generalPopulation: "Enjoy your outdoor activities.", * sensitivePopulation: "Enjoy your outdoor activities.", * }, * }, * }, * ], * pollutants: [ * { * code: "pm2p5", * name: "PM 2.5", * fullName: "Fine particulate matter (<2.5µm)", * concentration: { * value: 11.88, * unit: "μg/m3", * }, * subIndexes: [ * { * code: "qaqi", * aqi: 1, * aqiDisplay: "1", * }, * ], * }, * ], * }, * ], * } * ``` */ export interface AirDailyForecastResult { /** * @description 响应元数据。 */ metadata: AirQualityMetadata /** * @description 未来 3 天的空气质量预报数据。 */ days: AirDailyForecastResultDay[] } const internalNormalizeAirDailyForecastCoordinate = ( value: number | string, name: "latitude" | "longitude", ): string => { if (typeof value === "number") { if (Number.isFinite(value) === false) { throw new TypeError(`AirDailyForecast ${name} must be a finite number.`) } return String(value) } const normalized = value.trim() if (normalized === "") { throw new TypeError(`AirDailyForecast ${name} must not be empty.`) } return normalized } const internalValidateAirDailyForecastOptions = ( options: AirDailyForecastOptions, ): { baseUrl: string jwt: string latitude: string longitude: string } => { const baseUrl = options.baseUrl.trim() const jwt = options.jwt.trim() const latitude = internalNormalizeAirDailyForecastCoordinate(options.latitude, "latitude") const longitude = internalNormalizeAirDailyForecastCoordinate(options.longitude, "longitude") if (baseUrl === "") { throw new TypeError("AirDailyForecast baseUrl must not be empty.") } if (jwt === "") { throw new TypeError("AirDailyForecast jwt must not be empty.") } return { baseUrl, jwt, latitude, longitude, } } /** * @description 获取指定经纬度坐标的空气质量每日预报数据。 * * @see {@link https://dev.qweather.com/docs/api/air-quality/air-daily-forecast/} */ export const airDailyForecast = async ( options: AirDailyForecastOptions, ): Promise => { const { baseUrl, jwt, latitude, longitude } = internalValidateAirDailyForecastOptions(options) const { localTime, lang, timeout, abortSignal } = options return await generalFetch< { query: { localTime?: boolean | undefined lang?: Lang | undefined } }, { type: "json" data: AirDailyForecastResult } >({ baseUrl, path: `/airquality/v1/daily/${latitude}/${longitude}`, method: "get", headers: { Authorization: `Bearer ${jwt}`, }, query: { localTime, lang, }, responseType: "json", timeout, abortSignal, }).getJson() }