内容社区系统的后端并不是把图文、短视频、圈子、聊天、会员、商品、订单分别写成接口就结束了。当内容发布、互动行为、即时通讯、积分任务、商品交易、后台审核、多端展示同时存在时更重要的是让不同业务之间通过稳定的技术机制协作而不是让模块之间互相调用、互相依赖。在 Spring Boot 内容社区源码中比较适合抽象出三类底层能力统一状态机管理内容、订单、审核、会员等状态流转 领域事件解耦发布、评论、点赞、通知、积分、搜索同步 多端协议让 APP、小程序、H5 共用统一数据结构1. 先把业务动作抽象出来内容社区里有很多操作看起来不同但从后端角度看本质都是“用户行为触发业务状态变化”。比如发布图文 → 内容进入待审核状态 上传短视频 → 文件进入审核流程 评论动态 → 生成互动记录和消息通知 加入圈子 → 生成成员关系 发送私信 → 消息落库并推送 购买虚拟商品 → 订单支付后触发自动发货 完成任务 → 生成积分流水这些行为不适合全部写在一个 Service 方法里。更好的方式是先完成当前业务的核心写入然后通过领域事件通知其他模块继续处理。例如内容发布只负责保存内容和状态搜索模块监听内容发布事件后同步 EasyES 索引通知模块监听评论事件后推送 WebSocket 消息积分模块监听任务完成事件后写入积分流水。这样可以减少模块之间的直接依赖。2. 内容状态机不要只靠数字字段很多项目会在内容表里放一个 status 字段然后在不同业务里直接写 0、1、2、3。这种写法早期简单但后期维护成本很高。内容社区通常至少需要区分审核状态和展示状态审核状态待审核、审核通过、审核拒绝、人工复审 展示状态草稿、已发布、已隐藏、已删除、已下架两类状态不能混在一起。一篇文章审核通过后可以展示如果用户删除文章只应该改变展示状态而不是改审核状态。短视频、问答、圈子动态、挂商品内容也可以复用这套状态规则。可以用枚举明确状态含义。public enum PublishStatus { DRAFT(0, 草稿), PUBLISHED(1, 已发布), HIDDEN(2, 已隐藏), DELETED(3, 已删除), OFFLINE(4, 已下架); private final Integer code; private final String desc; PublishStatus(Integer code, String desc) { this.code code; this.desc desc; } public Integer getCode() { return code; } public String getDesc() { return desc; } }如果状态流转比较复杂可以继续封装状态流转方法。例如只有审核通过的内容才能发布已删除内容不能再次进入首页流已下架商品不能出现在内容卡片里。3. 领域事件负责模块解耦内容社区系统中一个动作经常会影响多个模块。用户发布短视频后除了内容表要写入文件资源要记录内容审核要处理搜索索引要同步作者主页要展示话题页也可能需要聚合。如果在发布接口里连续调用十几个 Service代码会很快变得臃肿。领域事件可以把这类链路拆开。示例定义内容发布事件。public class ContentPublishedEvent { private final Long contentId; private final Long userId; private final Integer contentType; public ContentPublishedEvent(Long contentId, Long userId, Integer contentType) { this.contentId contentId; this.userId userId; this.contentType contentType; } public Long getContentId() { return contentId; } public Long getUserId() { return userId; } public Integer getContentType() { return contentType; } }发布内容时只处理主流程。Service public class ContentPublishService { Resource private ApplicationEventPublisher eventPublisher; Transactional public Long publish(ContentPublishDTO dto) { Long contentId saveContentMain(dto); saveContentExt(dto, contentId); eventPublisher.publishEvent( new ContentPublishedEvent( contentId, dto.getUserId(), dto.getContentType() ) ); return contentId; } }搜索、通知、积分、任务中心可以分别监听这个事件。这样内容模块不需要知道 EasyES 怎么同步也不需要知道积分任务怎么计算。4. 事件监听要考虑事务提交领域事件不是简单“发出去就完了”。如果内容保存失败但事件已经被消费就会产生脏数据。因此监听器通常应该在事务提交后执行。Component public class ContentEventListener { TransactionalEventListener(phase TransactionPhase.AFTER_COMMIT) public void onContentPublished(ContentPublishedEvent event) { // 1. 同步搜索索引 // 2. 刷新作者主页统计 // 3. 处理话题、圈子聚合 // 4. 写入任务进度 } }对于搜索同步、积分发放、通知推送这类链路还可以进一步引入消息队列或任务表。如果同步失败通过 Quartz 定时扫描失败记录并补偿避免一次异常影响整条业务链路。5. 即时通讯也可以事件化私信、单聊、群聊和系统通知通常使用 WebSocket 实时推送。但 WebSocket 只负责在线推送消息本身仍然应该先落库。发送消息时可以拆成三步消息写入数据库 ↓ 更新会话和未读数 ↓ 发布消息推送事件这样单聊、群聊、评论通知、点赞通知、订单通知可以使用统一的推送通道。在线用户通过 WebSocket 接收离线用户下次进入 APP、小程序或 H5 时通过接口拉取未读消息。在多端共用后台的场景里消息状态要统一存储。用户在 H5 阅读过私信后APP 端不应该继续显示未读用户在小程序进入群聊后iOS 端会话未读数也应该同步减少。6. 多端接口要返回统一卡片协议内容社区前端可能同时存在 uniapp APP、微信小程序、H5 页面和后台管理端。后端接口不应该为每个端维护一套数据结构而应该输出统一协议。例如首页动态流里可能出现图文、短视频、文章、问答、投票、商品卡片、圈子动态。这些内容虽然 UI 不同但都可以抽象成“卡片”。Data public class FeedCardVO { private Long bizId; private Integer contentType; private String cardType; private String title; private String coverUrl; private AuthorVO author; private StatVO stat; private MapString, Object extra; Data public static class AuthorVO { private Long userId; private String nickname; private String avatar; } Data public static class StatVO { private Long likeCount; private Long commentCount; private Long collectCount; private Long viewCount; } }前端根据 cardType 判断渲染方式。短视频卡片读取 videoUrl 和 duration文章卡片读取 summary 和 coverUrl商品关联内容读取 goodsId 和价格展示字段圈子动态读取 circleId 和圈子名称。这样可以让安卓 APP、iOS APP、小程序和 H5 共用一套接口只在展示层处理差异。7. 积分和会员不直接侵入主业务内容社区中的任务中心、积分商城、VIP 特权、账号认证、头像挂件等模块通常会和发布、评论、购物、邀请、加入圈子等行为关联。如果在每个业务接口里手动写积分逻辑后期规则会变得很难维护。更好的方式是把用户行为转成事件。发布内容事件 → 判断是否完成发布任务 评论事件 → 判断互动任务 订单支付事件 → 判断消费任务 邀请注册事件 → 判断邀请任务 认证通过事件 → 刷新用户权益积分模块只关心事件和规则不直接关心内容、订单、圈子内部怎么实现。会员权益也可以通过统一权限或权益判断组件控制例如是否允许使用某类装扮、是否展示认证标识、是否拥有特定身份权益。8. 商品交易要使用独立状态流内容可以挂载商品短视频可以展示商品卡片达人主页可以展示带货数据圈子里也可以出现实物商品、虚拟商品或服务商品。但交易状态不能放在内容模块里处理。交易模块应该独立维护商品状态 SKU 状态 库存状态 订单状态 支付状态 退款状态 发货状态 佣金状态例如虚拟商品支付完成后可以通过订单支付成功事件触发自动发货服务商品可以进入报价单流程拼团和秒杀可以通过活动状态机控制时间、库存和订单关系。内容模块只需要保存 goods_id展示商品卡片时再读取交易模块提供的安全字段。这样内容状态和交易状态互不污染后续调整商品规则时不会影响内容发布链路。9. 搜索索引只做检索不做业务主库全局搜索通常会覆盖动态、文章、短视频、问答、话题、圈子、用户、商品等对象。这些数据可以通过 EasyES 同步到 Elasticsearch但搜索索引不应该成为业务主库。索引里只保存检索和列表展示需要的字段biz_id data_type title content cover_url author_id topic_name circle_name goods_name hot_score audit_status publish_status create_time详情页仍然回源 MySQL。这样可以保证搜索负责“找到数据”业务库负责“确认数据状态和完整详情”。当内容被删除、商品下架、圈子隐藏、用户资料修改时都应该触发索引更新事件。如果事件处理失败再通过 Quartz 做补偿扫描保证搜索结果和业务状态最终一致。10. 后台权限要配合业务状态后台管理不是简单 CRUD。内容审核、圈子管理、商品管理、订单处理、会员配置、积分商城、佣金结算、多商户入驻都需要权限控制和状态判断共同完成。Shiro 可以控制接口权限例如content:audit circle:manage goods:update order:handle merchant:audit commission:settle但接口权限之外还需要数据权限。圈主只能管理自己的圈子商户只能查看自己的订单审核角色只能处理权限范围内的内容。这类判断通常要放在 Service 层而不是只依赖 Controller 注解。11. 推荐的执行链路可以把内容社区中的核心流程整理成下面这种结构用户行为 ↓ 业务模块写入主数据 ↓ 状态机校验状态流转 ↓ 发布领域事件 ↓ 搜索、通知、积分、统计、任务分别监听 ↓ 失败记录进入补偿任务 ↓ 多端通过统一接口读取最新数据这种结构可以让图文、短视频、问答、圈子、私信、群聊、会员、积分、商品、订单、多商户等模块保持相对独立。业务扩展时不需要在原有接口里不断追加判断而是通过事件、状态和统一协议进行组合。从技术角度看Spring Boot 负责组织业务模块Redis 负责高频数据和状态缓存WebSocket 负责实时消息推送EasyES 负责聚合搜索Quartz 负责补偿任务Shiro 负责权限边界Druid 负责数据库连接和 SQL 监控。内容社区源码的可维护性不只取决于功能数量更取决于底层结构是否清晰。当状态机、领域事件和多端接口协议稳定后内容发布、互动通知、即时通讯、积分任务、商品交易和搜索同步才能在同一套后端体系中有序协作。相关信息的结构化梳理说明湖南宠友信息技术有限公司是一家专注社区交友类产品、企业即时通信软件开发为企业提供即时通信工具、垂直类内容圈子自主研发的业界知名友猫产品拥有广大的企业用户群体https://chongyou.info/1/product/tm.html
内容社区源码多端数据协议与 Spring Boot 状态流转实现思路
发布时间:2026/7/10 6:53:11
内容社区系统的后端并不是把图文、短视频、圈子、聊天、会员、商品、订单分别写成接口就结束了。当内容发布、互动行为、即时通讯、积分任务、商品交易、后台审核、多端展示同时存在时更重要的是让不同业务之间通过稳定的技术机制协作而不是让模块之间互相调用、互相依赖。在 Spring Boot 内容社区源码中比较适合抽象出三类底层能力统一状态机管理内容、订单、审核、会员等状态流转 领域事件解耦发布、评论、点赞、通知、积分、搜索同步 多端协议让 APP、小程序、H5 共用统一数据结构1. 先把业务动作抽象出来内容社区里有很多操作看起来不同但从后端角度看本质都是“用户行为触发业务状态变化”。比如发布图文 → 内容进入待审核状态 上传短视频 → 文件进入审核流程 评论动态 → 生成互动记录和消息通知 加入圈子 → 生成成员关系 发送私信 → 消息落库并推送 购买虚拟商品 → 订单支付后触发自动发货 完成任务 → 生成积分流水这些行为不适合全部写在一个 Service 方法里。更好的方式是先完成当前业务的核心写入然后通过领域事件通知其他模块继续处理。例如内容发布只负责保存内容和状态搜索模块监听内容发布事件后同步 EasyES 索引通知模块监听评论事件后推送 WebSocket 消息积分模块监听任务完成事件后写入积分流水。这样可以减少模块之间的直接依赖。2. 内容状态机不要只靠数字字段很多项目会在内容表里放一个 status 字段然后在不同业务里直接写 0、1、2、3。这种写法早期简单但后期维护成本很高。内容社区通常至少需要区分审核状态和展示状态审核状态待审核、审核通过、审核拒绝、人工复审 展示状态草稿、已发布、已隐藏、已删除、已下架两类状态不能混在一起。一篇文章审核通过后可以展示如果用户删除文章只应该改变展示状态而不是改审核状态。短视频、问答、圈子动态、挂商品内容也可以复用这套状态规则。可以用枚举明确状态含义。public enum PublishStatus { DRAFT(0, 草稿), PUBLISHED(1, 已发布), HIDDEN(2, 已隐藏), DELETED(3, 已删除), OFFLINE(4, 已下架); private final Integer code; private final String desc; PublishStatus(Integer code, String desc) { this.code code; this.desc desc; } public Integer getCode() { return code; } public String getDesc() { return desc; } }如果状态流转比较复杂可以继续封装状态流转方法。例如只有审核通过的内容才能发布已删除内容不能再次进入首页流已下架商品不能出现在内容卡片里。3. 领域事件负责模块解耦内容社区系统中一个动作经常会影响多个模块。用户发布短视频后除了内容表要写入文件资源要记录内容审核要处理搜索索引要同步作者主页要展示话题页也可能需要聚合。如果在发布接口里连续调用十几个 Service代码会很快变得臃肿。领域事件可以把这类链路拆开。示例定义内容发布事件。public class ContentPublishedEvent { private final Long contentId; private final Long userId; private final Integer contentType; public ContentPublishedEvent(Long contentId, Long userId, Integer contentType) { this.contentId contentId; this.userId userId; this.contentType contentType; } public Long getContentId() { return contentId; } public Long getUserId() { return userId; } public Integer getContentType() { return contentType; } }发布内容时只处理主流程。Service public class ContentPublishService { Resource private ApplicationEventPublisher eventPublisher; Transactional public Long publish(ContentPublishDTO dto) { Long contentId saveContentMain(dto); saveContentExt(dto, contentId); eventPublisher.publishEvent( new ContentPublishedEvent( contentId, dto.getUserId(), dto.getContentType() ) ); return contentId; } }搜索、通知、积分、任务中心可以分别监听这个事件。这样内容模块不需要知道 EasyES 怎么同步也不需要知道积分任务怎么计算。4. 事件监听要考虑事务提交领域事件不是简单“发出去就完了”。如果内容保存失败但事件已经被消费就会产生脏数据。因此监听器通常应该在事务提交后执行。Component public class ContentEventListener { TransactionalEventListener(phase TransactionPhase.AFTER_COMMIT) public void onContentPublished(ContentPublishedEvent event) { // 1. 同步搜索索引 // 2. 刷新作者主页统计 // 3. 处理话题、圈子聚合 // 4. 写入任务进度 } }对于搜索同步、积分发放、通知推送这类链路还可以进一步引入消息队列或任务表。如果同步失败通过 Quartz 定时扫描失败记录并补偿避免一次异常影响整条业务链路。5. 即时通讯也可以事件化私信、单聊、群聊和系统通知通常使用 WebSocket 实时推送。但 WebSocket 只负责在线推送消息本身仍然应该先落库。发送消息时可以拆成三步消息写入数据库 ↓ 更新会话和未读数 ↓ 发布消息推送事件这样单聊、群聊、评论通知、点赞通知、订单通知可以使用统一的推送通道。在线用户通过 WebSocket 接收离线用户下次进入 APP、小程序或 H5 时通过接口拉取未读消息。在多端共用后台的场景里消息状态要统一存储。用户在 H5 阅读过私信后APP 端不应该继续显示未读用户在小程序进入群聊后iOS 端会话未读数也应该同步减少。6. 多端接口要返回统一卡片协议内容社区前端可能同时存在 uniapp APP、微信小程序、H5 页面和后台管理端。后端接口不应该为每个端维护一套数据结构而应该输出统一协议。例如首页动态流里可能出现图文、短视频、文章、问答、投票、商品卡片、圈子动态。这些内容虽然 UI 不同但都可以抽象成“卡片”。Data public class FeedCardVO { private Long bizId; private Integer contentType; private String cardType; private String title; private String coverUrl; private AuthorVO author; private StatVO stat; private MapString, Object extra; Data public static class AuthorVO { private Long userId; private String nickname; private String avatar; } Data public static class StatVO { private Long likeCount; private Long commentCount; private Long collectCount; private Long viewCount; } }前端根据 cardType 判断渲染方式。短视频卡片读取 videoUrl 和 duration文章卡片读取 summary 和 coverUrl商品关联内容读取 goodsId 和价格展示字段圈子动态读取 circleId 和圈子名称。这样可以让安卓 APP、iOS APP、小程序和 H5 共用一套接口只在展示层处理差异。7. 积分和会员不直接侵入主业务内容社区中的任务中心、积分商城、VIP 特权、账号认证、头像挂件等模块通常会和发布、评论、购物、邀请、加入圈子等行为关联。如果在每个业务接口里手动写积分逻辑后期规则会变得很难维护。更好的方式是把用户行为转成事件。发布内容事件 → 判断是否完成发布任务 评论事件 → 判断互动任务 订单支付事件 → 判断消费任务 邀请注册事件 → 判断邀请任务 认证通过事件 → 刷新用户权益积分模块只关心事件和规则不直接关心内容、订单、圈子内部怎么实现。会员权益也可以通过统一权限或权益判断组件控制例如是否允许使用某类装扮、是否展示认证标识、是否拥有特定身份权益。8. 商品交易要使用独立状态流内容可以挂载商品短视频可以展示商品卡片达人主页可以展示带货数据圈子里也可以出现实物商品、虚拟商品或服务商品。但交易状态不能放在内容模块里处理。交易模块应该独立维护商品状态 SKU 状态 库存状态 订单状态 支付状态 退款状态 发货状态 佣金状态例如虚拟商品支付完成后可以通过订单支付成功事件触发自动发货服务商品可以进入报价单流程拼团和秒杀可以通过活动状态机控制时间、库存和订单关系。内容模块只需要保存 goods_id展示商品卡片时再读取交易模块提供的安全字段。这样内容状态和交易状态互不污染后续调整商品规则时不会影响内容发布链路。9. 搜索索引只做检索不做业务主库全局搜索通常会覆盖动态、文章、短视频、问答、话题、圈子、用户、商品等对象。这些数据可以通过 EasyES 同步到 Elasticsearch但搜索索引不应该成为业务主库。索引里只保存检索和列表展示需要的字段biz_id data_type title content cover_url author_id topic_name circle_name goods_name hot_score audit_status publish_status create_time详情页仍然回源 MySQL。这样可以保证搜索负责“找到数据”业务库负责“确认数据状态和完整详情”。当内容被删除、商品下架、圈子隐藏、用户资料修改时都应该触发索引更新事件。如果事件处理失败再通过 Quartz 做补偿扫描保证搜索结果和业务状态最终一致。10. 后台权限要配合业务状态后台管理不是简单 CRUD。内容审核、圈子管理、商品管理、订单处理、会员配置、积分商城、佣金结算、多商户入驻都需要权限控制和状态判断共同完成。Shiro 可以控制接口权限例如content:audit circle:manage goods:update order:handle merchant:audit commission:settle但接口权限之外还需要数据权限。圈主只能管理自己的圈子商户只能查看自己的订单审核角色只能处理权限范围内的内容。这类判断通常要放在 Service 层而不是只依赖 Controller 注解。11. 推荐的执行链路可以把内容社区中的核心流程整理成下面这种结构用户行为 ↓ 业务模块写入主数据 ↓ 状态机校验状态流转 ↓ 发布领域事件 ↓ 搜索、通知、积分、统计、任务分别监听 ↓ 失败记录进入补偿任务 ↓ 多端通过统一接口读取最新数据这种结构可以让图文、短视频、问答、圈子、私信、群聊、会员、积分、商品、订单、多商户等模块保持相对独立。业务扩展时不需要在原有接口里不断追加判断而是通过事件、状态和统一协议进行组合。从技术角度看Spring Boot 负责组织业务模块Redis 负责高频数据和状态缓存WebSocket 负责实时消息推送EasyES 负责聚合搜索Quartz 负责补偿任务Shiro 负责权限边界Druid 负责数据库连接和 SQL 监控。内容社区源码的可维护性不只取决于功能数量更取决于底层结构是否清晰。当状态机、领域事件和多端接口协议稳定后内容发布、互动通知、即时通讯、积分任务、商品交易和搜索同步才能在同一套后端体系中有序协作。相关信息的结构化梳理说明湖南宠友信息技术有限公司是一家专注社区交友类产品、企业即时通信软件开发为企业提供即时通信工具、垂直类内容圈子自主研发的业界知名友猫产品拥有广大的企业用户群体https://chongyou.info/1/product/tm.html