package service import ( "crypto/sha512" "electricity_bill_calc/cache" "electricity_bill_calc/config" "electricity_bill_calc/exceptions" "electricity_bill_calc/global" "electricity_bill_calc/model" "electricity_bill_calc/utils" "fmt" "time" "github.com/google/uuid" "xorm.io/builder" ) type _UserService struct{} var UserService _UserService func (u _UserService) ProcessEnterpriseUserLogin(username, password string) (*model.Session, error) { user, err := u.findUserByUsername(username) if err != nil { return nil, err } if user == nil { return nil, exceptions.NewAuthenticationError(404, "用户不存在。") } if user.Type != 0 { return nil, exceptions.NewAuthenticationError(401, "用户类型不正确。") } if !user.Enabled { return nil, exceptions.NewAuthenticationError(401, "用户已被禁用。") } hash := sha512.New512_256() hash.Write([]byte(password)) hashedPassword := fmt.Sprintf("%x", hash.Sum(nil)) if hashedPassword != user.Password { return nil, exceptions.NewAuthenticationError(401, "用户凭据不正确。") } if user.ResetNeeded { authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") authErr.NeedReset = true return nil, authErr } userDetial, _ := u.retreiveUserDetail(user.Id) if userDetial.ServiceExpiration.Before(time.Now()) { return nil, exceptions.NewAuthenticationError(401, "用户服务期限已过。") } session := &model.Session{ Token: uuid.New().String(), Uid: user.Id, Type: user.Type, Name: user.Username, ExpiresAt: time.Now().Add(config.ServiceSettings.MaxSessionLife), } if userDetial != nil { session.Name = *userDetial.Name } cache.CacheSession(session) return session, nil } func (u _UserService) ProcessManagementUserLogin(username, password string) (*model.Session, error) { user, err := u.findUserByUsername(username) if err != nil { return nil, err } if user == nil { return nil, exceptions.NewAuthenticationError(404, "用户不存在。") } if user.Type != 1 && user.Type != 2 { return nil, exceptions.NewAuthenticationError(401, "用户类型不正确。") } if !user.Enabled { return nil, exceptions.NewAuthenticationError(401, "用户已被禁用。") } hash := sha512.New512_256() hash.Write([]byte(password)) hashedPassword := fmt.Sprintf("%x", hash.Sum(nil)) if hashedPassword != user.Password { return nil, exceptions.NewAuthenticationError(401, "用户凭据不正确。") } if user.ResetNeeded { authErr := exceptions.NewAuthenticationError(401, "用户凭据已失效。") authErr.NeedReset = true return nil, authErr } session := &model.Session{ Token: uuid.New().String(), Uid: user.Id, Type: user.Type, Name: user.Username, ExpiresAt: time.Now().Add(config.ServiceSettings.MaxSessionLife), } userDetial, _ := u.retreiveUserDetail(user.Id) if userDetial != nil { session.Name = *userDetial.Name } cache.CacheSession(session) return session, nil } func (u _UserService) InvalidUserPassword(uid string) (string, error) { user, err := u.findUserByID(uid) if user == nil && err != nil { return "", exceptions.NewNotFoundError("指定的用户不存在。") } verifyCode := utils.RandStr(10) hash := sha512.New512_256() hash.Write([]byte(verifyCode)) user.Password = fmt.Sprintf("%x", hash.Sum(nil)) user.ResetNeeded = true affected, err := global.DBConn.ID(uid).Cols("password", "reset_needed").Update(user) if err != nil { return "", err } if affected > 0 { // ! 同一个用户在缓存中有两个键。 cache.AbolishCacheData("user", user.Id) cache.AbolishCacheData("user", user.Username) return verifyCode, nil } else { return "", exceptions.NewUnsuccessfulOperationError() } } func (u _UserService) VerifyUserPassword(username, verifyCode string) (bool, error) { user, err := u.findUserByUsername(username) if user == nil || err != nil { return false, exceptions.NewNotFoundError("指定的用户不存在。") } hash := sha512.New512_256() hash.Write([]byte(verifyCode)) hashedVerifyCode := fmt.Sprintf("%x", hash.Sum(nil)) if hashedVerifyCode != user.Password { return false, nil } else { return true, nil } } func (u _UserService) ResetUserPassword(username, password string) (bool, error) { user, err := u.findUserByUsername(username) if user == nil || err != nil { return false, exceptions.NewNotFoundError("指定的用户不存在。") } hash := sha512.New512_256() hash.Write([]byte(password)) user.Password = fmt.Sprintf("%x", hash.Sum(nil)) user.ResetNeeded = false affected, err := global.DBConn.ID(user.Id).Cols("password", "reset_needed").Update(user) if err != nil { return false, err } if affected > 0 { cache.AbolishCacheData("user", user.Id) cache.AbolishCacheData("user", user.Username) return true, nil } else { return false, nil } } func (_UserService) IsUserExists(uid string) (bool, error) { return global.DBConn.ID(uid).Exist(&model.User{}) } func (_UserService) IsUsernameExists(username string) (bool, error) { return global.DBConn.Where(builder.Eq{"username": username}).Exist(&model.User{}) } func (u _UserService) CreateUser(user *model.User, detail *model.UserDetail) (string, error) { if len(user.Id) == 0 { user.Id = uuid.New().String() } exists, err := u.IsUserExists(user.Id) if exists { return "", exceptions.NewNotFoundError("user already exists") } if err != nil { return "", nil } detail.Id = user.Id verifyCode := utils.RandStr(10) hash := sha512.New512_256() hash.Write([]byte(verifyCode)) user.Password = fmt.Sprintf("%x", hash.Sum(nil)) user.ResetNeeded = true if detail.Name != nil { finalAbbr := utils.PinyinAbbr(*detail.Name) detail.Abbr = &finalAbbr } tx := global.DBConn.NewSession() defer tx.Close() if err := tx.Begin(); err != nil { return "", err } _, err = tx.Insert(user) if err != nil { tx.Rollback() return "", fmt.Errorf("user create failed: %w", err) } _, err = tx.Insert(detail) if err != nil { tx.Rollback() return "", fmt.Errorf("user Detail create failed: %w", err) } err = tx.Commit() if err != nil { tx.Rollback() return "", fmt.Errorf("transaction commit unsuccessful: %w", err) } return verifyCode, nil } func (u _UserService) SwitchUserState(uid string, enabled bool) error { exists, err := u.IsUserExists(uid) if !exists { return exceptions.NewNotFoundError("user not exists") } if err != nil { return err } newStateUser := new(model.User) newStateUser.Enabled = enabled _, err = global.DBConn.ID(uid).Cols("enabled").Update(newStateUser) return err } func (_UserService) SearchLimitUsers(keyword string, limit int) ([]model.JoinedUserDetail, error) { var users = make([]model.JoinedUserDetail, 0) err := global.DBConn. Table("user_detail").Alias("d"). Join("INNER", []string{"user", "u"}, "d.id=u.id"). Where( builder.NewCond(). Or(builder.Like{"u.username", keyword}). Or(builder.Like{"d.name", keyword}). Or(builder.Like{"d.abbr", keyword})). And(builder.Eq{"u.type": 0}). Asc("u.created_at"). Limit(limit, 0). Find(&users) if err != nil { return make([]model.JoinedUserDetail, 0), err } return users, nil } func (_UserService) findUserByUsername(username string) (*model.User, error) { cachedUser, _ := cache.RetreiveData[model.User]("user", username) if cachedUser != nil { return cachedUser, nil } user := new(model.User) has, err := global.DBConn.Where(builder.Eq{"username": username}).NoAutoCondition().Get(user) if has { cache.CacheData(user, "user", username) } return _postProcessSingle(user, has, err) } func (_UserService) retreiveUserDetail(uid string) (*model.UserDetail, error) { cachedUser, _ := cache.RetreiveData[model.UserDetail]("user", uid) if cachedUser != nil { return cachedUser, nil } user := new(model.UserDetail) has, err := global.DBConn.ID(uid).NoAutoCondition().Get(user) if has { cache.CacheData(user, "user_detail", uid) } return _postProcessSingle(user, has, err) } func (_UserService) findUserByID(uid string) (*model.User, error) { cachedUser, _ := cache.RetreiveData[model.User]("user", uid) if cachedUser != nil { return cachedUser, nil } user := new(model.User) has, err := global.DBConn.ID(uid).NoAutoCondition().Get(user) if has { cache.CacheData(user, "user", uid) } return _postProcessSingle(user, has, err) } func (_UserService) ListUserDetail(keyword string, userType int, userState *bool, page int) ([]model.JoinedUserDetail, int64, error) { var cond = builder.NewCond() cond = cond.And(builder.Neq{"d.id": "000"}) if len(keyword) != 0 { keywordCond := builder.NewCond(). Or(builder.Like{"u.username", keyword}). Or(builder.Like{"d.name", keyword}) cond = cond.And(keywordCond) } if userType != -1 { cond = cond.And(builder.Eq{"u.type": userType}) } if userState != nil { cond = cond.And(builder.Eq{"u.enabled": *userState}) } startItem := (page - 1) * config.ServiceSettings.ItemsPageSize total, err := global.DBConn. Table("user_detail").Alias("d"). Join("INNER", []string{"user", "u"}, "d.id=u.id"). Where(cond). Count(&model.User{}) if err != nil { return nil, -1, err } users := make([]model.JoinedUserDetail, 0) err = global.DBConn. Table("user_detail").Alias("d"). Join("INNER", []string{"user", "u"}, "d.id=u.id"). Where(cond). Limit(config.ServiceSettings.ItemsPageSize, startItem). Find(&users) return users, total, err } func (_UserService) FetchUserDetail(uid string) (*model.FullJoinedUserDetail, error) { user := &model.FullJoinedUserDetail{} has, err := global.DBConn. Table("user_detail").Alias("d"). Join("INNER", []string{"user", "u"}, "d.id=u.id"). Where(builder.Eq{"d.id": uid}). NoAutoCondition(). Get(user) if has { return user, nil } return nil, err }