news 2026/5/27 2:37:17

【应用程序】基于 Spring Boot + Spring AI的虚拟宠物Web 应用(三)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【应用程序】基于 Spring Boot + Spring AI的虚拟宠物Web 应用(三)

四、数据持久化方案

目前的状态管理最大的问题就是:应用重启,猫就饿死了(数据全丢了)。作为一只负责任的铲屎官,我们得给猫咪的状态找个靠谱的家。

4.1 方案对比

方案优点缺点适用场景推荐度
内存 Map(现状)简单、快、零配置重启丢失、不支持多实例、无 TTL本地演示、开发调试
Redis持久化、高性能、原生支持 TTL、数据结构丰富需要额外部署 Redis 服务生产环境首选⭐⭐⭐⭐⭐
关系型数据库(MySQL/PostgreSQL)结构化、易查询、事务支持速度稍慢、需要表设计需要复杂查询、报表统计⭐⭐⭐⭐
文件存储(JSON/YAML)简单、无需外部依赖并发差、不适合高频读写单机轻量应用、配置存储⭐⭐
H2 / SQLite(嵌入式数据库)零配置、持久化、SQL 支持并发性能一般中小型应用、快速原型⭐⭐⭐

4.2 推荐方案:Redis + 持久化

对于这个场景,Redis 是比较 sweet 的选择

** Redis**

  1. 数据结构匹配:宠物状态就是简单的 key-value(Hash),Redis 天生擅长
  2. TTL 自动清理:支持设置过期时间,长时间不活跃的会话自动清理,省内存
  3. 性能极好:读写都是微秒级,不会影响 AI 交互的响应速度
  4. Spring Boot 集成简单spring-boot-starter-data-redis一行依赖搞定
  5. 支持集群:应用多实例部署时,Redis 是共享状态的最佳选择
  6. 持久化选项:RDB 快照 + AOF 日志,数据不会丢

4.3 Redis 数据结构设计

TTL管理

EXPIRE pet:state:user-123 604800

7天后自动删除

Redis数据结构

Key: pet:state:user-123

Hash

field: hunger
value: 69

field: happiness
value: 53

field: lastInteractionTime
value: 2026-05-26T14:30:00

field: mood
value: NORMAL

field: version
value: 1

Key 设计规范

prefix : 业务标识 separator : ":" namespace : 状态类型 id : 会话 ID 完整格式 : pet:state:{conversationId} 示例 : pet:state:user-123-abc

Hash 字段设计

字段类型说明
hungerInteger饥饿度 0-100
happinessInteger开心度 0-100
lastInteractionTimeISO-8601 String上次互动时间
moodString当前心情枚举值
versionInteger乐观锁版本号(防止并发覆盖)

TTL 策略

  • 默认 TTL:7 天(604800秒)
  • 每次互动后刷新 TTL
  • 长时间不活跃的宠物自动"放生"(清理数据)

4.4 存储层代码实现

