gRPC 的增刪改查系列之service服務端介面編寫

夜幕下的風之發表於2020-08-31

1.路徑:\src\api\v1\service\todo-service.go
2.service服務端增刪改查介面:

package v1

import (
    "context"
    "database/sql"
    "fmt"
    "github.com/golang/protobuf/ptypes"
    v1 "goWebGin/api/proto/v1"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    "time"
)

const (
    apiVersion = "v1"
)

type ToDoServiceServer struct {
    db *sql.DB
}

func NewToDoServiceServer(db *sql.DB) *ToDoServiceServer {
    return &ToDoServiceServer{db: db}
}

//校驗版本號
func (s *ToDoServiceServer) checkAPI(api string) error {
    if len(api) > 0 && apiVersion != api {
        return status.Error(codes.Unimplemented, fmt.Sprintf("不支援當前API版本號%s", api))
    }
    return nil
}

//連線資料庫
func (s *ToDoServiceServer) connect(ctx context.Context) (*sql.Conn, error) {
    c, err := s.db.Conn(ctx)
    if err != nil {
        return nil, status.Error(codes.Unknown, "資料庫連線失敗:"+err.Error())
    }
    return c, nil
}

//新增單條記錄
func (s *ToDoServiceServer) Create(ctx context.Context, req *v1.CreateRequest) (*v1.CreateResponse, error) {
    if err := s.checkAPI(req.Api); err != nil {
        return nil, err
    }
    c, err := s.connect(ctx)
    if err != nil {
        return nil, err
    }
    defer func() {
        _ = c.Close()
    }()
    reminder, err := ptypes.Timestamp(req.ToDo.Reminder)
    if err != nil {
        return nil, status.Error(codes.InvalidArgument, "引數錯誤:"+err.Error())
    }
    res, err := c.ExecContext(ctx, "INSERT INTO ToDO(`title`,`description`,`reminder`) VALUES (?,?,?)", req.ToDo.Title, req.ToDo.Description, reminder)
    if err != nil {
        return nil, status.Error(codes.Unknown, "新增 ToDo失敗:"+err.Error())
    }
    id, err := res.LastInsertId()
    if err != nil {
        return nil, status.Error(codes.Unknown, "獲取最新ID失敗"+err.Error())
    }
    return &v1.CreateResponse{Api: apiVersion, Id: id}, nil
}

//讀取單條記錄
func (s *ToDoServiceServer) Read(ctx context.Context, req *v1.ReadRequest) (*v1.ReadResponse, error) {
    if err := s.checkAPI(req.Api); err != nil {
        return nil, err
    }
    c, err := s.connect(ctx)
    if err != nil {
        return nil, err
    }
    defer func() {
        _ = c.Close()
    }()
    row, err := c.QueryContext(ctx, "SELECT * FROM ToDo WHERE `id`=?", req.Id)
    if err != nil {
        return nil, status.Error(codes.Unknown, "查詢失敗"+err.Error())
    }
    defer func() {
        _ = row.Close()
    }()
    if !row.Next() {
        if err := row.Err(); err != nil {
            return nil, status.Error(codes.Unknown, "獲取資料失敗"+err.Error())
        }
        return nil, status.Error(codes.NotFound, fmt.Sprintf("ID='%d'找不到", req.Id))
    }
    var td v1.ToDo
    var reminder time.Time
    if err := row.Scan(&td.Id, &td.Title, &td.Description, &reminder); err != nil {
        return nil, status.Error(codes.Unknown, "查詢資料失敗"+err.Error())
    }
    if row.Next() {
        return nil, status.Error(codes.Unknown, fmt.Sprintf("查詢到多條記錄ID=%d", req.Id))
    }
    td.Reminder, err = ptypes.TimestampProto(reminder)
    if err != nil {
        return nil, status.Error(codes.Unknown, "reminder無效:"+err.Error())
    }
    return &v1.ReadResponse{Api: apiVersion, ToDo: &td}, nil
}

//更新記錄
func (s *ToDoServiceServer) Update(ctx context.Context, req *v1.UpdateRequest) (*v1.UpdateResponse, error) {
    if err := s.checkAPI(req.Api); err != nil {
        return nil, err
    }
    c, err := s.connect(ctx)
    if err != nil {
        return nil, err
    }
    defer func() {
        _ = c.Close()
    }()
    reminder, err := ptypes.Timestamp(req.ToDo.Reminder)

    res, err := c.ExecContext(ctx, "UPDATE toDo SET `title`=?,`description`=?,`reminder`=? WHERE `id`=?",
        req.ToDo.Title, req.ToDo.Description,reminder, req.ToDo.Id)
    if err != nil {
        return nil, status.Error(codes.Unknown, "更新失敗")
    }
    row, err := res.RowsAffected()
    if err != nil {
        return nil, status.Error(codes.Unknown, "更新失敗")
    }
    if row == 0 {
        return nil, status.Error(codes.NotFound, fmt.Sprintf("ID=%d找不到", req.ToDo.Id))
    }
    return &v1.UpdateResponse{
        Api:     apiVersion,
        Updated: row,
    }, nil
}

//刪除記錄
func (s *ToDoServiceServer) Delete(ctx context.Context, req *v1.DeleteRequest) (*v1.DeleteResponse, error) {
    if err := s.checkAPI(req.Api); err != nil {
        return nil, err
    }
    c, err := s.connect(ctx)
    if err != nil {
        return nil, err
    }
    defer func() {
        _ = c.Close()
    }()

    res, err := c.ExecContext(ctx, "DELETE FROM TOdO WHERE `id`=?", req.Id)
    if err != nil {
        return nil, status.Error(codes.Unknown, "刪除失敗:"+err.Error())
    }
    row, err := res.RowsAffected()
    if err != nil {
        return nil, status.Error(codes.Unknown, "刪除失敗:"+err.Error())
    }
    if row == 0 {
        return nil, status.Error(codes.NotFound, fmt.Sprintf("ID=%d找不到", req.Id))
    }
    return &v1.DeleteResponse{
        Api:     apiVersion,
        Deleted: row,
    }, nil
}

//獲取多行記錄
func (s *ToDoServiceServer) ReadAll(ctx context.Context, req *v1.ReadAllRequest) (*v1.ReadAllResponse, error) {
    if err := s.checkAPI(req.Api); err != nil {
        return nil, err
    }
    c, err := s.connect(ctx)
    if err != nil {
        return nil, err
    }
    defer func() {
        _ = c.Close()
    }()
    rows, err := c.QueryContext(ctx, "SELECT * FROM toDo")
    if err != nil {
        return nil, status.Error(codes.Unknown, "查詢失敗:"+err.Error())
    }
    defer func() {
        _ = rows.Close()
    }()
    var reminder time.Time
    list := []*v1.ToDo{}
    for rows.Next() {
        td := new(v1.ToDo)
        if err := rows.Scan(&td.Title, &td.Description, &reminder); err != nil {
            return nil, status.Error(codes.Unknown, "查詢失敗:"+err.Error())
        }
        td.Reminder, err = ptypes.TimestampProto(reminder)
        if err != nil {
            return nil, status.Error(codes.Unknown, "reminder無效:"+err.Error())
        }
        list = append(list, td)
        if err := rows.Err(); err != nil {
            return nil, status.Error(codes.Unknown, "獲取資料失敗:"+err.Error())
        }
    }
    return &v1.ReadAllResponse{
        Api:   apiVersion,
        ToDos: list,
    }, nil
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結
一切皆自學

相關文章