feat: bootstrap reusable GoOmni framework

This commit is contained in:
2026-09-20 22:06:24 +08:00
commit 8b19aa79ee
60 changed files with 4923 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
# GoOmni service
This service was generated with `go-omni new`.
```sh
go mod tidy
go run ./cmd/server
```
运行时配置从 `GO_OMNI_*` 环境变量加载,例如:
```sh
GO_OMNI_ENABLE_LOGGER=true GO_OMNI_LOG_LEVEL=debug go run ./cmd/server
```
`GO_OMNI_HTTP_ADDR` 可以修改监听地址。生成项目默认提供 `/health/live` 和
`/health/ready`,日志、请求 ID 和优雅退出等基础能力由 GoOmni 统一初始化;业务依赖集中放在项目自己的 `internal/servicecontext`。
The generated layout keeps HTTP handlers, business services, repositories,
models and a project-owned ServiceContext separate. Replace the memory
repository with a database implementation when the service is ready for
persistence, then add it to `internal/servicecontext`.

View File

@@ -0,0 +1,49 @@
package main
import (
"log"
"strings"
omni "git.yuntuo.hk/k2safe/go-omni"
"git.yuntuo.hk/k2safe/go-omni/config"
"git.yuntuo.hk/k2safe/go-omni/health"
"git.yuntuo.hk/k2safe/go-omni/logx"
"{{.Module}}/internal/handler"
"{{.Module}}/internal/repository"
"{{.Module}}/internal/service"
"{{.Module}}/internal/servicecontext"
)
func main() {
settings, err := config.LoadFromEnv("")
if err != nil {
log.Fatal(err)
}
if strings.TrimSpace(settings.Log.Service) == "" {
settings.Log.Service = "{{.Module}}"
}
logger, err := logx.New(settings.Log)
if err != nil {
log.Fatal(err)
}
// Access logs and business logs share the same configured sink.
logx.SetDefault(logger)
users := repository.NewMemoryUsers()
services := servicecontext.New(logger, users)
userService := service.NewUsers(services)
userHandler := handler.NewUsers(userService)
checker, err := health.New()
if err != nil {
log.Fatal(err)
}
application := omni.New(omni.WithConfig(settings))
application.OnShutdown(logger.Close)
application.GET("/health/live", checker.LivenessHandler())
application.GET("/health/ready", checker.ReadinessHandler())
userHandler.RegisterRoutes(application)
if err := application.Run(settings.Address); err != nil {
log.Fatal(err)
}
}

View File

@@ -0,0 +1,8 @@
module {{.Module}}
go 1.22
require git.yuntuo.hk/k2safe/go-omni v0.0.0
{{- if .FrameworkReplace}}
replace git.yuntuo.hk/k2safe/go-omni => {{.FrameworkReplace}}
{{- end}}

View File

@@ -0,0 +1,31 @@
package handler
import (
"net/http"
omni "git.yuntuo.hk/k2safe/go-omni"
"{{.Module}}/internal/model"
"{{.Module}}/internal/service"
)
type Users struct {
service *service.Users
}
func NewUsers(service *service.Users) *Users { return &Users{service: service} }
func (h *Users) RegisterRoutes(application *omni.App) {
application.POST("/users", h.Create)
}
func (h *Users) Create(ctx *omni.Context) error {
var input model.UserInput
if err := ctx.BindJSON(&input); err != nil {
return err
}
user, err := h.service.Create(ctx.Context(), input)
if err != nil {
return err
}
return ctx.JSON(http.StatusCreated, user)
}

View File

@@ -0,0 +1,16 @@
package model
import "time"
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type UserInput struct {
Name string `json:"name"`
Email string `json:"email"`
}

View File

@@ -0,0 +1,31 @@
package repository
import (
"context"
"sync"
"time"
"{{.Module}}/internal/model"
)
type UserRepository interface {
Create(context.Context, model.UserInput) (model.User, error)
}
type MemoryUsers struct {
mu sync.Mutex
nextID int64
users []model.User
}
func NewMemoryUsers() *MemoryUsers { return &MemoryUsers{nextID: 1} }
func (r *MemoryUsers) Create(_ context.Context, input model.UserInput) (model.User, error) {
r.mu.Lock()
defer r.mu.Unlock()
now := time.Now().UTC()
user := model.User{ID: r.nextID, Name: input.Name, Email: input.Email, CreatedAt: now, UpdatedAt: now}
r.nextID++
r.users = append(r.users, user)
return user, nil
}

View File

@@ -0,0 +1,21 @@
package service
import (
"context"
"{{.Module}}/internal/model"
"{{.Module}}/internal/servicecontext"
)
type Users struct {
context *servicecontext.Context
}
func NewUsers(context *servicecontext.Context) *Users {
return &Users{context: context}
}
func (s *Users) Create(ctx context.Context, input model.UserInput) (model.User, error) {
// Put business normalization and rules here.
return s.context.Users.Create(ctx, input)
}

View File

@@ -0,0 +1,17 @@
package servicecontext
import (
"git.yuntuo.hk/k2safe/go-omni/logx"
"{{.Module}}/internal/repository"
)
// Context owns infrastructure dependencies shared by the service layer.
// Add database, cache and external clients here as the project grows.
type Context struct {
Logger *logx.Logger
Users repository.UserRepository
}
func New(logger *logx.Logger, users repository.UserRepository) *Context {
return &Context{Logger: logger, Users: users}
}