feat(go-services): inventory-go item-processor (extract_item_properties) — exact
Ports the core item processing: raw item JSON -> normalized columns for all 7 tables, with the exact per-table sentinel->NULL rules, material/item_set string translation, the Spells/ActiveSpells union (is_active), and compute_base_values (the spell_effects buff-reversal for base_armor_level/base_max_damage/ base_attack_bonus/etc., with the data embedded and the 167772170-vs-167772172 attack-bonus id discrepancy preserved). loadEnums now also loads MaterialType. A loopback POST /debug/process returns the normalized columns for validation. Validated against production's STORED rows (read-only, no writes): 0 mismatches across 200 items for every sampled column of items, item_enhancements (incl. translated material + set), item_combat_stats (incl. base_* values), and item_ratings. This unlocks ingestion (the processor produces the rows) and the remaining search-response enrichment (spells/weapon/mana from the same extractor). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1294ec4418
commit
b90b52c515
2 changed files with 461 additions and 28 deletions
|
|
@ -28,6 +28,7 @@ type Server struct {
|
|||
pool *pgxpool.Pool
|
||||
attributeSets map[string]string // AttributeSetInfo: set-id -> set name
|
||||
objectClasses map[int]string // ObjectClass: id -> name
|
||||
materials map[int]string // MaterialType: id -> name
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
|
|
@ -45,14 +46,15 @@ func main() {
|
|||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
srv := &Server{log: logger, attributeSets: map[string]string{}, objectClasses: map[int]string{}}
|
||||
srv := &Server{log: logger, attributeSets: map[string]string{}, objectClasses: map[int]string{}, materials: map[int]string{}}
|
||||
|
||||
if sets, classes, err := loadEnums(enumPath); err != nil {
|
||||
logger.Warn("could not load enum DB (set/class names will be unknown)", "err", err, "path", enumPath)
|
||||
if e, err := loadEnums(enumPath); err != nil {
|
||||
logger.Warn("could not load enum DB (set/class/material names will be unknown)", "err", err, "path", enumPath)
|
||||
} else {
|
||||
srv.attributeSets = sets
|
||||
srv.objectClasses = classes
|
||||
logger.Info("loaded enum DB", "sets", len(sets), "object_classes", len(classes))
|
||||
srv.attributeSets = e.sets
|
||||
srv.objectClasses = e.objectClasses
|
||||
srv.materials = e.materials
|
||||
logger.Info("loaded enum DB", "sets", len(e.sets), "object_classes", len(e.objectClasses), "materials", len(e.materials))
|
||||
}
|
||||
|
||||
if dsn == "" {
|
||||
|
|
@ -74,6 +76,7 @@ func main() {
|
|||
mux.HandleFunc("GET /sets/list", srv.handleSetsList)
|
||||
mux.HandleFunc("GET /characters/list", srv.handleCharactersList)
|
||||
mux.HandleFunc("GET /search/items", srv.handleSearchItems)
|
||||
mux.HandleFunc("POST /debug/process", srv.handleDebugProcess)
|
||||
|
||||
httpSrv := &http.Server{Addr: addr, Handler: withLogging(mux), ReadHeaderTimeout: 10 * time.Second}
|
||||
go func() {
|
||||
|
|
@ -162,41 +165,50 @@ func (s *Server) dbErr(w http.ResponseWriter, where string, err error) {
|
|||
writeJSON(w, http.StatusInternalServerError, map[string]any{"detail": "Internal server error"})
|
||||
}
|
||||
|
||||
type enumMaps struct {
|
||||
sets map[string]string
|
||||
objectClasses map[int]string
|
||||
materials map[int]string
|
||||
}
|
||||
|
||||
// loadEnums reads the comprehensive enum DB and extracts AttributeSetInfo
|
||||
// (set-id -> name) and ObjectClass (id -> name), mirroring
|
||||
// load_comprehensive_enums (dictionaries first, then enums).
|
||||
func loadEnums(path string) (sets map[string]string, classes map[int]string, err error) {
|
||||
// (set-id -> name), ObjectClass (id -> name), and MaterialType (id -> name),
|
||||
// mirroring load_comprehensive_enums (dictionaries first, then enums).
|
||||
func loadEnums(path string) (enumMaps, error) {
|
||||
var em enumMaps
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return em, err
|
||||
}
|
||||
type valmap struct {
|
||||
Values map[string]string `json:"values"`
|
||||
}
|
||||
var db struct {
|
||||
Dictionaries map[string]struct {
|
||||
Values map[string]string `json:"values"`
|
||||
} `json:"dictionaries"`
|
||||
Enums map[string]struct {
|
||||
Values map[string]string `json:"values"`
|
||||
} `json:"enums"`
|
||||
ObjectClasses struct {
|
||||
Values map[string]string `json:"values"`
|
||||
} `json:"object_classes"`
|
||||
Dictionaries map[string]valmap `json:"dictionaries"`
|
||||
Enums map[string]valmap `json:"enums"`
|
||||
ObjectClasses valmap `json:"object_classes"`
|
||||
}
|
||||
if err := json.Unmarshal(b, &db); err != nil {
|
||||
return nil, nil, err
|
||||
return em, err
|
||||
}
|
||||
sets = map[string]string{}
|
||||
em.sets = map[string]string{}
|
||||
if d, ok := db.Dictionaries["AttributeSetInfo"]; ok && len(d.Values) > 0 {
|
||||
sets = d.Values
|
||||
em.sets = d.Values
|
||||
} else if e, ok := db.Enums["AttributeSetInfo"]; ok {
|
||||
sets = e.Values
|
||||
em.sets = e.Values
|
||||
}
|
||||
classes = map[int]string{}
|
||||
for k, v := range db.ObjectClasses.Values {
|
||||
if n, err := strconv.Atoi(k); err == nil {
|
||||
classes[n] = v
|
||||
intMap := func(v valmap) map[int]string {
|
||||
m := map[int]string{}
|
||||
for k, val := range v.Values {
|
||||
if n, err := strconv.Atoi(k); err == nil {
|
||||
m[n] = val
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
return sets, classes, nil
|
||||
em.objectClasses = intMap(db.ObjectClasses)
|
||||
em.materials = intMap(db.Enums["MaterialType"])
|
||||
return em, nil
|
||||
}
|
||||
|
||||
func envOr(key, def string) string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue