feat(database): add gearTxt field to parsed results and update related functions
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
2115
internal/optimizer/optimizer.go
Normal file
2115
internal/optimizer/optimizer.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user