初學(xué)者友好:Go-Kratos 集成 go-crud,Ent ORM CRUD 無(wú)需重復(fù)編碼,輕松上手

初學(xué)者友好:Go-Kratos 集成 go-crud,Ent ORM CRUD 無(wú)需重復(fù)編碼,輕松上手

對(duì)于剛接觸 Go 微服務(wù)開(kāi)發(fā)的初學(xué)者來(lái)說(shuō),直接上手 “框架 + ORM” 的組合常顯復(fù)雜。而 kratos-ent-example 項(xiàng)目已為我們搭建好了 Go-KratosEnt 的基礎(chǔ)集成框架,本文將基于該項(xiàng)目,聚焦如何快速接入 go-curd 工具簡(jiǎn)化 CRUD(增刪改查)操作,全程以 step-by-step 的方式講解,新手也能輕松跟隨實(shí)操。

先明確核心工具關(guān)系:kratos-ent-example是 “基礎(chǔ)骨架”(已整合 Kratos 與 Ent),go-curd是 “效率工具”(封裝重復(fù) CRUD 邏輯),我們的核心目標(biāo)是 “在現(xiàn)有骨架上裝工具,讓數(shù)據(jù)操作更簡(jiǎn)單”。

一、核心工具速覽:3 分鐘理清分工

在動(dòng)手前,先明確三個(gè)工具的分工,避免越做越亂:

  • Go-Kratos:微服務(wù)框架核心,負(fù)責(zé) API 定義、服務(wù)啟動(dòng)、請(qǐng)求分發(fā),kratos-ent-example 已完成其基礎(chǔ)配置;
  • Ent:現(xiàn)代化 ORM 框架,通過(guò)代碼生成實(shí)現(xiàn)類型安全的數(shù)據(jù)庫(kù)操作,采用 schema 定義模型,比傳統(tǒng) ORM 更注重類型檢查;
  • go-crud:Ent 的上層封裝工具,把重復(fù)的 CRUD 邏輯(如創(chuàng)建、查詢、更新、刪除)做成現(xiàn)成方法,無(wú)需手動(dòng)編寫(xiě) Ent 原生查詢語(yǔ)句。

二、環(huán)境準(zhǔn)備:5 分鐘搞定前置依賴

先完成基礎(chǔ)環(huán)境搭建和項(xiàng)目準(zhǔn)備,確保后續(xù)步驟無(wú)報(bào)錯(cuò):

  1. 安裝基礎(chǔ)工具:要求 Go 1.24+(項(xiàng)目 go.mod 指定版本),安裝后用go version驗(yàn)證;
  2. Git:用于克隆示例項(xiàng)目;
  3. 準(zhǔn)備數(shù)據(jù)庫(kù):支持 PostgreSQL/MySQL(Ent 適配多種數(shù)據(jù)庫(kù)),新建一個(gè)數(shù)據(jù)庫(kù)(比如叫example),不用建表(后續(xù) Ent 會(huì)自動(dòng)生成);
  4. 獲取 kratos-ent-example 項(xiàng)目
    • 打開(kāi)終端,執(zhí)行以下命令克隆項(xiàng)目并進(jìn)入目錄:git clone https://github.com/tx7do/kratos-ent-example.git
    • cd kratos-ent-example
  5. 引入 go-curd 依賴:項(xiàng)目已預(yù)設(shè) go-curd 的 Ent 適配模塊(見(jiàn) go.mod 中的github.com/tx7do/go-crud/entgo),直接拉取依賴即可:go mod tidy
  6. 確認(rèn)項(xiàng)目核心目錄:無(wú)需關(guān)注所有文件,重點(diǎn)記住 3 個(gè)核心目錄(kratos-ent-example 已預(yù)設(shè)):
    • api:放 API 定義文件(.proto),用于定義 “創(chuàng)建用戶”“查詢用戶” 等接口;
    • app/user/service/internal/data/ent/schema:放 Ent 模型定義(通過(guò) schema 描述數(shù)據(jù)庫(kù)表結(jié)構(gòu));
    • app/user/service/internal/data:放業(yè)務(wù)邏輯,這里會(huì)調(diào)用 go-curd 操作 Ent 客戶端。

