商品价格计划
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pricing/app/net.go

275 lines
5.8 KiB

package app
import (
"encoding/json"
"github.com/go-chi/chi/v5"
"net/http"
"strconv"
"sync"
)
type TapFunc[V any] func(V) V
type Convertor[V any] func(s string) (V, error)
type HandlerFunc func(w *ResponseWriter, r *Request)
type ParamGetter func(key string) (string, bool)
// Binder 参数绑定接口
type Binder interface {
Bind(r *Request) error
}
func GetParam[V any](r *Params, key string, def V, convertor Convertor[V], taps []TapFunc[V]) V {
var v V
if str, ok := r.Get(key); ok {
if x, err := convertor(str); err != nil {
v = def
} else {
v = x
}
} else {
v = def
}
for _, tap := range taps {
v = tap(v)
}
return v
}
type Params struct {
pg ParamGetter
}
func NewParams(pg ParamGetter) *Params {
return &Params{pg}
}
func NewPathParams(r *Request) *Params {
return NewParams(func(key string) (string, bool) {
ctx := chi.RouteContext(r.Context())
for k := len(ctx.URLParams.Keys) - 1; k >= 0; k-- {
if ctx.URLParams.Keys[k] == key {
return ctx.URLParams.Values[k], true
}
}
return "", false
})
}
func NewQueryParams(r *Request) *Params {
return NewParams(func(key string) (string, bool) {
if values, ok := r.URL.Query()[key]; ok && len(values) > 0 {
return values[0], true
} else {
return "", false
}
})
}
func (u *Params) Get(key string) (string, bool) {
return u.pg(key)
}
func (u *Params) Value(key string, taps ...TapFunc[string]) string {
val, _ := u.Get(key)
for _, tap := range taps {
val = tap(val)
}
return val
}
func (u *Params) Int(key string, def int, taps ...TapFunc[int]) int {
return GetParam[int](u, key, def, func(s string) (int, error) {
n, e := strconv.ParseInt(s, 10, 64)
return int(n), e
}, taps)
}
func (u *Params) Int32(key string, def int32, taps ...TapFunc[int32]) int32 {
return GetParam[int32](u, key, def, func(s string) (int32, error) {
n, e := strconv.ParseInt(s, 10, 64)
return int32(n), e
}, taps)
}
func (u *Params) Int64(key string, def int64, taps ...TapFunc[int64]) int64 {
return GetParam[int64](u, key, def, func(s string) (int64, error) {
return strconv.ParseInt(s, 10, 64)
}, taps)
}
func (u *Params) Uint(key string, def uint, taps ...TapFunc[uint]) uint {
return GetParam[uint](u, key, def, func(s string) (uint, error) {
n, e := strconv.ParseUint(s, 10, 64)
return uint(n), e
}, taps)
}
func (u *Params) Uint32(key string, def uint32, taps ...TapFunc[uint32]) uint32 {
return GetParam[uint32](u, key, def, func(s string) (uint32, error) {
n, e := strconv.ParseUint(s, 10, 64)
return uint32(n), e
}, taps)
}
func (u *Params) Uint64(key string, def uint64, taps ...TapFunc[uint64]) uint64 {
return GetParam[uint64](u, key, def, func(s string) (uint64, error) {
return strconv.ParseUint(s, 10, 64)
}, taps)
}
func (u *Params) Float32(key string, def float32, taps ...TapFunc[float32]) float32 {
return GetParam[float32](u, key, def, func(s string) (float32, error) {
f, e := strconv.ParseFloat(s, 32)
return float32(f), e
}, taps)
}
func (u *Params) Float64(key string, def float64, taps ...TapFunc[float64]) float64 {
return GetParam[float64](u, key, def, func(s string) (float64, error) {
return strconv.ParseFloat(s, 32)
}, taps)
}
type Request struct {
*http.Request
*Params
pathParams *Params
queryParams *Params
}
func NewRequest(r *http.Request) *Request {
return &Request{
Request: r,
Params: NewParams(func(key string) (string, bool) {
_ = r.ParseForm()
if values, ok := r.Form[key]; ok && len(values) > 0 {
return values[0], true
}
ctx := chi.RouteContext(r.Context())
for k := len(ctx.URLParams.Keys) - 1; k >= 0; k-- {
if ctx.URLParams.Keys[k] == key {
return ctx.URLParams.Values[k], true
}
}
return "", false
}),
}
}
func (r *Request) get(key string) (string, bool) {
_ = r.ParseForm()
values, ok := r.PostForm[key]
if !ok {
values, ok = r.Form[key]
}
if !ok {
values, ok = r.URL.Query()[key]
}
if ok {
return values[0], true
}
return "", false
}
func (r *Request) PathParams() *Params {
if r.pathParams == nil {
r.pathParams = NewPathParams(r)
}
return r.pathParams
}
func (r *Request) QueryParams() *Params {
if r.queryParams == nil {
r.queryParams = NewQueryParams(r)
}
return r.queryParams
}
type ResponseWriter struct {
http.ResponseWriter
mutex sync.RWMutex
sent bool
}
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
return &ResponseWriter{
ResponseWriter: w,
mutex: sync.RWMutex{},
sent: false,
}
}
func (w *ResponseWriter) IsSent() bool {
w.mutex.RLock()
defer w.mutex.RUnlock()
return w.sent
}
func (w *ResponseWriter) Send(status int, body any) {
w.mutex.Lock()
defer w.mutex.Unlock()
if w.sent {
LogWarning("the response writer was sent")
return
}
buf, err := json.Marshal(body)
if err != nil {
w.Error(err)
return
}
w.sent = true
w.WriteHeader(status)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
if _, err := w.Write(buf); err != nil {
LogError(err)
}
}
func (w *ResponseWriter) Error(err error) {
var status int
var code int
var message string
if ex, ok := err.(*Error); ok {
status = http.StatusBadRequest
code = ex.Code
message = ex.Message
if ex.Status > 0 {
status = ex.Status
}
} else {
status = http.StatusInternalServerError
code = -1
message = err.Error()
LogError(err)
}
if len(message) == 0 {
message = http.StatusText(status)
}
w.Send(status, map[string]any{
"code": code,
"message": message,
"success": code == 0,
})
}
func (w *ResponseWriter) Ok(data any, message ...string) {
info := map[string]any{
"code": 0,
"message": "ok",
"success": true,
"data": data,
}
if len(message) > 0 {
info["message"] = message[0]
}
w.Send(http.StatusOK, info)
}
func Handler(hf HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
hf(NewResponseWriter(w), NewRequest(r))
}
}