u
This commit is contained in:
1 parent
07e0001b87
commit
46cbb2f50d
543 files changed
+32576
-4231
No files matched your search
@@ -6,18 +6,24 @@ import (
|
||||
"allapp-go/internal/types"
|
||||
"allapp-go/pkg/db"
|
||||
"allapp-go/pkg/jwtx"
|
||||
"allapp-go/pkg/requestx"
|
||||
"allapp-go/pkg/s3store"
|
||||
"allapp-go/pkg/uniqueid"
|
||||
"allapp-go/pkg/wechat"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/log"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// LoginQq ======================== QQ 登录 ========================
|
||||
func LoginQq(c fiber.Ctx) error {
|
||||
var req types.LoginQqDTO
|
||||
vo := new(types.LoginVO)
|
||||
var req types.LoginQqReq
|
||||
vo := new(types.LoginResp)
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return errors.WithStack(err)
|
||||
@@ -28,8 +34,8 @@ func LoginQq(c fiber.Ctx) error {
|
||||
|
||||
// LoginWechat ======================== 微信登录 ========================
|
||||
func LoginWechat(c fiber.Ctx) error {
|
||||
var req types.LoginWechatDTO
|
||||
vo := new(types.LoginVO)
|
||||
var req types.LoginWechatReq
|
||||
vo := new(types.LoginResp)
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return errors.WithStack(err)
|
||||
@@ -37,13 +43,13 @@ func LoginWechat(c fiber.Ctx) error {
|
||||
|
||||
openid, token, err := wechat.GetWechatAccess(req.Code)
|
||||
if err != nil {
|
||||
log.Errorw("wechat access failed", "code", req.Code, "error", err)
|
||||
log.Errorw("微信获取Token失败", "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)
|
||||
log.Errorw("微信获取用户信息系失败", "openid", openid, "error", err)
|
||||
return httpx.Fail(c, "微信登录失败,请重试")
|
||||
}
|
||||
|
||||
@@ -57,7 +63,7 @@ func handleThirdLogin(
|
||||
nickname string,
|
||||
avatar string,
|
||||
loginType int16,
|
||||
vo *types.LoginVO,
|
||||
vo *types.LoginResp,
|
||||
) error {
|
||||
dbClient := db.New()
|
||||
|
||||
@@ -133,15 +139,58 @@ func registerUser(
|
||||
c fiber.Ctx,
|
||||
openid, nickname, avatar string,
|
||||
loginType int16,
|
||||
vo *types.LoginVO,
|
||||
vo *types.LoginResp,
|
||||
) error {
|
||||
userId := uniqueid.NextId()
|
||||
|
||||
defaultAvatar := "https://default-avatar-url.com/default.png"
|
||||
|
||||
if nickname == "" {
|
||||
nickname = fmt.Sprintf("用户_%d", userId)
|
||||
}
|
||||
|
||||
if avatar == "" {
|
||||
avatar = "https://default-avatar-url.com/default.png"
|
||||
avatar = defaultAvatar
|
||||
} else {
|
||||
// 下载头像(流式)
|
||||
stream, size, contentType, err := requestx.GetFileStream(c.Context(), avatar)
|
||||
if err != nil {
|
||||
avatar = defaultAvatar
|
||||
} else {
|
||||
defer func() {
|
||||
_ = stream.Close()
|
||||
}()
|
||||
|
||||
// ✅ 从 avatar URL 获取扩展名(简化版)
|
||||
u, _ := url.Parse(avatar)
|
||||
ext := filepath.Ext(u.Path)
|
||||
if ext == "" {
|
||||
ext = ".jpg" // 兜底
|
||||
}
|
||||
|
||||
// 年月日 + uuid
|
||||
now := time.Now()
|
||||
key := fmt.Sprintf("%d/%02d/%02d/%s%s",
|
||||
now.Year(),
|
||||
now.Month(),
|
||||
now.Day(),
|
||||
uuid.NewString(),
|
||||
ext,
|
||||
)
|
||||
|
||||
// 上传到 S3
|
||||
if err := s3store.UploadToRustFS(
|
||||
c.Context(),
|
||||
stream,
|
||||
key,
|
||||
size,
|
||||
contentType,
|
||||
); err != nil {
|
||||
avatar = defaultAvatar
|
||||
} else {
|
||||
avatar = key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := dbClient.WithTx(c.Context(), func(tx *db.Client) error {
|
||||
|
||||
@@ -3,32 +3,18 @@ package handle
|
||||
import (
|
||||
"allapp-go/internal/errors"
|
||||
"allapp-go/internal/httpx"
|
||||
"allapp-go/internal/types"
|
||||
"allapp-go/pkg/logger"
|
||||
"allapp-go/pkg/s3store"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func S3ListBuckets(c fiber.Ctx) error {
|
||||
result, err := s3store.S3Client.ListBuckets(c.Context(), &s3.ListBucketsInput{})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
buckets := make([]string, 0, len(result.Buckets))
|
||||
for _, b := range result.Buckets {
|
||||
if b.Name != nil {
|
||||
buckets = append(buckets, *b.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return httpx.OK(c, buckets)
|
||||
}
|
||||
|
||||
func S3UploadFile(c fiber.Ctx) error {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
@@ -64,8 +50,8 @@ func S3UploadFile(c fiber.Ctx) error {
|
||||
|
||||
err = s3store.UploadToRustFS(
|
||||
c.Context(),
|
||||
key,
|
||||
file,
|
||||
key,
|
||||
fileHeader.Size,
|
||||
contentType,
|
||||
)
|
||||
@@ -81,3 +67,23 @@ func S3UploadFile(c fiber.Ctx) error {
|
||||
|
||||
return httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func S3DeleteFile(c fiber.Ctx) error {
|
||||
var req types.S3DeleteReq
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
log := logger.FromCtx(c.Context())
|
||||
|
||||
if err := s3store.DeleteBatch(c.Context(), req.Keys); err != nil {
|
||||
log.Warn("S3 批量删除失败",
|
||||
zap.Int("count", len(req.Keys)),
|
||||
zap.Error(err),
|
||||
)
|
||||
// ❗仍然返回成功(符合你的要求)
|
||||
}
|
||||
|
||||
return httpx.OK(c, nil)
|
||||
}
|
||||
@@ -20,8 +20,8 @@ func SetupRouter(app *fiber.App, cfg *config.Config) {
|
||||
data.Post("/getUniqueId", handle.GetUniqueId)
|
||||
|
||||
s3 := api.Group("/s3", middleware.Auth())
|
||||
s3.Post("/listBuckets", handle.S3ListBuckets)
|
||||
s3.Post("/upload", handle.S3UploadFile)
|
||||
s3.Post("/delete", handle.S3DeleteFile)
|
||||
|
||||
// ==================== auth ====================
|
||||
api.Post("/auth/login/qq", handle.LoginQq)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package types
|
||||
|
||||
type LoginQqDTO struct {
|
||||
type LoginQqReq struct {
|
||||
Openid string `json:"openid" validate:"required"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
type LoginWechatDTO struct {
|
||||
type LoginWechatReq struct {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginVO struct {
|
||||
type LoginResp struct {
|
||||
Token string `json:"token"`
|
||||
UserId int64 `json:"userId"`
|
||||
Nickname string `json:"nickname"`
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package types
|
||||
|
||||
type S3DeleteReq struct {
|
||||
Keys []string `json:"keys" validate:"required"`
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package requestx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/client"
|
||||
@@ -97,3 +100,31 @@ func PostForm(url string, formData map[string]string, cfg client.Config) (string
|
||||
}
|
||||
return string(resp.Body()), resp.StatusCode(), nil
|
||||
}
|
||||
|
||||
func GetFileStream(ctx context.Context, url string) (io.ReadCloser, int64, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
err := resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
return nil, 0, "", fmt.Errorf("request failed: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return resp.Body, resp.ContentLength, resp.Header.Get("Content-Type"), nil
|
||||
}
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"allapp-go/internal/config"
|
||||
"context"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
)
|
||||
|
||||
var S3Client *s3.Client
|
||||
@@ -39,7 +40,8 @@ func NewS3Client(cfg *config.Config) {
|
||||
fmt.Println("S3 client and Presign client initialized successfully!")
|
||||
}
|
||||
|
||||
func UploadToRustFS(ctx context.Context, key string, file multipart.File, size int64, contentType string) error {
|
||||
// UploadToRustFS 上传
|
||||
func UploadToRustFS(ctx context.Context, file io.Reader, key string, size int64, contentType string) error {
|
||||
_, err := S3Client.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
@@ -47,6 +49,47 @@ func UploadToRustFS(ctx context.Context, key string, file multipart.File, size i
|
||||
ContentLength: aws.Int64(size),
|
||||
ContentType: aws.String(contentType),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteFromRustFS 删除
|
||||
func DeleteFromRustFS(ctx context.Context, key string) error {
|
||||
_, err := S3Client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteBatch(ctx context.Context, keys []string) error {
|
||||
const maxBatch = 1000
|
||||
|
||||
for i := 0; i < len(keys); i += maxBatch {
|
||||
end := i + maxBatch
|
||||
if end > len(keys) {
|
||||
end = len(keys)
|
||||
}
|
||||
|
||||
var objects []types.ObjectIdentifier
|
||||
for _, k := range keys[i:end] {
|
||||
objects = append(objects, types.ObjectIdentifier{
|
||||
Key: aws.String(k),
|
||||
})
|
||||
}
|
||||
|
||||
_, err := S3Client.DeleteObjects(ctx, &s3.DeleteObjectsInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Delete: &types.Delete{
|
||||
Objects: objects,
|
||||
Quiet: aws.Bool(false), // 返回删除结果(建议保留)
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err // 整批失败才返回
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user