feat:添加服务模型映射功能

This commit is contained in:
2944321442@qq.com
2025-03-05 10:15:05 +08:00
parent 4b263c1e7a
commit 860874ef5c
17 changed files with 211 additions and 379 deletions
+44
View File
@@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "frontend"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true
-1
View File
@@ -8,7 +8,6 @@ require (
github.com/eolinker/ap-account v1.0.15
github.com/eolinker/eosc v0.18.3
github.com/eolinker/go-common v1.1.5
github.com/eolinker/go-common v1.1.5
github.com/gabriel-vasile/mimetype v1.4.4
github.com/getkin/kin-openapi v0.127.0
github.com/gin-contrib/gzip v1.0.1
+2
View File
@@ -40,6 +40,8 @@ github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3
github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
github.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+16 -26
View File
@@ -19,7 +19,7 @@ import (
"github.com/eolinker/ap-account/service/role"
application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"
"github.com/APIParkLab/APIPark/service/service_model_mapping"
service_model_mapping "github.com/APIParkLab/APIPark/service/service-model-mapping"
api_doc "github.com/APIParkLab/APIPark/service/api-doc"
@@ -244,8 +244,8 @@ func (i *imlServiceModule) Get(ctx context.Context, id string) (*service_dto.Ser
}
}
serviceModelMapping, err := i.serviceModelMappingService.GetByService(ctx, id)
serviceModelMapping, err := i.serviceModelMappingService.Get(ctx, id)
if err != nil {
return nil, err
}
@@ -359,7 +359,6 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser
return nil, fmt.Errorf("ai service: model can not be empty")
}
mo.AdditionalConfig["model"] = *input.Model
}
if input.AsApp == nil {
// 默认值为false
@@ -391,14 +390,13 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser
}
}
}
if input.ModelMapping != "" {
err := i.serviceModelMappingService.Create(ctx, &service_model_mapping.Create{
Service: input.Id,
Content: input.ModelMapping,
})
if err != nil {
return err
}
err := i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{
Sid: input.Id,
Content: input.ModelMapping,
})
if err != nil {
return err
}
return i.serviceService.Create(ctx, mo)
})
@@ -468,20 +466,12 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d
}
}
}
if input.ModelMapping != "" {
// 先删除旧的映射
err := i.serviceModelMappingService.Delete(ctx, id)
if err != nil {
return err
}
// 创建新的映射
err = i.serviceModelMappingService.Create(ctx, &service_model_mapping.Create{
Service: id,
Content: input.ModelMapping,
})
if err != nil {
return err
}
err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{
Sid: id,
Content: input.ModelMapping,
})
if err != nil {
return err
}
return nil
})
+2 -1
View File
@@ -3,9 +3,10 @@ package service_doc
import (
"context"
"errors"
"github.com/APIParkLab/APIPark/service/universally/commit"
"time"
"github.com/APIParkLab/APIPark/service/universally/commit"
"github.com/eolinker/go-common/utils"
"gorm.io/gorm"
+2 -1
View File
@@ -2,9 +2,10 @@ package service_doc
import (
"context"
"github.com/APIParkLab/APIPark/service/universally/commit"
"reflect"
"github.com/APIParkLab/APIPark/service/universally/commit"
"github.com/eolinker/go-common/autowire"
)
+67
View File
@@ -0,0 +1,67 @@
package service_model_mapping
import (
"context"
"errors"
"time"
"github.com/APIParkLab/APIPark/stores/service"
"github.com/eolinker/go-common/utils"
"gorm.io/gorm"
)
var _ IServiceModelMappingService = (*imlServiceModelMappingService)(nil)
type imlServiceModelMappingService struct {
store service.IServiceModelMappingStore `autowired:""`
}
func (i *imlServiceModelMappingService) Get(ctx context.Context, sid string) (*ModelMapping, error) {
entity, err := i.store.First(ctx, map[string]interface{}{"sid": sid})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return &ModelMapping{
Sid: sid,
Content: "",
}, nil
}
return nil, err
}
return FromEntity(entity), nil
}
func FromEntity(e *service.ModelMapping) *ModelMapping {
content := ""
if e.Content != "" {
content = e.Content
}
return &ModelMapping{
ID: e.Id,
Sid: e.Sid,
Content: content,
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
}
}
func (i *imlServiceModelMappingService) Save(ctx context.Context, input *Save) error {
info, err := i.store.First(ctx, map[string]interface{}{"sid": input.Sid})
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
userID := utils.UserId(ctx)
if info != nil {
info.Content = input.Content
info.Updater = userID
info.UpdateAt = time.Now()
return i.store.Save(ctx, info)
}
return i.store.Insert(ctx, &service.ModelMapping{
Sid: input.Sid,
Content: input.Content,
CreateAt: time.Now(),
UpdateAt: time.Now(),
Creator: userID,
Updater: userID,
})
}
+20
View File
@@ -0,0 +1,20 @@
package service_model_mapping
import (
"time"
)
type ModelMapping struct {
ID int64 `json:"id"`
Sid string `json:"sid"`
Content string `json:"content"`
Creator string `json:"creator"`
Updater string `json:"updater"`
CreateAt time.Time `json:"create_at"`
UpdateAt time.Time `json:"update_at"`
}
type Save struct {
Sid string `json:"sid" validate:"required"`
Content string `json:"content" validate:"required"`
}
+19
View File
@@ -0,0 +1,19 @@
package service_model_mapping
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
)
type IServiceModelMappingService interface {
Get(ctx context.Context, sid string) (*ModelMapping, error)
Save(ctx context.Context, input *Save) error
}
func init() {
autowire.Auto[IServiceModelMappingService](func() reflect.Value {
return reflect.ValueOf(new(imlServiceModelMappingService))
})
}
-88
View File
@@ -1,88 +0,0 @@
package service_model_mapping
import (
"context"
"errors"
"time"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/service"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/go-common/utils"
"github.com/google/uuid"
"gorm.io/gorm"
)
var _ IServiceModelMappingService = (*imlServiceModelMappingService)(nil)
type imlServiceModelMappingService struct {
store service.IModelMappingStore `autowired:""`
universally.IServiceGet[ModelMapping]
universally.IServiceDelete
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
}
func (i *imlServiceModelMappingService) GetByService(ctx context.Context, serviceId string) (*ModelMapping, error) {
w := map[string]interface{}{
"service": serviceId,
}
entity, err := i.store.First(ctx, w)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return &ModelMapping{
Service: serviceId,
Content: "",
}, nil
}
return nil, err
}
return FromEntity(entity), nil
}
func (i *imlServiceModelMappingService) GetLabels(ctx context.Context, ids ...string) map[string]string {
if len(ids) == 0 {
return nil
}
list, err := i.store.ListQuery(ctx, "`uuid` in (?)", []interface{}{ids}, "id")
if err != nil {
return nil
}
return utils.SliceToMapO(list, func(i *service.ModelMapping) (string, string) {
return i.UUID, i.Service
})
}
func (i *imlServiceModelMappingService) OnComplete() {
i.IServiceGet = universally.NewGetSoftDelete[ModelMapping, service.ModelMapping](i.store, FromEntity)
i.IServiceDelete = universally.NewSoftDelete[service.ModelMapping](i.store)
i.IServiceCreate = universally.NewCreatorSoftDelete[Create, service.ModelMapping](i.store, "service_model_mapping", createEntityHandler, uniquestHandler, labelHandler)
i.IServiceEdit = universally.NewEdit[Edit, service.ModelMapping](i.store, updateHandler, labelHandler)
auto.RegisterService("service_model_mapping", i)
}
func labelHandler(e *service.ModelMapping) []string {
return []string{e.Service, e.UUID}
}
func uniquestHandler(i *Create) []map[string]interface{} {
return []map[string]interface{}{{"service": i.Service}}
}
func createEntityHandler(i *Create) *service.ModelMapping {
now := time.Now()
return &service.ModelMapping{
UUID: uuid.New().String(),
Service: i.Service,
Content: i.Content,
CreateAt: now,
UpdateAt: now,
}
}
func updateHandler(e *service.ModelMapping, i *Edit) {
if i.Content != nil {
e.Content = *i.Content
e.UpdateAt = time.Now()
}
}
-38
View File
@@ -1,38 +0,0 @@
package service_model_mapping
import (
"time"
"github.com/APIParkLab/APIPark/stores/service"
)
type ModelMapping struct {
UUID string `json:"uuid"`
Service string `json:"service"`
Content string `json:"content"`
CreateAt time.Time `json:"create_at"`
UpdateAt time.Time `json:"update_at"`
}
type Create struct {
Service string `json:"service" validate:"required"`
Content string `json:"content" validate:"required"`
}
type Edit struct {
Content *string `json:"content,omitempty"`
}
func FromEntity(e *service.ModelMapping) *ModelMapping {
content := ""
if e.Content != "" {
content = e.Content
}
return &ModelMapping{
UUID: e.UUID,
Service: e.Service,
Content: content,
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
}
}
-138
View File
@@ -1,138 +0,0 @@
package service_model_mapping
import (
"testing"
"time"
"github.com/APIParkLab/APIPark/stores/service"
"github.com/stretchr/testify/assert"
)
func TestFromEntity(t *testing.T) {
tests := []struct {
name string
entity *service.ModelMapping
expected *ModelMapping
}{
{
name: "正常转换",
entity: &service.ModelMapping{
UUID: "test-uuid",
Service: "test-service",
Content: "{\"key\":\"value\"}",
CreateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
expected: &ModelMapping{
UUID: "test-uuid",
Service: "test-service",
Content: "{\"key\":\"value\"}",
CreateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
{
name: "空内容转换",
entity: &service.ModelMapping{
UUID: "test-uuid",
Service: "test-service",
Content: "",
CreateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
expected: &ModelMapping{
UUID: "test-uuid",
Service: "test-service",
Content: "",
CreateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
UpdateAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := FromEntity(tt.entity)
assert.Equal(t, tt.expected, result)
})
}
}
func TestCreate_Validation(t *testing.T) {
tests := []struct {
name string
create Create
isValid bool
}{
{
name: "有效的创建参数",
create: Create{
Service: "test-service",
Content: "{\"key\":\"value\"}",
},
isValid: true,
},
{
name: "缺少Service",
create: Create{
Content: "{\"key\":\"value\"}",
},
isValid: false,
},
{
name: "缺少Content",
create: Create{
Service: "test-service",
},
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 这里需要实现验证逻辑的测试
// 由于验证逻辑可能依赖于具体的验证框架,这里只是示例
// 实际使用时需要根据项目使用的验证框架来实现
if tt.isValid {
assert.NotEmpty(t, tt.create.Service)
assert.NotEmpty(t, tt.create.Content)
} else {
assert.True(t, tt.create.Service == "" || tt.create.Content == "")
}
})
}
}
func TestEdit_Validation(t *testing.T) {
content := "new content"
tests := []struct {
name string
edit Edit
isValid bool
}{
{
name: "有效的编辑参数",
edit: Edit{
Content: &content,
},
isValid: true,
},
{
name: "空编辑参数",
edit: Edit{
Content: nil,
},
isValid: true, // 允许空编辑
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.isValid {
if tt.edit.Content != nil {
assert.NotEmpty(t, *tt.edit.Content)
}
}
})
}
}
-25
View File
@@ -1,25 +0,0 @@
package service_model_mapping
import (
"context"
"reflect"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/eolinker/go-common/autowire"
)
type IServiceModelMappingService interface {
universally.IServiceGet[ModelMapping]
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
// GetByService 根据服务ID获取模型映射
GetByService(ctx context.Context, serviceId string) (*ModelMapping, error)
}
func init() {
autowire.Auto[IServiceModelMappingService](func() reflect.Value {
return reflect.ValueOf(new(imlServiceModelMappingService))
})
}
+4 -6
View File
@@ -4,15 +4,14 @@ import (
"context"
"errors"
"fmt"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/go-common/store"
"github.com/eolinker/go-common/utils"
"gorm.io/gorm"
)
var (
_ IServiceDelete = (*imlServiceDelete[any])(nil)
)
var _ IServiceDelete = (*imlServiceDelete[any])(nil)
type IServiceDelete interface {
Delete(ctx context.Context, uuid string) error
@@ -26,10 +25,12 @@ func NewDelete[E any](store store.ISearchStore[E]) IServiceDelete {
assert(new(E))
return &imlServiceDelete[E]{store: store}
}
func NewSoftDelete[E any](s store.ISearchStore[E]) IServiceDelete {
assert(new(E))
return &imlServiceSoftDelete[E]{store: s}
}
func (p *imlServiceDelete[E]) Delete(ctx context.Context, uuid string) error {
return p.store.Transaction(ctx, func(ctx context.Context) error {
o, err := p.store.First(ctx, map[string]interface{}{"uuid": uuid})
@@ -46,7 +47,6 @@ func (p *imlServiceDelete[E]) Delete(ctx context.Context, uuid string) error {
}
return p.store.SetLabels(ctx, idValue(o))
})
}
type imlServiceSoftDelete[E any] struct {
@@ -66,7 +66,5 @@ func (p *imlServiceSoftDelete[E]) Delete(ctx context.Context, uuid string) error
auto.Auto("operator", operator, o)
return p.store.SoftDelete(ctx, map[string]interface{}{"uuid": uuid})
})
}
+22 -1
View File
@@ -1,6 +1,8 @@
package service
import "time"
import (
"time"
)
type Service struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
@@ -27,6 +29,7 @@ type Service struct {
func (p *Service) IdValue() int64 {
return p.Id
}
func (p *Service) TableName() string {
return "service"
}
@@ -87,3 +90,21 @@ func (d *Doc) IdValue() int64 {
func (d *Doc) TableName() string {
return "server_doc"
}
type ModelMapping struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:PRIMARY ID;primary_key"`
Sid string `gorm:"size:36;not null;column:sid;comment:service uuid;uniqueIndex:unique_sid;"`
Content string `gorm:"type:text;not null;column:content;comment:mapping json"`
Creator string `gorm:"type:varchar(36);not null;column:creator;comment:creator" aovalue:"creator"`
Updater string `gorm:"type:varchar(36);not null;column:updater;comment:updater" aovalue:"updater"`
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:create_at"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;column:update_at;comment:update_at"`
}
func (m *ModelMapping) TableName() string {
return "service_model_mapping"
}
func (m *ModelMapping) IdValue() int64 {
return m.Id
}
-42
View File
@@ -1,42 +0,0 @@
package service
import (
"reflect"
"time"
"github.com/eolinker/go-common/store"
"github.com/eolinker/go-common/autowire"
)
type ModelMapping struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:PRIMARY ID;primary_key"`
UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:uuid"`
Service string `gorm:"type:varchar(36);not null;column:service;index:service;comment:service:uuid"`
Content string `gorm:"type:text;not null;column:content;comment:mapping json"`
Creator string `gorm:"type:varchar(36);not null;column:creator;comment:creator" aovalue:"creator"`
Updater string `gorm:"type:varchar(36);not null;column:updater;comment:updater" aovalue:"updater"`
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:create_at"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;column:update_at;comment:update_at"`
}
func (m *ModelMapping) TableName() string {
return "service_model_mapping"
}
func (m *ModelMapping) IdValue() int64 {
return m.Id
}
type IModelMappingStore interface {
store.ISearchStore[ModelMapping]
}
type imlModelMappingStore struct {
store.SearchStore[ModelMapping]
}
func init() {
autowire.Auto[IModelMappingStore](func() reflect.Value {
return reflect.ValueOf(new(imlModelMappingStore))
})
}
+13 -12
View File
@@ -1,27 +1,17 @@
package service
import (
"reflect"
"github.com/eolinker/go-common/autowire"
"github.com/eolinker/go-common/store"
)
import "reflect"
type IServiceStore interface {
store.ISearchStore[Service]
GetModelMapping(service string) (*ModelMapping, error)
SaveModelMapping(mapping *ModelMapping) error
}
type imlServiceStore struct {
store.SearchStore[Service]
mappingStore IModelMappingStore `autowired:""`
}
func (i *imlServiceStore) GetModelMapping(service string) (*ModelMapping, error) {
return i.mappingStore.First(nil, map[string]interface{}{"service": service})
}
func (i *imlServiceStore) SaveModelMapping(mapping *ModelMapping) error {
return i.mappingStore.Save(nil, mapping)
}
type IServiceTagStore interface {
@@ -48,6 +38,14 @@ type imlAuthorizationStore struct {
store.SearchStore[Authorization]
}
type IServiceModelMappingStore interface {
store.ISearchStore[ModelMapping]
}
type imlServiceModelMappingStore struct {
store.SearchStore[ModelMapping]
}
func init() {
autowire.Auto[IServiceStore](func() reflect.Value {
return reflect.ValueOf(new(imlServiceStore))
@@ -63,4 +61,7 @@ func init() {
return reflect.ValueOf(new(imlServiceDocStore))
})
autowire.Auto[IServiceModelMappingStore](func() reflect.Value {
return reflect.ValueOf(new(imlServiceModelMappingStore))
})
}