三、核心步驟 1:在 kratos-ent-example 中集成 go-curd

kratos-ent-example 已完成 Ent 的初始化配置(如數(shù)據(jù)庫(kù)連接、代碼生成),我們只需在現(xiàn)有基礎(chǔ)上,將 go-curd 的 Ent 客戶端集成進(jìn)來(lái),讓業(yè)務(wù)層可以調(diào)用其簡(jiǎn)化方法。

1.1 修改數(shù)據(jù)層:集成 go-curd 的 Ent 客戶端

打開(kāi)app/user/service/internal/data/user.go修改代碼以集成go-curdEnt 適配模塊:

package data

import (
  entCurd "github.com/tx7do/go-crud/entgo"
)

type UserRepo struct {
    data *Data
    log  *log.Helper

    mapper     *mapper.CopierMapper[userV1.User, *ent.User]
    repository *entCurd.Repository[
        ent.UserQuery, ent.UserSelect, ent.UserCreate, ent.UserCreateBulk, ent.UserUpdate, ent.UserUpdateOne, ent.UserDelete,
        predicate.User,
        userV1.User, ent.User,
    ]
}

func NewUserRepo(data *Data, logger log.Logger) *UserRepo {
    l := log.NewHelper(log.With(logger, "module", "user/repo/user-service"))
    repo := &UserRepo{
        data:   data,
        log:    l,
        mapper: mapper.NewCopierMapper[userV1.User, *ent.User](),
    }

    // 初始化go-curd的Ent倉(cāng)庫(kù),傳入映射器和Ent客戶端
    repo.repository = entCurd.NewRepository[
        ent.UserQuery, ent.UserSelect, ent.UserCreate, ent.UserCreateBulk, ent.UserUpdate, ent.UserUpdateOne, ent.UserDelete,
        predicate.User,
        userV1.User, ent.User,
    ](repo.mapper)

    return repo
}

核心改動(dòng)說(shuō)明:新增repository字段存儲(chǔ)go-curdEnt 客戶端,通過(guò)entCurd.NewRepository()初始化,后續(xù) CRUD 操作均通過(guò)該客戶端完成。

1.2 確認(rèn)數(shù)據(jù)庫(kù)配置(無(wú)需修改,僅驗(yàn)證)

kratos-ent-example 已在配置文件中預(yù)設(shè)數(shù)據(jù)庫(kù)連接,打開(kāi)configs/data.yaml驗(yàn)證:

data:
  database:
    driver: "postgres"  # 支持mysql/postgres/sqlite
    source: "host=localhost port=5432 user=postgres password=your_password dbname=example sslmode=disable"
    migrate: true  # 啟動(dòng)時(shí)自動(dòng)執(zhí)行數(shù)據(jù)庫(kù)遷移

注意:將source中的用戶名、密碼改為自己的數(shù)據(jù)庫(kù)信息,確保能連接到之前新建的example數(shù)據(jù)庫(kù)。

四、核心步驟 2:用 go-curd 實(shí)現(xiàn) CRUD 業(yè)務(wù)邏輯

我們將以 “用戶模塊” 為例,基于項(xiàng)目現(xiàn)有的目錄結(jié)構(gòu),用 go-curd 實(shí)現(xiàn)用戶的增、刪、改、查。kratos-ent-example 已預(yù)設(shè)部分基礎(chǔ)代碼,我們只需補(bǔ)充和修改。

2.1 定義用戶模型(Ent Schema)

Ent 通過(guò) schema 定義模型(而非傳統(tǒng)結(jié)構(gòu)體),打開(kāi)app/user/service/internal/data/ent/schema/user.go,定義用戶模型的 schema:

package schema

