package syncer import ( "fmt" "strings" "time" "xk-hy-transit-go/internal/applog" "xk-hy-transit-go/internal/config" "xk-hy-transit-go/internal/fileauth" "xk-hy-transit-go/internal/hy" "xk-hy-transit-go/internal/xkapi" ) // ConfigSnapshot 当前 Runner 配置快照(明文仅进程内使用,Web 层需脱敏)。 type ConfigSnapshot struct { Cloud xkapi.ConfigResponse CloudOK bool CloudFetchError string RefreshedAt time.Time Local config.Config EffectiveOrgan hy.OrganConfig EffectiveAppKey string EffectiveSecret string EffectiveAesKey string EffectiveFileBucket string UploadToken string UploadTokenSource string CloudUploadToken string FileUploadURL string } func firstNonEmpty(values ...string) string { for _, v := range values { if strings.TrimSpace(v) != "" { return strings.TrimSpace(v) } } return "" } func (r *Runner) applyCloudConfig(conf *xkapi.ConfigResponse) { if conf == nil { return } r.organ = hy.OrganConfig{ UnitID: conf.UnitID, OrganID: conf.OrganID, OrganName: conf.OrganName, } r.cloudUploadToken = conf.UploadToken r.fileUploadURL = resolveFileUploadURL(r.cfg, conf.FileUploadURL) r.hyAppKey = firstNonEmpty(conf.AppKey, r.cfg.HyAppKey) r.hyAppSecret = firstNonEmpty(conf.AppSecret, r.cfg.HyAppSecret) r.hyAesKey = firstNonEmpty(conf.AesKey, r.cfg.HyAesKey) r.fileBucket = firstNonEmpty(conf.FileBucket, r.cfg.HyFileBucket) r.lastCloud = *conf r.lastCloudOK = true r.lastConfigErr = nil r.configRefreshedAt = time.Now() if err := r.refreshUploadTokenLocked(); err != nil { r.uploadToken = "" applog.Appf("warning: generate uploadToken failed: %v", err) } } // refreshUploadToken 按 FileAuth.java upload profile 重新生成 uploadToken(scope=仅 bucket)。 func (r *Runner) refreshUploadToken() error { r.configMu.Lock() defer r.configMu.Unlock() return r.refreshUploadTokenLocked() } func (r *Runner) refreshUploadTokenLocked() error { key := strings.TrimSpace(r.hyAppKey) secret := strings.TrimSpace(r.hyAppSecret) bucket := strings.TrimSpace(r.fileBucket) if key == "" || secret == "" || bucket == "" { return fmt.Errorf("appKey/appSecret/fileBucket 未配置(检查 xk-api HY_APP_KEY、HY_APP_SECRET、HY_FILE_BUCKET)") } tok, err := fileauth.CreateUploadToken(key, secret, bucket, 0) if err != nil { return err } r.uploadToken = tok return nil } // ReloadCloudConfig 从 xk-api GET /supervise/config 刷新机构、凭证与 uploadToken。 func (r *Runner) ReloadCloudConfig() error { r.configMu.Lock() defer r.configMu.Unlock() conf, err := r.xk.Config() if err != nil { r.lastCloudOK = false r.lastConfigErr = err applog.Appf("reload cloud config failed: %v", err) return err } r.applyCloudConfig(conf) if strings.TrimSpace(r.uploadToken) == "" { applog.Appf("warning: uploadToken empty after config reload") } applog.Appf("cloud config reloaded organID=%s appKey=%s…", conf.OrganID, previewKey(r.hyAppKey)) return nil } // ConfigSnapshot 返回当前配置快照(读锁)。 func (r *Runner) ConfigSnapshot() ConfigSnapshot { r.configMu.RLock() defer r.configMu.RUnlock() snap := ConfigSnapshot{ Cloud: r.lastCloud, CloudOK: r.lastCloudOK, RefreshedAt: r.configRefreshedAt, Local: r.cfg, EffectiveOrgan: r.organ, EffectiveAppKey: r.hyAppKey, EffectiveSecret: r.hyAppSecret, EffectiveAesKey: r.hyAesKey, EffectiveFileBucket: r.fileBucket, UploadToken: r.uploadToken, UploadTokenSource: "local-fileauth-java", CloudUploadToken: r.cloudUploadToken, FileUploadURL: r.fileUploadURL, } if r.lastConfigErr != nil { snap.CloudFetchError = r.lastConfigErr.Error() } return snap } func previewKey(s string) string { s = strings.TrimSpace(s) if len(s) <= 12 { if s == "" { return "(empty)" } return s } return s[:8] + "…" }