// Package app1hdl 是小程序 app1 的 HTTP 接入层。 package app1hdl import ( "strconv" "github.com/gin-gonic/gin" "portal-mini-go/internal/middleware" "portal-mini-go/internal/model" "portal-mini-go/internal/pkg/errors" "portal-mini-go/internal/pkg/response" app1svc "portal-mini-go/internal/service/miniapp/app1" ) // Handler app1 请求处理。 type Handler struct { svc *app1svc.Service } func NewHandler(svc *app1svc.Service) *Handler { return &Handler{svc: svc} } type LoginReq struct { Code string `json:"code" binding:"required"` } // Login 处理小程序登录请求。app_id 由 MiniAppAuth 中间件从请求头校验并注入。 func (h *Handler) Login(c *gin.Context) { var req LoginReq if err := c.ShouldBindJSON(&req); err != nil { c.Error(errors.ErrBadRequest) return } appID := middleware.AppID(c) token, u, err := h.svc.Login(c.Request.Context(), appID, req.Code) if err != nil { c.Error(err) return } response.OK(c, gin.H{"token": token, "user": toProfileView(u)}) } // Logout 注销当前用户会话。需在用户会话中间件通过后调用。 func (h *Handler) Logout(c *gin.Context) { if err := h.svc.Logout(c.Request.Context(), middleware.SessionToken(c)); err != nil { c.Error(err) return } response.OK(c, nil) } // Profile 获取当前登录用户资料。 func (h *Handler) Profile(c *gin.Context) { u, err := h.svc.GetProfile(c.Request.Context(), middleware.MiniID(c), middleware.UserID(c)) if err != nil { c.Error(err) return } response.OK(c, toProfileView(u)) } type UpdateProfileReq struct { Nickname string `json:"nickname"` Avatar string `json:"avatar"` } // UpdateProfile 更新当前登录用户的昵称/头像,留空字段不更新。 func (h *Handler) UpdateProfile(c *gin.Context) { var req UpdateProfileReq if err := c.ShouldBindJSON(&req); err != nil { c.Error(errors.ErrBadRequest) return } if err := h.svc.UpdateProfile(c.Request.Context(), middleware.MiniID(c), middleware.UserID(c), req.Nickname, req.Avatar); err != nil { c.Error(err) return } response.OK(c, nil) } // GetUser 获取同小程序下指定用户(仅公开字段)。 func (h *Handler) GetUser(c *gin.Context) { id, ok := parseUserID(c) if !ok { return } u, err := h.svc.GetUser(c.Request.Context(), middleware.MiniID(c), id) if err != nil { c.Error(err) return } response.OK(c, toPublicUserView(u)) } // ListUsers 分页列出同小程序下的用户。 func (h *Handler) ListUsers(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("size", "20")) list, total, err := h.svc.ListUsers(c.Request.Context(), middleware.MiniID(c), page, size) if err != nil { c.Error(err) return } views := make([]PublicUserView, 0, len(list)) for i := range list { views = append(views, toPublicUserView(&list[i])) } response.OK(c, gin.H{"list": views, "total": total, "page": page, "size": size}) } // ProfileView 当前用户自身的资料视图(含自身 openid/unionid)。 type ProfileView struct { ID uint64 `json:"id"` OpenID string `json:"openid"` UnionID string `json:"union_id"` Nickname string `json:"nickname"` Avatar string `json:"avatar"` CreatedAt int64 `json:"created_at"` } // PublicUserView 对外的用户公开视图,剔除 openid/unionid 等敏感字段。 type PublicUserView struct { ID uint64 `json:"id"` Nickname string `json:"nickname"` Avatar string `json:"avatar"` } func toProfileView(u *model.User) ProfileView { return ProfileView{ ID: u.ID, OpenID: u.OpenID, UnionID: u.UnionID, Nickname: u.Nickname, Avatar: u.Avatar, CreatedAt: u.CreatedAt.Unix(), } } func toPublicUserView(u *model.User) PublicUserView { return PublicUserView{ID: u.ID, Nickname: u.Nickname, Avatar: u.Avatar} } func parseUserID(c *gin.Context) (uint64, bool) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.Error(errors.ErrBadRequest) return 0, false } return id, true } // Register 注册 app1 的路由。g 已带 /api/ma 前缀与 MiniAppAuth 中间件(校验请求头 app_id)。 // userAuth 为用户会话中间件,用于保护需要登录态的接口。 func Register(g *gin.RouterGroup, h *Handler, userAuth gin.HandlerFunc) { g.POST("/login", h.Login) g.POST("/logout", userAuth, h.Logout) g.GET("/profile", userAuth, h.Profile) g.PUT("/profile", userAuth, h.UpdateProfile) g.GET("/users/:id", userAuth, h.GetUser) g.GET("/users", userAuth, h.ListUsers) }