import (
    "time"
    "entgo.io/ent"
    "entgo.io/ent/dialect"
    "entgo.io/ent/schema/field"
    "entgo.io/ent/schema/index"
)

// User holds the schema definition for the User entity.
type User struct {
    ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("user_name").
            SchemaType(map[string]string{
                dialect.MySQL:    "varchar(64)", // MySQL特定類型
                dialect.Postgres: "varchar(64)", // PostgreSQL特定類型
            }).
            Unique(), // 用戶名唯一
        field.String("nick_name").
            SchemaType(map[string]string{
                dialect.MySQL:    "varchar(64)",
                dialect.Postgres: "varchar(64)",
            }),
        field.String("password").
            SchemaType(map[string]string{
                dialect.MySQL:    "varchar(128)",
                dialect.Postgres: "varchar(128)",
            }),
        field.Time("created_at").
            Default(time.Now).
            SchemaType(map[string]string{
                dialect.MySQL: "datetime",
            }),
        field.Time("updated_at").
            Default(time.Now).
            UpdateDefault(time.Now).
            SchemaType(map[string]string{
                dialect.MySQL: "datetime",
            }),
        field.Time("deleted_at").
            Optional().
            Nillable().
            SchemaType(map[string]string{
                dialect.MySQL: "datetime",
            }),
    }
}

// Edges of the User.
func (User) Edges() []ent.Edge {
    return nil
}

// Indexes of the User.
func (User) Indexes() []ent.Index {
    return []ent.Index{
        index.Fields("user_name").Unique(), // 用戶名索引(唯一)
    }
}

說(shuō)明:Ent 會(huì)根據(jù)該 schema 自動(dòng)生成 Go 代碼(實(shí)體、查詢器等),后續(xù)通過(guò)生成的代碼操作數(shù)據(jù)庫(kù)。在app/user/service目錄下執(zhí)行以下命令生成 Ent 代碼:

make ent

生成的代碼會(huì)放在app/user/service/internal/data/ent目錄下,包含User實(shí)體及 CRUD 基礎(chǔ)方法。

2.2 編寫(xiě) Data 層:用 go-curd 實(shí)現(xiàn) CRUD

打開(kāi)app/user/service/internal/data/user.go,編寫(xiě)業(yè)務(wù)邏輯方法。核心優(yōu)勢(shì):用 go-curd 的現(xiàn)成方法替代原生 Ent 代碼,減少重復(fù)工作。

package data

// List 查詢用戶列表(帶分頁(yè))
func (r *UserRepo) List(ctx context.Context, req *pagination.PagingRequest) (*userV1.ListUserResponse, error) {
    if req == nil {
        return nil, errors.New("request is nil")
    }

    builder := r.data.db.Client().Debug().User.Query()

    ret, err := r.repository.ListWithPaging(ctx, builder, builder.Clone(), req)
    if err != nil {
        return nil, err
    }
    if ret == nil {
        return &userV1.ListUserResponse{Total: 0, Items: nil}, nil
    }

    return &userV1.ListUserResponse{
        Total: ret.Total,
        Items: ret.Items,
    }, nil
}

// Get 查詢單個(gè)用戶(支持按ID或用戶名查詢)
func (r *UserRepo) Get(ctx context.Context, req *userV1.GetUserRequest) (*userV1.User, error) {
    if req == nil {
        return nil, errors.New("request is nil")
    }

    var whereCond []func(s *sql.Selector)
    switch req.QueryBy.(type) {
    case *userV1.GetUserRequest_Id:
        whereCond = append(whereCond, user.IDEQ(req.GetId()))
    case *userV1.GetUserRequest_UserName:
        whereCond = append(whereCond, user.UserNameEQ(req.GetUserName()))
    default:
        whereCond = append(whereCond, user.IDEQ(req.GetId()))
    }

    builder := r.data.db.Client().Debug().User.Query()
    dto, err := r.repository.Get(ctx, builder, whereCond, req.GetViewMask())
    if err != nil {
        return nil, err
    }

    return dto, err
}

