feat: 对齐 Waffo 包年/永久结账与客户端席位 API
同步 OpsXShell 档位定价与 Waffo 商品映射,补齐 checkout/webhook/设备列表契约,便于桌面端购买与满席释放。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
4ae318335c
commit
95405de119
14 files changed
+958
-155
No files matched your search
@@ -8,6 +8,31 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func inferPlanType(lic model.License) string {
|
||||
if lic.IsTrial {
|
||||
return "trial"
|
||||
}
|
||||
if lic.Duration <= 0 || lic.EndDate.Equal(model.PermanentEndDate) || lic.EndDate.Year() >= 2099 {
|
||||
return "perpetual"
|
||||
}
|
||||
if lic.Duration <= 180 {
|
||||
return "trial"
|
||||
}
|
||||
if lic.Duration <= 31 {
|
||||
return "monthly"
|
||||
}
|
||||
return "yearly"
|
||||
}
|
||||
|
||||
func licenseClientPayload(lic model.License) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"expires_at": lic.EndDate.Format("2006-01-02 15:04:05"),
|
||||
"days_left": utils.CalculateDaysLeft(lic.EndDate),
|
||||
"max_devices": lic.MaxActivations,
|
||||
"plan_type": inferPlanType(lic),
|
||||
}
|
||||
}
|
||||
|
||||
// Verify 客户端 License 验证 // Logan.Li
|
||||
func (s *LicenseService) Verify(req dto.LicenseVerifyRequest) (*dto.LicenseVerifyResponse, error) {
|
||||
var lic model.License
|
||||
@@ -15,12 +40,10 @@ func (s *LicenseService) Verify(req dto.LicenseVerifyRequest) (*dto.LicenseVerif
|
||||
return &dto.LicenseVerifyResponse{Valid: false, Message: "无效的 License"}, nil
|
||||
}
|
||||
|
||||
// 校验产品归属 // Logan.Li
|
||||
if req.ProductID > 0 && lic.ProductID != req.ProductID {
|
||||
return &dto.LicenseVerifyResponse{Valid: false, Message: "License 与产品不匹配"}, nil
|
||||
}
|
||||
|
||||
// 状态检查 // Logan.Li
|
||||
switch lic.Status {
|
||||
case model.LicenseStatusRevoked:
|
||||
return &dto.LicenseVerifyResponse{Valid: false, Message: "License 已被吊销"}, nil
|
||||
@@ -30,7 +53,6 @@ func (s *LicenseService) Verify(req dto.LicenseVerifyRequest) (*dto.LicenseVerif
|
||||
return &dto.LicenseVerifyResponse{Valid: false, Message: "License 已过期"}, nil
|
||||
}
|
||||
|
||||
// 过期检查(惰性更新状态) // Logan.Li
|
||||
if utils.IsLicenseExpired(lic.EndDate) {
|
||||
lic.Status = model.LicenseStatusExpired
|
||||
s.db.Save(&lic)
|
||||
@@ -48,6 +70,7 @@ func (s *LicenseService) Verify(req dto.LicenseVerifyRequest) (*dto.LicenseVerif
|
||||
ExpiresAt: lic.EndDate.Format("2006-01-02 15:04:05"),
|
||||
DaysLeft: utils.CalculateDaysLeft(lic.EndDate),
|
||||
MaxDevices: lic.MaxActivations,
|
||||
PlanType: inferPlanType(lic),
|
||||
Message: "验证成功",
|
||||
}, nil
|
||||
}
|
||||
@@ -66,7 +89,6 @@ func (s *LicenseService) Activate(req dto.LicenseActivateRequest, clientIP strin
|
||||
return nil, errors.New("License 已过期")
|
||||
}
|
||||
|
||||
// 同设备重复激活视为重新激活,不消耗额度 // Logan.Li
|
||||
var existing model.Activation
|
||||
if err := s.db.Where("license_id = ? AND device_id = ?", lic.ID, req.DeviceID).First(&existing).Error; err == nil {
|
||||
existing.LastSeenAt = time.Now()
|
||||
@@ -75,18 +97,13 @@ func (s *LicenseService) Activate(req dto.LicenseActivateRequest, clientIP strin
|
||||
existing.DeviceInfo = req.DeviceInfo
|
||||
existing.IP = clientIP
|
||||
s.db.Save(&existing)
|
||||
return map[string]interface{}{
|
||||
"expires_at": lic.EndDate.Format("2006-01-02 15:04:05"),
|
||||
"days_left": utils.CalculateDaysLeft(lic.EndDate),
|
||||
}, nil
|
||||
return licenseClientPayload(lic), nil
|
||||
}
|
||||
|
||||
// 检查激活额度 // Logan.Li
|
||||
if lic.CurrentActivations >= lic.MaxActivations {
|
||||
return nil, errors.New("已达到最大激活数量限制")
|
||||
}
|
||||
|
||||
// 创建激活记录 // Logan.Li
|
||||
activation := model.Activation{
|
||||
LicenseID: lic.ID,
|
||||
DeviceID: req.DeviceID,
|
||||
@@ -108,10 +125,7 @@ func (s *LicenseService) Activate(req dto.LicenseActivateRequest, clientIP strin
|
||||
}
|
||||
s.db.Save(&lic)
|
||||
|
||||
return map[string]interface{}{
|
||||
"expires_at": lic.EndDate.Format("2006-01-02 15:04:05"),
|
||||
"days_left": utils.CalculateDaysLeft(lic.EndDate),
|
||||
}, nil
|
||||
return licenseClientPayload(lic), nil
|
||||
}
|
||||
|
||||
// Deactivate 客户端停用 // Logan.Li
|
||||
@@ -146,13 +160,27 @@ func (s *LicenseService) Heartbeat(req dto.LicenseHeartbeatRequest) (map[string]
|
||||
return nil, errors.New("License 已过期")
|
||||
}
|
||||
|
||||
// 更新心跳时间与在线状态 // Logan.Li
|
||||
s.db.Model(&model.Activation{}).
|
||||
Where("license_id = ? AND device_id = ?", lic.ID, req.DeviceID).
|
||||
Updates(map[string]interface{}{"last_seen_at": time.Now(), "is_online": true})
|
||||
|
||||
return map[string]interface{}{
|
||||
"expires_at": lic.EndDate.Format("2006-01-02 15:04:05"),
|
||||
"days_left": utils.CalculateDaysLeft(lic.EndDate),
|
||||
}, nil
|
||||
return licenseClientPayload(lic), nil
|
||||
}
|
||||
|
||||
// ListDevicesByKey returns activations for a license key (client self-service). // Logan.Li
|
||||
func (s *LicenseService) ListDevicesByKey(licenseKey string) ([]dto.ActivationResponse, int, error) {
|
||||
var lic model.License
|
||||
if err := s.db.Where("license_key = ?", licenseKey).First(&lic).Error; err != nil {
|
||||
return nil, 0, errors.New("无效的 License")
|
||||
}
|
||||
var activations []model.Activation
|
||||
if err := s.db.Where("license_id = ? AND status = ?", lic.ID, "active").Order("activated_at DESC").Find(&activations).Error; err != nil {
|
||||
return nil, 0, errors.New("获取设备列表失败")
|
||||
}
|
||||
results := make([]dto.ActivationResponse, 0, len(activations))
|
||||
for _, a := range activations {
|
||||
results = append(results, toActivationResponse(a))
|
||||
}
|
||||
return results, lic.MaxActivations, nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user