You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
79 lines
3.1 KiB
79 lines
3.1 KiB
package console
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"io/fs"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// console 是**单例服务**:InitSys 只初始化 postgres(主库)/redis/nats/email,**从不 mysql.OnInit**。
|
|
// 业务库连接是按 X-App-Id 用 mysql.NewSys 现建的实例(registry.buildAppConn),跟包级连接是两回事。
|
|
//
|
|
// 所以包级 mysql 的 defsys 恒为 nil,任何走它的调用都是 nil interface method call panic,
|
|
// 被 gin.Recovery 兜成 HTTP 500 —— 前端只拿得到状态码,看不到任何业务错误信息,极难定位。
|
|
// 2026-09-14 的「赠送资源点」500 就是 giftUserResource 调了 comm.LoadComputeRates() 造成的。
|
|
//
|
|
// 而且这些配置本来就是**按应用**存在各自业务库里的,包级连接即使初始化了也会读到别人那一份。
|
|
// console 侧一律走 conn(见 api_compute.go 的 computeRatesOf / appConnFromCtx)。
|
|
|
|
// comm 包里直接使用包级 mysql/postgres 连接的导出函数,console 不能调。
|
|
var commPkgLevelDBFuncs = map[string]string{
|
|
"LoadOpsParams": "改用 conn.Find(comm.TableAppConfig, ...) + comm.ParseOpsParams",
|
|
"LoadComputeRates": "改用 computeRatesOf(conn)",
|
|
"SaveComputeRates": "改用 saveComputeConfig 里按 conn 写的那套",
|
|
"BoundFactoryDevices": "改用 registry 主库连接自己查 device_mac",
|
|
"ResolveUserVip": "改用 userComputeSummary(conn, user) 那条跨库拼装",
|
|
"ApplyComputeUsage": "console 不记消耗,只按 conn 调整余额",
|
|
"AdjustUserCompute": "改用 api_compute.go 里按 conn 重写的那份",
|
|
"GrantUserCompute": "改用 conn.Table(comm.TableUser).Update(...)",
|
|
}
|
|
|
|
func TestConsoleDoesNotUsePackageLevelDB(t *testing.T) {
|
|
fset := token.NewFileSet()
|
|
pkgs, err := parser.ParseDir(fset, ".", func(fi fs.FileInfo) bool {
|
|
return !strings.HasSuffix(fi.Name(), "_test.go")
|
|
}, 0) // 0 = 丢弃注释,注释里提到函数名不算调用
|
|
if err != nil {
|
|
t.Fatalf("解析 console 包失败: %v", err)
|
|
}
|
|
for _, pkg := range pkgs {
|
|
for path, f := range pkg.Files {
|
|
ast.Inspect(f, func(n ast.Node) bool {
|
|
call, ok := n.(*ast.CallExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
ident, ok := sel.X.(*ast.Ident)
|
|
if !ok {
|
|
return true
|
|
}
|
|
pos := fset.Position(call.Pos())
|
|
switch ident.Name {
|
|
case "comm":
|
|
if fix, bad := commPkgLevelDBFuncs[sel.Sel.Name]; bad {
|
|
t.Errorf("%s:%d: console 不能调 comm.%s —— 它走包级 mysql 连接,"+
|
|
"console 从不初始化,运行时必 panic → HTTP 500。%s",
|
|
path, pos.Line, sel.Sel.Name, fix)
|
|
}
|
|
case "mysql":
|
|
// 只允许建实例与设置选项:mysql.NewSys / mysql.SetXxx。
|
|
// 其余(Find/FindOne/Insert/Save/Table/Exec…)都是包级 defsys 的转发。
|
|
name := sel.Sel.Name
|
|
if name != "NewSys" && !strings.HasPrefix(name, "Set") {
|
|
t.Errorf("%s:%d: console 不能调包级 mysql.%s —— defsys 恒为 nil,"+
|
|
"改用按 X-App-Id 取出的 conn(appConnFromCtx / registry.getServiceDB)",
|
|
path, pos.Line, name)
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|