// Create 創(chuàng)建用戶(密碼加密存儲(chǔ))
func (r *UserRepo) Create(ctx context.Context, req *userV1.CreateUserRequest) (*userV1.User, error) {
    if req == nil || req.Data == nil {
        return nil, errors.New("request is nil")
    }

    if req.Data.Password != nil && req.Data.GetPassword() != "" {
        cryptoPassword, err := crypto.HashPassword(req.Data.GetPassword())
        if err != nil {
            return nil, err
        }
        req.Data.Password = &cryptoPassword
    }

    builder := r.data.db.Client().Debug().User.Create()
    result, err := r.repository.Create(ctx, builder, req.Data, nil, func(dto *userV1.User) {
        builder.
            SetNillableUserName(req.Data.UserName).
            SetNillableNickName(req.Data.NickName).
            SetCreatedAt(time.Now())

        if req.Data.Password != nil {
            builder.SetPassword(req.Data.GetPassword())
        }
    })

    return result, err
}

// Update 更新用戶信息
func (r *UserRepo) Update(ctx context.Context, req *userV1.UpdateUserRequest) (*userV1.User, error) {
if req == nil || req.Data == nil {
        return nil, errors.New("request is nil")
    }

    if req.Data.Password != nil && req.Data.GetPassword() != "" {
        cryptoPassword, err := crypto.HashPassword(req.Data.GetPassword())
        if err != nil {
            return nil, err
        }
        req.Data.Password = &cryptoPassword
    }

    builder := r.data.db.Client().Debug().User.UpdateOneID(req.Data.GetId())
    result, err := r.repository.UpdateOne(ctx, builder, req.Data, req.GetUpdateMask(),
        []predicate.User{
            func(s *sql.Selector) {
                s.Where(sql.EQ(user.FieldID, req.Data.GetId()))
            },
        },
        func(dto *userV1.User) {
            builder.
                SetNillableNickName(req.Data.NickName).
                SetUpdatedAt(time.Now())

            if req.Data.Password != nil {
                builder.SetPassword(req.Data.GetPassword())
            }
        },
    )

    return result, err
}

// Delete 刪除用戶
func (r *UserRepo) Delete(ctx context.Context, req *userV1.DeleteUserRequest) (bool, error) {
    if req == nil {
        return false, errors.New("request is nil")
    }

    builder := r.data.db.Client().Debug().User.Delete()
    affected, err := r.repository.Delete(ctx, builder, []predicate.User{
        func(s *sql.Selector) {
            s.Where(sql.EQ(user.FieldID, req.GetId()))
        },
    })

    return err == nil && affected > 0, err
}

核心簡(jiǎn)化點(diǎn):對(duì)比原生 Ent,go-curdListWithPagingGet、Create等方法封裝了查詢條件構(gòu)建、結(jié)果映射等重復(fù)邏輯,直接傳入 DTO(數(shù)據(jù)傳輸對(duì)象)即可完成操作。

2.3 定義 API 接口(Proto)并生成代碼

kratos-ent-example已在api/protos/user/service/v1/user.proto中預(yù)設(shè)了用戶 API 定義(與 GORM 示例類似),我們只需確認(rèn)內(nèi)容,然后生成 Go 代碼:

syntax = "proto3";

package user.service.v1;

import "google/api/annotations.proto";
import "pagination/v1/pagination.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";

// 用戶服務(wù)
service UserService {
  rpc ListUser (pagination.PagingRequest) returns (ListUserResponse) {
    option (google.api.http) = { get: "/users" };
  }
  rpc GetUser (GetUserRequest) returns (User) {
    option (google.api.http) = { get: "/users/{id}" };
  }
  rpc CreateUser (CreateUserRequest) returns (User) {
    option (google.api.http) = { post: "/users", body: "*" };
  }
  rpc UpdateUser (UpdateUserRequest) returns (User) {
    option (google.api.http) = { put: "/users/{data.id}", body: "*" };
  }
  rpc DeleteUser (DeleteUserRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = { delete: "/users/{id}" };
  }
}

