Skip to content

Speaker Program 状态体系分析报告 ​

一、核心状态清单 ​

1. Meeting Status(会议/项目状态)— 15个值 ​

Code状态名关联预算版本说明
0AssignedSOW(1)已分配,初始状态
5EstimateSOW(1)估算中
9RegisteredSOW(1)已注册
11WaitlistedSOW(1)等待名单(预算不足时)
12Pending ApprovalSOW(1)等待审批
13DeniedN/A(-1)审批拒绝
6PlanningEST(2)策划中
1BillingBILL(3)账单处理
2CancelledBILL(3)已取消
4ClosedACT(4)已关闭
3Cancelled ClosedACT(4)取消后关闭
14ReopenedACT(4)重新打开
7PostponedCXL(5)已推迟
8Postponed ClosedCXL(5)推迟后关闭
10VoidN/A(-1)作废

数据库实际分布:

状态数量说明
6(Planning)25最多,策划中的项目
3(Cancelled Closed)13取消关闭的项目
4(Closed)9正常关闭
10(Void)7作废
0(Assigned)6初始分配
12(Pending Approval)2待审批
1(Billing)1账单中
2(Cancelled)1已取消
7(Postponed)1已推迟

2. Meeting Approval Status(审批状态)— 3个值 ​

Code状态名说明
1Pending Approval等待审批
2Approved已批准
3Denied已拒绝

审批链类型:

  • Field Exhibit 项目:RBD → Marketing → Legal(从下到上)
  • 非 Field Exhibit 项目:DM → RM → UM(从下到上)

每个层级可配置基于费用阈值(exceedsCost)的规则:费用超过阈值才需要该级审批。

数据库实际分布: Pending=2, Approved=63

3. Budget Version(预算版本)— 5个值 ​

ID名称说明生命周期位置
1SOWStatement of Work初始阶段
2ESTEstimate策划阶段
3BILLBilling账单阶段
4ACTActual最终实际费用
5CXLCancellation取消费用

数据库实际分布: SOW=14, EST=27, BILL=2, ACT=22

4. Registration Site Status(注册站点状态)— 4个值 ​

Code状态名说明
0Draft草稿,正在配置
1Active活跃,开放注册
2Suspend暂停注册
3Completed已完成/关闭

数据库实际分布: Draft=3, Completed=65(没有Active和Suspend的数据)

5. Attendee Registration Status(参会者注册状态)— 7个值 ​

Code状态名说明
0Pending待处理
10Invited已邀请
20Accepted已接受
40Registered已注册
50Cancelled已取消
60Declined已拒绝
70Rep Invited销售代表邀请

数据库实际分布: Pending=2, Invited=801, Registered=390, Accepted=2, Cancelled=21, Declined=2

6. Attendee HCP/Reconciliation Status(参会者HCP核查状态)— 3个值 ​

Code状态名说明
1Not Reconciled未核查
2Reconciled已核查
3Not HCP非医疗专业人员

核查来源类型:NPI(1), Target List(2), Salesforce(3)

数据库实际分布: NotReconciled+NPI=94, NotReconciled+Salesforce=212, Reconciled+Salesforce=912

7. Attendee List Close Status(参会者列表关闭状态)— 2个值 ​

Code状态名说明
0Closed参会者列表已关闭
1Open参会者列表开放

数据库实际分布: Closed(0)=9, Open(1)=59

8. Sign-in Status(签到状态)— 4个值 ​

Code状态名说明
0/NULLDefault未签到
1Signed In已签到
2No Show未出席
3Checked In已签入

数据库实际分布: NULL=1034, Signed In=26, No Show=71, Checked In=87

9. Speaker Status(讲者状态)— 6个值 ​

Code状态名说明
0Inactive未激活
1Active活跃
2W9 Pending等待W9税表
3Contract Creation Pending等待创建合同
4Contract Signature Pending等待签署合同
5Expired已过期

数据库实际分布: Inactive=5, Active=23

10. Contract Status(合同状态)— 2个值 ​

Code状态名
0Not Signed
1Signed

11. Expense Status(费用报销状态)— 4个值 ​

Code状态名
0Draft
1Approved
2Rejected
3Pending Approved

12. Training Status(培训状态)— 3个值 ​

Code状态名
0Not Completed
1Completed
2Waived

13. Meeting Project Task Status(项目任务状态)— 3个值 ​

Code状态名
0Pending
1Completed
2Not Applicable

二、状态之间的关联关系 ​

Meeting Status ↔ Budget Version 强绑定 ​

t_meeting_request_status 表定义了每个 Meeting Status 必须使用哪个 Budget Version:

Assigned(0)          ──── SOW(1)
Estimate(5)          ──── SOW(1)
Registered(9)        ──── SOW(1)
Waitlisted(11)       ──── SOW(1)
Pending Approval(12) ──── SOW(1)
Planning(6)          ──── EST(2)
Billing(1)           ──── BILL(3)
Cancelled(2)         ──── BILL(3)
Closed(4)            ──── ACT(4)
Cancelled Closed(3)  ──── ACT(4)
Reopened(14)         ──── ACT(4)
Postponed(7)         ──── CXL(5)
Postponed Closed(8)  ──── CXL(5)
Void(10)             ──── N/A
Denied(13)           ──── N/A

数据库验证: 交叉查询完全符合这个映射。例如所有 Planning(6) 的记录都在 EST(2),所有 Closed(4) 都在 ACT(4)。

Meeting Status ↔ Approval Status ​

Meeting创建时:
  ├── 不需要审批 → approval_status = APPROVED(2)
  └── 需要审批 → meeting_status = PENDING_APPROVAL(12)
                  approval_status = PENDING(1)
                      │
               ┌──────┴──────┐
               ↓              ↓
         全部批准          任一拒绝
    approval_status=2     approval_status=3
    meeting_status回退     meeting_status=DENIED(13)
    到oldMeetingStatus

Meeting Status ↔ Attendee List Status ↔ Reg Site Status ​

关闭项目前置条件 (closeProgram):
  ├── Meeting Status → CLOSED(4) 需要:
  │     ├── Budget Version = ACT(4)
  │     ├── Attendee List Status = CLOSED(0)
  │     └── TOV (Transfer of Value) 已完成
  │
  ├── Meeting Status → CANCELLED_CLOSED(3): 无额外前置条件
  └── Meeting Status → POSTPONED_CLOSED(8): 无额外前置条件

数据库验证交叉数据:

Attendee ListReg Site数量
Closed(0)Completed(3)9
Open(1)Draft(0)3
Open(1)Completed(3)56

Attendee Registration Status ↔ HCP Reconciliation Status ↔ Sign-in Status ​

这三个状态在参会者维度上独立运作但有时序依赖:

注册流程:  Pending → Invited → Accepted → Registered → (Cancelled/Declined)
             │
核查流程:  Not Reconciled → Reconciled / Not HCP  (可在任意注册状态发生)
             │
签到流程:  Default → Checked In / Signed In / No Show  (活动当天)

Speaker Status ↔ Contract Status ↔ Meeting ​

Speaker 生命周期:
  Inactive(0) → W9_Pending(2) → Contract_Creation_Pending(3)
    → Contract_Signature_Pending(4) → Active(1) → Expired(5)

Contract 绑定:
  Speaker 必须有 Signed 的合同才能参与 Meeting
  Contract Status: Not Signed(0) → Signed(1)

三、完整业务流状态地图 ​

