Skip to content

Pharmagin 讲者平台 - 完整重构规范

版本: 2.0 | 日期: 2026-02-21 基于: 跨 15 个业务领域的 22 份分析文档


目录

  1. 执行摘要
  2. 当前系统问题
  3. 目标架构概述
  4. 领域重构
    • 4.1 项目生命周期 (Program Lifecycle)
    • 4.2 讲者管理 (Speaker Management)
    • 4.3 参会者与注册 (Attendee & Registration)
    • 4.4 预算与财务 (Budget & Financial)
    • 4.5 合规与监管 (Compliance & Regulatory)
    • 4.6 用户与权限 (User & Permission)
    • 4.7 沟通与通知 (Communication & Notification)
    • 4.8 地理与管辖区 (Geographic & Territory)
    • 4.9 报告与分析 (Reporting & Analytics)
    • 4.10 配置与特性管理 (Configuration & Feature Management)
    • 4.11 内容与文档 (Content & Document)
    • 4.12 问卷与反馈 (Survey & Feedback)
    • 4.13 费用管理 (Expense Management)
    • 4.14 虚拟项目 (Virtual Program)
    • 4.15 集成 (Integration)
  5. 统一数据模型
  6. API 架构
  7. 前端架构
  8. 安全架构
  9. 基础设施与 DevOps
  10. 迁移策略
  11. 实施路线图

1. 执行摘要

1.1 平台目的

Pharmagin 是一个医药讲者项目管理平台,支持以下角色:

  • 代理策划团队 (Agency Planners) 配置、管理并结算讲者项目
  • 销售代表 (Sales Representatives) 提交项目申请并管理讲者互动
  • 讲者 (Speakers/HCPs) 管理个人资料、合同、培训及费用

1.2 当前系统规模

指标数量
后端模块37
数据库实体138+
前端应用3 (Plannerview, Salesview, Speakerview)
外部集成12
特性开关(Feature flags)165+
API 端点~200+
业务规则123+
已知问题332

1.3 为什么要重写

当前系统存在以下问题:

  • 18 个严重的安全漏洞(SQL 注入、RCE、凭证暴露、缺少鉴权)
  • 架构技术债:12 个上帝类(600-2100+ 行),包含 98 个字段的实体,散落各处的硬编码状态机
  • 可扩展性瓶颈:内存鉴权令牌、本地文件系统存储、线程不安全的单例
  • 数据模型不一致:7 种不同的软删除模式、10+ 个无类型的 JSONB 字段、缺少主键 (PK)
  • 技术栈老旧:React 15-16.x, Ant Design 3.x, Spring Boot 2.1.7 (已终止支持)
  • 缺乏正式的状态机:所有状态转换在服务层中以硬编码的 if-else 形式存在

1.4 重写目标

  1. 安全第一:消除所有 18 个严重漏洞;每个端点强制实施 RBAC(基于角色的访问控制)
  2. 领域驱动设计 (DDD):将上帝实体分解为有界上下文 (Bounded Contexts)
  3. 生命周期状态机:引入正式的状态机并结合分阶段的锁定机制
  4. 基于品牌的预算:预算分配由品牌(药物)驱动,而不仅仅依靠地理位置
  5. 统一组织架构层级:用灵活的 OrgNode 树取代硬编码的 4 层地理结构
  6. 现代化技术栈:Spring Boot 3.x, React 18+, TypeScript, Vite
  7. 水平扩展能力:无状态 JWT、分布式缓存、对象存储

2. 当前系统问题

2.1 严重安全问题 (P0)

ID问题所在位置风险
S-01SQL 注入SiteController, FileService, ExpenseMapper, Salesforce SOQL数据泄露
S-02远程代码执行 (RCE)Custom Reports 的 ProcessBuilder服务器被完全攻陷
S-03API 密钥暴露/v1/public/configuration 暴露 SendGrid/SFTP/API 密钥凭证被盗
S-04未经过滤的数据导出执行绕过权限过滤器的原生 SQL数据外发
S-05SSO Cookie 不安全缺失 HttpOnly/Secure/SameSite 标记会话劫持
S-06遗留的明文密码旧版密码比对逻辑仍在起效账户被盗
S-07JWT 声明未校验External-Authorization 缺少受众 (audience) 和过期校验令牌伪造
S-088 个未受保护的 ControllerUser, Role, Geography, SalesForce, Team, Content, File, Compliance未经授权的访问

