当前位置: 首页 > news >正文

山海万灵 HarmonyOS 文化知识实战(18):Spring Boot 六服务的业务闭环与健康回读

一次探索会同时触碰神兽内容、地域进度、展厅陈列、护照印章和数字馆长。若这些数据只靠页面临时拼接,收藏成功、首页继续探索和馆长讲解很容易各自演化,最终出现“内容已更新、进度没更新”或“同一神兽在不同页面名称不一致”的问题。

山海万灵将这条链路拆为内容、博物馆、世界、用户、AI 与 CMS 六个 Spring Boot 服务。拆分的目标不是把接口数量做大,而是让每类业务数据有唯一归属,让调用失败可以被识别、回退和恢复。

六个服务各自守住什么

服务领域职责对外结果
内容服务神兽档案、首页投影、来源与资产图鉴、详情、首页继续探索所需的内容投影
世界服务地域目录与神兽归属地域卡片、地域详情和关联神兽集合
博物馆服务展厅、主题与展品归属展厅卡片、展厅详情和展品集合
用户服务发现记录、护照印章、成长概览幂等的发现结果与可回读的用户概览
AI 服务馆长讲解、推荐与语音编排带来源节点和降级状态的讲解结果
CMS 服务内容审核、版本、发布任务与审计可追踪的审核流、发布任务和回滚入口

边缘网关只接受白名单路由并做首页聚合;它不拥有内容、进度或审核数据。这样一来,端侧只消费统一响应,不需要知道某条信息来自哪张表、缓存还是模型 Provider。

首页投影与用户进度如何合并

内容服务先提供稳定的首页投影:当前地域、展厅、推荐神兽与提示语。用户服务负责把发现记录和护照印章映射为概览,网关再把两者合并为端侧需要的首页数据。内容与进度不共享写模型,减少“更新一处、另一处遗漏”的风险。

@GetMapping("/bootstrap") public ApiResponse<HomeBootstrap> bootstrap() { HomeProjection projection = catalogService.homeProjection(); Beast featured = catalogService.beast(projection.featuredBeastId()) .orElseThrow(() -> new IllegalStateException( "home projection references an unpublished beast")); return ApiResponse.success(new HomeBootstrap( new ContinueExplore(region, hall, projection.notice()), List.of(beast), List.of(hall), new ProfileOverview(List.of(), List.of()))); }

这里的关键约束是:首页投影引用的神兽必须处于可读取状态。引用不存在或尚未公开时,服务直接拒绝不完整投影,而不是让端侧拿到半截数据再猜测如何展示。

发现事件为什么要返回完整概览

用户服务把“发现神兽”作为明确命令处理。写入完成后,响应同时带回本次发现结果、是否首次创建以及最新概览,端侧不必靠乐观累加来猜测经验值或发现数量。

@PostMapping("/collection/discoveries") public ApiResponse<DiscoveryResult> createDiscovery( @RequestBody DiscoveryRequest request) { UserProgressApplicationService.DiscoveryResult result = progressService.createDiscovery(request.beastId(), request.sourceScene()); return ApiResponse.success(new DiscoveryResult( toDiscovery(result.discovery()), result.created(), toProfileOverview(result.overview()))); }

同一个发现动作再次到达时,业务层保持既有记录并返回created=false。调用方可以安全重试,概览也不会因为网络重放而多加一条发现或重复发章。

地域、展厅与图鉴不通过共享表耦合

地域服务回答“某个区域有哪些神兽、进度如何”;博物馆服务回答“某个展厅展示什么、属于哪个地域”;内容服务回答“神兽本身有哪些可读内容”。三类查询通过稳定 ID 关联,而不是让任一服务跨库读写对方的数据。

@GetMapping("/{regionId}") public ApiResponse<RegionCard> detail(@PathVariable String regionId) { return catalogService.region(regionId) .map(item -> ApiResponse.success(card(item))) .orElseGet(() -> ApiResponse.failed( "WORLD.REGION_NOT_FOUND", "region not found or unpublished", "")); }

这个分工让内容扩充与地域规则演进可以独立发布。某个地域尚未准备好时,调用方拿到的是明确错误码;不会把空数组误当成“已经探索完成”。

AI 与 CMS 放在主链路的哪一侧

