package user import ( "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" systemReq "github.com/flipped-aurora/gin-vue-admin/server/model/system/request" "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request" wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response" "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service" "github.com/flipped-aurora/gin-vue-admin/server/utils" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type MiniUserApi struct{} var miniService = service.ServiceGroupApp.MiniService // Login 小程序登录 // @Tags WechatMiniUser // @Summary 小程序登录 // @Description 微信小程序登录接口 // @Accept json // @Produce json // @Param data body request.MiniLoginRequest true "登录参数" // @Success 200 {object} wechatResponse.Response{data=wechatResponse.MiniLoginResponse} "登录成功" // @Router /wechat/user/mini/login [post] func (w *MiniUserApi) Login(c *gin.Context) { var req request.MiniLoginRequest err := c.ShouldBindJSON(&req) if err != nil { c.JSON(200, wechatResponse.ErrorResponseWithMsg(wechatResponse.ERROR_PARAM_INVALID, err.Error())) return } if req.Code == "" { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_PARAM_MISSING)) return } user, err := miniService.Code2Session(req.Code) if err != nil { global.GVA_LOG.Error("小程序登录失败!", zap.Error(err)) // 根据错误类型返回不同的错误码 if err.Error() == "获取用户 OpenID 失败" { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_WX_OPENID_EMPTY)) } else if err.Error() == "微信小程序 Code2Session 失败" { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_WX_SESSION_FAILED)) } else { c.JSON(200, wechatResponse.ErrorResponseWithMsg(wechatResponse.ERROR_WX_CODE_INVALID, err.Error())) } return } // 生成JWT token token, _, err := utils.AppUserLoginToken(user) if err != nil { global.GVA_LOG.Error("生成token失败!", zap.Error(err)) c.JSON(200, wechatResponse.ErrorResponseWithMsg(wechatResponse.ERROR_INTERNAL, "生成token失败")) return } resp := wechatResponse.MiniLoginResponse{ OpenID: user.OpenID, UnionID: user.UnionID, Token: token, } c.JSON(200, wechatResponse.SuccessResponseWithMsg(resp, "登录成功")) } // GetUserInfo 获取用户信息 // @Tags WechatMiniUser // @Summary 获取用户信息 // @Description 获取当前登录用户的详细信息 // @Accept json // @Produce json // @Success 200 {object} wechatResponse.Response{data=wechatResponse.MiniUserInfoResponse} "获取成功" // @Router /user/wechat/mini/userinfo [get] func (w *MiniUserApi) GetUserInfo(c *gin.Context) { // 从JWT token中获取用户信息 claims, exists := c.Get("user_claims") if !exists { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_UNAUTHORIZED)) return } userClaims, ok := claims.(*systemReq.AppUserClaims) if !ok { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_TOKEN_INVALID)) return } openid := userClaims.AppBaseClaims.OpenID if openid == "" { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_WX_OPENID_EMPTY)) return } user, err := miniService.GetUserInfo(openid) if err != nil { global.GVA_LOG.Error("获取用户信息失败!", zap.Error(err)) if err.Error() == "用户不存在" { c.JSON(200, wechatResponse.ErrorResponse(wechatResponse.ERROR_USER_NOT_FOUND)) } else { c.JSON(200, wechatResponse.ErrorResponseWithMsg(wechatResponse.ERROR_INTERNAL, err.Error())) } return } // 构造响应数据 resp := wechatResponse.MiniUserInfoResponse{ NickName: user.Nickname, Avatar: user.AvatarURL, Gender: user.Gender, City: user.City, Province: user.Province, Country: user.Country, NeedPhoneAuth: user.Phone == nil || *user.Phone == "", // 如果没有手机号则需要授权 } c.JSON(200, wechatResponse.SuccessResponseWithMsg(resp, "获取成功")) } // UpdateUserInfo 更新用户信息 // @Tags WechatMiniUser // @Summary 更新用户信息 // @Description 更新小程序用户信息 // @Accept json // @Produce json // @Param data body request.UpdateUserInfoRequest true "用户信息" // @Success 200 {object} response.Response "更新成功" // @Router /wechat/user/mini/userinfo [put] func (w *MiniUserApi) UpdateUserInfo(c *gin.Context) { var req request.UpdateUserInfoRequest err := c.ShouldBindJSON(&req) if err != nil { response.FailWithMessage(err.Error(), c) return } if req.OpenID == "" { response.FailWithMessage("openid不能为空", c) return } err = miniService.UpdateUserInfo(req.OpenID, req.UserInfo) if err != nil { global.GVA_LOG.Error("更新用户信息失败!", zap.Error(err)) response.FailWithMessage("更新失败: "+err.Error(), c) return } response.OkWithMessage("更新成功", c) } // BindPhone 绑定手机号 // @Tags WechatMiniUser // @Summary 绑定手机号 // @Description 绑定手机号并关联系统用户 // @Accept json // @Produce json // @Param data body request.BindPhoneRequest true "绑定参数" // @Success 200 {object} response.Response{data=model.WechatMiniUser} "绑定成功" // @Router /wechat/user/mini/bind-phone [post] func (w *MiniUserApi) BindPhone(c *gin.Context) { var req request.BindPhoneRequest err := c.ShouldBindJSON(&req) if err != nil { response.FailWithMessage(err.Error(), c) return } if req.OpenID == "" || req.Phone == "" { response.FailWithMessage("openid和手机号不能为空", c) return } user, err := miniService.BindPhone(req.OpenID, req.Phone) if err != nil { global.GVA_LOG.Error("绑定手机号失败!", zap.Error(err)) response.FailWithMessage("绑定失败: "+err.Error(), c) return } response.OkWithDetailed(user, "绑定成功", c) } // UpdatePhoneNumber 更新用户手机号 // @Tags WechatMiniUser // @Summary 更新用户手机号 // @Description 通过微信手机号授权更新用户手机号 // @Accept json // @Produce json // @Param data body request.WxPhoneLoginRequest true "手机号授权参数" // @Success 200 {object} response.Response{data=string} "更新成功" // @Router /user/wechat/mini/phone-update [post] func (w *MiniUserApi) UpdatePhoneNumber(c *gin.Context) { var req request.WxPhoneLoginRequest err := c.ShouldBindJSON(&req) if err != nil { response.FailWithMessage(err.Error(), c) return } if req.EncryptedData == "" || req.IV == "" { response.FailWithMessage("encryptedData和iv不能为空", c) return } // 从 JWT token 中获取用户信息 claims, exists := c.Get("user_claims") if !exists { response.FailWithMessage("获取用户信息失败", c) return } // 获取当前用户的信息 userClaims, ok := claims.(*systemReq.AppUserClaims) if !ok { response.FailWithMessage("用户信息格式错误", c) return } // 直接从 claims 中获取 openid openid := userClaims.AppBaseClaims.OpenID if openid == "" { response.FailWithMessage("用户 OpenID 不存在", c) return } phoneNumber, err := miniService.UpdatePhoneByEncryption(openid, req.EncryptedData, req.IV) if err != nil { global.GVA_LOG.Error("更新手机号失败!", zap.Error(err)) response.FailWithMessage("更新失败: "+err.Error(), c) return } response.OkWithDetailed(phoneNumber, "手机号更新成功", c) } // CheckUnionID 检查UnionID是否存在(为APP登录预留) // @Tags WechatMiniUser // @Summary 检查UnionID // @Description 检查UnionID是否已存在用户 // @Accept json // @Produce json // @Param unionid query string true "UnionID" // @Success 200 {object} response.Response{data=model.WechatMiniUser} "检查成功" // @Router /wechat/user/mini/check-unionid [get] func (w *MiniUserApi) CheckUnionID(c *gin.Context) { unionid := c.Query("unionid") if unionid == "" { response.FailWithMessage("unionid不能为空", c) return } user, err := miniService.CheckUnionIDExists(unionid) if err != nil { global.GVA_LOG.Error("检查UnionID失败!", zap.Error(err)) response.FailWithMessage("检查失败: "+err.Error(), c) return } if user == nil { response.OkWithDetailed(nil, "UnionID不存在", c) } else { response.OkWithDetailed(user, "UnionID已存在", c) } }