2.2 架构反模式 (Anti-Patterns)

模式实例数影响
上帝类 (>600 行)12AttendeeService (2100+), ProgramService (1577), ProductReportService (1430)
上帝实体 (>70 个字段)4MeetingRequest (98), Speaker (88), Meeting (77), Attendee (70+)
命名混乱6+MeetingRequest 就是 Program, Meeting 就是 RegistrationSite, SalesTeam 就是 SalesForce
滥用 JSONB (无类型)10+approvals, dynamicFields, virtualProgramInfo, content, config
硬编码值15+productId==24, categoryId 9/4, 财年 2025, DST 范围 2017-2030
缺失 @Id5MeetingChangeLog, FiscalYear, AttendeeSurveyResponse, UserProductRole, SiteTemplate
软删除模式不一致7 种Boolean, Integer, String, status 字段, del_flag, deleted, 以及硬编码
双重路口7/meetings 与 /programs 混用, /site 与 /registration 混用等

2.3 可扩展性瓶颈

  • 基于内存的 Guava Cache 管理鉴权令牌(无法水平扩展)
  • 本地文件系统 存储文件(无法多节点部署)
  • 线程不安全的单例(FieldMappingFactory, SfdcFacade, ZoomAccessTokenManager)
  • SimpleDateFormat 实例变量(存在竞态条件)
  • 原生 Thread() 用于文档生成(没有线程池)
  • 无消息队列(邮件发送采用发后即忘的 @Async)
  • 无分布式缓存(重启服务会丢失会话)
  • 无 API 网关(各服务自行实现鉴权/限流)

2.4 数据完整性风险

  • 缺少事务处理:区域/管辖区重新分配、参会者回复保存、Agg Spend 配置、预算分配
  • 硬删除:Speaker, Expense, MeetingAlert(附带空 Example 会导致删除全体数据)
  • 孤儿数据:t_user_auth_token 无上限膨胀、临时文件泄漏、历史表数据堆积
  • 25% 的参会者未对账率,导致项目无法结算
  • 85% 是 NULL 的 sign_in_status(创建时从未初始化)

3. 目标架构概述

3.1 架构原则

  1. 有界上下文 (Bounded Contexts):每个领域拥有专属实体、服务与 API
  2. 事件驱动 (Event-Driven):利用领域事件实现跨域通信
  3. 状态机优先 (State Machine First):所有生命周期类实体都采用正式的状态机
  4. 渐进式锁定 (Progressive Locking):随着生命周期推进,各项权限逐步收窄
  5. 品牌优先预算 (Brand-First Budget):预算层级自品牌起,其次才是地理位置
  6. 配置即数据 (Configuration as Data):特性的开关存储在数据库中,支持运行时切换
  7. 默认安全 (Security by Default):所有端点默认加上 @PreAuthorize,所有查询采用参数化

3.2 技术栈

层级当前目标
Java817+
Spring Boot2.1.73.2+
Spring SecurityOAuth2/JWT (自定义)Spring Security 6 + OAuth2 Resource Server
ORMMyBatis + tk.mybatisMyBatis-Plus 或 jOOQ
数据库PostgreSQLPostgreSQL 15+
缓存Guava (基于内存)Redis
消息队列RabbitMQ 或 SQS
文件存储本地文件系统S3/MinIO
配置Spring Cloud Config (YAML)数据库为主 + Spring Cloud Config (降级方案)
React15-16.x18+
UI 框架Ant Design 3.xAnt Design 5.x
路由React Router 3-4.xReact Router 6+
状态管理Redux (手动模板代码)Redux Toolkit + RTK Query
构建工具CRACOVite
语言JavaScriptTypeScript
微前端Single-SPA (SystemJS)Module Federation 或是 Single-SPA (ES modules)

