年卡是票务系统里规则密度高于单次票的一类产品。它同时涉及有效期、可用日期、使用次数、实名绑定与顺延策略,一旦跨过淡旺季切换,这几个维度会互相干扰。本文给出一套可落地的年卡有效期模型,并说明换季时哪些字段需要重算、哪些必须冻结。

一、年卡的五个维度
单次票只需要回答"这张票能不能在某个日期使用"。年卡要回答五个问题:
| 维度 | 含义 | 换季是否受影响 |
|---|---|---|
| 有效期 | 卡从激活到失效的时间区间 | 否(但可能被顺延影响) |
| 可用日期 | 哪些日期可用(工作日/周末/节假日/特定排除日) | 是(淡季可能新增不可用日) |
| 使用次数 | 有效期内可使用次数(不限次 / N 次) | 否 |
| 实名绑定 | 是否绑定持卡人、是否可转让 | 否 |
| 顺延规则 | 不可抗力或闭园时是否延长有效期 | 是(闭园、限流可能触发) |
关键结论:换季影响的是"可用日期"和"顺延规则"两个维度,有效期与次数不应在换季时被重算。
二、有效期模型:区间 + 日历
不要用"激活日 + 365 天"这种单一算法。可行模型是三层:
```
年卡实例
├── validity: start_date, end_date # 有效区间(可被顺延修改)
├── calendar: { # 可用日历(按规则版本生成)
│ weekday_mask: 0b1111111, # 周一至周日可用位
│ exclude_dates: 2026-10-01, ..., # 排除日(节假日、维护日)
│ exclude_ranges: \[2026-12-20, 2027-01-05],
│ rule_version: 3
│ }
└── usage: { limit: null | N, used: n, records: ... }
```
日历按"规则版本"生成,而不是在每次核销时实时判断。换季时新增不可用日期,只需发布新版本日历,历史核销记录不受影响。
```sql
CREATE TABLE annual_card (
card_no VARCHAR(32) PRIMARY KEY,
holder_id VARCHAR(64) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL, -- 可被顺延修改
calendar_ver INT NOT NULL, -- 指向日历规则版本
usage_limit INT NULL, -- NULL = 不限次
usage_used INT NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL -- ACTIVE / SUSPENDED / EXPIRED / REFUNDED
);
```
三、不可用日期的三种写法与代价
| 写法 | 实现 | 换季调整成本 | 风险 |
|---|---|---|---|
| 白名单(只列可用日) | 维护可用日期集合 | 高(每次换季重列) | 漏列即"卡不能用",投诉集中 |
| 黑名单(列不可用日) | 维护排除日期集合 | 低(只加不减) | 排除日叠加后可用日被挤压 |
| 位图 + 规则版本 | weekday_mask + 排除区间 + 版本 | 低(发新版) | 需要版本管理与回滚能力 |
实践中推荐第三种:位图解决"每周哪几天可用",排除区间解决"节假日与维护期",版本解决"规则变更可追溯"。
四、顺延规则:什么时候该延长
顺延是年卡争议集中的地方。触发条件通常有三类:
-
闭园:景区因天气、检修、政策原因停止开放;
-
限流导致无法预约:淡季限流收紧,持卡人多次约不到;
-
不可抗力:公共卫生事件、自然灾害。
工程上要区分"应顺延"与"不应顺延":
| 情形 | 是否顺延 | 判据 |
|---|---|---|
| 全天闭园 | 应顺延 | 当日无任何入园记录且公告闭园 |
| 部分时段关闭 | 视合同 | 若影响超过阈值(如单日 4 小时)可折算 |
| 限流约满 | 通常不顺延 | 卡本身未受限,属于供给约束 |
| 持卡人自身原因 | 不顺延 | 无系统侧事件 |
顺延的实现方式建议用"延长 end_date + 写入顺延流水",而不是"作废重发"。前者保留卡的连续性,后者会产生大量新卡号与对账问题。

五、换季时的四个动作
-
冻结有效期与次数:换季不重算 start_date/end_date 与 usage_limit;
-
发布新日历版本:新增不可用日期,旧版本保留可查;
-
重算剩余可用日:按新日历计算剩余可用天数,用于对外展示与客服解释;
-
生成顺延候选清单:把闭园日与限流约满日列出来,按合同判定是否顺延,人工确认后写入流水。
六、可验证的判据
给三类角色各一条判据:
- 产品:一张年卡在换季前后,剩余可用天数是否能解释清楚来源(有效期 - 排除日 - 已用日);
- 研发:换季发布是否只改日历版本号,而不改卡实例的有效期字段;
- 运营:客服能否在 30 秒内查到某张卡某天为什么不可用(命中哪条排除规则)。

七、结论
年卡真正的难点不是有效期,是可用日期算不清。
换季不是改价,是把三套规则同时搬家。
能配置不等于配置对了;配错了,比不能配更贵。
年卡模型的本质是把"时间"拆成三个可独立演进的维度:区间、日历、次数。拆开之后,换季只是日历换版本,其余两个维度保持稳定------这也是判断一套票务系统年卡模块是否成熟的标志。
(本文为工程实践总结,涉及的产品规则因景区而异。)