commit
b41fdd7816
@ -0,0 +1,5 @@ |
||||
.idea |
||||
tmp |
||||
.air.toml |
||||
*.db |
||||
*.log |
@ -0,0 +1,82 @@ |
||||
package app |
||||
|
||||
import ( |
||||
"gorm.io/driver/sqlite" |
||||
"gorm.io/gorm" |
||||
"math" |
||||
"strings" |
||||
) |
||||
|
||||
var DB *gorm.DB |
||||
|
||||
func ConfigGormDB() { |
||||
var err error |
||||
DB, err = gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{}) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
if err = DB.AutoMigrate(&User{}, &Goods{}, &Price{}); err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
type User struct { |
||||
gorm.Model |
||||
Name string `json:"name"` // 用户名称
|
||||
PhoneNumber string `json:"phone_number"` // 用户手机
|
||||
Password string `json:"password"` // 登录密码
|
||||
} |
||||
|
||||
// Goods 商品
|
||||
type Goods struct { |
||||
gorm.Model |
||||
Name string `json:"name"` // 商品名称
|
||||
Price float32 `json:"price"` // 商品当前价格
|
||||
Prices []Price `json:"prices,omitempty"` // 商品价格列表
|
||||
} |
||||
|
||||
// Price 商品价格
|
||||
type Price struct { |
||||
gorm.Model |
||||
GoodsID uint `json:"goods_id"` // 管理商品
|
||||
Price float32 `json:"price"` // 商品价格
|
||||
} |
||||
|
||||
func Paginate(r *Request) func(db *gorm.DB) *gorm.DB { |
||||
return func(db *gorm.DB) *gorm.DB { |
||||
page := r.Int("page", 1, func(p int) int { |
||||
return int(math.Max(float64(p), 1)) |
||||
}) |
||||
perPage := r.Int("per_page", 30, func(i int) int { |
||||
return int(math.Max(float64(i), 1)) |
||||
}) |
||||
offset := (page - 1) * perPage |
||||
return db.Offset(offset).Limit(perPage) |
||||
} |
||||
} |
||||
|
||||
func Search(r *Request, key, query string) func(db *gorm.DB) *gorm.DB { |
||||
return func(db *gorm.DB) *gorm.DB { |
||||
if keyword, ok := r.Get(key); ok { |
||||
return db.Where(query, keyword) |
||||
} else { |
||||
return db |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TimeRange(r *Request, column string) func(db *gorm.DB) *gorm.DB { |
||||
return func(db *gorm.DB) *gorm.DB { |
||||
var queries []string |
||||
var args []any |
||||
if val, ok := r.Get("start_time"); ok { |
||||
queries = append(queries, column+" >= ?") |
||||
args = append(args, val) |
||||
} |
||||
if val, ok := r.Get("end_time"); ok { |
||||
queries = append(queries, column+" <= ?") |
||||
args = append(args, val) |
||||
} |
||||
return db.Where(strings.Join(queries, " AND "), args...) |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
package app |
||||
|
||||
type Error struct { |
||||
Status int `json:"-"` |
||||
Code int `json:"code"` |
||||
Message string `json:"message"` |
||||
} |
||||
|
||||
func NewError(code int, message string) *Error { |
||||
return &Error{Code: code, Message: message} |
||||
} |
||||
|
||||
func (e *Error) Error() string { |
||||
return e.Message |
||||
} |
@ -0,0 +1,122 @@ |
||||
package app |
||||
|
||||
import ( |
||||
"fmt" |
||||
"log" |
||||
"os" |
||||
"path" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
const ( |
||||
LogWhenSecond = iota |
||||
LogWhenMinute |
||||
LogWhenHour |
||||
LogWhenDay |
||||
) |
||||
|
||||
var ( |
||||
lg func(level string, data any) |
||||
fd *os.File |
||||
) |
||||
|
||||
func ConfigLogger(file string, when int8) { |
||||
// 解决 Windows 电脑路径问题
|
||||
file = strings.ReplaceAll(file, "\\", "/") |
||||
if err := os.MkdirAll(path.Dir(file), 0777); err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
var interval int64 |
||||
var suffix string |
||||
switch when { |
||||
case LogWhenSecond: |
||||
interval = 1 |
||||
suffix = "2006-01-02_15-04-05" |
||||
case LogWhenMinute: |
||||
interval = 60 |
||||
suffix = "2006-01-02_15-04" |
||||
case LogWhenHour: |
||||
interval = 3600 |
||||
suffix = "2006-01-02_15" |
||||
case LogWhenDay: |
||||
interval = 3600 * 24 |
||||
suffix = "2006-01-02" |
||||
default: |
||||
panic(fmt.Errorf("invalid when rotate: %d", when)) |
||||
} |
||||
|
||||
var err error |
||||
fd, err = os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
fInfo, err := fd.Stat() |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
rolloverAt := fInfo.ModTime().Unix() + interval |
||||
locker := sync.RWMutex{} |
||||
|
||||
resolveRotatedFile := func(suffix string) string { |
||||
filenameWithSuffix := path.Base(file) |
||||
fileSuffix := path.Ext(filenameWithSuffix) |
||||
filename := strings.TrimSuffix(filenameWithSuffix, fileSuffix) |
||||
return path.Dir(file) + "/" + filename + "." + suffix + fileSuffix |
||||
} |
||||
|
||||
lg = func(level string, data any) { |
||||
now := time.Now() |
||||
|
||||
if rolloverAt <= now.Unix() { |
||||
locker.Lock() |
||||
defer locker.Unlock() |
||||
|
||||
fName := resolveRotatedFile(now.Format(suffix)) |
||||
//fName := file + now.Format(suffix)
|
||||
_ = fd.Close() |
||||
e := os.Rename(file, fName) |
||||
if e != nil { |
||||
log.Println(e) |
||||
return |
||||
} |
||||
fd, err = os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) |
||||
if err != nil { |
||||
log.Println(e) |
||||
return |
||||
} |
||||
now = time.Now() |
||||
rolloverAt = now.Unix() + interval |
||||
} |
||||
|
||||
t := now.Format("2006-01-02 15:04:05") |
||||
_, err := fmt.Fprintf(fd, "%s [%s] %v", t, level, data) |
||||
if err != nil { |
||||
log.Println(err) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func LogError(data any) { |
||||
lg("ERROR", data) |
||||
} |
||||
|
||||
func LogWarning(data any) { |
||||
lg("WARNING", data) |
||||
} |
||||
|
||||
func LogInfo(data any) { |
||||
lg("INFO", data) |
||||
} |
||||
|
||||
func LogoDebug(data any) { |
||||
lg("DEBUG", data) |
||||
} |
||||
|
||||
func LogFatal(data any) { |
||||
lg("FATAL", data) |
||||
} |
@ -0,0 +1,268 @@ |
||||
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) |
||||
|
||||
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 { |
||||
LogError(err) |
||||
w.Fail(http.StatusInternalServerError, -1, err.Error()) |
||||
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) Fail(status int, code int, message ...string) { |
||||
info := map[string]any{"code": code, "message": ""} |
||||
if len(message) > 0 && len(message[0]) > 0 { |
||||
info["message"] = message[0] |
||||
} else { |
||||
info["message"] = http.StatusText(status) |
||||
} |
||||
w.Send(status, info) |
||||
} |
||||
|
||||
func (w *ResponseWriter) Error(err error) { |
||||
if ex, ok := err.(*Error); ok { |
||||
status := http.StatusBadRequest |
||||
if ex.Status > 0 { |
||||
status = ex.Status |
||||
} |
||||
w.Fail(status, ex.Code, ex.Message) |
||||
} else { |
||||
LogError(err) |
||||
w.Fail(http.StatusInternalServerError, -1, err.Error()) |
||||
} |
||||
} |
||||
|
||||
func (w *ResponseWriter) Ok(data any, message ...string) { |
||||
info := map[string]any{ |
||||
"code": 0, |
||||
"message": "ok", |
||||
"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)) |
||||
} |
||||
} |
||||
|
@ -0,0 +1,220 @@ |
||||
package app |
||||
|
||||
import ( |
||||
"errors" |
||||
"github.com/go-chi/chi/v5" |
||||
"gorm.io/gorm" |
||||
"net/http" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
func userInfoFromRequest(r *Request) (*User, error) { |
||||
var user User |
||||
|
||||
if name, ok := r.Get("name"); ok && len(name) > 0 { |
||||
user.Name = name |
||||
} else { |
||||
return nil, NewError(1, "缺少用户名称") |
||||
} |
||||
|
||||
if phoneNumber, ok := r.Get("phone_number"); ok && len(phoneNumber) > 0 { |
||||
if len(phoneNumber) != 11 { |
||||
return nil, NewError(2, "手机号码格式错误") |
||||
} |
||||
user.PhoneNumber = phoneNumber |
||||
} else { |
||||
return nil, NewError(2, "缺少手机号码") |
||||
} |
||||
|
||||
if password, ok := r.Get("password"); ok && len(password) > 0 { |
||||
if len(password) < 6 { |
||||
return nil, NewError(2, "密码太短") |
||||
} |
||||
user.Password = password |
||||
} else { |
||||
return nil, NewError(2, "缺少密码") |
||||
} |
||||
|
||||
return &user, nil |
||||
} |
||||
|
||||
// CreateUser 创建用户
|
||||
func CreateUser(w *ResponseWriter, r *Request) { |
||||
user, err := userInfoFromRequest(r) |
||||
if err != nil { |
||||
w.Error(err) |
||||
return |
||||
} |
||||
|
||||
var count int64 |
||||
if err = DB.Model(&User{}).Where("phone_number = ?", user.PhoneNumber).Count(&count).Error; err != nil { |
||||
w.Error(err) |
||||
return |
||||
} |
||||
if count > 0 { |
||||
w.Error(NewError(2, "手机号码已经被使用了")) |
||||
return |
||||
} |
||||
|
||||
if err = DB.Create(&user).Error; err != nil { |
||||
w.Error(err) |
||||
} else { |
||||
w.Ok(user, "创建用户成功") |
||||
} |
||||
} |
||||
|
||||
// UpdateUser 更新用户
|
||||
func UpdateUser(w *ResponseWriter, r *Request) { |
||||
id := chi.URLParam(r.Request, "id") |
||||
if len(id) == 0 { |
||||
w.Error(NewError(1, "缺少用户ID")) |
||||
} |
||||
user, err := userInfoFromRequest(r) |
||||
if err != nil { |
||||
w.Error(err) |
||||
return |
||||
} |
||||
|
||||
} |
||||
|
||||
// DeleteUser 删除用户
|
||||
func DeleteUser(w *ResponseWriter, r *Request) { |
||||
|
||||
} |
||||
|
||||
// ListUser 用户列表
|
||||
func ListUser(w *ResponseWriter, r *Request) { |
||||
|
||||
} |
||||
|
||||
// Login 用户登录
|
||||
func Login(w *ResponseWriter, r *Request) { |
||||
|
||||
} |
||||
|
||||
// CreateGoods 创建商品
|
||||
func CreateGoods(w *ResponseWriter, r *Request) { |
||||
name := r.Value("name") |
||||
price := r.Float32("price", 0) |
||||
if len(name) == 0 { |
||||
w.Fail(http.StatusBadRequest, 1, "商品名称错误") |
||||
return |
||||
} |
||||
if price <= 0 { |
||||
w.Fail(http.StatusBadRequest, 2, "商品价格错误") |
||||
return |
||||
} |
||||
var goods Goods |
||||
err := DB.First(&goods, "name = ?", name).Error |
||||
if errors.Is(err, gorm.ErrRecordNotFound) { |
||||
goods = Goods{ |
||||
Name: name, |
||||
Price: price, |
||||
} |
||||
err = DB.Create(&goods).Error |
||||
if err != nil { |
||||
w.Fail(http.StatusBadRequest, 3, "创建商品失败") |
||||
} else { |
||||
w.Ok(goods) |
||||
} |
||||
} else if err != nil { |
||||
LogError(err) |
||||
w.Fail(http.StatusBadRequest, 4, "商品价格错误") |
||||
} else { |
||||
w.Fail(http.StatusBadRequest, 5, "商品已经存在") |
||||
} |
||||
} |
||||
|
||||
// UpdateGoods 更新产品信息
|
||||
func UpdateGoods(w *ResponseWriter, r *Request) { |
||||
name := r.Value("name") |
||||
price := r.Float32("price", 0) |
||||
id := uint(r.Uint64("id", 0)) |
||||
if len(name) == 0 { |
||||
w.Fail(http.StatusBadRequest, 1, "商品名称错误") |
||||
return |
||||
} |
||||
if price <= 0 { |
||||
w.Fail(http.StatusBadRequest, 2, "商品价格错误") |
||||
return |
||||
} |
||||
var goods Goods |
||||
err := DB.First(&goods, "id = ?", id).Error |
||||
if errors.Is(err, gorm.ErrRecordNotFound) { |
||||
w.Fail(http.StatusBadRequest, 2, "商品不存在") |
||||
} else if err != nil { |
||||
LogError(err) |
||||
w.Fail(http.StatusBadRequest, 3, err.Error()) |
||||
} else { |
||||
// 商品名称不能重复
|
||||
err = DB.Where("id <> ?", id).First(&Goods{}, "name = ?", name).Error |
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { |
||||
LogError(err) |
||||
w.Fail(http.StatusBadRequest, 4, err.Error()) |
||||
return |
||||
} |
||||
if goods.Name != name || goods.Price != price { |
||||
goods.Name = name |
||||
goods.Price = price |
||||
err = DB.Save(&goods).Error |
||||
if err != nil { |
||||
LogError(err) |
||||
w.Fail(http.StatusBadRequest, 5, err.Error()) |
||||
return |
||||
} |
||||
} |
||||
w.Ok(goods, "修改成功") |
||||
} |
||||
} |
||||
|
||||
// GetGoodsList 查询商品列表
|
||||
func GetGoodsList(w *ResponseWriter, r *Request) { |
||||
search := func(db *gorm.DB) *gorm.DB { |
||||
return db. |
||||
Model(&Goods{}). |
||||
Scopes(TimeRange(r, "created_at")). |
||||
Scopes(Paginate(r)). |
||||
Scopes(Search(r, "name", "name LIKE ?")) |
||||
} |
||||
var goodsList []Goods |
||||
var total int64 |
||||
var err error |
||||
if err = DB.Scopes(search).Count(&total).Error; err == nil { |
||||
err = DB.Scopes(search).Find(&goodsList).Error |
||||
} |
||||
if err != nil { |
||||
w.Fail(http.StatusInternalServerError, 1, err.Error()) |
||||
} else { |
||||
w.Ok(map[string]any{ |
||||
"list": goodsList, |
||||
"total": total, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func GetGoodsPrices(w *ResponseWriter, r *Request) { |
||||
id := uint(r.Uint64("id", 0)) |
||||
var goods Goods |
||||
if err := DB.Scopes(func(db *gorm.DB) *gorm.DB { |
||||
var queries []string |
||||
var args []any |
||||
if val, ok := r.Get("start_time"); ok { |
||||
queries = append(queries, "created_at >= ?") |
||||
args = append(args, val) |
||||
} |
||||
if val, ok := r.Get("end_time"); ok { |
||||
queries = append(queries, "created_at <= ?") |
||||
args = append(args, val) |
||||
} |
||||
if len(queries) == 0 { |
||||
return db.Preload("Prices") |
||||
} |
||||
args = append([]any{strings.Join(queries, " AND ")}, args...) |
||||
return db.Preload("Prices", args...) |
||||
}).First(&goods, "id = ?", id).Error; err != nil { |
||||
w.Fail(http.StatusInternalServerError, 1, err.Error()) |
||||
} else { |
||||
w.Ok(goods) |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
module hupeh.vip/pricing |
||||
|
||||
go 1.19 |
||||
|
||||
require ( |
||||
github.com/go-chi/chi/v5 v5.0.8 |
||||
gorm.io/driver/sqlite v1.4.3 |
||||
gorm.io/gorm v1.24.2 |
||||
) |
||||
|
||||
require ( |
||||
github.com/jinzhu/inflection v1.0.0 // indirect |
||||
github.com/jinzhu/now v1.1.5 // indirect |
||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect |
||||
) |
@ -0,0 +1,14 @@ |
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= |
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= |
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= |
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= |
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= |
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= |
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= |
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= |
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= |
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= |
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= |
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= |
||||
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0= |
||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= |
@ -0,0 +1,24 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"github.com/go-chi/chi/v5" |
||||
"github.com/go-chi/chi/v5/middleware" |
||||
"hupeh.vip/pricing/app" |
||||
"log" |
||||
"net/http" |
||||
) |
||||
func main() { |
||||
app.ConfigLogger("debug.log", app.LogWhenMinute) |
||||
app.ConfigGormDB() |
||||
|
||||
r := chi.NewRouter() |
||||
r.Use(middleware.Logger) |
||||
r.Get("/goods", app.Handler(app.GetGoodsList)) |
||||
r.Post("/goods", app.Handler(app.CreateGoods)) |
||||
r.Get("/goods/:id/prices", app.Handler(app.GetGoodsPrices)) |
||||
r.Post("/goods/:id", app.Handler(app.UpdateGoods)) |
||||
|
||||
if err := http.ListenAndServe(":3000", r); err != nil { |
||||
log.Fatalln(err) |
||||
} |
||||
} |
Loading…
Reference in new issue