3.3 服务架构

                    ┌─────────────────┐
                    │     API 网关     │  (鉴权, 限流, 路由)
                    │   (Spring Cloud  │
                    │    Gateway)      │
                    └────────┬────────┘

              ┌──────────────┼──────────────┐
              │              │              │
     ┌────────▼───┐  ┌──────▼──────┐  ┌───▼────────┐
     │ pharmagin  │  │ pharmagin   │  │ pharmagin  │
     │ -web       │  │ -auth       │  │ -worker    │
     │ (REST API) │  │ (SSO+JWT)   │  │ (Async)    │
     └─────┬──────┘  └─────────────┘  └─────┬──────┘
           │                                  │
     ┌─────▼──────────────────────────────────▼─────┐
     │                 PostgreSQL                     │
     │  schemas: ooto, public, target                 │
     └──────────────┬─────────────────────────────────┘

     ┌──────────────┼──────────────┐
     │              │              │
  ┌──▼──┐    ┌─────▼──┐    ┌─────▼────┐
  │Redis │    │S3/MinIO│    │RabbitMQ  │
  └──────┘    └────────┘    └──────────┘

服务职责:

  • pharmagin-web: 核心 REST API (处理全部 15 个领域)
  • pharmagin-auth: 统一的 SSO 登录中心(合并 sso 与 login)+ 生成 / 验证 JWT
  • pharmagin-worker: 异步任务节点(发送邮件, 数据报表, 第三方集成, Cron 调度)
  • pharmagin-config: Spring Cloud Config Server (保留用于非核心的 YAML 降级)

3.4 领域划分视图 (有界上下文)

(译注:图表同原文档一致,分为核心业务领域、支撑领域和集成层)


4. 领域重构

4.1 项目生命周期领域 (Program Lifecycle)

4.1.1 实体更名

现名新名理由
MeetingRequestProgram反映真实的业务概念
MeetingRegistrationSite澄清此实体只扮演注册门户或站点的角色
MeetingProgramTypeProgramType简化命名
ProgramServiceTypeServiceType简化命名
MeetingChangeLogProgramChangeLog命名一致性
MeetingProjectTaskProgramTask命名一致性

4.1.2 实体拆解

当前: MeetingRequest (包含 98 个字段) - 标准上帝实体。 目标: 拆解为职能单一的多个实体。拆分后的表涵盖 Program核心、ProgramLocation(地点)、ProgramRequestor(申请人)、ProgramSpeaker(1对多分配讲者)、ProgramFinancial(财务估算)、ProgramApproval(1对多审批明细)、ProgramCustomField(1对多自定义字段)。

(详细字段设计参考原英文文档)

4.1.3 项目状态机 (14 个状态)

引入一套严谨的 14 个阶段状态机,彻底替换混乱的 old_status 管理法。

核心状态: DRAFT, WAITLISTED, PENDING_APPROVAL, DENIED, PLANNING, REGISTRATION_OPEN, REGISTRATION_CLOSED, EVENT_COMPLETE, RECONCILED, CLOSED, CANCELLED, POSTPONED, VOID, REOPENED.

预算阶段 (Budget Version) 会直接由以上状态得出 (SOW、EST、BILL、ACT、CXL),取消数据库单独字段的冗余存储。

4.1.4 渐进式权限矩阵 (MeetingPhaseGuard)

在服务端实施一个核心权限门控。通过矩阵判断:例如处于 PLANNING 时,可以编辑讲者和预算;但到了 REGISTRATION_CLOSED 时刻,参会者和基础信息均被锁定,此时重点转入现场活动及对账。

4.1.5 自动状态切换

基于时间的触发器(Cron 作业,每小时运行):自动关停注册,或在会议时间结束后自动转为 EVENT_COMPLETE基于条件的流转:依赖系统通知,提醒执行结算步骤(计算 TOV、确认预算等于)。

4.1.6 移除重复状态字段

原系统中的 budget_version_id, budget_status, reg_site_status, attendee_list_status 都会被合并至统一的 Program Status 判断中。

4.1.7 项目结算就绪状态 API

提供 /api/v2/programs/{id}/close-out-readiness 用于一键审计项目是否具备完结 (Closed) 的条件(如对账、签到、TOV 是不是全部完成)。


