重构代码结构与认证体系,集成异步任务消费者
This commit is contained in:
@@ -1,201 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"log/slog"
|
||||
"platform/pkg/u"
|
||||
auth2 "platform/web/auth"
|
||||
"platform/web/core"
|
||||
client2 "platform/web/domains/client"
|
||||
user2 "platform/web/domains/user"
|
||||
"platform/web/globals/orm"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var Auth = &authService{}
|
||||
|
||||
type authService struct{}
|
||||
|
||||
// OauthAuthorizationCode 验证授权码
|
||||
func (s *authService) OauthAuthorizationCode(ctx context.Context, client *m.Client, code, redirectURI, codeVerifier string) (*auth2.TokenDetails, error) {
|
||||
return nil, errors.New("TODO")
|
||||
}
|
||||
|
||||
// OauthClientCredentials 验证客户端凭证
|
||||
func (s *authService) OauthClientCredentials(ctx context.Context, client *m.Client, scope ...string) (*auth2.TokenDetails, error) {
|
||||
|
||||
var clientType = auth2.PayloadTypeFromClientSpec(client2.Spec(client.Spec))
|
||||
|
||||
var permissions = make(map[string]struct{}, len(scope))
|
||||
for _, item := range scope {
|
||||
permissions[item] = struct{}{}
|
||||
}
|
||||
|
||||
// 保存会话并返回令牌
|
||||
authCtx := auth2.Context{
|
||||
Permissions: permissions,
|
||||
Payload: auth2.Payload{
|
||||
Id: client.ID,
|
||||
Type: clientType,
|
||||
Name: client.Name,
|
||||
},
|
||||
}
|
||||
|
||||
token, err := auth2.CreateSession(ctx, &authCtx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// OauthRefreshToken 验证刷新令牌
|
||||
func (s *authService) OauthRefreshToken(ctx context.Context, _ *m.Client, refreshToken string, scope ...[]string) (*auth2.TokenDetails, error) {
|
||||
details, err := auth2.RefreshSession(ctx, refreshToken, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return details, nil
|
||||
}
|
||||
|
||||
// OauthPassword 验证密码
|
||||
func (s *authService) OauthPassword(ctx context.Context, _ *m.Client, data *GrantPasswordData, ip, agent string) (*auth2.TokenDetails, error) {
|
||||
var user *m.User
|
||||
err := q.Q.Transaction(func(tx *q.Query) error {
|
||||
|
||||
switch data.LoginType {
|
||||
case auth2.GrantPasswordPhone:
|
||||
// 验证验证码
|
||||
err := Verifier.VerifySms(ctx, data.Username, data.Password)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrVerifierServiceInvalid) {
|
||||
return ErrOauthInvalidRequest
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
user, err =
|
||||
tx.User.Where(tx.User.Phone.Eq(data.Username)).Take()
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
case auth2.GrantPasswordEmail:
|
||||
return core.NewServErr("邮箱登录暂不可用")
|
||||
case auth2.GrantPasswordSecret:
|
||||
var err error
|
||||
user, err = tx.User.
|
||||
Where(tx.User.Phone.Eq(data.Username)).
|
||||
Or(tx.User.Email.Eq(data.Username)).
|
||||
Or(tx.User.Username.Eq(data.Username)).
|
||||
Take()
|
||||
if err != nil {
|
||||
slog.Debug("查找用户失败", "error", err)
|
||||
return core.NewBizErr("用户不存在或密码错误")
|
||||
}
|
||||
|
||||
// 账户状态
|
||||
if user2.Status(user.Status) == user2.StatusDisabled {
|
||||
slog.Debug("账户状态异常", "username", data.Username, "status", user.Status)
|
||||
return core.NewBizErr("用户不存在或密码错误")
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if user.Password == nil || *user.Password == "" {
|
||||
slog.Debug("用户未设置密码", "username", data.Username)
|
||||
return core.NewBizErr("用户不存在或密码错误")
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(data.Password)) != nil {
|
||||
slog.Debug("密码验证失败", "username", data.Username)
|
||||
return core.NewBizErr("用户不存在或密码错误")
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrOauthInvalidRequest
|
||||
}
|
||||
|
||||
// 如果用户不存在,初始化用户 todo 初始化默认权限信息
|
||||
if user == nil {
|
||||
user = &m.User{
|
||||
Phone: data.Username,
|
||||
Username: u.P(data.Username),
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户的登录时间
|
||||
user.LastLogin = u.P(orm.LocalDateTime(time.Now()))
|
||||
user.LastLoginHost = u.P(ip)
|
||||
user.LastLoginAgent = u.P(agent)
|
||||
if err := tx.User.Save(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 保存到会话
|
||||
var name = ""
|
||||
if user.Name != nil {
|
||||
name = *user.Name
|
||||
}
|
||||
authCtx := auth2.Context{
|
||||
Payload: auth2.Payload{
|
||||
Id: user.ID,
|
||||
Type: auth2.PayloadUser,
|
||||
Name: name,
|
||||
Avatar: user.Avatar,
|
||||
},
|
||||
}
|
||||
|
||||
token, err := auth2.CreateSession(ctx, &authCtx, data.Remember)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
type GrantCodeData struct {
|
||||
Code string `json:"code" form:"code"`
|
||||
RedirectURI string `json:"redirect_uri" form:"redirect_uri"`
|
||||
CodeVerifier string `json:"code_verifier" form:"code_verifier"`
|
||||
}
|
||||
|
||||
type GrantClientData struct {
|
||||
}
|
||||
|
||||
type GrantRefreshData struct {
|
||||
RefreshToken string `json:"refresh_token" form:"refresh_token"`
|
||||
}
|
||||
|
||||
type GrantPasswordData struct {
|
||||
LoginType auth2.PasswordGrantType `json:"login_type" form:"login_type"`
|
||||
Username string `json:"username" form:"username"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Remember bool `json:"remember" form:"remember"`
|
||||
}
|
||||
|
||||
type AuthServiceError string
|
||||
|
||||
func (e AuthServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
const (
|
||||
ErrOauthInvalidRequest = AuthServiceError("invalid_request")
|
||||
ErrOauthInvalidClient = AuthServiceError("invalid_client")
|
||||
ErrOauthInvalidGrant = AuthServiceError("invalid_grant")
|
||||
ErrOauthInvalidScope = AuthServiceError("invalid_scope")
|
||||
ErrOauthUnauthorizedClient = AuthServiceError("unauthorized_client")
|
||||
ErrOauthUnsupportedGrantType = AuthServiceError("unsupported_grant_type")
|
||||
)
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"log/slog"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
@@ -15,15 +14,17 @@ import (
|
||||
edge2 "platform/web/domains/edge"
|
||||
proxy2 "platform/web/domains/proxy"
|
||||
resource2 "platform/web/domains/resource"
|
||||
"platform/web/events"
|
||||
g "platform/web/globals"
|
||||
"platform/web/globals/orm"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/tasks"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
@@ -296,7 +297,7 @@ func (s *channelService) CreateChannel(
|
||||
ids[i] = channels[i].ID
|
||||
}
|
||||
_, err = g.Asynq.Enqueue(
|
||||
tasks.NewRemoveChannel(ids),
|
||||
events.NewRemoveChannel(ids),
|
||||
asynq.ProcessIn(duration),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
"platform/web/core"
|
||||
coupon2 "platform/web/domains/coupon"
|
||||
trade2 "platform/web/domains/trade"
|
||||
"platform/web/events"
|
||||
g "platform/web/globals"
|
||||
"platform/web/globals/orm"
|
||||
m "platform/web/models"
|
||||
q "platform/web/queries"
|
||||
"platform/web/tasks"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
@@ -240,7 +240,7 @@ func (s *tradeService) CreateTrade(uid int32, now time.Time, data *CreateTradeDa
|
||||
}
|
||||
|
||||
// 提交异步关闭事件
|
||||
_, err = g.Asynq.Enqueue(tasks.NewCancelTrade(tasks.CancelTradeData{
|
||||
_, err = g.Asynq.Enqueue(events.NewCancelTrade(events.CancelTradeData{
|
||||
TradeNo: tradeNo,
|
||||
Method: method,
|
||||
}))
|
||||
@@ -417,7 +417,7 @@ func (s *tradeService) CancelTrade(tradeNo string, method trade2.Method, now tim
|
||||
MchOrderNo: &tradeNo,
|
||||
})
|
||||
if err != nil {
|
||||
slog.Debug(fmt.Sprintf("订单无需关闭:%s", err.Error()))
|
||||
slog.Debug(fmt.Sprintf("订单无需关闭: %s", err.Error()))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -546,11 +546,11 @@ func (s *tradeService) CheckTrade(data *ModifyTradeData) (*CheckTradeResult, err
|
||||
return nil, core.NewBizErr("订单不存在")
|
||||
}
|
||||
return nil, core.NewServErr(
|
||||
fmt.Sprintf("微信上游接口异常:code=%v,message=%v", apiErr.Code, apiErr.Message),
|
||||
fmt.Sprintf("微信上游接口异常: code=%v,message=%v", apiErr.Code, apiErr.Message),
|
||||
apiErr,
|
||||
)
|
||||
}
|
||||
return nil, core.NewServErr(fmt.Sprintf("微信上游支付接口异常:%s", err.Error()))
|
||||
return nil, core.NewServErr(fmt.Sprintf("微信上游支付接口异常: %s", err.Error()))
|
||||
}
|
||||
|
||||
// 填充返回值
|
||||
|
||||
@@ -19,28 +19,6 @@ import (
|
||||
|
||||
var Verifier = &verifierService{}
|
||||
|
||||
type VerifierServiceError string
|
||||
|
||||
func (e VerifierServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrVerifierServiceInvalid = VerifierServiceError("验证码错误")
|
||||
)
|
||||
|
||||
type VerifierServiceSendLimitErr int
|
||||
|
||||
func (e VerifierServiceSendLimitErr) Error() string {
|
||||
return "发送频率过快"
|
||||
}
|
||||
|
||||
type VerifierSmsPurpose int
|
||||
|
||||
const (
|
||||
VerifierSmsPurposeLogin VerifierSmsPurpose = iota
|
||||
)
|
||||
|
||||
type verifierService struct {
|
||||
}
|
||||
|
||||
@@ -148,6 +126,43 @@ func (s *verifierService) VerifySms(ctx context.Context, phone, code string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *verifierService) GetSms(ctx context.Context, phone string) (string, error) {
|
||||
key := smsKey(phone, VerifierSmsPurposeLogin)
|
||||
|
||||
val, err := g.Redis.Get(ctx, key).Result()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("验证码获取失败: %w", err)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func smsKey(phone string, purpose VerifierSmsPurpose) string {
|
||||
return fmt.Sprintf("verify:sms:%d:%s", purpose, phone)
|
||||
}
|
||||
|
||||
// region 短信目的
|
||||
|
||||
type VerifierSmsPurpose int
|
||||
|
||||
const (
|
||||
VerifierSmsPurposeLogin VerifierSmsPurpose = iota // 登录
|
||||
)
|
||||
|
||||
// region 服务异常
|
||||
|
||||
type VerifierServiceError string
|
||||
|
||||
func (e VerifierServiceError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrVerifierServiceInvalid = VerifierServiceError("验证码错误")
|
||||
)
|
||||
|
||||
type VerifierServiceSendLimitErr int
|
||||
|
||||
func (e VerifierServiceSendLimitErr) Error() string {
|
||||
return "发送频率过快"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user