mirror of
https://github.com/gowvp/gb28181.git
synced 2026-04-22 15:07:10 +08:00
feat(metadata): 配置持久化
This commit is contained in:
@@ -14,7 +14,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gowvp/owl/internal/conf"
|
||||
"github.com/ixugo/goddd/domain/version/versionapi"
|
||||
"github.com/ixugo/goddd/pkg/logger"
|
||||
"github.com/ixugo/goddd/pkg/server"
|
||||
"github.com/ixugo/goddd/pkg/system"
|
||||
@@ -51,10 +50,6 @@ func Run(bc *conf.Bootstrap) {
|
||||
go setupAIClient(ctx, "http://127.0.0.1:15123/ai", bc.Debug)
|
||||
}
|
||||
|
||||
// 如果需要执行表迁移,递增此版本号和表更新说明
|
||||
versionapi.DBVersion = "0.0.23"
|
||||
versionapi.DBRemark = "onvif device support"
|
||||
|
||||
handler, cleanUp, err := wireApp(bc, log)
|
||||
if err != nil {
|
||||
slog.Error("程序构建失败", "err", err)
|
||||
|
||||
@@ -8,6 +8,7 @@ package app
|
||||
|
||||
import (
|
||||
"github.com/gowvp/owl/internal/conf"
|
||||
"github.com/gowvp/owl/internal/core/metadata/metadataapi"
|
||||
"github.com/gowvp/owl/internal/data"
|
||||
"github.com/gowvp/owl/internal/web/api"
|
||||
"github.com/gowvp/owl/pkg/gbs"
|
||||
@@ -43,6 +44,8 @@ func wireApp(bc *conf.Bootstrap, log *slog.Logger) (http.Handler, func(), error)
|
||||
aiWebhookAPI := api.NewAIWebhookAPIWithDeps(bc, eventCore, ipcBundle)
|
||||
eventAPI := api.NewEventAPI(eventCore, bc)
|
||||
recordingAPI := api.NewRecordingAPI(recordingCore, bc)
|
||||
metadataCore := metadataapi.NewMetadataCore(db)
|
||||
metadataAPI := metadataapi.NewMetadataAPI(metadataCore)
|
||||
usecase := &api.Usecase{
|
||||
Conf: bc,
|
||||
DB: db,
|
||||
@@ -57,6 +60,7 @@ func wireApp(bc *conf.Bootstrap, log *slog.Logger) (http.Handler, func(), error)
|
||||
AIWebhookAPI: aiWebhookAPI,
|
||||
EventAPI: eventAPI,
|
||||
RecordingAPI: recordingAPI,
|
||||
MetadataAPI: metadataAPI,
|
||||
}
|
||||
handler := api.NewHTTPHandler(usecase)
|
||||
return handler, func() {
|
||||
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadata
|
||||
|
||||
// Storer data persistence
|
||||
type Storer interface {
|
||||
Metadata() MetadataStorer
|
||||
}
|
||||
|
||||
// Core business domain
|
||||
type Core struct {
|
||||
store Storer
|
||||
}
|
||||
|
||||
// NewCore create business domain
|
||||
func NewCore(store Storer) Core {
|
||||
return Core{store: store}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package metadata 通用数据持久化领域
|
||||
// 提供 JSON 数据的存储能力,ID 由调用方指定,
|
||||
// 仅支持创建、修改、按 ID 查询,不提供列表和删除接口
|
||||
package metadata
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/ixugo/goddd/pkg/orm"
|
||||
"github.com/ixugo/goddd/pkg/reason"
|
||||
"github.com/jinzhu/copier"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MetadataStorer Instantiation interface
|
||||
type MetadataStorer interface {
|
||||
Find(context.Context, *[]*Metadata, orm.Pager, ...orm.QueryOption) (int64, error)
|
||||
Get(context.Context, *Metadata, ...orm.QueryOption) error
|
||||
Add(context.Context, *Metadata) error
|
||||
Edit(context.Context, *Metadata, func(*Metadata), ...orm.QueryOption) error
|
||||
Del(context.Context, *Metadata, ...orm.QueryOption) error
|
||||
Count(context.Context, ...orm.QueryOption) (int64, error)
|
||||
|
||||
Session(context.Context, ...func(*gorm.DB) error) error
|
||||
EditWithSession(*gorm.DB, *Metadata, func(b *Metadata) error, ...orm.QueryOption) error
|
||||
}
|
||||
|
||||
// FindMetadatas 分页查询(保留代码,当前不提供列表接口)
|
||||
// func (c Core) FindMetadatas(ctx context.Context, in *FindMetadataInput) ([]*Metadata, int64, error) {
|
||||
// query := orm.NewQuery(1).OrderBy("created_at DESC")
|
||||
// items := make([]*Metadata, 0, in.Limit())
|
||||
// total, err := c.store.Metadata().Find(ctx, &items, in, query.Encode()...)
|
||||
// if err != nil {
|
||||
// return nil, 0, reason.ErrDB.Withf(`Find in[%+v] err[%s]`, in, err.Error())
|
||||
// }
|
||||
// return items, total, nil
|
||||
// }
|
||||
|
||||
// GetMetadata 按 ID 查询单条数据
|
||||
func (c Core) GetMetadata(ctx context.Context, id string) (*Metadata, error) {
|
||||
out := Metadata{ID: id}
|
||||
if err := c.store.Metadata().Get(ctx, &out, orm.Where("id=?", id)); err != nil {
|
||||
if orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrNotFound.Withf(`Get id[%v] err[%s]`, id, err.Error())
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Get id[%v] err[%s]`, id, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// AddMetadata 创建数据记录
|
||||
func (c Core) AddMetadata(ctx context.Context, in *AddMetadataInput) (*Metadata, error) {
|
||||
var out Metadata
|
||||
if err := copier.Copy(&out, in); err != nil {
|
||||
slog.ErrorContext(ctx, "Copy", "err", err)
|
||||
}
|
||||
|
||||
if err := c.store.Metadata().Add(ctx, &out); err != nil {
|
||||
if orm.IsDuplicatedKey(err) {
|
||||
return nil, reason.ErrBadRequest.SetMsg("数据重复").Withf("key[%s]", in.ID)
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Add err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// EditMetadata 更新数据记录
|
||||
func (c Core) EditMetadata(ctx context.Context, in *EditMetadataInput, id string) (*Metadata, error) {
|
||||
var out Metadata
|
||||
if err := c.store.Metadata().Edit(ctx, &out, func(b *Metadata) {
|
||||
if err := copier.Copy(b, in); err != nil {
|
||||
slog.ErrorContext(ctx, "Copy", "err", err)
|
||||
}
|
||||
b.LastUpdatedBy = in.LastUpdatedBy
|
||||
}, orm.Where("id=?", id)); err != nil {
|
||||
return nil, reason.ErrDB.Withf(`Edit id[%v] err[%s]`, id, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// SaveMetadata 幂等保存:先尝试更新已有记录,不存在则创建
|
||||
func (c Core) SaveMetadata(ctx context.Context, in *SaveMetadataInput, id string) (*Metadata, error) {
|
||||
var out Metadata
|
||||
err := c.store.Metadata().Edit(ctx, &out, func(b *Metadata) {
|
||||
b.Ext = in.Ext
|
||||
b.LastUpdatedBy = in.LastUpdatedBy
|
||||
}, orm.Where("id=?", id))
|
||||
if err == nil {
|
||||
return &out, nil
|
||||
}
|
||||
if !orm.IsErrRecordNotFound(err) {
|
||||
return nil, reason.ErrDB.Withf(`Edit id[%v] err[%s]`, id, err.Error())
|
||||
}
|
||||
|
||||
out = Metadata{
|
||||
ID: id,
|
||||
Ext: in.Ext,
|
||||
CreatedBy: in.CreatedBy,
|
||||
LastUpdatedBy: in.LastUpdatedBy,
|
||||
}
|
||||
if err := c.store.Metadata().Add(ctx, &out); err != nil {
|
||||
if orm.IsDuplicatedKey(err) {
|
||||
return nil, reason.ErrBadRequest.SetMsg("数据重复").Withf("key[%s]", id)
|
||||
}
|
||||
return nil, reason.ErrDB.Withf(`Add err[%s]`, err.Error())
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
// DelMetadata 删除数据(保留代码,当前不提供删除接口)
|
||||
// func (c Core) DelMetadata(ctx context.Context, id string) (*Metadata, error) {
|
||||
// var out Metadata
|
||||
// if err := c.store.Metadata().Del(ctx, &out, orm.Where("id=?", id)); err != nil {
|
||||
// return nil, reason.ErrDB.Withf(`Del id[%v] err[%s]`, id, err.Error())
|
||||
// }
|
||||
// return &out, nil
|
||||
// }
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadata
|
||||
|
||||
import "github.com/ixugo/goddd/pkg/orm"
|
||||
|
||||
// Metadata domain model
|
||||
type Metadata struct {
|
||||
ID string `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt orm.Time `gorm:"column:created_at;notNull;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
|
||||
UpdatedAt orm.Time `gorm:"column:updated_at;notNull;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
|
||||
CreatedBy string `gorm:"column:created_by;notNull;default:'';comment:创建者" json:"created_by"` // 创建者
|
||||
LastUpdatedBy string `gorm:"column:last_updated_by;notNull;default:'';comment:最后更新者" json:"last_updated_by"` // 最后更新者
|
||||
Ext string `gorm:"column:ext;type:text;notNull;default:'';comment:序列化的 JSON 字符串" json:"ext"` // 序列化的 JSON 字符串
|
||||
}
|
||||
|
||||
// TableName database table name
|
||||
func (*Metadata) TableName() string {
|
||||
return "metadatas"
|
||||
}
|
||||
|
||||
// CacheKey 缓存主键,必须唯一
|
||||
// godddx 生成缓存代码时,依赖的主键
|
||||
// 默认应该是 ID 字段,但也可以自定义
|
||||
func (m *Metadata) CacheKey() string {
|
||||
return m.ID
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadata
|
||||
|
||||
// FindMetadataInput 查询数据参数(保留未使用)
|
||||
// type FindMetadataInput struct {
|
||||
// web.PagerFilter
|
||||
// }
|
||||
|
||||
// AddMetadataInput 新增数据参数
|
||||
type AddMetadataInput struct {
|
||||
ID string `json:"id" binding:"required,max=64"` // 调用方指定的唯一标识
|
||||
Ext string `json:"ext" binding:"required,max=131072"` // 序列化的 JSON 字符串
|
||||
|
||||
CreatedBy string `json:"-"` // 创建者(API层填充)
|
||||
LastUpdatedBy string `json:"-"` // 最后更新者(API层填充)
|
||||
}
|
||||
|
||||
// EditMetadataInput 编辑数据参数
|
||||
type EditMetadataInput struct {
|
||||
Ext string `json:"ext" binding:"required,max=131072"` // 序列化的 JSON 字符串
|
||||
|
||||
LastUpdatedBy string `json:"-"` // 最后更新者(API层填充)
|
||||
}
|
||||
|
||||
// SaveMetadataInput 保存数据参数,合并创建与更新为一个幂等操作
|
||||
type SaveMetadataInput struct {
|
||||
Ext string `json:"ext" binding:"required,max=131072"` // 序列化的 JSON 字符串
|
||||
|
||||
CreatedBy string `json:"-"` // 创建者(API层填充)
|
||||
LastUpdatedBy string `json:"-"` // 最后更新者(API层填充)
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadataapi
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gowvp/owl/internal/core/metadata"
|
||||
"github.com/gowvp/owl/internal/core/metadata/store/metadatadb"
|
||||
"github.com/ixugo/goddd/pkg/orm"
|
||||
"github.com/ixugo/goddd/pkg/web"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// MetadataAPI 为 http 提供通用数据持久化的业务方法
|
||||
type MetadataAPI struct {
|
||||
metadataCore metadata.Core
|
||||
}
|
||||
|
||||
func NewMetadataCore(db *gorm.DB) metadata.Core {
|
||||
var store metadata.Storer
|
||||
store = metadatadb.NewDB(db).AutoMigrate(orm.GetEnabledAutoMigrate())
|
||||
return metadata.NewCore(store)
|
||||
}
|
||||
|
||||
func NewMetadataAPI(core metadata.Core) MetadataAPI {
|
||||
return MetadataAPI{metadataCore: core}
|
||||
}
|
||||
|
||||
// RegisterMetadata 注册通用数据持久化路由
|
||||
func RegisterMetadata(g gin.IRouter, api MetadataAPI, handler ...gin.HandlerFunc) {
|
||||
group := g.Group("/metadatas", handler...)
|
||||
group.GET("/:id", web.WrapH(api.getMetadata))
|
||||
group.POST("/:id", web.WrapH(api.saveMetadata))
|
||||
}
|
||||
|
||||
// >>> metadata >>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
// findMetadatas 分页查询(保留代码,当前不提供)
|
||||
// func (a MetadataAPI) findMetadatas(c *gin.Context, in *metadata.FindMetadataInput) (any, error) {
|
||||
// items, total, err := a.metadataCore.FindMetadatas(c.Request.Context(), in)
|
||||
// return gin.H{"items": items, "total": total}, err
|
||||
// }
|
||||
|
||||
// getMetadata 按 ID 查询数据
|
||||
func (a MetadataAPI) getMetadata(c *gin.Context, _ *struct{}) (*metadata.Metadata, error) {
|
||||
metadataID := c.Param("id")
|
||||
return a.metadataCore.GetMetadata(c.Request.Context(), metadataID)
|
||||
}
|
||||
|
||||
// saveMetadata 幂等保存:已存在则更新,不存在则创建
|
||||
func (a MetadataAPI) saveMetadata(c *gin.Context, in *metadata.SaveMetadataInput) (*metadata.Metadata, error) {
|
||||
metadataID := c.Param("id")
|
||||
in.CreatedBy = web.GetUsername(c)
|
||||
in.LastUpdatedBy = in.CreatedBy
|
||||
return a.metadataCore.SaveMetadata(c.Request.Context(), in, metadataID)
|
||||
}
|
||||
|
||||
// delMetadata 删除数据(保留代码,当前不提供)
|
||||
// func (a MetadataAPI) delMetadata(c *gin.Context, _ *struct{}) (*metadata.Metadata, error) {
|
||||
// metadataID := c.Param("id")
|
||||
// return a.metadataCore.DelMetadata(c.Request.Context(), metadataID)
|
||||
// }
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadata
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadatadb
|
||||
|
||||
import (
|
||||
"github.com/gowvp/owl/internal/core/metadata"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ metadata.Storer = DB{}
|
||||
|
||||
// DB Related business namespaces
|
||||
type DB struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewDB instance object
|
||||
func NewDB(db *gorm.DB) DB {
|
||||
return DB{db: db}
|
||||
}
|
||||
|
||||
// Metadata Get business instance
|
||||
func (d DB) Metadata() metadata.MetadataStorer {
|
||||
return Metadata(d)
|
||||
}
|
||||
|
||||
// AutoMigrate sync database
|
||||
func (d DB) AutoMigrate(ok bool) DB {
|
||||
if !ok {
|
||||
return d
|
||||
}
|
||||
if err := d.db.AutoMigrate(
|
||||
new(metadata.Metadata),
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// Code generated by godddx, DO AVOID EDIT.
|
||||
package metadatadb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gowvp/owl/internal/core/metadata"
|
||||
"github.com/ixugo/goddd/pkg/orm"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _ metadata.MetadataStorer = Metadata{}
|
||||
|
||||
// Metadata Related business namespaces
|
||||
type Metadata DB
|
||||
|
||||
// NewMetadata instance object
|
||||
func NewMetadata(db *gorm.DB) Metadata {
|
||||
return Metadata{db: db}
|
||||
}
|
||||
|
||||
// Find implements metadata.MetadataStorer.
|
||||
func (d Metadata) Find(ctx context.Context, bs *[]*metadata.Metadata, page orm.Pager, opts ...orm.QueryOption) (int64, error) {
|
||||
return orm.FindWithContext(ctx, d.db, bs, page, opts...)
|
||||
}
|
||||
|
||||
// Get implements metadata.MetadataStorer.
|
||||
func (d Metadata) Get(ctx context.Context, model *metadata.Metadata, opts ...orm.QueryOption) error {
|
||||
return orm.FirstWithContext(ctx, d.db, model, opts...)
|
||||
}
|
||||
|
||||
// Add implements metadata.MetadataStorer.
|
||||
func (d Metadata) Add(ctx context.Context, model *metadata.Metadata) error {
|
||||
return d.db.WithContext(ctx).Create(model).Error
|
||||
}
|
||||
|
||||
// Edit implements metadata.MetadataStorer.
|
||||
func (d Metadata) Edit(ctx context.Context, model *metadata.Metadata, changeFn func(*metadata.Metadata), opts ...orm.QueryOption) error {
|
||||
return orm.UpdateWithContext(ctx, d.db, model, changeFn, opts...)
|
||||
}
|
||||
|
||||
// Del implements metadata.MetadataStorer.
|
||||
func (d Metadata) Del(ctx context.Context, model *metadata.Metadata, opts ...orm.QueryOption) error {
|
||||
return orm.DeleteWithContext(ctx, d.db, model, opts...)
|
||||
}
|
||||
|
||||
// Count implements metadata.MetadataStorer.
|
||||
func (d Metadata) Count(ctx context.Context, opts ...orm.QueryOption) (int64, error) {
|
||||
return orm.CountWithContext[metadata.Metadata](ctx, d.db, opts...)
|
||||
}
|
||||
|
||||
// Session 事务组合
|
||||
func (d Metadata) Session(ctx context.Context, changeFns ...func(*gorm.DB) error) error {
|
||||
return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for _, fn := range changeFns {
|
||||
if err := fn(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// EditWithSession 修改事务
|
||||
func (d Metadata) EditWithSession(tx *gorm.DB, model *metadata.Metadata, changeFn func(b *metadata.Metadata) error, opts ...orm.QueryOption) error {
|
||||
return orm.UpdateWithSession(tx, model, changeFn, opts...)
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-contrib/gzip"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gowvp/owl/internal/core/metadata/metadataapi"
|
||||
"github.com/gowvp/owl/internal/core/sms"
|
||||
"github.com/gowvp/owl/pkg/ota"
|
||||
"github.com/gowvp/owl/plugin/stat"
|
||||
@@ -122,6 +123,7 @@ func setupRouter(r *gin.Engine, uc *Usecase) {
|
||||
uc.AIWebhookAPI.StartAISyncLoop(context.Background(), uc.SMSAPI.smsCore)
|
||||
RegisterEvent(r, uc.EventAPI, auth)
|
||||
RegisterRecording(r, uc.RecordingAPI, auth)
|
||||
metadataapi.RegisterMetadata(r, uc.MetadataAPI, auth)
|
||||
}
|
||||
|
||||
type playOutput struct {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/gowvp/owl/internal/core/ipc"
|
||||
"github.com/gowvp/owl/internal/core/ipc/store/ipccache"
|
||||
"github.com/gowvp/owl/internal/core/ipc/store/ipcdb"
|
||||
"github.com/gowvp/owl/internal/core/metadata/metadataapi"
|
||||
"github.com/gowvp/owl/internal/core/recording"
|
||||
"github.com/gowvp/owl/internal/core/recording/adapter"
|
||||
"github.com/gowvp/owl/internal/core/sms"
|
||||
@@ -49,6 +50,7 @@ var (
|
||||
NewEventCore, NewEventAPI,
|
||||
// Recording: Store -> SMSProvider(adapter) -> Core -> API
|
||||
NewRecordingStore, NewSMSProviderAdapter, NewRecordingCore, NewRecordingAPI,
|
||||
metadataapi.NewMetadataCore, metadataapi.NewMetadataAPI,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -69,6 +71,7 @@ type Usecase struct {
|
||||
EventAPI EventAPI
|
||||
|
||||
RecordingAPI RecordingAPI
|
||||
MetadataAPI metadataapi.MetadataAPI
|
||||
}
|
||||
|
||||
// NewHTTPHandler 生成Gin框架路由内容
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/gowvp/owl/internal/app"
|
||||
"github.com/gowvp/owl/internal/conf"
|
||||
"github.com/ixugo/goddd/domain/version/versionapi"
|
||||
"github.com/ixugo/goddd/pkg/system"
|
||||
)
|
||||
|
||||
@@ -57,6 +58,10 @@ func main() {
|
||||
}))
|
||||
}
|
||||
|
||||
// 如果需要执行表迁移,递增此版本号和表更新说明
|
||||
versionapi.DBVersion = "0.0.25"
|
||||
versionapi.DBRemark = "onvif device support"
|
||||
|
||||
app.Run(&bc)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user