┌─────────────────────────────────────────────────────────────────────┐
│                      PROGRAM LIFECYCLE                              │
│                                                                     │
│  ┌──────────┐   ┌────────────┐   ┌──────────┐                     │
│  │ SPEAKER  │   │  CREATE    │   │  BUDGET  │                     │
│  │ READY    │   │  PROGRAM   │   │  CHECK   │                     │
│  │          │   │            │   │          │                     │
│  │ Active   │──→│ Assigned(0)│──→│ SOW(1)   │                     │
│  │ +Signed  │   │ or         │   │          │                     │
│  │ Contract │   │ Waitlisted │   │          │                     │
│  └──────────┘   │ (11)       │   └──────────┘                     │
│                 └─────┬──────┘                                     │
│                       │                                            │
│              ┌────────┴────────┐                                   │
│              │ APPROVAL NEEDED?│                                   │
│              └───┬─────────┬───┘                                   │
│              No  │         │ Yes                                   │
│                  │    ┌────┴──────────┐                            │
│                  │    │Pending        │                            │
│                  │    │Approval(12)   │                            │
│                  │    │ApprovalSts=1  │                            │
│                  │    └───┬───────┬───┘                            │
│                  │  Approve│      │Deny                            │
│                  │        │      │                                 │
│                  │        │  ┌───┴────┐                            │
│                  │        │  │Denied  │                            │
│                  │        │  │(13)    │                            │
│                  │        │  │AprSts=3│                            │
│                  │        │  └────────┘                            │
│                  ↓        ↓                                        │
│           ┌──────────────────┐                                     │
│           │  PLANNING PHASE  │                                     │
│           │                  │                                     │
│           │  Assigned(0)/SOW │                                     │
│           │      ↓           │     ┌──────────────────┐           │
│           │  Estimate(5)/SOW │     │ REG SITE:        │           │
│           │      ↓           │     │ Draft(0)→Active  │           │
│           │  Planning(6)/EST │←───→│ (1)→Completed(3) │           │
│           │      ↓           │     └──────────────────┘           │
│           │  Billing(1)/BILL │                                     │
│           └────────┬─────────┘     ┌──────────────────┐           │
│                    │               │ ATTENDEES:       │           │
│                    │               │ Invite→Accept→   │           │
│              ┌─────┴──────┐        │ Register         │           │
│              │   EVENT    │        │ Reconcile(HCP)   │           │
│              │   DAY      │←──────→│ Sign-in/Check-in │           │
│              └─────┬──────┘        └──────────────────┘           │
│                    │                                               │
│           ┌────────┴─────────┐                                     │
│           │  CLOSE-OUT PHASE │                                     │
│           │                  │                                     │
│           │ Preconditions:   │     ┌──────────────────┐           │
│           │ ✓ Budget=ACT(4)  │     │ ATTENDEE LIST:   │           │
│           │ ✓ List=Closed(0) │←───→│ Open(1)→Closed(0)│           │
│           │ ✓ TOV completed  │     └──────────────────┘           │
│           │      ↓           │                                     │
│           │  Closed(4)/ACT   │                                     │
│           └────────┬─────────┘                                     │
│                    │                                               │
│              ┌─────┴──────┐                                        │
│              │  REOPEN?   │                                        │
│              │ Reopened   │                                        │
│              │ (14)/ACT   │                                        │
│              └────────────┘                                        │
│                                                                     │
│  ── ALTERNATIVE PATHS ──                                           │
│                                                                     │
│  Cancel: Any → Cancelled(2)/BILL → Cancelled Closed(3)/ACT        │
│  Postpone: Any → Postponed(7)/CXL → Postponed Closed(8)/CXL      │
│  Void: Any → Void(10)/N/A                                         │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

四、当前实现的问题与发现 ​

问题 1:Meeting Status 编码不连续,语义模糊 ​

15个状态的编码(0-14)并不连续,且有些状态语义重叠:

  • Assigned(0) vs Estimate(5) vs Registered(9):三个状态都绑定 SOW,但在实际数据中 Estimate(5) 和 Registered(9) 从未使用
  • Cancelled(2) vs Cancelled Closed(3):取消后为什么还有两个阶段?因为 Cancelled 仍在 BILL 阶段需要结算取消费用,Cancelled Closed 才是最终态

问题 2:Budget Version 是"被动绑定"而非"主动驱动" ​

Budget Version 与 Meeting Status 通过 t_meeting_request_status 表进行静态映射。但实际上 Budget Version 的切换是通过 BudgetItemService.copy() 手动触发的,不会自动切换 Meeting Status。这导致:

  • Meeting Status 和 Budget Version 可能短暂不一致
  • 没有严格的状态机保证

问题 3:Attendee List Status 的值设计反直觉 ​

OPEN=1, CLOSE=0 — 0 表示关闭,1 表示打开。同时 label 用的是 "YES"/"NO"(YES 表示已关闭)。这种反直觉的设计容易在前端和业务逻辑中引起混淆。

问题 4:Registration Status 编码间隔不均 ​

使用 0, 10, 20, 40, 50, 60, 70 作为编码值,留出了 30 的空位。推测最初设计预留了扩展位,但 30 从未使用,且 70(RepInvited) 实际也没有数据。

问题 5:HCP Reconciliation 核查率较低 ​

数据库中 306 个参会者未核查(Not Reconciled),912个已核查。未核查率约 25%,分布在 NPI(94) 和 Salesforce(212) 两种来源。这些可能是关闭项目的阻塞项。

问题 6:Sign-in Status 大量 NULL ​

1034/1218(85%)的参会者 sign_in_status 为 NULL(而非 DEFAULT=0),这说明代码中没有在创建参会者时设置默认值。

问题 7:缺少正式的状态机框架 ​

状态转换逻辑分散在 ProgramService.java 的多个方法中,没有使用状态机模式(如 Spring State Machine),缺少:

  • 转换约束的集中定义
  • 非法转换的防护
  • 审计追踪的统一触发

五、优化建议 ​

建议 1:引入正式状态机(State Machine) ​

现状: 状态转换逻辑分散在 ProgramService 的 createMeetingRequest(), updateApprovalStatus(), cancelProgram(), closeProgram(), reopenProgram() 等多个方法中。

建议: 使用 Spring State Machine 或自定义枚举状态机:

