package payment import ( "os" "path/filepath" "strings" beego "github.com/beego/beego/v2/server/web" ) // ============================================================= // 渠道证书落盘路径解析 // // 历史实现把证书存成相对路径(certs/payment/<渠道>/xxx.pem), // 该相对路径在「上传时」与「读取时」分别相对各自的进程工作目录解析。 // 一旦服务的工作目录发生变化(例如从 /www/wwwroot/api.yunzer.cn 换到 /app), // 库里记的路径就指向不存在的文件,报 “The system cannot find the file specified”。 // // 因此读取时统一做一次解析: // - 落盘:写入 payment_cert_dir(未配置时退回工作目录下的 certs/payment),入库仍存相对路径 // (本地开发与线上可能共用同一个库,存绝对路径会互相覆盖对方环境); // - 读取:先按当前工作目录解析,再按配置的 payment_cert_dir 根目录兜底解析。 // ============================================================= // legacyCertPrefix 历史相对路径前缀,用于把 certs/payment/<渠道>/xxx.pem 剥成 <渠道>/xxx.pem const legacyCertPrefix = "certs/payment/" // CertBaseDir 渠道证书落盘根目录(各渠道目录的父目录) func CertBaseDir() string { dir, _ := beego.AppConfig.String("payment_cert_dir") dir = strings.TrimSpace(dir) if dir == "" { dir = filepath.Join("certs", "payment") } return dir } // CertDir 某渠道的证书目录 func CertDir(channel string) string { return filepath.Join(CertBaseDir(), filepath.Base(channel)) } // ResolveCertPath 把库中可能存的相对路径解析为当前可读的路径。 // 依次尝试:绝对路径 -> 相对当前工作目录 -> 相对 payment_cert_dir 根目录(兼容历史相对路径)。 // 都读不到时原样返回,让上层的 os.ReadFile 报出原始路径便于排查。 func ResolveCertPath(p string) string { p = strings.TrimSpace(p) if p == "" { return "" } if filepath.IsAbs(p) { return p } // 相对当前工作目录(历史行为,能读到就不动) if certPathExists(p) { return p } // 兼容历史相对路径:剥掉 certs/payment 前缀后挂到配置的根目录下 if cand := filepath.Join(CertBaseDir(), certRelative(p)); certPathExists(cand) { return cand } // 兜底:按文件名在根目录下找 if cand := filepath.Join(CertBaseDir(), filepath.Base(p)); certPathExists(cand) { return cand } return p } // certRelative 从历史相对路径中剥掉 certs/payment 前缀 func certRelative(p string) string { norm := filepath.ToSlash(filepath.Clean(p)) if i := strings.Index(norm, legacyCertPrefix); i >= 0 { norm = norm[i+len(legacyCertPrefix):] } return filepath.FromSlash(norm) } func certPathExists(path string) bool { info, err := os.Stat(path) return err == nil && !info.IsDir() }