diff --git a/backend/services/license.go b/backend/services/license.go index 6df0f98..2fbf9bc 100644 --- a/backend/services/license.go +++ b/backend/services/license.go @@ -139,7 +139,7 @@ func ActivateLicense(key string) (*models.License, error) { lic := &models.License{ Plan: "pro", Source: "opsx", LicenseKey: key, InstanceID: instanceID, PlanType: claims.PlanType, MaxDevices: claims.MaxDevices, Instances: claims.MaxDevices, - Edition: editionFromPlanType(claims.PlanType, claims.MaxDevices), + Edition: editionFromPlanType(claims.PlanType, claims.MaxDevices), ExpiresAt: claims.Exp, IssuedAt: time.Unix(claims.Iat, 0), ActivatedAt: time.Now(), LastVerifiedAt: time.Now().Unix(), Customer: claims.Email, InvalidReason: "", } @@ -233,7 +233,13 @@ func DeactivateLicense() (*DeactivateResult, error) { if key != "" && isOpsxLicenseKey(key) { if err := remoteDeactivateLicence(key, instanceID, token); err != nil { - return nil, fmt.Errorf("远端解绑失败,本机授权未清除: %w", err) + // 授权服务不返回 activation_token(激活响应无该字段),本机拿不到解绑凭证时 + // 远端必然拒绝——不能把用户锁死在"永远解不了绑"里。降级为仅清本机: + // 本机回免费版(用户诉求达成),远端座位由服务端到期/管理员释放。 + clearStoredLicense() + result.OfflineLocalOnly = true + result.Message = "已退出授权,本机已回到免费版。远端席位未能自动释放(服务未下发解绑凭证),如需释放请到授权平台管理或联系运营。" + return result, nil } clearStoredLicense() result.Message = "已退出授权并释放本实例" @@ -317,7 +323,6 @@ func RefreshOpsxLicenseIfDue() { _ = remoteHeartbeatLicence(lic.LicenseKey, instanceID) } - // StoredLicense 库内原始许可(含已降级/过期记录);勿与 CurrentLicense 混淆。 func StoredLicense() *models.License { db := repository.GetDB()