代金券与资源到期盯守:每日快照加一次性兑换开关

举报
yd_254065487 发表于 2026/10/22 07:44:23 2026/10/22
【摘要】 券的余额和有效期是两个维度,只看余额会漏掉过期。这里给一套每日只读快照、写操作隔离、兑换做成一次性开关的盯守方案。

一张 5000 元的券,余额还是满的,人却没发现它三天后过期

去年一批代金券进账后,我们盯守脚本的判断逻辑只有一行:balance > 0 就认为安全。结果一张 5000 元的券在月底 23:59 过期,次月账单原价付。复盘下来问题不在脚本写得差,在于盯错了维度。

库存和有效期是两个独立维度

券至少有四种“看起来没事”的状态,只看余额一种都发现不了:

  • 余额 5000,但到期时间在 3 天后;
  • 余额 5000,但生效时间在未来,现在用不了;
  • 余额 5000,但适用产品范围不含你正在跑的业务,等于零;
  • 余额为 0,但确实已经用完,属于正常。

所以快照要抓的是一整行,而不是一个数字:资源类型 / 资源ID / 面额 / 剩余可用 / 生效时间 / 到期时间 / 状态 / 适用产品。包年包月资源同理,除了到期时间,还要把“是否已开自动续费”一起抓进来——开了自动续费的到期告警和没开的,处理动作完全不同。

为什么是每日快照,不是实时轮询

券的状态和资源到期时间,变更频率是天级的,不是秒级。拿 1 分钟一次的轮询去盯它,三个坏处:

  • 收益接近零:一天 1440 次调用,其中 1439 次拿到的是同一份数据;
  • 撞限流:这类查询接口普遍有 QPS 和日调用量配额,轮询很容易打出 HTTP 429 或 RequestLimitExceeded,把真正需要调接口的其他系统一起拖下水;
  • 没有历史:实时轮询只保留最后一次状态,事后你回答不了“这张券是哪天从 5000 变成 0 的”“上周是不是就该告警了”。

每日快照还有个附带好处:天然幂等。同一天重复跑,只是覆盖同一个文件,不需要去重逻辑。建议每天凌晨跑一次全量,落成 /data/cost-snapshot/YYYYMMDD/*.tsv,同步一份到对象存储保留 90 天。时间统一用 UTC 存储,展示时再转 UTC+8,避免机器时区漂移导致“剩 1 天”和“剩 0 天”算错。

采集脚本只做三件事:拉、规整、算待办

脚本骨架大致是这样,注意查询操作名以你本地 CLI 的 hcloud <服务> --help 输出为准:

  • DAY=$(date -u +%Y%m%d); OUT=/data/cost-snapshot/$DAY; mkdir -p $OUT
  • hcloud <服务> <只读查询操作> --cli-region=cn-north-4 > $OUT/coupons.raw.json
  • jq -r '.items[] | [.id,.amount,.balance,.effective_time,.expire_time,.status] | @tsv' $OUT/coupons.raw.json > $OUT/coupons.tsv

判定放在本地做,剩下的天数 = 到期日 00:00(UTC+8)减去今天。规则可以粗一点:剩余 ≤ 7 天且余额 ≥ 100 元,进提醒;≤ 3 天,每天提醒;资源到期 ≤ 15 天且未开自动续费,进提醒。告警文案把结论和动作都写清楚,例如:“coupon-xxxx 面额 5000,剩余 5000,10-24 23:59 到期,适用 ECS/EVS。动作:确认是否需在 10-24 前下单抵扣。本条消息未执行任何操作。”

兑换动作:一次性开关 + 幂等记录 + 失败不自动重试

兑换、激活、下单抵扣都是写操作,必须单独一个脚本,和采集脚本没有任何调用关系。

一次性开关:默认关闭。[ "${COUPON_REDEEM_ENABLED:-0}" = "1" ] || { echo switch off; exit 0; }。要执行时由人临时打开,跑完立刻关掉,不留常开状态。

幂等记录:以“券ID + 动作”为唯一键落一张本地表,建唯一索引,插入成功才继续;插入冲突说明执行过,直接退出。这样即使 cron 抖了两次,也只可能进一次。

失败不自动重试:写接口超时不等于没成功,重试就可能重复兑换。记录分三态:started / done / failed。超时或报错就停在 started,把返回里的 X-Request-Id 原样打出来,人工拿这个 ID 去控制台核对,再手工改成 done 或 failed。started 状态的记录不允许任何自动化流程再次执行,这是整套方案里最重要的一条。

为什么监控脚本里不能顺手做写操作

三条理由,都是踩过的:

  • 权限上就做不到才最安全:采集用的 AK/SK 绑定只读策略,写操作走另一个身份。靠规范约束不如靠权限隔离。
  • 监控是无人值守的:它在凌晨两点跑,出 bug 就是一次没人看着的生产变更;写接口的网络重试、超时重发,直接等价于重复下单。
  • 职责混在一起就没法复盘:采集、判定、执行分开后,出问题能一眼看出是数据错、规则错还是动作错。

所以整条链路是:每日只读快照 → 本地算待办 → 推给人 → 人决定 → 独立脚本带开关执行。监控脚本的产出永远是“一份清单和一句话”,不是“一次变更”。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。