feat: replace mapping generation with checked copy utilities

This commit is contained in:
2026-09-25 17:27:28 +08:00
parent ad9257d202
commit fd83235ce7
45 changed files with 2366 additions and 1872 deletions

View File

@@ -0,0 +1,23 @@
# 结构复制示例
在框架仓库根目录运行 `go run ./examples/copying`;不需要数据库、网络服务或 JSON 映射配置。
示例包含:可复用构造器、Go 字段改名、时间自定义转换、保留 false、深拷贝切片、同类型 Clone,以及拒绝自定义转换器返回输入的原切片。
预期输出:
```text
name=sample enabled=false time=2026-01-01T00:00:00Z sourceTag=original
clone detached: true
shared converter rejected: true
```
测试:`go test -race ./examples/copying`。完整策略、限制与旧生成映射迁移见 [COPY.md](../../COPY.md)。
独立工资三端的实际生成、HTTP 与金额用例见 [集成测试](../../cmd/go-omni/apigen/copyConsumer_test.go) 和 [公共契约](../../cmd/go-omni/apigen/testdata/composition/service.api)。框架根目录运行:
```sh
GO_OMNI_SCAFFOLD_INTEGRATION=1 go test ./cmd/go-omni/apigen -run TestCopyxThreeEndpointConsumer -count=1
```
该测试在临时独立项目中执行,三个入口共用一个普通 Go copyx helper。公共字段新增时,只修改公共契约和独立业务结构,三个入口的转换规则不增加。

61
examples/copying/main.go Normal file
View File

@@ -0,0 +1,61 @@
// copying demonstrates optional Go-to-Go conversion and checked deep copying.
package main
import (
"errors"
"fmt"
"log"
"time"
"git.yuntuo.hk/k2safe/go-omni/copyx"
)
type Input struct {
Name string
Enabled bool
Tags []string
CreatedAt time.Time
}
type View struct {
DisplayName string
Enabled bool
Tags []string
CreatedAt string
}
func run() error {
converter, err := copyx.New(
copyx.WithDeepCopy(),
copyx.WithFieldNames[Input, View](map[string]string{"Name": "DisplayName"}),
copyx.WithConverter(func(v time.Time) (string, error) { return v.UTC().Format(time.RFC3339), nil }),
)
if err != nil {
return err
}
source := Input{Name: "sample", Enabled: false, Tags: []string{"original"}, CreatedAt: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)}
var target View
if err = converter.Copy(&target, source); err != nil {
return err
}
target.Tags[0] = "changed"
fmt.Printf("name=%s enabled=%t time=%s sourceTag=%s\n", target.DisplayName, target.Enabled, target.CreatedAt, source.Tags[0])
cloned, err := copyx.Clone(source)
if err != nil {
return err
}
cloned.Tags[0] = "separate"
fmt.Println("clone detached:", source.Tags[0] == "original")
_, err = copyx.Clone(source.Tags, copyx.WithConverter(func(v []string) ([]string, error) { return v, nil }))
fmt.Println("shared converter rejected:", errors.Is(err, copyx.ErrSharedReference))
if !errors.Is(err, copyx.ErrSharedReference) {
return errors.New("expected alias rejection")
}
return nil
}
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}

View File

@@ -0,0 +1,9 @@
package main
import "testing"
func TestExample(t *testing.T) {
if err := run(); err != nil {
t.Fatal(err)
}
}

View File

@@ -1,6 +1,6 @@
{
"generatorConfig": true,
"templateVersion": "v0.18.0-preview",
"templateVersion": "v0.19.0-preview",
"profile": {
"database": "none",
"storage": "none",

View File

@@ -2,7 +2,7 @@ module example.com/managed
go 1.22.0
require git.yuntuo.hk/k2safe/go-omni v0.18.0-preview
require git.yuntuo.hk/k2safe/go-omni v0.19.0-preview
require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect