feat(server/pay): 优惠套餐(Promo SKU)每账号限购一次——已付过再下单 409 PROMO_ALREADY_USED
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 23s
ci-pangolin / Cleartext Scan — Android 禁明文 (pull_request) Successful in 17s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 18s
ci-pangolin / Lint — shellcheck (pull_request) Successful in 6s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 35s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 32s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 3s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (pull_request) Successful in 4s
ci-pangolin / Go — build + test (pull_request) Failing after 10s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 9s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Failing after 4m40s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (pull_request) Failing after 21s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
wangjia
2026-07-13 11:36:24 +08:00
parent 92943258ab
commit 1c2ec2844d
3 changed files with 106 additions and 0 deletions
+13
View File
@@ -158,6 +158,19 @@ func (s *Store) MarkCanceled(ctx context.Context, userID int64, outTradeNo strin
return nil
}
// HasPaidPurchase 该用户是否已有该 SKU 的 paid 台账——供优惠档(Promo SKU)限购一次校验用
// (CreateOrder 在调上游前查,已购则 409 拒单,不打上游)。
func (s *Store) HasPaidPurchase(ctx context.Context, userID int64, sku string) (bool, error) {
var n int
err := s.db.QueryRowContext(ctx,
`SELECT COUNT(*) FROM pay_purchases WHERE user_id = ? AND sku = ? AND status = 'paid'`,
userID, sku).Scan(&n)
if err != nil {
return false, fmt.Errorf("pay.Store.HasPaidPurchase: %w", err)
}
return n > 0, nil
}
// SubscriptionExpiry 查开通行的到期时间(查单响应回带给客户端)。
func (s *Store) SubscriptionExpiry(ctx context.Context, subID int64) (time.Time, error) {
var exp time.Time