こんにちは、KOUKIです。
GolangのWebフレームワークであるfiberを使って、APIを開発しています。
前回は、キャッシュの更新処理を実装しました。
今回は、LinkとStatsの実装を行います。
尚、本記事は「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
<目次>
前回
作るもの
Ambassdor機能を作りたいと思います。エンドポイントは、次の通りです。
- GET /api/ambassador/products/frontend
- GET /api/ambassador/products/backend
- POST /api/ambassador/links
- GET /api/ambassador/stats
- GET /api/ambassador/rankings
今回は、「/api/ambassador/links」、「/api/ambassador/stats」への処理を実装します。
Linkの作成
以前、Linkモデルを作成しました。
1 2 3 4 5 6 7 8 |
type Link struct { Model Code string `json:"code"` UserID uint `json:"user_id"` User User `json:"user" gorm:"foreignKey:UserID"` Products []Product `json:"products" gorm:"many2many:link_products"` Orders []Order `json:"orders" gorm:"-"` } |
ご覧の通り、UserやProduct、Orderなどの情報を紐付ける構造体です。
この構造体を作成する処理を実装します。
ルートの追加
「/api/ambassador/links」へのルートを追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// routes/routes.go ... func Setup(app *fiber.App) { // Group api := app.Group("api") // admin ... // Ambassador ... ambassadorAuthentication.Post("links", controllers.CreateLink) } |
コントローラーの追加
コントローラーを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// linkController.go package controllers import ( "admin/src/database" "admin/src/middleware" "admin/src/models" "strconv" "github.com/bxcodec/faker/v3" "github.com/gofiber/fiber/v2" ) func Link(ctx *fiber.Ctx) error {...} type CreateLinkRequest struct { ProductIDs []int } func CreateLink(ctx *fiber.Ctx) error { var request CreateLinkRequest // リクエストデータからProductIDを取得 if err := ctx.BodyParser(&request); err != nil { return err } // ユーザーIDを取得 id, _ := middleware.GetUserID(ctx) // リンク作成 link := models.Link{ UserID: id, Code: faker.Username(), } // プロダクトID, ユーザーIDを紐づける for _, productID := range request.ProductIDs { product := models.Product{} product.ID = uint(productID) link.Products = append(link.Products, product) } // DB保存 database.DB.Create(&link) return ctx.JSON(link) } |
検証
以下のパラメーターで、検証を行いましょう。
- URL: http://localhost:8000/api/ambassador/links
- 形式: POST
1 2 3 |
{ "productIDs": [2, 4, 5] } |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 取得データ(途中何度かAPIへリクエストを送っているので、idが3になっています) { "id": 3, "code": "udyoSQo", "user_id": 71, "user":{ "id": 0, "first_name": "", "last_name": "", "email": "" }, "products":[ {"id": 2, "title": "", "description": "", "image": "",…}, {"id": 4, "title": "", "description": "", "image": "",…}, {"id": 5, "title": "", "description": "", "image": "",…} ], "orders": null } |
OKですね。
統合情報の取得
次の情報を返却するAPIを実装します。
- CODE — ユーザーID
- COUNT — オーダー数
- REVENUE — 合計金額
ルートの追加
「/api/ambassador/stats」へのルートを追加しましょう。
1 2 3 4 5 6 7 8 |
// routes/routes.go ... func Setup(app *fiber.App) { ... // Ambassador ... ambassadorAuthentication.Get("stats", controllers.Stats) } |
コントローラーの追加
コントローラーを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// linkController.go ... func Stats(ctx *fiber.Ctx) error { // ユーザーIDを取得 id, _ := middleware.GetUserID(ctx) // DB検索 var links []models.Link database.DB.Find(&links, models.Link{ UserID: id, }) var result []interface{} var orders []models.Order for _, link := range links { // OrderItemsを先にロードしてからOrderデータを検索 database.DB.Preload("OrderItems").Find(&orders, &models.Order{ Code: link.Code, Complete: true, }) var revenue float64 = 0 for _, order := range orders { revenue += order.GetTotal() } result = append(result, fiber.Map{ "code": link.Code, "count": len(orders), "revenue": revenue, }) } return ctx.JSON(result) } |
検証
以下のパラメーターで、検証を行いましょう。
- URL: http://localhost:8000/api/ambassador/stats
- 形式: GET

OKですね。
次回
次回は、Ranking APIとRedisのソート機能を実装しましょう。
Go言語まとめ
ソースコード
ここまでのソースコードを以下に記載します。
linkController.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
// linkController.go package controllers import ( "admin/src/database" "admin/src/middleware" "admin/src/models" "fmt" "strconv" "github.com/bxcodec/faker/v3" "github.com/gofiber/fiber/v2" ) func Link(ctx *fiber.Ctx) error { id, _ := strconv.Atoi(ctx.Params("id")) var links []models.Link database.DB.Where("user_id = ?", id).Find(&links) for i, link := range links { var orders []models.Order database.DB.Where("code = ? and complete = true", link.Code).Find(&orders) links[i].Orders = orders } return ctx.JSON(links) } type CreateLinkRequest struct { ProductIDs []int } func CreateLink(ctx *fiber.Ctx) error { var request CreateLinkRequest // リクエストデータからProductIDを取得 if err := ctx.BodyParser(&request); err != nil { return err } fmt.Println(request) // ユーザーIDを取得 id, _ := middleware.GetUserID(ctx) // リンク作成 link := models.Link{ UserID: id, Code: faker.Username(), } // プロダクトID, ユーザーIDを紐づける for _, productID := range request.ProductIDs { product := models.Product{} product.ID = uint(productID) link.Products = append(link.Products, product) } // DB保存 database.DB.Create(&link) return ctx.JSON(link) } func Stats(ctx *fiber.Ctx) error { // ユーザーIDを取得 id, _ := middleware.GetUserID(ctx) // DB検索 var links []models.Link database.DB.Find(&links, models.Link{ UserID: id, }) var result []interface{} var orders []models.Order for _, link := range links { // OrderItemsを先にロードしてからOrderデータを検索 database.DB.Preload("OrderItems").Find(&orders, &models.Order{ Code: link.Code, Complete: true, }) var revenue float64 = 0 for _, order := range orders { revenue += order.GetTotal() } result = append(result, fiber.Map{ "code": link.Code, "count": len(orders), "revenue": revenue, }) } return ctx.JSON(result) } |
routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// routes/routes.go package routes import ( "admin/src/controllers" "admin/src/middleware" "github.com/gofiber/fiber/v2" ) func Setup(app *fiber.App) { // Group api := app.Group("api") // admin admin := api.Group("admin") admin.Post("register", controllers.Register) admin.Post("login", controllers.Login) adminAuthenticated := admin.Use(middleware.IsAuthenticate) adminAuthenticated.Get("user", controllers.User) adminAuthenticated.Post("logout", controllers.Logout) adminAuthenticated.Put("info", controllers.UpdateInfo) adminAuthenticated.Put("password", controllers.UpdatePassword) adminAuthenticated.Get("ambassadors", controllers.Ambassadors) adminAuthenticated.Get("products", controllers.Products) adminAuthenticated.Post("products", controllers.CreateProducts) adminAuthenticated.Get("products/:id", controllers.GetProduct) adminAuthenticated.Put("products/:id", controllers.UpdateProduct) adminAuthenticated.Delete("products/:id", controllers.DeleteProduct) adminAuthenticated.Get("users/:id/links", controllers.Link) adminAuthenticated.Get("orders", controllers.Orders) // Ambassador ambassador := api.Group("ambassador") ambassador.Post("register", controllers.Register) ambassador.Post("login", controllers.Login) ambassador.Get("products/frontend", controllers.ProductFrontend) ambassador.Get("products/backend", controllers.ProductBackend) ambassadorAuthentication := ambassador.Use(middleware.IsAuthenticate) ambassadorAuthentication.Get("user", controllers.User) ambassadorAuthentication.Post("logout", controllers.Logout) ambassadorAuthentication.Put("users/info", controllers.UpdateInfo) ambassadorAuthentication.Put("users/password", controllers.UpdatePassword) ambassadorAuthentication.Post("links", controllers.CreateLink) ambassadorAuthentication.Get("stats", controllers.Stats) } |
コメントを残す
コメントを投稿するにはログインしてください。