fix(db): 缩短 license_key 列至 VARCHAR(768) 避免 InnoDB 索引超限 [tsk_Qmk4wT_dkqRR]
原 size:2048 在 utf8mb4 下产生 8192 字节索引,超过 InnoDB 3072 字节上限, 全新环境 AutoMigrate 必失败。改为 VARCHAR(768)(768×4=3072 字节,恰好 在上限内),schema.sql 同步更新列类型并将 uk_license_key 改为全列索引(无 需前缀)。新增测试 TestLicenseTokenFitsColumnLimit 验证最坏情况 token 长度 确实在 768 字符以内。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import "time"
|
||||
type License struct {
|
||||
Base
|
||||
ShopID uint64 `gorm:"not null;index" json:"shop_id"`
|
||||
LicenseKey string `gorm:"size:2048;uniqueIndex" json:"license_key"`
|
||||
LicenseKey string `gorm:"size:768;uniqueIndex" json:"license_key"`
|
||||
Type string `gorm:"type:enum('trial','monthly','annual','lifetime');default:'trial'" json:"type"`
|
||||
ExpiresAt *time.Time `json:"expires_at"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
|
||||
@@ -73,3 +73,38 @@ func TestVerifyLicenseToken_InvalidFormat(t *testing.T) {
|
||||
_, err = VerifyLicenseToken("not-a-valid-token", pub)
|
||||
assert.ErrorIs(t, err, ErrInvalidLicenseToken)
|
||||
}
|
||||
|
||||
// TestLicenseTokenFitsColumnLimit verifies that a realistic (even worst-case)
|
||||
// license token fits within the VARCHAR(768) column limit imposed by the
|
||||
// InnoDB index constraint (768 chars × 4 bytes/char = 3072 bytes max).
|
||||
func TestLicenseTokenFitsColumnLimit(t *testing.T) {
|
||||
const columnLimit = 768
|
||||
|
||||
priv, _, err := GenerateEd25519KeyPair()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Use a large features map to simulate a worst-case payload.
|
||||
exp := time.Now().Add(365 * 24 * time.Hour).Unix()
|
||||
payload := LicensePayload{
|
||||
ShopID: 999999999,
|
||||
LicenseID: 999999999,
|
||||
Type: "lifetime",
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: &exp,
|
||||
MaxDevices: 99,
|
||||
Features: map[string]any{
|
||||
"finance": true,
|
||||
"inventory": true,
|
||||
"reports": true,
|
||||
"export": true,
|
||||
"multi_shop": true,
|
||||
"api_access": true,
|
||||
},
|
||||
}
|
||||
|
||||
token, err := IssueLicenseToken(payload, priv)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.LessOrEqual(t, len(token), columnLimit,
|
||||
"license token length %d exceeds VARCHAR(%d) column limit", len(token), columnLimit)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS `users` (
|
||||
CREATE TABLE IF NOT EXISTS `licenses` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`shop_id` BIGINT UNSIGNED NOT NULL,
|
||||
`license_key` VARCHAR(2048) NOT NULL COMMENT 'Ed25519 signed token',
|
||||
`license_key` VARCHAR(768) NOT NULL COMMENT 'Ed25519 signed token',
|
||||
`device_id` VARCHAR(255) DEFAULT NULL COMMENT 'deprecated: use license_devices',
|
||||
`type` ENUM('trial','monthly','annual','lifetime') NOT NULL DEFAULT 'trial',
|
||||
`expires_at` DATETIME DEFAULT NULL COMMENT 'NULL=永久',
|
||||
@@ -69,7 +69,7 @@ CREATE TABLE IF NOT EXISTS `licenses` (
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_license_key` (`license_key`(255)),
|
||||
UNIQUE KEY `uk_license_key` (`license_key`),
|
||||
KEY `idx_shop_id` (`shop_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许可证';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user