// 用戶
message User {
  uint32 id = 1;

  optional string user_name = 2 [json_name = "userName", (gnostic.openapi.v3.property) = {description: "賬戶名"}];// 賬戶名
  optional string nick_name = 3 [json_name = "nickName", (gnostic.openapi.v3.property) = {description: "昵稱"}];// 昵稱
  optional string password = 4 [json_name = "password", (gnostic.openapi.v3.property) = {description: "密碼"}];// 密碼

  optional google.protobuf.Timestamp created_at = 200 [json_name = "createdAt", (gnostic.openapi.v3.property) = {description: "創(chuàng)建時(shí)間"}];// 創(chuàng)建時(shí)間
  optional google.protobuf.Timestamp updated_at = 201 [json_name = "updatedAt", (gnostic.openapi.v3.property) = {description: "更新時(shí)間"}];// 更新時(shí)間
  optional google.protobuf.Timestamp deleted_at = 202 [json_name = "deletedAt", (gnostic.openapi.v3.property) = {description: "刪除時(shí)間"}];// 刪除時(shí)間
}

// 獲取用戶列表 - 答復(fù)
message ListUserResponse {
  repeated User items = 1;
  uint64 total = 2;
}

// 獲取用戶數(shù)據(jù) - 請(qǐng)求
message GetUserRequest {
  oneof query_by {
    uint32 id = 1 [
      (gnostic.openapi.v3.property) = {description: "用戶ID", read_only: true},
      json_name = "id"
    ]; // 用戶ID

    string user_name = 2 [
      (gnostic.openapi.v3.property) = {description: "用戶登錄名", read_only: true},
      json_name = "userName"
    ]; // 用戶登錄名
  }

  optional google.protobuf.FieldMask view_mask = 100 [
    json_name = "viewMask",
    (gnostic.openapi.v3.property) = {
      description: "視圖字段過(guò)濾器,用于控制返回的字段"
    }
  ]; // 視圖字段過(guò)濾器,用于控制返回的字段
}

// 創(chuàng)建用戶 - 請(qǐng)求
message CreateUserRequest {
  User data = 1;

  uint32 operator_id = 2 [json_name = "operatorId", (gnostic.openapi.v3.property) = {description: "操作者用戶ID"}];// 操作者用戶ID
}

// 更新用戶 - 請(qǐng)求
message UpdateUserRequest {
  User data = 1;

  google.protobuf.FieldMask update_mask = 2 [
    json_name = "updateMask",
    (gnostic.openapi.v3.property) = {
      description: "要更新的字段列表",
      example: {yaml : "id,realname,username"}
    }
  ]; // 要更新的字段列表

  optional bool allow_missing = 3 [
    json_name = "allowMissing",
    (gnostic.openapi.v3.property) = {description: "如果設(shè)置為true的時(shí)候,資源不存在則會(huì)新增(插入),并且在這種情況下`updateMask`字段將會(huì)被忽略。"}
  ]; // 如果設(shè)置為true的時(shí)候,資源不存在則會(huì)新增(插入),并且在這種情況下`updateMask`字段將會(huì)被忽略。
}

// 刪除用戶 - 請(qǐng)求
message DeleteUserRequest {
  uint32 id = 1;

  uint32 operator_id = 2 [json_name = "operatorId", (gnostic.openapi.v3.property) = {description: "操作者用戶ID"}];// 操作者用戶ID
}

執(zhí)行以下命令生成 Go 代碼(項(xiàng)目已預(yù)設(shè)make api命令):

make api

生成的代碼會(huì)放在api/gen/go/user/service/v1目錄下,供 Data 層和 Service 層調(diào)用。

2.4 Server 層綁定接口與 Service

