feat: 添加批量生成CDK功能
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

This commit is contained in:
xinian
2026-04-08 14:17:10 +08:00
committed by cnb
parent ca96be3905
commit 9825944efc
3 changed files with 252 additions and 5 deletions

View File

@@ -4,20 +4,104 @@ import (
"blazing/cool"
"blazing/modules/config/model"
"context"
"crypto/rand"
"fmt"
"math/big"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/grand"
"github.com/google/uuid"
)
const charsetWithSymbol = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz"
const (
cdkCodeLength = 16
maxBatchGenerateCount = 5000
)
var charsetWithSymbolSize = big.NewInt(int64(len(charsetWithSymbol)))
func Generate16CharSecure() string {
result := make([]byte, 16)
for i := 0; i < 16; i++ {
result[i] = charsetWithSymbol[grand.N(0, len(charsetWithSymbol)-1)]
code, err := generateSecureCode(cdkCodeLength)
if err == nil {
return code
}
return string(result)
fallback := fmt.Sprintf("%x", uuid.New())
if len(fallback) >= cdkCodeLength {
return fallback[:cdkCodeLength]
}
return fmt.Sprintf("%-16s", fallback)
}
func generateSecureCode(length int) (string, error) {
if length <= 0 {
return "", nil
}
result := make([]byte, length)
for i := 0; i < length; i++ {
index, err := rand.Int(rand.Reader, charsetWithSymbolSize)
if err != nil {
return "", err
}
result[i] = charsetWithSymbol[index.Int64()]
}
return string(result), nil
}
func buildUniqueCodes(count int, generator func() (string, error), exists func([]string) (map[string]struct{}, error)) ([]string, error) {
if count <= 0 {
return nil, nil
}
codes := make([]string, 0, count)
selected := make(map[string]struct{}, count)
for len(codes) < count {
remaining := count - len(codes)
batchSize := remaining * 2
if batchSize < remaining+4 {
batchSize = remaining + 4
}
candidates := make([]string, 0, batchSize)
candidateSet := make(map[string]struct{}, batchSize)
for len(candidates) < batchSize {
code, err := generator()
if err != nil {
return nil, err
}
if _, ok := selected[code]; ok {
continue
}
if _, ok := candidateSet[code]; ok {
continue
}
candidateSet[code] = struct{}{}
candidates = append(candidates, code)
}
existing, err := exists(candidates)
if err != nil {
return nil, err
}
for _, code := range candidates {
if _, ok := existing[code]; ok {
continue
}
selected[code] = struct{}{}
codes = append(codes, code)
if len(codes) == count {
break
}
}
}
return codes, nil
}
type CdkService struct {
@@ -71,3 +155,74 @@ func (s *CdkService) Set(id string) bool {
}
return true
}
func (s *CdkService) BatchGenerate(ctx context.Context, count int) (interface{}, error) {
if count <= 0 {
return nil, gerror.New("生成数量必须大于0")
}
if count > maxBatchGenerateCount {
return nil, gerror.Newf("单次最多生成%d个CDK", maxBatchGenerateCount)
}
requestData := g.RequestFromCtx(ctx).GetMap()
delete(requestData, "count")
delete(requestData, "cdk_code")
delete(requestData, "id")
codes, err := s.generateAvailableCodes(count)
if err != nil {
return nil, err
}
insertData := make(g.List, 0, len(codes))
for _, code := range codes {
row := g.Map{}
for key, value := range requestData {
row[key] = value
}
row["cdk_code"] = code
insertData = append(insertData, row)
}
err = g.DB(s.Model.GroupName()).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
_, err := tx.Model(s.Model).Data(insertData).Insert()
return err
})
if err != nil {
return nil, err
}
g.DB(s.Model.GroupName()).GetCore().ClearCache(context.TODO(), s.Model.TableName())
return g.Map{
"count": count,
"codes": codes,
}, nil
}
func (s *CdkService) generateAvailableCodes(count int) ([]string, error) {
return buildUniqueCodes(count, func() (string, error) {
return generateSecureCode(cdkCodeLength)
}, func(candidates []string) (map[string]struct{}, error) {
return s.findExistingCodeSet(candidates)
})
}
func (s *CdkService) findExistingCodeSet(candidates []string) (map[string]struct{}, error) {
existingSet := make(map[string]struct{})
if len(candidates) == 0 {
return existingSet, nil
}
var records []struct {
CDKCode string `json:"cdk_code"`
}
if err := dbm_nocache_noenable(s.Model).Fields("cdk_code").WhereIn("cdk_code", candidates).Scan(&records); err != nil {
return nil, err
}
for _, record := range records {
existingSet[record.CDKCode] = struct{}{}
}
return existingSet, nil
}