修复支付不跳转
This commit is contained in:
@@ -301,8 +301,14 @@ const checkOrder = async (orderNo: string, silent = false) => {
|
||||
ElMessage.success("购买成功,套餐已开通");
|
||||
payVisible.value = false;
|
||||
loadMyOrders();
|
||||
// 重建动态路由,把新开通模块的「功能未开通」菜单占位页切换为真实页面
|
||||
reloadMenusAndDynamicRoutes().catch(() => {});
|
||||
// 重建动态路由,把新开通模块的「功能未开通」菜单占位页切换为真实页面。
|
||||
// 必须等重建完成后再跳首页,否则会按旧的菜单路由跳转、落到占位页
|
||||
try {
|
||||
await reloadMenusAndDynamicRoutes();
|
||||
} catch {
|
||||
/* 忽略:进入首页时路由守卫会再拉一次菜单 */
|
||||
}
|
||||
router.replace("/home");
|
||||
return;
|
||||
}
|
||||
if (status === "paid") {
|
||||
|
||||
@@ -312,8 +312,14 @@ const checkOrder = async (silent = false) => {
|
||||
ElMessage.success("购买成功,功能已开通");
|
||||
payVisible.value = false;
|
||||
loadMyOrders();
|
||||
// 重建动态路由,把刚开通模块的「功能未开通」菜单占位页切换为真实页面
|
||||
reloadMenusAndDynamicRoutes().catch(() => {});
|
||||
// 重建动态路由,把刚开通模块的「功能未开通」菜单占位页切换为真实页面。
|
||||
// 必须等重建完成后再跳首页,否则会按旧的菜单路由跳转、落到占位页
|
||||
try {
|
||||
await reloadMenusAndDynamicRoutes();
|
||||
} catch {
|
||||
/* 忽略:进入首页时路由守卫会再拉一次菜单 */
|
||||
}
|
||||
router.replace("/home");
|
||||
return;
|
||||
}
|
||||
if (status === "paid") {
|
||||
|
||||
@@ -93,3 +93,11 @@ payment_secret_key = f656fe85ed6a1caba9f19966d8cdde46114709969bea5875cde7664d4ad
|
||||
# 要求:公网可访问、HTTPS、无鉴权无重定向;微信 JSAPI 还需在商户平台「支付授权目录」登记同一域名。
|
||||
# 本地开发收不到渠道回调,留空即可(回调地址退化为相对路径 /api/payment/callback/{渠道},由网关补齐域名)。
|
||||
payment_callback_base = https://api.yunzer.cn
|
||||
|
||||
# 渠道证书落盘根目录(各渠道目录的父目录,实际路径 = {payment_cert_dir}/{渠道}/xxx.pem)。
|
||||
# 留空时按进程工作目录下的 certs/payment 处理。
|
||||
# 历史数据里 cert_json 存的是相对路径(相对「当初上传时」的进程工作目录),
|
||||
# 若服务的工作目录与当初不一致(例如上传时是 /www/wwwroot/api.yunzer.cn,现在跑在 /app),
|
||||
# 把这里配成当初上传用的绝对目录即可继续读到旧证书,无需重新上传:
|
||||
payment_cert_dir = /www/wwwroot/api.yunzer.cn/certs/payment/wechat
|
||||
# payment_cert_dir =
|
||||
@@ -384,7 +384,9 @@ func (c *PlatformPaymentController) UploadCertificate() {
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
dir := filepath.Join("certs", "payment", channel)
|
||||
// 落盘目录:payment_cert_dir 配置优先,未配置时用相对工作目录的 certs/payment/<渠道>。
|
||||
// 入库保持相对路径——本地开发与线上可能共用同一个库,存绝对路径会互相覆盖对方环境。
|
||||
dir := payment.CertDir(channel)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
c.jsonErr(500, 500, "创建证书目录失败: "+err.Error())
|
||||
return
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
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()
|
||||
}
|
||||
@@ -221,6 +221,10 @@ func LoadChannelConfig(channel string) (*ChannelConfig, error) {
|
||||
}
|
||||
if row.CertJSON != nil && *row.CertJSON != "" {
|
||||
_ = json.Unmarshal([]byte(*row.CertJSON), &cfg.CertPaths)
|
||||
// 历史数据存的是相对路径,这里统一解析为当前可读路径,避免依赖进程工作目录
|
||||
for key, val := range cfg.CertPaths {
|
||||
cfg.CertPaths[key] = ResolveCertPath(val)
|
||||
}
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user