package config import ( "cls/internal/infrastructure/payment/wechat_pay" "cls/internal/infrastructure/wechat" Ihttp "cls/pkg/http" "cls/pkg/logger" "cls/pkg/sms" "cls/pkg/util/security" "cls/pkg/xorm_engine" "cls/ui" "context" "encoding/json" "fmt" _ "github.com/go-sql-driver/mysql" "net/http" "time" auth_middleware "cls/internal/infrastructure/middleware/auth" "github.com/ClickHouse/clickhouse-go/v2" "github.com/cenkalti/backoff/v4" "github.com/redis/go-redis/v9" "net" "strings" middleware "cls/pkg/web/middleware" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" "github.com/gin-gonic/gin" "go.uber.org/fx" "xorm.io/xorm" "xorm.io/xorm/names" ) func NewGinEngine(lc fx.Lifecycle, auth *auth_middleware.AuthMiddleware, appConfig *AppConfig) *gin.Engine { if strings.ToLower(appConfig.ServerConfig.Mode) == "prod" { gin.SetMode(gin.ReleaseMode) } handler := gin.New() handler.Use(gin.Recovery()) handler.GET("/MP_verify_dbfX2r5hEeBPV3qp.txt", func(c *gin.Context) { c.File("MP_verify_dbfX2r5hEeBPV3qp.txt") }) handler.GET("/callback_test", func(c *gin.Context) { o := "oC8UY6Tc_qFZ33JDFZPzyqL_ZqnU" token := auth.HandleCallback(o) p := fmt.Sprintf("/?token=%s", token) fmt.Println(p) c.Redirect(http.StatusFound, p) }) handler.GET("/callback", func(c *gin.Context) { code := c.Query("code") if code == "" { c.String(http.StatusBadRequest, "Missing code") return } fmt.Println("get code done", code) url := fmt.Sprintf( "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", appConfig.WeChatConfig.AppId, appConfig.WeChatConfig.AppSecret, code, ) resp, err := http.Get(url) if err != nil { c.String(http.StatusInternalServerError, "Request WeChat failed: %v", err) return } defer resp.Body.Close() var res struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` OpenID string `json:"openid"` Scope string `json:"scope"` ErrCode int `json:"errcode"` ErrMsg string `json:"errmsg"` } if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { c.String(http.StatusInternalServerError, "Failed to decode response") return } fmt.Printf("%+v\n", res) if res.ErrCode != 0 { c.String(http.StatusInternalServerError, "WeChat error: %s", res.ErrMsg) return } // 🎯 获取到 openid openid := res.OpenID fmt.Println("get openid,", openid) fmt.Println("openid:", openid) token := auth.HandleCallback(openid) fmt.Println(token) c.Redirect(http.StatusFound, fmt.Sprintf("/?token=%s", token)) //c.String(http.StatusFound, token) }) handler.Use(auth.Handle()) handler.Use(middleware.ServeRoot("/", "dist", &ui.RESOURCE)) handler.NoRoute(func(context *gin.Context) { if context.Request.RequestURI != "/" { context.Redirect(http.StatusTemporaryRedirect, "/") } }) srv := &http.Server{Addr: fmt.Sprintf(":%d", appConfig.ServerConfig.Port), Handler: handler} lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { ln, err := net.Listen("tcp", srv.Addr) if err != nil { return err } log.Infof("Web server starting on port %v", srv.Addr) log.Info("Press Ctrl+C to stop") go func() { if err0 := srv.Serve(ln); err0 != nil { log.Error(err0) } }() return nil }, OnStop: func(ctx context.Context) error { return srv.Shutdown(ctx) }, }) return handler } func NewXormEngine(appConfig *AppConfig, l logger.New) *xorm_engine.Engine { dbConfig := appConfig.MysqlConfig url := fmt.Sprintf("%s:%s@(%s)/%s", dbConfig.Username, dbConfig.Password, dbConfig.Address, dbConfig.Database) engine, err := xorm.NewEngineWithParams("mysql", url, dbConfig.Params) if err != nil { log.Fatal(err) } fmt.Println("done") engine.SetTableMapper(names.NewPrefixMapper(names.GonicMapper{}, "lc_")) engine.SetColumnMapper(names.GonicMapper{}) engine.SetLogger(logger.NewXormLogger(l("xorm"))) if dbConfig.ShowSql { engine.ShowSQL(true) } if err = backoff.Retry(engine.Ping, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } urlCls := fmt.Sprintf("%s:%s@(%s)/%s", dbConfig.Username, dbConfig.Password, dbConfig.Address, dbConfig.DatabaseCls) engineCls, err := xorm.NewEngineWithParams("mysql", urlCls, dbConfig.Params) if err != nil { log.Fatal(err) } if err = backoff.Retry(engineCls.Ping, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } return xorm_engine.NewEngine(engine, engineCls) } func NewClickhouseConn(appConfig *AppConfig) driver.Conn { dbConfig := appConfig.ClickhouseConfig conn, err := clickhouse.Open(&clickhouse.Options{ Addr: dbConfig.Address, Auth: clickhouse.Auth{ Database: dbConfig.Database, Username: dbConfig.Username, Password: dbConfig.Password, }, Settings: clickhouse.Settings{ "max_execution_time": 60, }, Compression: &clickhouse.Compression{ Method: clickhouse.CompressionLZ4, }, DialTimeout: time.Duration(10) * time.Second, MaxOpenConns: 5, MaxIdleConns: 5, ConnMaxLifetime: time.Duration(10) * time.Minute, ConnOpenStrategy: clickhouse.ConnOpenInOrder, BlockBufferSize: 10, }) if err != nil { log.Fatal(err) } if err = backoff.Retry(func() error { return conn.Ping(context.Background()) }, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } if v, err := conn.ServerVersion(); err != nil { log.Fatal(err) } else { log.Info(v) } return conn } func NewRedisClient(appConfig *AppConfig) redis.Cmdable { dbConfig := appConfig.RedisConfig var rdbCli redis.Cmdable switch dbConfig.Mode { case "sentinel": rdbCli = redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: dbConfig.MasterName, SentinelAddrs: dbConfig.SentinelAddrs, SentinelUsername: dbConfig.SentinelUsername, SentinelPassword: dbConfig.SentinelPassword, Username: dbConfig.Username, Password: dbConfig.Password, DB: 0, }) case "cluster": rdbCli = redis.NewClusterClient(&redis.ClusterOptions{ Addrs: dbConfig.Addrs, Username: dbConfig.Username, Password: dbConfig.Password, }) default: rdbCli = redis.NewClient(&redis.Options{ Addr: dbConfig.Addr, Username: dbConfig.Username, Password: dbConfig.Password, DB: dbConfig.DB, }) } if err := backoff.Retry(func() error { return rdbCli.Ping(context.Background()).Err() }, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 10)); err != nil { log.Fatal(err) } return rdbCli } func NewLoggerFactory(config *AppConfig) logger.New { return func(name string) logger.Logger { var options []logger.Option if v, ok := config.LogConfig.Loggers[strings.ToLower(name)]; ok { options = append(options, logger.Level(logger.LogLevel(v))) } return logger.NewLogger(name, options...) } } func NewPasswordEncoder() security.PasswordEncoder { return &security.BCryptPasswordEncoder{} } func NewSmsService() sms.IsmsService { return sms.NewSmsService() } func NewInternalClient() *Ihttp.Client { return Ihttp.NewClient() } func NewJWTAuthMiddleware(appConfig *AppConfig, log logger.New, redis redis.Cmdable) *auth_middleware.AuthMiddleware { return auth_middleware.NewAuthMiddleware(appConfig.JwtConfig.Secret, nil, redis, log) } func NewPayService(config *AppConfig) *wechat_pay.PayService { return wechat_pay.NewPayService(config.WeChatConfig.MchId, config.WeChatConfig.AppId, config.WeChatConfig.MchCertificateSerialNumber, config.WeChatConfig.MchAPIv3Key, config.WeChatConfig.PrivateCertPath, config.WeChatConfig.PublicCertPath, ) } func NewWechatService(config *AppConfig) *wechat.WechatService { return wechat.NewWechatService(config.WeChatConfig.AppId, config.WeChatConfig.AppSecret) }