// PetStateRepository.java - 存储接口publicinterfacePetStateRepository{Optional<PetState>findById(StringconversationId);voidsave(PetStatestate);voiddeleteById(StringconversationId);}// RedisPetStateRepository.java - Redis 实现@Repository@PrimarypublicclassRedisPetStateRepositoryimplementsPetStateRepository{privatefinalStringRedisTemplateredisTemplate;privatefinalObjectMapperobjectMapper;privatestaticfinalStringKEY_PREFIX="pet:state:";privatestaticfinallongTTL_SECONDS=7*24*60*60;// 7天publicRedisPetStateRepository(StringRedisTemplateredisTemplate,ObjectMapperobjectMapper){this.redisTemplate=redisTemplate;this.objectMapper=objectMapper;}@OverridepublicOptional<PetState>findById(StringconversationId){Stringkey=KEY_PREFIX+conversationId;// 使用 Hash 操作获取所有字段Map<Object,Object>entries=redisTemplate.opsForHash().entries(key);if(entries.isEmpty()){returnOptional.empty();}returnOptional.of(mapToPetState(conversationId,entries));}@Overridepublicvoidsave(PetStatestate){Stringkey=KEY_PREFIX+state.getConversationId();// 使用 Hash 存储,字段清晰,更新灵活Map<String,String>map=newHashMap<>();map.put("hunger",String.valueOf(state.getHunger()));map.put("happiness",String.valueOf(state.getHappiness()));map.put("lastInteractionTime",state.getLastInteractionTime().toString());map.put("mood",state.getMood().name());// 使用 putAll 原子性写入redisTemplate.opsForHash().putAll(key,map);// 刷新 TTLredisTemplate.expire(key,TTL_SECONDS,TimeUnit.SECONDS);}@OverridepublicvoiddeleteById(StringconversationId){redisTemplate.delete(KEY_PREFIX+conversationId);}/** * 将 Redis Hash 映射为 PetState 对象 */privatePetStatemapToPetState(StringconversationId,Map<Object,Object>entries){PetStatestate=newPetState(conversationId);if(entries.containsKey("hunger")){state.setHunger(Integer.parseInt(entries.get("hunger").toString()));}if(entries.containsKey("happiness")){state.setHappiness(Integer.parseInt(entries.get("happiness").toString()));}if(entries.containsKey("lastInteractionTime")){state.setLastInteractionTime(LocalDateTime.parse(entries.get("lastInteractionTime").toString()));}if(entries.containsKey("mood")){state.setMood(PetMood.valueOf(entries.get("mood").toString()));}returnstate;}}

4.5 降级方案:内存实现(用于开发测试)

// InMemoryPetStateRepository.java - 内存实现(开发/测试用)@Repository@Profile("dev")// 只在 dev 环境生效publicclassInMemoryPetStateRepositoryimplementsPetStateRepository{privatefinalMap<String,PetState>store=newConcurrentHashMap<>();@OverridepublicOptional<PetState>findById(StringconversationId){returnOptional.ofNullable(store.get(conversationId));}@Overridepublicvoidsave(PetStatestate){store.put(state.getConversationId(),state);}@OverridepublicvoiddeleteById(StringconversationId){store.remove(conversationId);}}

五、定时任务设计

猫咪不是机器,它需要**"活着"的感觉**。即使主人不在线,它也应该有自己的生活规律——会饿、会无聊、会想主人。

5.1 状态自然衰减

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/27 2:31:07

大模型应用开发重心迁移:从Prompt到Harness,收藏这篇看懂演进路径!

过去两年&#xff0c;大模型应用开发重心从“写Prompt”转向“管上下文”&#xff0c;再到如今的“搭运行时系统”。文章核心是解释Prompt、Context、Harness三个概念如何解决问题及代表工程能力升级。Prompt时代重点是“怎么说”&#xff0c;Context时代重点变成“给什么”&am…

作者头像 李华
网站建设 2026/5/27 2:28:38

住宅 IP 和机房 IP 有什么区别?跨境账号为什么不能只看 IP 国家

很多刚做跨境电商、海外社媒或者广告投放的新手&#xff0c;第一次检测 IP 环境时&#xff0c;最容易犯的一个错误是&#xff1a;只看 IP 国家。 比如检测结果显示美国、日本、英国&#xff0c;就觉得这个网络环境没问题。 但实际上&#xff0c;平台判断一个账号环境时&#…

作者头像 李华
网站建设 2026/5/27 2:24:13

NumPy 创建数组

NumPy 创建数组 NumPy 是 Python 中一个用于科学计算的基础库,它提供了大量用于数组创建、操作和计算的函数。本文将详细介绍如何使用 NumPy 创建数组,包括一维数组的创建、二维数组的创建以及一些高级用法。 引言 在 Python 中,数组是进行科学计算的基础。NumPy 提供了一…

作者头像 李华
网站建设 2026/5/27 2:21:12

真理归来:论贾子之路对西方伪科学体系的终结与人类认知共同体的重建

真理归来&#xff1a;论贾子之路对西方伪科学体系的终结与人类认知共同体的重建摘要摘要&#xff1a;​ 本文旨在彻底清算自波普尔以降、被西方包装为“普世真理”的伪科学垃圾体系。本文指出&#xff0c;所谓“科学哲学”、“同行评审”、“可证伪性”并非中立的知识标尺&…

作者头像 李华