今天會繼續把剩下的 To-do List 專案架構完成:handler、repository、service。
那我們就直接開始吧!
真正新增、刪除資料的地方!也就是連結資料庫的所在。所以後續如果改成撈資料庫的資料,就可以直接從這邊修改,就可以囉!
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
}
加入 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
}
只解析請求、呼叫 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,拆完程式要記得再測試看看程式是不是還活著,沒有壞掉唷~ 🤣