@@ -330,16 +330,68 @@ func DeleteBotConfigSnapshot(ctx context.Context, st Storage, botID string) erro
330330 return err
331331}
332332
333- // SaveAppConfigSnapshot 將完整主配置 JSON 寫入 app_config 並追加 app_config_history(與 MigrateYAMLToAppConfigDB 入庫一致)。
333+ // SyncBotConfigSnapshotsFromMainConfig 將主配置中的 cfg.Bots 逐條寫入 bot_configs(與 app_config 快照對齊)。
334+ // 用於 SaveAppConfigSnapshot、main 引導等僅寫入主快照的路徑,避免與 bot_configs 文檔表不一致。
335+ func SyncBotConfigSnapshotsFromMainConfig (ctx context.Context , st Storage , cfg * config.Config , operator , source string ) error {
336+ if cfg == nil || len (cfg .Bots ) == 0 {
337+ return nil
338+ }
339+ ss , ok := st .(* SQLStorage )
340+ if ! ok || ss == nil {
341+ return nil
342+ }
343+ if err := ss .EnsureAppConfigDocumentTables (); err != nil {
344+ return err
345+ }
346+ for i := range cfg .Bots {
347+ bc := & cfg .Bots [i ]
348+ id := strings .TrimSpace (bc .ID )
349+ if id == "" {
350+ id = config .GenerateBotID (bc .Exchange , bc .Symbol , bc .GetMarketType ())
351+ }
352+ bf := config .ConvertFromBotConfig (* bc )
353+ bf .UpdatedAt = time .Now ().Format (time .RFC3339 )
354+ if doc , err := ss .GetBotConfigDocument (ctx , id ); err == nil && doc != nil && strings .TrimSpace (doc .Content ) != "" {
355+ var prev config.BotConfigFile
356+ if json .Unmarshal ([]byte (doc .Content ), & prev ) == nil && prev .CreatedAt != "" {
357+ bf .CreatedAt = prev .CreatedAt
358+ }
359+ } else if bc .CreatedAt != "" {
360+ bf .CreatedAt = bc .CreatedAt
361+ }
362+ if _ , err := SaveBotConfigSnapshot (ctx , st , bf , operator , source ); err != nil {
363+ return fmt .Errorf ("sync bot_configs %s: %w" , id , err )
364+ }
365+ }
366+ return nil
367+ }
368+
369+ // SaveAppConfigSnapshot 將完整主配置 JSON 寫入 app_config 並追加 app_config_history,並同步 cfg.Bots 至 bot_configs。
334370func SaveAppConfigSnapshot (ctx context.Context , st Storage , cfg * config.Config , operator , source string ) (revision int , err error ) {
371+ return SaveAppConfigSnapshotWithBotSource (ctx , st , cfg , operator , source , "" )
372+ }
373+
374+ // SaveAppConfigSnapshotWithBotSource 同上;若 botHistorySource 為空,bot_config_history.source 使用 appSource,否則使用 botHistorySource。
375+ func SaveAppConfigSnapshotWithBotSource (ctx context.Context , st Storage , cfg * config.Config , operator , appSource , botHistorySource string ) (revision int , err error ) {
335376 if st == nil || cfg == nil {
336377 return 0 , fmt .Errorf ("SaveAppConfigSnapshot: storage 或配置為空" )
337378 }
338379 jsonBytes , err := json .Marshal (cfg )
339380 if err != nil {
340381 return 0 , fmt .Errorf ("序列化配置為 JSON: %w" , err )
341382 }
342- return SaveAppConfigSnapshotFromJSON (ctx , st , jsonBytes , operator , source )
383+ rev , err := SaveAppConfigSnapshotFromJSON (ctx , st , jsonBytes , operator , appSource )
384+ if err != nil {
385+ return 0 , err
386+ }
387+ botSrc := botHistorySource
388+ if strings .TrimSpace (botSrc ) == "" {
389+ botSrc = appSource
390+ }
391+ if err := SyncBotConfigSnapshotsFromMainConfig (ctx , st , cfg , operator , botSrc ); err != nil {
392+ return rev , err
393+ }
394+ return rev , nil
343395}
344396
345397// SaveAppConfigSnapshotFromJSON 將主配置 JSON 寫入 app_config(可含 config.Config 結構體未涵蓋的鍵,例如 security)。
0 commit comments