本文根据我参与的一套企业内部会议与事项管理系统整理。为保护业务信息,项目名称、组织名称、域名、内部接口和部署标识均已泛化。
会议结束后,结论还要分给具体责任人,约定完成时间,并在接下来几周持续跟进。一份静态纪要很难承载后续的调整、提醒、签核和归档,每一步都有自己的状态、权限与时间规则。
我持续迭代过一套会议与事项管理系统。初期业务集中在会议记录和基础管理,后来陆续加入参会关系、决议事项、进度提醒、版本历史、电子签核和文档归档,后端也随之开始处理业务版本、并发控制、分布式任务、动态字段和文件生命周期。
会议记录是执行流程的起点
一场会议会关联主持人、会议负责人、参会人、事项责任人和签核人。同一个人在不同会议中可能承担不同职责,会议记录需要同时连接人员关系、事项状态和文档版本。
主持人创建草稿后,可以维护会议类型、时间、地点、参会人、负责人、事项和附件。保存分为手动保存与自动保存,发布时再把草稿变成正式记录。发布完成后,系统按配置发送邮件或企业消息,事项责任人开始更新执行状态,定时任务负责临期与延期提醒。需要确认的会议还会进入签核流程,全部同意后生成带签名的归档 PDF。
移动端围绕日常工作组织入口,包括我的会议、我主持的会议、待办、即将延期、已延期、我的关注、团队事项、信息查询和待签核。运营管理端维护会议类型、事项字段、用户扩展信息、系统开关、推送记录和访问统计。
每条决议都有责任人、计划时间、完成时间和状态。主持人、执行人和团队管理者读取同一份会议数据,提醒与签核也沿用其中的人员关系和状态。
权限取决于用户与会议的关系
系统同时存在平台权限和业务权限。平台权限决定账号能否进入管理功能,业务权限决定用户能否查看或修改某一场会议。固定角色无法表达同一个人在不同会议中的职责变化。
草稿的查看范围更严格,只有主持人、系统管理员及被授予编辑关系的人员可以访问。会议发布后,参会人、负责人和事项责任人按各自关系查看内容。团队视图还会结合组织上下级关系,查询直属成员参与的会议和负责的事项。
移动端使用独立的 Token 拦截器。认证完成后,完整用户上下文保存在 Redis 中。请求到达时,拦截器从 Token 还原用户信息并刷新有效期,后续 Service 只需读取当前用户及租户上下文。
租户 ID 在请求进入业务层前写入上下文,新增记录时自动填充。会议、事项、人员关系、附件、签核和日志都保留租户字段,主表与关联表使用相同的数据隔离规则。
权限判断集中在查询条件与业务 Service 中。Controller 识别请求意图,Service 根据会议状态、用户关系和组织范围完成判断,Mapper 把租户与可见范围落实到 SQL。权限规则如果散落在页面入口和单条接口里,新增列表、导出或统计功能时很容易漏掉限制。
业务版本和乐观锁处理不同问题
会议发布以后仍然可能需要调整。直接覆盖旧记录会丢掉当时已经发布的内容,历史通知、事项和附件也会失去对应版本。系统因此为会议设置独立的业务版本。
已发布会议再次编辑时,后端根据最新版本创建新草稿。会议基本信息会被复制,负责人、参会人、事项、事项责任人、动态字段、附件和关注关系也会一并带入。新版本发布后,旧版本继续保留。普通参与者只看到已发布内容,主持人和管理员可以查看完整历史。
事项复制时记录来源事项 ID。即使新版本为事项生成了新的主键和编号,系统仍然能判断两个版本中的记录来自同一项工作。删除未发布版本后,版本号可以按业务规则恢复使用,不会因为一次草稿删除留下没有实际内容的版本空档。
会议主记录和事项记录还保留数据库乐观锁版本。业务版本回答「发布内容演进到了哪一版」,乐观锁版本负责发现并发修改。保存会议时,Service 读取当前乐观锁版本,再更新主记录和人员关系。会议内容、负责人和参会人等关系表在同一事务中完成写入。
这两个版本号不能混用。业务版本属于用户可见的历史,需要支持复制、发布、删除和查询;乐观锁版本是数据库更新条件,用来拒绝基于旧数据的覆盖写入。
固定模型与动态字段各管一部分
会议事项包含描述、责任人、计划完成时间、实际完成时间、状态、备注和附件。责任人使用独立关联表,一项工作可以分给多人。事项状态包括进行中、暂停、完成和取消,完成与取消属于终态,延期判断只针对仍需执行的事项。
不同会议类型对事项内容的要求并不相同。有些事项只需要描述和日期,有些还要记录所属模块、短期方案或长期方案。每增加一个字段都修改事项主表,会让数据库、接口、导出和前端表单一起变化。
项目使用 EAV 模式保存变化较多的补充字段。字段配置维护编码、名称、输入类型、长度、默认值、启用状态和排序,字段值负责关联事项与实际内容。内置字段可以停用但不能删除,业务新增字段由管理端配置。移动端读取启用字段后动态生成表单,导出服务也根据同一份配置组织表头。
事项状态、责任人和完成时间仍然保留在固定模型中。它们参与高频筛选、权限判断、提醒和统计,需要清晰的字段语义与数据库索引。EAV 只保存低频变化的扩展内容,常用查询继续使用固定字段和索引。
提醒任务按照责任关系组织消息
事项提醒由分布式任务调度执行,包括临期提醒、每日延期提醒和历史延期周期汇总。每种任务先读取系统开关,再查询符合时间与状态条件的事项。
提醒按接收人和职责组织。临期事项主要通知责任人,延期信息按主持人或会议负责人汇总。同一接收人在一次任务中只收到一条整理后的消息,其中包含相关会议与事项,发送结果写入推送日志。
任务开始时通过 Redis 获取互斥锁。已有实例正在处理时,本次调度直接结束。发送完成后,系统更新事项的最后推送时间。后续查询同时参考业务日期和发送记录,减少同一批事项在多个实例或相邻调度中重复发送。
发布操作先保存会议状态,再按渠道发送通知,并分别记录邮件、企业消息和接收人信息。再次发送通知只重试外部动作,不会重复改变会议状态。
签核归档必须保留当时的结果
会议可以配置多名签核人,每个人都有待确认、已同意和已驳回三种状态。签核操作只允许本人执行,主持人负责维护签核人列表。签核人同意时,后端保存当时的签名文件路径和签核时间。
后端在签核时保存签名快照。用户后来替换当前签名,不会改变过去的签核记录。删除签名文件前,后端先检查是否仍有签核快照引用,只有不再被引用的对象才会从存储中移除。
全部签核人同意后,附件下载进入归档文档生成流程:
Office 文档和常见图片统一转换为 PDF,原始 PDF 直接进入后处理。
PDF 末页加入签名区,文档尾部追加结构化签核页。
页面写入会议标识水印。
文档允许阅读、打印和复制,编辑、注释、表单填写和页面修改受到限制。
生成文件保存到对象存储,并返回限时访问 URL。
归档文件按附件 ID 和签核版本缓存。签核人或审批状态变化时,缓存失效,再次下载时重新生成。
会议版本之间还会共享附件对象。创建新版本时,附件记录可以复用原来的对象路径。删除其中一个版本的附件时,系统统计同一路径是否仍被其他记录引用,引用数归零后才删除物理文件。数据库记录的生命周期和对象存储中的文件生命周期由此分开管理。
批量组装避免列表查询退化
待办、关注、信息查询和团队视图都要在一页中展示会议、事项、责任人、负责人、附件和动态字段。分页结果如果逐条加载这些关系,SQL 数量会随着记录数增加。
查询服务先收集当前页的事项 ID 和会议记录 ID,再批量查询责任人、附件、动态字段与会议负责人。查询结果按外键分组,最后在内存中组装响应 VO。会议列表也使用相同方法填充负责人、参会人和关注状态。
MyBatis-Plus 继续负责常规分页,复杂筛选和统计查询放在自定义 Mapper SQL 中。接口层只接收 Request 并返回 VO,数据库实体不会直接成为移动端或管理端的响应结构。
导出服务复用相同的查询条件和字段配置,可以生成会议详情、待办、关注内容、团队事项及批量会议记录。Word 与 Excel 导出还要处理动态表头、日期格式、纸张方向、打印区域和分页布局,导出结果还能进入 PDF 转换与签核流程。
分层模块承载不同部署方式
后端基于 Java 17 和 Spring Boot 构建,业务代码按 Controller、Service、Mapper 与领域实体分层。请求模型、响应 VO 和 Converter 独立组织,会议、事项、附件、签核、通知、导出和系统配置各自保留 Service 接口。
数据访问使用 MyBatis-Plus 和自定义 Mapper SQL,关系型数据库保存业务数据,Redis 保存移动端 Token 并承担任务互斥,对象存储负责附件与签名文件。统一身份、组织架构、消息通知、在线会议和文档转换通过独立 Service 或远程客户端接入,核心业务只依赖能力接口。
同一业务模块可以装配为单体或独立服务。单体模式通过本地 API 调用系统能力,独立服务模式通过远程客户端接入注册中心、网关和其他服务。领域代码与数据访问层保持不变,部署差异集中在 Maven 依赖和启动模块中。
核心 Service 与 Controller 使用 JUnit 5、Mockito 和 Spring Boot Test 验证会议权限、版本历史、发布、删除、事项状态、导航统计、附件与签核流程。文档导出与 PDF 处理则围绕日期格式、宽表打印、分页布局、签名位置和并发上传补充用例。
我负责的后端范围
新增功能时,我先确认它属于会议内容、人员关系、事项执行还是文档确认,再确定状态、权限、时间规则与历史保留方式。会议发布、事项执行、提醒、签核和归档都沿用这套判断顺序。
我负责的后端范围覆盖领域建模、移动端与管理端接口、会议版本、事项与动态字段、认证权限、通知任务、附件与导出、签核和 PDF 归档。多轮迭代后,会议记录、事项执行与文档确认使用同一套数据关系,通知渠道、身份系统、文件存储和部署方式仍可以独立替换。
延伸阅读
从一场活动到一套经营系统:多组织实时交易平台的三年架构实践:继续了解多角色权限、业务生命周期、定时任务和模块化单体的设计。
支付与账户平台的一年:业务边界、账本与异步架构:从资金系统角度展开业务事实、状态机、幂等与异步任务。
从 API 到 Worker:Go 服务骨架如何把架构约束写进代码:补充多进程、依赖注入、分层事务和架构验证的实现方式。