diff --git a/server/plugin/wechat-integration/api/user/mini_user_api.go b/server/plugin/wechat-integration/api/user/mini_user_api.go new file mode 100644 index 00000000..f4c6cf7f --- /dev/null +++ b/server/plugin/wechat-integration/api/user/mini_user_api.go @@ -0,0 +1,182 @@ +package user + +import ( + "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/flipped-aurora/gin-vue-admin/server/model/common/response" + "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} response.Response{data=response.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 { + response.FailWithMessage(err.Error(), c) + return + } + + if req.Code == "" { + response.FailWithMessage("code不能为空", c) + return + } + + user, err := miniService.Code2Session(req.Code) + if err != nil { + global.GVA_LOG.Error("小程序登录失败!", zap.Error(err)) + response.FailWithMessage("登录失败: "+err.Error(), c) + return + } + + // 生成JWT token + token, _, err := utils.AppUserLoginToken(user) + if err != nil { + global.GVA_LOG.Error("生成token失败!", zap.Error(err)) + response.FailWithMessage("登录失败: "+err.Error(), c) + return + } + + resp := wechatResponse.MiniLoginResponse{ + OpenID: user.OpenID, + UnionID: user.UnionID, + Token: token, + } + + response.OkWithDetailed(resp, "登录成功", c) +} + +// GetUserInfo 获取用户信息 +// @Tags WechatMiniUser +// @Summary 获取用户信息 +// @Description 获取小程序用户信息 +// @Accept json +// @Produce json +// @Param openid query string true "用户openid" +// @Success 200 {object} response.Response{data=model.WechatMiniUser} "获取成功" +// @Router /wechat/user/mini/userinfo [get] +func (w *MiniUserApi) GetUserInfo(c *gin.Context) { + openid := c.Query("openid") + if openid == "" { + response.FailWithMessage("openid不能为空", c) + return + } + + user, err := miniService.GetUserInfo(openid) + if err != nil { + global.GVA_LOG.Error("获取用户信息失败!", zap.Error(err)) + response.FailWithMessage("获取失败: "+err.Error(), c) + return + } + + response.OkWithData(user, c) +} + +// 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) +} + +// 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) + } +} diff --git a/server/plugin/wechat-integration/initialize/router.go b/server/plugin/wechat-integration/initialize/router.go index 02f7294e..16d924b8 100644 --- a/server/plugin/wechat-integration/initialize/router.go +++ b/server/plugin/wechat-integration/initialize/router.go @@ -25,4 +25,7 @@ func Router(engine *gin.Engine) { // 私有路由(需要认证) wechatRouter.InitRouter(privateGroup) + + // 用户端路由(用户认证) + wechatRouter.InitUserRouter(userGroup) } diff --git a/server/plugin/wechat-integration/router/router.go b/server/plugin/wechat-integration/router/router.go index 591b816b..81bb54ab 100644 --- a/server/plugin/wechat-integration/router/router.go +++ b/server/plugin/wechat-integration/router/router.go @@ -2,6 +2,7 @@ package router import ( "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/api" + "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/api/user" "github.com/gin-gonic/gin" ) @@ -26,15 +27,10 @@ func (w *Router) InitRouter(Router *gin.RouterGroup) { mpDraftApi := api.MpDraftApi{} { - // 微信小程序路由 + // 微信小程序管理端路由 miniGroup := wechatRouter.Group("mini") { - miniGroup.POST("login", miniApi.Login) // 小程序登录 - miniGroup.GET("userinfo", miniApi.GetUserInfo) // 获取用户信息 - miniGroup.PUT("userinfo", miniApi.UpdateUserInfo) // 更新用户信息 - miniGroup.POST("bind-phone", miniApi.BindPhone) // 绑定手机号 - miniGroup.GET("users", miniApi.GetUserList) // 获取用户列表(管理) - miniGroup.GET("check-unionid", miniApi.CheckUnionID) // 检查UnionID + miniGroup.GET("users", miniApi.GetUserList) // 获取用户列表(管理) // 小程序统计 miniGroup.GET("statistics", miniStatisticsApi.GetMiniStatistics) // 获取基础统计数据 @@ -155,9 +151,39 @@ func (w *Router) InitWechatPublicRouter(Router *gin.RouterGroup) { wechatPublicRouter := Router.Group("wechat") webhookApi := api.WebhookApi{} + // 用户端API实例 + miniUserApi := user.MiniUserApi{} { + // 用户端API路由(不需要认证) + userGroup := wechatPublicRouter.Group("user") + { + // 微信小程序用户端路由 + userMiniGroup := userGroup.Group("mini") + { + userMiniGroup.POST("login", miniUserApi.Login) // 小程序登录(不需要认证) + } + } + // 微信公众号Webhook(公开接口,微信服务器调用) wechatPublicRouter.Any("official/webhook", webhookApi.OfficialAccountWebhook) } } + +// InitUserRouter 初始化用户端路由(需要用户认证) +func (w *Router) InitUserRouter(Router *gin.RouterGroup) { + wechatUserRouter := Router.Group("user/wechat") + + // 用户端API实例 + miniUserApi := user.MiniUserApi{} + + // 用户端API路由(需要用户认证) + // 微信小程序用户端路由 + userMiniGroup := wechatUserRouter.Group("mini") + { + userMiniGroup.GET("userinfo", miniUserApi.GetUserInfo) // 获取用户信息 + userMiniGroup.PUT("userinfo", miniUserApi.UpdateUserInfo) // 更新用户信息 + userMiniGroup.POST("bind-phone", miniUserApi.BindPhone) // 绑定手机号 + userMiniGroup.GET("check-unionid", miniUserApi.CheckUnionID) // 检查UnionID + } +}