定义合法转换:
ASSIGNED → PLANNING, PENDING_APPROVAL, CANCELLED, VOID
PENDING_APPROVAL → ASSIGNED(approved), DENIED(denied)
PLANNING → BILLING, CANCELLED, POSTPONED
BILLING → CLOSED, CANCELLED
CLOSED → REOPENED
REOPENED → CLOSED

好处:非法状态转换在编译期/运行时被拦截,审计日志自动生成。

建议 2:精简 Meeting Status,减少不必要的中间态 ​

建议精简方案(10个核心状态):

状态含义阶段
DRAFT草稿/初始创建
PENDING_APPROVAL等待审批审批
DENIED审批拒绝审批
PLANNING策划中准备
CONFIRMED已确认(含Billing)执行
COMPLETED已完成收尾
CLOSED已关闭归档
CANCELLED已取消终止
POSTPONED已推迟暂停
VOID作废终止

合并逻辑:

  • Assigned + Estimate + Registered + Waitlisted → DRAFT(预算不足标记为单独 flag 而非状态)
  • Billing → CONFIRMED
  • Cancelled Closed + Postponed Closed → 在 CANCELLED/POSTPONED 上增加 closedOut: boolean 标记

建议 3:统一 Attendee List Status 的语义 ​

当前: OPEN=1, CLOSE=0(反直觉)
建议: OPEN=0, CLOSED=1(或使用布尔值 isListClosed: true/false)

建议 4:Budget Version 与 Meeting Status 的自动联动 ​

当前 Budget Version 切换(BudgetItemService.copy())不会自动更新 Meeting Status。建议:

  • Budget 从 SOW→EST 时,自动将 Meeting Status 推进到 Planning
  • Budget 从 EST→BILL 时,自动推进到 Billing/Confirmed
  • Budget 从 BILL→ACT 时,验证所有收尾条件并自动推进到 Completed

建议 5:增加 Close-Out Checklist 前置条件检查 ​

当前关闭项目的前置条件(Budget=ACT, AttendeeList=Closed, TOV完成)分散在代码逻辑中。建议提供统一的 close-out readiness API:

json
GET /programs/{id}/close-out-readiness
{
  "budgetInACT": true,
  "attendeeListClosed": true,
  "tovCompleted": false,
  "allAttendeesReconciled": false,
  "allSignInsRecorded": false,
  "projectTasksCompleted": true,
  "readyToClose": false,
  "blockers": ["TOV not completed", "25 attendees not reconciled", "1034 sign-ins missing"]
}

建议 6:为 Attendee 引入完整生命周期视图 ​

当前 Attendee 有三个独立状态维度(Registration、HCP、Sign-in),但缺少一个统一的"就绪度"指标。建议增加计算属性:

Attendee Readiness Score:
  ✓ Registered (reg_status=40)     → +1
  ✓ Reconciled (hcp_status=2)      → +1
  ✓ Signed In (sign_in=1 or 3)     → +1
  Score 3/3 = Fully Complete

建议 7:Approval Chain 支持可配置审批流 ​

当前审批链硬编码了两种模式(Field Exhibit 和非 Field Exhibit)。建议改为配置驱动:

  • 支持自定义审批层级数量
  • 支持并行审批(而非仅串行)
  • 支持条件分支(不同金额走不同审批链)
  • 考虑集成通用工作流引擎

建议 8:修复默认值和数据一致性 ​

  • 创建 Attendee 时初始化 sign_in_status = 0(而非 NULL)
  • 创建 Meeting 时初始化 attendee_list_status = 1(Open)
  • 确保 hcp_status 始终有值(默认 NOT_RECONCILED=1)
  • 为状态字段添加数据库级 NOT NULL 和 DEFAULT 约束

六、总结 ​

整个系统围绕 Meeting/Program 为核心实体,构建了一套完整但分散的状态管理体系。核心关系链是:

Speaker(Active+Signed) → Program(Meeting Status) ↔ Budget Version
                              ↕                        ↕
                        Approval Status          Close-out Gates
                              ↕                        ↕
                   Registration Site ← Attendee(Reg+HCP+SignIn)
                              ↕
                     Attendee List Status

最大的架构痛点是缺乏正式的状态机框架,所有转换逻辑都以 if-else 硬编码在 Service 层。这使得:

  1. 新状态/新规则的添加容易遗漏边界情况
  2. 状态一致性依赖开发者自律而非框架保障
  3. 审计追踪需要手动触发而非自动完成

如果要进行一次有意义的改造,优先级最高的是建议1(引入状态机)和建议5(Close-Out Checklist API),这两项改动影响面小但能显著提升数据一致性和运维体验。