package handler import ( "errors" "net/http" "git.yuntuo.hk/k2safe/go-omni" "git.yuntuo.hk/k2safe/go-omni/examples/reference/model" "git.yuntuo.hk/k2safe/go-omni/examples/reference/service" ) type Users struct { service *service.Users } func NewUsers(service *service.Users) *Users { return &Users{service: service} } func (h *Users) RegisterRoutes(api *omni.RouterGroup, writeMiddleware, deleteMiddleware omni.Middleware) { users := api.Group("/users") users.POST("", h.Create, writeMiddleware) users.GET("", h.List) users.GET("/:id", h.Get) users.PUT("/:id", h.Update, writeMiddleware) users.DELETE("/:id", h.Delete, writeMiddleware, deleteMiddleware) } // HTTP-specific validation tags stay here, outside the business model. type userBody struct { Name string `json:"name" binding:"required,max=100"` Email string `json:"email" binding:"required,email,max=254"` } type userPath struct { ID int64 `uri:"id" binding:"gt=0"` } type listQuery struct { Page int `form:"page,default=1" binding:"gte=1"` PageSize int `form:"pageSize,default=20" binding:"gte=1,lte=100"` } func (h *Users) Create(ctx *omni.Context) error { var body userBody if err := ctx.BindJSON(&body); err != nil { return err } user, err := h.service.Create(ctx.Request().Context(), model.UserInput(body)) if err != nil { return userError(err) } return ctx.Created(user) } func (h *Users) Get(ctx *omni.Context) error { var path userPath if err := ctx.BindURI(&path); err != nil { return err } user, err := h.service.Get(ctx.Request().Context(), path.ID) if err != nil { return userError(err) } return ctx.OK(user) } func (h *Users) List(ctx *omni.Context) error { var query listQuery if err := ctx.BindQuery(&query); err != nil { return err } page, err := h.service.List(ctx.Request().Context(), query.Page, query.PageSize) if err != nil { return userError(err) } return ctx.OK(page) } func (h *Users) Update(ctx *omni.Context) error { var path userPath if err := ctx.BindURI(&path); err != nil { return err } var body userBody if err := ctx.BindJSON(&body); err != nil { return err } user, err := h.service.Update(ctx.Request().Context(), path.ID, model.UserInput(body)) if err != nil { return userError(err) } return ctx.OK(user) } func (h *Users) Delete(ctx *omni.Context) error { var path userPath if err := ctx.BindURI(&path); err != nil { return err } if err := h.service.Delete(ctx.Request().Context(), path.ID); err != nil { return userError(err) } return ctx.NoContent() } func userError(err error) error { switch { case errors.Is(err, model.ErrUserNotFound): return omni.NotFound("USER_NOT_FOUND", "user was not found").WithCause(err) case errors.Is(err, model.ErrEmailTaken): return omni.NewError(http.StatusConflict, "EMAIL_TAKEN", "email is already in use").WithCause(err) case errors.Is(err, model.ErrInvalidUser): return omni.BadRequest("INVALID_USER", "name or email is invalid").WithCause(err) case errors.Is(err, model.ErrInvalidPage): return omni.BadRequest("INVALID_QUERY", "pagination is invalid").WithCause(err) default: // GoOmni masks unexpected errors; internal details stay in server logs. return err } }