feat(database): add gearTxt field to parsed results and update related functions

This commit is contained in:
kever
2026-05-31 13:52:47 +08:00
parent 886c5fda08
commit bcf5d3d657
11 changed files with 3635 additions and 285 deletions

View File

@@ -87,9 +87,17 @@ func (d *Database) initTables() error {
updated_at INTEGER NOT NULL
);`
optimizerPreferencesTable := `
CREATE TABLE IF NOT EXISTS optimizer_preferences (
hero_id TEXT PRIMARY KEY,
options_json TEXT NOT NULL,
updated_at INTEGER NOT NULL
);`
tables := []string{
parsedDataTable,
settingsTable,
optimizerPreferencesTable,
}
for _, table := range tables {
@@ -221,6 +229,9 @@ func (d *Database) GetSetting(key string) (string, error) {
var value string
err := d.db.QueryRow(stmt, key).Scan(&value)
if err != nil {
if err == sql.ErrNoRows {
return "", nil
}
return "", err
}
return value, nil
@@ -246,3 +257,26 @@ func (d *Database) GetAllSettings() (map[string]string, error) {
return settings, nil
}
// SaveOptimizerPreference saves the latest optimizer options for a hero.
func (d *Database) SaveOptimizerPreference(heroID string, optionsJSON string) error {
stmt := `
INSERT OR REPLACE INTO optimizer_preferences (hero_id, options_json, updated_at)
VALUES (?, ?, ?)`
_, err := d.db.Exec(stmt, heroID, optionsJSON, time.Now().Unix())
return err
}
// GetOptimizerPreference returns saved optimizer options for a hero.
func (d *Database) GetOptimizerPreference(heroID string) (string, error) {
stmt := "SELECT options_json FROM optimizer_preferences WHERE hero_id = ?"
var optionsJSON string
err := d.db.QueryRow(stmt, heroID).Scan(&optionsJSON)
if err != nil {
if err == sql.ErrNoRows {
return "", nil
}
return "", err
}
return optionsJSON, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -116,3 +116,22 @@ func (s *DatabaseService) GetAllAppSettings() (map[string]string, error) {
return settings, nil
}
// SaveOptimizerPreference saves the latest optimizer options for a hero.
func (s *DatabaseService) SaveOptimizerPreference(heroID string, optionsJSON string) error {
if err := s.db.SaveOptimizerPreference(heroID, optionsJSON); err != nil {
s.logger.Error("保存配装偏好失败", "error", err, "hero_id", heroID)
return fmt.Errorf("保存配装偏好失败: %w", err)
}
return nil
}
// GetOptimizerPreference gets saved optimizer options for a hero.
func (s *DatabaseService) GetOptimizerPreference(heroID string) (string, error) {
optionsJSON, err := s.db.GetOptimizerPreference(heroID)
if err != nil {
s.logger.Error("获取配装偏好失败", "error", err, "hero_id", heroID)
return "", fmt.Errorf("获取配装偏好失败: %w", err)
}
return optionsJSON, nil
}

View File

@@ -3,8 +3,10 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"sync"
"time"
"equipment-analyzer/internal/capture"
@@ -23,6 +25,8 @@ type App struct {
parserService *ParserService
database *model.Database
databaseService *DatabaseService
optimizeMu sync.Mutex
optimizeCancel context.CancelFunc
}
func NewApp(cfg *config.Config, logger *utils.Logger) *App {
@@ -84,16 +88,97 @@ func (a *App) Shutdown(ctx context.Context) {
}
}
// OptimizeBuilds runs the optimizer on latest parsed data.
// OptimizeBuilds runs the optimizer on latest parsed data and returns when done.
func (a *App) OptimizeBuilds(req model.OptimizeRequest) (*model.OptimizeResponse, error) {
log.Printf("[service] OptimizeBuilds entry heroId=%s", req.HeroID)
log.Printf("[service] OptimizeBuilds sync entry heroId=%s", req.HeroID)
optCtx, cancel := context.WithCancel(context.Background())
a.optimizeMu.Lock()
if a.optimizeCancel != nil {
a.optimizeMu.Unlock()
cancel()
return nil, fmt.Errorf("已有配装计算正在进行")
}
a.optimizeCancel = cancel
a.optimizeMu.Unlock()
defer func() {
cancel()
a.optimizeMu.Lock()
a.optimizeCancel = nil
a.optimizeMu.Unlock()
}()
resp, err := a.runOptimizeBuilds(optCtx, req, a.emitOptimizeProgress)
if err != nil {
if errors.Is(err, context.Canceled) {
log.Printf("[service] OptimizeBuilds canceled")
return nil, fmt.Errorf("配装计算已中断")
}
log.Printf("[service] OptimizeBuilds optimize failed: %v", err)
return nil, err
}
log.Printf("[service] OptimizeBuilds done results=%d totalCombos=%d", len(resp.Results), resp.TotalCombos)
return resp, nil
}
// StartOptimizeBuilds starts the optimizer in the background and reports progress through Wails events.
func (a *App) StartOptimizeBuilds(req model.OptimizeRequest) error {
log.Printf("[service] StartOptimizeBuilds entry heroId=%s", req.HeroID)
optCtx, cancel := context.WithCancel(context.Background())
a.optimizeMu.Lock()
if a.optimizeCancel != nil {
a.optimizeMu.Unlock()
cancel()
return fmt.Errorf("已有配装计算正在进行")
}
a.optimizeCancel = cancel
a.optimizeMu.Unlock()
go func() {
defer func() {
cancel()
a.optimizeMu.Lock()
a.optimizeCancel = nil
a.optimizeMu.Unlock()
}()
resp, err := a.runOptimizeBuilds(optCtx, req, a.emitOptimizeProgress)
if err != nil {
if errors.Is(err, context.Canceled) {
log.Printf("[service] StartOptimizeBuilds canceled")
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "optimize:canceled", "配装计算已中断")
}
return
}
log.Printf("[service] StartOptimizeBuilds optimize failed: %v", err)
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "optimize:error", err.Error())
}
return
}
log.Printf("[service] StartOptimizeBuilds done results=%d totalCombos=%d", len(resp.Results), resp.TotalCombos)
if a.ctx != nil {
runtime.EventsEmit(a.ctx, "optimize:done", resp)
}
}()
return nil
}
func (a *App) runOptimizeBuilds(ctx context.Context, req model.OptimizeRequest, progress optimizer.ProgressCallback) (*model.OptimizeResponse, error) {
parsedResult, err := a.GetLatestParsedDataFromDatabase()
if err != nil {
log.Printf("[service] OptimizeBuilds get data failed: %v", err)
return nil, err
}
if parsedResult == nil || (len(parsedResult.Items) == 0 && len(parsedResult.Heroes) == 0) {
log.Printf("[service] OptimizeBuilds no data items=%d heroes=%d", len(parsedResult.Items), len(parsedResult.Heroes))
itemCount := 0
heroCount := 0
if parsedResult != nil {
itemCount = len(parsedResult.Items)
heroCount = len(parsedResult.Heroes)
}
if parsedResult == nil || (itemCount == 0 && heroCount == 0) {
log.Printf("[service] OptimizeBuilds no data items=%d heroes=%d", itemCount, heroCount)
return &model.OptimizeResponse{TotalCombos: 0, Results: []model.OptimizeResult{}}, nil
}
@@ -140,13 +225,37 @@ func (a *App) OptimizeBuilds(req model.OptimizeRequest) (*model.OptimizeResponse
heroes = templateHeroes
log.Printf("[service] OptimizeBuilds parsed items=%d heroes=%d", len(items), len(heroes))
resp, err := optimizer.Optimize(items, heroes, req)
if err != nil {
log.Printf("[service] OptimizeBuilds optimize failed: %v", err)
return nil, err
return optimizer.OptimizeWithContext(ctx, items, heroes, req, progress)
}
func (a *App) emitOptimizeProgress(checked int, total int, matched int) {
if a.ctx == nil {
return
}
log.Printf("[service] OptimizeBuilds done results=%d totalCombos=%d", len(resp.Results), resp.TotalCombos)
return resp, nil
percent := 0.0
if total > 0 {
percent = float64(checked) * 100 / float64(total)
if percent > 100 {
percent = 100
}
}
runtime.EventsEmit(a.ctx, "optimize:progress", map[string]interface{}{
"checked": checked,
"matched": matched,
"total": total,
"percent": percent,
})
}
// CancelOptimizeBuilds stops the currently running optimizer, if any.
func (a *App) CancelOptimizeBuilds() error {
a.optimizeMu.Lock()
cancel := a.optimizeCancel
a.optimizeMu.Unlock()
if cancel != nil {
cancel()
}
return nil
}
// GetNetworkInterfaces returns available network interfaces.
@@ -529,6 +638,28 @@ func (a *App) GetAllAppSettings() (map[string]string, error) {
return a.databaseService.GetAllAppSettings()
}
// SaveOptimizerPreference saves optimizer options for a hero.
func (a *App) SaveOptimizerPreference(heroID string, optionsJSON string) error {
if a.databaseService == nil {
return fmt.Errorf("database service not initialized")
}
if heroID == "" {
return fmt.Errorf("hero id is required")
}
return a.databaseService.SaveOptimizerPreference(heroID, optionsJSON)
}
// GetOptimizerPreference gets optimizer options for a hero.
func (a *App) GetOptimizerPreference(heroID string) (string, error) {
if a.databaseService == nil {
return "", fmt.Errorf("database service not initialized")
}
if heroID == "" {
return "", nil
}
return a.databaseService.GetOptimizerPreference(heroID)
}
func logMissingSetStats(items []interface{}) {
if len(items) == 0 {
return