This commit is contained in:
oneao committed 2026-04-16 21:29:38 +08:00
1 parent 6300778150
commit e893ef2e48
27 files changed
+870 -33

No files matched your search

@@ -24,7 +24,7 @@ func RunServer(cfg *config.Config) error {
app.Use(middleware.Logger())
// ===== router =====
router.SetupRouter(app)
router.SetupRouter(app, cfg)
addr := fmt.Sprintf(":%d", cfg.App.Port)
@@ -7,11 +7,13 @@ type Config struct {
JWT JwtConfig `mapstructure:"jwt"`
UniqueID UniqueIDConfig `mapstructure:"unique_id"`
Postgres PostgresConfig `mapstructure:"postgres"`
Wechat WechatConfig `mapstructure:"wechat"`
}
type AppConfig struct {
Name string `mapstructure:"name"`
Port int `mapstructure:"port"`
Name string `mapstructure:"name"`
Port int `mapstructure:"port"`
BaseUrl string `mapstructure:"base_url"`
}
type PostgresConfig struct {
@@ -36,3 +38,8 @@ type UniqueIDConfig struct {
DataCenterID uint16 `mapstructure:"datacenter_id"`
WorkerID uint16 `mapstructure:"worker_id"`
}
type WechatConfig struct {
AppId string `mapstructure:"app_id"`
AppSecret string `mapstructure:"app_secret"`
}
@@ -52,6 +52,7 @@ func Load() (*Config, error) {
func setDefaults(v *viper.Viper) {
v.SetDefault("app.name", "allapp-go")
v.SetDefault("app.port", 8080)
v.SetDefault("app.base_url", "app")
// 可以继续扩展
v.SetDefault("unique_id.datacenter_id", 1)
@@ -0,0 +1,201 @@
package handle
import (
"allapp-go/internal/errors"
"allapp-go/internal/httpx"
"allapp-go/internal/types"
"allapp-go/pkg/db"
"allapp-go/pkg/jwtx"
"allapp-go/pkg/uniqueid"
"allapp-go/pkg/wechat"
"fmt"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/log"
)
// LoginQq ======================== QQ 登录 ========================
func LoginQq(c fiber.Ctx) error {
var req types.LoginQqDTO
vo := new(types.LoginVO)
if err := httpx.BindAndValidate(c, &req); err != nil {
return errors.WithStack(err)
}
return handleThirdLogin(c, req.Openid, req.Nickname, req.Avatar, 1, vo)
}
// LoginWechat ======================== 微信登录 ========================
func LoginWechat(c fiber.Ctx) error {
var req types.LoginWechatDTO
vo := new(types.LoginVO)
if err := httpx.BindAndValidate(c, &req); err != nil {
return errors.WithStack(err)
}
openid, token, err := wechat.GetWechatAccess(req.Code)
if err != nil {
log.Errorw("wechat access failed", "code", req.Code, "error", err)
return httpx.Fail(c, "微信登录失败,请重试")
}
nickname, avatar, err := wechat.GetWechatUserInfo(token, openid)
if err != nil {
log.Errorw("wechat userinfo failed", "openid", openid, "error", err)
return httpx.Fail(c, "微信登录失败,请重试")
}
return handleThirdLogin(c, openid, nickname, avatar, 0, vo)
}
// ======================== 第三方登录主流程 ========================
func handleThirdLogin(
c fiber.Ctx,
openid string,
nickname string,
avatar string,
loginType int16,
vo *types.LoginVO,
) error {
dbClient := db.New()
user, err := getUserByOpenID(dbClient, c, openid, loginType)
if err != nil {
return errors.WithStack(err)
}
// 不存在 -> 注册
if user == nil {
return registerUser(dbClient, c, openid, nickname, avatar, loginType, vo)
}
// ========= 校验状态 =========
id := toInt64(user["id"])
status := toInt64(user["status"])
if status == 0 {
return httpx.Fail(c, "账号已被禁用")
}
// ========= 更新登录时间 =========
if err := dbClient.Update(
c.Context(),
"user",
"id",
map[string]any{
"id": id,
"last_login_time": "NOW()",
},
); err != nil {
return errors.WithStack(err)
}
// ========= token =========
token, err := jwtx.CreateToken(c.Context(), map[string]any{
"id": id,
})
if err != nil {
return errors.WithStack(err)
}
// ========= 返回 =========
vo.Token = token
vo.UserId = id
vo.Nickname, _ = user["nickname"].(string)
vo.Avatar, _ = user["avatar"].(string)
return httpx.OK(c, vo)
}
// ======================== 注册 ========================
func registerUser(
dbClient *db.Client,
c fiber.Ctx,
openid, nickname, avatar string,
loginType int16,
vo *types.LoginVO,
) error {
userId := uniqueid.NextId()
if nickname == "" {
nickname = fmt.Sprintf("用户_%d", userId)
}
if avatar == "" {
avatar = "https://default-avatar-url.com/default.png"
}
err := dbClient.WithTx(c.Context(), func(tx *db.Client) error {
if err := tx.Insert(c.Context(), "user", "id", map[string]any{
"id": userId,
"nickname": nickname,
"avatar": avatar,
"status": 1,
"create_time": "NOW()",
"update_time": "NOW()",
"last_login_time": "NOW()",
}); err != nil {
return err
}
if err := tx.Insert(c.Context(), "user_oauth", "id", map[string]any{
"id": uniqueid.NextId(),
"user_id": userId,
"type": loginType,
"openid": openid,
"create_time": "NOW()",
"update_time": "NOW()",
}); err != nil {
return err
}
return nil
})
if err != nil {
return errors.WithStack(err)
}
token, err := jwtx.CreateToken(c.Context(), map[string]any{
"id": userId,
})
if err != nil {
return errors.WithStack(err)
}
vo.Token = token
vo.UserId = userId
vo.Nickname = nickname
vo.Avatar = avatar
return httpx.OK(c, vo)
}
// ======================== DB 查询封装(去重复 SQL) ========================
func getUserByOpenID(dbClient *db.Client, c fiber.Ctx, openid string, loginType int16) (map[string]any, error) {
users, err := dbClient.LoadDataBySQL(
c.Context(),
"SELECT u.* FROM user_oauth o JOIN user u ON u.id = o.user_id WHERE o.openid = ? AND o.type = ?",
[]any{openid, loginType},
)
if err != nil {
return nil, err
}
if len(users) == 0 {
return nil, nil
}
return users[0], nil
}
// ======================== 类型工具(极简版) ========================
func toInt64(v any) int64 {
i, _ := v.(int64)
return i
}
@@ -1,6 +1,7 @@
package handle
import (
"allapp-go/internal/errors"
"allapp-go/internal/httpx"
"allapp-go/internal/types"
"allapp-go/pkg/db"
@@ -12,7 +13,7 @@ func SaveData(c fiber.Ctx) error {
var req []types.SaveDataReq
if err := httpx.BindAndValidate(c, &req); err != nil {
return err
return errors.WithStack(err)
}
if len(req) == 0 {
@@ -49,7 +50,7 @@ func SaveData(c fiber.Ctx) error {
})
if err != nil {
return err
return errors.WithStack(err)
}
return httpx.OK(c, nil)
@@ -1,14 +1,20 @@
package router
import (
"allapp-go/internal/config"
"allapp-go/internal/handle"
"github.com/gofiber/fiber/v3"
)
func SetupRouter(app *fiber.App) {
func SetupRouter(app *fiber.App, cfg *config.Config) {
api := app.Group(cfg.App.BaseUrl)
// ==================== data ====================
app.Post("/data/loadData", handle.LoadData)
app.Post("/data/loadDataBySql", handle.LoadDataBySql)
app.Post("/data/saveData", handle.SaveData)
api.Post("/data/loadData", handle.LoadData)
api.Post("/data/loadDataBySql", handle.LoadDataBySql)
api.Post("/data/saveData", handle.SaveData)
api.Post("/auth/login/qq", handle.LoginQq)
api.Post("/auth/login/wechat", handle.LoginWechat)
}
@@ -0,0 +1,18 @@
package types
type LoginQqDTO struct {
Openid string `json:"openid" validate:"required"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}
type LoginWechatDTO struct {
Code string `json:"code" validate:"required"`
}
type LoginVO struct {
Token string `json:"token"`
UserId int64 `json:"userId"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}