package system import ( "bytes" "encoding/json" "errors" "fmt" "io" "log" "net/http" url2 "net/url" "strconv" "sundynix-go/global" common "sundynix-go/model/commom/request" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" systemResp "sundynix-go/model/system/response" "sundynix-go/pkg/httpclient" "sundynix-go/utils" location "sundynix-go/utils/location" "sundynix-go/utils/uniqueid" "sundynix-go/utils/wechat" "go.uber.org/zap" "gorm.io/gorm" ) type UserService struct{} var UserServiceApp = new(UserService) func (userService *UserService) Login(u *system.User) (userInfo *system.User, err error) { var user system.User // 查询出用户信息的同时查询出角色信息 err = global.DB.Model(&system.User{}).Where("account = ?", u.Account).First(&user).Error if err == nil { if ok := utils.BcryptCheck(u.Password, user.Password); !ok { return nil, errors.New("密码错误") } } return &user, err } func (userService *UserService) SaveUser(user system.User) error { if !errors.Is(global.DB.Where("account = ?", user.Account).First(&system.User{}).Error, gorm.ErrRecordNotFound) { return errors.New("存在重复Account,请修改Account") } user.Password = utils.BcryptHash(user.Password) return global.DB.Create(&user).Error } func (userService *UserService) UpdateUser(user *system.User) (err error) { var sysUser system.User userMap := map[string]interface{}{ "account": user.Account, "phone": user.Phone, "name": user.Name, "avatar_id": user.AvatarId, } err = global.DB.Where("id = ?", user.Id).First(&sysUser).Error if err != nil { global.Logger.Debug(err.Error()) return errors.New("查询用户失败") } err = global.DB.Model(&sysUser).Updates(userMap).Error return err } func (userService *UserService) GetUserList(info systemReq.GetUserList) (list interface{}, total int64, err error) { limit := info.PageSize offset := info.PageSize * (info.Current - 1) db := global.DB.Model(&system.User{}) var userList []system.User if info.Account != "" { db = db.Where("account LIKE ?", "%"+info.Account+"%") } if info.Phone != "" { db = db.Where("phone LIKE ?", "%"+info.Phone+"%") } err = db.Count(&total).Error if err != nil { return } err = db.Limit(limit).Offset(offset).Find(&userList).Error return userList, total, err } func (userService *UserService) DeleteUserByIds(ids common.IdsReq) error { return global.DB.Where("id IN (?)", ids.Ids).Delete(&system.User{}).Error } func (userService *UserService) GetUserById(id string) (user *system.User, err error) { var u system.User err = global.DB.Where("id = ?", id).Preload("Avatar").First(&u).Error return &u, err } func (userService *UserService) ChangePassword(id string, pwd string) (err error) { return global.DB.Model(&system.User{}).Where("id = ?", id).Update("password", utils.BcryptHash(pwd)).Error } func (userService *UserService) MiniLogin(code string) (result *system.User, err error) { //构建参数 params := url2.Values{} params.Set("appid", global.Config.MiniProgram.AppId) params.Set("secret", global.Config.MiniProgram.AppSecret) params.Set("js_code", code) params.Set("grant_type", "authorization_code") fullURL := "https://api.weixin.qq.com/sns/jscode2session?" + params.Encode() //1. 获取全局 HTTP Client(复用连接池) myHttpClient := httpclient.GetClient() //2.发起请求 resp, err := myHttpClient.Get(fullURL) if err != nil { global.Logger.Error("微信登录接口请求失败", zap.Error(err)) return nil, fmt.Errorf("微信登录接口请求失败: %w", err) } defer resp.Body.Close() //3.读取响应 body, err := io.ReadAll(resp.Body) if err != nil { global.Logger.Error("读取微信接口响应失败", zap.Error(err)) return nil, fmt.Errorf("读取微信登录接口响应失败: %w", err) } // 4. 解析JSON(用结构体替代map,提升效率+类型安全) var wxResp systemResp.WxCode2SessionResp if err = json.Unmarshal(body, &wxResp); err != nil { global.Logger.Error("解析微信接口响应失败", zap.Error(err)) return nil, fmt.Errorf("解析微信登录接口响应失败: %w", err) } // 5. 检查微信接口错误码(关键:原代码未处理errcode,导致无法定位真实错误) if wxResp.Errcode != 0 { errMsg := fmt.Sprintf("微信接口返回错误: errcode=%d, errmsg=%s", wxResp.Errcode, wxResp.Errmsg) global.Logger.Error(errMsg) return nil, errors.New(errMsg) } // 6. 校验openid(空值直接返回) if wxResp.Openid == "" { global.Logger.Error("微信接口返回openid为空") return nil, errors.New("openid为空") } // 7. 根据openid查询用户 存在--> 更新session_key 返回数据 var user system.User err = global.DB.Where("mini_open_id = ?", wxResp.Openid).Preload("Avatar").First(&user).Error if errors.Is(err, gorm.ErrRecordNotFound) { // 8. 使用 Transaction 闭包管理事务 err = global.DB.Transaction(func(tx *gorm.DB) error { // 创建新用户 newUser := system.User{ Name: uniqueid.GenerateName(), MiniOpenId: wxResp.Openid, SessionKey: wxResp.SessionKey, } if err := tx.Create(&newUser).Error; err != nil { return err } // 赋值给外部变量以便返回 user = newUser return nil }) if err != nil { global.Logger.Error("创建用户失败", zap.Error(err)) return nil, fmt.Errorf("登录失败: %w", err) } return &user, nil } if err == nil && user.Id != "" { // UpdateColumn:只更新字段,不触发模型钩子,比Update更高效 if err = global.DB.Model(&user).UpdateColumn("session_key", wxResp.SessionKey).Error; err != nil { global.Logger.Error("更新session_key失败", zap.Error(err)) return nil, fmt.Errorf("更新session_key失败: %w", err) } return &user, nil } return nil, errors.New("登录失败") } func (userService *UserService) LoginByPhone(code string, openId string) (result *system.User, err error) { token := wechat.GetMiniAccessToken() url := "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + token data := map[string]interface{}{ "code": code, } jsonData, _ := json.Marshal(data) resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) if err != nil { log.Fatalf("Error making POST request: %s", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("Error reading response body: %s", err) } var dataMap map[string]interface{} err = json.Unmarshal([]byte(body), &dataMap) if err != nil { log.Fatalf("Error unmarshalling JSON: %s", err) } //用户已经存在 --> 如果有手机号直接返回user,没有则更新手机号并返回user if openId != "" { var user system.User err = global.DB.Where("mini_open_id = ?", openId).First(&user).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("用户不存在") } if err == nil && user.Id != "" { if user.Phone != "" { return &user, nil } else { user.Phone = dataMap["phone_info"].(map[string]interface{})["phoneNumber"].(string) return &user, global.DB.Save(&user).Error } } } return nil, errors.New("登录失败") } // GetLocation 获取位置信息 func (userService *UserService) GetLocation(longitude, latitude string) (res map[string]interface{}, err error) { long, err := strconv.ParseFloat(longitude, 32) if err != nil { return res, err } lati, err := strconv.ParseFloat(latitude, 32) if err != nil { return res, err } entity, err := location.Point2code(float32(long), float32(lati)) result := map[string]interface{}{ "city": entity.AddressComponent.City.String(), "adcode": entity.AddressComponent.Adcode, } return result, err } // GetWeather 获取天气信息 adcode 行政区划代码 func (userService *UserService) GetWeather(adcode string) (res map[string]interface{}, err error) { weatherResp, err := location.GetWeather(adcode, "base") live := weatherResp.Lives[0] // 实时天气数组仅1条数据 result := map[string]interface{}{ "province": live.Province, "city": live.City, "adcode": live.Adcode, "weather": live.Weather, "temperature": live.Temperature, "windPower": live.WindPower, "windDirection": live.WindDirection, "humidity": live.Humidity, "reportTime": live.ReportTime, } return result, err }