kratos-ent-example 通過(guò)NewRESTServer方法完成 HTTP Server 的創(chuàng)建,并將 UserService 注冊(cè)到 KratosHTTP 服務(wù)中,實(shí)現(xiàn) API 接口與 Service 層的綁定。核心代碼如下(文件路徑:app/user/service/internal/server/rest.go):

// NewRESTServer new an HTTP server.
func NewRESTServer(
    cfg *conf.Bootstrap, logger log.Logger,
    userService *service.UserService,
) *http.Server {
    if cfg == nil || cfg.Server == nil || cfg.Server.Rest == nil {
        return nil
    }

    srv := bootstrap.CreateRestServer(cfg, logging.Server(logger))

    userV1.RegisterUserServiceHTTPServer(srv, userService)

    if cfg.GetServer().GetRest().GetEnableSwagger() {
        swaggerUI.RegisterSwaggerUIServerWithOption(
            srv,
            swaggerUI.WithTitle("Kratos Ent Example User Service API"),
            swaggerUI.WithMemoryData(assets.OpenApiData, "yaml"),
        )
    }

    return srv
}

代碼說(shuō)明:

  1. bootstrap.CreateRestServer:基于配置創(chuàng)建 Kratos 的 HTTP Server 實(shí)例,包含端口、中間件等基礎(chǔ)配置;
  2. userV1.RegisterUserServiceHTTPServer:將實(shí)現(xiàn)了UserService接口的userService實(shí)例注冊(cè)到 HTTP Server 中,完成 API 接口(如/users)與 Service 層方法的綁定;
  3. Swagger 相關(guān)配置:可選開(kāi)啟 Swagger UI,方便調(diào)試 API 接口。

此步驟無(wú)需手動(dòng)修改代碼(項(xiàng)目已實(shí)現(xiàn)),只需驗(yàn)證該文件存在且代碼完整即可 —— 啟動(dòng)服務(wù)后,Kratos 會(huì)自動(dòng)將 HTTP 請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的 Service 層方法。

五、核心步驟 3:運(yùn)行項(xiàng)目并測(cè)試 CRUD 接口

所有代碼修改完成后,啟動(dòng)項(xiàng)目并測(cè)試接口,驗(yàn)證 go-curdEnt 的集成是否正常工作。

3.1 自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表(Ent 遷移)

kratos-ent-example 已在app/user/service/internal/data/ent_client.go中實(shí)現(xiàn) Ent 自動(dòng)遷移邏輯,啟動(dòng)項(xiàng)目時(shí)會(huì)根據(jù) schema 創(chuàng)建數(shù)據(jù)庫(kù)表:

// 關(guān)鍵遷移代碼(項(xiàng)目已實(shí)現(xiàn))
if cfg.Data.Database.GetMigrate() {
  if err = client.Schema.Create(context.Background(), migrate.WithForeignKeys(true)); err != nil {
    l.Fatalf("failed creating schema resources: %v", err)
  }
}

3.2 啟動(dòng)項(xiàng)目

在項(xiàng)目的服務(wù)目錄app/user/service下執(zhí)行以下命令啟動(dòng)服務(wù):

make run

看到終端輸出類似以下日志,說(shuō)明項(xiàng)目啟動(dòng)成功:

DEBUG msg=config loaded: data.yaml format: yaml
DEBUG msg=ent: connecting to postgres://postgres:***@localhost:5432/example?sslmode=disable
DEBUG msg=ent: schema migrated successfully

3.3 測(cè)試接口(用 curl 或 Postman)

以下用 curl 命令測(cè)試 4 個(gè) CRUD 接口,確保功能正常:

1. 創(chuàng)建用戶:

curl -X 'POST' \
  'http://localhost:7788/users' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "data": {
    "id": 0,
    "userName": "zhangsan",
    "nickName": "張三",
    "password": "123456"
  }
}'

成功響應(yīng):

