23dff69c62
- 新增 license_codes 码池表 + model.LicenseCode;licenses 加 tier 档位列 - LicenseService.Redeem:单事务 FOR UPDATE 校验码未用 → 时长叠加(可叠加,0=永久) → 写 type/tier/max_devices → 绑设备(超限整笔回滚) → 标记已用 → 即时失效 phase 缓存 路由仍 POST /license/activate,客户端零破坏 - util.GenerateRedeemCode/NormalizeCode:JIUKU-XXXX-XXXX 短码(crypto/rand) - cmd/gencode:平台批量生成兑换码并落库;删除 cmd/issue、cmd/genkey - 退役 ed25519 + HMAC:删 util/license_key、GenerateKey、License 全部 config 字段 及生产启动私钥校验;trial 改直接建行(无需私钥、去 Fatal) - tier 档位钩子默认 standard,分档消费模式后续设计 - 测试:Redeem 全场景(叠加/过期重置/永久/一码一次/无效/设备上限回滚) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
28 lines
1.5 KiB
Go
28 lines
1.5 KiB
Go
package model
|
||
|
||
import "time"
|
||
|
||
// LicenseCode 兑换券(激活码)。由平台方批量生成,用户购买/活动获得后在自己门店兑换。
|
||
// 每张码代表一段时长(duration_days),兑换时叠加到门店当前到期时间上;一码一次(status 控制)。
|
||
type LicenseCode struct {
|
||
ID uint64 `gorm:"primaryKey;autoIncrement" json:"id"`
|
||
Code string `gorm:"size:32;uniqueIndex;not null" json:"code"` // 归一化大写无连字符存储
|
||
|
||
Type string `gorm:"type:enum('trial','monthly','annual','lifetime');default:'annual'" json:"type"` // 计费/时长标签
|
||
// Tier 档位钩子(类似 pro/max)。当前只用 'standard' 一种,分档能力/消费模式后续设计。
|
||
Tier string `gorm:"size:32;not null;default:'standard'" json:"tier"`
|
||
DurationDays int `gorm:"not null;default:0" json:"duration_days"` // 授予时长;0 = 永久(lifetime)
|
||
MaxDevices int `gorm:"not null;default:0" json:"max_devices"` // 授予设备上限;0 = 不改变现值
|
||
|
||
Status string `gorm:"type:enum('unused','redeemed','void');default:'unused';index" json:"status"`
|
||
RedeemedShopID *uint64 `gorm:"index" json:"redeemed_shop_id,omitempty"`
|
||
RedeemedAt *time.Time `json:"redeemed_at,omitempty"`
|
||
RedeemedDeviceID string `gorm:"size:255" json:"redeemed_device_id,omitempty"`
|
||
|
||
Batch string `gorm:"size:64" json:"batch,omitempty"` // 发放批次/活动名
|
||
Note string `gorm:"size:255" json:"note,omitempty"`
|
||
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|