signalgap9-del/peachtree-dispatch

GitHub: signalgap9-del/peachtree-dispatch

一款全美天气风险感知路线规划系统,帮助驾驶者在多条备选路径中选择天气和道路危险暴露最低的出行方案。

Stars: 0 | Forks: 0

# AtmosPath **感知天气的路线规划,旨在回答大多数导航工具忽略的一个问题:** *现在哪条路线能让我面临最少的天气和道路危险风险?* AtmosPath 在比较备选路线时,不仅参考行程时间,还综合考虑了实时天气风险、官方 NWS 预警以及道路事件暴露程度。司机可以查看分段级别的风险明细(降雨、洪水、大风、高温、冬季路况),将路线保存到带有监控阈值的私有关注列表中,并获得具备配额和使用情况追踪的 SaaS 风格账户层,而所有基础地图操作流程均无需登录即可使用。 [在线预览](https://d23c97ytqgl4xu.cloudfront.net/) | [API 健康状态](https://d23c97ytqgl4xu.cloudfront.net/api/health) | [演示手册](docs/demo-playbook.md) | [更新日志](CHANGELOG.md) English | **[한국어](README.ko.md)** ![AtmosPath 路线对比](https://static.pigsec.cn/wp-content/uploads/repos/cas/e4/e464b32bd0e04daf63187d290be37a863d0e7682c2b501d85bf9ec442172ab8a.png) ## 功能特性 **路线风险对比。** 在任意两个美国城市之间规划行程,获取三个备选方案:最快、天气风险较低、均衡。每条路线都会显示逐段的风险说明,让你了解*为什么*某条走廊比另一条更危险。 **实时危险情报。** 全国天气风险展望、位置级风险评分,以及按危险类型、城市、县或走廊搜索官方预警。数据来自 NWS 和 NOAA;如果数据源不可用,UI 会明确提示,而不是显示虚假数据。 **已保存路线关注列表。** 经过身份验证的用户可以保存路线、设置风险阈值、启用监控、手动刷新风险评分以及查看风险历史记录。所有保存的数据都通过条件写入以所有者范围为隔离存储在 DynamoDB 中。 **SaaS 账户层。** 基于套餐的权限控制(FREE/PRO/TEAM/INTERNAL)、每日使用量计量、已保存资产容量检查、结构化配额错误,以及支持安全重试的幂等键。计费功能特意未接入;设置权限边界是为了让支付功能能在日后顺利集成。 **多停靠点优化。** 由 OR-Tools 支持的 VRP 基础,具备风险加权边缘成本,外加一个 ML 影子工作流,该工作流只有在通过显式的提升关卡后才能影响求解器成本。 **运营透明度。** `/status` 页面显示实时数据源健康状况、前端重试/降级计数、浏览器性能快照以及会话级错误遥测数据。无第三方分析工具。 ## 系统架构 ``` flowchart LR User["Browser"] --> CF["CloudFront
geo restriction, security headers"] CF --> S3["Private S3
React/Vite SPA"] CF --> APIGW["API Gateway
/api/*"] APIGW --> Spring["Spring Boot 3.5
auth, tenants, quotas, saved data"] Spring --> Cognito["Cognito / Google OAuth"] Spring --> DDB["DynamoDB
single-table design"] Spring --> Risk["FastAPI Risk Engine
routing, weather, alerts, VRP"] Risk --> NWS["NWS / NOAA"] Risk --> WZDX["USDOT WZDx / 511 feeds"] Risk --> S3Data["S3 weather artifacts"] ``` 前端是一个 React 19 单页应用(SPA),通过 CloudFront 从私有 S3 提供服务。API 调用通过 API Gateway 路由到两个后端:一个是 Java Spring Boot 服务,负责身份验证、租户上下文、权限控制和持久化用户数据;另一个是 Python FastAPI 服务,负责路线规划、天气风险评分、预警聚合和车辆路径优化。DynamoDB 是运营数据存储(零空闲成本);PostgreSQL/PostGIS 被记录为用于空间连接的扩展路径。 ## 工程决策 本节解释了关键子系统是*如何*工作的,以及*为什么*要这样构建。每一条内容都可以作为技术评审的探讨起点。 ### 风险评分流水线 位置风险不是简单的平均值。评分器根据实时的 NWS/NOAA 数据计算出独立的类别评分(降水、大风、高温、洪水、活动预警),然后取 `max(alert_score, weighted_weather_composite)`。其加权方式如下: ``` score = max( alert_score, precipitation * 0.35 + wind * 0.25 + heat * 0.20 + flood * 0.20 ) ``` 使用 `max` 可确保单一的严重预警(例如龙卷风警告)占据主导地位,即使整体天气看起来很平静。加权综合评分则用于处理常见情况:即没有任何单一预警生效,但综合条件具有危险性的情况。全国风险使用前 20 名预警的平均值,以避免被数百个低严重性咨询预警稀释。 全国端点上的 60 秒内存 TTL 缓存可防止在并发负载下重复调用 NWS API,从而无需增加 Redis 基础设施成本。 验证者:`test_weather_snapshot.py`, `test_hazards.py` ### 路线对比与标记 备选路线来自 OSRM 的 `alternatives=3` 参数,该参数返回几何形态上不同的路径。然后通过对路线途经点进行天气采样,并检查走廊沿线的 NWS 预警几何交集,对每个候选方案进行评分。评分后,备选方案将按持续时间排序并标记为: - **最快**:不考虑风险的最短持续时间。 - **天气风险较低**:非最快备选方案中风险评分最优的路线。 - **均衡**:时间和风险权衡结合下的最优选择。 分段级别的明细将路线划分为天气采样区间,并附加每段的风险解释,以便 UI 可以显示风险在路线上的*具体集中位置*。 验证者:`test_directions.py` ### 防范 SSRF 的出站 HTTP 风险引擎会调用外部提供程序(NWS、Open-Meteo、OSRM、WZDx)。所有出站请求都不信任任意 URL,而是通过 `outbound_http.py` 进行,该脚本强制执行: 1. **主机白名单**:仅允许预先批准的域名(api.weather.gov、router.project-osrm.org 等)以及显式的环境变量扩展列表。 2. **强制 HTTPS**:禁止与公共主机进行明文 HTTP 通信。 3. **阻止重定向**:自定义的 `HTTPRedirectHandler` 会抛出异常而不是跟随重定向,防止通过开放重定向绕过白名单。 4. **DNS 解析验证**:在连接之前,解析主机名并检查其是否属于回环地址、本地链路、私有地址、保留地址和多播范围。这可以阻止解析到 `169.254.169.254`(云元数据)或内部 IP 的 DNS 重绑定攻击。 5. **剥离凭据**:拒绝包含内嵌 `user:pass@` 的 URL。 本地开发出口(`localhost`)由显式的 `ATMOSPATH_ALLOW_LOCAL_OUTBOUND=true` 标志进行控制。 验证者:`test_outbound_http.py` ### 速率限制 Spring Platform API 使用一个 servlet 过滤器(`RateLimitFilter`),它根据方法 + 路径模式将请求分类到不同的桶中: | 桶 | 应用于 | 默认限制 | | --- | --- | --- | | `public-risk-read` | GET /risk/national, weather-snapshot, weather-raster | 每分钟可配置 | | `place-search` | GET /places/search | mutation 级别限制 | | `route-risk-mutation` | POST /directions, /risk/location | mutation 级别限制 | | `authenticated-me` | /me/** | 独立的已验证用户限制 | 密钥为 `bucket:method:path:clientIP`。默认存储是内存中的固定窗口(零基础设施成本);设置 `RATE_LIMIT_STORE=redis` 可切换为基于 Redis 的计数器,适用于多实例部署。每个决策都会导出 Micrometer 计数器(带有 `bucket` 和 `outcome` 标签的 `atmospath.rate_limit.requests`),并返回标准的 `X-RateLimit-*` 标头以及带有 `Retry-After` 的结构化 `429` 响应体。 验证者:`RateLimitFilterTests.java`, `InMemoryRateLimitRepositoryTests.java` ### 幂等性 已保存路线和已保存地点的变更操作接受 `Idempotency-Key` 标头。其实现方式为: 1. 验证密钥格式(1-128 个字符,`[A-Za-z0-9._:-]`)。 2. 在存储之前使用 SHA-256 对其进行哈希处理,因此原始客户端密钥永远不会持久化存储在 DynamoDB 中。 3. 存储作用域限定为 `tenantId + operation` 的哈希,防止跨租户的密钥冲突。 4. 重试时,返回先前创建的资源 ID,而不是创建重复项。 这使得前端可以安全地重试网络故障,而不会创建虚假的已保存路线。幂等命中情况通过 `atmospath.saved_route.commands` 指标进行追踪。 验证者:`IdempotencyServiceTests.java`, `DynamoDbIdempotencyRepositoryTests.java` ### 服务层模式 Platform API 中的控制器是轻量级的 HTTP 边界:解析请求,提取租户上下文,委托给服务。所有业务逻辑都位于 `SavedRouteService` 中,它负责协调: 1. 幂等性检查(如果密钥匹配,则返回现有资源)。 2. 权限容量检查(如果超出套餐配额则拒绝)。 3. 领域对象构建和持久化。 4. 风险观测记录(用于未来的 ML 训练数据)。 5. 幂等密钥存储。 6. 命令指标发射。 这种顺序很重要:在持久化*之前*检查配额,因此被拒绝的请求永远不会触及 DynamoDB。该服务采用 `@ConditionalOnProperty(atmospath.auth.enabled=true)` 设置,因此没有 Cognito 的本地开发路径仍然可以正常工作。 验证者:`SavedRouteServiceTests.java` ### ML 服务关卡(故障关闭设计) VRP 延迟模型可以影响路线优化成本,但前提是必须通过四个独立的关卡: 1. **训练关卡**:对模型进行训练,并针对平均延迟基准进行回测。发布关卡会检查 MAE(平均绝对误差)的改进情况。 2. **提升关卡**:CLI 工具写入一个带有 `served_to_users=true` 的新制品。影子制品保持为 `false`。 3. **运行时关卡**:环境必须设置 `VRP_ML_WORKFLOW_MODE=SERVING_ENABLED` 和 `VRP_ML_ALLOW_SERVED_COST=true`。 4. **请求关卡**:单个请求必须通过 `useMlServedCost=true` 验证。 任何缺失的条件都会故障关闭为基于规则的成本。应用的延迟有额外的上限限制(`mlMaxDelaySeconds`)、进行加权(`mlDelayWeight=0.35`),并在低于置信阈值时被阻止。制品是纯 JSON 格式(系数、特征名称、指标),而不是 pickle/joblib 文件,因此它们是可检查的,并且不能在加载时执行任意代码。 验证者:`test_ml_workflow.py` ### 前端弹性 Web 应用无需 service worker 或外部库即可处理不稳定连接: - **安全重试**:暂时性故障(5xx,网络错误)会进行带退避机制的重试;4xx 错误绝不重试。 - **陈旧缓存降级**:公共风险响应缓存在 `sessionStorage` 中(上限为 512 kB)。当最新获取失败时,UI 会提供缓存版本,并带有可见的“陈旧数据”指示器和时间戳。 - **连接感知**:`navigator.onLine` 和 `NetworkInformation.effectiveType` 驱动一个用于显示离线/慢速网络状态横幅。 - **无第三方遥测**:重试计数、降级事件和错误详细信息存储在会话范围的内存中,并在 `/status` 公开。不会有任何数据离开浏览器。 验证者:`resilience.spec.ts`, `security-hardening.spec.ts` ### DynamoDB 单表设计 预览版使用带有组合键的单表: | 访问模式 | PK | SK | | --- | --- | --- | | 用户个人资料 | `USER#{userId}` | `PROFILE` | | 列出已保存地点 | `USER#{userId}` | `SAVED_PLACE#{id}` (begins_with) | | 列出已保存路线 | `USER#{userId}` | `SAVED_ROUTE#{id}` (begins_with) | | 每日使用计数器 | `TENANT#{tenantId}` | `USAGE#{date}#{feature}` | 在预览版中选择 DynamoDB 而不是 Postgres 的原因:零空闲成本(按需计费)、针对所有者范围的查询实现个位数毫秒级的读取,并且条件写入无需事务协调器即可提供乐观并发控制。其权衡是没有空间查询,这就是为什么将 PostGIS 记录为扩展路径的原因(见 ADR-006)。 验证者:`test_dynamodb_repository.py` ### VRP 成本模型 多停靠点路线优化构建了一个经过风险调整的成本矩阵。对于每条边: ``` adjusted_cost = base_duration * duration_weight + weather_risk * weather_weight * 6 + traffic_risk * traffic_weight * 6 + flood_risk * flood_weight * 8 + alert_risk * alert_weight * 10 + (distance_km * distance_weight) ``` 乘数(6, 6, 8, 10)将 0-100 的风险评分转换为惩罚秒数。洪水和预警风险具有更高的乘数,因为它们与道路无法通行有关,而不仅仅是不适。缺失的矩阵边将受到 24 小时的惩罚,使其实际上无法通行。ML 影子模型在激活时,会在此基础成本之上增加一个受置信度控制的延迟。 验证者:`test_vrp_route_engine.py`, `test_road_event_edge_risk.py` ## 技术栈 | 层级 | 技术 | | --- | --- | | 前端 | React 19, TypeScript, Vite, MapLibre GL, Lucide icons, Playwright E2E, axe-core a11y | | Platform API | Java 21, Spring Boot 3.5, Spring Security (OAuth2 Resource Server), AWS SDK v2 | | 风险引擎 | Python 3.12,API, Pydantic, OR-Tools, scikit-learn (ML workflow) | | 数据 | DynamoDB (单表), S3 (天气制品), 可选 PostGIS | | 基础设施 | CloudFront, API Gateway, Lambda, Cognito, CloudWatch, Terraform | | CI/CD | GitHub Actions (OIDC, 无长期密钥), Maven, pytest, Playwright, bundle budget checks | ## 屏幕截图 使用 Playwright 从运行中的应用截取(非模型图)。使用 `npm run screenshots:release --prefix web` 重新生成。 | 首页 | 仪表盘 | | --- | --- | | ![首页](https://static.pigsec.cn/wp-content/uploads/repos/cas/2f/2f2c9b07f7fc6696e81dc77677e19d762f076b2e534f90e5fe6dc19e5463be6d.png) | ![仪表盘](https://static.pigsec.cn/wp-content/uploads/repos/cas/ab/ab01dda5aa8d07b7e45f0da6824b342a093f9235b971a24671ee0a0a8f2a2ab6.png) | | 路线对比 | 状态 | | --- | --- | | ![路线](https://static.pigsec.cn/wp-content/uploads/repos/cas/e4/e464b32bd0e04daf63187d290be37a863d0e7682c2b501d85bf9ec442172ab8a.png) | ![状态](https://static.pigsec.cn/wp-content/uploads/repos/cas/49/491c61abd08d9893468091a274d3c937c21edb02145f38725f7898ac023e5711.png) | | 移动端 | | --- | | ![移动端](https://static.pigsec.cn/wp-content/uploads/repos/cas/ab/ab7851b8eb39da62a09a8b3e82d7e0edd4cd347392355037b8ef370a6eee95bd.png) | ## 快速开始 ### 前置条件 Docker(用于全栈),或单独安装:Node 20+,Python 3.12+,Java 21(Windows 环境下打包在 `.tools/` 中)。 ### 全栈 ``` docker compose up --build ``` | 服务 | URL | | --- | --- | | Web 应用 | http://localhost:5173 | | 风险引擎文档 | http://localhost:8000/docs | | Platform API 健康检查 | http://localhost:8080/health | ### 仅前端 ``` npm install --prefix web npm run dev --prefix web # local dev server npm run build --prefix web # production build npm run test:e2e --prefix web # Playwright E2E (28 tests) ``` ### Platform API (Spring Boot) 在 Windows 上无需系统级安装 Java/Maven;仓库已在 `.tools/` 中打包了这两者。 ``` cd services/platform-api ../../scripts/mvn.ps1 --batch-mode test # 53 tests ``` ### 风险引擎 ``` $env:PYTHONPATH = 'services/api' python -m pytest services/api/tests -q # 78 tests ``` ## 安全模型 身份验证使用 Cognito JWT(已记录 Google OAuth 联合登录路径)。公共地图端点无需登录即可工作,但在服务器端受速率限制;所有 `/me/**` 端点都需要有效的 token。 关键加固决策: - 所有者范围的 DynamoDB 分区键,具有条件写入/删除功能;无法进行跨租户读取。 - 变更操作的幂等密钥,以租户范围的哈希格式存储(绝不存储原始客户端密钥)。 - Spring API 上的固定窗口速率限制(默认内存存储,可通过 `RATE_LIMIT_STORE=redis` 选择使用 Redis)。 - CloudFront 源验证(`X-Origin-Verify`),CSP,frame-deny,referrer policy。 - 出站 HTTP 调用被限制在白名单内,并强制执行 HTTPS,阻止重定向,拒绝访问元数据/localhost/私有网络。 - 部署使用 GitHub Actions OIDC;任何地方都不存在长期有效的 AWS 访问密钥。 - 前端 E2E 涵盖了地图标记上的 XSS 加固、身份验证不可用时的消息提示以及陈旧数据降级处理。 ## ML 与优化 VRP 延迟模型默认作为**影子工作流**运行。除非所有关卡都通过,否则基于规则的风险评分是权威的: 1. 模型已经过训练和回测(MAE、RMSE、p95 误差与基准对比)。 2. 制品通过发布关卡,并通过 CLI 提升状态(`served_to_users=true`)。 3. 运行时环境启用服务功能(`VRP_ML_WORKFLOW_MODE=SERVING_ENABLED`, `VRP_ML_ALLOW_SERVED_COST=true`)。 4. 单个请求选择启用(`useMlServedCost=true`)。 任何未通过的关卡、缺失的标志或低置信度预测都将故障关闭至基于规则的成本。制品是纯 JSON 格式(系数、指标、元数据),而不是 pickle 二进制大对象。 ``` # 训练 python services/api/scripts/train_vrp_delay_model.py --input --output --model-version v1 # Promote (需要通过 release gate) python services/api/scripts/promote_vrp_delay_model.py --input --output # 演示端到端 served-cost 流程 python services/api/scripts/run_vrp_served_cost_demo.py --artifact-dir tmp/demo-vrp-ml --model-version demo-v1 ``` ## 测试策略与覆盖率 ### 覆盖率摘要 | 层级 | 测试用例数 | 行覆盖率 | 工具 | | --- | --- | --- | --- | | Python 风险引擎 | 78 | **80%** | pytest-cov | | Spring Platform API | 53 (全部通过) | 未测量 | JUnit 5 (尚未配置 JaCoCo) | | Playwright E2E | 28 | n/a | Playwright | ### 核心模块覆盖率 (风险引擎) | 模块 | 覆盖率 | 备注 | | --- | --- | --- | | `vrp/ml/shadow_cost_model.py` | 98% | ML 关卡逻辑已充分测试 | | `vrp/ortools_solver.py` | 98% | 求解器包装器、约束设置 | | `vrp/cost_model.py` | 96% | 风险加权边缘成本公式 | | `outbound_http.py` | 85% | SSRF 强制执行路径 | | `main.py` | 82% | FastAPI 应用装配、中间件 | | `directions.py` | 54% | 外部 OSRM 调用路径难以进行单元测试 | | `risk.py` | 28% | NWS/NOAA 获取路径;评分数学逻辑通过 `test_weather_snapshot.py` 覆盖 | ### 分层测试理念 **单元测试**覆盖了无论基础设施如何都必须正确的逻辑:风险评分公式和权重、VRP 成本模型算术、幂等键哈希和格式验证、速率限制桶分类以及 ML 关卡故障关闭行为。这些测试在几毫秒内运行,无需网络或数据库。 **集成测试**验证了编排顺序和仓库契约:DynamoDB 单表访问模式(条件写入、begins_with 查询)、服务层排序(在持久化之前检查配额)以及幂等重试返回原始资源 ID。 **E2E 测试**(Playwright,无头 Chromium)测试了接近部署形态的体验:地图标记上的 XSS 加固、带有可见指示器的陈旧缓存降级、身份验证不可用时的 UX 消息提示、网络重试行为以及可访问性。这些测试针对带有模拟 API 层的 Vite 开发服务器运行。 **压力测试**在本地检测性能回归,无需产生云成本:180 个请求,并发数为 8,断言零故障和 p95 基准(缓存端点 < 500 ms,多停靠点规划约 3.2 秒)。 ### 已知差距 - `risk.py` (28%):这个较低的数字反映了需要实时 API 的外部 HTTP 获取路径(NWS,NOAA)。评分数学逻辑本身已通过 `test_weather_snapshot.py` 和 `test_hazards.py` 覆盖。 - `directions.py` (54%):OSRM 调用路径需要运行中的路由器;已测试契约形状,未测试实时路由。 - Spring Platform API 尚未有 JaCoCo 覆盖率报告。所有 53 个测试均通过;覆盖率测量是计划中的 CI 补充功能。 - E2E 测试模拟了后端;针对已部署 Lambda 的真正端到端测试是手动步骤(参见演示手册)。 ### 运行测试套件 ``` # Risk Engine (Python) - 78 个测试,约 4 秒 $env:PYTHONPATH = 'services/api' python -m pytest services/api/tests -q --cov=services/api --cov-report=term-missing # Platform API (Spring) - 53 个测试,约 20 秒 cd services/platform-api ../../scripts/mvn.ps1 --batch-mode test # E2E (Playwright) - 28 个测试,需要 dev server npm run test:e2e --prefix web # Stress (in-process,无 cloud cost) python perf/local_api_stress.py --requests 180 --concurrency 8 ``` ### 其他验证 | 检查项 | 结果 | | --- | --- | | 前端 lint + 构建 + bundle 预算 | 通过 | | 设计 lint | 0 个错误,0 个警告 | | 依赖审计 | 0 个高危漏洞 | | ML 服务成本演示 | 通过 | **Bundle 预算**(在 CI 中强制执行):初始 JS 98.8 kB gz / 上限 180 kB,MapLibre vendor 278 kB gz / 上限 320 kB,CSS 21.8 kB gz / 上限 90 kB。 ## 数据模型 预览部署的 DynamoDB 单表设计: | PK | SK | 用途 | | --- | --- | --- | | `USER#{userId}` | `PROFILE` | 用户个人资料 | | `USER#{userId}` | `SAVED_PLACE#{id}` | 已保存地点 | | `USER#{userId}` | `SAVED_ROUTE#{id}` | 带有监控设置的已保存路线 | | `TENANT#{tenantId}` | `USAGE#{date}#{feature}` | 每日使用计数器 | PostGIS 扩展(已记录,默认未部署):具有 GiST 索引的空间列、基于所有者的 RLS 策略、路线暴露观测以及未来的租户/工作区表。请参阅 [docs/data-model.md](docs/data-model.md) 和 [docs/relational-data-model.md](docs/relational-data-model.md)。 ## 部署 在 AWS `us-east-1` 上 Serverless 优先:CloudFront + 私有 S3 用于 SPA,API Gateway + Lambda 用于后端,DynamoDB 按需用于持久化,Cognito 用于身份验证。默认预览版中不包含 Kubernetes、Aurora 或托管 Redis。 基础设施由 Terraform 管理。CI/CD 通过带有 OIDC 的 GitHub Actions 运行(无静态凭据)。CloudFront 限制地理位置为美国和韩国。所有资源均标记为 `Project=awsresumeproject`, `ManagedBy=IaC`, `Environment=dev`。 完整部署文档:[docs/deployment.md](docs/deployment.md)。成本模型:[docs/cost-model.md](docs/cost-model.md)。 ## 项目状态 **目前已就绪:** 天气路线对比、预警搜索、已保存的关注列表、SaaS 配额、操作状态页面、结构化错误契约、各层的全面测试覆盖率、bundle 预算、本地压力测试工具。 **尚待完成:** 在部署环境中的 Google OAuth 密钥、针对广泛公共流量的 WAF 规则、异步多停靠点作业执行、达到生产节奏的 HRRR/MRMS 栅格摄取、合成监控。 发布版本:`v0.1.0-preview`(2026 年 7 月)。路线图见 [CHANGELOG.md](CHANGELOG.md)。 ## 文档 - [架构概述](docs/architecture.md) - [演示手册](docs/demo-playbook.md)(包含 2 分钟面试脚本) - [研究与安全基础](docs/architecture/research-and-security-basis.md) - [应用弹性](docs/architecture/application-resilience.md) - [后端生产环境加固](docs/architecture/backend-production-hardening.md) - [天气风险流水线](docs/architecture/weather-risk.md) - [VRP 路径引擎基础](docs/architecture/route-engine-vrp-foundation.md) - [ML 影子模型](docs/architecture/vrp-ml-shadow-model.md) - [WZDx 道路事件源](docs/architecture/road-events-wzdx-feeds.md) - [SaaS 加固检查清单](docs/saas-production-hardening-checklist.md) - [部署指南](docs/deployment.md) - [Google OAuth 设置](docs/google-auth.md) - [操作手册](docs/runbooks/) - [架构决策记录 (ADRs)](docs/adr/)
标签:AWS, DPI, React, Spring Boot, Syscalls, 气象导航