50 | ```
51 |
52 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/api/blog/v1/blog.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package api.blog.v1;
4 |
5 | import "google/api/annotations.proto";
6 | import "google/protobuf/timestamp.proto";
7 |
8 | option go_package = "blog-service/api/blog/v1;v1";
9 | option java_multiple_files = true;
10 | option java_package = "api.blog.v1";
11 |
12 | service Blog {
13 | rpc ListBlogs (ListBlogsRequest) returns (ListBlogsReply) {
14 | option (google.api.http) = {
15 | get: "/v1/blogs"
16 | };
17 | }
18 | rpc GetBlog (GetBlogRequest) returns (GetBlogReply) {
19 | option (google.api.http) = {
20 | get: "/v1/blogs/{id}"
21 | };
22 | }
23 | rpc CreateBlog (CreateBlogRequest) returns (CreateBlogReply) {
24 | option (google.api.http) = {
25 | post: "/v1/blogs"
26 | body: "blog"
27 | };
28 | }
29 | rpc UpdateBlog (UpdateBlogRequest) returns (UpdateBlogReply) {
30 | option (google.api.http) = {
31 | put: "/v1/blogs/{id}"
32 | body: "blog"
33 | };
34 | }
35 | rpc DeleteBlog (DeleteBlogRequest) returns (DeleteBlogReply) {
36 | option (google.api.http) = {
37 | delete: "/v1/blogs/{id}"
38 | };
39 | }
40 | }
41 |
42 | message BlogInfo {
43 | string id = 1;
44 | string title = 2;
45 | string content = 3;
46 | google.protobuf.Timestamp updated_at = 4;
47 | }
48 |
49 | message ListBlogsRequest {}
50 |
51 | message ListBlogsReply {
52 | repeated BlogInfo blogs = 1;
53 | }
54 |
55 | message GetBlogRequest {
56 | string id = 1;
57 | }
58 |
59 | message GetBlogReply {
60 | BlogInfo blog = 1;
61 | }
62 |
63 | message CreateBlogRequest {
64 | BlogInfo blog = 1;
65 | }
66 |
67 | message CreateBlogReply {
68 | BlogInfo blog = 1;
69 | }
70 |
71 | message UpdateBlogRequest {
72 | string id = 1;
73 | BlogInfo blog = 2;
74 | }
75 |
76 | message UpdateBlogReply {
77 | BlogInfo blog = 1;
78 | }
79 |
80 | message DeleteBlogRequest {
81 | string id = 1;
82 | }
83 |
84 | message DeleteBlogReply {}
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/cmd/blog-service/blogs.csv:
--------------------------------------------------------------------------------
1 | id,title,content,updated_at
2 | 1745585384549783000,golang 入门,test,2025-04-25T20:49:44+08:00
3 | 1745585406657214000,白泽说,golang / ai 编程社区,2025-04-25T20:50:06+08:00
4 | 1745652170756714000,一个测试标题,测试 编辑,2025-04-26T15:23:00+08:00
5 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/cmd/blog-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "os"
6 |
7 | "blog-service/internal/conf"
8 |
9 | "github.com/go-kratos/kratos/v2"
10 | "github.com/go-kratos/kratos/v2/config"
11 | "github.com/go-kratos/kratos/v2/config/file"
12 | "github.com/go-kratos/kratos/v2/log"
13 | "github.com/go-kratos/kratos/v2/middleware/tracing"
14 | "github.com/go-kratos/kratos/v2/transport/grpc"
15 | "github.com/go-kratos/kratos/v2/transport/http"
16 |
17 | _ "go.uber.org/automaxprocs"
18 | )
19 |
20 | // go build -ldflags "-X main.Version=x.y.z"
21 | var (
22 | // Name is the name of the compiled software.
23 | Name string
24 | // Version is the version of the compiled software.
25 | Version string
26 | // flagconf is the config flag.
27 | flagconf string
28 |
29 | id, _ = os.Hostname()
30 | )
31 |
32 | func init() {
33 | flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
34 | }
35 |
36 | func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {
37 | return kratos.New(
38 | kratos.ID(id),
39 | kratos.Name(Name),
40 | kratos.Version(Version),
41 | kratos.Metadata(map[string]string{}),
42 | kratos.Logger(logger),
43 | kratos.Server(
44 | gs,
45 | hs,
46 | ),
47 | )
48 | }
49 |
50 | func main() {
51 | flag.Parse()
52 | logger := log.With(log.NewStdLogger(os.Stdout),
53 | "ts", log.DefaultTimestamp,
54 | "caller", log.DefaultCaller,
55 | "service.id", id,
56 | "service.name", Name,
57 | "service.version", Version,
58 | "trace.id", tracing.TraceID(),
59 | "span.id", tracing.SpanID(),
60 | )
61 | c := config.New(
62 | config.WithSource(
63 | file.NewSource(flagconf),
64 | ),
65 | )
66 | defer c.Close()
67 |
68 | if err := c.Load(); err != nil {
69 | panic(err)
70 | }
71 |
72 | var bc conf.Bootstrap
73 | if err := c.Scan(&bc); err != nil {
74 | panic(err)
75 | }
76 |
77 | app, cleanup, err := wireApp(bc.Server, bc.Data, logger)
78 | if err != nil {
79 | panic(err)
80 | }
81 | defer cleanup()
82 |
83 | // start and wait for stop signal
84 | if err := app.Run(); err != nil {
85 | panic(err)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/cmd/blog-service/wire.go:
--------------------------------------------------------------------------------
1 | //go:build wireinject
2 | // +build wireinject
3 |
4 | // The build tag makes sure the stub is not built in the final build.
5 |
6 | package main
7 |
8 | import (
9 | "blog-service/internal/biz"
10 | "blog-service/internal/conf"
11 | "blog-service/internal/data"
12 | "blog-service/internal/server"
13 | "blog-service/internal/service"
14 |
15 | "github.com/go-kratos/kratos/v2"
16 | "github.com/go-kratos/kratos/v2/log"
17 | "github.com/google/wire"
18 | )
19 |
20 | // wireApp init kratos application.
21 | func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
22 | panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
23 | }
24 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/cmd/blog-service/wire_gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by Wire. DO NOT EDIT.
2 |
3 | //go:generate go run -mod=mod github.com/google/wire/cmd/wire
4 | //go:build !wireinject
5 | // +build !wireinject
6 |
7 | package main
8 |
9 | import (
10 | "blog-service/internal/biz"
11 | "blog-service/internal/conf"
12 | "blog-service/internal/data"
13 | "blog-service/internal/server"
14 | "blog-service/internal/service"
15 | "github.com/go-kratos/kratos/v2"
16 | "github.com/go-kratos/kratos/v2/log"
17 | )
18 |
19 | import (
20 | _ "go.uber.org/automaxprocs"
21 | )
22 |
23 | // Injectors from wire.go:
24 |
25 | // wireApp init kratos application.
26 | func wireApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (*kratos.App, func(), error) {
27 | dataData, cleanup, err := data.NewData(confData, logger)
28 | if err != nil {
29 | return nil, nil, err
30 | }
31 | blogRepo := data.NewBlogRepo(dataData, logger)
32 | blogUsecase := biz.NewBlogUsecase(blogRepo, logger)
33 | blogService := service.NewBlogService(blogUsecase, logger)
34 | grpcServer := server.NewGRPCServer(confServer, blogService, logger)
35 | httpServer := server.NewHTTPServer(confServer, blogService, logger)
36 | app := newApp(logger, grpcServer, httpServer)
37 | return app, func() {
38 | cleanup()
39 | }, nil
40 | }
41 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/configs/config.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | http:
3 | addr: 0.0.0.0:8000
4 | timeout: 1s
5 | grpc:
6 | addr: 0.0.0.0:9000
7 | timeout: 1s
8 | data:
9 | database:
10 | driver: mysql
11 | source: root:root@tcp(127.0.0.1:3306)/test?parseTime=True&loc=Local
12 | redis:
13 | addr: 127.0.0.1:6379
14 | read_timeout: 0.2s
15 | write_timeout: 0.2s
16 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/go.mod:
--------------------------------------------------------------------------------
1 | module blog-service
2 |
3 | go 1.21
4 |
5 | toolchain go1.22.6
6 |
7 | require (
8 | github.com/go-kratos/kratos/v2 v2.8.0
9 | github.com/google/wire v0.6.0
10 | go.uber.org/automaxprocs v1.5.1
11 | google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157
12 | google.golang.org/grpc v1.65.0
13 | google.golang.org/protobuf v1.34.1
14 | )
15 |
16 | require (
17 | dario.cat/mergo v1.0.0 // indirect
18 | github.com/fsnotify/fsnotify v1.6.0 // indirect
19 | github.com/go-kratos/aegis v0.2.0 // indirect
20 | github.com/go-logr/logr v1.4.1 // indirect
21 | github.com/go-logr/stdr v1.2.2 // indirect
22 | github.com/go-playground/form/v4 v4.2.1 // indirect
23 | github.com/google/uuid v1.6.0 // indirect
24 | github.com/gorilla/mux v1.8.1 // indirect
25 | github.com/kr/text v0.2.0 // indirect
26 | go.opentelemetry.io/otel v1.24.0 // indirect
27 | go.opentelemetry.io/otel/metric v1.24.0 // indirect
28 | go.opentelemetry.io/otel/trace v1.24.0 // indirect
29 | golang.org/x/net v0.25.0 // indirect
30 | golang.org/x/sync v0.7.0 // indirect
31 | golang.org/x/sys v0.20.0 // indirect
32 | golang.org/x/text v0.15.0 // indirect
33 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
34 | gopkg.in/yaml.v3 v3.0.1 // indirect
35 | )
36 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/biz/README.md:
--------------------------------------------------------------------------------
1 | # Biz
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/biz/biz.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import (
4 | "github.com/google/wire"
5 | )
6 |
7 | // ProviderSet is biz providers.
8 | var ProviderSet = wire.NewSet(NewBlogUsecase)
9 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/biz/blog.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/go-kratos/kratos/v2/log"
8 | )
9 |
10 | // Blog 博客业务对象
11 | type Blog struct {
12 | ID string `json:"id"`
13 | Title string `json:"title"`
14 | Content string `json:"content"`
15 | UpdatedAt time.Time `json:"updated_at"`
16 | }
17 |
18 | // BlogRepo 博客存储接口
19 | type BlogRepo interface {
20 | List(ctx context.Context) ([]*Blog, error)
21 | Get(ctx context.Context, id string) (*Blog, error)
22 | Create(ctx context.Context, blog *Blog) (*Blog, error)
23 | Update(ctx context.Context, id string, blog *Blog) (*Blog, error)
24 | Delete(ctx context.Context, id string) error
25 | }
26 |
27 | // BlogUsecase 博客用例
28 | type BlogUsecase struct {
29 | repo BlogRepo
30 | log *log.Helper
31 | }
32 |
33 | // NewBlogUsecase 创建博客用例
34 | func NewBlogUsecase(repo BlogRepo, logger log.Logger) *BlogUsecase {
35 | return &BlogUsecase{repo: repo, log: log.NewHelper(logger)}
36 | }
37 |
38 | // List 获取所有博客
39 | func (uc *BlogUsecase) List(ctx context.Context) ([]*Blog, error) {
40 | return uc.repo.List(ctx)
41 | }
42 |
43 | // Get 获取单个博客
44 | func (uc *BlogUsecase) Get(ctx context.Context, id string) (*Blog, error) {
45 | return uc.repo.Get(ctx, id)
46 | }
47 |
48 | // Create 创建博客
49 | func (uc *BlogUsecase) Create(ctx context.Context, blog *Blog) (*Blog, error) {
50 | return uc.repo.Create(ctx, blog)
51 | }
52 |
53 | // Update 更新博客
54 | func (uc *BlogUsecase) Update(ctx context.Context, id string, blog *Blog) (*Blog, error) {
55 | return uc.repo.Update(ctx, id, blog)
56 | }
57 |
58 | // Delete 删除博客
59 | func (uc *BlogUsecase) Delete(ctx context.Context, id string) error {
60 | return uc.repo.Delete(ctx, id)
61 | }
62 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/conf/conf.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package kratos.api;
3 |
4 | option go_package = "blog-service/internal/conf;conf";
5 |
6 | import "google/protobuf/duration.proto";
7 |
8 | message Bootstrap {
9 | Server server = 1;
10 | Data data = 2;
11 | }
12 |
13 | message Server {
14 | message HTTP {
15 | string network = 1;
16 | string addr = 2;
17 | google.protobuf.Duration timeout = 3;
18 | }
19 | message GRPC {
20 | string network = 1;
21 | string addr = 2;
22 | google.protobuf.Duration timeout = 3;
23 | }
24 | HTTP http = 1;
25 | GRPC grpc = 2;
26 | }
27 |
28 | message Data {
29 | message Database {
30 | string driver = 1;
31 | string source = 2;
32 | }
33 | message Redis {
34 | string network = 1;
35 | string addr = 2;
36 | google.protobuf.Duration read_timeout = 3;
37 | google.protobuf.Duration write_timeout = 4;
38 | }
39 | Database database = 1;
40 | Redis redis = 2;
41 | }
42 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/data/README.md:
--------------------------------------------------------------------------------
1 | # Data
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/data/blog.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "context"
5 | "strconv"
6 | "time"
7 |
8 | "blog-service/internal/biz"
9 | "blog-service/internal/data/model"
10 |
11 | "github.com/go-kratos/kratos/v2/log"
12 | )
13 |
14 | type blogRepo struct {
15 | data *Data
16 | log *log.Helper
17 | }
18 |
19 | // NewBlogRepo .
20 | func NewBlogRepo(data *Data, logger log.Logger) biz.BlogRepo {
21 | return &blogRepo{
22 | data: data,
23 | log: log.NewHelper(logger),
24 | }
25 | }
26 |
27 | func (r *blogRepo) List(ctx context.Context) ([]*biz.Blog, error) {
28 | blogs, err := r.data.blogStore.List()
29 | if err != nil {
30 | return nil, err
31 | }
32 |
33 | result := make([]*biz.Blog, 0, len(blogs))
34 | for _, b := range blogs {
35 | result = append(result, &biz.Blog{
36 | ID: b.ID,
37 | Title: b.Title,
38 | Content: b.Content,
39 | UpdatedAt: b.UpdatedAt,
40 | })
41 | }
42 | return result, nil
43 | }
44 |
45 | func (r *blogRepo) Get(ctx context.Context, id string) (*biz.Blog, error) {
46 | blog, err := r.data.blogStore.Get(id)
47 | if err != nil {
48 | return nil, err
49 | }
50 | return &biz.Blog{
51 | ID: blog.ID,
52 | Title: blog.Title,
53 | Content: blog.Content,
54 | UpdatedAt: blog.UpdatedAt,
55 | }, nil
56 | }
57 |
58 | func (r *blogRepo) Create(ctx context.Context, blog *biz.Blog) (*biz.Blog, error) {
59 | blogModel := &model.Blog{
60 | ID: strconv.FormatInt(time.Now().UnixNano(), 10),
61 | Title: blog.Title,
62 | Content: blog.Content,
63 | UpdatedAt: time.Now(),
64 | }
65 |
66 | r.log.Infof("blogRepo Create blogModel %v\n", blogModel)
67 | if err := r.data.blogStore.Save(blogModel); err != nil {
68 | return nil, err
69 | }
70 |
71 | return &biz.Blog{
72 | ID: blogModel.ID,
73 | Title: blogModel.Title,
74 | Content: blogModel.Content,
75 | UpdatedAt: blogModel.UpdatedAt,
76 | }, nil
77 | }
78 |
79 | func (r *blogRepo) Update(ctx context.Context, id string, blog *biz.Blog) (*biz.Blog, error) {
80 | // 首先检查博客是否存在
81 | _, err := r.data.blogStore.Get(id)
82 | if err != nil {
83 | return nil, err
84 | }
85 |
86 | blogModel := &model.Blog{
87 | ID: id,
88 | Title: blog.Title,
89 | Content: blog.Content,
90 | UpdatedAt: time.Now(),
91 | }
92 |
93 | if err := r.data.blogStore.Save(blogModel); err != nil {
94 | return nil, err
95 | }
96 |
97 | return &biz.Blog{
98 | ID: blogModel.ID,
99 | Title: blogModel.Title,
100 | Content: blogModel.Content,
101 | UpdatedAt: blogModel.UpdatedAt,
102 | }, nil
103 | }
104 |
105 | func (r *blogRepo) Delete(ctx context.Context, id string) error {
106 | return r.data.blogStore.Delete(id)
107 | }
108 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/data/data.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "blog-service/internal/conf"
5 | "blog-service/internal/data/model"
6 |
7 | "github.com/go-kratos/kratos/v2/log"
8 | "github.com/google/wire"
9 | )
10 |
11 | // ProviderSet is data providers.
12 | var ProviderSet = wire.NewSet(NewData, NewBlogRepo)
13 |
14 | // Data .
15 | type Data struct {
16 | blogStore model.BlogStore
17 | }
18 |
19 | // NewData .
20 | func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {
21 | blogStore, err := model.NewCSVBlogStore("blogs.csv")
22 | if err != nil {
23 | return nil, nil, err
24 | }
25 |
26 | cleanup := func() {
27 | log.NewHelper(logger).Info("closing the data resources")
28 | }
29 |
30 | return &Data{
31 | blogStore: blogStore,
32 | }, cleanup, nil
33 | }
34 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/server/grpc.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "blog-service/api/blog/v1"
5 | "blog-service/internal/conf"
6 | "blog-service/internal/service"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/grpc"
11 | )
12 |
13 | // NewGRPCServer new a gRPC server.
14 | func NewGRPCServer(c *conf.Server, blog *service.BlogService, logger log.Logger) *grpc.Server {
15 | var opts = []grpc.ServerOption{
16 | grpc.Middleware(
17 | recovery.Recovery(),
18 | ),
19 | }
20 | if c.Grpc.Network != "" {
21 | opts = append(opts, grpc.Network(c.Grpc.Network))
22 | }
23 | if c.Grpc.Addr != "" {
24 | opts = append(opts, grpc.Address(c.Grpc.Addr))
25 | }
26 | if c.Grpc.Timeout != nil {
27 | opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
28 | }
29 | srv := grpc.NewServer(opts...)
30 | v1.RegisterBlogServer(srv, blog)
31 | return srv
32 | }
33 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/server/http.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "blog-service/api/blog/v1"
5 | "blog-service/internal/conf"
6 | "blog-service/internal/service"
7 |
8 | nethttp "net/http"
9 |
10 | "github.com/go-kratos/kratos/v2/log"
11 | "github.com/go-kratos/kratos/v2/middleware/recovery"
12 | "github.com/go-kratos/kratos/v2/transport/http"
13 | )
14 |
15 | // NewHTTPServer new an HTTP server.
16 | func NewHTTPServer(c *conf.Server, blog *service.BlogService, logger log.Logger) *http.Server {
17 | var opts = []http.ServerOption{
18 | http.Middleware(
19 | recovery.Recovery(),
20 | ),
21 | // 添加CORS支持
22 | http.Filter(
23 | func(handler nethttp.Handler) nethttp.Handler {
24 | return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) {
25 | w.Header().Set("Access-Control-Allow-Origin", "*")
26 | w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
27 | w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
28 |
29 | if r.Method == "OPTIONS" {
30 | w.WriteHeader(200)
31 | return
32 | }
33 |
34 | handler.ServeHTTP(w, r)
35 | })
36 | },
37 | ),
38 | }
39 | if c.Http.Network != "" {
40 | opts = append(opts, http.Network(c.Http.Network))
41 | }
42 | if c.Http.Addr != "" {
43 | opts = append(opts, http.Address(c.Http.Addr))
44 | }
45 | if c.Http.Timeout != nil {
46 | opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
47 | }
48 | srv := http.NewServer(opts...)
49 | v1.RegisterBlogHTTPServer(srv, blog)
50 | return srv
51 | }
52 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/google/wire"
5 | )
6 |
7 | // ProviderSet is server providers.
8 | var ProviderSet = wire.NewSet(NewGRPCServer, NewHTTPServer)
9 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/service/README.md:
--------------------------------------------------------------------------------
1 | # Service
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/internal/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/google/wire"
5 | )
6 |
7 | // ProviderSet is service providers.
8 | var ProviderSet = wire.NewSet(NewBlogService)
9 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/README.md:
--------------------------------------------------------------------------------
1 | # third_party
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/errors/errors.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package errors;
4 |
5 | option go_package = "github.com/go-kratos/kratos/v2/errors;errors";
6 | option java_multiple_files = true;
7 | option java_package = "com.github.kratos.errors";
8 | option objc_class_prefix = "KratosErrors";
9 |
10 | import "google/protobuf/descriptor.proto";
11 |
12 | extend google.protobuf.EnumOptions {
13 | int32 default_code = 1108;
14 | }
15 |
16 | extend google.protobuf.EnumValueOptions {
17 | int32 code = 1109;
18 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "AnnotationsProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | extend google.protobuf.MethodOptions {
29 | // See `HttpRule`.
30 | HttpRule http = 72295728;
31 | }
32 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/google/protobuf/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | // The JSON representation for `Empty` is empty JSON object `{}`.
52 | message Empty {}
53 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/google/protobuf/source_context.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "SourceContextProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
41 |
42 | // `SourceContext` represents information about the source of a
43 | // protobuf element, like the file in which it is defined.
44 | message SourceContext {
45 | // The path-qualified name of the .proto file that contained the associated
46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`.
47 | string file_name = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/openapi/v3/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package openapi.v3;
18 |
19 | import "openapi/v3/openapi.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | // This option lets the proto compiler generate Java code inside the package
23 | // name (see below) instead of inside an outer class. It creates a simpler
24 | // developer experience by reducing one-level of name nesting and be
25 | // consistent with most programming languages that don't support outer classes.
26 | option java_multiple_files = true;
27 |
28 | // The Java outer classname should be the filename in UpperCamelCase. This
29 | // class is only used to hold proto descriptor, so developers don't need to
30 | // work with it directly.
31 | option java_outer_classname = "AnnotationsProto";
32 |
33 | // The Java package name must be proto package name with proper prefix.
34 | option java_package = "org.openapi_v3";
35 |
36 | // A reasonable prefix for the Objective-C symbols generated from the package.
37 | // It should at a minimum be 3 characters long, all uppercase, and convention
38 | // is to use an abbreviation of the package name. Something short, but
39 | // hopefully unique enough to not conflict with things that may come along in
40 | // the future. 'GPB' is reserved for the protocol buffer implementation itself.
41 | option objc_class_prefix = "OAS";
42 |
43 | // The Go package name.
44 | option go_package = "github.com/google/gnostic/openapiv3;openapi_v3";
45 |
46 | extend google.protobuf.FileOptions {
47 | Document document = 1143;
48 | }
49 |
50 | extend google.protobuf.MethodOptions {
51 | Operation operation = 1143;
52 | }
53 |
54 | extend google.protobuf.MessageOptions {
55 | Schema schema = 1143;
56 | }
57 |
58 | extend google.protobuf.FieldOptions {
59 | Schema property = 1143;
60 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/blog-demo/backend/blog-service/third_party/validate/README.md:
--------------------------------------------------------------------------------
1 | # protoc-gen-validate (PGV)
2 |
3 | * https://github.com/envoyproxy/protoc-gen-validate
4 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/docker-intro/Dockerfile:
--------------------------------------------------------------------------------
1 | # 使用官方 Node.js 镜像作为基础镜像
2 | FROM node:16-alpine
3 |
4 | # 设置工作目录
5 | WORKDIR /app
6 |
7 | # 复制 package.json 和 package-lock.json 到工作目录
8 | COPY package*.json ./
9 |
10 | # 安装依赖
11 | RUN npm install
12 |
13 | # 复制应用程序代码到工作目录
14 | COPY . .
15 |
16 | # 暴露应用程序运行的端口
17 | EXPOSE 3000
18 |
19 | # 定义环境变量
20 | ENV NODE_ENV=production
21 |
22 | # 运行应用程序
23 | CMD ["npm", "start"]
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/docker-intro/app.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const port = process.env.PORT || 3000;
4 |
5 | app.get('/', (req, res) => {
6 | res.send(`
7 | Docker 示例应用
8 | 这是一个运行在 Docker 容器中的简单 Node.js 应用。
9 | 环境: ${process.env.NODE_ENV || '开发'}
10 | 主机名: ${process.env.HOSTNAME || '未知'}
11 | 当前时间: ${new Date().toLocaleString()}
12 | `);
13 | });
14 |
15 | app.get('/health', (req, res) => {
16 | res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
17 | });
18 |
19 | app.listen(port, () => {
20 | console.log(`应用正在监听端口 ${port}`);
21 | console.log(`环境: ${process.env.NODE_ENV || '开发'}`);
22 | });
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/docker-intro/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | # Web 应用服务
5 | webapp:
6 | build: .
7 | container_name: my-web-app
8 | ports:
9 | - "3000:3000"
10 | environment:
11 | - NODE_ENV=production
12 | - DB_HOST=db
13 | - DB_PORT=5432
14 | - DB_USER=postgres
15 | - DB_PASSWORD=mysecretpassword
16 | - DB_NAME=mydatabase
17 | volumes:
18 | # - ./app:/app
19 | - /app/node_modules
20 | depends_on:
21 | - db
22 | restart: always
23 | networks:
24 | - app-network
25 |
26 | # 数据库服务
27 | db:
28 | image: postgres:13
29 | container_name: my-postgres
30 | ports:
31 | - "5432:5432"
32 | environment:
33 | - POSTGRES_USER=postgres
34 | - POSTGRES_PASSWORD=mysecretpassword
35 | - POSTGRES_DB=mydatabase
36 | volumes:
37 | - postgres-data:/var/lib/postgresql/data
38 | networks:
39 | - app-network
40 |
41 | # Redis 缓存服务
42 | redis:
43 | image: redis:6-alpine
44 | container_name: my-redis
45 | ports:
46 | - "6379:6379"
47 | networks:
48 | - app-network
49 |
50 | networks:
51 | app-network:
52 | driver: bridge
53 |
54 | volumes:
55 | postgres-data:
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/docker-intro/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docker-demo-app",
3 | "version": "1.0.0",
4 | "description": "Docker 示例应用",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "node app.js",
8 | "dev": "nodemon app.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [
12 | "docker",
13 | "nodejs",
14 | "express"
15 | ],
16 | "author": "Docker 学习者",
17 | "license": "MIT",
18 | "dependencies": {
19 | "express": "^4.17.1"
20 | },
21 | "devDependencies": {
22 | "nodemon": "^2.0.15"
23 | }
24 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/golang-intro/hello.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strconv"
7 | )
8 |
9 | func main() {
10 | // 检查命令行参数
11 | if len(os.Args) > 1 && os.Args[1] == "syntax" {
12 | // 如果提供了syntax参数,则运行语法示例
13 | runSyntaxExamples()
14 | return
15 | }
16 |
17 | // 原来的Hello World程序
18 | // 打印 Hello World
19 | fmt.Println("Hello, Golang!")
20 |
21 | // 变量声明与使用
22 | var name string = "学习者"
23 | age := 25
24 | fmt.Printf("你好,%s!你今年 %d 岁。\n", name, age)
25 |
26 | // 简单的条件语句
27 | if age >= 18 {
28 | fmt.Println("你已经成年了")
29 | } else {
30 | fmt.Println("你还未成年")
31 | }
32 |
33 | // 创建一个切片并遍历
34 | languages := []string{"Go", "Python", "Java", "JavaScript"}
35 | fmt.Println("流行的编程语言:")
36 | for i, lang := range languages {
37 | fmt.Printf("%d: %s\n", i+1, lang)
38 | }
39 |
40 | // 调用函数
41 | result := add(10, 20)
42 | fmt.Printf("10 + 20 = %d\n", result)
43 |
44 | // 输出提示信息
45 | fmt.Println("\n提示: 运行 'go run hello.go syntax' 以查看更多语法示例")
46 | fmt.Println("或者运行 'go run hello.go syntax <数字>' 以查看特定类别的示例:")
47 | fmt.Println("1: 切片示例")
48 | fmt.Println("2: 映射示例")
49 | fmt.Println("3: 通道示例")
50 | fmt.Println("4: 遍历方法示例")
51 | fmt.Println("5: 错误处理示例")
52 | fmt.Println("6: 结构体和方法示例")
53 | }
54 |
55 | // 定义一个简单的函数
56 | func add(a, b int) int {
57 | return a + b
58 | }
59 |
60 | // 运行语法示例的函数
61 | func runSyntaxExamples() {
62 | if len(os.Args) > 2 {
63 | // 如果提供了第二个参数,根据参数运行特定示例
64 | category, err := strconv.Atoi(os.Args[2])
65 | if err != nil {
66 | fmt.Println("无效的参数,请提供1-6之间的数字")
67 | return
68 | }
69 |
70 | switch category {
71 | case 1:
72 | RunSliceExamples()
73 | case 2:
74 | RunMapExamples()
75 | case 3:
76 | RunChannelExamples()
77 | case 4:
78 | RunIterationExamples()
79 | case 5:
80 | RunErrorHandlingExamples()
81 | case 6:
82 | RunStructExamples()
83 | default:
84 | fmt.Println("无效的类别,请提供1-6之间的数字")
85 | }
86 | } else {
87 | // 如果没有提供第二个参数,运行所有示例
88 | RunAllExamples()
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/golang-intro/syntax_examples_README.md:
--------------------------------------------------------------------------------
1 | # Go语言语法示例说明
2 |
3 | 这个文件包含了Go语言常用语法特性的示例代码,适合Go语言初学者参考。运行这个示例可以加深对Go语言核心特性的理解。
4 |
5 | ## 示例内容
6 |
7 | 该示例包含以下六个主要部分:
8 |
9 | 1. **切片(Slice)** - 展示了Go语言中切片的创建、操作和内部原理
10 | 2. **映射(Map)** - 介绍了映射的创建及常用操作
11 | 3. **通道(Channel)** - 演示了通道的基本用法和常见模式
12 | 4. **遍历方法** - 展示如何遍历不同类型的数据结构
13 | 5. **错误处理** - 包含错误返回、panic/recover和defer的示例
14 | 6. **结构体和方法** - 演示结构体定义、方法、接口和并发安全
15 |
16 | ## 如何运行
17 |
18 | 你可以通过以下方式运行这些示例:
19 |
20 | ```bash
21 | # 运行所有语法示例
22 | go run ./ syntax
23 |
24 | # 运行特定类别的示例(用1-6的数字替换)
25 | go run ./ syntax
26 | ```
27 |
28 | 类别对应关系:
29 | 1. 切片示例
30 | 2. 映射示例
31 | 3. 通道示例
32 | 4. 遍历方法示例
33 | 5. 错误处理示例
34 | 6. 结构体和方法示例
35 |
36 | ## 关键特性详解
37 |
38 | ### 1. 切片(Slice)
39 |
40 | Go中的切片是对数组的抽象,它比数组更灵活、强大。示例展示了:
41 |
42 | - 通过字面量、make函数和从数组创建切片的不同方式
43 | - 切片的添加、复制、合并等操作
44 | - 切片的内部工作原理(底层数组共享和扩容机制)
45 |
46 | ### 2. 映射(Map)
47 |
48 | 映射是哈希表的实现,用于存储键值对:
49 |
50 | - 通过字面量和make函数创建映射
51 | - 添加、删除、查找元素的操作
52 | - 检查键是否存在
53 | - 映射的引用特性
54 |
55 | ### 3. 通道(Channel)
56 |
57 | 通道是Go并发编程的核心,用于goroutine之间的通信:
58 |
59 | - 无缓冲和有缓冲通道的创建
60 | - 通道的发送和接收操作
61 | - 通道方向限制(只读、只写)
62 | - 使用select处理多个通道
63 | - 使用通道进行goroutine同步
64 |
65 | ### 4. 遍历方法
66 |
67 | Go提供了灵活的遍历语法,适用于多种数据类型:
68 |
69 | - 数组和切片的遍历(索引遍历和range遍历)
70 | - 映射的遍历(键值对)
71 | - 字符串遍历(字节和Unicode字符)
72 | - 通道遍历
73 |
74 | ### 5. 错误处理
75 |
76 | Go的错误处理机制简单而有效:
77 |
78 | - 返回错误值
79 | - panic和recover机制
80 | - defer语句及其执行顺序
81 | - 资源清理的最佳实践
82 |
83 | ### 6. 结构体和方法
84 |
85 | Go的面向对象特性主要通过结构体和接口实现:
86 |
87 | - 结构体的定义和创建
88 | - 值接收者和指针接收者方法
89 | - 结构体嵌套
90 | - 接口定义和实现
91 | - 并发安全的结构体
92 |
93 | ## 学习建议
94 |
95 | 1. 先浏览整个代码文件,了解Go语言的核心特性
96 | 2. 运行代码,观察输出结果
97 | 3. 尝试修改代码,查看不同的行为
98 | 4. 针对感兴趣的部分深入学习相关知识
99 |
100 | Go语言强调简洁、效率和并发性,通过这些示例可以帮助你更好地理解Go的设计理念和编程模式。
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | "github.com/go-kratos/kratos/v2"
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/http"
11 | )
12 |
13 | // 这是一个简化的 Kratos 应用示例
14 | // 在真实环境中,需要安装 Kratos 并使用 kratos 命令行工具生成完整项目
15 |
16 | func main() {
17 | // 初始化 logger
18 | logger := log.NewStdLogger(os.Stdout)
19 | log := log.NewHelper(logger)
20 |
21 | // 创建 HTTP 服务器
22 | httpSrv := http.NewServer(
23 | http.Address(":8000"),
24 | http.Middleware(
25 | recovery.Recovery(), // 添加异常恢复中间件
26 | ),
27 | )
28 | // 使用路由组
29 | r := httpSrv.Route("/")
30 | r.GET("", func(ctx http.Context) error {
31 | return ctx.String(200, "Hello Kratos!")
32 | })
33 |
34 | r.GET("/hello", func(ctx http.Context) error {
35 | return ctx.String(200, "Hello Kratos API!")
36 | })
37 |
38 | // 创建 Kratos 应用实例
39 | app := kratos.New(
40 | kratos.Name("kratos-demo"),
41 | kratos.Server(
42 | httpSrv,
43 | ),
44 | kratos.Logger(logger),
45 | )
46 |
47 | // 启动应用
48 | if err := app.Run(); err != nil {
49 | log.Errorf("启动应用失败: %v", err)
50 | return
51 | }
52 |
53 | fmt.Println("服务启动成功,监听端口: 8000")
54 | }
55 |
56 | // 说明:这是示例代码,需要先安装依赖才能运行
57 | // 安装 Kratos:
58 | // go get github.com/go-kratos/kratos/v2
59 | //
60 | // 完整项目建议使用 Kratos 工具创建:
61 | // go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
62 | // kratos new server
63 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/.gitignore:
--------------------------------------------------------------------------------
1 | # Reference https://github.com/github/gitignore/blob/master/Go.gitignore
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | vendor/
16 |
17 | # Go workspace file
18 | go.work
19 |
20 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
21 | *.o
22 | *.a
23 | *.so
24 |
25 | # OS General
26 | Thumbs.db
27 | .DS_Store
28 |
29 | # project
30 | *.cert
31 | *.key
32 | *.log
33 | bin/
34 |
35 | # Develop tools
36 | .vscode/
37 | .idea/
38 | *.swp
39 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.19 AS builder
2 |
3 | COPY . /src
4 | WORKDIR /src
5 |
6 | RUN GOPROXY=https://goproxy.cn make build
7 |
8 | FROM debian:stable-slim
9 |
10 | RUN apt-get update && apt-get install -y --no-install-recommends \
11 | ca-certificates \
12 | netbase \
13 | && rm -rf /var/lib/apt/lists/ \
14 | && apt-get autoremove -y && apt-get autoclean -y
15 |
16 | COPY --from=builder /src/bin /app
17 |
18 | WORKDIR /app
19 |
20 | EXPOSE 8000
21 | EXPOSE 9000
22 | VOLUME /data/conf
23 |
24 | CMD ["./server", "-conf", "/data/conf"]
25 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 go-kratos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/Makefile:
--------------------------------------------------------------------------------
1 | GOHOSTOS:=$(shell go env GOHOSTOS)
2 | GOPATH:=$(shell go env GOPATH)
3 | VERSION=$(shell git describe --tags --always)
4 |
5 | ifeq ($(GOHOSTOS), windows)
6 | #the `find.exe` is different from `find` in bash/shell.
7 | #to see https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/find.
8 | #changed to use git-bash.exe to run find cli or other cli friendly, caused of every developer has a Git.
9 | #Git_Bash= $(subst cmd\,bin\bash.exe,$(dir $(shell where git)))
10 | Git_Bash=$(subst \,/,$(subst cmd\,bin\bash.exe,$(dir $(shell where git))))
11 | INTERNAL_PROTO_FILES=$(shell $(Git_Bash) -c "find internal -name *.proto")
12 | API_PROTO_FILES=$(shell $(Git_Bash) -c "find api -name *.proto")
13 | else
14 | INTERNAL_PROTO_FILES=$(shell find internal -name *.proto)
15 | API_PROTO_FILES=$(shell find api -name *.proto)
16 | endif
17 |
18 | .PHONY: init
19 | # init env
20 | init:
21 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
22 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
23 | go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
24 | go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest
25 | go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest
26 | go install github.com/google/wire/cmd/wire@latest
27 |
28 | .PHONY: config
29 | # generate internal proto
30 | config:
31 | protoc --proto_path=./internal \
32 | --proto_path=./third_party \
33 | --go_out=paths=source_relative:./internal \
34 | $(INTERNAL_PROTO_FILES)
35 |
36 | .PHONY: api
37 | # generate api proto
38 | api:
39 | protoc --proto_path=./api \
40 | --proto_path=./third_party \
41 | --go_out=paths=source_relative:./api \
42 | --go-http_out=paths=source_relative:./api \
43 | --go-grpc_out=paths=source_relative:./api \
44 | --openapi_out=fq_schema_naming=true,default_response=false:. \
45 | $(API_PROTO_FILES)
46 |
47 | .PHONY: build
48 | # build
49 | build:
50 | mkdir -p bin/ && go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./...
51 |
52 | .PHONY: generate
53 | # generate
54 | generate:
55 | go generate ./...
56 | go mod tidy
57 |
58 | .PHONY: all
59 | # generate all
60 | all:
61 | make api;
62 | make config;
63 | make generate;
64 |
65 | # show help
66 | help:
67 | @echo ''
68 | @echo 'Usage:'
69 | @echo ' make [target]'
70 | @echo ''
71 | @echo 'Targets:'
72 | @awk '/^[a-zA-Z\-\_0-9]+:/ { \
73 | helpMessage = match(lastLine, /^# (.*)/); \
74 | if (helpMessage) { \
75 | helpCommand = substr($$1, 0, index($$1, ":")); \
76 | helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \
77 | printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \
78 | } \
79 | } \
80 | { lastLine = $$0 }' $(MAKEFILE_LIST)
81 |
82 | .DEFAULT_GOAL := help
83 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/README.md:
--------------------------------------------------------------------------------
1 | # Kratos Project Template
2 |
3 | ## Install Kratos
4 | ```
5 | go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
6 | ```
7 | ## Create a service
8 | ```
9 | # Create a template project
10 | kratos new server
11 |
12 | cd server
13 | # Add a proto template
14 | kratos proto add api/server/server.proto
15 | # Generate the proto code
16 | kratos proto client api/server/server.proto
17 | # Generate the source code of service by proto file
18 | kratos proto server api/server/server.proto -t internal/service
19 |
20 | go generate ./...
21 | go build -o ./bin/ ./...
22 | ./bin/server -conf ./configs
23 | ```
24 | ## Generate other auxiliary files by Makefile
25 | ```
26 | # Download and update dependencies
27 | make init
28 | # Generate API files (include: pb.go, http, grpc, validate, swagger) by proto file
29 | make api
30 | # Generate all files
31 | make all
32 | ```
33 | ## Automated Initialization (wire)
34 | ```
35 | # install wire
36 | go get github.com/google/wire/cmd/wire
37 |
38 | # generate wire
39 | cd cmd/server
40 | wire
41 | ```
42 |
43 | ## Docker
44 | ```bash
45 | # build
46 | docker build -t .
47 |
48 | # run
49 | docker run --rm -p 8000:8000 -p 9000:9000 -v :/data/conf
50 | ```
51 |
52 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/api/helloworld/v1/error_reason.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package helloworld.v1;
4 |
5 | option go_package = "myproject/api/helloworld/v1;v1";
6 | option java_multiple_files = true;
7 | option java_package = "helloworld.v1";
8 | option objc_class_prefix = "APIHelloworldV1";
9 |
10 | enum ErrorReason {
11 | GREETER_UNSPECIFIED = 0;
12 | USER_NOT_FOUND = 1;
13 | }
14 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/api/helloworld/v1/greeter.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package helloworld.v1;
4 |
5 | import "google/api/annotations.proto";
6 |
7 | option go_package = "myproject/api/helloworld/v1;v1";
8 | option java_multiple_files = true;
9 | option java_package = "dev.kratos.api.helloworld.v1";
10 | option java_outer_classname = "HelloworldProtoV1";
11 |
12 | // The greeting service definition.
13 | service Greeter {
14 | // Sends a greeting
15 | rpc SayHello (HelloRequest) returns (HelloReply) {
16 | option (google.api.http) = {
17 | get: "/helloworld/{name}"
18 | };
19 | }
20 | }
21 |
22 | // The request message containing the user's name.
23 | message HelloRequest {
24 | string name = 1;
25 | }
26 |
27 | // The response message containing the greetings
28 | message HelloReply {
29 | string message = 1;
30 | }
31 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/api/helloworld/v1/greeter_http.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-http. DO NOT EDIT.
2 | // versions:
3 | // protoc-gen-go-http v2.1.3
4 |
5 | package v1
6 |
7 | import (
8 | context "context"
9 | http "github.com/go-kratos/kratos/v2/transport/http"
10 | binding "github.com/go-kratos/kratos/v2/transport/http/binding"
11 | )
12 |
13 | // This is a compile-time assertion to ensure that this generated file
14 | // is compatible with the kratos package it is being compiled against.
15 | var _ = new(context.Context)
16 | var _ = binding.EncodeURL
17 |
18 | const _ = http.SupportPackageIsVersion1
19 |
20 | type GreeterHTTPServer interface {
21 | SayHello(context.Context, *HelloRequest) (*HelloReply, error)
22 | }
23 |
24 | func RegisterGreeterHTTPServer(s *http.Server, srv GreeterHTTPServer) {
25 | r := s.Route("/")
26 | r.GET("/helloworld/{name}", _Greeter_SayHello0_HTTP_Handler(srv))
27 | }
28 |
29 | func _Greeter_SayHello0_HTTP_Handler(srv GreeterHTTPServer) func(ctx http.Context) error {
30 | return func(ctx http.Context) error {
31 | var in HelloRequest
32 | if err := ctx.BindQuery(&in); err != nil {
33 | return err
34 | }
35 | if err := ctx.BindVars(&in); err != nil {
36 | return err
37 | }
38 | http.SetOperation(ctx, "/helloworld.v1.Greeter/SayHello")
39 | h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
40 | return srv.SayHello(ctx, req.(*HelloRequest))
41 | })
42 | out, err := h(ctx, &in)
43 | if err != nil {
44 | return err
45 | }
46 | reply := out.(*HelloReply)
47 | return ctx.Result(200, reply)
48 | }
49 | }
50 |
51 | type GreeterHTTPClient interface {
52 | SayHello(ctx context.Context, req *HelloRequest, opts ...http.CallOption) (rsp *HelloReply, err error)
53 | }
54 |
55 | type GreeterHTTPClientImpl struct {
56 | cc *http.Client
57 | }
58 |
59 | func NewGreeterHTTPClient(client *http.Client) GreeterHTTPClient {
60 | return &GreeterHTTPClientImpl{client}
61 | }
62 |
63 | func (c *GreeterHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http.CallOption) (*HelloReply, error) {
64 | var out HelloReply
65 | pattern := "/helloworld/{name}"
66 | path := binding.EncodeURL(pattern, in, true)
67 | opts = append(opts, http.Operation("/helloworld.v1.Greeter/SayHello"))
68 | opts = append(opts, http.PathTemplate(pattern))
69 | err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
70 | if err != nil {
71 | return nil, err
72 | }
73 | return &out, err
74 | }
75 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/cmd/myproject/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "os"
6 |
7 | "myproject/internal/conf"
8 |
9 | "github.com/go-kratos/kratos/v2"
10 | "github.com/go-kratos/kratos/v2/config"
11 | "github.com/go-kratos/kratos/v2/config/file"
12 | "github.com/go-kratos/kratos/v2/log"
13 | "github.com/go-kratos/kratos/v2/middleware/tracing"
14 | "github.com/go-kratos/kratos/v2/transport/grpc"
15 | "github.com/go-kratos/kratos/v2/transport/http"
16 |
17 | _ "go.uber.org/automaxprocs"
18 | )
19 |
20 | // go build -ldflags "-X main.Version=x.y.z"
21 | var (
22 | // Name is the name of the compiled software.
23 | Name string
24 | // Version is the version of the compiled software.
25 | Version string
26 | // flagconf is the config flag.
27 | flagconf string
28 |
29 | id, _ = os.Hostname()
30 | )
31 |
32 | func init() {
33 | flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
34 | }
35 |
36 | func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {
37 | return kratos.New(
38 | kratos.ID(id),
39 | kratos.Name(Name),
40 | kratos.Version(Version),
41 | kratos.Metadata(map[string]string{}),
42 | kratos.Logger(logger),
43 | kratos.Server(
44 | gs,
45 | hs,
46 | ),
47 | )
48 | }
49 |
50 | func main() {
51 | flag.Parse()
52 | logger := log.With(log.NewStdLogger(os.Stdout),
53 | "ts", log.DefaultTimestamp,
54 | "caller", log.DefaultCaller,
55 | "service.id", id,
56 | "service.name", Name,
57 | "service.version", Version,
58 | "trace.id", tracing.TraceID(),
59 | "span.id", tracing.SpanID(),
60 | )
61 | c := config.New(
62 | config.WithSource(
63 | file.NewSource(flagconf),
64 | ),
65 | )
66 | defer c.Close()
67 |
68 | if err := c.Load(); err != nil {
69 | panic(err)
70 | }
71 |
72 | var bc conf.Bootstrap
73 | if err := c.Scan(&bc); err != nil {
74 | panic(err)
75 | }
76 |
77 | app, cleanup, err := wireApp(bc.Server, bc.Data, logger)
78 | if err != nil {
79 | panic(err)
80 | }
81 | defer cleanup()
82 |
83 | // start and wait for stop signal
84 | if err := app.Run(); err != nil {
85 | panic(err)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/cmd/myproject/wire.go:
--------------------------------------------------------------------------------
1 | //go:build wireinject
2 | // +build wireinject
3 |
4 | // The build tag makes sure the stub is not built in the final build.
5 |
6 | package main
7 |
8 | import (
9 | "myproject/internal/biz"
10 | "myproject/internal/conf"
11 | "myproject/internal/data"
12 | "myproject/internal/server"
13 | "myproject/internal/service"
14 |
15 | "github.com/go-kratos/kratos/v2"
16 | "github.com/go-kratos/kratos/v2/log"
17 | "github.com/google/wire"
18 | )
19 |
20 | // wireApp init kratos application.
21 | func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
22 | panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
23 | }
24 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/cmd/myproject/wire_gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by Wire. DO NOT EDIT.
2 |
3 | //go:generate go run -mod=mod github.com/google/wire/cmd/wire
4 | //go:build !wireinject
5 | // +build !wireinject
6 |
7 | package main
8 |
9 | import (
10 | "myproject/internal/biz"
11 | "myproject/internal/conf"
12 | "myproject/internal/data"
13 | "myproject/internal/server"
14 | "myproject/internal/service"
15 | "github.com/go-kratos/kratos/v2"
16 | "github.com/go-kratos/kratos/v2/log"
17 | )
18 |
19 | import (
20 | _ "go.uber.org/automaxprocs"
21 | )
22 |
23 | // Injectors from wire.go:
24 |
25 | // wireApp init kratos application.
26 | func wireApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (*kratos.App, func(), error) {
27 | dataData, cleanup, err := data.NewData(confData, logger)
28 | if err != nil {
29 | return nil, nil, err
30 | }
31 | greeterRepo := data.NewGreeterRepo(dataData, logger)
32 | greeterUsecase := biz.NewGreeterUsecase(greeterRepo, logger)
33 | greeterService := service.NewGreeterService(greeterUsecase)
34 | grpcServer := server.NewGRPCServer(confServer, greeterService, logger)
35 | httpServer := server.NewHTTPServer(confServer, greeterService, logger)
36 | app := newApp(logger, grpcServer, httpServer)
37 | return app, func() {
38 | cleanup()
39 | }, nil
40 | }
41 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/configs/config.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | http:
3 | addr: 0.0.0.0:8000
4 | timeout: 1s
5 | grpc:
6 | addr: 0.0.0.0:9000
7 | timeout: 1s
8 | data:
9 | database:
10 | driver: mysql
11 | source: root:root@tcp(127.0.0.1:3306)/test?parseTime=True&loc=Local
12 | redis:
13 | addr: 127.0.0.1:6379
14 | read_timeout: 0.2s
15 | write_timeout: 0.2s
16 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/go.mod:
--------------------------------------------------------------------------------
1 | module myproject
2 |
3 | go 1.21
4 |
5 | toolchain go1.22.6
6 |
7 | require (
8 | github.com/go-kratos/kratos/v2 v2.8.0
9 | github.com/google/wire v0.6.0
10 | go.uber.org/automaxprocs v1.5.1
11 | google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157
12 | google.golang.org/grpc v1.65.0
13 | google.golang.org/protobuf v1.34.1
14 | )
15 |
16 | require (
17 | dario.cat/mergo v1.0.0 // indirect
18 | github.com/fsnotify/fsnotify v1.6.0 // indirect
19 | github.com/go-kratos/aegis v0.2.0 // indirect
20 | github.com/go-logr/logr v1.4.1 // indirect
21 | github.com/go-logr/stdr v1.2.2 // indirect
22 | github.com/go-playground/form/v4 v4.2.1 // indirect
23 | github.com/google/uuid v1.6.0 // indirect
24 | github.com/gorilla/mux v1.8.1 // indirect
25 | github.com/kr/text v0.2.0 // indirect
26 | go.opentelemetry.io/otel v1.24.0 // indirect
27 | go.opentelemetry.io/otel/metric v1.24.0 // indirect
28 | go.opentelemetry.io/otel/trace v1.24.0 // indirect
29 | golang.org/x/net v0.25.0 // indirect
30 | golang.org/x/sync v0.7.0 // indirect
31 | golang.org/x/sys v0.20.0 // indirect
32 | golang.org/x/text v0.15.0 // indirect
33 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
34 | gopkg.in/yaml.v3 v3.0.1 // indirect
35 | )
36 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/biz/README.md:
--------------------------------------------------------------------------------
1 | # Biz
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/biz/biz.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import "github.com/google/wire"
4 |
5 | // ProviderSet is biz providers.
6 | var ProviderSet = wire.NewSet(NewGreeterUsecase)
7 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/biz/greeter.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import (
4 | "context"
5 |
6 | v1 "myproject/api/helloworld/v1"
7 |
8 | "github.com/go-kratos/kratos/v2/errors"
9 | "github.com/go-kratos/kratos/v2/log"
10 | )
11 |
12 | var (
13 | // ErrUserNotFound is user not found.
14 | ErrUserNotFound = errors.NotFound(v1.ErrorReason_USER_NOT_FOUND.String(), "user not found")
15 | )
16 |
17 | // Greeter is a Greeter model.
18 | type Greeter struct {
19 | Hello string
20 | }
21 |
22 | // GreeterRepo is a Greater repo.
23 | type GreeterRepo interface {
24 | Save(context.Context, *Greeter) (*Greeter, error)
25 | Update(context.Context, *Greeter) (*Greeter, error)
26 | FindByID(context.Context, int64) (*Greeter, error)
27 | ListByHello(context.Context, string) ([]*Greeter, error)
28 | ListAll(context.Context) ([]*Greeter, error)
29 | }
30 |
31 | // GreeterUsecase is a Greeter usecase.
32 | type GreeterUsecase struct {
33 | repo GreeterRepo
34 | log *log.Helper
35 | }
36 |
37 | // NewGreeterUsecase new a Greeter usecase.
38 | func NewGreeterUsecase(repo GreeterRepo, logger log.Logger) *GreeterUsecase {
39 | return &GreeterUsecase{repo: repo, log: log.NewHelper(logger)}
40 | }
41 |
42 | // CreateGreeter creates a Greeter, and returns the new Greeter.
43 | func (uc *GreeterUsecase) CreateGreeter(ctx context.Context, g *Greeter) (*Greeter, error) {
44 | uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Hello)
45 | return uc.repo.Save(ctx, g)
46 | }
47 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/conf/conf.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package kratos.api;
3 |
4 | option go_package = "myproject/internal/conf;conf";
5 |
6 | import "google/protobuf/duration.proto";
7 |
8 | message Bootstrap {
9 | Server server = 1;
10 | Data data = 2;
11 | }
12 |
13 | message Server {
14 | message HTTP {
15 | string network = 1;
16 | string addr = 2;
17 | google.protobuf.Duration timeout = 3;
18 | }
19 | message GRPC {
20 | string network = 1;
21 | string addr = 2;
22 | google.protobuf.Duration timeout = 3;
23 | }
24 | HTTP http = 1;
25 | GRPC grpc = 2;
26 | }
27 |
28 | message Data {
29 | message Database {
30 | string driver = 1;
31 | string source = 2;
32 | }
33 | message Redis {
34 | string network = 1;
35 | string addr = 2;
36 | google.protobuf.Duration read_timeout = 3;
37 | google.protobuf.Duration write_timeout = 4;
38 | }
39 | Database database = 1;
40 | Redis redis = 2;
41 | }
42 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/data/README.md:
--------------------------------------------------------------------------------
1 | # Data
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/data/data.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "myproject/internal/conf"
5 |
6 | "github.com/go-kratos/kratos/v2/log"
7 | "github.com/google/wire"
8 | )
9 |
10 | // ProviderSet is data providers.
11 | var ProviderSet = wire.NewSet(NewData, NewGreeterRepo)
12 |
13 | // Data .
14 | type Data struct {
15 | // TODO wrapped database client
16 | }
17 |
18 | // NewData .
19 | func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {
20 | cleanup := func() {
21 | log.NewHelper(logger).Info("closing the data resources")
22 | }
23 | return &Data{}, cleanup, nil
24 | }
25 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/data/greeter.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "context"
5 |
6 | "myproject/internal/biz"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | )
10 |
11 | type greeterRepo struct {
12 | data *Data
13 | log *log.Helper
14 | }
15 |
16 | // NewGreeterRepo .
17 | func NewGreeterRepo(data *Data, logger log.Logger) biz.GreeterRepo {
18 | return &greeterRepo{
19 | data: data,
20 | log: log.NewHelper(logger),
21 | }
22 | }
23 |
24 | func (r *greeterRepo) Save(ctx context.Context, g *biz.Greeter) (*biz.Greeter, error) {
25 | return g, nil
26 | }
27 |
28 | func (r *greeterRepo) Update(ctx context.Context, g *biz.Greeter) (*biz.Greeter, error) {
29 | return g, nil
30 | }
31 |
32 | func (r *greeterRepo) FindByID(context.Context, int64) (*biz.Greeter, error) {
33 | return nil, nil
34 | }
35 |
36 | func (r *greeterRepo) ListByHello(context.Context, string) ([]*biz.Greeter, error) {
37 | return nil, nil
38 | }
39 |
40 | func (r *greeterRepo) ListAll(context.Context) ([]*biz.Greeter, error) {
41 | return nil, nil
42 | }
43 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/server/grpc.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "myproject/api/helloworld/v1"
5 | "myproject/internal/conf"
6 | "myproject/internal/service"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/grpc"
11 | )
12 |
13 | // NewGRPCServer new a gRPC server.
14 | func NewGRPCServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *grpc.Server {
15 | var opts = []grpc.ServerOption{
16 | grpc.Middleware(
17 | recovery.Recovery(),
18 | ),
19 | }
20 | if c.Grpc.Network != "" {
21 | opts = append(opts, grpc.Network(c.Grpc.Network))
22 | }
23 | if c.Grpc.Addr != "" {
24 | opts = append(opts, grpc.Address(c.Grpc.Addr))
25 | }
26 | if c.Grpc.Timeout != nil {
27 | opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
28 | }
29 | srv := grpc.NewServer(opts...)
30 | v1.RegisterGreeterServer(srv, greeter)
31 | return srv
32 | }
33 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/server/http.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "myproject/api/helloworld/v1"
5 | "myproject/internal/conf"
6 | "myproject/internal/service"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/http"
11 | )
12 |
13 | // NewHTTPServer new an HTTP server.
14 | func NewHTTPServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *http.Server {
15 | var opts = []http.ServerOption{
16 | http.Middleware(
17 | recovery.Recovery(),
18 | ),
19 | }
20 | if c.Http.Network != "" {
21 | opts = append(opts, http.Network(c.Http.Network))
22 | }
23 | if c.Http.Addr != "" {
24 | opts = append(opts, http.Address(c.Http.Addr))
25 | }
26 | if c.Http.Timeout != nil {
27 | opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
28 | }
29 | srv := http.NewServer(opts...)
30 | v1.RegisterGreeterHTTPServer(srv, greeter)
31 | return srv
32 | }
33 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/google/wire"
5 | )
6 |
7 | // ProviderSet is server providers.
8 | var ProviderSet = wire.NewSet(NewGRPCServer, NewHTTPServer)
9 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/service/README.md:
--------------------------------------------------------------------------------
1 | # Service
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/service/greeter.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 |
6 | v1 "myproject/api/helloworld/v1"
7 | "myproject/internal/biz"
8 | )
9 |
10 | // GreeterService is a greeter service.
11 | type GreeterService struct {
12 | v1.UnimplementedGreeterServer
13 |
14 | uc *biz.GreeterUsecase
15 | }
16 |
17 | // NewGreeterService new a greeter service.
18 | func NewGreeterService(uc *biz.GreeterUsecase) *GreeterService {
19 | return &GreeterService{uc: uc}
20 | }
21 |
22 | // SayHello implements helloworld.GreeterServer.
23 | func (s *GreeterService) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) {
24 | g, err := s.uc.CreateGreeter(ctx, &biz.Greeter{Hello: in.Name})
25 | if err != nil {
26 | return nil, err
27 | }
28 | return &v1.HelloReply{Message: "Hello " + g.Hello}, nil
29 | }
30 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/internal/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import "github.com/google/wire"
4 |
5 | // ProviderSet is service providers.
6 | var ProviderSet = wire.NewSet(NewGreeterService)
7 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/openapi.yaml:
--------------------------------------------------------------------------------
1 | # Generated with protoc-gen-openapi
2 | # https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi
3 |
4 | openapi: 3.0.3
5 | info:
6 | title: Greeter API
7 | description: The greeting service definition.
8 | version: 0.0.1
9 | paths:
10 | /helloworld/{name}:
11 | get:
12 | tags:
13 | - Greeter
14 | - subgroup
15 | description: Sends a greeting
16 | operationId: Greeter_SayHello
17 | parameters:
18 | - name: name
19 | in: path
20 | required: true
21 | schema:
22 | type: string
23 | responses:
24 | "200":
25 | description: OK
26 | content:
27 | application/json:
28 | schema:
29 | $ref: '#/components/schemas/helloworld.v1.HelloReply'
30 | components:
31 | schemas:
32 | helloworld.v1.HelloReply:
33 | type: object
34 | properties:
35 | message:
36 | type: string
37 | description: The response message containing the greetings
38 | tags:
39 | - name: Greeter
40 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/README.md:
--------------------------------------------------------------------------------
1 | # third_party
2 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/errors/errors.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package errors;
4 |
5 | option go_package = "github.com/go-kratos/kratos/v2/errors;errors";
6 | option java_multiple_files = true;
7 | option java_package = "com.github.kratos.errors";
8 | option objc_class_prefix = "KratosErrors";
9 |
10 | import "google/protobuf/descriptor.proto";
11 |
12 | extend google.protobuf.EnumOptions {
13 | int32 default_code = 1108;
14 | }
15 |
16 | extend google.protobuf.EnumValueOptions {
17 | int32 code = 1109;
18 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "AnnotationsProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | extend google.protobuf.MethodOptions {
29 | // See `HttpRule`.
30 | HttpRule http = 72295728;
31 | }
32 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/google/protobuf/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | // The JSON representation for `Empty` is empty JSON object `{}`.
52 | message Empty {}
53 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/google/protobuf/source_context.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "SourceContextProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
41 |
42 | // `SourceContext` represents information about the source of a
43 | // protobuf element, like the file in which it is defined.
44 | message SourceContext {
45 | // The path-qualified name of the .proto file that contained the associated
46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`.
47 | string file_name = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/openapi/v3/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package openapi.v3;
18 |
19 | import "openapi/v3/openapi.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | // This option lets the proto compiler generate Java code inside the package
23 | // name (see below) instead of inside an outer class. It creates a simpler
24 | // developer experience by reducing one-level of name nesting and be
25 | // consistent with most programming languages that don't support outer classes.
26 | option java_multiple_files = true;
27 |
28 | // The Java outer classname should be the filename in UpperCamelCase. This
29 | // class is only used to hold proto descriptor, so developers don't need to
30 | // work with it directly.
31 | option java_outer_classname = "AnnotationsProto";
32 |
33 | // The Java package name must be proto package name with proper prefix.
34 | option java_package = "org.openapi_v3";
35 |
36 | // A reasonable prefix for the Objective-C symbols generated from the package.
37 | // It should at a minimum be 3 characters long, all uppercase, and convention
38 | // is to use an abbreviation of the package name. Something short, but
39 | // hopefully unique enough to not conflict with things that may come along in
40 | // the future. 'GPB' is reserved for the protocol buffer implementation itself.
41 | option objc_class_prefix = "OAS";
42 |
43 | // The Go package name.
44 | option go_package = "github.com/google/gnostic/openapiv3;openapi_v3";
45 |
46 | extend google.protobuf.FileOptions {
47 | Document document = 1143;
48 | }
49 |
50 | extend google.protobuf.MethodOptions {
51 | Operation operation = 1143;
52 | }
53 |
54 | extend google.protobuf.MessageOptions {
55 | Schema schema = 1143;
56 | }
57 |
58 | extend google.protobuf.FieldOptions {
59 | Schema property = 1143;
60 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/kratos-intro/myproject/third_party/validate/README.md:
--------------------------------------------------------------------------------
1 | # protoc-gen-validate (PGV)
2 |
3 | * https://github.com/envoyproxy/protoc-gen-validate
4 |
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/react-intro/App.css:
--------------------------------------------------------------------------------
1 | .app {
2 | max-width: 800px;
3 | margin: 0 auto;
4 | padding: 20px;
5 | font-family: Arial, sans-serif;
6 | }
7 |
8 | h1 {
9 | color: #333;
10 | text-align: center;
11 | }
12 |
13 | h2 {
14 | color: #555;
15 | }
16 |
17 | hr {
18 | margin: 30px 0;
19 | border: 0;
20 | height: 1px;
21 | background-color: #ddd;
22 | }
23 |
24 | /* 计数器样式 */
25 | .counter {
26 | background-color: #f5f5f5;
27 | padding: 20px;
28 | border-radius: 5px;
29 | text-align: center;
30 | }
31 |
32 | .counter button {
33 | margin: 0 5px;
34 | padding: 5px 15px;
35 | background-color: #4caf50;
36 | color: white;
37 | border: none;
38 | border-radius: 3px;
39 | cursor: pointer;
40 | }
41 |
42 | .counter button:hover {
43 | background-color: #45a049;
44 | }
45 |
46 | /* 计时器样式 */
47 | .timer {
48 | background-color: #e0f7fa;
49 | padding: 15px;
50 | text-align: center;
51 | font-size: 18px;
52 | border-radius: 5px;
53 | }
54 |
55 | /* 待办事项列表样式 */
56 | .todo-list {
57 | background-color: #f9f9f9;
58 | padding: 20px;
59 | border-radius: 5px;
60 | }
61 |
62 | .add-todo {
63 | display: flex;
64 | margin-bottom: 15px;
65 | }
66 |
67 | .add-todo input {
68 | flex-grow: 1;
69 | padding: 8px;
70 | border: 1px solid #ddd;
71 | border-radius: 3px 0 0 3px;
72 | }
73 |
74 | .add-todo button {
75 | padding: 8px 15px;
76 | background-color: #2196f3;
77 | color: white;
78 | border: none;
79 | border-radius: 0 3px 3px 0;
80 | cursor: pointer;
81 | }
82 |
83 | .add-todo button:hover {
84 | background-color: #0b7dda;
85 | }
86 |
87 | ul {
88 | list-style-type: none;
89 | padding: 0;
90 | }
91 |
92 | li {
93 | display: flex;
94 | justify-content: space-between;
95 | align-items: center;
96 | padding: 10px;
97 | border-bottom: 1px solid #eee;
98 | }
99 |
100 | li:last-child {
101 | border-bottom: none;
102 | }
103 |
104 | li.completed span {
105 | text-decoration: line-through;
106 | color: #888;
107 | }
108 |
109 | li span {
110 | cursor: pointer;
111 | }
112 |
113 | li button {
114 | padding: 5px 10px;
115 | background-color: #f44336;
116 | color: white;
117 | border: none;
118 | border-radius: 3px;
119 | cursor: pointer;
120 | }
121 |
122 | li button:hover {
123 | background-color: #d32f2f;
124 | }
--------------------------------------------------------------------------------
/doutok-course/basic-knowledge/react-intro/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import './App.css';
3 |
4 | // 一个简单的计数器组件
5 | function Counter() {
6 | const [count, setCount] = useState(0);
7 |
8 | return (
9 |
10 |
计数器: {count}
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | // 一个简单的待办事项组件
19 | function TodoList() {
20 | const [todos, setTodos] = useState([
21 | { id: 1, text: '学习 React', completed: false },
22 | { id: 2, text: '创建一个应用', completed: false },
23 | { id: 3, text: '部署应用', completed: false }
24 | ]);
25 | const [newTodo, setNewTodo] = useState('');
26 |
27 | const addTodo = () => {
28 | if (newTodo.trim() === '') return;
29 | setTodos([
30 | ...todos,
31 | {
32 | id: Date.now(),
33 | text: newTodo,
34 | completed: false
35 | }
36 | ]);
37 | setNewTodo('');
38 | };
39 |
40 | const toggleTodo = (id) => {
41 | setTodos(
42 | todos.map(todo =>
43 | todo.id === id ? { ...todo, completed: !todo.completed } : todo
44 | )
45 | );
46 | };
47 |
48 | const deleteTodo = (id) => {
49 | setTodos(todos.filter(todo => todo.id !== id));
50 | };
51 |
52 | return (
53 |
54 |
待办事项列表
55 |
56 | setNewTodo(e.target.value)}
60 | placeholder="添加新任务"
61 | />
62 |
63 |
64 |
65 |
66 | {todos.map(todo => (
67 | -
68 | toggleTodo(todo.id)}>{todo.text}
69 |
70 |
71 | ))}
72 |
73 |
74 | );
75 | }
76 |
77 | // 使用 useEffect 的示例组件
78 | function Timer() {
79 | const [seconds, setSeconds] = useState(0);
80 |
81 | useEffect(() => {
82 | const interval = setInterval(() => {
83 | setSeconds(seconds => seconds + 1);
84 | }, 1000);
85 |
86 | // 清理函数
87 | return () => clearInterval(interval);
88 | }, []);
89 |
90 | return 计时器: {seconds} 秒
;
91 | }
92 |
93 | // 主应用组件
94 | function App() {
95 | return (
96 |
97 |
React 基础示例
98 |
99 |
100 |
101 |
102 |
103 |
104 | );
105 | }
106 |
107 | export default App;
--------------------------------------------------------------------------------
/doutok-course/doutok-mono/readme.md:
--------------------------------------------------------------------------------
1 | 欢迎加入知识星球:[白泽说](https://wx.zsxq.com/group/51111852421224)
2 |
3 | 进阶课程录制中,敬请期待。
--------------------------------------------------------------------------------
/doutok-course/go.mod:
--------------------------------------------------------------------------------
1 | module doutok-course
2 |
3 | go 1.22
4 |
5 | require github.com/go-kratos/kratos/v2 v2.8.4
6 |
7 | require (
8 | github.com/go-kratos/aegis v0.2.0 // indirect
9 | github.com/go-playground/form/v4 v4.2.0 // indirect
10 | github.com/golang/protobuf v1.5.4 // indirect
11 | github.com/google/uuid v1.4.0 // indirect
12 | github.com/gorilla/mux v1.8.1 // indirect
13 | github.com/kr/text v0.2.0 // indirect
14 | golang.org/x/sync v0.5.0 // indirect
15 | golang.org/x/sys v0.18.0 // indirect
16 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
17 | google.golang.org/grpc v1.61.1 // indirect
18 | google.golang.org/protobuf v1.33.0 // indirect
19 | gopkg.in/yaml.v3 v3.0.1 // indirect
20 | )
21 |
--------------------------------------------------------------------------------
/dragon/.gitignore:
--------------------------------------------------------------------------------
1 | log*
--------------------------------------------------------------------------------
/dragon/basic.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type basic struct {
4 | maxLife int
5 | life int
6 | attack int
7 | defense int
8 | }
9 |
10 | func (b *basic) isAlive() bool {
11 | return b.life > 0
12 | }
13 |
14 | func (b *basic) attacked(attackPower int) int {
15 | if b.defense > attackPower {
16 | return 0
17 | }
18 |
19 | deduct := attackPower - b.defense
20 | b.life -= deduct
21 | if b.life < 0 {
22 | b.life = 0
23 | }
24 | return deduct
25 | }
26 |
--------------------------------------------------------------------------------
/dragon/config.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/mum4k/termdash/cell"
5 | "github.com/mum4k/termdash/linestyle"
6 | "github.com/mum4k/termdash/widgets/text"
7 | )
8 |
9 | const (
10 | DEBUG = false
11 |
12 | // TotalBorderStyle 游戏区域边界样式
13 | TotalBorderStyle = linestyle.Double
14 | // TotalBorderColor 游戏区域边界颜色
15 | TotalBorderColor = cell.ColorYellow
16 | // TotalBorderTitle 游戏区域边界标题
17 | TotalBorderTitle = "按Ctrl + W退出"
18 |
19 | // OperateAreaBorderStyle 操作提示区边界样式
20 | OperateAreaBorderStyle = linestyle.Round
21 | // OperateAreaBorderColor 操作提示区边界颜色
22 | OperateAreaBorderColor = cell.ColorYellow
23 | // OperateAreaBorderTitle 操作提示区边界标题
24 | OperateAreaBorderTitle = "操作提示"
25 |
26 | // InputAreaBorderStyle 输入区边界样式
27 | InputAreaBorderStyle = linestyle.Round
28 | // InputAreaBorderColor 输入区边界颜色
29 | InputAreaBorderColor = cell.ColorYellow
30 | // InputAreaBorderTitle 输入区边界标题
31 | InputAreaBorderTitle = "按回车完成输入"
32 |
33 | // ValuesAreaBorderStyle 数值区边界样式
34 | ValuesAreaBorderStyle = linestyle.Round
35 | // ValuesAreaBorderColor 数值区边界颜色
36 | ValuesAreaBorderColor = cell.ColorYellow
37 | // ValuesAreaBorderTitle 数值区边界标题
38 | ValuesAreaBorderTitle = "龙の数值"
39 |
40 | // HistoryAreaBorderStyle 历史记录区边界样式
41 | HistoryAreaBorderStyle = linestyle.Round
42 | // HistoryAreaBorderColor 历史记录区边界颜色
43 | HistoryAreaBorderColor = cell.ColorYellow
44 | // HistoryAreaBorderTitle 历史记录区边界标题
45 | HistoryAreaBorderTitle = "龙生经历"
46 |
47 | // RankAreaBorderStyle 排行榜区边界样式
48 | RankAreaBorderStyle = linestyle.Round
49 | // RankAreaBorderColor 排行榜区边界颜色
50 | RankAreaBorderColor = cell.ColorYellow
51 | // RankAreaBorderTitle 排行榜区边界标题
52 | RankAreaBorderTitle = "龙の排行榜 TOP 10"
53 | )
54 |
55 | var (
56 | TextOptionBold = text.WriteCellOpts(cell.Bold())
57 | TextOptionUnderline = text.WriteCellOpts(cell.Underline())
58 | TextOptionItalic = text.WriteCellOpts(cell.Italic())
59 | TextOptionBlink = text.WriteCellOpts(cell.Blink())
60 | TextOptionInverse = text.WriteCellOpts(cell.Inverse())
61 | TextOptionStrikethrough = text.WriteCellOpts(cell.Strikethrough())
62 |
63 | TextOptionRed = text.WriteCellOpts(cell.FgColor(cell.ColorRed))
64 | TextOptionBlue = text.WriteCellOpts(cell.FgColor(cell.ColorBlue))
65 | TextOptionCyan = text.WriteCellOpts(cell.FgColor(cell.ColorCyan))
66 | TextOptionGray = text.WriteCellOpts(cell.FgColor(cell.ColorGray))
67 | TextOptionGreen = text.WriteCellOpts(cell.FgColor(cell.ColorGreen))
68 | TextOptionMagenta = text.WriteCellOpts(cell.FgColor(cell.ColorMagenta))
69 | TextOptionYellow = text.WriteCellOpts(cell.FgColor(cell.ColorYellow))
70 | TextOptionWhite = text.WriteCellOpts(cell.FgColor(cell.ColorWhite))
71 | TextOptionBlack = text.WriteCellOpts(cell.FgColor(cell.ColorBlack))
72 | )
73 |
--------------------------------------------------------------------------------
/dragon/dragon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BaiZe1998/go-learning/ff9bb158730be78a5a6f327d9525c9f21876fb7c/dragon/dragon.gif
--------------------------------------------------------------------------------
/dragon/dragon.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // Dragon 龙的结构体
8 | type Dragon struct {
9 | basic *basic
10 | ID int
11 | Name string
12 | Experience int
13 | ExperienceStage int
14 | Remaining int
15 | MaxRemaining int
16 | }
17 |
18 | func (d *Dragon) decreaseRemaining() {
19 | d.Remaining--
20 | }
21 |
22 | // Fight 与 NPC 战斗
23 | func (d *Dragon) Fight(n *NPC) {
24 | // 根本打不过直接润
25 | if d.basic.attack <= n.basic.defense {
26 | p.addHistory(newHistoryInfo(fmt.Sprintf("绝无可能击败的敌人%s\n", n.Name)))
27 | deduct := d.basic.attacked(n.basic.attack)
28 | if d.basic.isAlive() {
29 | p.addHistory(newHistoryInfo(fmt.Sprintf("逃跑成功 耗费%d点血量\n", deduct)))
30 | } else {
31 | p.addHistory(newHistoryInfo("逃跑失败\n"))
32 | }
33 | return
34 | }
35 |
36 | for d.basic.isAlive() {
37 | n.basic.attacked(d.basic.attack)
38 | if !n.basic.isAlive() {
39 | p.addHistory(newHistoryInfo("你打败了"))
40 | p.addHistory(newHistoryInfo(n.Name, TextOptionUnderline))
41 | appendExperience(d, n.Experience)
42 | return
43 | }
44 | d.basic.attacked(n.basic.attack)
45 | }
46 |
47 | p.addHistory(newHistoryInfo("你被"))
48 | p.addHistory(newHistoryInfo(n.Name, TextOptionUnderline))
49 | p.addHistoryLn(newHistoryInfo("打败了"))
50 | appendExperience(d, -d.Experience/2)
51 | randomDecreaseState(d)
52 | }
53 |
54 | // Process 处理偶发事件
55 | func (d *Dragon) Process(e *Event) {
56 | p.addHistoryLn(newHistoryInfo(e.Name))
57 | d.basic.attack += e.Attack
58 | d.basic.defense += e.Defense
59 | appendLife(d, e.Life)
60 | appendExperience(d, e.Experience)
61 | }
62 |
--------------------------------------------------------------------------------
/dragon/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/BaiZe1998/go-learning/dragon
2 |
3 | go 1.21.3
4 |
5 | require github.com/mum4k/termdash v0.18.0
6 |
7 | require (
8 | github.com/gdamore/encoding v1.0.0 // indirect
9 | github.com/gdamore/tcell/v2 v2.5.4 // indirect
10 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
11 | github.com/mattn/go-runewidth v0.0.15 // indirect
12 | github.com/mattn/go-sqlite3 v1.14.19 // indirect
13 | github.com/rivo/uniseg v0.4.4 // indirect
14 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
15 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
16 | golang.org/x/text v0.5.0 // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/dragon/logger.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 | )
7 |
8 | var logger = initLogger()
9 |
10 | func initLogger() *log.Logger {
11 | if !DEBUG {
12 | return nil
13 | }
14 |
15 | f, err := os.Create("log.txt")
16 | if err != nil {
17 | panic(err)
18 | }
19 | logger := log.New(f, "dragon: ", log.LstdFlags)
20 | return logger
21 | }
22 |
23 | func loggerInfo(format string, v ...interface{}) {
24 | if logger != nil {
25 | logger.Printf(format, v...)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/dragon/rank.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BaiZe1998/go-learning/ff9bb158730be78a5a6f327d9525c9f21876fb7c/dragon/rank.db
--------------------------------------------------------------------------------
/eino_assistant/.env:
--------------------------------------------------------------------------------
1 | # ark model: https://console.volcengine.com/ark
2 | # 必填,
3 | # 火山云方舟 ChatModel 的 Endpoint ID
4 | export ARK_CHAT_MODEL="doubao-pro-32k-241215" # 这个替换成你自己申请的
5 | # 火山云方舟 向量化模型的 Endpoint ID
6 | export ARK_EMBEDDING_MODEL="doubao-embedding-large-text-240915" # 这个替换成你自己申请的
7 | # 火山云方舟的 API Key
8 | # export ARK_API_KEY="" 这个替换成你自己申请的
9 | export ARK_API_BASE_URL="https://ark.cn-beijing.volces.com/api/v3" # 这个替换成你自己申请的
10 |
11 | # apmplus: https://console.volcengine.com/apmplus-server
12 | # 下面必填环境变量如果为空,则不开启 apmplus callback
13 | # APMPlus 的 App Key,必填
14 | export APMPLUS_APP_KEY=""
15 | # APMPlus 的 Region,选填,不填写时,默认是 cn-beijing
16 | export APMPLUS_REGION=""
17 |
18 | # langfuse: https://cloud.langfuse.com/
19 | # 下面两个环境变量如果为空,则不开启 langfuse callback
20 | # Langfuse Project 的 Public Key
21 | export LANGFUSE_PUBLIC_KEY=""
22 | # Langfuse Project 的 Secret Key。 注意,Secret Key 仅可在被创建时查看一次
23 | export LANGFUSE_SECRET_KEY=""
24 |
25 | # Redis Server 的地址,不填写时,默认是 localhost:6379
26 | export REDIS_ADDR=
27 |
--------------------------------------------------------------------------------
/eino_assistant/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.22 as builder
2 |
3 | WORKDIR /build
4 |
5 | COPY go.mod ./
6 | COPY go.sum ./
7 | RUN go mod download
8 |
9 | COPY . .
10 |
11 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/main ./cmd/einoagent/*.go
12 |
13 |
14 | FROM alpine:3.21
15 |
16 | WORKDIR /
17 |
18 | RUN apk --no-cache add ca-certificates redis \
19 | && update-ca-certificates
20 |
21 | COPY .env /.env
22 | COPY data /data
23 | COPY --from=builder /out/main /main
24 |
25 | EXPOSE 8080
26 |
27 | CMD [ "/main" ]
--------------------------------------------------------------------------------
/eino_assistant/cmd/knowledgeindexing/eino-docs/_index.md:
--------------------------------------------------------------------------------
1 |
2 | > Eino 发音:美 / 'aino /,近似音: i know,有希望应用程序达到 "i know" 的愿景
3 |
4 | # Eino 是什么
5 |
6 | > 💡
7 | > Go AI 集成组件的研发框架。
8 |
9 | Eino 旨在提供 Golang 语言的 AI 应用开发框架。 Eino 参考了开源社区中诸多优秀的 AI 应用开发框架,例如 LangChain、LangGraph、LlamaIndex 等,提供了更符合 Golang 编程习惯的 AI 应用开发框架。
10 |
11 | Eino 提供了丰富的辅助 AI 应用开发的**原子组件**、**集成组件**、**组件编排**、**切面扩展**等能力,可以帮助开发者更加简单便捷地开发出架构清晰、易维护、高可用的 AI 应用。
12 |
13 | 以 React Agent 为例:
14 |
15 | - 提供了 ChatModel、ToolNode、PromptTemplate 等常用组件,并且业务可定制、可扩展。
16 | - 可基于现有组件进行灵活编排,产生集成组件,在业务服务中使用。
17 |
18 | 
19 |
20 | # Eino 组件
21 |
22 | > Eino 的其中一个目标是:搜集和完善 AI 应用场景下的组件体系,让业务可轻松找到一些通用的 AI 组件,方便业务的迭代。
23 |
24 | Eino 会围绕 AI 应用的场景,提供具有比较好的抽象的组件,并围绕该抽象提供一些常用的实现。
25 |
26 | - Eino 组件的抽象定义在:[eino/components](https://github.com/cloudwego/eino/tree/main/components)
27 | - Eino 组件的实现在:[eino-ext/components](https://github.com/cloudwego/eino-ext/tree/main/components)
28 |
29 | # Eino 应用场景
30 |
31 | 得益于 Eino 轻量化和内场亲和属性,用户只需短短数行代码就能给你的存量微服务引入强力的大模型能力,让传统微服务进化出 AI 基因。
32 |
33 | 可能大家听到【Graph 编排】这个词时,第一反应就是将整个应用接口的实现逻辑进行分段、分层的逻辑拆分,并将其转换成可编排的 Node。 这个过程中遇到的最大问题就是**长距离的上下文传递(跨 Node 节点的变量传递)**问题,无论是使用 Graph/Chain 的 State,还是使用 Options 透传,整个编排过程都极其复杂,远没有直接进行函数调用简单。
34 |
35 | 基于当前的 Graph 编排能力,适合编排的场景具有如下几个特点:
36 |
37 | - 整体是围绕模型的语义处理相关能力。这里的语义不限模态
38 | - 编排产物中有极少数节点是 Session 相关的。整体来看,绝大部分节点没有类似用户/设备等不可枚举地业务实体粒度的处理逻辑
39 |
40 | - 无论是通过 Graph/Chain 的 State、还是通过 CallOptions,对于读写或透传用户/设备粒度的信息的方式,均不简便
41 | - 需要公共的切面能力,基于此建设观测、限流、评测等横向治理能力
42 |
43 | 编排的意义是什么: 把长距离的编排元素上下文以固定的范式进行聚合控制和呈现。
44 |
45 | **整体来说,“Graph 编排”适用的场景是: 业务定制的 AI 集成组件。 ****即把 AI 相关的原子能力,进行灵活编排****,提供简单易用的场景化的 AI 组件。 并且该 AI 组件中,具有统一且完整的横向治理能力。**
46 |
47 | - 推荐的使用方式
48 |
49 | 
50 |
51 | - 挑战较大的方式 -- 【业务全流程的节点编排】
52 | - Biz Handler 一般重业务逻辑,轻数据流,比较适合函数栈调用的方式进行开发
53 | - 如果采用图编排的方式进行逻辑划分与组合,会增大业务逻辑开发的难度
54 |
55 | 
56 |
--------------------------------------------------------------------------------
/eino_assistant/cmd/knowledgeindexing/eino-docs/big_data.md:
--------------------------------------------------------------------------------
1 | # 大数据
2 | 大数据(Big Data)是指规模庞大、结构复杂且无法通过传统数据处理工具在合理时间内进行有效捕捉、管理和处理的数据集合。其核心价值在于通过专业化分析挖掘数据中蕴含的信息,从而提升决策力、优化流程并创造新价值。
--------------------------------------------------------------------------------
/eino_assistant/cmd/knowledgeindexing/eino-docs/deeplearning.md:
--------------------------------------------------------------------------------
1 | # 神经网络
2 | 神经网络是深度学习的核心
3 |
--------------------------------------------------------------------------------
/eino_assistant/data/memory/ce10d8a8-a866-474f-919a-c1ef094c8236.jsonl:
--------------------------------------------------------------------------------
1 | {"role":"user","content":"你好,可以帮我介绍一下 Eino 的优势吗? Thanks♪(・ω・)ノ"}
2 | {"role":"assistant","content":"Eino具有以下优势:\n- **借鉴优秀框架且符合Go语言习惯**:Eino参考了开源社区中诸多优秀的AI应用开发框架,例如LangChain、LangGraph、LlamaIndex 等,提供了更符合Golang编程习惯的AI应用开发框架。 \n- **编排优势明显**:以组件为第一编排对象,同时提供具有极强扩展能力的Lambda节点作为编排对象,能够实现快速上手和定制扩展的双优势。并且编排过程中强化了 “数据流” ,callbacks 提供了观测和调试的基础能力。 \n- **助力项目精准定义与成本控制**:其AI处理功能可帮助用户准确地定义项目,进而降低部署成本。\n- **响应灵活**:能快速响应不断变化的客户或项目需求,让用户对所做更改充满信心。 \n- **支持团队协作**:支持团队成员在单个项目上进行协作,并可通过编辑历史和项目状态进行管理。 \n\n你可以点击[此处](https://www.cloudwego.io/zh/docs/eino/overview/)查看更多相关信息。 ","response_meta":{"finish_reason":"stop","usage":{"prompt_tokens":1973,"completion_tokens":234,"total_tokens":2207}}}
3 | {"role":"user","content":"Eino 的项目地址是什么?官方文档在哪里?可以帮我打开下吗"}
4 | {"role":"assistant","content":"Eino 的官方文档地址为:https://www.cloudwego.io/zh/docs/eino/ ,我已经帮你打开了该文档链接。如果你需要了解 Eino 项目地址等更多信息,可以在文档中进一步查找。如果你还有其他需求,请随时告诉我。 ","response_meta":{"finish_reason":"stop","usage":{"prompt_tokens":1102,"completion_tokens":59,"total_tokens":1161}}}
5 | {"role":"user","content":"在网上查一下 cloudwego 有什么新的项目发布吗?"}
6 | {"role":"assistant","content":"为你查询到CloudWeGo的相关信息如下:\n- CloudWeGo是一套由字节跳动开源的、可快速构建企业级云原生微服务架构的中间件集合。该项目共同的特点是高性能、高扩展性、高可靠,专注于微服务通信与治理。\n- 第一批开源了四个项目:Kitex、Netpoll、Thriftgo和netpoll-http2, 以RPC框架Kitex和网络库Netpoll为主。最近又发布了面向 AI 应用场景的开发框架 Eino,致力于让开发者轻松构建高质量的 AI 应用。\n\n更多信息可查看[CloudWeGo官网](https://www.cloudwego.io/)。","response_meta":{"finish_reason":"stop","usage":{"prompt_tokens":2377,"completion_tokens":186,"total_tokens":2563}}}
7 | {"role":"user","content":"帮我定一个大后天晚上 19:00 的 todo,要把 Eino 的 example 通读一遍"}
8 | {"role":"assistant","content":"已成功为你添加一个待办事项:在2025-1-17 19:00,把 Eino 的 example 通读一遍。","response_meta":{"finish_reason":"stop","usage":{"prompt_tokens":1083,"completion_tokens":36,"total_tokens":1119}}}
9 |
--------------------------------------------------------------------------------
/eino_assistant/data/redis/dump.rdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BaiZe1998/go-learning/ff9bb158730be78a5a6f327d9525c9f21876fb7c/eino_assistant/data/redis/dump.rdb
--------------------------------------------------------------------------------
/eino_assistant/data/task/tasks.jsonl:
--------------------------------------------------------------------------------
1 | {"id":"a13f1db5-0a00-4a9f-8151-52fdc097b11a","title":"阅读 Eino example","content":"把 Eino 的 example 通读一遍","completed":false,"deadline":"2025-3-01 19:00:00","is_deleted":false,"created_at":"2025-02-25T14:45:13+08:00"}
2 |
--------------------------------------------------------------------------------
/eino_assistant/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | redis-stack:
3 | image: redis/redis-stack:latest
4 | container_name: redis-stack
5 | ports:
6 | - "6379:6379" # Redis port
7 | - "8001:8001" # RedisInsight port
8 | volumes:
9 | - ./data/redis:/data
10 | environment:
11 | - REDIS_ARGS=--dir /data --appendonly no --save 1800 1
12 | restart: unless-stopped
13 | healthcheck:
14 | test: [ "CMD", "redis-cli", "ping" ]
15 | interval: 10s
16 | timeout: 5s
17 | retries: 3
18 |
--------------------------------------------------------------------------------
/eino_assistant/eino/knowledgeindexing/embedding.go:
--------------------------------------------------------------------------------
1 | package knowledgeindexing
2 |
3 | import (
4 | "context"
5 | "os"
6 |
7 | "github.com/cloudwego/eino-ext/components/embedding/ark"
8 | "github.com/cloudwego/eino/components/embedding"
9 | )
10 |
11 | func newEmbedding(ctx context.Context) (eb embedding.Embedder, err error) {
12 | // TODO Modify component configuration here.
13 | config := &ark.EmbeddingConfig{
14 | BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
15 | APIKey: os.Getenv("ARK_API_KEY"),
16 | Model: os.Getenv("ARK_EMBEDDING_MODEL"),
17 | }
18 | eb, err = ark.NewEmbedder(ctx, config)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return eb, nil
23 | }
24 |
--------------------------------------------------------------------------------
/eino_assistant/eino/knowledgeindexing/indexer.go:
--------------------------------------------------------------------------------
1 | package knowledgeindexing
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "os"
9 |
10 | "github.com/cloudwego/eino-ext/components/indexer/redis"
11 | "github.com/cloudwego/eino/components/indexer"
12 | "github.com/cloudwego/eino/schema"
13 | "github.com/google/uuid"
14 | redisCli "github.com/redis/go-redis/v9"
15 |
16 | redispkg "eino_assistant/pkg/redis"
17 | )
18 |
19 | func init() {
20 | err := redispkg.Init()
21 | if err != nil {
22 | log.Fatalf("failed to init redis index: %v", err)
23 | }
24 | }
25 |
26 | // newIndexer component initialization function of node 'RedisIndexer' in graph 'KnowledgeIndexing'
27 | func newIndexer(ctx context.Context) (idr indexer.Indexer, err error) {
28 | // TODO Modify component configuration here.
29 | redisAddr := os.Getenv("REDIS_ADDR")
30 | redisClient := redisCli.NewClient(&redisCli.Options{
31 | Addr: redisAddr,
32 | Protocol: 2,
33 | })
34 |
35 | config := &redis.IndexerConfig{
36 | Client: redisClient,
37 | KeyPrefix: redispkg.RedisPrefix,
38 | BatchSize: 1,
39 | DocumentToHashes: func(ctx context.Context, doc *schema.Document) (*redis.Hashes, error) {
40 | if doc.ID == "" {
41 | doc.ID = uuid.New().String()
42 | }
43 | key := doc.ID
44 |
45 | metadataBytes, err := json.Marshal(doc.MetaData)
46 | if err != nil {
47 | return nil, fmt.Errorf("failed to marshal metadata: %w", err)
48 | }
49 |
50 | return &redis.Hashes{
51 | Key: key,
52 | Field2Value: map[string]redis.FieldValue{
53 | redispkg.ContentField: {Value: doc.Content, EmbedKey: redispkg.VectorField},
54 | redispkg.MetadataField: {Value: metadataBytes},
55 | },
56 | }, nil
57 | },
58 | }
59 | embeddingIns11, err := newEmbedding(ctx)
60 | if err != nil {
61 | return nil, err
62 | }
63 | config.Embedding = embeddingIns11
64 | idr, err = redis.NewIndexer(ctx, config)
65 | if err != nil {
66 | return nil, err
67 | }
68 | return idr, nil
69 | }
70 |
--------------------------------------------------------------------------------
/eino_assistant/eino/knowledgeindexing/loader.go:
--------------------------------------------------------------------------------
1 | package knowledgeindexing
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudwego/eino-ext/components/document/loader/file"
7 | "github.com/cloudwego/eino/components/document"
8 | )
9 |
10 | // newLoader component initialization function of node 'FileLoader' in graph 'KnowledgeIndexing'
11 | func newLoader(ctx context.Context) (ldr document.Loader, err error) {
12 | // TODO Modify component configuration here.
13 | config := &file.FileLoaderConfig{}
14 | ldr, err = file.NewFileLoader(ctx, config)
15 | if err != nil {
16 | return nil, err
17 | }
18 | return ldr, nil
19 | }
20 |
--------------------------------------------------------------------------------
/eino_assistant/eino/knowledgeindexing/orchestration.go:
--------------------------------------------------------------------------------
1 | package knowledgeindexing
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudwego/eino/components/document"
7 | "github.com/cloudwego/eino/compose"
8 | )
9 |
10 | func BuildKnowledgeIndexing(ctx context.Context) (r compose.Runnable[document.Source, []string], err error) {
11 | const (
12 | FileLoader = "FileLoader"
13 | MarkdownSplitter = "MarkdownSplitter"
14 | RedisIndexer = "RedisIndexer"
15 | )
16 | g := compose.NewGraph[document.Source, []string]()
17 | fileLoaderKeyOfLoader, err := newLoader(ctx)
18 | if err != nil {
19 | return nil, err
20 | }
21 | _ = g.AddLoaderNode(FileLoader, fileLoaderKeyOfLoader)
22 | markdownSplitterKeyOfDocumentTransformer, err := newDocumentTransformer(ctx)
23 | if err != nil {
24 | return nil, err
25 | }
26 | _ = g.AddDocumentTransformerNode(MarkdownSplitter, markdownSplitterKeyOfDocumentTransformer)
27 | redisIndexerKeyOfIndexer, err := newIndexer(ctx)
28 | if err != nil {
29 | return nil, err
30 | }
31 | _ = g.AddIndexerNode(RedisIndexer, redisIndexerKeyOfIndexer)
32 | _ = g.AddEdge(compose.START, FileLoader)
33 | _ = g.AddEdge(RedisIndexer, compose.END)
34 | _ = g.AddEdge(FileLoader, MarkdownSplitter)
35 | _ = g.AddEdge(MarkdownSplitter, RedisIndexer)
36 | r, err = g.Compile(ctx, compose.WithGraphName("KnowledgeIndexing"), compose.WithNodeTriggerMode(compose.AllPredecessor))
37 | if err != nil {
38 | return nil, err
39 | }
40 | return r, err
41 | }
42 |
--------------------------------------------------------------------------------
/eino_assistant/eino/knowledgeindexing/transformer.go:
--------------------------------------------------------------------------------
1 | package knowledgeindexing
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
7 | "github.com/cloudwego/eino/components/document"
8 | )
9 |
10 | // newDocumentTransformer component initialization function of node 'MarkdownSplitter' in graph 'KnowledgeIndexing'
11 | func newDocumentTransformer(ctx context.Context) (tfr document.Transformer, err error) {
12 | // TODO Modify component configuration here.
13 | config := &markdown.HeaderConfig{
14 | Headers: map[string]string{
15 | "#": "title",
16 | },
17 | TrimHeaders: false}
18 | tfr, err = markdown.NewHeaderSplitter(ctx, config)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return tfr, nil
23 | }
24 |
--------------------------------------------------------------------------------
/eino_assistant/eino/rag/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "context"
6 | "flag"
7 | "fmt"
8 | "os"
9 | "strings"
10 |
11 | "eino_assistant/eino/rag"
12 | "eino_assistant/pkg/env"
13 | )
14 |
15 | func main() {
16 | // 定义命令行参数
17 | useRedis := flag.Bool("redis", true, "是否使用Redis进行检索增强")
18 | topK := flag.Int("topk", 3, "检索的文档数量")
19 |
20 | flag.Parse()
21 |
22 | // 检查环境变量
23 | env.MustHasEnvs("ARK_API_KEY")
24 |
25 | // 构建RAG系统
26 | ctx := context.Background()
27 | ragSystem, err := rag.BuildRAG(ctx, *useRedis, *topK)
28 | if err != nil {
29 | fmt.Fprintf(os.Stderr, "构建RAG系统失败: %v\n", err)
30 | os.Exit(1)
31 | }
32 |
33 | // 显示启动信息
34 | if *useRedis {
35 | fmt.Println("启动RAG系统 (使用Redis检索)")
36 | } else {
37 | fmt.Println("启动RAG系统 (不使用检索)")
38 | }
39 | fmt.Println("输入问题或输入'exit'退出")
40 |
41 | // 创建输入扫描器
42 | scanner := bufio.NewScanner(os.Stdin)
43 |
44 | // 主循环
45 | for {
46 | fmt.Print("\n问题> ")
47 |
48 | // 读取用户输入
49 | if !scanner.Scan() {
50 | break
51 | }
52 |
53 | input := strings.TrimSpace(scanner.Text())
54 | if input == "" {
55 | continue
56 | }
57 |
58 | // 检查退出命令
59 | if strings.ToLower(input) == "exit" {
60 | break
61 | }
62 |
63 | // 处理问题
64 | answer, err := ragSystem.Answer(ctx, input)
65 | if err != nil {
66 | fmt.Fprintf(os.Stderr, "处理问题时出错: %v\n", err)
67 | continue
68 | }
69 |
70 | // 显示回答
71 | fmt.Println("\n回答:")
72 | fmt.Println(answer)
73 | }
74 |
75 | if err := scanner.Err(); err != nil {
76 | fmt.Fprintf(os.Stderr, "读取输入时出错: %v\n", err)
77 | }
78 |
79 | fmt.Println("再见!")
80 | }
81 |
--------------------------------------------------------------------------------
/eino_assistant/eino/rag/cmd/rag_cli:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BaiZe1998/go-learning/ff9bb158730be78a5a6f327d9525c9f21876fb7c/eino_assistant/eino/rag/cmd/rag_cli
--------------------------------------------------------------------------------
/eino_assistant/go.mod:
--------------------------------------------------------------------------------
1 | module eino_assistant
2 |
3 | go 1.22.10
4 |
5 | require (
6 | github.com/cloudwego/eino v0.3.27
7 | github.com/cloudwego/eino-ext/components/document/loader/file v0.0.0-20250429121045-a2545a66f5cf
8 | github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown v0.0.0-20250429121045-a2545a66f5cf
9 | github.com/cloudwego/eino-ext/components/embedding/ark v0.0.0-20250429121045-a2545a66f5cf
10 | github.com/cloudwego/eino-ext/components/indexer/redis v0.0.0-20250429121045-a2545a66f5cf
11 | )
12 |
13 | require (
14 | github.com/bytedance/sonic v1.13.2 // indirect
15 | github.com/bytedance/sonic/loader v0.2.4 // indirect
16 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
17 | github.com/cloudwego/base64x v0.1.5 // indirect
18 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
19 | github.com/dustin/go-humanize v1.0.1 // indirect
20 | github.com/getkin/kin-openapi v0.118.0 // indirect
21 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
22 | github.com/go-openapi/swag v0.19.5 // indirect
23 | github.com/google/uuid v1.3.0 // indirect
24 | github.com/goph/emperror v0.17.2 // indirect
25 | github.com/invopop/yaml v0.1.0 // indirect
26 | github.com/jmespath/go-jmespath v0.4.0 // indirect
27 | github.com/josharian/intern v1.0.0 // indirect
28 | github.com/json-iterator/go v1.1.12 // indirect
29 | github.com/klauspost/cpuid/v2 v2.0.9 // indirect
30 | github.com/mailru/easyjson v0.7.7 // indirect
31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
32 | github.com/modern-go/reflect2 v1.0.2 // indirect
33 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
34 | github.com/nikolalohinski/gonja v1.5.3 // indirect
35 | github.com/pelletier/go-toml/v2 v2.0.9 // indirect
36 | github.com/perimeterx/marshmallow v1.1.4 // indirect
37 | github.com/pkg/errors v0.9.1 // indirect
38 | github.com/redis/go-redis/v9 v9.7.0 // indirect
39 | github.com/sirupsen/logrus v1.9.3 // indirect
40 | github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f // indirect
41 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
42 | github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
43 | github.com/volcengine/volcengine-go-sdk v1.0.181 // indirect
44 | github.com/yargevad/filepathx v1.0.0 // indirect
45 | golang.org/x/arch v0.11.0 // indirect
46 | golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
47 | golang.org/x/sys v0.28.0 // indirect
48 | gopkg.in/yaml.v2 v2.4.0 // indirect
49 | gopkg.in/yaml.v3 v3.0.1 // indirect
50 | )
51 |
--------------------------------------------------------------------------------
/eino_assistant/pkg/env/env.go:
--------------------------------------------------------------------------------
1 | package env
2 |
3 | import (
4 | "log"
5 | "os"
6 | )
7 |
8 | // func init() {
9 | // err := godotenv.Load()
10 | // if err != nil {
11 | // log.Fatalf("❌ [ERROR] Error loading .env file: %v", err)
12 | // }
13 |
14 | // }
15 |
16 | func MustHasEnvs(envs ...string) {
17 | for _, env := range envs {
18 | if os.Getenv(env) == "" {
19 | log.Fatalf("❌ [ERROR] env [%s] is required, but is not set now, please check your .env file", env)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/eino_assistant/readme.md:
--------------------------------------------------------------------------------
1 | 这个案例中,大部分代码取自 Eino 官方案例库:https://github.com/cloudwego/eino-examples
2 | 在此基础上进行了补充和文档的完善。
3 |
4 | 在运行案例之前,先确保:
5 | 1. 执行 docker-compose up -d 确保 redis 启动成功,可以查看:http://127.0.0.1:8001/redis-stack/browser
6 | 2. 执行 source .env 确保环境变量设置正确
--------------------------------------------------------------------------------
/generics/main.go:
--------------------------------------------------------------------------------
1 | // 一个简单的泛型示例
2 | package main
3 |
4 | import "fmt"
5 |
6 | type iface1 interface {
7 | Foo()
8 | }
9 |
10 | type iface2[T iface1] interface {
11 | Get() T
12 | }
13 |
14 | type s1 struct{}
15 |
16 | func (s *s1) Foo() {
17 | fmt.Println("this is s1.Foo")
18 | }
19 |
20 | type S2 struct {
21 | s1 *s1
22 | }
23 |
24 | func (s *S2) Get() *s1 {
25 | return s.s1
26 | }
27 |
28 | func NewS2() *S2 {
29 | return &S2{
30 | s1: &s1{},
31 | }
32 | }
33 |
34 | func test[T iface1](obj iface2[T]) {
35 | obj.Get().Foo()
36 | }
37 |
38 | func main() {
39 | obj := NewS2()
40 | test[*s1](obj)
41 | }
42 |
--------------------------------------------------------------------------------
/kit/i18n/Makefile:
--------------------------------------------------------------------------------
1 | i18n-extract:
2 | goi18n extract
3 | goi18n merge active.*.toml
4 |
5 | i18n-merge:
6 | goi18n merge active.*.toml translate.*.toml
7 |
--------------------------------------------------------------------------------
/kit/i18n/active.en.toml:
--------------------------------------------------------------------------------
1 | PersonCatsDII = "{{.Name}} hasfhudhfsa {{.Count}} cats."
2 | user_not_found = "User not found {{.UserID}}"
3 |
--------------------------------------------------------------------------------
/kit/i18n/active.zh.toml:
--------------------------------------------------------------------------------
1 | [PersonCatsDII]
2 | hash = "sha1-60f5e91090a4d4b765315e11ebe5ebae7a927620"
3 | other = "{{.Name}} 有很多 {{.Count}} 猫."
4 |
5 | [user_not_found]
6 | hash = "sha1-9d093a4291e6fde9d899c9d50d6171c54859a5ec"
7 | other = "用户不存在 {{.UserID}}"
8 |
--------------------------------------------------------------------------------
/kit/i18n/go.mod:
--------------------------------------------------------------------------------
1 | module i18n
2 |
3 | go 1.22.2
4 |
5 | require (
6 | github.com/BurntSushi/toml v1.3.2
7 | github.com/nicksnyder/go-i18n/v2 v2.4.0
8 | golang.org/x/text v0.14.0
9 | )
10 |
--------------------------------------------------------------------------------
/kit/i18n/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
2 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
3 | github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
4 | github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
5 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
6 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
7 |
--------------------------------------------------------------------------------
/kit/i18n/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | )
8 |
9 | func helloHandler(w http.ResponseWriter, r *http.Request) {
10 | err := NewUserNotFoundErr(123)
11 | // 某些业务处零零落落、
12 | //err, _ := someFunc()
13 | fmt.Fprintf(w, FormatErr(err))
14 | }
15 |
16 | func main() {
17 | http.HandleFunc("/", helloHandler)
18 |
19 | fmt.Println("Starting server on port 8080...")
20 | if err := http.ListenAndServe(":8080", nil); err != nil {
21 | log.Fatal(err)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/kit/i18n/message.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "github.com/BurntSushi/toml"
7 | "github.com/nicksnyder/go-i18n/v2/i18n"
8 | "golang.org/x/text/language"
9 | )
10 |
11 | var (
12 | bundle = i18n.NewBundle(language.English)
13 | )
14 |
15 | func init() {
16 | bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
17 | bundle.LoadMessageFile("active.zh.toml")
18 | }
19 |
20 | type BaseError struct {
21 | ID string
22 | DefaultMessage string
23 | TempData map[string]interface{}
24 | }
25 |
26 | func (b BaseError) Error() string {
27 | return b.DefaultMessage
28 | }
29 |
30 | func (b BaseError) LocalizedID() string {
31 | return b.ID
32 | }
33 |
34 | func (b BaseError) TemplateData() map[string]interface{} {
35 | return b.TempData
36 | }
37 |
38 | type LocalizedError interface {
39 | error
40 | LocalizedID() string
41 | TemplateData() map[string]interface{}
42 | }
43 |
44 | type UserNotFoundErr struct {
45 | BaseError
46 | }
47 |
48 | func NewUserNotFoundErr(userID int) LocalizedError {
49 | msg := i18n.Message{
50 | ID: "user_not_found",
51 | Other: "User not found {{.UserID}}",
52 | }
53 | e := UserNotFoundErr{}
54 | e.ID = msg.ID
55 | e.DefaultMessage = msg.Other
56 | e.TempData = map[string]interface{}{
57 | "UserID": userID,
58 | }
59 | return e
60 | }
61 |
62 | func GetLang(_ context.Context) string {
63 | return "zh"
64 | }
65 |
66 | func FormatErr(err error) string {
67 | lang := GetLang(context.Background())
68 | loc := i18n.NewLocalizer(bundle, lang)
69 | var i18nErr LocalizedError
70 | if errors.As(err, &i18nErr) {
71 | msg, _ := loc.Localize(&i18n.LocalizeConfig{
72 | DefaultMessage: &i18n.Message{
73 | ID: i18nErr.LocalizedID(),
74 | Other: i18nErr.Error(),
75 | },
76 | //MessageID: i18nErr.LocalizedID(),
77 | TemplateData: i18nErr.TemplateData(),
78 | })
79 | return msg
80 | }
81 | return err.Error()
82 | }
83 |
--------------------------------------------------------------------------------
/kit/i18n/translate.zh.toml:
--------------------------------------------------------------------------------
1 | [PersonCatsDII]
2 | hash = "sha1-60f5e91090a4d4b765315e11ebe5ebae7a927620"
3 | other = "{{.Name}} 有很多 {{.Count}} 猫."
4 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/.gitignore:
--------------------------------------------------------------------------------
1 | # Reference https://github.com/github/gitignore/blob/master/Go.gitignore
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | vendor/
16 |
17 | # Go workspace file
18 | go.work
19 |
20 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
21 | *.o
22 | *.a
23 | *.so
24 |
25 | # OS General
26 | Thumbs.db
27 | .DS_Store
28 |
29 | # project
30 | *.cert
31 | *.key
32 | *.log
33 | bin/
34 |
35 | # Develop tools
36 | .vscode/
37 | .idea/
38 | *.swp
39 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.19 AS builder
2 |
3 | COPY . /src
4 | WORKDIR /src
5 |
6 | RUN GOPROXY=https://goproxy.cn make build
7 |
8 | FROM debian:stable-slim
9 |
10 | RUN apt-get update && apt-get install -y --no-install-recommends \
11 | ca-certificates \
12 | netbase \
13 | && rm -rf /var/lib/apt/lists/ \
14 | && apt-get autoremove -y && apt-get autoclean -y
15 |
16 | COPY --from=builder /src/bin /app
17 |
18 | WORKDIR /app
19 |
20 | EXPOSE 8000
21 | EXPOSE 9000
22 | VOLUME /data/conf
23 |
24 | CMD ["./server", "-conf", "/data/conf"]
25 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 go-kratos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/Makefile:
--------------------------------------------------------------------------------
1 | GOHOSTOS:=$(shell go env GOHOSTOS)
2 | GOPATH:=$(shell go env GOPATH)
3 | VERSION=$(shell git describe --tags --always)
4 |
5 | ifeq ($(GOHOSTOS), windows)
6 | #the `find.exe` is different from `find` in bash/shell.
7 | #to see https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/find.
8 | #changed to use git-bash.exe to run find cli or other cli friendly, caused of every developer has a Git.
9 | #Git_Bash= $(subst cmd\,bin\bash.exe,$(dir $(shell where git)))
10 | Git_Bash=$(subst \,/,$(subst cmd\,bin\bash.exe,$(dir $(shell where git))))
11 | INTERNAL_PROTO_FILES=$(shell $(Git_Bash) -c "find internal -name *.proto")
12 | API_PROTO_FILES=$(shell $(Git_Bash) -c "find api -name *.proto")
13 | else
14 | INTERNAL_PROTO_FILES=$(shell find internal -name *.proto)
15 | API_PROTO_FILES=$(shell find api -name *.proto)
16 | endif
17 |
18 | .PHONY: init
19 | # init env
20 | init:
21 | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
22 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
23 | go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
24 | go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest
25 | go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest
26 | go install github.com/google/wire/cmd/wire@latest
27 |
28 | .PHONY: config
29 | # generate internal proto
30 | config:
31 | protoc --proto_path=./internal \
32 | --proto_path=./third_party \
33 | --go_out=paths=source_relative:./internal \
34 | $(INTERNAL_PROTO_FILES)
35 |
36 | .PHONY: api
37 | # generate api proto
38 | api:
39 | protoc --proto_path=./api \
40 | --proto_path=./third_party \
41 | --go_out=paths=source_relative:./api \
42 | --go-http_out=paths=source_relative:./api \
43 | --go-grpc_out=paths=source_relative:./api \
44 | --openapi_out=fq_schema_naming=true,default_response=false:. \
45 | $(API_PROTO_FILES)
46 |
47 | .PHONY: build
48 | # build
49 | build:
50 | mkdir -p bin/ && go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./...
51 |
52 | .PHONY: generate
53 | # generate
54 | generate:
55 | go mod tidy
56 | go get github.com/google/wire/cmd/wire@latest
57 | go generate ./...
58 |
59 | .PHONY: all
60 | # generate all
61 | all:
62 | make api;
63 | make config;
64 | make generate;
65 |
66 | # show help
67 | help:
68 | @echo ''
69 | @echo 'Usage:'
70 | @echo ' make [target]'
71 | @echo ''
72 | @echo 'Targets:'
73 | @awk '/^[a-zA-Z\-\_0-9]+:/ { \
74 | helpMessage = match(lastLine, /^# (.*)/); \
75 | if (helpMessage) { \
76 | helpCommand = substr($$1, 0, index($$1, ":")); \
77 | helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \
78 | printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \
79 | } \
80 | } \
81 | { lastLine = $$0 }' $(MAKEFILE_LIST)
82 |
83 | .DEFAULT_GOAL := help
84 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/README.md:
--------------------------------------------------------------------------------
1 | # Kratos Project Template
2 |
3 | ## Install Kratos
4 | ```
5 | go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
6 | ```
7 | ## Create a service
8 | ```
9 | # Create a template project
10 | kratos new server
11 |
12 | cd server
13 | # Add a proto template
14 | kratos proto add api/server/server.proto
15 | # Generate the proto code
16 | kratos proto client api/server/server.proto
17 | # Generate the source code of service by proto file
18 | kratos proto server api/server/server.proto -t internal/service
19 |
20 | go generate ./...
21 | go build -o ./bin/ ./...
22 | ./bin/server -conf ./configs
23 | ```
24 | ## Generate other auxiliary files by Makefile
25 | ```
26 | # Download and update dependencies
27 | make init
28 | # Generate API files (include: pb.go, http, grpc, validate, swagger) by proto file
29 | make api
30 | # Generate all files
31 | make all
32 | ```
33 | ## Automated Initialization (wire)
34 | ```
35 | # install wire
36 | go get github.com/google/wire/cmd/wire
37 |
38 | # generate wire
39 | cd cmd/server
40 | wire
41 | ```
42 |
43 | ## Docker
44 | ```bash
45 | # build
46 | docker build -t .
47 |
48 | # run
49 | docker run --rm -p 8000:8000 -p 9000:9000 -v :/data/conf
50 | ```
51 |
52 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/api/helloworld/v1/error_reason.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package helloworld.v1;
4 |
5 | option go_package = "helloworld/api/helloworld/v1;v1";
6 | option java_multiple_files = true;
7 | option java_package = "helloworld.v1";
8 | option objc_class_prefix = "APIHelloworldV1";
9 |
10 | enum ErrorReason {
11 | GREETER_UNSPECIFIED = 0;
12 | USER_NOT_FOUND = 1;
13 | }
14 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/api/helloworld/v1/greeter.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package helloworld.v1;
4 |
5 | import "google/api/annotations.proto";
6 |
7 | option go_package = "helloworld/api/helloworld/v1;v1";
8 | option java_multiple_files = true;
9 | option java_package = "dev.kratos.api.helloworld.v1";
10 | option java_outer_classname = "HelloworldProtoV1";
11 |
12 | // The greeting service definition.
13 | service Greeter {
14 | // Sends a greeting
15 | rpc SayHello (HelloRequest) returns (HelloReply) {
16 | option (google.api.http) = {
17 | get: "/helloworld/{name}"
18 | };
19 | }
20 | }
21 |
22 | // The request message containing the user's name.
23 | message HelloRequest {
24 | string name = 1;
25 | }
26 |
27 | // The response message containing the greetings
28 | message HelloReply {
29 | string message = 1;
30 | }
31 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/api/helloworld/v1/greeter_http.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-http. DO NOT EDIT.
2 | // versions:
3 | // protoc-gen-go-http v2.1.3
4 |
5 | package v1
6 |
7 | import (
8 | context "context"
9 | http "github.com/go-kratos/kratos/v2/transport/http"
10 | binding "github.com/go-kratos/kratos/v2/transport/http/binding"
11 | )
12 |
13 | // This is a compile-time assertion to ensure that this generated file
14 | // is compatible with the kratos package it is being compiled against.
15 | var _ = new(context.Context)
16 | var _ = binding.EncodeURL
17 |
18 | const _ = http.SupportPackageIsVersion1
19 |
20 | type GreeterHTTPServer interface {
21 | SayHello(context.Context, *HelloRequest) (*HelloReply, error)
22 | }
23 |
24 | func RegisterGreeterHTTPServer(s *http.Server, srv GreeterHTTPServer) {
25 | r := s.Route("/")
26 | r.GET("/helloworld/{name}", _Greeter_SayHello0_HTTP_Handler(srv))
27 | }
28 |
29 | func _Greeter_SayHello0_HTTP_Handler(srv GreeterHTTPServer) func(ctx http.Context) error {
30 | return func(ctx http.Context) error {
31 | var in HelloRequest
32 | if err := ctx.BindQuery(&in); err != nil {
33 | return err
34 | }
35 | if err := ctx.BindVars(&in); err != nil {
36 | return err
37 | }
38 | http.SetOperation(ctx, "/helloworld.v1.Greeter/SayHello")
39 | h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
40 | return srv.SayHello(ctx, req.(*HelloRequest))
41 | })
42 | out, err := h(ctx, &in)
43 | if err != nil {
44 | return err
45 | }
46 | reply := out.(*HelloReply)
47 | return ctx.Result(200, reply)
48 | }
49 | }
50 |
51 | type GreeterHTTPClient interface {
52 | SayHello(ctx context.Context, req *HelloRequest, opts ...http.CallOption) (rsp *HelloReply, err error)
53 | }
54 |
55 | type GreeterHTTPClientImpl struct {
56 | cc *http.Client
57 | }
58 |
59 | func NewGreeterHTTPClient(client *http.Client) GreeterHTTPClient {
60 | return &GreeterHTTPClientImpl{client}
61 | }
62 |
63 | func (c *GreeterHTTPClientImpl) SayHello(ctx context.Context, in *HelloRequest, opts ...http.CallOption) (*HelloReply, error) {
64 | var out HelloReply
65 | pattern := "/helloworld/{name}"
66 | path := binding.EncodeURL(pattern, in, true)
67 | opts = append(opts, http.Operation("/helloworld.v1.Greeter/SayHello"))
68 | opts = append(opts, http.PathTemplate(pattern))
69 | err := c.cc.Invoke(ctx, "GET", path, nil, &out, opts...)
70 | if err != nil {
71 | return nil, err
72 | }
73 | return &out, err
74 | }
75 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/cmd/helloworld/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "os"
6 |
7 | "helloworld/internal/conf"
8 |
9 | "github.com/go-kratos/kratos/v2"
10 | "github.com/go-kratos/kratos/v2/config"
11 | "github.com/go-kratos/kratos/v2/config/file"
12 | "github.com/go-kratos/kratos/v2/log"
13 | "github.com/go-kratos/kratos/v2/middleware/tracing"
14 | "github.com/go-kratos/kratos/v2/transport/grpc"
15 | "github.com/go-kratos/kratos/v2/transport/http"
16 |
17 | _ "go.uber.org/automaxprocs"
18 | )
19 |
20 | // go build -ldflags "-X main.Version=x.y.z"
21 | var (
22 | // Name is the name of the compiled software.
23 | Name string
24 | // Version is the version of the compiled software.
25 | Version string
26 | // flagconf is the config flag.
27 | flagconf string
28 |
29 | id, _ = os.Hostname()
30 | )
31 |
32 | func init() {
33 | flag.StringVar(&flagconf, "conf", "./configs/config.yaml", "config path, eg: -conf config.yaml")
34 | }
35 |
36 | func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {
37 | return kratos.New(
38 | kratos.ID(id),
39 | kratos.Name(Name),
40 | kratos.Version(Version),
41 | kratos.Metadata(map[string]string{}),
42 | kratos.Logger(logger),
43 | kratos.Server(
44 | gs,
45 | hs,
46 | ),
47 | )
48 | }
49 |
50 | func main() {
51 | flag.Parse()
52 | logger := log.With(log.NewStdLogger(os.Stdout),
53 | "ts", log.DefaultTimestamp,
54 | "caller", log.DefaultCaller,
55 | "service.id", id,
56 | "service.name", Name,
57 | "service.version", Version,
58 | "trace.id", tracing.TraceID(),
59 | "span.id", tracing.SpanID(),
60 | )
61 | c := config.New(
62 | config.WithSource(
63 | file.NewSource(flagconf),
64 | ),
65 | )
66 | defer c.Close()
67 |
68 | if err := c.Load(); err != nil {
69 | panic(err)
70 | }
71 |
72 | var bc conf.Bootstrap
73 | if err := c.Scan(&bc); err != nil {
74 | panic(err)
75 | }
76 |
77 | app, cleanup, err := wireApp(bc.Server, bc.Data, logger)
78 | if err != nil {
79 | panic(err)
80 | }
81 | defer cleanup()
82 |
83 | // start and wait for stop signal
84 | if err := app.Run(); err != nil {
85 | panic(err)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/cmd/helloworld/wire.go:
--------------------------------------------------------------------------------
1 | //go:build wireinject
2 | // +build wireinject
3 |
4 | //go:generate wire
5 |
6 | // The build tag makes sure the stub is not built in the final build.
7 |
8 | package main
9 |
10 | import (
11 | "helloworld/internal/biz"
12 | "helloworld/internal/conf"
13 | "helloworld/internal/data"
14 | "helloworld/internal/server"
15 | "helloworld/internal/service"
16 | "helloworld/pkg/db"
17 |
18 | "github.com/go-kratos/kratos/v2"
19 | "github.com/go-kratos/kratos/v2/log"
20 | "github.com/google/wire"
21 | )
22 |
23 | // wireApp init kratos application.
24 | func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {
25 | panic(wire.Build(db.NewDBClient, server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
26 | }
27 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/cmd/helloworld/wire_gen.go:
--------------------------------------------------------------------------------
1 | // Code generated by Wire. DO NOT EDIT.
2 |
3 | //go:generate go run -mod=mod github.com/google/wire/cmd/wire
4 | //go:build !wireinject
5 | // +build !wireinject
6 |
7 | package main
8 |
9 | import (
10 | "github.com/go-kratos/kratos/v2"
11 | "github.com/go-kratos/kratos/v2/log"
12 | "helloworld/internal/biz"
13 | "helloworld/internal/conf"
14 | "helloworld/internal/data"
15 | "helloworld/internal/server"
16 | "helloworld/internal/service"
17 | "helloworld/pkg/db"
18 | )
19 |
20 | import (
21 | _ "go.uber.org/automaxprocs"
22 | )
23 |
24 | // Injectors from wire.go:
25 |
26 | // wireApp init kratos application.
27 | func wireApp(confServer *conf.Server, confData *conf.Data, logger log.Logger) (*kratos.App, func(), error) {
28 | dbClient, err := db.NewDBClient(confData)
29 | if err != nil {
30 | return nil, nil, err
31 | }
32 | dataData, cleanup, err := data.NewData(dbClient, logger)
33 | if err != nil {
34 | return nil, nil, err
35 | }
36 | greeterRepo := data.NewGreeterRepo(dataData, logger)
37 | greeterUsecase := biz.NewGreeterUsecase(greeterRepo, dbClient, logger)
38 | greeterService := service.NewGreeterService(greeterUsecase)
39 | grpcServer := server.NewGRPCServer(confServer, greeterService, logger)
40 | httpServer := server.NewHTTPServer(confServer, greeterService, logger)
41 | app := newApp(logger, grpcServer, httpServer)
42 | return app, func() {
43 | cleanup()
44 | }, nil
45 | }
46 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/configs/config.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | http:
3 | addr: 0.0.0.0:8000
4 | timeout: 1s
5 | grpc:
6 | addr: 0.0.0.0:9000
7 | timeout: 1s
8 | data:
9 | database:
10 | driver: mysql
11 | source: root:root@tcp(127.0.0.1:3306)/dev?parseTime=True&loc=Local
12 | redis:
13 | addr: 127.0.0.1:6379
14 | read_timeout: 0.2s
15 | write_timeout: 0.2s
16 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/go.mod:
--------------------------------------------------------------------------------
1 | module helloworld
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/go-kratos/kratos/v2 v2.7.2
7 | github.com/google/wire v0.5.0
8 | go.uber.org/automaxprocs v1.5.1
9 | google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529
10 | google.golang.org/grpc v1.56.3
11 | google.golang.org/protobuf v1.32.0
12 | gorm.io/driver/mysql v1.5.7
13 | gorm.io/gorm v1.25.11
14 | )
15 |
16 | require (
17 | github.com/fsnotify/fsnotify v1.6.0 // indirect
18 | github.com/go-kratos/aegis v0.2.0 // indirect
19 | github.com/go-logr/logr v1.2.4 // indirect
20 | github.com/go-logr/stdr v1.2.2 // indirect
21 | github.com/go-playground/form/v4 v4.2.1 // indirect
22 | github.com/go-sql-driver/mysql v1.7.0 // indirect
23 | github.com/golang/protobuf v1.5.3 // indirect
24 | github.com/google/uuid v1.6.0 // indirect
25 | github.com/gorilla/mux v1.8.0 // indirect
26 | github.com/imdario/mergo v0.3.16 // indirect
27 | github.com/jinzhu/inflection v1.0.0 // indirect
28 | github.com/jinzhu/now v1.1.5 // indirect
29 | github.com/kr/text v0.2.0 // indirect
30 | go.opentelemetry.io/otel v1.16.0 // indirect
31 | go.opentelemetry.io/otel/metric v1.16.0 // indirect
32 | go.opentelemetry.io/otel/trace v1.16.0 // indirect
33 | golang.org/x/net v0.17.0 // indirect
34 | golang.org/x/sync v0.6.0 // indirect
35 | golang.org/x/sys v0.13.0 // indirect
36 | golang.org/x/text v0.14.0 // indirect
37 | google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 // indirect
38 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
39 | gopkg.in/yaml.v3 v3.0.1 // indirect
40 | )
41 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/biz/README.md:
--------------------------------------------------------------------------------
1 | # Biz
2 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/biz/biz.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import "github.com/google/wire"
4 |
5 | // ProviderSet is biz providers.
6 | var ProviderSet = wire.NewSet(NewGreeterUsecase)
7 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/biz/greeter.go:
--------------------------------------------------------------------------------
1 | package biz
2 |
3 | import (
4 | "context"
5 | "helloworld/pkg/db"
6 |
7 | v1 "helloworld/api/helloworld/v1"
8 |
9 | "github.com/go-kratos/kratos/v2/errors"
10 | "github.com/go-kratos/kratos/v2/log"
11 | )
12 |
13 | var (
14 | // ErrUserNotFound is user not found.
15 | ErrUserNotFound = errors.NotFound(v1.ErrorReason_USER_NOT_FOUND.String(), "user not found")
16 | )
17 |
18 | // Greeter is a Greeter model.
19 | type Greeter struct {
20 | Hello string `gorm:"column:hello;type:varchar(20)"`
21 | }
22 |
23 | func (*Greeter) TableName() string {
24 | return "greater"
25 | }
26 |
27 | // GreeterRepo is a Greater repo.
28 | type GreeterRepo interface {
29 | Save(context.Context, *Greeter) (*Greeter, error)
30 | Update(context.Context, *Greeter) (*Greeter, error)
31 | FindByID(context.Context, int64) (*Greeter, error)
32 | ListByHello(context.Context, string) ([]*Greeter, error)
33 | ListAll(context.Context) ([]*Greeter, error)
34 | }
35 |
36 | // GreeterUsecase is a Greeter usecase.
37 | type GreeterUsecase struct {
38 | db *db.DBClient
39 | repo GreeterRepo
40 | log *log.Helper
41 | }
42 |
43 | // NewGreeterUsecase new a Greeter usecase.
44 | func NewGreeterUsecase(repo GreeterRepo, db *db.DBClient, logger log.Logger) *GreeterUsecase {
45 | return &GreeterUsecase{db: db, repo: repo, log: log.NewHelper(logger)}
46 | }
47 |
48 | // CreateGreeter creates a Greeter, and returns the new Greeter.
49 | func (uc *GreeterUsecase) CreateGreeter(ctx context.Context, g *Greeter) (*Greeter, error) {
50 | uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Hello)
51 | var (
52 | greater *Greeter
53 | err error
54 | )
55 | err = uc.db.ExecTx(ctx, func(ctx context.Context) error {
56 | // 更新所有 hello 为 hello + "updated",且插入新的 hello
57 | greater, err = uc.repo.Save(ctx, g)
58 | _, err = uc.repo.Update(ctx, g)
59 | return err
60 | })
61 | //greater, err = uc.repo.Save(ctx, g)
62 | //_, err = uc.repo.Update(ctx, g)
63 | if err != nil {
64 | return nil, err
65 | }
66 | return greater, nil
67 | }
68 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/conf/conf.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package kratos.api;
3 |
4 | option go_package = "helloworld/internal/conf;conf";
5 |
6 | import "google/protobuf/duration.proto";
7 |
8 | message Bootstrap {
9 | Server server = 1;
10 | Data data = 2;
11 | }
12 |
13 | message Server {
14 | message HTTP {
15 | string network = 1;
16 | string addr = 2;
17 | google.protobuf.Duration timeout = 3;
18 | }
19 | message GRPC {
20 | string network = 1;
21 | string addr = 2;
22 | google.protobuf.Duration timeout = 3;
23 | }
24 | HTTP http = 1;
25 | GRPC grpc = 2;
26 | }
27 |
28 | message Data {
29 | message Database {
30 | string driver = 1;
31 | string source = 2;
32 | }
33 | message Redis {
34 | string network = 1;
35 | string addr = 2;
36 | google.protobuf.Duration read_timeout = 3;
37 | google.protobuf.Duration write_timeout = 4;
38 | }
39 | Database database = 1;
40 | Redis redis = 2;
41 | }
42 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/data/README.md:
--------------------------------------------------------------------------------
1 | # Data
2 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/data/data.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "helloworld/pkg/db"
5 |
6 | "github.com/go-kratos/kratos/v2/log"
7 | "github.com/google/wire"
8 | )
9 |
10 | // ProviderSet is data providers.
11 | var ProviderSet = wire.NewSet(NewData, NewGreeterRepo)
12 |
13 | // Data .
14 | type Data struct {
15 | // TODO wrapped database client
16 | db *db.DBClient
17 | }
18 |
19 | // NewData .
20 | func NewData(c *db.DBClient, logger log.Logger) (*Data, func(), error) {
21 | cleanup := func() {
22 | log.NewHelper(logger).Info("closing the data resources")
23 | }
24 | return &Data{db: c}, cleanup, nil
25 | }
26 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/data/greeter.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "helloworld/internal/biz"
8 |
9 | "github.com/go-kratos/kratos/v2/log"
10 | )
11 |
12 | type greeterRepo struct {
13 | data *Data
14 | log *log.Helper
15 | }
16 |
17 | // NewGreeterRepo .
18 | func NewGreeterRepo(data *Data, logger log.Logger) biz.GreeterRepo {
19 | return &greeterRepo{
20 | data: data,
21 | log: log.NewHelper(logger),
22 | }
23 | }
24 |
25 | func (r *greeterRepo) Save(ctx context.Context, g *biz.Greeter) (*biz.Greeter, error) {
26 | result := r.data.db.DB(ctx).Create(g)
27 | return g, result.Error
28 | }
29 |
30 | func (r *greeterRepo) Update(ctx context.Context, g *biz.Greeter) (*biz.Greeter, error) {
31 | result := r.data.db.DB(ctx).Model(&biz.Greeter{}).Where("hello = ?", g.Hello).Update("hello", g.Hello+"updated")
32 | if result.RowsAffected == 0 {
33 | return nil, fmt.Errorf("greeter %s not found", g.Hello)
34 | }
35 | return nil, fmt.Errorf("custom error")
36 | //return g, nil
37 | }
38 |
39 | func (r *greeterRepo) FindByID(context.Context, int64) (*biz.Greeter, error) {
40 | return nil, nil
41 | }
42 |
43 | func (r *greeterRepo) ListByHello(ctx context.Context, hello string) ([]*biz.Greeter, error) {
44 | var greeters []*biz.Greeter
45 | result := r.data.db.DB(ctx).Where("hello = ?", hello).Find(&greeters)
46 | return greeters, result.Error
47 | }
48 |
49 | func (r *greeterRepo) ListAll(context.Context) ([]*biz.Greeter, error) {
50 | return nil, nil
51 | }
52 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/server/grpc.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "helloworld/api/helloworld/v1"
5 | "helloworld/internal/conf"
6 | "helloworld/internal/service"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/grpc"
11 | )
12 |
13 | // NewGRPCServer new a gRPC server.
14 | func NewGRPCServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *grpc.Server {
15 | var opts = []grpc.ServerOption{
16 | grpc.Middleware(
17 | recovery.Recovery(),
18 | ),
19 | }
20 | if c.Grpc.Network != "" {
21 | opts = append(opts, grpc.Network(c.Grpc.Network))
22 | }
23 | if c.Grpc.Addr != "" {
24 | opts = append(opts, grpc.Address(c.Grpc.Addr))
25 | }
26 | if c.Grpc.Timeout != nil {
27 | opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
28 | }
29 | srv := grpc.NewServer(opts...)
30 | v1.RegisterGreeterServer(srv, greeter)
31 | return srv
32 | }
33 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/server/http.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | v1 "helloworld/api/helloworld/v1"
5 | "helloworld/internal/conf"
6 | "helloworld/internal/service"
7 |
8 | "github.com/go-kratos/kratos/v2/log"
9 | "github.com/go-kratos/kratos/v2/middleware/recovery"
10 | "github.com/go-kratos/kratos/v2/transport/http"
11 | )
12 |
13 | // NewHTTPServer new an HTTP server.
14 | func NewHTTPServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *http.Server {
15 | var opts = []http.ServerOption{
16 | http.Middleware(
17 | recovery.Recovery(),
18 | ),
19 | }
20 | if c.Http.Network != "" {
21 | opts = append(opts, http.Network(c.Http.Network))
22 | }
23 | if c.Http.Addr != "" {
24 | opts = append(opts, http.Address(c.Http.Addr))
25 | }
26 | if c.Http.Timeout != nil {
27 | opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
28 | }
29 | srv := http.NewServer(opts...)
30 | v1.RegisterGreeterHTTPServer(srv, greeter)
31 | return srv
32 | }
33 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/google/wire"
5 | )
6 |
7 | // ProviderSet is server providers.
8 | var ProviderSet = wire.NewSet(NewGRPCServer, NewHTTPServer)
9 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/service/README.md:
--------------------------------------------------------------------------------
1 | # Service
2 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/service/greeter.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 |
6 | v1 "helloworld/api/helloworld/v1"
7 | "helloworld/internal/biz"
8 | )
9 |
10 | // GreeterService is a greeter service.
11 | type GreeterService struct {
12 | v1.UnimplementedGreeterServer
13 |
14 | uc *biz.GreeterUsecase
15 | }
16 |
17 | // NewGreeterService new a greeter service.
18 | func NewGreeterService(uc *biz.GreeterUsecase) *GreeterService {
19 | return &GreeterService{uc: uc}
20 | }
21 |
22 | // SayHello implements helloworld.GreeterServer.
23 | func (s *GreeterService) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) {
24 | g, err := s.uc.CreateGreeter(ctx, &biz.Greeter{Hello: in.Name})
25 | if err != nil {
26 | return nil, err
27 | }
28 | return &v1.HelloReply{Message: "Hello " + g.Hello}, nil
29 | }
30 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/internal/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import "github.com/google/wire"
4 |
5 | // ProviderSet is service providers.
6 | var ProviderSet = wire.NewSet(NewGreeterService)
7 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/openapi.yaml:
--------------------------------------------------------------------------------
1 | # Generated with protoc-gen-openapi
2 | # https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi
3 |
4 | openapi: 3.0.3
5 | info:
6 | title: Greeter API
7 | description: The greeting service definition.
8 | version: 0.0.1
9 | paths:
10 | /helloworld/{name}:
11 | get:
12 | tags:
13 | - Greeter
14 | - subgroup
15 | description: Sends a greeting
16 | operationId: Greeter_SayHello
17 | parameters:
18 | - name: name
19 | in: path
20 | required: true
21 | schema:
22 | type: string
23 | responses:
24 | "200":
25 | description: OK
26 | content:
27 | application/json:
28 | schema:
29 | $ref: '#/components/schemas/helloworld.v1.HelloReply'
30 | components:
31 | schemas:
32 | helloworld.v1.HelloReply:
33 | type: object
34 | properties:
35 | message:
36 | type: string
37 | description: The response message containing the greetings
38 | tags:
39 | - name: Greeter
40 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/pkg/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "context"
5 | "gorm.io/driver/mysql"
6 | "gorm.io/gorm"
7 | "helloworld/internal/conf"
8 | )
9 |
10 | type DBClient struct {
11 | db *gorm.DB
12 | }
13 |
14 | func NewDBClient(c *conf.Data) (*DBClient, error) {
15 | dsn := c.Database.Source
16 | if c.Database.Driver == "mysql" {
17 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
18 | SkipDefaultTransaction: true,
19 | })
20 | if err != nil {
21 | return nil, err
22 | }
23 | return &DBClient{db: db}, nil
24 | }
25 | return nil, nil
26 | }
27 |
28 | func (c *DBClient) GetDB() *gorm.DB {
29 | return c.db
30 | }
31 |
32 | type Transaction interface {
33 | ExecTx(context.Context, func(ctx context.Context) error) error
34 | }
35 |
36 | type contextTxKey struct{}
37 |
38 | // ExecTx gorm Transaction
39 | func (c *DBClient) ExecTx(ctx context.Context, fn func(ctx context.Context) error) error {
40 | return c.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
41 | ctx = context.WithValue(ctx, contextTxKey{}, tx)
42 | return fn(ctx)
43 | })
44 | }
45 |
46 | func (c *DBClient) DB(ctx context.Context) *gorm.DB {
47 | tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
48 | if ok {
49 | return tx
50 | }
51 | return c.db
52 | }
53 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/schema/20240806134830_init.sql:
--------------------------------------------------------------------------------
1 | -- +goose Up
2 | -- +goose StatementBegin
3 | CREATE TABLE IF NOT EXISTS greater (
4 | hello VARCHAR(20) NOT NULL
5 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
6 | -- +goose StatementEnd
7 |
8 | -- +goose Down
9 | -- +goose StatementBegin
10 | DROP TABLE IF EXISTS account;
11 | -- +goose StatementEnd
12 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/schema/Makefile:
--------------------------------------------------------------------------------
1 | up:
2 | goose mysql "root:root@tcp(localhost:3306)/dev?parseTime=true" up
3 |
4 | down:
5 | goose mysql "root:root@tcp(localhost:3306)/dev?parseTime=true" down
6 |
7 | create:
8 | goose mysql "root:root@tcp(localhost:3306)/dev?parseTime=true" create ${name} sql
9 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/README.md:
--------------------------------------------------------------------------------
1 | # third_party
2 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/errors/errors.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package errors;
4 |
5 | option go_package = "github.com/go-kratos/kratos/v2/errors;errors";
6 | option java_multiple_files = true;
7 | option java_package = "com.github.kratos.errors";
8 | option objc_class_prefix = "KratosErrors";
9 |
10 | import "google/protobuf/descriptor.proto";
11 |
12 | extend google.protobuf.EnumOptions {
13 | int32 default_code = 1108;
14 | }
15 |
16 | extend google.protobuf.EnumValueOptions {
17 | int32 code = 1109;
18 | }
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/google/api/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2015, Google Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package google.api;
18 |
19 | import "google/api/http.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
23 | option java_multiple_files = true;
24 | option java_outer_classname = "AnnotationsProto";
25 | option java_package = "com.google.api";
26 | option objc_class_prefix = "GAPI";
27 |
28 | extend google.protobuf.MethodOptions {
29 | // See `HttpRule`.
30 | HttpRule http = 72295728;
31 | }
32 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/google/protobuf/empty.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option go_package = "google.golang.org/protobuf/types/known/emptypb";
37 | option java_package = "com.google.protobuf";
38 | option java_outer_classname = "EmptyProto";
39 | option java_multiple_files = true;
40 | option objc_class_prefix = "GPB";
41 | option cc_enable_arenas = true;
42 |
43 | // A generic empty message that you can re-use to avoid defining duplicated
44 | // empty messages in your APIs. A typical example is to use it as the request
45 | // or the response type of an API method. For instance:
46 | //
47 | // service Foo {
48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
49 | // }
50 | //
51 | // The JSON representation for `Empty` is empty JSON object `{}`.
52 | message Empty {}
53 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/google/protobuf/source_context.proto:
--------------------------------------------------------------------------------
1 | // Protocol Buffers - Google's data interchange format
2 | // Copyright 2008 Google Inc. All rights reserved.
3 | // https://developers.google.com/protocol-buffers/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are
7 | // met:
8 | //
9 | // * Redistributions of source code must retain the above copyright
10 | // notice, this list of conditions and the following disclaimer.
11 | // * Redistributions in binary form must reproduce the above
12 | // copyright notice, this list of conditions and the following disclaimer
13 | // in the documentation and/or other materials provided with the
14 | // distribution.
15 | // * Neither the name of Google Inc. nor the names of its
16 | // contributors may be used to endorse or promote products derived from
17 | // this software without specific prior written permission.
18 | //
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 | syntax = "proto3";
32 |
33 | package google.protobuf;
34 |
35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes";
36 | option java_package = "com.google.protobuf";
37 | option java_outer_classname = "SourceContextProto";
38 | option java_multiple_files = true;
39 | option objc_class_prefix = "GPB";
40 | option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
41 |
42 | // `SourceContext` represents information about the source of a
43 | // protobuf element, like the file in which it is defined.
44 | message SourceContext {
45 | // The path-qualified name of the .proto file that contained the associated
46 | // protobuf element. For example: `"google/protobuf/source_context.proto"`.
47 | string file_name = 1;
48 | }
49 |
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/openapi/v3/annotations.proto:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | syntax = "proto3";
16 |
17 | package openapi.v3;
18 |
19 | import "openapi/v3/openapi.proto";
20 | import "google/protobuf/descriptor.proto";
21 |
22 | // This option lets the proto compiler generate Java code inside the package
23 | // name (see below) instead of inside an outer class. It creates a simpler
24 | // developer experience by reducing one-level of name nesting and be
25 | // consistent with most programming languages that don't support outer classes.
26 | option java_multiple_files = true;
27 |
28 | // The Java outer classname should be the filename in UpperCamelCase. This
29 | // class is only used to hold proto descriptor, so developers don't need to
30 | // work with it directly.
31 | option java_outer_classname = "AnnotationsProto";
32 |
33 | // The Java package name must be proto package name with proper prefix.
34 | option java_package = "org.openapi_v3";
35 |
36 | // A reasonable prefix for the Objective-C symbols generated from the package.
37 | // It should at a minimum be 3 characters long, all uppercase, and convention
38 | // is to use an abbreviation of the package name. Something short, but
39 | // hopefully unique enough to not conflict with things that may come along in
40 | // the future. 'GPB' is reserved for the protocol buffer implementation itself.
41 | option objc_class_prefix = "OAS";
42 |
43 | // The Go package name.
44 | option go_package = "github.com/google/gnostic/openapiv3;openapi_v3";
45 |
46 | extend google.protobuf.FileOptions {
47 | Document document = 1143;
48 | }
49 |
50 | extend google.protobuf.MethodOptions {
51 | Operation operation = 1143;
52 | }
53 |
54 | extend google.protobuf.MessageOptions {
55 | Schema schema = 1143;
56 | }
57 |
58 | extend google.protobuf.FieldOptions {
59 | Schema property = 1143;
60 | }
--------------------------------------------------------------------------------
/kit/transaction/helloworld/third_party/validate/README.md:
--------------------------------------------------------------------------------
1 | # protoc-gen-validate (PGV)
2 |
3 | * https://github.com/envoyproxy/protoc-gen-validate
4 |
--------------------------------------------------------------------------------
/storage/go.mod:
--------------------------------------------------------------------------------
1 | module storage
2 |
3 | go 1.21.3
4 |
5 | require go.mongodb.org/mongo-driver v1.14.0
6 |
7 | require (
8 | github.com/davecgh/go-spew v1.1.1 // indirect
9 | github.com/golang/snappy v0.0.1 // indirect
10 | github.com/klauspost/compress v1.13.6 // indirect
11 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
12 | github.com/pmezard/go-difflib v1.0.0 // indirect
13 | github.com/stretchr/testify v1.9.0 // indirect
14 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect
15 | github.com/xdg-go/scram v1.1.2 // indirect
16 | github.com/xdg-go/stringprep v1.0.4 // indirect
17 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
18 | golang.org/x/crypto v0.17.0 // indirect
19 | golang.org/x/sync v0.1.0 // indirect
20 | golang.org/x/text v0.14.0 // indirect
21 | gopkg.in/yaml.v3 v3.0.1 // indirect
22 | )
23 |
--------------------------------------------------------------------------------
/storage/mongodb/Makefile:
--------------------------------------------------------------------------------
1 | # 定义项目名称
2 | PROJECT_NAME := mongodb
3 |
4 | # 默认目标
5 | .DEFAULT_GOAL := help
6 |
7 | # 定义命令
8 | DOCKER_COMPOSE := docker-compose
9 |
10 | # 配置 Docker Compose 文件路径
11 | DOCKER_COMPOSE_FILE := docker-compose.yaml
12 |
13 | # 启动 MongoDB 服务
14 | up:
15 | $(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) up -d
16 |
17 | # 停止 MongoDB 服务
18 | down:
19 | $(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) down
20 |
21 | # 清理 MongoDB 数据卷
22 | clean:
23 | $(DOCKER_COMPOSE) -f $(DOCKER_COMPOSE_FILE) down -v
24 |
25 | # 帮助信息
26 | help:
27 | @echo "Usage:"
28 | @echo " make up Start MongoDB container"
29 | @echo " make down Stop MongoDB container"
30 | @echo " make clean Remove MongoDB data volume"
31 | @echo " make help Show this help message"
32 |
--------------------------------------------------------------------------------
/storage/mongodb/config.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | // Config defines the config for storage.
4 | type Config struct {
5 | // Connection string to use for DB. Will override all other authentication values if used
6 | //
7 | // Optional. Default is ""
8 | ConnectionURI string
9 |
10 | // Host name where the DB is hosted
11 | //
12 | // Optional. Default is "127.0.0.1"
13 | Host string
14 |
15 | // Port where the DB is listening on
16 | //
17 | // Optional. Default is 27017
18 | Port int
19 |
20 | // Server username
21 | //
22 | // Optional. Default is ""
23 | Username string
24 |
25 | // Server password
26 | //
27 | // Optional. Default is ""
28 | Password string
29 |
30 | // Database name
31 | //
32 | // Optional. Default is "doutok"
33 | Database string
34 |
35 | // Collection name
36 | //
37 | // Optional. Default is "default"
38 | Collection string
39 |
40 | // Reset clears any existing keys in existing Table
41 | //
42 | // Optional. Default is false
43 | Reset bool
44 | }
45 |
46 | // ConfigDefault is the default config
47 | var ConfigDefault = Config{
48 | ConnectionURI: "",
49 | Host: "127.0.0.1",
50 | Port: 27017,
51 | Database: "doutok",
52 | Collection: "default",
53 | Reset: false,
54 | }
55 |
56 | // Helper function to set default values
57 | func configDefault(config ...Config) Config {
58 | // Return default config if nothing provided
59 | if len(config) < 1 {
60 | return ConfigDefault
61 | }
62 |
63 | // Override default config
64 | cfg := config[0]
65 |
66 | // Set default values
67 | if cfg.Host == "" {
68 | cfg.Host = ConfigDefault.Host
69 | }
70 | if cfg.Port <= 0 {
71 | cfg.Port = ConfigDefault.Port
72 | }
73 | if cfg.Database == "" {
74 | cfg.Database = ConfigDefault.Database
75 | }
76 | if cfg.Collection == "" {
77 | cfg.Collection = ConfigDefault.Collection
78 | }
79 | return cfg
80 | }
81 |
--------------------------------------------------------------------------------
/storage/mongodb/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | mongodb:
5 | image: mongo:latest
6 | container_name: mongodb
7 | restart: always
8 | ports:
9 | - "27017:27017"
10 | environment:
11 | MONGO_INITDB_ROOT_USERNAME: root
12 | MONGO_INITDB_ROOT_PASSWORD: root
13 | volumes:
14 | - mongodb_data:/data/db
15 |
16 | volumes:
17 | mongodb_data:
18 |
--------------------------------------------------------------------------------
/storage/mongodb/mongodb_test.go:
--------------------------------------------------------------------------------
1 | package mongodb
2 |
3 | import (
4 | "context"
5 | "github.com/stretchr/testify/require"
6 | "testing"
7 | )
8 |
9 | type User struct {
10 | Id string `json:"id" bson:"_id"` // 自定义的 Id 字段
11 | Username string `json:"username"`
12 | Password string `json:"password"`
13 | Email string `json:"email"`
14 | }
15 |
16 | // TestStorage_InsertAndFindOne 需要通过 Makefile 提前启动本地 MongoDB 服务
17 | func TestStorage_InsertAndFindOne(t *testing.T) {
18 | config := Config{
19 | Username: "root",
20 | Password: "root",
21 | Database: "doutok",
22 | Collection: "default",
23 | }
24 | store := New(config)
25 |
26 | // 创建用户
27 | _, err := store.InsertOne(context.Background(), User{
28 | Id: "123", // 手动指定自定义的 Id 值
29 | Username: "zhangsan",
30 | Password: "123456",
31 | Email: "123@.com",
32 | })
33 | require.NoError(t, err)
34 |
35 | // 查询用户
36 | result := store.FindOne(context.Background(), map[string]interface{}{"_id": "123"})
37 |
38 | // 解码查询结果
39 | var user map[string]interface{}
40 | err = result.Decode(&user)
41 | require.NoError(t, err)
42 | require.Equal(t, "123@.com", user["email"])
43 |
44 | // 修改用户
45 | update := map[string]interface{}{
46 | "$set": map[string]interface{}{
47 | "email": "321@.com",
48 | },
49 | }
50 | _, err = store.UpdateOne(context.Background(),
51 | map[string]interface{}{"_id": "123"},
52 | update)
53 | require.NoError(t, err)
54 |
55 | // 查询用户
56 | result = store.FindOne(context.Background(), map[string]interface{}{"_id": "123"})
57 |
58 | // 解码查询结果
59 | err = result.Decode(&user)
60 | require.NoError(t, err)
61 | require.Equal(t, "321@.com", user["email"])
62 |
63 | // 删除用户
64 | count, err := store.DeleteOne(context.Background(), map[string]interface{}{"_id": "123"})
65 | require.NoError(t, err)
66 | require.Equal(t, int64(1), count)
67 |
68 | // 查询用户
69 | result = store.FindOne(context.Background(), map[string]interface{}{"_id": "123"})
70 | user2 := User{}
71 | err = result.Decode(&user2)
72 | require.Equal(t, "mongo: no documents in result", err.Error())
73 | }
74 |
--------------------------------------------------------------------------------
/temporal/go.mod:
--------------------------------------------------------------------------------
1 | module app
2 |
3 | go 1.22.2
4 |
5 | require go.temporal.io/sdk v1.29.1 // indirect
6 |
--------------------------------------------------------------------------------
/temporal/go.sum:
--------------------------------------------------------------------------------
1 | go.temporal.io/sdk v1.29.1 h1:y+sUMbUhTU9rj50mwIZAPmcXCtgUdOWS9xHDYRYSgZ0=
2 | go.temporal.io/sdk v1.29.1/go.mod h1:kp//DRvn3CqQVBCtjL51Oicp9wrZYB2s6row1UgzcKQ=
3 |
--------------------------------------------------------------------------------