iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0

今天會繼續把剩下的 To-do List 專案架構完成:handler、repository、service。
那我們就直接開始吧!


4. repository

真正新增、刪除資料的地方!也就是連結資料庫的所在。所以後續如果改成撈資料庫的資料,就可以直接從這邊修改,就可以囉!

package repository
import (
	"app/model"
)

// 暫存資料
var tasks = []model.Task{
	{ID: 1, Item: "鐵人賽文章", Status: false},
}

func GetAllTasks() []model.Task{
	res := make([]model.Task, len(tasks))  // 建立一個空 slice
	copy(res, tasks)					   //複製 tasks 內容到 res
	return  res
}

func AddTask(new model.Task) model.Task{
	new.ID = len(tasks) + 1
	tasks = append(tasks, new)
	return new
}

func UpdateTask(id int, input model.UpdateTask)(model.Task, bool){
	for i := range tasks{
		if id == tasks[i].ID{
			if input.Item != nil{
				tasks[i].Item = *input.Item
			}
			if input.Status != nil{
				tasks[i].Status = *input.Status
			}
			return tasks[i], true
		}
	}
	return model.Task{}, false   // 如果沒有找到符合的 id 就回傳原本的
}

func DeleteTask(id int) bool{
	for i := range tasks{
		if id == tasks[i].ID{
			tasks = append(tasks[:i], tasks[i+1:]...) //移除
			return  true
		}
	}
	return false
}

5. service

加入 CRUD 的流程 → 處理回傳的錯誤。

package taskService
import (
	"errors"
	"app/model"
	"app/repository"
)

// Error messages
var (
	ErrNotFound      = errors.New("task not found")
	ErrInvalidID     = errors.New("invalid id")
	ErrInvalidInput	 = errors.New("input could not be null")
)

func GetTasks() []model.Task{
	return  repository.GetAllTasks()
}

func AddTask(item model.Task) (model.Task){
	newTask := repository.AddTask(item)
	return newTask
}

func UpdateTask(id int, input model.UpdateTask) (model.Task, error){
	if input.Item == nil && input.Status == nil {
		return  model.Task{}, ErrInvalidInput
	}

	editTask, err := repository.UpdateTask(id, input)
	if !err {
		return  model.Task{}, ErrNotFound
	}
	return  editTask, nil
}

func DeleteTask(id int) error{
	msg := repository.DeleteTask((id))
	if !msg {
		return ErrNotFound
	}
	return  nil
}

6. handler

只解析請求、呼叫 service,同時把錯誤丟給 middleware 判斷處理。

package apihandler
import (
	"app/model"
	"app/service"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
)

// GET /tasks
func GetTasks(c *gin.Context) {
	task := taskService.GetTasks()
	c.JSON(http.StatusOK, task)
}

// POST /tasks
func AddTask(c *gin.Context) {
	var input model.Task
	if err := c.ShouldBindJSON(&input); err != nil {
		c.Error(taskService.ErrInvalidInput)
		return
	}
	newTask := taskService.AddTask(input)
	c.JSON(http.StatusOK, newTask)
}

// PATCH /task/:id
func UpdateTask(c *gin.Context) {
	get_id := c.Param("id")
	id, err := strconv.Atoi(get_id)

	if err != nil {
		c.Error(taskService.ErrInvalidID)
		return
	}

	var input model.UpdateTask
	if err := c.ShouldBindJSON(&input); err != nil {
		c.Error(taskService.ErrInvalidInput)
		return
	}

	editTask, err := taskService.UpdateTask(id, input)
	if err != nil{
		c.Error(err)
		return
	}
	c.JSON(http.StatusOK, editTask)
}

// DELETE /task/:id
func DeleteTask(c *gin.Context) {
	get_id := c.Param("id")
	id, err := strconv.Atoi(get_id)

	if err != nil {
		c.Error(taskService.ErrInvalidID)
		return
	}

	err := taskService.DeleteTask(id)
	if err != nil{
		c.Error(err)
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Delete successfully"})
}

以上就是完整的拆分架構了!一開始看到這麼多檔案會眼花撩亂,但仔細一個一個對照的話,就會發現它們的前後邏輯是可以依循的,而把架構拆分成這樣子的話,對未來程式的擴充與維護會比較有利、也比較輕鬆~
By the way,拆完程式要記得再測試看看程式是不是還活著,沒有壞掉唷~ 🤣


上一篇
Day15 - 優化 To-do List 專案結構1
下一篇
Day17 - Go 與資料庫:SQLite
系列文
Go,一起成為全端吧!—— 給前端工程師的 Golang 後端學習筆記24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言