4.2 讲者管理领域 (Speaker Management)

4.2.1 实体拆解

把含有 88 个字段的 Speaker 原表拆为核心 Speaker 表、SpeakerAddress(地址库)、SpeakerFinancial(1对1财务信息)、SpeakerCredential(医疗执照、证书等 1对多集合)和 SpeakerProfile(个性化偏好及履历)。

4.2.2 讲者合同模型重构

原硬编码了超过 15 个酬金字段的表,替换为了 SpeakerContract 加 1对多的 ContractHonorariaItem 以及 ContractTerritory 以支持灵活的酬金设定。

4.2.3 状态机

精简了不必要的状态,确立为: DRAFT (草稿) → ONBOARDING (补充资料中) → CONTRACT_PENDING (等签) → ACTIVE (激活生效) → SUSPENDED (临时冻结) → EXPIRED (过期)。


4.3 参会者与注册领域 (Attendee & Registration)

  • 优化表字段,抽取单独的 AttendeeSignatureAttendeeVirtualInfo
  • 确立唯一的注册状态枚举流转过程。
  • 对账服务 (Reconciliation) 统一化:利用策略模式,整合原来的三种对账方式 (NPI, Target, Salesforce);满足匹配要求后自动将状态判定为 RECONCILED

4.4 预算与财务领域 (Budget & Financial)

4.4.1 “品牌优先”的预算模型

问题:当前系统以区域为主进行下拨,不符合实际。 目标:设立多层预算池体系: t_budget_pool 顶部为品牌母池,向下分配至 大区/销区。并且统一记录每次流动的 t_budget_transaction (划拨/调整审计轨迹),和各项对应消耗 t_budget_consumption

4.4.2 预算约束 (Cap)

将判断维度划分为纯粹的两股独立维度:cap_type (金额/数量/两者皆限) 和 sharing_mode (指定/跨类型共享)。

4.4.3 后向兼容

为遗留的 API 创建诸如 v_legacy_region_budget 等数据库视图。


4.5 合规与监管领域 (Compliance & Regulatory)

  • 价值转移 (TOV) 计算:必须在项目处于 RECONCILED 和名单确认后才可计算。公式:总审计后花费 / 人数。可进行修改追踪。
  • 汇总统计 (Aggregate Spend):支持更多灵活报表的映射输出。
  • 文档审计检查单:标准化了项目文档的检查审核流程表。

4.6 用户与权限领域 (User & Permission)

4.6.1 认证架构更新

利用 Redis 存贮会话信息以及基于 JWT 做无状态访问。提供用于刷新的 Refresh Token (7天),并将 Access Token 生命周期控制在 15 分钟内。

4.6.2 统一的 RBAC (基于角色的权限访问控制)

把原来各自独立的 Role 双系统合成一套。增加资源限制维度 (实例级别 / 公司级别 / 品牌全网 / 特定管辖区级别)。对于 API 强制加设 @PreAuthorize 保护并实现基于权限等级及地域的 DataScopeFilter 过滤。


4.7 沟通与通知领域 (Communication & Notification)

  • 统一通知存储至 t_notification,代替分散各个业务实体的通知系统。
  • 引入可靠的电子邮件发送架构:请求 → RabbitMQ → pharmagin-worker → SendGrid,附带错误追踪与重试,发送状态存在 t_email_log
  • 将动态模板定义移到单独的 t_email_template 引擎进行预置字段替换渲染。

4.8 地理与管辖区 (Geographic & Territory)

采用更加灵活的 t_org_node 树代替了当前写死的 SalesForce 层级:可以完美承载不同规模药企的多层树结构;基于生效日期、物化路径(Materialized path)加速查询,通过沿着节点树往上寻找最近的 Planner 分配配置。


4.9 报告与分析 (Reporting & Analytics)

摒弃调用原始 Python/R 脚本、裸 SQL,改为 Java 构建的统合报表引擎。耗时长的报表采用异步队列处理,存入 S3/MinIO 给终端下载;并把原本静态的自定义字段声明全面数据库化。


4.10 配置与特性管理 (Configuration & Feature Management)