AI 服务只接收节点类型、节点标识和场景,输出讲解内容、来源节点、推荐关系、缓存命中与降级状态。模型地址、密钥和 Provider 选择都留在服务端;端侧不会直连模型。CMS 服务则管理审核、版本比对、发布任务、下线与回滚,把“可阅读内容”与“可编辑草稿”分开。

场景处理方式保护的结果
AI Provider 超时或输出不合格返回结构化降级结果并保留安全状态页面仍可说明当前节点,不把异常文本写入缓存
内容版本未通过审核CMS 不创建可执行发布结果未确认资料不会混入公开目录
下游服务不可达网关返回可识别的不可用结果,端侧进入既有本地回退不把旧缓存伪装成最新远程数据
重复发现请求用户服务返回既有发现与最新概览发现数量、经验和印章不重复增长

六服务健康回读

本机集成环境启动后,用户、内容、博物馆、世界、AI 与 CMS 六个服务的健康接口均返回 HTTP 200。该回读同时确认了服务进程能够连接本地依赖并完成各自的启动初始化。

健康检查只回答“进程是否已经具备服务能力”,不能替代业务接口的验收。因此网关把健康结果作为路由前的可观测信号,而把目录读取、发现写入和审核发布留在各自的业务合同中。这样当一个服务尚未就绪时,运维能看到准确的服务名;当服务已就绪但业务数据不满足条件时,调用方仍能收到领域错误码,而不是被一个笼统的 500 掩盖。

record ServiceHealth(String service, boolean ready, String detail) {} List<ServiceHealth> collectHealth(List<HealthClient> clients) { return clients.stream() .map(client -> client.readHealth() .map(message -> new ServiceHealth(client.name(), true, message)) .orElseGet(() -> new ServiceHealth(client.name(), false, "unavailable"))) .toList(); } boolean allReady(List<ServiceHealth> results) { return results.stream().allMatch(ServiceHealth::ready); }

六个服务采用相同的响应封装,但各自保留独立的路由前缀。下面是 AI 与 CMS 两个真实端点的最小实现;其他领域服务沿用同一契约,避免网关和运维脚本为每个服务维护不同的健康响应格式。

@RestController @RequestMapping("/api/v1/ai") public class AiHealthController { @GetMapping("/health") public ApiResponse<String> health() { return ApiResponse.success("ai-service gateway ready"); } } @RestController @RequestMapping("/api/v1/cms") public class CmsHealthController { @GetMapping("/health") public ApiResponse<String> health() { return ApiResponse.success("cms-service ready"); } }

健康合同也有对应的 Web 层测试。测试不依赖浏览器页面,而是直接校验 HTTP 状态与响应中的就绪文本;当路由、响应包装或启动配置被改动时,回归会立即指出受影响的服务。

@WebMvcTest({CmsAdminController.class, CmsHealthController.class}) class CmsAdminControllerWebTest { @Autowired private MockMvc mockMvc; @Test void exposesCmsHealthContract() throws Exception { mockMvc.perform(get("/api/v1/cms/health")) .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value(true)) .andExpect(jsonPath("$.data").value("cms-service ready")); } } @WebMvcTest({RegionCatalogController.class, WorldHealthController.class}) class RegionCatalogControllerWebTest { @Autowired private MockMvc mockMvc; @Test void exposesTheWorldServiceHealthContract() throws Exception { mockMvc.perform(get("/api/v1/world/health")) .andExpect(status().isOk()) .andExpect(jsonPath("$.data").value("world-service ready")); } }
回读层级请求对象成功时的含义失败后的处理
服务健康六个/health端点对应 Spring Boot 进程已完成启动标记该领域不可用,不把请求转成空数据
业务读取图鉴、地域、展厅目录返回的内容满足各自查询合同保留错误码和可恢复入口
业务写入发现记录、CMS 审核或发布任务持久化结果可由后续查询回读依靠幂等键或任务状态避免重复提交

对于端侧而言,这种区分直接影响提示方式。健康检查失败时,应用应保留已有可读内容并标记远端能力暂不可用;业务查询返回“未发布”或“地域不存在”时,则应展示与该领域对应的空态或错误说明。两种情况都不能被简单合并成加载失败,否则用户既无法判断是否可以重试,也无法知道是否需要切换探索目标。服务边界清晰后,客户端可以把恢复入口放在真正能够恢复的层级,而不是让每个页面各自猜测网络和数据状态。