{
  "id": 1, //(ID為自動(dòng)生成的主鍵)。
  "userName": "zhangsan",
  "nickName": "張三",
  "password": "$2a$10$Jd34ATGgTJ2sV7xvPruMLONArXk9KYQ2O6XDY42UxVO37p5DO8CVu",
  "createdAt": "1970-01-01T00:00:00Z",
  "updatedAt": "1970-01-01T00:00:00Z"
}

2. 查詢用戶(使用上面返回的ID=1):

curl -X 'GET' \
  'http://localhost:7788/users/1' \
  -H 'accept: application/json'

成功響應(yīng):

{
  "id": 1,
  "userName": "zhangsan",
  "nickName": "張三",
  "password": "$2a$10$Jd34ATGgTJ2sV7xvPruMLONArXk9KYQ2O6XDY42UxVO37p5DO8CVu",
  "createdAt": "1970-01-01T00:00:00Z",
  "updatedAt": "1970-01-01T00:00:00Z"
}

3. 更新用戶:

curl -X 'PUT' \
  'http://localhost:7788/users/1' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "data": {
    "id": 1,
    "userName": "zhangsan",
    "nickName": "張三三"
  }
}'

成功響應(yīng):

{
  "id": 1,
  "userName": "zhangsan",
  "nickName": "張三三",
  "password": "$2a$10$Jd34ATGgTJ2sV7xvPruMLONArXk9KYQ2O6XDY42UxVO37p5DO8CVu",
  "createdAt": "1970-01-01T00:00:00Z",
  "updatedAt": "1970-01-01T00:00:00Z"
}

4. 刪除用戶:

curl -X 'DELETE' \
  'http://localhost:7788/users/1' \
  -H 'accept: */*'

成功響應(yīng):

{}

六、新手避坑注意事項(xiàng)

  1. Ent 代碼生成必須執(zhí)行:修改 schema 后必須運(yùn)行make ent生成新代碼,否則會(huì)報(bào) “未定義字段 / 方法” 錯(cuò)誤;
  2. 數(shù)據(jù)庫(kù)驅(qū)動(dòng)適配:Ent 對(duì)不同數(shù)據(jù)庫(kù)的字段類型支持有差異(如時(shí)間類型),schema 中需用SchemaType指定數(shù)據(jù)庫(kù)特定類型;
  3. 查詢條件構(gòu)建:Ent 的查詢條件通過(guò)函數(shù)閉包實(shí)現(xiàn)(如q.Where(ent.User.ID(1))),與 GORM 的鏈?zhǔn)秸{(diào)用不同,需注意語(yǔ)法;
  4. go-curd 版本兼容:項(xiàng)目依賴的github.com/tx7do/go-crud/entgo版本需與 Ent 版本(項(xiàng)目中為 v0.14.5)匹配,否則可能出現(xiàn)方法不兼容;
  5. 遷移操作謹(jǐn)慎執(zhí)行:生產(chǎn)環(huán)境中,migrate.WithForeignKeys(true)可能導(dǎo)致表結(jié)構(gòu)變更風(fēng)險(xiǎn),建議先通過(guò)ent migrate plan預(yù)覽變更。

七、總結(jié)

基于 kratos-ent-example 項(xiàng)目集成 go-curd 的核心邏輯是:在 Ent 自動(dòng)生成的代碼基礎(chǔ)上,通過(guò) go-curd 封裝 CRUD 邏輯,減少重復(fù)的查詢構(gòu)建和結(jié)果映射工作。相比直接使用 Ent 原生 API,go-curd 讓業(yè)務(wù)代碼更簡(jiǎn)潔,尤其適合快速開(kāi)發(fā)。

如果需要擴(kuò)展其他模塊(如訂單、商品),只需復(fù)制用戶模塊的邏輯:定義 Ent schema→生成代碼→用 go-curd 實(shí)現(xiàn) CRUD→綁定 API 接口。若遇到問(wèn)題,可參考項(xiàng)目的官方文檔(go-curd、kratos-ent-example)獲取更多細(xì)節(jié)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容