package internal import ( "context" "fmt" "github.com/gogf/gf/v2/frame/g" "server/internal/consts" "server/internal/dao" "server/internal/model" "server/internal/model/do" "server/internal/model/entity" "server/internal/service" "server/utility" "server/utility/ecode" "server/utility/encrypt" "server/utility/gamelife" "server/utility/jwt" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/grand" ) type sUser struct{} func init() { service.RegisterUser(New()) go checkUserRole() } func checkUserRole() { ctx := context.Background() exist, err := dao.Roles.Ctx(ctx).Where(do.Roles{Code: consts.UserRoleCode}).Exist() if err != nil { return } if !exist { _, err := dao.Roles.Ctx(ctx).Data(do.Roles{ Name: "用户", Code: consts.UserRoleCode, Description: "用户角色", Status: consts.RoleEnable, IsDeletable: false, }).Insert() if err != nil { return } } } func New() service.IUser { return &sUser{} } func (s *sUser) Login(ctx context.Context, in *model.UserLoginIn) (out *model.UserLoginOut, err error) { value, err := dao.Roles.Ctx(ctx).Where(do.Roles{Code: consts.UserRoleCode}).Fields(dao.Roles.Columns().Code, dao.Roles.Columns().Id).One() if err != nil { return nil, ecode.Fail.Sub("查找角色失败") } // 根据 OpenId 查找用户 exist, err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId}).Exist() if err != nil { return nil, ecode.Fail.Sub("查找用户失败") } var userId int64 v, err := dao.Stores.Ctx(ctx).Where(do.Stores{StoreCode: in.StoreCode}).Fields(dao.Stores.Columns().Id).Value() if err != nil { return nil, ecode.Fail.Sub("查找门店失败") } if !exist { // 用户不存在,创建新用户 // 生成 username: qy_ + 8位随机字母数字 var username string for { randomStr := grand.Str("abcdefghijklmnopqrstuvwxyz0123456789", 8) username = "qy_" + randomStr // 检查 username 是否唯一 count, err := dao.Users.Ctx(ctx).Where(do.Users{Username: username}).Count() if err != nil { return nil, ecode.Fail.Sub("检查用户名失败") } if count == 0 { break // username 唯一,退出循环 } } password, err := encrypt.EncryptPassword(consts.DefaultPassword) if err != nil { return nil, ecode.Fail.Sub("加密密码失败") } user := &entity.Users{ WxOpenId: in.OpenId, Username: username, Nickname: username, PasswordHash: password, Avatar: "adssssssssssssss", // FIXME 后续替换成默认头像的 oss 链接 FirstVisitAt: gtime.Now(), LastLoginAt: gtime.Now(), WxPopenId: utility.GenerateUserID("WX"), QqPopenId: utility.GenerateUserID("QQ"), RoleId: value[dao.Roles.Columns().Id].Int64(), LastLoginStoreId: v.Int64(), } result, err := dao.Users.Ctx(ctx).Insert(user) if err != nil { return nil, ecode.Fail.Sub("创建用户失败") } userId, err = result.LastInsertId() if err != nil { return nil, ecode.Fail.Sub("获取用户ID失败") } } else { // 用户存在,更新最后登录时间 var user entity.Users if err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId}).Scan(&user); err != nil { return nil, ecode.Fail.Sub("查找用户失败") } userId = user.Id if _, err := dao.Users.Ctx(ctx).Where(do.Users{Id: userId}).Update(do.Users{LastLoginAt: gtime.Now(), LastLoginStoreId: v.Int64()}); err != nil { return nil, ecode.Fail.Sub("更新登录时间失败") } } // 生成 token token, err := jwt.GenerateToken(&jwt.TokenIn{ UserId: userId, Role: value[dao.Roles.Columns().Code].String(), }) if err != nil { return nil, ecode.Fail.Sub("生成token失败") } out = &model.UserLoginOut{ Token: token, } return } func (s *sUser) WeChatLogin(ctx context.Context, in *model.WeChatLogin) (out *model.WeChatLoginOut, err error) { return } func (s *sUser) Info(ctx context.Context, in *model.UserInfoIn) (out *model.UserInfoOut, err error) { exist, err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId, Id: in.Id}).OmitEmptyWhere().Exist() if err != nil { return nil, ecode.Fail.Sub("查找用户失败") } if !exist { return nil, ecode.Params.Sub("用户不存在") } var user entity.Users if err := dao.Users.Ctx(ctx).Where(do.Users{WxOpenId: in.OpenId, Id: in.Id}).OmitEmptyWhere().Scan(&user); err != nil { return nil, ecode.Fail.Sub("查找用户失败") } out = &model.UserInfoOut{ Id: user.Id, WxOpenId: user.WxOpenId, Username: user.Username, Nickname: user.Nickname, Avatar: user.Avatar, PhoneNumber: user.PhoneNumber, WxPopenId: user.WxPopenId, QQPopenId: user.QqPopenId, RoleId: user.RoleId, } return out, nil } func (s *sUser) Code(ctx context.Context, in *model.GetPhoneCodeIn) (out *model.GetPhoneCodeOut, err error) { // TODO 短信平台获取验证码 code := "9999" // 存入 redis err = g.Redis().SetEX(ctx, fmt.Sprintf(consts.UserBindPhoneKey, in.Id), code, consts.UserCodeExpire) if err != nil { return nil, ecode.Fail.Sub("设置验证码失败") } return &model.GetPhoneCodeOut{ Success: true, }, nil } func (s *sUser) Update(ctx context.Context, in *model.UserUpdateIn) (out *model.UpdateOut, err error) { exist, err := dao.Users.Ctx(ctx).Where(do.Users{Id: in.Id}).Exist() if err != nil { return nil, ecode.Fail.Sub("该用户不存在") } if !exist { return nil, ecode.Params.Sub("用户不存在") } _, err = dao.Users.Ctx(ctx).Where(do.Users{Id: in.Id}).Update(do.Users{ Nickname: in.Nickname, Avatar: in.Avatar, }) if err != nil { return nil, ecode.Fail.Sub("更新用户信息失败") } return &model.UpdateOut{ Success: true, }, nil } func (s *sUser) BindPhone(ctx context.Context, in *model.UserBindPhoneIn) (out *model.UserBindPhoneOut, err error) { // 绑定手机号,需要验证入参和缓存中的验证码时候相同 value, err := g.Redis().Get(ctx, fmt.Sprintf(consts.UserBindPhoneKey, in.Id)) if err != nil { return nil, ecode.Fail.Sub("获取失败") } if value.IsEmpty() { return nil, ecode.Fail.Sub("验证码已过期") } if value.String() != in.PhoneCode { return nil, ecode.Fail.Sub("验证码错误") } _, err = dao.Users.Ctx(ctx).Where(do.Users{Id: in.Id}).Update(do.Users{ PhoneNumber: in.Phone, }) if err != nil { return nil, ecode.Fail.Sub("绑定手机号失败") } return &model.UserBindPhoneOut{ Success: true, }, nil } func (s *sUser) List(ctx context.Context, in *model.UserListIn) (out *model.UserListOut, err error) { list := make([]model.User, 0) var total int orm := dao.Users.Ctx(ctx) if in.Nickname != "" { orm = orm.WhereLike(dao.Users.Columns().Nickname, "%"+in.Nickname+"%") } if err = orm.Page(in.Page, in.Size).LeftJoin( dao.Stores.Table(), fmt.Sprintf("%s.%s = %s.%s", dao.Users.Table(), dao.Users.Columns().LastLoginStoreId, dao.Stores.Table(), dao.Stores.Columns().Id), ).Fields(fmt.Sprintf("%s.*, %s.%s %s", dao.Users.Table(), dao.Stores.Table(), dao.Stores.Columns().Name, "last_login_store_name")).ScanAndCount(&list, &total, false); err != nil { return nil, ecode.Fail.Sub("获取用户列表失败") } return &model.UserListOut{ List: list, Total: total, }, nil } func (s *sUser) BoundUrl(ctx context.Context, in *model.UserBoundUrlIn) (out *model.UserBoundUrlOut, err error) { url, err := gamelife.GetGamelifeClient(ctx).GetUrl(ctx, in.PopenId, in.AppName, "", in.BindType, in.IsBound) if err != nil { return nil, ecode.Fail.Sub("获取绑定链接失败") } return &model.UserBoundUrlOut{ Url: url, }, nil } func (s *sUser) UnBoundUrl(ctx context.Context, in *model.UserBoundUrlIn) (out *model.UserUnBoundUrlOut, err error) { url, err := gamelife.GetGamelifeClient(ctx).GetUrl(ctx, in.PopenId, in.AppName, in.Nickname, in.BindType, in.IsBound) if err != nil { return nil, ecode.Fail.Sub("获取绑定链接失败") } return &model.UserUnBoundUrlOut{ Url: url, }, nil } func (s *sUser) BoundInfo(ctx context.Context, in *model.UserBoundInfoIn) (out *model.UserBoundInfoOut, err error) { result, err := gamelife.GetGamelifeClient(ctx).GetBound(ctx, in.PopenId) if err != nil { return nil, err } return &model.UserBoundInfoOut{ IsBound: result.Result, AppNames: result.AppNames, Ctime: result.Ctime, Nick: result.Nick, Utype: result.Utype, }, nil }