u
This commit is contained in:
1 parent
6300778150
commit
e893ef2e48
27 files changed
+868
-31
No files matched your search
@@ -1,3 +1,7 @@
|
|||||||
app:
|
app:
|
||||||
name: allapp
|
name: allapp
|
||||||
port: 8080
|
port: 8080
|
||||||
|
base_url: /app
|
||||||
|
wechat:
|
||||||
|
app_id: wxdab3e21a1f7e392f
|
||||||
|
app_secret: 0895443c6fedfc21cb92c9cd60de2a26
|
||||||
@@ -7,6 +7,7 @@ require (
|
|||||||
github.com/gofiber/fiber/v3 v3.1.0
|
github.com/gofiber/fiber/v3 v3.1.0
|
||||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||||
github.com/jackc/pgx/v5 v5.9.1
|
github.com/jackc/pgx/v5 v5.9.1
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/spf13/viper v1.21.0
|
github.com/spf13/viper v1.21.0
|
||||||
go.uber.org/zap v1.27.1
|
go.uber.org/zap v1.27.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
@@ -15,6 +16,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
|
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
@@ -31,7 +33,6 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/philhofer/fwd v1.2.0 // indirect
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
github.com/spf13/afero v1.15.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
@@ -41,6 +42,7 @@ require (
|
|||||||
github.com/tinylib/msgp v1.6.3 // indirect
|
github.com/tinylib/msgp v1.6.3 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.69.0 // indirect
|
github.com/valyala/fasthttp v1.69.0 // indirect
|
||||||
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/crypto v0.49.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func RunServer(cfg *config.Config) error {
|
|||||||
app.Use(middleware.Logger())
|
app.Use(middleware.Logger())
|
||||||
|
|
||||||
// ===== router =====
|
// ===== router =====
|
||||||
router.SetupRouter(app)
|
router.SetupRouter(app, cfg)
|
||||||
|
|
||||||
addr := fmt.Sprintf(":%d", cfg.App.Port)
|
addr := fmt.Sprintf(":%d", cfg.App.Port)
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ type Config struct {
|
|||||||
JWT JwtConfig `mapstructure:"jwt"`
|
JWT JwtConfig `mapstructure:"jwt"`
|
||||||
UniqueID UniqueIDConfig `mapstructure:"unique_id"`
|
UniqueID UniqueIDConfig `mapstructure:"unique_id"`
|
||||||
Postgres PostgresConfig `mapstructure:"postgres"`
|
Postgres PostgresConfig `mapstructure:"postgres"`
|
||||||
|
Wechat WechatConfig `mapstructure:"wechat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Name string `mapstructure:"name"`
|
Name string `mapstructure:"name"`
|
||||||
Port int `mapstructure:"port"`
|
Port int `mapstructure:"port"`
|
||||||
|
BaseUrl string `mapstructure:"base_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostgresConfig struct {
|
type PostgresConfig struct {
|
||||||
@@ -36,3 +38,8 @@ type UniqueIDConfig struct {
|
|||||||
DataCenterID uint16 `mapstructure:"datacenter_id"`
|
DataCenterID uint16 `mapstructure:"datacenter_id"`
|
||||||
WorkerID uint16 `mapstructure:"worker_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) {
|
func setDefaults(v *viper.Viper) {
|
||||||
v.SetDefault("app.name", "allapp-go")
|
v.SetDefault("app.name", "allapp-go")
|
||||||
v.SetDefault("app.port", 8080)
|
v.SetDefault("app.port", 8080)
|
||||||
|
v.SetDefault("app.base_url", "app")
|
||||||
|
|
||||||
// 可以继续扩展
|
// 可以继续扩展
|
||||||
v.SetDefault("unique_id.datacenter_id", 1)
|
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
|
package handle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"allapp-go/internal/errors"
|
||||||
"allapp-go/internal/httpx"
|
"allapp-go/internal/httpx"
|
||||||
"allapp-go/internal/types"
|
"allapp-go/internal/types"
|
||||||
"allapp-go/pkg/db"
|
"allapp-go/pkg/db"
|
||||||
@@ -12,7 +13,7 @@ func SaveData(c fiber.Ctx) error {
|
|||||||
var req []types.SaveDataReq
|
var req []types.SaveDataReq
|
||||||
|
|
||||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req) == 0 {
|
if len(req) == 0 {
|
||||||
@@ -49,7 +50,7 @@ func SaveData(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpx.OK(c, nil)
|
return httpx.OK(c, nil)
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"allapp-go/internal/config"
|
||||||
"allapp-go/internal/handle"
|
"allapp-go/internal/handle"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v3"
|
"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 ====================
|
// ==================== data ====================
|
||||||
app.Post("/data/loadData", handle.LoadData)
|
api.Post("/data/loadData", handle.LoadData)
|
||||||
app.Post("/data/loadDataBySql", handle.LoadDataBySql)
|
api.Post("/data/loadDataBySql", handle.LoadDataBySql)
|
||||||
app.Post("/data/saveData", handle.SaveData)
|
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"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSONStringToStruct 将 JSON 字符串反序列化到指定结构体类型
|
||||||
|
func JSONStringToStruct[T any](jsonStr string) (*T, error) {
|
||||||
|
var result T
|
||||||
|
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func JSONStringToMap(jsonStr string) (map[string]interface{}, error) {
|
||||||
|
var result map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateCode 生成指定长度的随机邀请码(大写字母+数字)
|
||||||
|
// 不会返回错误,如果随机失败,会自动重试
|
||||||
|
func GenerateCode(length int) string {
|
||||||
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
code := make([]byte, length)
|
||||||
|
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
for {
|
||||||
|
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||||
|
if err != nil {
|
||||||
|
// 随机失败,重试
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
code[i] = charset[num.Int64()]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(code)
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ func (c *Client) LoadData(
|
|||||||
orderBy string,
|
orderBy string,
|
||||||
searchColumns []string,
|
searchColumns []string,
|
||||||
args []any,
|
args []any,
|
||||||
) ([][]any, error) {
|
) ([]map[string]any, error) {
|
||||||
|
|
||||||
var where string
|
var where string
|
||||||
|
|
||||||
@@ -50,12 +50,14 @@ func (c *Client) LoadDataBySQL(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
sql string,
|
sql string,
|
||||||
args []any,
|
args []any,
|
||||||
) ([][]any, error) {
|
) ([]map[string]any, error) {
|
||||||
return c.query(ctx, sql, args...)
|
return c.query(ctx, sql, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一查询方法(核心优化)
|
// ==========================
|
||||||
func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, error) {
|
// 核心查询方法(已升级)
|
||||||
|
// ==========================
|
||||||
|
func (c *Client) query(ctx context.Context, sql string, args ...any) ([]map[string]any, error) {
|
||||||
sql = convertPlaceholder(sql)
|
sql = convertPlaceholder(sql)
|
||||||
|
|
||||||
rows, err := c.exec().Query(ctx, sql, args...)
|
rows, err := c.exec().Query(ctx, sql, args...)
|
||||||
@@ -64,7 +66,10 @@ func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, e
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
result := make([][]any, 0, 16)
|
// 获取字段名
|
||||||
|
fields := rows.FieldDescriptions()
|
||||||
|
|
||||||
|
result := make([]map[string]any, 0, 16)
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
values, err := rows.Values()
|
values, err := rows.Values()
|
||||||
@@ -72,8 +77,13 @@ func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
row := make([]any, len(values))
|
row := make(map[string]any, len(values))
|
||||||
copy(row, values)
|
|
||||||
|
for i, f := range fields {
|
||||||
|
if i < len(values) {
|
||||||
|
row[string(f.Name)] = values[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = append(result, row)
|
result = append(result, row)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package requestx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全局 Fiber Client
|
||||||
|
var cc = client.New()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cc.SetTimeout(10 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeConfig 合并默认配置和用户配置
|
||||||
|
func mergeConfig(defaultCfg, userCfg client.Config) client.Config {
|
||||||
|
if userCfg.Ctx != nil {
|
||||||
|
defaultCfg.Ctx = userCfg.Ctx
|
||||||
|
}
|
||||||
|
if userCfg.Body != nil {
|
||||||
|
defaultCfg.Body = userCfg.Body
|
||||||
|
}
|
||||||
|
if userCfg.Header != nil {
|
||||||
|
if defaultCfg.Header == nil {
|
||||||
|
defaultCfg.Header = map[string]string{}
|
||||||
|
}
|
||||||
|
for k, v := range userCfg.Header {
|
||||||
|
defaultCfg.Header[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if userCfg.Param != nil {
|
||||||
|
defaultCfg.Param = userCfg.Param
|
||||||
|
}
|
||||||
|
if userCfg.PathParam != nil {
|
||||||
|
defaultCfg.PathParam = userCfg.PathParam
|
||||||
|
}
|
||||||
|
if userCfg.FormData != nil {
|
||||||
|
defaultCfg.FormData = userCfg.FormData
|
||||||
|
}
|
||||||
|
if userCfg.Timeout != 0 {
|
||||||
|
defaultCfg.Timeout = userCfg.Timeout
|
||||||
|
}
|
||||||
|
if userCfg.UserAgent != "" {
|
||||||
|
defaultCfg.UserAgent = userCfg.UserAgent
|
||||||
|
}
|
||||||
|
if userCfg.Referer != "" {
|
||||||
|
defaultCfg.Referer = userCfg.Referer
|
||||||
|
}
|
||||||
|
defaultCfg.DisablePathNormalizing = userCfg.DisablePathNormalizing
|
||||||
|
defaultCfg.MaxRedirects = userCfg.MaxRedirects
|
||||||
|
|
||||||
|
return defaultCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get(url string, cfg client.Config) (string, int, error) {
|
||||||
|
defaultCfg := client.Config{
|
||||||
|
Ctx: context.Background(),
|
||||||
|
}
|
||||||
|
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||||
|
|
||||||
|
resp, err := cc.Get(url, finalCfg)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(resp.Body()), resp.StatusCode(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostJSON(url string, data any, cfg client.Config) (string, int, error) {
|
||||||
|
defaultCfg := client.Config{
|
||||||
|
Ctx: context.Background(),
|
||||||
|
Body: data,
|
||||||
|
Header: map[string]string{"Content-Type": "application/json"},
|
||||||
|
}
|
||||||
|
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||||
|
|
||||||
|
resp, err := cc.Post(url, finalCfg)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
return string(resp.Body()), resp.StatusCode(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostForm(url string, formData map[string]string, cfg client.Config) (string, int, error) {
|
||||||
|
defaultCfg := client.Config{
|
||||||
|
Ctx: context.Background(),
|
||||||
|
FormData: formData,
|
||||||
|
Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
|
}
|
||||||
|
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||||
|
|
||||||
|
resp, err := cc.Post(url, finalCfg)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
return string(resp.Body()), resp.StatusCode(), nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package wechat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"allapp-go/pkg/common"
|
||||||
|
"allapp-go/pkg/requestx"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jwtManager struct {
|
||||||
|
appid string
|
||||||
|
secret string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
manager *jwtManager
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init(appid string, secret string) {
|
||||||
|
once.Do(func() {
|
||||||
|
manager = &jwtManager{
|
||||||
|
appid: appid,
|
||||||
|
secret: secret,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getManager() *jwtManager {
|
||||||
|
if manager == nil {
|
||||||
|
panic("wechat未初始化,请先调用Init")
|
||||||
|
}
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWechatAccess(code string) (openid string, accessToken string, err error) {
|
||||||
|
bodyData, bodyCode, err := requestx.Get(
|
||||||
|
"https://api.weixin.qq.com/sns/oauth2/access_token",
|
||||||
|
client.Config{
|
||||||
|
Param: map[string]string{
|
||||||
|
"appid": getManager().appid,
|
||||||
|
"secret": getManager().secret,
|
||||||
|
"code": code,
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bodyCode != 200 {
|
||||||
|
return "", "", fmt.Errorf("微信Http请求失败: %d", bodyCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessMap, err := common.JSONStringToMap(bodyData)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
accessToken, _ = accessMap["access_token"].(string)
|
||||||
|
openid, _ = accessMap["openid"].(string)
|
||||||
|
|
||||||
|
if accessToken == "" || openid == "" {
|
||||||
|
return "", "", fmt.Errorf("获取微信登录凭证失败: %s", bodyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWechatUserInfo(accessToken, openid string) (nickname, avatar string, err error) {
|
||||||
|
bodyData, bodyCode, err := requestx.Get(
|
||||||
|
"https://api.weixin.qq.com/sns/userinfo",
|
||||||
|
client.Config{
|
||||||
|
Param: map[string]string{
|
||||||
|
"access_token": accessToken,
|
||||||
|
"openid": openid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bodyCode != 200 {
|
||||||
|
return "", "", fmt.Errorf("微信Http请求失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap, err := common.JSONStringToMap(bodyData)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
nickname, _ = userMap["nickname"].(string)
|
||||||
|
avatar, _ = userMap["headimgurl"].(string)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -5,8 +5,12 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./assets/images/icon.png",
|
||||||
"scheme": "apprn",
|
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
|
"scheme": [
|
||||||
|
"oneao",
|
||||||
|
"tencent102826474",
|
||||||
|
"wxdab3e21a1f7e392f"
|
||||||
|
],
|
||||||
"ios": {
|
"ios": {
|
||||||
"icon": "./assets/expo.icon"
|
"icon": "./assets/expo.icon"
|
||||||
},
|
},
|
||||||
@@ -18,7 +22,7 @@
|
|||||||
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||||
},
|
},
|
||||||
"predictiveBackGestureEnabled": false,
|
"predictiveBackGestureEnabled": false,
|
||||||
"package": "com.anonymous.apprn"
|
"package": "cn.oneao.app"
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"output": "static",
|
"output": "static",
|
||||||
@@ -26,6 +30,7 @@
|
|||||||
"bundler": "metro"
|
"bundler": "metro"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
"./plugins/withAndroidSignature",
|
||||||
"expo-router",
|
"expo-router",
|
||||||
[
|
[
|
||||||
"expo-splash-screen",
|
"expo-splash-screen",
|
||||||
@@ -37,7 +42,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"expo-web-browser"
|
"expo-web-browser",
|
||||||
|
[
|
||||||
|
"expo-qq",
|
||||||
|
{
|
||||||
|
"appId": "102826474"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"expo-build-properties",
|
||||||
|
{
|
||||||
|
"android": {
|
||||||
|
"usesCleartextTraffic": true,
|
||||||
|
"extraProguardRules": "-keep class com.tencent.mm.opensdk.** { *; }\n-keep class com.tencent.wxop.** { *; }\n-keep class com.tencent.mm.sdk.** { *; }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expo-wechat"
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true,
|
"typedRoutes": true,
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"android": {
|
||||||
|
"keystore": {
|
||||||
|
"keystorePath": "credentials/oneao-android.keystore",
|
||||||
|
"keystorePassword": "zhang520..",
|
||||||
|
"keyAlias": "oneao-android",
|
||||||
|
"keyPassword": "zhang520.."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"provisioningProfilePath": "ios/certs/profile.mobileprovision",
|
||||||
|
"distributionCertificate": {
|
||||||
|
"path": "ios/certs/dist-cert.p12",
|
||||||
|
"password": "password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -24,6 +24,7 @@
|
|||||||
"axios": "^1.15.0",
|
"axios": "^1.15.0",
|
||||||
"expo": "~55.0.15",
|
"expo": "~55.0.15",
|
||||||
"expo-blur": "~55.0.14",
|
"expo-blur": "~55.0.14",
|
||||||
|
"expo-build-properties": "~55.0.13",
|
||||||
"expo-constants": "~55.0.14",
|
"expo-constants": "~55.0.14",
|
||||||
"expo-device": "~55.0.15",
|
"expo-device": "~55.0.15",
|
||||||
"expo-font": "~55.0.6",
|
"expo-font": "~55.0.6",
|
||||||
@@ -31,12 +32,14 @@
|
|||||||
"expo-image": "~55.0.8",
|
"expo-image": "~55.0.8",
|
||||||
"expo-linear-gradient": "~55.0.13",
|
"expo-linear-gradient": "~55.0.13",
|
||||||
"expo-linking": "~55.0.13",
|
"expo-linking": "~55.0.13",
|
||||||
|
"expo-qq": "^0.1.8",
|
||||||
"expo-router": "~55.0.12",
|
"expo-router": "~55.0.12",
|
||||||
"expo-splash-screen": "~55.0.18",
|
"expo-splash-screen": "~55.0.18",
|
||||||
"expo-status-bar": "~55.0.5",
|
"expo-status-bar": "~55.0.5",
|
||||||
"expo-symbols": "~55.0.7",
|
"expo-symbols": "~55.0.7",
|
||||||
"expo-system-ui": "~55.0.15",
|
"expo-system-ui": "~55.0.15",
|
||||||
"expo-web-browser": "~55.0.14",
|
"expo-web-browser": "~55.0.14",
|
||||||
|
"expo-wechat": "^0.2.6",
|
||||||
"heroui-native": "^1.0.1",
|
"heroui-native": "^1.0.1",
|
||||||
"lucide-react-native": "^1.8.0",
|
"lucide-react-native": "^1.8.0",
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
const { withAppBuildGradle } = require("expo/config-plugins");
|
||||||
|
|
||||||
|
const CREDENTIAL_PATH = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../credentials/credentials.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
const KEYSTORE_PATH = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../credentials/oneao-android.keystore"
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = function withAndroidSignature(config) {
|
||||||
|
return withAppBuildGradle(config, (config) => {
|
||||||
|
if (config.modResults.language !== "groovy") {
|
||||||
|
throw new Error("only groovy build.gradle is supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
config.modResults.contents = setAndroidSignature(
|
||||||
|
config.modResults.contents
|
||||||
|
);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ======================== 主入口 ========================
|
||||||
|
function setAndroidSignature(appBuildGradle) {
|
||||||
|
if (!fs.existsSync(CREDENTIAL_PATH)) {
|
||||||
|
console.warn("⚠️ credentials.json 不存在,跳过签名配置");
|
||||||
|
return appBuildGradle;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = JSON.parse(
|
||||||
|
fs.readFileSync(CREDENTIAL_PATH, "utf8")
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = appBuildGradle;
|
||||||
|
|
||||||
|
// ⭐ 1. 重写 signingConfigs(关键)
|
||||||
|
output = replaceSigningConfigs(output, info);
|
||||||
|
|
||||||
|
// ⭐ 2. 确保所有 debug/release 使用统一 keystore
|
||||||
|
output = unifySigningUsage(output);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== ⭐核心:重写 signingConfigs ========================
|
||||||
|
function replaceSigningConfigs(appBuildGradle, info) {
|
||||||
|
const block = `
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
storeFile file(${JSON.stringify(KEYSTORE_PATH)})
|
||||||
|
storePassword ${JSON.stringify(info.android.keystore.keystorePassword)}
|
||||||
|
keyAlias ${JSON.stringify(info.android.keystore.keyAlias)}
|
||||||
|
keyPassword ${JSON.stringify(info.android.keystore.keyPassword)}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
storeFile file(${JSON.stringify(KEYSTORE_PATH)})
|
||||||
|
storePassword ${JSON.stringify(info.android.keystore.keystorePassword)}
|
||||||
|
keyAlias ${JSON.stringify(info.android.keystore.keyAlias)}
|
||||||
|
keyPassword ${JSON.stringify(info.android.keystore.keyPassword)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const regex = /signingConfigs\s*\{[\s\S]*?\n\s*\}/m;
|
||||||
|
|
||||||
|
if (regex.test(appBuildGradle)) {
|
||||||
|
return appBuildGradle.replace(regex, block.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有就插入
|
||||||
|
return appBuildGradle.replace(
|
||||||
|
/(android\s*\{)/,
|
||||||
|
`$1\n${block.trim()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================== ⭐统一使用 signingConfig ========================
|
||||||
|
function unifySigningUsage(appBuildGradle) {
|
||||||
|
let output = appBuildGradle;
|
||||||
|
|
||||||
|
// debug -> release(你如果想 debug 也用 release,可以改这里)
|
||||||
|
output = output.replace(
|
||||||
|
/signingConfig\s+signingConfigs\.debug/g,
|
||||||
|
"signingConfig signingConfigs.release"
|
||||||
|
);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
Generated
+47
@@ -41,6 +41,9 @@ importers:
|
|||||||
expo-blur:
|
expo-blur:
|
||||||
specifier: ~55.0.14
|
specifier: ~55.0.14
|
||||||
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
|
expo-build-properties:
|
||||||
|
specifier: ~55.0.13
|
||||||
|
version: 55.0.13(expo@55.0.15)
|
||||||
expo-constants:
|
expo-constants:
|
||||||
specifier: ~55.0.14
|
specifier: ~55.0.14
|
||||||
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3)
|
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3)
|
||||||
@@ -62,6 +65,9 @@ importers:
|
|||||||
expo-linking:
|
expo-linking:
|
||||||
specifier: ~55.0.13
|
specifier: ~55.0.13
|
||||||
version: 55.0.13(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
version: 55.0.13(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||||
|
expo-qq:
|
||||||
|
specifier: ^0.1.8
|
||||||
|
version: 0.1.8(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
expo-router:
|
expo-router:
|
||||||
specifier: ~55.0.12
|
specifier: ~55.0.12
|
||||||
version: 55.0.12(@expo/log-box@55.0.10)(@expo/metro-runtime@55.0.9)(@types/react@19.2.14)(expo-constants@55.0.14)(expo-font@55.0.6)(expo-linking@55.0.13)(expo@55.0.15)(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-web@0.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
version: 55.0.12(@expo/log-box@55.0.10)(@expo/metro-runtime@55.0.9)(@types/react@19.2.14)(expo-constants@55.0.14)(expo-font@55.0.6)(expo-linking@55.0.13)(expo@55.0.15)(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-web@0.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
@@ -80,6 +86,9 @@ importers:
|
|||||||
expo-web-browser:
|
expo-web-browser:
|
||||||
specifier: ~55.0.14
|
specifier: ~55.0.14
|
||||||
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))
|
version: 55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))
|
||||||
|
expo-wechat:
|
||||||
|
specifier: ^0.2.6
|
||||||
|
version: 0.2.6(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
heroui-native:
|
heroui-native:
|
||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
version: 1.0.2(f8f492cc9ab1e25a4f7a8dde00438ce1)
|
version: 1.0.2(f8f492cc9ab1e25a4f7a8dde00438ce1)
|
||||||
@@ -2205,6 +2214,11 @@ packages:
|
|||||||
react: '*'
|
react: '*'
|
||||||
react-native: '*'
|
react-native: '*'
|
||||||
|
|
||||||
|
expo-build-properties@55.0.13:
|
||||||
|
resolution: {integrity: sha512-UYZhUKyh7YQhbJdkBvo68WUQ7fOtZeSV7F8kfYkjEiN/ADRHG0WfEIiddvGfi9cH/5iwpptv/+Lu5cx6uvfegA==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-constants@55.0.14:
|
expo-constants@55.0.14:
|
||||||
resolution: {integrity: sha512-l23QVQCYBPKT5zbxxZdJeuhiunadvWdjcQ9+GC8h+02jCoLmWRk20064nCINnQTP3Hf+uLPteUiwYrJd0e446w==}
|
resolution: {integrity: sha512-l23QVQCYBPKT5zbxxZdJeuhiunadvWdjcQ9+GC8h+02jCoLmWRk20064nCINnQTP3Hf+uLPteUiwYrJd0e446w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2280,6 +2294,13 @@ packages:
|
|||||||
react-native-worklets:
|
react-native-worklets:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
expo-qq@0.1.8:
|
||||||
|
resolution: {integrity: sha512-Khuu4LWOjpYGGBamtUFBxaA5q8oIcFiVgXkyRVqfNdSEP6Z0Fy3eBj3HuXP8mxonT/hJjG3GCX7ypJD7M9nU/g==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
|
||||||
expo-router@55.0.12:
|
expo-router@55.0.12:
|
||||||
resolution: {integrity: sha512-Bm6IhI0Kl5/tDlCHPms8jDqy1O6HLHIOrMsEmmAQ5Lgg5UBtDfRThEyHPVOLNTOs8e7/bG/Ftz6a4UgQVA+NhQ==}
|
resolution: {integrity: sha512-Bm6IhI0Kl5/tDlCHPms8jDqy1O6HLHIOrMsEmmAQ5Lgg5UBtDfRThEyHPVOLNTOs8e7/bG/Ftz6a4UgQVA+NhQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2354,6 +2375,13 @@ packages:
|
|||||||
expo: '*'
|
expo: '*'
|
||||||
react-native: '*'
|
react-native: '*'
|
||||||
|
|
||||||
|
expo-wechat@0.2.6:
|
||||||
|
resolution: {integrity: sha512-hLWFPDqSKwEgG8Jm0cTthyQftD/ip9xjJLQUEd+fJJVq8YEiEt0gJCCgCEMbr1SrEjoK4TUlkG/HETZobNReuQ==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
|
||||||
expo@55.0.15:
|
expo@55.0.15:
|
||||||
resolution: {integrity: sha512-sHIvqG477UU1jZHhaexXbUgsU7y+xnYZqDW1HrUkEBYiuEb5lobvWLmwea76EBVkityQx46UDtepFtarpUJQqQ==}
|
resolution: {integrity: sha512-sHIvqG477UU1jZHhaexXbUgsU7y+xnYZqDW1HrUkEBYiuEb5lobvWLmwea76EBVkityQx46UDtepFtarpUJQqQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@@ -6385,6 +6413,13 @@ snapshots:
|
|||||||
react: 19.2.0
|
react: 19.2.0
|
||||||
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
||||||
|
|
||||||
|
expo-build-properties@55.0.13(expo@55.0.15):
|
||||||
|
dependencies:
|
||||||
|
'@expo/schema-utils': 55.0.3
|
||||||
|
expo: 55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||||
|
resolve-from: 5.0.0
|
||||||
|
semver: 7.7.4
|
||||||
|
|
||||||
expo-constants@55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3):
|
expo-constants@55.0.14(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/config': 55.0.15(typescript@5.9.3)
|
'@expo/config': 55.0.15(typescript@5.9.3)
|
||||||
@@ -6467,6 +6502,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
react-native-worklets: 0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
react-native-worklets: 0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
|
|
||||||
|
expo-qq@0.1.8(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
expo: 55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||||
|
react: 19.2.0
|
||||||
|
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
||||||
|
|
||||||
expo-router@55.0.12(@expo/log-box@55.0.10)(@expo/metro-runtime@55.0.9)(@types/react@19.2.14)(expo-constants@55.0.14)(expo-font@55.0.6)(expo-linking@55.0.13)(expo@55.0.15)(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-web@0.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0):
|
expo-router@55.0.12(@expo/log-box@55.0.10)(@expo/metro-runtime@55.0.9)(@types/react@19.2.14)(expo-constants@55.0.14)(expo-font@55.0.6)(expo-linking@55.0.13)(expo@55.0.15)(react-dom@19.2.0(react@19.2.0))(react-native-gesture-handler@2.30.1(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.2.1(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-screens@4.23.0(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native-web@0.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/log-box': 55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
'@expo/log-box': 55.0.10(@expo/dom-webview@55.0.5)(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)
|
||||||
@@ -6556,6 +6597,12 @@ snapshots:
|
|||||||
expo: 55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
expo: 55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||||
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
||||||
|
|
||||||
|
expo-wechat@0.2.6(expo@55.0.15)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0):
|
||||||
|
dependencies:
|
||||||
|
expo: 55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||||
|
react: 19.2.0
|
||||||
|
react-native: 0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0)
|
||||||
|
|
||||||
expo@55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3):
|
expo@55.0.15(@babel/core@7.29.0)(@expo/dom-webview@55.0.5)(@expo/metro-runtime@55.0.9)(expo-router@55.0.12)(react-dom@19.2.0(react@19.2.0))(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.4(@babel/core@7.29.0)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.29.2
|
'@babel/runtime': 7.29.2
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import type { Method } from "alova";
|
|
||||||
import type { AxiosError } from "axios";
|
|
||||||
import { axiosRequestAdapter } from "@alova/adapter-axios";
|
|
||||||
import { createAlova } from "alova";
|
|
||||||
import { toast } from "sonner-native";
|
|
||||||
|
|
||||||
const alovaInstance = createAlova({
|
|
||||||
baseURL: "",
|
|
||||||
requestAdapter: axiosRequestAdapter(),
|
|
||||||
timeout: 5000,
|
|
||||||
cacheFor: null,
|
|
||||||
});
|
|
||||||
@@ -7,6 +7,7 @@ import * as SplashScreen from "expo-splash-screen";
|
|||||||
import "@/global.css";
|
import "@/global.css";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
|
import { Toaster } from "sonner-native";
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync();
|
SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
@@ -52,6 +53,8 @@ export default function RootLayout() {
|
|||||||
{!isDev && <StatusBar style={isDark ? "light" : "dark"} />}
|
{!isDev && <StatusBar style={isDark ? "light" : "dark"} />}
|
||||||
</HeroUINativeProvider>
|
</HeroUINativeProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
||||||
|
<Toaster duration={1000} />
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,24 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import LayoutView from "@/components/layout/view";
|
import LayoutView from "@/components/layout/view";
|
||||||
import { Text } from "react-native";
|
import { Text } from "react-native";
|
||||||
import EmptyState from "@/components/EmptyStatus";
|
import EmptyState from "@/components/EmptyStatus";
|
||||||
|
import { loadDataApi } from "@/request/api";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [dataList, setDataList] = useState<any>([]);
|
const [dataList, setDataList] = useState<any>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadDataApi({
|
||||||
|
view_name: "v_finance_record",
|
||||||
|
search_condition: "",
|
||||||
|
search_columns: [],
|
||||||
|
order_by: "",
|
||||||
|
args: [],
|
||||||
|
}).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutView>
|
<LayoutView>
|
||||||
<LayoutView.SectionView<any>
|
<LayoutView.SectionView<any>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { LoginQqParams, LoginResult, LoginWxParams } from "@/types/auth";
|
||||||
|
import alovaInstance from "./index";
|
||||||
|
|
||||||
|
export function loginQqApi(params: LoginQqParams) {
|
||||||
|
return alovaInstance.Post<ResponseData<LoginResult>>(`/auth/login/qq`, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loginWxApi(params: LoginWxParams) {
|
||||||
|
return alovaInstance.Post<ResponseData<LoginResult>>(`/auth/login/wechat`, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoadDataBySqlParams {
|
||||||
|
sql: string;
|
||||||
|
args: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadDataBySqlApi(params: LoadDataBySqlParams) {
|
||||||
|
return alovaInstance.Post<ResponseData<any[]>>(`/data/loadDataBySql`, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoadDataParams {
|
||||||
|
view_name: string;
|
||||||
|
search_condition: string;
|
||||||
|
order_by: string;
|
||||||
|
search_columns: string[];
|
||||||
|
args: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadDataApi(params: LoadDataParams) {
|
||||||
|
return alovaInstance.Post<ResponseData<any[]>>(`/data/loadData`, params);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import type { Method } from "alova";
|
||||||
|
import type { AxiosError } from "axios";
|
||||||
|
import { axiosRequestAdapter } from "@alova/adapter-axios";
|
||||||
|
import { createAlova } from "alova";
|
||||||
|
import { toast } from "sonner-native";
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
|
||||||
|
enum HttpCode {
|
||||||
|
Success = 1000,
|
||||||
|
Unauthorized = 1001,
|
||||||
|
Fail = 500,
|
||||||
|
BusinessError = 2000,
|
||||||
|
SystemError = 3000,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅保留「无需登录」接口
|
||||||
|
const WhiteList = ["/auth/login/**"];
|
||||||
|
|
||||||
|
function isWhiteListed(url: string) {
|
||||||
|
return WhiteList.some((pattern) => {
|
||||||
|
if (pattern.endsWith("/**")) {
|
||||||
|
const base = pattern.slice(0, -3);
|
||||||
|
return url.startsWith(base);
|
||||||
|
}
|
||||||
|
return url === pattern;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const alovaInstance = createAlova({
|
||||||
|
baseURL: process.env.EXPO_PUBLIC_API_URL,
|
||||||
|
requestAdapter: axiosRequestAdapter(),
|
||||||
|
timeout: 5000,
|
||||||
|
cacheFor: null,
|
||||||
|
|
||||||
|
responded: {
|
||||||
|
onSuccess: async (response, _method: Method) => {
|
||||||
|
const resData = response.data as ResponseData<any>;
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
...resData,
|
||||||
|
isSuccess: resData.code === HttpCode.Success,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resData.code === HttpCode.Unauthorized) {
|
||||||
|
toast.error("登录状态已过期,请重新登录");
|
||||||
|
useUserStore.getState().setAuthExpired(true);
|
||||||
|
return result;
|
||||||
|
} else if (resData.code === HttpCode.BusinessError) {
|
||||||
|
toast.error(resData.message || "业务异常,请稍后再试");
|
||||||
|
return result;
|
||||||
|
} else if (resData.code === HttpCode.SystemError) {
|
||||||
|
toast.error(resData.message || "系统异常,请稍后再试");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: (error: AxiosError) => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeRequest(method) {
|
||||||
|
// 白名单接口直接放行
|
||||||
|
if (isWhiteListed(method.url)) return;
|
||||||
|
|
||||||
|
const userStore = useUserStore.getState();
|
||||||
|
|
||||||
|
// 未登录直接拦截
|
||||||
|
if (!userStore.isLogged()) {
|
||||||
|
userStore.setAuthExpired(true);
|
||||||
|
return Promise.reject(new Error("未登录"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注入 JWT
|
||||||
|
method.config.headers.Token = userStore.userInfo!.token;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default alovaInstance;
|
||||||
@@ -4,9 +4,15 @@ import { createJSONStorage, persist } from "zustand/middleware";
|
|||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
userInfo: UserInfo;
|
userInfo: UserInfo;
|
||||||
|
|
||||||
isLogged: () => boolean;
|
isLogged: () => boolean;
|
||||||
|
|
||||||
setUserInfo: (userInfo: UserInfo) => void;
|
setUserInfo: (userInfo: UserInfo) => void;
|
||||||
|
|
||||||
clear: () => void;
|
clear: () => void;
|
||||||
|
|
||||||
|
authExpired: boolean;
|
||||||
|
setAuthExpired: (v: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultUserInfo: UserInfo = {
|
const defaultUserInfo: UserInfo = {
|
||||||
@@ -21,6 +27,8 @@ export const useUserStore = create<UserState>()(
|
|||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
userInfo: defaultUserInfo,
|
userInfo: defaultUserInfo,
|
||||||
|
|
||||||
|
authExpired: false,
|
||||||
|
|
||||||
isLogged: () => !!get().userInfo?.token,
|
isLogged: () => !!get().userInfo?.token,
|
||||||
|
|
||||||
setUserInfo: (userInfo) =>
|
setUserInfo: (userInfo) =>
|
||||||
@@ -33,10 +41,19 @@ export const useUserStore = create<UserState>()(
|
|||||||
userInfo: defaultUserInfo,
|
userInfo: defaultUserInfo,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setAuthExpired: (v: boolean) =>
|
||||||
|
set({
|
||||||
|
authExpired: v,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "user-storage",
|
name: "user-storage",
|
||||||
storage: createJSONStorage(() => AsyncStorage),
|
storage: createJSONStorage(() => AsyncStorage),
|
||||||
|
|
||||||
|
partialize: (state) => ({
|
||||||
|
userInfo: state.userInfo,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export interface LoginQqParams {
|
||||||
|
openid: string
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginWxParams {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoginResult {
|
||||||
|
token: string
|
||||||
|
userId: number
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
}
|
||||||
+8
@@ -10,6 +10,14 @@ declare global {
|
|||||||
SystemError = 3000,
|
SystemError = 3000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ResponseData<T> {
|
||||||
|
track_id: string;
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
data: T;
|
||||||
|
isSuccess: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
id: number;
|
id: number;
|
||||||
token: string;
|
token: string;
|
||||||
|
|||||||
Reference in new issue
Block a user