SmartMail AI助手采用经典的三层架构设计,从下至上依次为基础设施层、数据持久化层、业务逻辑层和接口层。整个系统基于Java 17构建,以Spring Boot 3.x为核心框架,通过模块化的设计思想实现各功能组件的解耦。
系统采用单体应用架构部署,所有模块集成在一个进程中运行,简化部署和运维复杂度。模块之间的通信通过Spring IoC容器管理的Bean依赖注入实现,部分非核心流程通过Spring事件机制进行异步解耦。
| 技术组件 | 版本 | 作用 |
|---|---|---|
| Java | 17+ | 基础运行环境 |
| Spring Boot | 3.2.x | 应用主框架,IoC容器 |
| Spring Mail | 集成 | 邮件收发协议实现 |
| Spring Scheduler | 集成 | 定时任务调度 |
| Spring Data JPA | 3.2.x | 数据持久化抽象 |
| Jsoup | 1.15.4 | HTML内容解析清洗 |
| FreeTTS | 1.2.2 | 语音合成引擎 |
| H2 Database | 2.2.x | 嵌入式数据库 |
| Lombok | 1.18.30 | 代码简化工具 |
| SLF4J + Logback | 集成 | 日志记录 |
| RestTemplate/OkHttp | 集成 | HTTP客户端,调用阿里云API |
基础设施层是系统运行的底座,包括Java运行时环境、操作系统文件系统和网络通信设施。
Java运行时环境 :要求JDK 17及以上版本,利用JDK 17的新特性如文本块、增强的Switch表达式、Record类等简化代码编写。同时依赖JVM的内存管理和垃圾回收机制保证系统长期运行的稳定性。
文件系统 :用于存储配置文件、日志文件、嵌入式数据库文件和生成的语音文件。系统遵循XDG Base Directory规范,在用户主目录下创建 .smartmail文件夹存放所有运行时数据,避免污染系统目录。
网络通信 :系统需要访问两个外部网络服务:邮箱服务器(通过IMAPS协议,端口993)和阿里云百炼API(通过HTTPS协议,端口443)。网络模块配置了连接超时(10秒)和读取超时(30秒)参数,避免网络问题导致线程阻塞。
数据持久化层负责系统数据的存储和访问,采用Repository模式封装数据访问逻辑。
数据库选型 :采用H2 Database作为嵌入式数据库,支持两种运行模式:文件模式(持久化到磁盘)和内存模式(仅运行时存在)。生产环境使用文件模式,数据存储路径为 ~/smartmail/data/smartmail.mv.db。H2的优势在于零配置、支持标准SQL、体积小,适合个人桌面应用场景。
数据源配置 :通过Spring Boot自动配置H2数据源,连接池使用HikariCP,配置最小空闲连接2个,最大连接数10个,连接超时30秒。
ORM框架 :采用Spring Data JPA作为对象关系映射框架,配合Hibernate作为JPA实现。实体类使用JPA注解标记,利用Lombok的@Data注解简化实体类的编写。Repository接口继承 JpaRepository,自动获得基础的CRUD方法。
数据库版本管理 :集成Flyway进行数据库版本管理,每次启动自动检查并执行未执行的迁移脚本。迁移脚本存放在 resources/db/migration目录下,命名规范为 V1__init.sql、V2__add_index.sql等。
数据模型设计 :核心数据模型包含三个实体类:Email(邮件实体)、UserConfig(用户配置)、ProcessLog(处理日志)。Email实体与 UserConfig实体多对一关系,一封邮件属于一个用户配置。ProcessLog记录每次调度任务的执行情况。
业务逻辑层是系统的核心,包含六个核心服务组件,每个组件封装独立的业务功能。
2.3.1 邮件抓取服务
邮件抓取服务基于JavaMail API实现,封装为 MailFetcher组件。该服务通过IMAPS协议连接邮箱服务器,实现邮件的增量抓取。
连接管理采用连接池模式,MailConnectionPool内部维护一组 Store对象,每次抓取任务从池中获取连接,使用完毕后归还。连接参数包括:超时时间设置(连接超时10秒,读取超时30秒)、SSL安全套接字工厂、调试模式开关等。
增量抓取策略:服务记录每次成功抓取的时间戳,存储在 lastFetchTime变量中并持久化到数据库。下次抓取时,通过IMAP的 RECENT标志或 SINCE条件仅获取该时间戳之后的新邮件。抓取完成后,可以选择性将邮件标记为已读(通过设置 Flags.Flag.SEEN)。
异常处理机制:采用重试模板 RetryTemplate封装网络操作,配置指数退避策略:第一次失败后等待2秒重试,第二次等待4秒,第三次等待8秒,最多重试3次。连续失败超过阈值则抛出异常,由上层调度模块处理。
2.3.2 内容清洗服务
内容清洗服务 ContentCleaner负责将原始邮件内容转换为适合AI分析的纯净文本。该服务接收 javax.mail.Message对象,提取MIME内容后进行处理。
MIME类型检测:首先检查邮件的Content-Type,如果是 text/plain则直接读取文本;如果是 text/html则交给Jsoup处理器;如果是 multipart/*则递归解析各个部分,优先选择文本部分,如果没有文本则解析HTML部分。
HTML解析流程:Jsoup解析器将HTML字符串解析为DOM树,然后执行清理操作:移除所有 script、style、iframe、noscript等标签;移除所有内联事件处理器(如 onclick);移除所有样式属性;移除隐藏元素(style="display:none")。清理后的DOM树调用 text()方法提取纯文本。
编码处理:从邮件头部的 Content-Type中提取 charset参数,如果没有则使用ICU4J的 CharsetDetector进行编码检测,确保中文字符正确解码。解码后的文本统一转换为UTF-8编码。
正文提取算法:采用启发式算法识别并剔除邮件签名、免责声明、营销信息。算法基于文本块的特征进行判断:链接密度超过30%的区域判定为导航区;包含"退订"、"不再接收"等关键词的区域判定为营销尾部;连续的短行(少于10字符)判定为签名。这些区域被移除后,剩余文本作为邮件的核心内容。
长度控制策略:清洗后的文本如果超过2000字符,采用智能截断算法:首先检测是否存在"总结"、"要点"、"关键"等关键词,优先保留这些段落;如果没有,采用"开头+结尾"策略,保留前500字符和后300字符,中间用省略号连接。
2.3.3 AI分析服务
AI分析服务 AIAnalysisService封装对阿里云百炼平台的API调用,实现邮件分类和摘要生成。该服务基于HTTP客户端实现,支持同步调用和超时控制。
HTTP客户端配置:使用 RestTemplate作为HTTP客户端,配置连接工厂设置超时参数:连接超时5秒,读取超时30秒。请求拦截器添加认证头:Authorization: Bearer ${api-key}。响应拦截器记录响应状态和耗时,用于监控和调试。
请求封装:构建 AnalysisRequest对象,包含模型名称、消息列表、温度参数等字段。模型名称固定为 deepseek-r1,温度设置为0.1保证输出的确定性。消息列表包含两条消息:系统消息("你是一个专业的邮件助手,擅长分类和总结")和用户消息(包含邮件内容)。
批量处理实现:BatchAnalysisService继承 AIAnalysisService,实现批量逻辑。构建用户消息时,按顺序列出所有邮件的主题和内容,格式为:"邮件1主题:xxx\n邮件1内容:xxx\n邮件2主题:xxx\n邮件2内容:xxx..."。在提示词末尾指定返回格式:"请返回JSON数组,格式:[{id:1,category:"重要",summary:"摘要"},{id:2...}]"。
响应解析:API返回的JSON响应包含 choices[0].message.content字段,该字段是AI生成的文本内容。解析器提取该字段后,使用Jackson库将其解析为 List<AnalysisResult>对象。解析过程中进行严格的类型检查和字段验证,如果格式不正确则记录异常并抛出 AnalysisException。
降级策略:在 AIAnalysisService接口中定义了默认方法 fallbackAnalyze(),当API调用失败或超时时调用。降级实现 LocalAnalysisService基于关键词规则进行分类,使用正则表达式匹配邮件内容中的关键信息。虽然准确率不如AI,但保证系统基本可用。
Token用量监控:每次API调用后解析响应头中的 x-ai-usage信息,记录消耗的token数量。累计用量达到阈值时发出告警,避免免费额度用尽后产生费用。
2.3.4 分类决策服务
分类决策服务 ClassificationService对AI分析结果进行后处理,结合用户偏好和历史数据进行优化。该服务实现 Classifier接口,支持多种分类器的组合使用。
分类体系实现:采用枚举类型 EmailCategory定义四种分类:IMPORTANT、TODO、NOTIFICATION、OTHER。每个枚举包含优先级级别(1-4,1最高)和默认处理动作。
优先级排序算法:PriorityCalculator根据发件人域、发件人地址和历史行为计算邮件优先级。计算公式为:priority = basePriority - (favoriteBoost if in whitelist) + (spamPenalty if in blacklist)。基础优先级根据发件人角色确定:老板邮件基础优先级1,客户2,同事3,其他4。白名单中的发件人优先级提升1级,黑名单中的降低1级(最低为4)。
历史学习机制:LearningEngine组件收集用户对邮件的后续操作(如在邮箱客户端中标记重要、删除、回复等),通过简单的贝叶斯分类器更新分类权重。实现方式为统计每个发件人每类邮件的出现频率,当频率超过阈值时自动将该发件人加入对应分类的白名单。
去重聚合算法:ConversationAggregator检测邮件主题中是否包含"Re:"、"Fwd:"等前缀,将相同基础主题的邮件聚合为会话。对于会话线程,只播报最新一封邮件的摘要,但统计信息(如"您与张三有3封往来邮件")会播报。
2.3.5 语音合成服务
语音合成服务 VoiceSynthesisService基于FreeTTS引擎实现文本到语音的转换,生成可播放的音频流或文件。
引擎初始化:静态代码块中注册FreeTTS引擎中心:Central.registerEngineCentral("com.sun.speech.freetts.jsapi.FreeTTSEngineCentral")。创建合成器时指定语音风格,默认使用"kevin16"语音(美式英语,适合读中文)。
文本预处理:TextNormalizer对输入文本进行规范化处理。数字转换:将"123"转换为"一百二十三";日期转换:"2026-03-12"转换为"二零二六年三月十二日";英文单词按拼读规则处理,特殊符号替换为文字描述("&"→"和","%"→"百分之")。预处理后的文本更符合语音合成引擎的发音习惯。
分段播报实现:SegmentedSpeaker接收邮件列表,首先生成总述:"您有X封重要邮件",然后遍历列表逐条播报。每条邮件播报格式为:"第X封,来自[发件人],[摘要]"。分段之间插入300毫秒的静音间隔,便于用户区分。播报过程中支持暂停、继续、跳过等控制命令(通过键盘监听实现)。
音频输出控制:AudioOutput接口定义两种实现:RealtimePlayer通过Java Sound API实时播放音频;FileRecorder将音频编码为WAV格式保存到文件。文件命名格式为 smartmail_yyyyMMdd_HHmmss.wav,存储在 ~/smartmail/audio/目录下。用户可配置保留最近N天的音频文件,系统自动清理过期文件。
语音参数配置:支持语速(默认150字/分钟)、音调(默认1.0)、音量(默认0.8)的配置。这些参数通过 VoiceManager获取当前语音对象后设置,支持运行时动态调整。
2.3.6 定时调度服务
定时调度服务基于Spring Scheduler实现,是整个业务流程的驱动引擎。该服务封装为 ScheduledTasks组件,使用 @Scheduled注解标记调度方法。
调度策略配置:在 application.yml中定义两个cron表达式:mail.schedule.morning=0 30 8 * * MON-FRI(工作日上午8:30)和 mail.schedule.noon=0 0 12 * * MON-FRI(工作日上午12:00)。cron表达式支持在线修改,应用重启后生效。
任务编排实现:WorkflowEngine组件管理任务执行状态机。状态机定义六个状态:INIT、FETCHING、CLEANING、ANALYZING、SYNTHESIZING、COMPLETED。每个步骤执行前检查当前状态,执行成功后更新状态,执行失败时回滚到上一个状态。状态信息存储在数据库的 process_status表中,支持应用重启后恢复中断的任务。
并发控制机制:基于数据库悲观锁实现任务互斥。每次任务开始前尝试获取锁记录(select for update),如果锁已被其他进程持有则等待10秒后放弃。锁记录包含进程ID和开始时间,如果持有锁的进程超过30分钟未释放(可能是崩溃),则强制释放锁。
任务超时保护:每个任务步骤设置最大执行时间,通过 @Async配合 Future实现超时控制。邮件抓取步骤超时5分钟,AI分析步骤超时2分钟(每批),语音合成步骤超时1分钟。超时后强制中断任务并记录错误日志。
基于Spring IoC容器,各服务组件通过构造器注入建立依赖关系。ScheduledTasks依赖 MailFetcher、ContentCleaner、AIAnalysisService等核心服务;ClassificationService依赖 AIAnalysisService;VoiceSynthesisService依赖 ClassificationService。这种显式依赖声明使得模块间的调用关系清晰可见,便于单元测试时模拟依赖。
对于非核心流程,采用Spring事件机制实现异步解耦。定义 EmailProcessedEvent事件,包含处理后的邮件列表和统计数据。StatisticsListener监听该事件,更新统计数据;NotificationListener监听该事件,发送处理完成通知。事件发布采用 ApplicationEventPublisher,监听器使用 @EventListener注解标记,默认异步执行(配合 @Async)。
对于需要多次访问的数据,采用内存缓存减少重复计算。使用Spring Cache抽象,底层实现选用Caffeine Cache。邮件清洗结果缓存配置过期时间10分钟,AI分析结果缓存配置过期时间30分钟。缓存键设计为邮件ID加时间戳的组合,确保不同时间处理的同一邮件不会冲突。
系统通过JavaMail API与邮箱服务器通信,实现IMAPS协议。接口调用封装在 MailFetcher中,对外暴露 fetchUnreadEmails()方法,返回 List<Email>对象。
协议细节 :IMAPS使用SSL/TLS加密,默认端口993。认证方式为用户名+密码(或授权码)。支持的邮件文件夹包括INBOX(收件箱)、SENT(已发送)、TRASH(垃圾箱),当前版本只处理收件箱。
邮件属性获取 :从 Message对象中提取以下属性:getFrom()获取发件人,getSubject()获取主题,getSentDate()获取发送时间,getContent()获取内容,getMessageNumber()获取消息编号,getFolder()获取所属文件夹。内容类型通过 isMimeType()方法判断。
标志位操作 :通过 message.getFlags()获取和设置邮件标志。主要使用 Flags.Flag.SEEN(已读)和 Flags.Flag.RECENT(最近到达)。处理完成后可选设置SEEN标志,避免重复处理。
系统通过HTTPS协议调用阿里云百炼的API接口,实现大模型服务集成。
接口地址 :https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
认证方式 :HTTP Header中添加 Authorization: Bearer ${api-key},API Key从阿里云控制台获取,存储在配置文件中。
请求格式 :JSON格式,包含以下字段:
model: 模型名称,固定为deepseek-r1messages: 消息列表,包含role和contenttemperature: 温度参数,固定为0.1max_tokens: 最大生成token数,默认2048stream: 是否流式返回,固定为false
响应格式 :JSON格式,主要字段:
choices[0].message.content: AI生成的文本内容usage.prompt_tokens: 提示词消耗token数usage.completion_tokens: 生成内容消耗token数usage.total_tokens: 总消耗token数
错误处理 :API返回非200状态码时,根据错误码进行差异化处理:401(认证失败)记录错误并禁用API调用;429(频率超限)等待后重试;500(服务器错误)降级到本地规则。
为未来Web管理界面预留REST API,通过Spring MVC实现。API遵循RESTful设计规范,使用JSON数据格式。
邮件管理API :
GET /api/emails- 分页获取邮件列表,支持按分类、时间筛选GET /api/emails/{id}- 获取邮件详情PUT /api/emails/{id}/category- 修改邮件分类DELETE /api/emails/{id}- 删除邮件记录
配置管理API :
GET /api/config- 获取当前配置PUT /api/config- 更新配置POST /api/config/test- 测试邮箱连接
统计API :
GET /api/stats/summary- 获取汇总统计GET /api/stats/trend- 获取趋势数据GET /api/stats/category- 获取分类占比
API统一返回 ResponseEntity包装的 ApiResponse对象,包含状态码、消息和数据字段。异常处理通过 @ControllerAdvice统一处理,返回友好的错误信息。
敏感配置信息(邮箱密码、API密钥)在配置文件中加密存储。采用Jasypt库实现配置加密,启动时通过环境变量传入解密密钥。加密后的配置格式为 ENC(加密字符串),JasyptPropertySource在加载配置时自动解密。
发送到阿里云API的邮件内容经过脱敏处理。脱敏规则包括:发件人邮箱地址替换为 [发件人邮箱],收件人邮箱地址替换为 [收件人邮箱],姓名替换为 [姓名],手机号替换为 [手机号]。脱敏在内容清洗步骤之后、AI分析步骤之前执行,确保敏感信息不会离开本地。
本地数据库文件默认不加密,但支持可选的AES加密配置。启用加密后,H2数据库连接URL添加 ;CIPHER=AES参数,启动时提示输入密码。音频文件默认不加密,如果存储在云同步文件夹(如iCloud、Dropbox),建议用户自行加密。
记录所有安全相关事件,包括:登录尝试(成功/失败)、API调用、配置修改、敏感数据访问。日志格式包含时间戳、事件类型、结果、源IP(如果适用)。日志文件限制访问权限(600),仅文件所有者可读。
邮件连接池配置:最大连接数5,最小空闲连接2,最大等待时间10秒,连接验证查询 NOOP(IMAP协议不需要验证查询)。连接空闲超时300秒,超时后自动关闭释放资源。
HTTP连接池配置:最大连接数20,每路由最大连接数10,连接存活时间5分钟,空闲连接检测周期1分钟。
AI分析采用批量处理,每批处理5-10封邮件,减少API调用次数。批量大小动态调整:根据历史平均token消耗和剩余免费额度自动调整,剩余额度多时减小批量(获得更好效果),剩余额度少时增大批量(节省token)。
多级缓存设计:一级缓存Caffeine(内存),二级缓存H2(磁盘)。热点数据(最近处理的邮件、常用配置)驻留一级缓存,冷数据下沉到二级缓存。缓存命中率统计通过Micrometer暴露,用于调优缓存大小。
非核心流程异步化:日志记录、统计更新、通知发送等操作通过 @Async异步执行,避免阻塞主流程。异步线程池配置:核心线程数2,最大线程数4,队列容量100,拒绝策略为调用者运行(避免任务丢失)。
采用SLF4J+Logback日志框架,配置三个日志级别:ERROR日志记录异常情况,WARN日志记录降级和重试,INFO日志记录任务执行情况。日志格式包含时间戳、线程名、日志级别、类名、消息内容。日志文件滚动策略:每天生成一个新文件,保留30天,单个文件最大100MB。
集成Micrometer指标库,暴露JVM指标(内存、GC、线程)、业务指标(邮件处理数、API调用数、分类分布)和自定义指标(处理耗时、API响应时间)。指标可以通过JMX或HTTP(集成Prometheus)获取。
Spring Boot Actuator提供健康检查端点 /actuator/health,包含以下健康指标:邮箱连接状态、API服务状态、磁盘空间、数据库连接。第三方监控系统可以定期轮询该端点,发现异常时告警。
虽然单体应用不需要分布式追踪,但预留了Trace ID机制。每个任务生成唯一Trace ID,贯穿所有日志和数据库记录,便于问题排查时串联相关事件。
应用打包为可执行Jar文件,包含所有依赖(fat jar)。目录结构:
smartmail.jar- 可执行文件config/application.yml- 外部配置文件data/- 数据库文件logs/- 日志文件audio/- 生成的音频文件
提供启动脚本 start.sh,包含以下功能:设置JVM参数(-Xmx256m,适合个人电脑)、加载环境变量(解密密钥)、启动应用、记录PID。停止脚本 stop.sh通过PID优雅关闭应用。
提供Dockerfile,基于 eclipse-temurin:17-jre-alpine基础镜像,减小镜像体积。容器启动时通过环境变量传入配置,数据卷挂载到宿主机,实现数据持久化。
版本升级时,用户替换Jar文件重启即可。数据库版本管理通过Flyway自动执行迁移脚本,无需人工干预。配置文件新增项提供默认值,兼容旧版本配置。
阿里云百炼API可能发生变更、限流或下线。应对策略:API调用封装在独立模块,便于切换供应商;实现本地降级规则,保证API不可用时系统基本可用;监控API调用成功率,异常时自动降级。
不同邮箱服务商对IMAP协议的支持存在差异。应对策略:在 MailFetcher中实现多套策略,根据邮箱类型(通过域名识别)选择最佳策略;提供连接测试功能,用户配置后立即验证;常见邮箱(163、QQ、Outlook)单独测试和适配。
FreeTTS音质一般,对中文支持有限。应对策略:允许用户选择其他TTS引擎,预留 TTSProvider接口;支持导出文本,用户可自行使用更高品质的TTS工具;未来可集成云端TTS服务(如阿里云语音合成)。
邮件数量过多时可能影响系统性能。应对策略:单次处理上限50封;任务超时保护;逐步处理机制(分页获取);用户可配置处理频率和最大处理数量。