99 lines
3.2 KiB
Go
99 lines
3.2 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"golang.org/x/crypto/bcrypt"
|
||
|
||
"observer-server/biz/dao"
|
||
"observer-server/biz/model/dto"
|
||
"observer-server/common"
|
||
)
|
||
|
||
// authService 账号业务:注册(license 表即账号表,注册即插行)、登录(bcrypt 校验 + 签发 token)
|
||
type authService struct{}
|
||
|
||
var Auth = &authService{}
|
||
|
||
// Register 注册:已注册(有密码)报错;存在运营手动授权先建的占位行(无密码)时补写密码完成注册。
|
||
func (s *authService) Register(ctx context.Context, req *dto.RegisterReq) (*dto.RegisterRes, error) {
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "密码加密失败")
|
||
}
|
||
err = common.Serial().Submit(ctx, func() error {
|
||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||
lic, err := dao.License.GetByPhoneInTx(ctx, tx, req.Phone)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if lic == nil {
|
||
return dao.License.Register(ctx, tx, req.Phone, string(hash))
|
||
}
|
||
if lic.Password == "" {
|
||
return dao.License.SetPasswordInTx(ctx, tx, req.Phone, string(hash))
|
||
}
|
||
return gerror.New("该手机号已注册")
|
||
})
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
common.ClearCache(ctx, "license:"+req.Phone)
|
||
return &dto.RegisterRes{}, nil
|
||
}
|
||
|
||
// Login 登录(自动注册式):绕缓存查账号(密码变更即时生效)。
|
||
// 手机号不存在 → 以输入密码创建账号;存在但密码为空(运营发卡占位行)→ 补写输入密码;
|
||
// 仅已有密码才做 bcrypt 比对,失败报错。创建/补密码后即视为登录成功签发 token。
|
||
func (s *authService) Login(ctx context.Context, req *dto.LoginReq) (*dto.LoginRes, error) {
|
||
lic, err := dao.License.GetByPhoneNoCache(ctx, req.Phone)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if lic == nil || lic.Password == "" {
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "密码加密失败")
|
||
}
|
||
err = common.Serial().Submit(ctx, func() error {
|
||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||
cur, err := dao.License.GetByPhoneInTx(ctx, tx, req.Phone)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if cur == nil {
|
||
return dao.License.Register(ctx, tx, req.Phone, string(hash))
|
||
}
|
||
if cur.Password == "" {
|
||
return dao.License.SetPasswordInTx(ctx, tx, req.Phone, string(hash))
|
||
}
|
||
return nil
|
||
})
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
common.ClearCache(ctx, "license:"+req.Phone)
|
||
// 并发下密码可能已被他人补写,重新读取后统一走比对
|
||
lic, err = dao.License.GetByPhoneNoCache(ctx, req.Phone)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
if lic == nil || lic.Password == "" {
|
||
return nil, gerror.New("账号或密码错误")
|
||
}
|
||
if err := bcrypt.CompareHashAndPassword([]byte(lic.Password), []byte(req.Password)); err != nil {
|
||
return nil, gerror.New("账号或密码错误")
|
||
}
|
||
token, err := common.SignToken(ctx, req.Phone)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &dto.LoginRes{Token: token}, nil
|
||
}
|