package main import ( "embed" "flag" "fmt" "io/fs" "net/http" "os/exec" "path" "runtime" "strings" "time" "bj_power_wms/internal/config" "bj_power_wms/internal/handler" "bj_power_wms/internal/logdaily" "bj_power_wms/internal/svc" "github.com/zeromicro/go-zero/core/conf" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/rest" ) // 前端静态资源(Vue 构建产物,由 frontend/ npm run build 输出到 web/static)。 // 原由 bj_power_wms_client(8891) 托管并反代 API,2026-09-07 合并进主服务: // 一个 exe、一个端口(8890)同时提供页面与 API。 // //go:embed all:web/static var webFS embed.FS var configFile = flag.String("f", "etc/bj_power_wms-api.yaml", "the config file") // newStaticHandler 嵌入式静态文件:命中则直接返回;未命中的非 /api 路径 // 回退到 index.html(SPA history 路由),由浏览器端 Vue Router 接管。 func newStaticHandler(fsys embed.FS) http.Handler { sub, err := fs.Sub(fsys, "web/static") if err != nil { panic(fmt.Sprintf("嵌入静态目录不可用: %v", err)) } fileServer := http.FileServer(http.FS(sub)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { name := strings.TrimPrefix(path.Clean(r.URL.Path), "/") if name == "" { name = "index.html" } if f, openErr := sub.Open(name); openErr == nil { f.Close() fileServer.ServeHTTP(w, r) return } r2 := r.Clone(r.Context()) r2.URL.Path = "/" fileServer.ServeHTTP(w, r2) }) } // openBrowser 调用系统默认浏览器打开 url(仅本机开发使用,服务器部署时 Web.OpenBrowser=false) func openBrowser(url string) { var cmd *exec.Cmd switch runtime.GOOS { case "windows": cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", url) case "darwin": cmd = exec.Command("open", url) default: cmd = exec.Command("xdg-open", url) } _ = cmd.Start() } func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) // 日志按天存储:logs/bj_power_wms-2026-01-02.log,保留 30 天 if c.Log.Mode == "file" { logx.SetUp(c.Log) w := logdaily.New(c.Log.Path, c.Name, 30) defer w.Close() logx.SetWriter(logx.NewWriter(w)) } staticHandler := newStaticHandler(webFS) // 兜底路由:已注册的 /api/* 走正常匹配;未注册的非 /api 路径走嵌入式静态资源 //(/api 下未注册的路径仍返回 404,不回退页面) notFound := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/api" { http.NotFound(w, r) return } staticHandler.ServeHTTP(w, r) }) server := rest.MustNewServer(c.RestConf, rest.WithNotFoundHandler(notFound)) defer server.Stop() ctx := svc.NewServiceContext(c) handler.RegisterHandlers(server, ctx) fmt.Printf("Starting bj_power_wms server at %s:%d...\n", c.Host, c.Port) logx.Infof("Starting bj_power_wms server at %s:%d...", c.Host, c.Port) // 服务起来后自动打开浏览器访问前端(本机开发便利;Web.OpenBrowser=false 关闭) if c.Web.OpenBrowser { go func() { time.Sleep(time.Second) host := c.Host if host == "0.0.0.0" || host == "" { host = "127.0.0.1" } openBrowser(fmt.Sprintf("http://%s:%d", host, c.Port)) }() } server.Start() }