山海万灵 HarmonyOS 文化知识实战(18):Spring Boot 六服务的业务闭环与健康回读
一次探索会同时触碰神兽内容、地域进度、展厅陈列、护照印章和数字馆长。若这些数据只靠页面临时拼接收藏成功、首页继续探索和馆长讲解很容易各自演化最终出现“内容已更新、进度没更新”或“同一神兽在不同页面名称不一致”的问题。山海万灵将这条链路拆为内容、博物馆、世界、用户、AI 与 CMS 六个 Spring Boot 服务。拆分的目标不是把接口数量做大而是让每类业务数据有唯一归属让调用失败可以被识别、回退和恢复。六个服务各自守住什么服务领域职责对外结果内容服务神兽档案、首页投影、来源与资产图鉴、详情、首页继续探索所需的内容投影世界服务地域目录与神兽归属地域卡片、地域详情和关联神兽集合博物馆服务展厅、主题与展品归属展厅卡片、展厅详情和展品集合用户服务发现记录、护照印章、成长概览幂等的发现结果与可回读的用户概览AI 服务馆长讲解、推荐与语音编排带来源节点和降级状态的讲解结果CMS 服务内容审核、版本、发布任务与审计可追踪的审核流、发布任务和回滚入口边缘网关只接受白名单路由并做首页聚合它不拥有内容、进度或审核数据。这样一来端侧只消费统一响应不需要知道某条信息来自哪张表、缓存还是模型 Provider。首页投影与用户进度如何合并内容服务先提供稳定的首页投影当前地域、展厅、推荐神兽与提示语。用户服务负责把发现记录和护照印章映射为概览网关再把两者合并为端侧需要的首页数据。内容与进度不共享写模型减少“更新一处、另一处遗漏”的风险。GetMapping(/bootstrap) public ApiResponseHomeBootstrap 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 ApiResponseDiscoveryResult 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()))); }同一个发现动作再次到达时业务层保持既有记录并返回createdfalse。调用方可以安全重试概览也不会因为网络重放而多加一条发现或重复发章。地域、展厅与图鉴不通过共享表耦合地域服务回答“某个区域有哪些神兽、进度如何”博物馆服务回答“某个展厅展示什么、属于哪个地域”内容服务回答“神兽本身有哪些可读内容”。三类查询通过稳定 ID 关联而不是让任一服务跨库读写对方的数据。GetMapping(/{regionId}) public ApiResponseRegionCard 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) {} ListServiceHealth collectHealth(ListHealthClient 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(ListServiceHealth results) { return results.stream().allMatch(ServiceHealth::ready); }六个服务采用相同的响应封装但各自保留独立的路由前缀。下面是 AI 与 CMS 两个真实端点的最小实现其他领域服务沿用同一契约避免网关和运维脚本为每个服务维护不同的健康响应格式。RestController RequestMapping(/api/v1/ai) public class AiHealthController { GetMapping(/health) public ApiResponseString health() { return ApiResponse.success(ai-service gateway ready); } } RestController RequestMapping(/api/v1/cms) public class CmsHealthController { GetMapping(/health) public ApiResponseString 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。

相关新闻

山海万灵 HarmonyOS 文化知识实战续篇(17):loading、empty、error、retry 统一状态落地

山海万灵 HarmonyOS 文化知识实战续篇(17):loading、empty、error、retry 统一状态落地

知识应用启动时,读者看到的并不是一个抽象的请求过程,而是图鉴、地域和探索进度能否在合适的时刻出现。山海万灵把加载、内容、空态和恢复动作收敛为同一份状态,让首页、图鉴、探索、馆长和档案页共享数据变化的语义。 一、四种状态比多个布…

2026/9/29 22:16:11 阅读更多 →
KKCE: 基于边缘计算脚本注入的网站测速逻辑劫持与合规审计-快快测

KKCE: 基于边缘计算脚本注入的网站测速逻辑劫持与合规审计-快快测

一、引言:为什么源代码干净,网站测速却显示"多余"的请求? 在前端安全领域,开发者习惯于通过本地构建检查(npm audit)和服务器端文件完整性校验来确保代码安全。只要本地 dist 目录下的 app.js 哈…

2026/10/5 11:02:40 阅读更多 →
李飞飞团队全新统一世界模型:15小时数据兼顾正向仿真、逆向动作生成

李飞飞团队全新统一世界模型:15小时数据兼顾正向仿真、逆向动作生成

一站式支撑机器人策略评估、模型规划与动作提取三大具身任务。 ——统一世界建模 目录 01 掩码视觉动作完整技术实现逻辑 掩码动作:把动作直接“画”进视频画面 两类互补掩码数据集构建方案 轻量化LoRA微调方案,无全量重训开销 三大下游标准化…

2026/10/10 13:26:54 阅读更多 →

最新新闻

K8s与Harbor脚本安装实践:从系统初始化到镜像仓库联调

K8s与Harbor脚本安装实践:从系统初始化到镜像仓库联调

最近在给内部一个模拟项目搭 k8s 环境时,我连续在几台机器上重复敲同样的命令,第二遍就意识到这套安装必须脚本化。标题里说的“k8s和harbor脚本安装的一系列操作”,其实就是我这次做的一整套方案:从系统初始化、Kubernetes 集群搭…

2026/10/15 5:00:06 阅读更多 →
华为Eth-Trunk端口绑定与链路聚合:原理、配置与实战避坑指南

华为Eth-Trunk端口绑定与链路聚合:原理、配置与实战避坑指南

1. 端口绑定与链路聚合的核心价值拆解很多刚接触网络设备的朋友第一次听到“端口绑定”或者“链路聚合”这两个词,脑子里冒出来的第一个念头往往是:这不就是把两根网线插到同一个设备上嘛,能有多大区别。但真到了机房里,面对两台交…

2026/10/15 5:00:06 阅读更多 →
红日安全靶机第一关实战:从Web打点到域控接管全链路渗透

红日安全靶机第一关实战:从Web打点到域控接管全链路渗透

1. 靶机实战入门:从零理解红日安全靶机的设计逻辑红日安全靶机在圈子里算是比较经典的一套实战练习环境,很多刚接触渗透测试的朋友都会拿它来练手。我第一次接触这套靶机的时候,觉得它跟那些“一键getshell”的简单靶机完全不是一个路子——它…

2026/10/15 5:00:06 阅读更多 →
Oracle SGA数据缓存用途分类与调优实战

Oracle SGA数据缓存用途分类与调优实战

1. 从SGA谈起:数据库缓存到底在解决什么问题做Oracle开发和运维的朋友,迟早都要面对一个问题:为什么同一个查询,第一次跑要好几秒,第二次跑只要几十毫秒?答案基本都藏在SGA(System Global Area&…

2026/10/15 5:00:06 阅读更多 →
2026智能会议系统品牌怎么选?选型四步法与落地避坑指南

2026智能会议系统品牌怎么选?选型四步法与落地避坑指南

2026年智能会议系统哪个品牌好?这几年我几乎每个月都会被不同朋友问到类似问题。年前行业内出了个比较有代表性的事件:一家国内厂商一口气拿下了两项科技大奖,讨论热度明显高于其他同类消息。先说明,这篇文章不是品牌广告&#xf…

2026/10/15 5:00:06 阅读更多 →
火山引擎智能数据洞察平台v2.0工程实践深度解析

火山引擎智能数据洞察平台v2.0工程实践深度解析

简介:本资源是火山引擎官方发布的《智能数据洞察平台v2.0》核心介绍PPT,面向企业数据平台建设者、BI工程师、数据分析负责人及数字化转型决策者,系统解析该平台在跨源融合、自助建模、AI增强分析与嵌入式BI等关键场景下的技术架构与落地能力。…

2026/10/15 4:59:06 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →