4、系统架构设计:前端、后端、数据库、消息推送等模块划分与交互流程

账单日还款日提醒系统的架构设计,决定了系统的稳定性、扩展性以及后续维护成本。一个合理的架构应当将前端展示、后端业务逻辑、数据持久化和消息推送四个核心模块清晰解耦,同时定义好它们之间的交互契约。本章将围绕模块划分原则、核心交互流程、关键数据模型以及消息推送的技术选型展开,帮助您构建一个高内聚、低耦合的提醒系统骨架。

账单日还款日提醒系统 — 模块架构与交互流程 前端展示层 用户设置账单日 查看提醒记录 管理通知偏好 Web / 小程序 / App 后端业务层 账单CRUD 提醒规则引擎 用户与设备管理 Spring Boot / Node.js 数据持久层 用户表 / 账单表 提醒记录表 设备表 MySQL / Redis 消息推送层 定时任务触发 模板消息组装 多渠道分发 微信 / 短信 / App Push HTTPS/JSON SQL/ORM 定时任务/事件触发 微信/短信/App推送 👤 终端用户 外部服务 微信公众平台 短信网关 / APNs 前端展示 后端业务 数据持久 消息推送 同步请求 异步推送

4.1 核心模块划分与职责边界

系统架构的第一要务是明确边界。我们将提醒系统划分为四个核心模块,每个模块只关注自己的领域,通过标准接口对外提供服务。

模块名称 核心职责 关键技术选型 典型接口/协议
前端展示层 账单录入、日历展示、提醒偏好设置、历史记录查询 Vue 3 / React Native / 微信小程序 RESTful API / WebSocket
后端业务层 账单CRUD、提醒规则计算、用户鉴权、设备管理 Spring Boot / Node.js (NestJS) JWT / RBAC权限
数据持久层 结构化数据存储、缓存热点数据、消息队列持久化 MySQL 8.0 + Redis 7.0 MyBatis-Plus / Spring Data Redis
消息推送层 定时扫描到期账单、组装模板消息、调用渠道网关 XXL-Job + 微信模板消息 + 阿里云短信 HTTP回调 / 消息队列
架构设计原则:模块间通信必须通过接口或消息队列,禁止直接访问对方数据库。例如,前端不能直连MySQL,推送层不能修改账单表。这样做的目的是为了后续扩展——当需要增加邮件推送渠道时,只需在推送层新增一个适配器,不影响其他模块。

4.2 关键交互流程设计

系统的核心交互流程包括三个:账单创建与提醒规则绑定、定时扫描与推送触发、用户反馈与状态回写。下面逐一拆解。

流程一:账单创建与提醒规则绑定

  1. 前端提交:用户录入账单名称、金额、账单日(如每月5日)、还款日(如每月20日),并选择提醒提前量(如提前3天)。
  2. 后端校验:验证日期合法性、金额范围,并计算下一个提醒时间点。例如当前日期为4月10日,账单日为5月5日,还款日为5月20日,则下一个提醒时间为5月17日(提前3天)。
  3. 持久化存储:将账单记录写入MySQL的bill表,同时将下一个提醒时间戳写入Redis的ZSet(用于定时扫描)。
  4. 返回结果:后端返回完整的账单对象(含生成的提醒计划),前端刷新日历视图。

流程二:定时扫描与推送触发

// 伪代码:定时任务核心逻辑(每天凌晨2点执行)
List<ReminderTask> tasks = reminderTaskMapper.findDueTasks(LocalDate.now());
for (ReminderTask task : tasks) {
    // 1. 组装模板消息
    String content = String.format("您的账单【%s】将于%s到期,金额%.2f元",
        task.getBillName(), task.getDueDate(), task.getAmount());
    // 2. 获取用户设备
    String  = deviceTokenMapper.getByUserId(task.getUserId());
    // 3. 调用推送渠道
    pushService.sendWechatTemplate(task.getOpenId(), content);
    // 4. 记录推送日志
    pushLogMapper.insert(new PushLog(task.getId(), "SUCCESS", content));
    // 5. 更新下次提醒时间(如还款日当天再次提醒)
    task.setNextRemindTime(task.getDueDate().plusDays(1));
    reminderTaskMapper.updateById(task);
}

流程三:用户反馈与状态回写

用户收到提醒后,可能点击“已还款”按钮。此时前端调用POST /api/bill/{id}/repay接口,后端更新账单状态为PAID,同时删除Redis中对应的定时任务,避免重复提醒。若用户未操作,系统在还款日当天再次推送一次强提醒,之后标记为OVERDUE。

4.3 数据库设计要点

数据库设计直接影响查询效率和扩展性。建议至少包含以下四张核心表:

表名 关键字段 索引策略 说明
user id, open_id, phone, notify_pref uk_open_id 存储用户基础信息与通知偏好(微信/短信/App)
bill id, user_id, name, amount, bill_day, due_day, status idx_user_id, idx_status 账单主表,status枚举:ACTIVE/PAID/OVERDUE
reminder_task id, bill_id, remind_time, channel, status idx_remind_time, idx_status 提醒任务表,由定时任务扫描
push_log id, task_id, channel, content, result, create_time idx_task_id 推送日志,用于排障和统计
风险提示:不要将提醒时间直接存储在bill表中,而应通过reminder_task表单独管理。原因在于:一个账单可能产生多条提醒(提前3天、提前1天、当天),如果冗余在bill表会导致字段膨胀且难以维护。独立表结构支持灵活的提醒策略调整。

4.4 消息推送模块的工程化实现

消息推送是系统的“最后一公里”,也是最容易出问题的环节。推送模块需要解决三个核心问题:可靠性(消息不丢失)、时效性(准时到达)、可观测性(推送失败可追溯)。

推送渠道适配器模式

建议采用策略模式封装不同渠道的推送逻辑。定义一个统一的PushChannel接口,微信、短信、App推送分别实现该接口。当需要新增渠道时,只需添加一个实现类并注册到Spring容器中。

public interface PushChannel {
    PushResult send(PushRequest request);
    boolean supports(ChannelType type);
}

@Component
public class WechatChannel implements PushChannel {
    @Override
    public PushResult send(PushRequest request) {
        // 调用微信模板消息API
        String accessToken = wechatTokenManager.getAccessToken();
        // ... 组装消息体并发送
    }
}

失败重试与补偿机制

推送失败是常态(如微信过期、用户取消关注)。必须设计重试机制:

  • 立即重试:网络超时或5xx错误,间隔1秒、5秒、30秒重试3次。
  • 延迟补偿:超过重试次数后,将任务写入死信队列,由补偿任务每小时扫描一次,尝试补发。
  • 人工介入:对于连续失败超过3次的用户,标记为PUSH_DISABLED,并在管理后台展示告警。

4.5 实战案例:某信用卡代还App的架构优化

某创业团队开发了一款信用卡账单管理App,初期架构将所有逻辑写在一个单体应用中,推送任务使用Spring的@Scheduled注解在应用内执行。上线后遇到三个问题:

  1. 推送延迟:用户量增长到5万后,定时任务扫描全表耗时超过10分钟,导致部分用户错过提醒时间。
  2. 重复推送:应用部署多个实例时,每个实例都会执行定时任务,用户收到重复消息。
  3. 排障困难:推送失败后没有日志记录,用户投诉时无法定位原因。

优化方案:

  • 将定时任务迁移至独立的XXL-Job调度中心,支持分片广播,解决重复执行问题。
  • 引入Redis ZSet存储待提醒任务,score为提醒时间戳,扫描时使用ZRANGEBYSCORE命令,查询效率从O(n)降为O(log n)。
  • 增加push_log表,记录每次推送的请求参数、响应结果和耗时,配合ELK日志系统实现全链路追踪。

优化效果:推送准时率从87%提升至99.2%,重复推送完全消除,排障时间从平均2小时缩短至15分钟。代价是需要额外维护调度中心和Redis集群,但相比用户流失的损失,这笔投入是值得的。

经验总结:架构设计不是一步到位的。初期单体架构可以快速验证业务,但当出现性能瓶颈或协作问题时,应果断拆分模块。本案例中,推送模块的独立是性价比最高的第一步,因为它不涉及核心账单数据的迁移,风险可控。

4.6 常见误区与设计复盘

在架构设计中,以下误区需要特别警惕:

误区 后果 正确做法
前端直接调用第三方推送API 暴露密钥、无法统一管理推送策略 所有推送请求必须经过后端,由后端统一签名和限流
数据库表设计时未考虑时间字段索引 定时扫描全表,性能急剧下降 对remind_time、status等查询字段建立联合索引
推送渠道耦合在业务代码中 新增渠道需要修改核心业务逻辑 使用策略模式+工厂模式,业务层只依赖抽象接口
忽略幂等性设计 网络重试导致用户收到重复消息 每次推送生成唯一message_id,渠道端做去重

复盘要点:架构设计的核心是识别变化点。在本系统中,变化最频繁的是推送渠道和提醒规则。因此,将这两个部分设计为可插拔的模块,是架构成功的关键。同时,要建立完善的监控体系,对推送成功率、任务积压量、接口响应时间等指标设置告警阈值,确保问题早发现、早处理。