feat: 对齐 Waffo 包年/永久结账与客户端席位 API

同步 OpsXShell 档位定价与 Waffo 商品映射,补齐 checkout/webhook/设备列表契约,便于桌面端购买与满席释放。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 11:42:56 +08:00
1 parent 4ae318335c
commit 95405de119
14 files changed
+958 -155

No files matched your search

@@ -1,6 +1,7 @@
package license
import (
"backend/internal/dto"
"net/http"
"github.com/gin-gonic/gin"
@@ -8,13 +9,7 @@ import (
// Verify 客户端 License 验证 // Logan.Li
func (ctrl *LicenseController) Verify(c *gin.Context) {
var req struct {
LicenseKey string `json:"license_key" binding:"required"`
DeviceID string `json:"device_id"`
DeviceName string `json:"device_name"`
DeviceInfo string `json:"device_info"`
ProductID uint `json:"product_id"`
}
var req dto.LicenseVerifyRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "参数错误: " + err.Error()})
return
@@ -25,12 +20,7 @@ func (ctrl *LicenseController) Verify(c *gin.Context) {
// ActivateDevice 客户端激活(机器绑定) // Logan.Li
func (ctrl *LicenseController) ActivateDevice(c *gin.Context) {
var req struct {
LicenseKey string `json:"license_key" binding:"required"`
DeviceID string `json:"device_id" binding:"required"`
DeviceName string `json:"device_name"`
DeviceInfo string `json:"device_info"`
}
var req dto.LicenseActivateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "参数错误: " + err.Error()})
return
@@ -45,10 +35,7 @@ func (ctrl *LicenseController) ActivateDevice(c *gin.Context) {
// Deactivate 客户端停用 // Logan.Li
func (ctrl *LicenseController) Deactivate(c *gin.Context) {
var req struct {
LicenseKey string `json:"license_key" binding:"required"`
DeviceID string `json:"device_id" binding:"required"`
}
var req dto.LicenseDeactivateRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "参数错误"})
return
@@ -62,10 +49,7 @@ func (ctrl *LicenseController) Deactivate(c *gin.Context) {
// Heartbeat 客户端心跳 // Logan.Li
func (ctrl *LicenseController) Heartbeat(c *gin.Context) {
var req struct {
LicenseKey string `json:"license_key" binding:"required"`
DeviceID string `json:"device_id" binding:"required"`
}
var req dto.LicenseHeartbeatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "参数错误"})
return
@@ -77,3 +61,25 @@ func (ctrl *LicenseController) Heartbeat(c *gin.Context) {
}
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "心跳成功", "data": data})
}
// ListDevices 客户端查询已绑定设备(用于席位满时释放) // Logan.Li
func (ctrl *LicenseController) ListDevices(c *gin.Context) {
var req struct {
LicenseKey string `json:"license_key" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "参数错误"})
return
}
devices, maxDevices, err := ctrl.svc.ListDevicesByKey(req.LicenseKey)
if err != nil {
c.JSON(http.StatusOK, gin.H{"code": 1, "message": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{
"devices": devices,
"max_devices": maxDevices,
"used": len(devices),
}})
}