ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

天气系统初版落地:从城市代码到结构化数据的主链路设计

天气系统初版落地:从城市代码到结构化数据的主链路设计 做天气系统初版我先给一个结论初版不需要功能多全核心是让“城市代码 - 原始天气数据 - 统一业务结构 - 页面或接口输出”这条链路完整跑一遍并且每一步失败都能被看见。很多人一开始就去画地图、做每天温度趋势、搞城市搜索最后真实数据接口一接发现字段名对不上、城市编码不一样、网络超时会把整个进程拖死。这篇文章记录的是我自己落地一个天气系统初版的过程适合第一次做数据展示类小系统的开发者也适合想把天气模块接入现有项目的人。另外一个判断是初版系统的难点不是某个框架没有学会而是你会同时遇到数据源、字段解析、缓存、前端展示和异常处理的问题。如果不把每一层拆开任何报错都会让你在整个项目里翻找半天。下面我按实际落地顺序拆一遍。1. 初版先想清楚你做的是“看天气”还是“接得通天气”开始写代码前先明确“天气系统初版”到底要证明什么。一种是纯界面演示。页面能展示天气信息数据是写死的用户不关心接口是否真实。这种情况重点在视觉和交互数据链路可以先不做。另一种是数据应用。系统必须能从真实数据源拿到天气并转换成自己的业务结构。这种情况即使页面简陋也要优先保证主链路正确。我更建议把初版定位成后一种。原因是如果只是写死数据的静态页面后面接真实数据时几乎要重写。反过来如果先做出一个能拿到数据、能输出结果、能处理失败的最小系统前端做到什么程度只是时间问题。如果一个项目连“城市代码变了之后页面内容会不会跟着变”都做不到那它只是一个带天气样式的前端稿还不是系统。1.1 初版最容易跑偏的功能我见过不少天气初版项目第一版就包含大量外围功能城市选择下拉框地图展示未来七天趋势曲线多个天气源切换用户收藏历史城市这些功能并不是不能做但它们都不是主链路的必要部分。初版把时间花在这些地方会带来两个后果真实接口还没调通前端只能靠写死数据继续真实接口字段和前端画好的图表不匹配最后还是回头改结构。初版最该做的是先用最简单的界面或命令行确认数据链路完整再逐步往外加展示能力。哪怕只是一个终端输出只要它能针对不同城市返回统一结构就已经完成了七成以上的验证工作。1.2 我给初版定的四个验收动作如果你不确定做到什么程度才算完可以参考下面这四条输入一个城市代码能得到一份结构化天气数据。同一次运行中只改城市代码其他逻辑不用改。城市不存在、接口超时、配置错误时程序能给出明确提示而不是堆一段难以理解的 Traceback。运行记录里有时间、城市、成功或失败原因方便回头排查。这四个动作对应的是接入、解析、异常、日志恰好是天气系统的最小骨架。页面和友好接口可以后面再加但四件事必须在初版完成。1.3 不要过早引入数据库天气查询初版不一定需要 MySQL 或 Redis。有些设计会在第一版就建城市表、天气表每次查询先写库再读库。这样做不是错但会明显增加工作量。更轻量的做法是把每次查询结果保存成独立 JSON 文件按城市代码命名。目录结构可以非常清晰data/ 101010100.json 101020100.json需要更新就重新请求并覆盖文件需要读取就按城市代码加载文件。这种方式对初版够用也方便人工检查。等到系统有了多用户、历史趋势、频繁并发查询时再考虑数据库也不迟。换句话说初版应该先解决“数据能不能拿到、结构是否一致、失败是否可见”而不是提前押注未来需要什么存储方案。这是我在天气系统初版里最先定下的原则。2. 天气系统的三层结构接入、解析、展示分开很多天气项目的代码到最后会变成一个“大函数”页面里直接请求第三方接口返回后直接塞给前端。初看很快但后面每次修 bug 都要动整块代码。更稳妥的做法是用三层结构接入层负责向数据源发请求拿到原始 JSON。解析层把原始字段转换成业务字段比如把temp转成temperature同时统一单位。展示层读取业务字段后输出到终端、接口或页面。这样划分的直接收益是换数据源的时候只改接入层页面不用动字段升级的时候只改解析层请求逻辑不用动。前端要新增字段时也只依赖统一的业务结构而不是依赖第三方字段名。2.1 为什么 UI 不能直接读第三方字段第三方天气接口的字段命名差异很大。同一个意思可能叫temp、temperature也可能叫t。如果页面直接写result.temp一旦上游调整字段名前端就会静默出现空值。单位问题更隐蔽。有的接口默认摄氏度有的默认华氏度有的按城市区域切换。初版如果不做转换层以后做历史数据或跨城市对比时会把不同单位的数据混在一起等发现时已经很难纠正。所以在解析层统一输出temperature、description、updated_at这类业务字段是最值得多花几分钟做的事。2.2 初版技术栈怎么选天气系统初版不涉及复杂算法选型标准只有一个写起来顺手、依赖少、方便排错。下面用 Python 做演示原因是 Python 处理 JSON 很方便使用标准库或 requests 都能完成请求。如果你更熟悉 Node.js 或 Java也没有问题。架构思路不变接入、解析、展示分开。初版不需要引入重量级微服务框架能跑通一个主流程即可。我的本地目录很轻weather_system/ main.py data/main.py中包含样例数据和三个函数fetch_raw、parse_weather、run。展示层先用命令行输出 JSON。后续如果有网页需要可以把这个 JSON 作为中间结果交给新服务或静态页面。2.3 没有真实天气 key 时先不要等开发初版时很可能还没申请到数据源权限。此时不要干等 key先把业务代码写出来。你可以准备一份样例 JSON模拟第三方返回然后让fetch_raw暂时返回这份样例数据。命令行、日志、异常处理都能先跑起来。等拿到真实接口后你只需要把fetch_raw内部从“读样例”换成“发网络请求”其他代码基本不用改。这个顺序适合从零入手的人先保证自己代码正确再接入外部依赖排查范围会小很多。3. 从零跑通第一版核心代码和参数说明我们以命令行版本为例输入是城市代码输出是标准化天气 JSON。这套流程跑通后再考虑页面和其他入口。3.1 准备一份样例 JSON假设某个天气服务返回这样的原始结构{ city_code: 101010100, city: 北京, temp_c: 26, humidity: 42, description: 晴, update_time: 2025-01-01 12:00:00 }这里的temp_c只是示意不代表所有服务都用这个字段。实际接数据时字段名以服务商当前文档为准。在main.py里先保存样例数据MOCK_DATA { 101010100: { city_code: 101010100, city: 北京, temp_c: 26, humidity: 42, description: 晴, update_time: 2025-01-01 12:00:00 } }3.2 接入层只负责返回原始数据写一个fetch_raw(city_code)它的职责只有一个根据城市代码返回原始数据。暂时从MOCK_DATA里取后面替换成真实接口时其他开发者看到这个函数名就知道该从哪里动手。class CityNotFoundError(Exception): pass def fetch_raw(city_code: str) - dict: raw MOCK_DATA.get(city_code) if raw is None: raise CityNotFoundError(f未知城市代码: {city_code}) return raw这里的关键点是抛出自定义异常而不是直接返回None。这样主流程可以根据业务提示用户而不是让上层代码因为拿到空值而报出莫名其妙的问题。3.3 解析层统一业务字段解析层负责把原始字段整理成下游真正关心的字段def parse_weather(raw: dict) - dict: return { city_code: raw[city_code], city: raw[city], temperature: raw[temp_c], humidity: raw[humidity], description: raw[description], updated_at: raw[update_time], }代码很短但它是一个稳定出口。后续如果上游把temp_c改成temperature_c你只需要改解析层不需要去翻页面和接口。3.4 主流程和命令行入口最后加一个run函数和入口import json import sys def run(city_code: str) - dict: raw fetch_raw(city_code) return parse_weather(raw) if __name__ __main__: city_code sys.argv[1] if len(sys.argv) 1 else 101010100 try: result run(city_code) print(json.dumps(result, ensure_asciiFalse, indent2)) except CityNotFoundError as e: print(f[错误] {e}) sys.exit(1)在项目目录执行python main.py 101010100样例数据存在且字段匹配时输出应该是一个格式化的 JSON。看到这段输出就说明“输入城市代码 - 原始数据 - 解析 - 输出”这条主链路已经通了。3.5 真实接口替换时的接入层写法真实接入时只需要替换fetch_raw内部逻辑。以某个需要 API Token 的 HTTP 接口为例代码示意如下import os import requests WEATHER_API_URL os.getenv(WEATHER_API_URL, ) WEATHER_API_TOKEN os.getenv(WEATHER_API_TOKEN, ) def fetch_raw(city_code: str) - dict: if not WEATHER_API_URL or not WEATHER_API_TOKEN: raise Runtime
返回列表