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 }