package vo import ( "net/http" "github.com/gin-gonic/gin" "hy2xs-admin/model/constant" ) // FieldError — одна причина отказа. // // `Field` заполняется, когда причина относится к конкретному полю формы, и // пуст для отказов уровня операции. `Params` несёт числа правила (границы // длины, допустимые значения), чтобы панель могла составить точную фразу, не // заводя у себя вторую копию этих чисел. type FieldError struct { Code string `json:"code"` Field string `json:"field,omitempty"` Message string `json:"message"` Params map[string]string `json:"params,omitempty"` } type result struct { Code int `json:"code"` Type string `json:"type"` Message string `json:"message"` Errors []FieldError `json:"errors,omitempty"` Data interface{} `json:"data"` } const ( TypeSuccess = "ok" TypeError = "no" ) func Success(data interface{}, c *gin.Context) { c.JSON(http.StatusOK, result{ Code: constant.CodeSuccess, Type: TypeSuccess, Data: data, }) } // FailWith — единственное место, где формируется ответ об ошибке. // // Код передаётся аргументом. Раньше он ВЫВОДИЛСЯ здесь сравнением текста // сообщения с тремя известными строками: // // if constant.UnauthorizedError == message { code = ... } // // Это тот же антипаттерн, который запрещён панели, только на сервере: смысл // ответа определялся совпадением литерала. Следствие было не теоретическим — // истёкший токен возвращал `token expired`, под условия не подходил и уезжал // как обычная системная ошибка с кодом 50000. Панель показывала оператору // голый тост и не понимала, что сессия кончилась: ветка входа заново не // срабатывала никогда. func FailWith(code int, message string, fieldErrors []FieldError, c *gin.Context) { c.JSON(http.StatusOK, result{ Code: code, Type: TypeError, Message: message, Errors: fieldErrors, Data: nil, }) } // Fail — отказ уровня операции: правила соблюдены, выполнить нельзя. func Fail(message string, c *gin.Context) { FailWith(constant.CodeSysError, message, nil, c) } // FailDomain — тот же отказ, но с машиночитаемым кодом причины. func FailDomain(code string, message string, c *gin.Context) { FailWith(constant.CodeSysError, message, []FieldError{{ Code: code, Message: message, }}, c) } // FailField — отказ уровня операции, привязанный к полю формы. func FailField(code string, field string, message string, c *gin.Context) { FailWith(constant.CodeSysError, message, []FieldError{{ Code: code, Field: field, Message: message, }}, c) } // FailValidation — вход не прошёл проверку правил. func FailValidation(message string, fieldErrors []FieldError, c *gin.Context) { FailWith(constant.CodeInvalidError, message, fieldErrors, c) } // FailUnauthorized — вход требуется или сессия больше не действует. // // Причина передаётся кодом: панель по-разному ведёт себя, когда токена нет // вовсе и когда он только что истёк под руками у оператора. func FailUnauthorized(code string, message string, c *gin.Context) { FailWith(constant.CodeUnauthorizedError, message, []FieldError{{ Code: code, Message: message, }}, c) } // FailForbidden — вход выполнен, но прав недостаточно. func FailForbidden(message string, c *gin.Context) { FailWith(constant.CodeForbiddenError, message, nil, c) }