diff --git a/cmd/bookstore/main.go b/cmd/bookstore/main.go index 51a894f..077831d 100644 --- a/cmd/bookstore/main.go +++ b/cmd/bookstore/main.go @@ -1,9 +1,11 @@ package main import ( + _ "awesomeProject/internal/store" "awesomeProject/server" "awesomeProject/store/factory" "context" + "fmt" "log" "os" "os/signal" @@ -11,6 +13,38 @@ import ( "time" ) +func sendOddNum(cn chan<- int) { + number := 1 + for { + cn <- number + number += 2 // 生成下一个奇数 + fmt.Println("inside:", number) + time.Sleep(1 * time.Second) // 每 1 秒发送一次 + } +} + +func sendEvenNum(cn chan<- int) { + number := 2 + for { + cn <- number + number += 2 + fmt.Println("inside:", number) + time.Sleep(1 * time.Second) + } +} + +//func main() { +// cn := make(chan int, 10) +// go sendOddNum(cn) +// go sendEvenNum(cn) +// +// for { +// num := <-cn +// time.Sleep(5 * time.Second) +// fmt.Println("Received:", num) +// } +//} + func main() { s, err := factory.New("mem") // 创建图书数据存储模块实例 if err != nil { diff --git a/go.mod b/go.mod index 41f7029..10d8343 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,5 @@ module awesomeProject + +go 1.23.2 + +require github.com/gorilla/mux v1.8.1 diff --git a/internal/store/memstore.go b/internal/store/memstore.go index 72440ea..b7baf41 100644 --- a/internal/store/memstore.go +++ b/internal/store/memstore.go @@ -1 +1,63 @@ package store + +import ( + mystore "awesomeProject/store" + factory "awesomeProject/store/factory" + "errors" + "sync" +) + +func init() { + factory.Register("mem", &MemStore{ + books: make(map[string]*mystore.Book), + }) +} + +type MemStore struct { + sync.RWMutex + books map[string]*mystore.Book +} + +func (m *MemStore) Create(book *mystore.Book) error { + if _, ok := m.books[book.Id]; ok { + return errors.New("book is existed") + } + m.books[book.Id] = book + return nil +} + +func (m *MemStore) Update(book *mystore.Book) error { + oldBook, ok := m.books[book.Id] + if !ok { + return errors.New("book not found") + } + oldBook.Name = book.Name + oldBook.Authors = book.Authors + oldBook.Press = book.Press + return nil +} + +func (m *MemStore) Get(s string) (mystore.Book, error) { + oldBook, ok := m.books[s] + if !ok { + return mystore.Book{}, errors.New("book not found") + } else { + return *oldBook, nil + } +} + +func (m *MemStore) GetAll() ([]mystore.Book, error) { + books := make([]mystore.Book, 0, len(m.books)) // 初始化切片,预分配容量 + for _, book := range m.books { + books = append(books, *book) // 解引用并添加到切片中 + } + return books, nil // 返回所有书籍 +} + +func (m *MemStore) Delete(s string) error { + if _, ok := m.books[s]; !ok { + return errors.New("book not found") // 如果书籍未找到,返回错误 + } + delete(m.books, s) // 从 map 中删除书籍 + return nil // 删除成功,返回 nil +} diff --git a/server/middleware/middleware.go b/server/middleware/middleware.go index c870d7c..6c357b7 100644 --- a/server/middleware/middleware.go +++ b/server/middleware/middleware.go @@ -1 +1,30 @@ package middleware + +import ( + "log" + "mime" + "net/http" +) + +func Logging(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + log.Printf("recv a %s request from %s", req.Method, req.RemoteAddr) + next.ServeHTTP(w, req) + }) +} + +func Validating(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + contentType := req.Header.Get("Content-Type") + mediatype, _, err := mime.ParseMediaType(contentType) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if mediatype != "application/json" { + http.Error(w, "invalid Content-Type", http.StatusUnsupportedMediaType) + return + } + next.ServeHTTP(w, req) + }) +} diff --git a/server/server.go b/server/server.go index abb4e43..adb174d 100644 --- a/server/server.go +++ b/server/server.go @@ -1 +1,121 @@ package server + +import ( + "awesomeProject/server/middleware" + "awesomeProject/store" + "context" + "encoding/json" + "github.com/gorilla/mux" + "net/http" + "time" +) + +type BookStoreServer struct { + s store.Store + srv *http.Server +} + +func NewBookStoreServer(addr string, s store.Store) *BookStoreServer { + srv := &BookStoreServer{s: s, srv: &http.Server{Addr: addr}} + router := mux.NewRouter() + router.HandleFunc("/book", srv.createBookHandler).Methods("POST") + router.HandleFunc("/book/{id}", srv.updateBookHandler).Methods("POST") + router.HandleFunc("/book/{id}", srv.getBookHandler).Methods("GET") + router.HandleFunc("/book", srv.getAllBooksHandler).Methods("GET") + router.HandleFunc("/book/{id}", srv.delBookHandler).Methods("DELETE") + srv.srv.Handler = middleware.Logging(middleware.Validating(router)) + return srv +} + +func (bs *BookStoreServer) Shutdown(ctx context.Context) error { + return bs.srv.Shutdown(ctx) +} + +func (bs *BookStoreServer) createBookHandler(w http.ResponseWriter, req *http.Request) { + dec := json.NewDecoder(req.Body) + var book store.Book + if err := dec.Decode(&book); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := bs.s.Create(&book); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +func (bs *BookStoreServer) updateBookHandler(w http.ResponseWriter, req *http.Request) { + dec := json.NewDecoder(req.Body) + var book store.Book + if err := dec.Decode(&book); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := bs.s.Update(&book); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +func (bs *BookStoreServer) getAllBooksHandler(w http.ResponseWriter, req *http.Request) { + books, err := bs.s.GetAll() + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + response(w, books) +} + +func (bs *BookStoreServer) getBookHandler(w http.ResponseWriter, req *http.Request) { + id, ok := mux.Vars(req)["id"] + if !ok { + http.Error(w, "no id found in request", http.StatusBadRequest) + return + } + err := bs.s.Delete(id) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } +} + +func (bs *BookStoreServer) delBookHandler(w http.ResponseWriter, req *http.Request) { + id, ok := mux.Vars(req)["id"] + if !ok { + http.Error(w, "no id found in request", http.StatusBadRequest) + return + } + book, err := bs.s.Get(id) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + response(w, book) +} + +func response(w http.ResponseWriter, v interface{}) { + data, err := json.Marshal(v) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + if _, err := w.Write(data); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func (bs *BookStoreServer) ListenAndServe() (<-chan error, error) { + var err error + errChan := make(chan error) + go func() { + err = bs.srv.ListenAndServe() + errChan <- err + }() + select { + case err = <-errChan: + return nil, err + case <-time.After(time.Second): + return errChan, nil + } +} diff --git a/store/factory/factory.go b/store/factory/factory.go index 95f87f0..3109115 100644 --- a/store/factory/factory.go +++ b/store/factory/factory.go @@ -22,6 +22,7 @@ func Register(name string, p store.Store) { } providers[name] = p } + func New(providerName string) (store.Store, error) { providersMu.RLock() p, ok := providers[providerName]