将 165+ 个分散且经常需要重启才生效的配置全部放入数据库 t_feature_flag;进行布尔状态变量正向化命名清理 (disableenabled)。安全方面剥离任何涉及公开配置界面的敏感串,严格分成普通展示接口和鉴权 Admin 接口。


4.11 内容与文档 (Content & Document)

  • 统一所有的媒体形态进 t_asset
  • 彻底将存储从本地切换至云端存储 S3/MinIO。
  • 修复安全漏洞:对文件执行参数化处理,上传端进行严苛校验(后缀、大小、真实MIME 类型),取缔直接展示文件目录的原型调试接口。

4.12 问卷与反馈 (Survey & Feedback)

  • 前后端去关联解耦:不再强依赖硬编码针对不同公司的 Survey 表单处理。
  • Survey 中由 JSONB 做内容预设,全部统一用 DTO 来接纳解析;所有的问卷表均带上主键并迁移入单一问卷存储架构下。

4.13 费用管理 (Expense Management)

增加独立状态机审批 (Draft → 待审批 → 审批完成/退回)。硬性规定系统利用项合计值计算得出总支费用,而不再由前端传递该数字。 把发票直接上传到文件模块;不再在 Expense 中冗余存 base64 体。


4.14 虚拟项目 (Virtual Program)

建立两张专用于存储会话数据的独立表,并提供更好的 Zoom 集成代码及每个产品对应的 Token 管理(废弃之前的单例串扰);提供第三方的简单 URL 会议集成入口。


4.15 集成领域 (Integration)

  • Salesforce: 转投更快的 REST API
  • 使用 Resilience4j 包裹所有第三方系统的调用(熔断、限流、重试防呆保护)。
  • 密码存储: yaml 内文变为诸如 AWS Secrets Manager 等秘钥引用。

5. 统一数据模型

主要改动原则:遵循统一 t_ 开头的表名、id (BIGINT 类型) 标准、强一致 deleted 表示软除标识、取消冗余表等。


6. API 架构

  • 按领域拆成了命名干净的一组 REST 端点 (/api/v2/{domain}/{resource})
  • 推荐游标式分页查询。采用 RFC 7807 报错反馈范式。
  • 修改并触发状态的方法全部集中在 PATCH /api/v2/programs/{id}/status ,拒绝其他野路子的修改途径,利用异常报错反馈哪些数据环节仍旧卡点。

7. 前端架构

全面将 CRACOJSReact 15 转成更快的并采用 TypeScript 的 Vite, React 18, Ant Design 5 全家桶架构; 创建独立的公共组件包 @pharmagin/ui 以避免在 Plannerview 等四个项目中各写一套冗余内容,术语上也全部收束为 ProgramRegistration Site


8. 安全架构

  • 鉴权依赖 JWT + Redis
  • 核心漏洞彻底干预:针对 RCE 删除 ProcessBuilder,把所有裸 SQL 和注入替换为预编译安全框架。加入 XSSUploadSFTP 后门检测方案;端点鉴权防护达到全覆盖。

9. 基础设施与 DevOps

清晰化梳理各个容器件关系 (Web、Job Worker 等服务独立,挂载 Redis/PostgreSQL 及 MQ 体系),强制采用 Actuator/Grafana 埋点追踪、执行 MDC 相关请求 ID,为以后的弹性伸缩提供可能。


10. 迁移策略

按照高保真但低风险的周期性执行路线:

  • 阶段 0 (立即执行):处理当前系统的各种要命安保漏洞;无数据库形态修改。
  • 阶段 1:铺设后端的底盘基建;引入基础状态机机制。
  • 阶段 2:高风险。进行原上帝实体表的各种手术拆分,做老表视图代理回老数据。
  • 阶段 3:重构并替换组织结构和预算分配池逻辑。
  • 阶段 4:前端架构拆分,组件复用,推陈出新。
  • 阶段 5/6:第三方集成与报表功能的现代翻修。

11. 实施路线图

按照 P0 至 P3 分发工作优先级;设定各项业务漏洞消除指征、覆盖度指征用于验证成果。并严格按照测试、兼容保留和逐级发布作为主要的降低风险机制。

(本文完)