初學(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-Kratos 與 Ent 的基礎(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ò):
-
安裝基礎(chǔ)工具:要求 Go 1.24+(項(xiàng)目 go.mod 指定版本),安裝后用
go version驗(yàn)證; - Git:用于克隆示例項(xiàng)目;
-
準(zhǔn)備數(shù)據(jù)庫(kù):支持 PostgreSQL/MySQL(Ent 適配多種數(shù)據(jù)庫(kù)),新建一個(gè)數(shù)據(jù)庫(kù)(比如叫
example),不用建表(后續(xù) Ent 會(huì)自動(dòng)生成); -
獲取 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
- 打開(kāi)終端,執(zhí)行以下命令克隆項(xiàng)目并進(jìn)入目錄:
-
引入 go-curd 依賴:項(xiàng)目已預(yù)設(shè) go-curd 的 Ent 適配模塊(見(jiàn) go.mod 中的
github.com/tx7do/go-crud/entgo),直接拉取依賴即可:go mod tidy -
確認(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-curd的 Ent 適配模塊:
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-curd的 Ent 客戶端,通過(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-curd 的ListWithPaging、Get、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è)到 Kratos 的 HTTP 服務(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ō)明:
-
bootstrap.CreateRestServer:基于配置創(chuàng)建 Kratos 的 HTTP Server 實(shí)例,包含端口、中間件等基礎(chǔ)配置; -
userV1.RegisterUserServiceHTTPServer:將實(shí)現(xiàn)了UserService接口的userService實(shí)例注冊(cè)到 HTTP Server 中,完成 API 接口(如/users)與Service層方法的綁定; - 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-curd 與 Ent 的集成是否正常工作。
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)
-
Ent 代碼生成必須執(zhí)行:修改 schema 后必須運(yùn)行
make ent生成新代碼,否則會(huì)報(bào) “未定義字段 / 方法” 錯(cuò)誤; -
數(shù)據(jù)庫(kù)驅(qū)動(dòng)適配:Ent 對(duì)不同數(shù)據(jù)庫(kù)的字段類型支持有差異(如時(shí)間類型),schema 中需用
SchemaType指定數(shù)據(jù)庫(kù)特定類型; -
查詢條件構(gòu)建:Ent 的查詢條件通過(guò)函數(shù)閉包實(shí)現(xiàn)(如
q.Where(ent.User.ID(1))),與 GORM 的鏈?zhǔn)秸{(diào)用不同,需注意語(yǔ)法; -
go-curd 版本兼容:項(xiàng)目依賴的
github.com/tx7do/go-crud/entgo版本需與 Ent 版本(項(xiàng)目中為 v0.14.5)匹配,否則可能出現(xiàn)方法不兼容; -
遷移操作謹(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é)。