不追新,
但每条业务线都要站得住。
五条业务线的技术选型按场景定,不按流行度定。这一页列出每条线各自的技术构成、 成熟度,以及共用的工程与安全底线——包括那些还只是方案储备的部分。
- ISOLATION
- 业务表按 tenant_id 行级隔离
- INTEGRITY
- 关键节点前后哈希关联,可校验
- RUNTIME
- 非 root 容器运行,无 shell 攻击面
五条业务线,各自的技术构成
不同业务线解决不同问题,技术构成也不同。下表按业务线列出主力技术、 当前成熟度与主要约束,避免用一套话术覆盖所有场景。
| 业务线 | 核心技术构成 | 成熟度 | 主要约束 |
|---|---|---|---|
| 信息系统 | Java 17 · Spring Boot · MySQL · Redis · React / Vue 3 · 微信小程序 | 已商用 | 强一致性与多租户隔离是硬要求,技术选型偏保守 |
| AI 智能体 | 大模型 API 集成 · 检索增强(RAG)· OCR 与文档解析 · 工作流编排 | 能力具备 | 效果依赖数据质量,必须配人工复核与降级路径 |
| 学习系统 | 课程与题库模型 · 考核流程 · 学习行为分析 · 移动端优先 | 能力具备 | 内容运营比技术更难,需客户侧持续投入 |
| 游戏 | 微信小游戏 · H5 / Canvas · 轻量包体优化 · 交互与动效 | 能力具备 | 受平台审核与包体限制,玩法需服务于目标而非炫技 |
| 工程与运维 | Docker 容器化 · 非 root 运行 · HTTPS · 审计日志 · 备份恢复 | 已落地 | 随客户环境差异调整,私有化部署需单独评估 |
以下各节以学服溯源项目为例展开。它是最能说明我们工程做法的一个项目, 多租户隔离、存证、设备对接与容器化部署都在其中实际运行。 其余业务线共用这套工程底线,但具体形态取决于项目场景。
分层总览:模块化单体,而非拼凑微服务
平台采用模块化单体(modular monolith)结构:单个可部署单元,内部按领域切分模块, 模块之间通过明确的接口通信。这个选择是刻意的—— 在区域产业带的业务体量下,微服务带来的分布式事务与运维复杂度, 远大于它带来的弹性收益。
代价是横向扩展粒度较粗,收益是追溯链路里的跨模块操作可以在本地事务内完成, 不必为「赋码与质检记录必须同时成功」去设计补偿逻辑。
技术栈:按端划分,各用其长
| 层面 | 选型 | 选择理由 |
|---|---|---|
| 服务端 | Java 17 + Spring Boot | 生态成熟,事务与并发模型适合追溯场景的强一致性要求 |
| 数据访问 | MyBatis 系持久层 | SQL 可控,便于做租户维度的强制条件与索引优化 |
| 存储 | MySQL + Redis | 关系型承载业务与流水,缓存承载令牌与会话态 |
| 企业管理后台 | React + 组件库 | 表单与表格密集,需要成熟的复杂交互组件 |
| PDA / H5 | Vue 3 + 移动端组件库 | 轻量、启动快,适配手持设备与低配安卓机 |
| 家长端 | 微信小程序 + H5 | 微信内直达,无需安装;H5 覆盖跨平台扫码场景 |
| 打印代理 | .NET 桌面代理 | 对接本地标签打印机,绕开浏览器打印的驱动限制 |
| OCR 服务 | 独立 Python 服务 | 面单与单据识别,与主应用解耦,可单独扩容 |
网页开发框架:两条路线,按场景分工
React 和 Vue 我们都在用。这不是「什么都试一下」,而是按端的特性分工: 后台管理要复杂表格与表单,移动端要包体小、启动快,小程序要一套代码多端运行。 用错框架不会让项目失败,但会让每一次改需求都变贵。
选型分工
| 场景 | 选型 | 为什么这么选 |
|---|---|---|
| 后台管理(表格与表单密集) | React 18 + Antd 5 | 复杂表格、可编辑表单、权限树这类组件成熟,二次开发成本低 |
| 数据看板与监管门户 | React 18 + Zustand + ECharts | 图表交互复杂,需要细粒度状态控制,Zustand 比全局单一 store 更轻 |
| 移动端扫码与轻量页 | Vue 3 + Element Plus | 包体与启动速度优先,低配手持机与旧款手机上更稳 |
| 微信小程序 | uni-app(Vue 3) | 一套代码同时出小程序与 H5,家长端与扫码页共用逻辑 |
六个前端工程的实际组合
下面这张表来自各工程的依赖清单,版本可核对。写这一行不是为了罗列, 而是说明「同一个团队能同时维护两套技术栈而不失控」——靠的是统一构建与统一规范。
| 工程 | 端 | 框架 | UI 组件库 | 状态管理 | 构建 |
|---|---|---|---|---|---|
| 企业管理后台 | Web | React 18.3 | Antd 5.17 | 组件状态 | Vite 5.4 |
| 监管端门户 | Web | React 18.3 | Antd 5.17 | Zustand 4.5 | Vite 5.2 |
| 开放平台 | Web | React 18.3 | Antd 5.17 | Zustand 4.5 | Vite 5.2 |
| PDA 扫码端 | 手持设备 | Vue 3.4 | Element Plus 2.7 | 组件状态 + 本地存储 | Vite 5.4 |
| 溯源 H5 | 移动 Web | Vue 3.4 | Element Plus 2.7 | 页面级状态 | Vite 5.4 |
| 家长端小程序 | 微信 | uni-app(Vue 3) | uni-ui | 页面级状态 | HBuilderX |
后端框架
| 层面 | 选型 | 说明 |
|---|---|---|
| 运行时 | Java 17 | 长期支持版本,容器内以 distroless 方式运行 |
| 应用框架 | Spring Boot 3.2.5 | 模块化单体,按领域切分模块,跨模块操作走本地事务 |
| 持久层 | MyBatis-Plus 3.5.6 | SQL 可控,便于做租户维度的强制条件与索引优化 |
| 数据库 | MySQL 8.0.33 | 业务数据与流转流水,迁移脚本按日期版本化管理 |
| 鉴权 | JJWT 0.12.5 | 令牌中携带租户与角色上下文 |
工程化的几条硬规矩
- 全项目 TypeScript:六个前端工程统一 TS 5.4,接口返回类型必须有定义,不用
any兜底。 - 构建工具统一:除小程序外统一 Vite 5,构建配置集中维护,避免每个工程一套写法。
- 路由懒加载:按路由分包,首屏只加载当前页面依赖。
- 请求层统一封装:axios 拦截器统一处理令牌刷新、错误码与静默请求,不在页面里散写。
- 不滥用状态库:Vue 侧两个工程没有引入 Pinia——页面级状态加本地存储足够,多一个依赖就多一处维护成本。
框架跟进的原则:主版本不追新(React 停在 18、Vite 停在 5), 新框架先在自有项目里跑通再用于交付。选型看三年后的维护成本, 不看当下的热度——客户不会因为用了最新版本而受益,但会因为升级踩坑而受损。
多租户隔离:不靠开发者自觉
多租户系统最常见的漏洞不是加密没做好,而是某条查询忘了加 tenant_id,
于是 A 企业的数据被 B 企业看到。这类问题无法靠代码评审完全避免,
必须在架构层面兜底。
- 行级隔离:所有业务表带
tenant_id,租户之间共用表结构但数据严格分离。 - 上下文传递:令牌中携带租户标识,请求进入应用后写入租户上下文,业务代码从中取值。
- 强制兜底:数据访问层对业务表自动追加租户条件,即使业务代码漏写也不会越界。
- 写入校验:新增与更新时校验租户归属,拒绝把数据写到其他租户名下。
- 码段隔离:Ecode 码段按租户分配,跨租户使用同一个码会被直接拦截。
-- 业务查询实际执行的形态(示意) SELECT id, code_value, batch_no, status FROM trace_code WHERE code_value = ? AND tenant_id = ? -- 由上下文注入,业务代码无须重复书写 AND deleted = 0;
一点边界说明:行级隔离意味着租户共享数据库实例, 适用于相互之间没有强物理隔离要求的场景。若监管或客户要求独立数据库实例, 需按私有化部署方案单独评估。
链式哈希存证:让改动留下痕迹
数据库里的记录,管理员理论上可以修改。追溯系统如果无法证明「这条记录没被动过」, 它的证明力就有限。平台的应对方式是把关键节点串成哈希链: 每条记录包含前一条记录的摘要,任何一条被改动,后续链条全部对不上。
// 存证节点的构造方式(示意) node_hash = SHA256( prev_hash + tenant_id + biz_type + biz_id + payload + created_at ) // 校验时从任意节点回溯,逐条重算并比对 verify(n) = node_hash(n) == SHA256( prev_hash(n) + ... ) && verify(n-1)
- 覆盖节点:备案、赋码、质检、出库、查验等关键业务事件写入存证链。
- 可校验:任一节点可独立重算摘要,与链上记录比对,判断是否被篡改。
- 定位断点:校验失败时能定位到具体哪一条记录异常,而不是整体判废。
- 与业务解耦:存证不阻塞业务主流程,写入失败进入补偿队列,不影响正常作业。
需要说清楚的:这是链式哈希存证,不是区块链。 它提供的是「篡改可发现」,而不是「篡改不可能」。 对绝大多数追溯与验收场景已经足够,但不宜表述为区块链存证。
设备与边缘:产线的网络并不总是好的
车间与仓库的 Wi-Fi 覆盖常常不理想,如果扫码端强依赖实时联网, 产线一断网就停摆。平台在设备侧做了相应处理。
- 弱网可用:PDA 端支持扫码记录的本地暂存与恢复联网后的补传,避免作业中断。
- 幂等提交:重复提交同一批扫码记录不会造成重复扣减,网络抖动不会污染库存。
- 设备适配:支持常见手持终端的扫码广播,也支持手机摄像头扫码,不必采购专用硬件。
- 标签打印:打印代理对接本地标签机,码图由服务端生成,避免前端渲染差异导致码不可读。
- OCR 辅助:面单与纸质单据可通过 OCR 服务识别,减少手工录入错误。
部署形态:最小攻击面优先
服务端采用多阶段构建:构建阶段完成编译与打包,运行阶段只保留 JRE 与产物。 运行时镜像基于 distroless,不含 shell、包管理器与常见调试工具, 以非 root 用户运行,显著缩小容器被突破后的可利用面。
| 项目 | 做法 | 目的 |
|---|---|---|
| 基础镜像 | distroless + 固定版本摘要 | 无 shell 可执行,构建可重现 |
| 运行用户 | 非 root(UID 65532) | 遵循最小权限原则 |
| 垃圾回收 | ZGC + 容器内存比例上限 | 降低停顿,避免容器内存超限被杀 |
| 异常策略 | 内存溢出即退出 | 交由编排层重启,避免僵尸状态 |
| 健康检查 | 由外部探针探测应用端点 | 镜像内无 curl,不依赖容器内工具 |
| 交付方式 | SaaS 订阅 / 私有化部署 | 按客户的数据存放要求选择 |
安全机制:分层设防,逐项可查
- AUTH
- 令牌鉴权
令牌绑定租户与角色,支持 HttpOnly Cookie 传输,降低脚本窃取风险
- AUDIT
- 操作留痕
关键操作记录操作人、时间与来源,异常行为可回溯
- MASK
- 数据脱敏
手机号、地址等个人信息按角色脱敏展示,非必要不落明文
- TRANSPORT
- 全站 HTTPS
传输层加密,接口不提供明文回退
- RATE
- 接口限流
按租户配额限制调用频率,防刷与防拖垮
- VALIDATE
- 入参校验
统一校验参数类型与范围,拒绝越权字段与注入尝试
关于等级保护:平台已完成安全自测并整理合规材料, 等保测评需按实际部署环境与属地要求推进,具体进度以双方确认为准。