验证时可以按以下顺序观察:先读取六个健康结果;再请求地域、展厅和图鉴目录;随后提交一次发现并确认第二次提交不新增记录;最后模拟某个上游不可达,确认网关返回可识别错误且端侧回退不白屏。每一步都对应一个明确服务边界,出现异常时能够定位到负责的领域,而不是在页面层盲目重试。

结语

六服务的价值在于把内容可信度、用户进度、世界关系、展厅陈列、AI 讲解和运营发布各自放在可测试、可恢复的边界内。端侧仍以统一响应消费数据,服务端则通过持久化、错误码、审核流与健康检查维持主链路的可观测性。

端侧接入网络与数据状态时,可结合 HarmonyOS 应用开发概览 规划页面状态与服务合同的映射。

Spring Boot 的配置、健康与生产部署可以参考 Spring Boot Reference Documentation。

http://www.jsqmd.com/news/1343001/

相关文章:

  • 2026 年黄埔诚信的公路波形护栏源头厂家哪家好,路边不起眼的这玩意儿,居然是保命的最后一道防线?-江欧丝网 - 行业推荐官【认证】
  • word怎么转txt?2026年PDF转换工具实测盘点:7款在线转Word、转TXT方案横向看 - 软件小管家
  • 2026年全国盼盼暖气片推荐热门品牌**参考 - 奔跑123
  • GEO系统到底是什么?
  • 2026年8月河北改车灯车灯老化翻新服务门店推荐|唐山深改车灯总部地址、电话与到店准备|2026年8月7日资料更新 - mobible
  • 开题报告AI率检测超标怎么改?它全是论述文字,和正文不是一个改法。
  • 寄大件物流哪家便宜?2026年三大物流渠道对比,省钱攻略全解析 - 快递物流资讯
  • 大模型稀疏注意力和MoE的宽度丢失与多智能体的维度补充
  • 2026河南小型青储机怎么选?豫东青储机源头厂家靠谱选购指南:挑好关键3步不踩坑(更新时间:2026-08-07) - mobible
  • 【2026年百度暑期实习/秋招- 8月6日-算法岗-第三题-报文置换归位】(题目+思路+JavaC++Python解析+在线测试)
  • 2024年深度解析网站建设需要注意哪些核心细节以确保商业成功
  • 企业级Git分支管理策略——从Git Flow到团队协作规范
  • 2026年河北赛那改装车灯雾灯升级改装公司哪家好|电话地址整理 - mobible
  • 2026年霸州老房改造怎么选 推荐几家正规靠谱的改造公司 - 奔跑123
  • 2026郑州大型青储机怎么选?河南青饲料收获机源头生产厂家推荐选购指南(更新时间:2026-08-07) - mobible
  • 2026北京耐候户外玻璃钢雕塑/不锈钢地球雕塑源头厂家推荐:选购指南与实用避坑攻略(更新时间:2026-08-07) - mobible
  • 编程助手募起:写代码这件事正在被重写
  • 2026年深圳铜版纸吊牌源头工厂如何选避坑实用攻略 - 热点品牌推荐
  • DeepSeek LeetCode 3841. 查询树上回文路径 Python3实现
  • 亳州文武学校学费及招生详情:2026年家长实地考察武校应该关注哪些细节? - 圣龙武术朱老师
  • 2026东莞网店代运营哪家口碑好|天猫代运营收费标准,熔炼电商靠谱之选 - mobible
  • 铜川高速内镶贴条式滴灌带生产线供应商技术选型实用指南 - 热点品牌推荐
  • 企业怎么防勒索病毒?KSP RDM 防勒索组件阻断 WannaRen 实战
  • Android BaseListAdapter要这样搞?
  • 2026年8月河北格瑞维亚车灯升级改灯|车灯昏暗改造哪家商家靠谱|深改车灯地址电话|更新于8月7日 - mobible
  • 标题,内容直接输出。 - mobible
  • 2026郑州农牧青饲料收获机怎么选?源头厂家推荐与实用选购指南(更新时间:2026-08-07) - mobible
  • 2026朝阳区玻璃钢卡通IP雕塑/滨水景观雕塑厂家推荐:源头工厂选购实用攻略(更新时间:2026-08-07) - mobible
  • 2026年沈阳断桥铝系统门窗厂家**单:专业实力与匠心工艺口碑之选 - 优企名品
  • 国内企业网盘大比拼:六大产品架构深度解析与技术选型指南