This commit is contained in:
oneao committed 2026-02-26 22:19:26 +08:00
1 parent 8a3dea8545
commit 1903861354
21 files changed
+335 -236

No files matched your search

@@ -12,11 +12,11 @@ import (
"allapp/utils/requestx" "allapp/utils/requestx"
"allapp/utils/responsex" "allapp/utils/responsex"
"allapp/utils/uniqueid" "allapp/utils/uniqueid"
"errors"
"fmt" "fmt"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/client" "github.com/gofiber/fiber/v3/client"
"github.com/gofiber/fiber/v3/log"
) )
// LoginQq qq登录 // LoginQq qq登录
@@ -48,6 +48,11 @@ func LoginWechat(c fiber.Ctx) error {
openid, accessToken, err := getWechatAccess(dto.Code) openid, accessToken, err := getWechatAccess(dto.Code)
if err != nil { if err != nil {
log.Errorw("调用微信 access 接口失败",
"code", dto.Code, // 不打印原始 code
"error", err.Error(),
)
fmt.Println(err.Error())
return responsex.Error(c, "微信登录失败,请重试") return responsex.Error(c, "微信登录失败,请重试")
} }
@@ -68,6 +73,10 @@ func LoginWechat(c fiber.Ctx) error {
// 新用户 → 拉用户信息 // 新用户 → 拉用户信息
nickname, avatar, err := getWechatUserInfo(accessToken, openid) nickname, avatar, err := getWechatUserInfo(accessToken, openid)
if err != nil { if err != nil {
log.Errorw("调用微信用户信息接口失败",
"openid", openid,
"error", err,
)
return responsex.Error(c, "微信登录失败,请重试") return responsex.Error(c, "微信登录失败,请重试")
} }
@@ -104,7 +113,7 @@ func getWechatAccess(code string) (openid string, accessToken string, err error)
openid, _ = accessMap["openid"].(string) openid, _ = accessMap["openid"].(string)
if accessToken == "" || openid == "" { if accessToken == "" || openid == "" {
return "", "", errors.New("获取微信登录凭证失败") return "", "", fmt.Errorf("获取微信登录凭证失败: %s", bodyData)
} }
return return
@@ -20,7 +20,7 @@ func Logger() fiber.Handler {
duration := time.Since(start) duration := time.Since(start)
// 记录日志 // 记录日志
log.Infow("API Request", log.WithContext(c.Context()).Infow("API Request",
"method", c.Method(), "method", c.Method(),
"path", c.Path(), "path", c.Path(),
"status", c.Response().StatusCode(), "status", c.Response().StatusCode(),
@@ -17,46 +17,60 @@ const CtxSpaceIDKey = "space_id"
func SpaceGuard() fiber.Handler { func SpaceGuard() fiber.Handler {
return func(c fiber.Ctx) error { return func(c fiber.Ctx) error {
userID, err := GetUserID(c) userID, err := GetUserID(c)
if err != nil { if err != nil {
return err return err
} }
spaceIDStr := strings.TrimSpace(c.Get("SPACE_ID")) spaceIDStr := strings.TrimSpace(c.Get("SPACE_ID"))
spaceID, err := strconv.ParseInt(spaceIDStr, 10, 64) spaceID, err := strconv.ParseInt(spaceIDStr, 10, 64)
if err != nil || spaceID <= 0 { if err != nil || spaceID <= 0 {
return responsex.SpaceError(c, "请选择空间") return responsex.SpaceError(c, "请选择空间")
} }
// 校验 // 1 先查 Redis
inSpace, err := redisx.SpaceIsMember(spaceID, userID) inSpace, err := redisx.SpaceIsMember(spaceID, userID)
if err != nil {
return err
}
if inSpace { if inSpace {
c.Locals(CtxSpaceIDKey, spaceID)
return c.Next() return c.Next()
} }
// 检验空间是否存在 // 2 Redis miss → 查数据库
space, err := db.Queries.GetSpaceById(c.Context(), spaceID) role, err := db.Queries.GetUserSpaceRole(c.Context(), repo.GetUserSpaceRoleParams{
SpaceID: spaceID,
UserID: userID,
})
if err != nil { if err != nil {
if errorx.IsNotFound(err) { if errorx.IsNotFound(err) {
// 未找到 return responsex.SpaceError(c, "未加入空间")
return responsex.SpaceError(c, "空间已失效")
} }
return err return err
} }
// 空间有效,判断是否在该空间内 // 3 写回 Redis(重建缓存)
role, err := db.Queries.GetUserSpaceRole(c.Context(), repo.GetUserSpaceRoleParams{ _ = redisx.SpaceAddOrUpdateMember(spaceID, userID, int(role))
SpaceID: spaceID, c.Locals(CtxSpaceIDKey, spaceID)
UserID: spaceID,
})
if err != nil {
if errorx.IsNotFound(err) {
}
}
// 4 放行
return c.Next()
} }
} }
func GetSpaceID(c fiber.Ctx) (int64, error) {
v := c.Locals(CtxSpaceIDKey)
if v == nil {
return 0, responsex.SpaceError(c, "空间无效")
}
spaceID, ok := v.(int64)
if !ok {
return 0, responsex.SpaceError(c, "空间无效")
}
return spaceID, nil
}
+13 -4
View File
@@ -10,13 +10,22 @@ import (
func SetupRouters(app *fiber.App) { func SetupRouters(app *fiber.App) {
api := app.Group(config.GetConf().Server.BaseUrl) api := app.Group(config.GetConf().Server.BaseUrl)
public := api.Group("") // 1️⃣ 公开接口(无需登录)
//private := api.Group("", middleware.Auth()) publicGroup := api.Group("")
// 2️⃣ 需要登录
//authGroup := api.Group("", middleware.Auth())
//
//// 3️⃣ 需要登录 + 需要空间
//spaceGroup := api.Group("", middleware.Auth(), middleware.SpaceGuard())
// --------------------
// auth // auth
authRouter := public.Group("/auth") authRouter := publicGroup.Group("/auth")
authRouter.Post("/login/qq", auth.LoginQq) authRouter.Post("/login/qq", auth.LoginQq)
authRouter.Post("/login/wechat", auth.LoginWechat) authRouter.Post("/login/wechat", auth.LoginWechat)
// 其他的都带中间件 // 示例
// authGroup.Get("/user/profile", user.GetProfile)
// spaceGroup.Get("/space/info", space.GetInfo)
} }
+1 -1
View File
@@ -28,4 +28,4 @@ INSERT INTO "user" (
-- name: UpdateUserLastLoginTime :exec -- name: UpdateUserLastLoginTime :exec
UPDATE "user" UPDATE "user"
SET last_login_time = NOW() SET last_login_time = NOW()
WHERE id = $1; WHERE id = $1;
+5 -5
View File
@@ -3,9 +3,13 @@ module allapp
go 1.25.0 go 1.25.0
require ( require (
github.com/go-playground/validator/v10 v10.30.1
github.com/gofiber/contrib/v3/zap v1.0.0 github.com/gofiber/contrib/v3/zap v1.0.0
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.8.0
github.com/redis/go-redis/v9 v9.18.0
github.com/shopspring/decimal v1.4.0
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
@@ -18,17 +22,15 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // 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/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // 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
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gofiber/schema v1.7.0 // indirect github.com/gofiber/schema v1.7.0 // indirect
github.com/gofiber/utils/v2 v2.0.2 // indirect github.com/gofiber/utils/v2 v2.0.2 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.8.0 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/compress v1.18.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
@@ -36,9 +38,7 @@ 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/redis/go-redis/v9 v9.18.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/shopspring/decimal v1.4.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
github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cast v1.10.0 // indirect
+2
View File
@@ -15,6 +15,8 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -11,6 +11,10 @@ log:
max_age: 7 max_age: 7
compress: true compress: true
wechat:
app_id: wxdab3e21a1f7e392f
app_secret: 0895443c6fedfc21cb92c9cd60de2a26
jwt: jwt:
secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s secret: 3Bde3BGEbYqtqyEUzW3ry8jKFcaPH17fRmTmqE7MDr05Lwj95uruRKrrkb44TJ4s
access_expiry: -1 access_expiry: -1
@@ -68,22 +68,6 @@ func SpaceIsMember(spaceID int64, userID int64) (bool, error) {
return ok, err return ok, err
} }
// SpaceInitMembers 初始化用户成员
func SpaceInitMembers(spaceID int64, members map[int64]int) error {
key := spaceMemberKey(spaceID)
data := make(map[string]interface{})
for userID, role := range members {
data[strconv.FormatInt(userID, 10)] = role
}
return Client.HSet(
context.Background(),
key,
data,
).Err()
}
// SpaceDelete 删除空间 // SpaceDelete 删除空间
func SpaceDelete(spaceID int64) error { func SpaceDelete(spaceID int64) error {
key := spaceMemberKey(spaceID) key := spaceMemberKey(spaceID)
@@ -7,7 +7,7 @@ type Result struct {
Code int `json:"code"` Code int `json:"code"`
Message string `json:"message"` Message string `json:"message"`
Data interface{} `json:"data"` Data interface{} `json:"data"`
TrackId string `json:"trackId,omitempty"` TrackId string `json:"track_id,omitempty"`
} }
// 业务码 // 业务码
+19 -3
View File
@@ -21,15 +21,31 @@ export default function Index() {
</Text> </Text>
<Text className="text-base text-gray-500 text-center mb-10"> <Text className="text-base text-gray-500 text-center mb-10">
记录生活,记录情绪,也记录每一笔重要的支出 记录生活,记录情绪
</Text> </Text>
<AButton {/* <AButton
className="w-full" className="w-full"
onPress={() => router.replace('/money')} onPress={() => router.replace('/money')}
> >
开始使用 开始使用
</AButton> </AButton> */}
<View className="flex flex-row gap-3 mt-4">
<AButton
className="flex-1"
onPress={() => router.replace('/money')}
>
创建空间
</AButton>
<AButton
className="flex-1"
variant="outline"
onPress={() => router.replace('/money')}
>
加入空间
</AButton>
</View>
</View> </View>
</SafeAreaView> </SafeAreaView>
) )
@@ -10,7 +10,7 @@ import ListItem from './components/ListItem'
export default function User() { export default function User() {
const userStore = useUserStore() const userStore = useUserStore()
console.log(userStore.userInfo)
return ( return (
<SafeAreaView className="flex-1 p-4"> <SafeAreaView className="flex-1 p-4">
<View className="flex flex-row items-center"> <View className="flex flex-row items-center">
@@ -20,7 +20,7 @@ export default function User() {
source={{ uri: userStore.userInfo?.avatar }} source={{ uri: userStore.userInfo?.avatar }}
/> />
</Avatar> </Avatar>
<Text className="text-xl font-semibold ml-3">{userStore.userInfo?.username}</Text> <Text className="text-xl font-semibold ml-3">{userStore.userInfo?.nickname}</Text>
<LucideIcon icon="ChevronRight" size={26} className="ml-auto" color="#aaa" strokeWidth={1.5} /> <LucideIcon icon="ChevronRight" size={26} className="ml-auto" color="#aaa" strokeWidth={1.5} />
</View> </View>
+170 -172
View File
@@ -1,7 +1,7 @@
import { useEvent } from 'expo' import { useEvent } from 'expo'
// import ExpoQQ from 'expo-qq' import ExpoQQ from 'expo-qq'
import { useRouter } from 'expo-router' import { useRouter } from 'expo-router'
// import ExpoWeChat from 'expo-wechat' import ExpoWeChat from 'expo-wechat'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Image, Text, View } from 'react-native' import { Image, Text, View } from 'react-native'
import { AButton, ACheckbox } from '@/components/common/ao' import { AButton, ACheckbox } from '@/components/common/ao'
@@ -25,176 +25,174 @@ export default function Auth() {
const userStore = useUserStore() const userStore = useUserStore()
const appStore = useAppStore() const appStore = useAppStore()
// const onLoginFinished = useEvent(ExpoQQ, 'onLoginFinished') const onLoginFinished = useEvent(ExpoQQ, 'onLoginFinished')
// const onGetUserInfo = useEvent(ExpoQQ, 'onGetUserInfo') const onGetUserInfo = useEvent(ExpoQQ, 'onGetUserInfo')
// const authResult = useEvent(ExpoWeChat, 'onAuthResult') const authResult = useEvent(ExpoWeChat, 'onAuthResult')
// async function onQQLogin() { async function onQQLogin() {
// setLoading(true) setLoading(true)
// setForm(val => ({ setForm(val => ({
// ...val, ...val,
// type: 1, type: 1,
// })) }))
// await ExpoQQ.login(['get_user_info']) await ExpoQQ.login(['get_user_info'])
// }
// async function onWeChatLogin() {
// setLoading(true)
// // 检查微信是否安装
// const isInstalled = await ExpoWeChat.isWXAppInstalled()
// if (!isInstalled) {
// toast.show({
// title: onLoginFinished?.errorMessage || '请先安装微信',
// })
// setLoading(false)
// return
// }
// setForm(val => ({
// ...val,
// type: 0,
// }))
// ExpoWeChat.sendAuthRequest('snsapi_userinfo', 'state123')
// }
// useEffect(() => {
// ExpoQQ.init('102826474', '')
// ExpoWeChat.registerApp('wxdab3e21a1f7e392f', '')
// }, [])
// useEffect(() => {
// if (!onLoginFinished) {
// return
// }
// if (!onLoginFinished.success) {
// toast.show({
// title: onLoginFinished?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
// })
// setLoading(false)
// return
// }
// const fetchUserInfo = async () => {
// try {
// const tokenInfo = await ExpoQQ.getLoginTokenInfo()
// if (!tokenInfo.openId) {
// toast.show({
// title: tokenInfo?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
// })
// setLoading(false)
// return
// }
// setForm(val => ({
// ...val,
// openid: String(tokenInfo.openId),
// }))
// await ExpoQQ.sendGetUserInfoReq()
// }
// catch (e) {
// console.error('QQ 登录未完成', e)
// setLoading(false)
// }
// }
// fetchUserInfo()
// }, [onLoginFinished])
// // QQ登录
// useEffect(() => {
// const handleLogin = async () => {
// if (!onGetUserInfo)
// return
// try {
// const loginRes = await loginQqApi({
// openid: form.openid,
// username: onGetUserInfo.nickname || '',
// avatar: onGetUserInfo.figureurlQq_2 || onGetUserInfo.figureurl_2,
// })
// if (loginRes.isSuccess) {
// userStore.setUserInfo({
// token: loginRes.data.token,
// id: loginRes.data.userId,
// username: loginRes.data.username,
// avatar: loginRes.data.avatar,
// })
// console.log(loginRes.data.token)
// router.push('/')
// }
// }
// catch (err) {
// console.error('登录接口出错', err)
// }
// finally {
// setLoading(false)
// }
// }
// handleLogin()
// }, [onGetUserInfo])
// // 微信登录
// useEffect(() => {
// if (!authResult)
// return
// const handleLogin = async () => {
// if (authResult.errorCode === 0) {
// try {
// const loginRes = await loginWxApi({
// code: authResult.code,
// })
// if (loginRes.isSuccess) {
// userStore.setUserInfo({
// token: loginRes.data.token,
// id: loginRes.data.userId,
// username: loginRes.data.username,
// avatar: loginRes.data.avatar,
// })
// router.push('/')
// }
// }
// catch (err) {
// console.error('登录接口出错', err)
// }
// finally {
// setLoading(false)
// }
// }
// else {
// console.log('授权失败:', authResult.errorMessage)
// setLoading(false)
// }
// }
// handleLogin()
// }, [authResult])
function testLogin() {
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NjY3ODU5ODIxNjUwNjEsImlhdCI6MTc2OTM0ODI0Nn0.17ZNaHD3HSLM8dVn_JQK-gvsG9MKj47IRYV9D4xZkzs
userStore.setUserInfo({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3Njc4NjM3NTU5MzE3MTcsImlhdCI6MTc2OTYwMzEzOX0.xfW-uyFBi799u4VJ-93A88c2CC8jwpYOPsALUuaIizg',
id: 767863755931717,
username: 'Zhang',
avatar: 'http://thirdqq.qlogo.cn/ek_qqapp/AQHVialeUibE42oV8RvTTVZ4tyRlPYeJrUibWTwtCPfV68E4rSzn9cw00GkXQ3QDTiawwhjoqQb7/100',
})
appStore.init()
router.push('/')
} }
async function onWeChatLogin() {
setLoading(true)
// 检查微信是否安装
const isInstalled = await ExpoWeChat.isWXAppInstalled()
if (!isInstalled) {
toast.show({
title: onLoginFinished?.errorMessage || '请先安装微信',
})
setLoading(false)
return
}
setForm(val => ({
...val,
type: 0,
}))
ExpoWeChat.sendAuthRequest('snsapi_userinfo', 'state123')
}
useEffect(() => {
ExpoQQ.init('102826474', '')
ExpoWeChat.registerApp('wxdab3e21a1f7e392f', '')
}, [])
useEffect(() => {
if (!onLoginFinished) {
return
}
if (!onLoginFinished.success) {
toast.show({
title: onLoginFinished?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
})
setLoading(false)
return
}
const fetchUserInfo = async () => {
try {
const tokenInfo = await ExpoQQ.getLoginTokenInfo()
if (!tokenInfo.openId) {
toast.show({
title: tokenInfo?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
})
setLoading(false)
return
}
setForm(val => ({
...val,
openid: String(tokenInfo.openId),
}))
await ExpoQQ.sendGetUserInfoReq()
}
catch (e) {
console.error('QQ 登录未完成', e)
setLoading(false)
}
}
fetchUserInfo()
}, [onLoginFinished])
// QQ登录
useEffect(() => {
const handleLogin = async () => {
if (!onGetUserInfo)
return
try {
const loginRes = await loginQqApi({
openid: form.openid,
nickname: onGetUserInfo.nickname || '',
avatar: onGetUserInfo.figureurlQq_2 || onGetUserInfo.figureurl_2,
})
if (loginRes.isSuccess) {
userStore.setUserInfo({
token: loginRes.data.token,
id: loginRes.data.userId,
nickname: loginRes.data.nickname,
avatar: loginRes.data.avatar,
})
router.push('/auth/space')
}
}
catch (err) {
console.error('登录接口出错', err)
}
finally {
setLoading(false)
}
}
handleLogin()
}, [onGetUserInfo])
// 微信登录
useEffect(() => {
if (!authResult)
return
const handleLogin = async () => {
if (authResult.errorCode === 0) {
try {
const loginRes = await loginWxApi({
code: authResult.code,
})
if (loginRes.isSuccess) {
userStore.setUserInfo({
token: loginRes.data.token,
id: loginRes.data.userId,
nickname: loginRes.data.nickname,
avatar: loginRes.data.avatar,
})
router.push('/')
}
}
catch (err) {
console.error('登录接口出错', err)
}
finally {
setLoading(false)
}
}
else {
console.log('授权失败:', authResult.errorMessage)
setLoading(false)
}
}
handleLogin()
}, [authResult])
// function testLogin() {
// // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NjY3ODU5ODIxNjUwNjEsImlhdCI6MTc2OTM0ODI0Nn0.17ZNaHD3HSLM8dVn_JQK-gvsG9MKj47IRYV9D4xZkzs
// userStore.setUserInfo({
// token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NzgxMTUwMzIxMDA5MzMsImlhdCI6MTc3MjExMDQ5N30.Ab7_YNwftJigktnPt8f-FwwcLeS45CkrH0jT2jLjRtA',
// id: 778115032100933,
// nickname: 'Zhang',
// avatar: 'http://thirdqq.qlogo.cn/ek_qqapp/AQHVialeUibE42oV8RvTTVZ4tyRlPYeJrUibWTwtCPfV68E4rSzn9cw00GkXQ3QDTiawwhjoqQb7/100',
// })
// appStore.init()
// router.push('/')
// }
return ( return (
<View className="flex-1 bg-white px-6 justify-center"> <View className="flex-1 bg-white px-6 justify-center">
<CustomHeader <CustomHeader
@@ -212,7 +210,7 @@ export default function Auth() {
</View> </View>
<View className="flex flex-col gap-4"> <View className="flex flex-col gap-4">
{/* <AButton <AButton
leftIcon={ leftIcon={
<IconWeixin color="#fff" /> <IconWeixin color="#fff" />
} }
@@ -231,7 +229,7 @@ export default function Auth() {
onPress={onQQLogin} onPress={onQQLogin}
> >
QQ QQ
</AButton> */} </AButton>
<AButton <AButton
variant="outline" variant="outline"
@@ -239,7 +237,7 @@ export default function Auth() {
<IconQq /> <IconQq />
} }
loading={loading} loading={loading}
onPress={testLogin} // onPress={testLogin}
> >
模拟登录 模拟登录
</AButton> </AButton>
@@ -0,0 +1,61 @@
import { Text, View } from 'react-native'
import { AButton, LucideIcon } from '@/components/common/ao'
import EmptyState from '@/components/common/EmptyStatus'
import CustomHeader from '@/components/custom/Header'
// name renshu
export default function AuthSpace() {
return (
<View className="flex-1">
<CustomHeader title="空间列表" />
<View className="flex-1 px-4 pt-2 pb-8">
<View>
<Text className="text-lg font-semibold">你的空间</Text>
<Text className="text-sm text-gray-600">选择要进入的空间</Text>
</View>
<View className="flex-1 my-4">
{/* <View className="items-center justify-center">
<EmptyState title="暂无空间" description="请选择 创建 或 加入 空间" />
</View> */}
<View>
<View className="rounded-lg p-3 flex flex-row items-center justify-between bg-gray-100">
<View className="flex-row items-center">
<View className="mr-4">
<LucideIcon
icon="Grip"
size={20}
color="#666"
/>
</View>
<View className="">
<View>
<Text className="font-semibold">A 的空间</Text>
</View>
<Text className="text-sm text-gray-500 mt-1">张三 · 8 名成员</Text>
</View>
</View>
<View>
<LucideIcon
icon="Bolt"
size={20}
color="#aaa"
style={{ padding: 6 }}
/>
</View>
</View>
</View>
</View>
<View className="flex gap-3">
<AButton>创建新空间</AButton>
<AButton variant="outline">加入其他空间</AButton>
</View>
</View>
</View>
)
}
@@ -39,7 +39,7 @@ export default function EmptyState({
{image ?? defaultImages[type]} {image ?? defaultImages[type]}
{title && ( {title && (
<Text className="text-2xl font-semibold text-gray-900 mb-2">{title}</Text> <Text className="text-2xl font-semibold text-gray-900 mb-2 text-center ">{title}</Text>
)} )}
{description && ( {description && (
@@ -12,6 +12,7 @@ export interface LucideIconProps {
style?: ViewStyle style?: ViewStyle
clickable?: boolean clickable?: boolean
className?: string className?: string
fill?: string
} }
export default function LucideIcon({ export default function LucideIcon({
@@ -22,6 +23,7 @@ export default function LucideIcon({
onPress, onPress,
style, style,
clickable = true, clickable = true,
fill = 'none',
className, className,
}: LucideIconProps) { }: LucideIconProps) {
const IconComponent = icons[icon] const IconComponent = icons[icon]
@@ -30,12 +32,12 @@ export default function LucideIcon({
return null return null
if (!clickable) { if (!clickable) {
return <IconComponent size={size} color={color} strokeWidth={strokeWidth} style={style} className={className} /> return <IconComponent size={size} color={color} strokeWidth={strokeWidth} style={style} className={className} fill={fill} />
} }
return ( return (
<Pressable onPress={onPress} style={style} className={className}> <Pressable onPress={onPress} style={style} className={className}>
<IconComponent size={size} color={color} strokeWidth={strokeWidth} /> <IconComponent size={size} color={color} strokeWidth={strokeWidth} fill={fill} />
</Pressable> </Pressable>
) )
} }
@@ -8,5 +8,5 @@ export function loginQqApi(params: LoginQqParams) {
} }
export function loginWxApi(params: LoginWxParams) { export function loginWxApi(params: LoginWxParams) {
return alovaInstance.Post<ResponseData<LoginResult>>(`${API_PREFIX}/login/wx`, params) return alovaInstance.Post<ResponseData<LoginResult>>(`${API_PREFIX}/login/wechat`, params)
} }
+1 -1
View File
@@ -7,7 +7,7 @@ import { useUserStore } from '@/store/user'
import { toast } from '@/utils/toast' import { toast } from '@/utils/toast'
enum HttpCode { enum HttpCode {
Success = 200, Success = 1000,
Unauthorized = 401, Unauthorized = 401,
Fail = 500, Fail = 500,
} }
@@ -23,6 +23,7 @@ const DEFAULT_NAV_MENUS: NavItem[] = [
{ label: '恋爱记', path: '/love', icon: 'Heart' }, { label: '恋爱记', path: '/love', icon: 'Heart' },
{ label: '聊天', path: '/chat', icon: 'MessageCircle' }, { label: '聊天', path: '/chat', icon: 'MessageCircle' },
{ label: '个人中心', path: '/user', icon: 'CircleUserRound' }, { label: '个人中心', path: '/user', icon: 'CircleUserRound' },
{ label: '我的空间', path: '/auth/space', icon: 'Earth' },
] ]
export const useNavStore = create<NavState>()( export const useNavStore = create<NavState>()(
@@ -43,7 +44,7 @@ export const useNavStore = create<NavState>()(
}), }),
{ {
name: 'nav-storage', name: 'nav-storage',
storage: createJSONStorage(() => AsyncStorage), // storage: createJSONStorage(() => AsyncStorage),
}, },
), ),
) )
+2 -2
View File
@@ -1,6 +1,6 @@
export interface LoginQqParams { export interface LoginQqParams {
openid: string openid: string
username: string nickname: string
avatar: string avatar: string
} }
@@ -11,6 +11,6 @@ export interface LoginWxParams {
export interface LoginResult { export interface LoginResult {
token: string token: string
userId: number userId: number
username: string nickname: string
avatar: string avatar: string
} }
+2 -3
View File
@@ -2,7 +2,7 @@ export {}
declare global { declare global {
interface ResponseData<T> { interface ResponseData<T> {
trackId: string track_id: string
code: number code: number
message: string message: string
data: T data: T
@@ -12,14 +12,13 @@ declare global {
interface UserInfo { interface UserInfo {
id: number id: number
token: string token: string
username?: string nickname?: string
avatar?: string avatar?: string
} }
interface UserSpace { interface UserSpace {
id: number id: number
name: string name: string
invite_code: string
role: number role: number
} }
} }