feat: 升级 samber/do v1 → v2
This commit is contained in:
+8
-1
@@ -1,6 +1,6 @@
|
||||
package contracts
|
||||
|
||||
type Service interface {
|
||||
type App interface {
|
||||
LoadGoPlugin(file string) error
|
||||
RegControllers(items map[string]Controller) error
|
||||
RegListens(items map[string]ResListener) error
|
||||
@@ -8,10 +8,17 @@ type Service interface {
|
||||
RegJobs(items map[string]Job) error
|
||||
RegCrons(items map[string]Cron) error
|
||||
RegMqtts(items map[string]Mqtt) error
|
||||
RegFlowDecisions(items map[string]FlowDecision) error
|
||||
RegGoBridges(items map[string]GoBridge) error
|
||||
RegResVirtualTables(items map[string]ResVirtualTable) error
|
||||
|
||||
GetResListener(code string) (ResListener, bool)
|
||||
GetAppController(code string) (Controller, bool)
|
||||
GetAppGRpc(code string) (GRpc, bool)
|
||||
GetAppJob(code string) (Job, bool)
|
||||
GetAppCron(code string) (Cron, bool)
|
||||
GetAppMqtt(code string) (Mqtt, bool)
|
||||
GetAppFlowDecision(code string) (FlowDecision, bool)
|
||||
GetAppGoBridge(code string) (GoBridge, bool)
|
||||
GetAppResVirtualTable(code string) (ResVirtualTable, bool)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package base
|
||||
|
||||
// DataType 定义参数数据类型
|
||||
type DataType string
|
||||
|
||||
const (
|
||||
DataTypeString DataType = "string" // 字符串类型
|
||||
DataTypeInteger DataType = "integer" // 整数类型
|
||||
DataTypeFloat DataType = "float" // 浮点数类型
|
||||
DataTypeBoolean DataType = "boolean" // 布尔类型
|
||||
DataTypeJSON DataType = "json" // JSON类型
|
||||
)
|
||||
|
||||
// SchemaParam 工作流参数
|
||||
type SchemaParam struct {
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
DataType DataType `db:"data_type" json:"data_type"`
|
||||
Required bool `db:"required" json:"required"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
Fields SchemaParams `db:"fields" json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
type SchemaParams []SchemaParam
|
||||
|
||||
// Condflow 工作流
|
||||
type Condflow struct {
|
||||
Uuid string `db:"uuid" json:"uuid"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
Params SchemaParams `db:"params" json:"params"`
|
||||
CreatedAt string `db:"created_at" json:"created_at"`
|
||||
UpdatedAt string `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type GetCondflow func(code string) (Condflow, bool)
|
||||
@@ -0,0 +1,23 @@
|
||||
package base
|
||||
|
||||
import "git.fsdpf.net/go/reflux/fieldx"
|
||||
|
||||
// CondflowCase 工作流条件分支
|
||||
type CondflowCase struct {
|
||||
Uuid string `db:"uuid" json:"uuid"`
|
||||
CondflowUuid string `db:"condflow_uuid" json:"condflow_uuid"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Priority int `db:"priority" json:"priority"`
|
||||
Action string `db:"action" json:"action"`
|
||||
ActionParams fieldx.Schema `db:"action_params" json:"action_params"` // 动作参数值, 对应 params_schema
|
||||
ActionConfig fieldx.Schema `db:"action_config" json:"action_config"` // 动作配置值, 对应 config_schema
|
||||
NextCaseOnSuccess bool `db:"next_case_on_success" json:"next_case_on_success"`
|
||||
NextFlowUuid string `db:"next_flow_uuid" json:"next_flow_uuid"`
|
||||
NextFlowParams fieldx.Schema `db:"next_flow_params" json:"next_flow_params"` // 流程间参数映射
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
CreatedAt string `db:"created_at" json:"created_at"`
|
||||
UpdatedAt string `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type GetCondflowCases func(uuid string) []CondflowCase
|
||||
@@ -0,0 +1,25 @@
|
||||
package base
|
||||
|
||||
// CondflowDecisionAction 执行器动作定义
|
||||
type CondflowDecisionAction struct {
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
ConfigSchema SchemaParams `db:"config_schema" json:"config_schema"` // 配置项Schema定义
|
||||
ParamsSchema SchemaParams `db:"params_schema" json:"params_schema"` // 参数Schema定义
|
||||
ResourceUuid string `db:"resource_uuid" json:"resource_uuid"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
}
|
||||
|
||||
// CondflowDecision 执行器注册表
|
||||
type CondflowDecision struct {
|
||||
Uuid string `db:"uuid" json:"uuid"`
|
||||
ResourceUuid string `db:"resource_uuid" json:"resource_uuid"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
Actions []CondflowDecisionAction `db:"actions" json:"actions"`
|
||||
CreatedAt string `db:"created_at" json:"created_at"`
|
||||
UpdatedAt string `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type GetCondflowDecision func(code string) (CondflowDecision, bool)
|
||||
@@ -51,6 +51,7 @@ type DataList struct {
|
||||
ThemeConfig map[string]any `db:"themeConfig"` // 列表布局配置
|
||||
Roles []string `db:"roles"` // 列表权限
|
||||
IsAuthDB int `db:"is_auth_db"` // 启用权限过滤
|
||||
SelectionType string `db:"selectionType"` // 行选择类型
|
||||
Platform string `db:"platform"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
|
||||
@@ -25,6 +25,7 @@ type DataListField struct {
|
||||
FilterByJsSetting map[string]any `db:"filterByJsSetting"` // 字段筛选组件设置参数
|
||||
FilterOperator string `db:"filterOperator"` // 字段筛选符号
|
||||
EditableRoles []string `db:"editableRoles"` // 字段编辑权限
|
||||
Middlewares [][2]any `db:"middlewares"` // 字段数据转换
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
+19
-16
@@ -1,22 +1,25 @@
|
||||
package base
|
||||
|
||||
type GridLayout struct {
|
||||
Uuid string `db:"uuid"`
|
||||
ResourceUuid string `db:"resource_uuid"` // 所属资源UUID
|
||||
Code string `db:"code"` // 布局标识
|
||||
Name string `db:"name"` // 布局名称
|
||||
Type int `db:"type"` // 布局类型 [0=普通布局, 1=辅助布局]
|
||||
PrimaryKey string `db:"primaryKey"` // 主键
|
||||
MarginX int `db:"marginX"` // 栅格 x 间距
|
||||
MarginY int `db:"marginY"` // 栅格 y 间距
|
||||
Cols int `db:"cols"` // 网格列基数
|
||||
RowHeight float32 `db:"rowHeight"` // 栅格行高
|
||||
Css map[string]any `db:"css"` // 布局CSS
|
||||
Props []any `db:"props"` // 布局PROPS
|
||||
Platform string `db:"platform"`
|
||||
Roles []string `db:"roles"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
Uuid string `db:"uuid"`
|
||||
ResourceUuid string `db:"resource_uuid"` // 所属资源UUID
|
||||
Code string `db:"code"` // 布局标识
|
||||
Name string `db:"name"` // 布局名称
|
||||
Type int `db:"type"` // 布局类型 [0=普通布局, 1=辅助布局]
|
||||
PrimaryKey string `db:"primaryKey"` // 主键
|
||||
MarginX int `db:"marginX"` // 栅格 x 间距
|
||||
MarginY int `db:"marginY"` // 栅格 y 间距
|
||||
Cols int `db:"cols"` // 网格列基数
|
||||
RowHeight float32 `db:"rowHeight"` // 栅格行高
|
||||
Css map[string]any `db:"css"` // 布局CSS
|
||||
Props []any `db:"props"` // 布局PROPS
|
||||
Groups []map[string]any `db:"groups"` // 布局分组
|
||||
Theme string `db:"theme"` // 布局样式
|
||||
ThemeConfig map[string]any `db:"themeConfig"` // 布局样式配置
|
||||
Platform string `db:"platform"`
|
||||
Roles []string `db:"roles"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
type GetGridLayout func(code string) (GridLayout, bool)
|
||||
|
||||
@@ -3,6 +3,7 @@ package base
|
||||
type GridLayoutField struct {
|
||||
Uuid string `db:"uuid"`
|
||||
GridLayoutUuid string `db:"grid_layout_uuid"` // 所属布局 UUID
|
||||
Group string `db:"group"`
|
||||
Type string `db:"type"`
|
||||
X int `db:"x"`
|
||||
Y int `db:"y"`
|
||||
|
||||
@@ -14,7 +14,9 @@ type GridLayoutForm struct {
|
||||
MarginY int `db:"marginY"` // 栅格 y 间距
|
||||
Cols int `db:"cols"` // 网格列基数
|
||||
FormProps []map[string]string `db:"formProps"` // 表单PROPS
|
||||
Groups []map[string]any `db:"groups"` // 表单分组
|
||||
Groups []map[string]any `db:"groups"` // 布局分组
|
||||
Theme string `db:"theme"` // 布局样式
|
||||
ThemeConfig map[string]any `db:"themeConfig"` // 布局样式配置
|
||||
ListenChangeFields []string `db:"listenChangeFields"` // 监听字段
|
||||
ListenChangeFieldsFunc string `db:"listenChangeFieldsFunc"` // 监听字段回调方法
|
||||
Platform string `db:"platform"`
|
||||
|
||||
+15
-9
@@ -6,7 +6,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"git.fsdpf.net/go/contracts"
|
||||
"github.com/samber/do"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type PkgVersion struct {
|
||||
@@ -39,7 +41,7 @@ func (this PkgVersion) GetSourceMapPath(prefix ...string) string {
|
||||
)
|
||||
}
|
||||
|
||||
func (this PkgVersion) GetFile(app *do.Injector) (file string, err error) {
|
||||
func (this PkgVersion) GetFile(app do.Injector, u req.User) (file string, err error) {
|
||||
if this.Id == 0 {
|
||||
return file, contracts.ErrPkgUnknown
|
||||
}
|
||||
@@ -50,23 +52,27 @@ func (this PkgVersion) GetFile(app *do.Injector) (file string, err error) {
|
||||
return file, contracts.ErrResNotFound
|
||||
}
|
||||
|
||||
_, err = res.GetDBTable().Where("id", this.Id).Value(&file, "file")
|
||||
ok, err = res.GetDBTable(u).Where(db.C("id").Eq(this.Id)).Select("file").ScanVal(&file)
|
||||
|
||||
if !ok {
|
||||
return file, contracts.ErrResNotFound
|
||||
}
|
||||
|
||||
return file, err
|
||||
}
|
||||
|
||||
func (this PkgVersion) GetSourceMap(app *do.Injector) (file string, err error) {
|
||||
func (this PkgVersion) GetSourceMap(app do.Injector) (file string, err error) {
|
||||
if this.Id == 0 {
|
||||
return file, contracts.ErrPkgUnknown
|
||||
}
|
||||
|
||||
res, ok := do.MustInvoke[contracts.GetResource](app)("PkgVersion")
|
||||
// res, ok := do.MustInvoke[contracts.GetResource](app)("PkgVersion")
|
||||
|
||||
if !ok {
|
||||
return file, contracts.ErrResNotFound
|
||||
}
|
||||
// if !ok {
|
||||
// return file, contracts.ErrResNotFound
|
||||
// }
|
||||
|
||||
_, err = res.GetDBTable().Where("id", this.Id).Value(&file, "sourcemap")
|
||||
// _, err = res.GetDBTable().Where("id", this.Id).Value(&file, "sourcemap")
|
||||
|
||||
return file, err
|
||||
}
|
||||
|
||||
+7
-8
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"git.fsdpf.net/go/contracts/res_type"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/req"
|
||||
)
|
||||
@@ -67,13 +66,13 @@ func (this *QueryField) ToSql() db.Expression {
|
||||
if this.Alias() != "" {
|
||||
if this.IsExpr() {
|
||||
if this.GetCode() == "" {
|
||||
return db.Raw("null as `" + this.Alias() + "`")
|
||||
return db.V(nil).As(this.Alias())
|
||||
}
|
||||
return db.Raw("(" + this.GetCode() + ") as `" + this.Alias() + "`")
|
||||
return db.L(this.GetCode()).As(this.Alias())
|
||||
}
|
||||
return db.Raw("`" + this.GetCodeResource() + "`.`" + this.GetCode() + "` as `" + this.Alias() + "`")
|
||||
return db.T(this.GetCodeResource()).Col(this.GetCode()).As(this.Alias())
|
||||
}
|
||||
return db.Raw("`" + this.GetCodeResource() + "`.`" + this.GetCode() + "`")
|
||||
return db.T(this.GetCodeResource()).Col(this.GetCode())
|
||||
}
|
||||
|
||||
func (this *QueryField) ToStructField(tags ...string) reflect.StructField {
|
||||
@@ -104,11 +103,11 @@ func (this *QueryField) ToStructField(tags ...string) reflect.StructField {
|
||||
case req.ReqString:
|
||||
typ = reflect.TypeOf(string(""))
|
||||
case req.ReqNumber:
|
||||
typ = reflect.TypeOf(res_type.ResFieldByNumber(0))
|
||||
typ = reflect.TypeOf(float64(0))
|
||||
case req.ReqInteger:
|
||||
typ = reflect.TypeOf(res_type.ResFieldByInteger(0))
|
||||
typ = reflect.TypeOf(int64(0))
|
||||
case req.ReqFloat:
|
||||
typ = reflect.TypeOf(res_type.ResFieldByFloat(0))
|
||||
typ = reflect.TypeOf(float64(0))
|
||||
case req.ReqBool:
|
||||
typ = reflect.TypeOf(true)
|
||||
case req.ReqArray:
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
package base
|
||||
|
||||
type ResApiParam struct {
|
||||
Code string `db:"code"`
|
||||
Name string `db:"name"`
|
||||
Category string `db:"category"`
|
||||
DataType string `db:"type"`
|
||||
IsRequired bool `db:"isRequired"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Category string `db:"category" json:"category"`
|
||||
DataType string `db:"type" json:"type"`
|
||||
IsRequired bool `db:"isRequired" json:"isRequired"`
|
||||
}
|
||||
|
||||
type ResApi struct {
|
||||
@@ -16,7 +16,7 @@ type ResApi struct {
|
||||
PrimaryKey string `db:"primaryKey"`
|
||||
ResourceUuid string `db:"resource_uuid"`
|
||||
Method string `db:"method"`
|
||||
Category string `db:"action"`
|
||||
Action string `db:"action"`
|
||||
Params []ResApiParam `db:"params"`
|
||||
Roles []string `db:"roles"`
|
||||
IsAuthDB int `db:"is_auth_db"` // 启用权限过滤
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package base
|
||||
|
||||
import "git.fsdpf.net/go/condition"
|
||||
import (
|
||||
"git.fsdpf.net/go/condition"
|
||||
)
|
||||
|
||||
type ResCondition struct {
|
||||
Id int `db:"id"`
|
||||
@@ -53,7 +55,7 @@ func NewConditionByRes(items []ResCondition, opts ...condition.Option) (root *co
|
||||
conditions[item.Id] = condition.NewExpr(
|
||||
item.ColumnResource, item.Column,
|
||||
condition.Token(item.Value, condition.TokenType(item.ValueType)),
|
||||
condition.Operator(condition.ConditionOperator(item.Operator)),
|
||||
condition.Operator(condition.ConditionOperator(condition.GetDBOperator(item.Operator))),
|
||||
condition.FieldSqlFn(item.ColumnSqlFunc, item.ColumnSqlFuncParam),
|
||||
condition.IgnoreEmptyParma(item.IgnoreEmptyParma),
|
||||
)
|
||||
|
||||
+43
-11
@@ -1,16 +1,48 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/reflux"
|
||||
"git.fsdpf.net/go/reflux/valuex"
|
||||
)
|
||||
|
||||
// role_uuid => value
|
||||
type ResRoleValue map[string]string
|
||||
|
||||
type ResConfigure struct {
|
||||
Uuid string `db:"uuid"`
|
||||
ResourceUuid string `db:"resource_uuid"`
|
||||
Key string `db:"key"`
|
||||
Value string `db:"value"`
|
||||
Label string `db:"label"`
|
||||
Type string `db:"type"`
|
||||
IsPrivate bool `db:"isPrivate"`
|
||||
Desc string `db:"desc"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
Uuid string `db:"uuid"`
|
||||
ResourceUuid string `db:"resource_uuid"`
|
||||
Key string `db:"key"`
|
||||
Value string `db:"value"`
|
||||
Label string `db:"label"`
|
||||
Type string `db:"type"`
|
||||
IsPrivate bool `db:"isPrivate"`
|
||||
Desc string `db:"desc"`
|
||||
RoleValues []ResRoleValue `db:"role_values"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
type GetResConfigures func(uuid string) []ResConfigure
|
||||
// GetValueByRoles 根据角色优先级获取配置值
|
||||
//
|
||||
// - roles: 角色UUIDs
|
||||
//
|
||||
// 返回: valuex.Accessor 对象和是否找到角色特定值的标志
|
||||
func (rc *ResConfigure) GetValueByRoles(roles ...string) (valuex.Accessor, bool) {
|
||||
// 如果没有角色值配置或没有传入角色,返回默认值
|
||||
if len(rc.RoleValues) == 0 || len(roles) == 0 {
|
||||
return reflux.New(rc.Value), false
|
||||
}
|
||||
|
||||
for _, rv := range rc.RoleValues {
|
||||
for _, role := range roles {
|
||||
if value, exists := rv[role]; exists {
|
||||
return reflux.New(value), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 没有找到匹配的角色值,返回默认值
|
||||
return reflux.New(rc.Value), false
|
||||
}
|
||||
|
||||
type GetResConfigure func(key string, roles ...string) (valuex.Accessor, bool)
|
||||
|
||||
+62
-54
@@ -7,24 +7,24 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.fsdpf.net/go/contracts/res_type"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/schema"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// 资源字段
|
||||
// ResField 资源字段
|
||||
type ResField struct {
|
||||
Uuid string `db:"uuid"`
|
||||
Name string `db:"name"`
|
||||
Code string `db:"code"`
|
||||
CodeResource string `db:"codeResource"`
|
||||
DataType req.ResDataType `db:"table_type"`
|
||||
Length string `db:"length"`
|
||||
Comment string `db:"comment"`
|
||||
Default string `db:"default"`
|
||||
Uuid string `db:"uuid" json:"uuid"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Code string `db:"code" json:"code"`
|
||||
CodeResource string `db:"codeResource" json:"codeResource"`
|
||||
DataType req.ResDataType `db:"data_type" json:"data_type"`
|
||||
Length string `db:"length" json:"length"`
|
||||
Comment string `db:"comment" json:"comment"`
|
||||
Default string `db:"default" json:"default"`
|
||||
Virtual bool `db:"virtual" json:"virtual"`
|
||||
VirtualExpr string `db:"virtual_expr" json:"virtual_expr"`
|
||||
}
|
||||
|
||||
func (this ResField) ToStructField(tags ...string) reflect.StructField {
|
||||
@@ -42,7 +42,7 @@ func (this ResField) ToStructField(tags ...string) reflect.StructField {
|
||||
req.ResTimestamp, req.ResDate, req.ResDatetime:
|
||||
typ = reflect.TypeOf(string(""))
|
||||
case req.ResInteger, req.ResSmallInteger:
|
||||
typ = reflect.TypeOf(res_type.ResFieldByInteger(0))
|
||||
typ = reflect.TypeOf(int64(0))
|
||||
case req.ResDecimal:
|
||||
typ = reflect.TypeOf(float64(0))
|
||||
case req.ResBoolean:
|
||||
@@ -62,6 +62,10 @@ func (this ResField) ToStructField(tags ...string) reflect.StructField {
|
||||
}
|
||||
}
|
||||
|
||||
func (this ResField) IsVirtual() bool {
|
||||
return this.Virtual
|
||||
}
|
||||
|
||||
func (this ResField) GetCode() string {
|
||||
return this.Code
|
||||
}
|
||||
@@ -101,30 +105,16 @@ func (this ResField) GetQueryDataType() req.RouteParamType {
|
||||
}
|
||||
|
||||
func (this ResField) ToValue(v any) any {
|
||||
switch this.DataType {
|
||||
case req.ResString, req.ResText, req.ResEnum,
|
||||
req.ResTimestamp, req.ResDate, req.ResDatetime:
|
||||
return strings.Trim(cast.ToString(v), " ")
|
||||
case req.ResInteger, req.ResSmallInteger:
|
||||
return cast.ToInt(v)
|
||||
case req.ResDecimal:
|
||||
return strings.Trim(cast.ToString(v), " ")
|
||||
case req.ResBoolean:
|
||||
if v, _ := strconv.ParseBool(fmt.Sprintf("%v", v)); v {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
case req.ResJson:
|
||||
if this.DataType == req.ResJson {
|
||||
if v == nil {
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
return db.Raw("'[]'")
|
||||
return db.V("[]")
|
||||
} else if this.Default != "" && this.Default[0:1] == "{" {
|
||||
return db.Raw("'{}'")
|
||||
return db.V("{}")
|
||||
} else if this.Default == "" {
|
||||
return db.Raw("'{}'")
|
||||
return db.V("{}")
|
||||
}
|
||||
|
||||
return this.Default
|
||||
return this.GetRawDefault()
|
||||
}
|
||||
|
||||
if str, ok := v.(string); ok {
|
||||
@@ -137,52 +127,61 @@ func (this ResField) ToValue(v any) any {
|
||||
panic(fmt.Sprintf("%s, 类型转换错误, %s", this.Code, err))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Trim(cast.ToString(v), " ")
|
||||
return v
|
||||
}
|
||||
|
||||
func (this ResField) GetRawDefault(driver string) db.Expression {
|
||||
func (this ResField) GetRawDefault() db.Expression {
|
||||
if this.DataType == req.ResJson {
|
||||
if this.Default != "" && this.Default[0:1] == "[" {
|
||||
return db.Raw("'[]'")
|
||||
return db.V("[]")
|
||||
} else if this.Default != "" && this.Default[0:1] == "{" {
|
||||
return db.Raw("'{}'")
|
||||
return db.V("{}")
|
||||
} else if this.Default == "" {
|
||||
return db.Raw("'{}'")
|
||||
return db.V("{}")
|
||||
}
|
||||
} else if this.DataType == req.ResBoolean {
|
||||
if v, _ := strconv.ParseBool(this.Default); v {
|
||||
return db.Raw("'1'")
|
||||
return db.V(true)
|
||||
}
|
||||
return db.Raw("'0'")
|
||||
return db.V(false)
|
||||
}
|
||||
|
||||
if len(this.Default) > 4 && strings.ToLower(this.Default[0:4]) == "sql:" {
|
||||
sql := strings.ToLower(this.Default[4:])
|
||||
if sql == "uuid()" {
|
||||
if driver == "sqlite" {
|
||||
return db.Raw("'" + uuid.NewString() + "'")
|
||||
}
|
||||
return db.Raw("uuid()")
|
||||
}
|
||||
return db.Raw(sql)
|
||||
return db.L(this.Default[4:])
|
||||
}
|
||||
|
||||
if this.Default == "" {
|
||||
if this.GetDataType() == req.ResDate || this.GetDataType() == req.ResDatetime {
|
||||
return db.Raw("NULL")
|
||||
return db.V(nil)
|
||||
}
|
||||
return db.Raw("''")
|
||||
return db.V("")
|
||||
}
|
||||
|
||||
if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" {
|
||||
return db.Raw(this.Default)
|
||||
return db.L(this.Default)
|
||||
}
|
||||
|
||||
return db.Raw("'" + this.Default + "'")
|
||||
return db.V(this.Default)
|
||||
}
|
||||
|
||||
func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDefinition) {
|
||||
switch this.Code {
|
||||
case "id":
|
||||
return table.BigIncrements("id").AutoIncrement().Comment("ID")
|
||||
case "enabled":
|
||||
return table.Boolean("enabled").Default("1").Comment("是否有效")
|
||||
case "created_user":
|
||||
return table.Char("created_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("创建者")
|
||||
case "owned_user":
|
||||
return table.Char("owned_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("拥有者")
|
||||
case "created_at":
|
||||
return table.Timestamp("created_at").UseCurrent().Comment("创建时间")
|
||||
case "updated_at":
|
||||
return table.Timestamp("updated_at").UseCurrent().Default(db.L("ON UPDATE CURRENT_TIMESTAMP")).Comment("更新时间")
|
||||
case "deleted_at":
|
||||
return table.DateTime("deleted_at").Nullable().Comment("删除时间")
|
||||
}
|
||||
|
||||
isNull := false
|
||||
comment := this.Name
|
||||
def := any(this.Default)
|
||||
@@ -207,15 +206,15 @@ func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDe
|
||||
case "integer":
|
||||
// integer 默认长度 11
|
||||
temp = table.Integer(this.Code)
|
||||
case "date", "dateTime":
|
||||
case "date", "dateTime", "timestamp":
|
||||
if this.DataType == "date" {
|
||||
temp = table.Date(this.Code)
|
||||
} else {
|
||||
temp = table.DateTime(this.Code)
|
||||
}
|
||||
|
||||
if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" {
|
||||
def = db.Raw(this.Default)
|
||||
if strings.ToUpper(this.Default) == "SQL:CURRENT_TIMESTAMP" {
|
||||
def = db.L("CURRENT_TIMESTAMP")
|
||||
} else if def == "" {
|
||||
isNull = true
|
||||
}
|
||||
@@ -242,6 +241,15 @@ func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDe
|
||||
case "text":
|
||||
temp = table.Text(this.Code)
|
||||
isNull = true
|
||||
case "vector":
|
||||
temp = table.Vector(this.Code, cast.ToInt(this.Length))
|
||||
isNull = false
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown type: %s", this.DataType))
|
||||
}
|
||||
|
||||
if this.IsVirtual() {
|
||||
temp.VirtualAs(this.VirtualExpr)
|
||||
}
|
||||
|
||||
if isNull {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.fsdpf.net/go/db/schema"
|
||||
"git.fsdpf.net/go/req"
|
||||
|
||||
_ "git.fsdpf.net/go/db/schema/dialect/mysql"
|
||||
_ "git.fsdpf.net/go/db/schema/dialect/sqlite3"
|
||||
)
|
||||
|
||||
func TestResFieldToBlueprint(t *testing.T) {
|
||||
t.Run("id", func(t *testing.T) {
|
||||
table := schema.NewBlueprint("users")
|
||||
|
||||
id := ResField{
|
||||
Uuid: "00000000-0000-0000-0000-000000000000",
|
||||
Name: "ID",
|
||||
Code: "id",
|
||||
CodeResource: "User",
|
||||
DataType: req.ResInteger,
|
||||
Length: "20",
|
||||
Comment: "",
|
||||
Default: "sql:AUTO_INCREMENT",
|
||||
}
|
||||
|
||||
id.ToBlueprint(table)
|
||||
})
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResFields []ResField
|
||||
|
||||
func (c *ResFields) Scan(value any) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(value.([]byte), c); err != nil {
|
||||
return fmt.Errorf("ResFields json.Unmarshal Error, %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ResFields) Value() (driver.Value, error) {
|
||||
b, err := json.Marshal(c)
|
||||
return string(b), err
|
||||
}
|
||||
+222
-249
@@ -5,21 +5,26 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"git.fsdpf.net/go/condition"
|
||||
"git.fsdpf.net/go/contracts"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/engine"
|
||||
"git.fsdpf.net/go/db/exec"
|
||||
"git.fsdpf.net/go/db/exp"
|
||||
"git.fsdpf.net/go/db/schema"
|
||||
"git.fsdpf.net/go/req"
|
||||
)
|
||||
|
||||
// 资源变更事件
|
||||
// ResChangeEventTopic 资源变更事件
|
||||
const ResChangeEventTopic = "res-change-event-topic"
|
||||
|
||||
// 资源变更数据
|
||||
// ResChangeRecordTopic 资源变更数据
|
||||
const ResChangeRecordTopic = "res-change-record-topic"
|
||||
|
||||
type ResChangeEventTopicPayload struct {
|
||||
@@ -37,61 +42,72 @@ type ResChangeRecordTopicPayload struct {
|
||||
New []map[string]any // 新数据
|
||||
}
|
||||
|
||||
// 资源
|
||||
// ResVirtualTableSetup 由 framework-v2 在 sqlite_vtable build tag 下注册到容器。
|
||||
// resource.DB() 通过 do.Invoke 取出后调用,完成 vtab 模块注册和 CREATE VIRTUAL TABLE。
|
||||
// res 提供 Code / Table / Fields 信息,vt 是 ResVirtualTable 的具体实现。
|
||||
type ResVirtualTableSetup func(res Resource) error
|
||||
|
||||
// Resource 资源
|
||||
type Resource struct {
|
||||
container *do.Injector
|
||||
container do.Injector
|
||||
initOnce *sync.Once
|
||||
|
||||
Uuid string `db:"uuid"`
|
||||
PUuid string `db:"pUuid"`
|
||||
Code string `db:"code"`
|
||||
Name string `db:"name"`
|
||||
IsResSystem bool `db:"isSystem"`
|
||||
IsResVirtual bool `db:"isVirtual"`
|
||||
Table string `db:"table"`
|
||||
Primarykey string `db:"primaryKey"`
|
||||
IsHistoryRecord bool `db:"isHistoryRecord"`
|
||||
HistoryCacheMax int `db:"historyCacheMax"`
|
||||
Fields ResFields `db:"fields"`
|
||||
Roles map[string]any `db:"roles"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
Uuid string `db:"uuid"`
|
||||
PUuid string `db:"pUuid"`
|
||||
Code string `db:"code"`
|
||||
Name string `db:"name"`
|
||||
Conn string `db:"conn"`
|
||||
IsResVirtual bool `db:"isVirtual"`
|
||||
Table string `db:"table"`
|
||||
Primarykey string `db:"primaryKey"`
|
||||
HistoryRoles []string `db:"historyRoles"`
|
||||
Fields []ResField `db:"fields"`
|
||||
Roles map[string]any `db:"roles"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
func (this *Resource) InitContainer(container *do.Injector) {
|
||||
func (this *Resource) InitContainer(container do.Injector) {
|
||||
this.container = container
|
||||
this.initOnce = &sync.Once{}
|
||||
}
|
||||
|
||||
// 资源UUID
|
||||
// GetUuid 资源UUID
|
||||
func (this Resource) GetUuid() string {
|
||||
return this.Uuid
|
||||
}
|
||||
|
||||
// 资源CODE
|
||||
// GetCode 资源CODE
|
||||
func (this Resource) GetCode() string {
|
||||
return this.Code
|
||||
}
|
||||
|
||||
// 资源名
|
||||
// GetName 资源名
|
||||
func (this Resource) GetName() string {
|
||||
return this.Name
|
||||
}
|
||||
|
||||
// 主键
|
||||
// GetConn 数据库连接
|
||||
func (this Resource) GetConn() string {
|
||||
return this.Conn
|
||||
}
|
||||
|
||||
// GetPrimarykey 主键
|
||||
func (this Resource) GetPrimarykey() string {
|
||||
return this.Primarykey
|
||||
}
|
||||
|
||||
// 是否虚拟资源
|
||||
// IsVirtual 是否虚拟资源
|
||||
func (this Resource) IsVirtual() bool {
|
||||
return this.IsResVirtual
|
||||
}
|
||||
|
||||
// 是否系统资源
|
||||
// IsSystem 是否系统资源
|
||||
func (this Resource) IsSystem() bool {
|
||||
return this.IsResSystem
|
||||
return this.Conn == "service-support"
|
||||
}
|
||||
|
||||
// 资源字段
|
||||
// GetFields 资源字段
|
||||
func (this Resource) GetFields() (result []req.ResField) {
|
||||
for _, item := range this.Fields {
|
||||
result = append(result, item)
|
||||
@@ -99,137 +115,145 @@ func (this Resource) GetFields() (result []req.ResField) {
|
||||
return result
|
||||
}
|
||||
|
||||
// 资源字段
|
||||
// GetField 资源字段
|
||||
func (this Resource) GetField(code string) (req.ResField, bool) {
|
||||
return lo.Find(this.GetFields(), func(v req.ResField) bool {
|
||||
return v.GetCode() == code
|
||||
})
|
||||
}
|
||||
|
||||
// 判断资源字段
|
||||
// HasField 判断资源字段
|
||||
func (this Resource) HasField(code string) bool {
|
||||
return lo.SomeBy(this.GetFields(), func(v req.ResField) bool {
|
||||
return v.GetCode() == code
|
||||
})
|
||||
}
|
||||
|
||||
// 开启事物
|
||||
func (this Resource) BeginTransaction() (*db.Transaction, error) {
|
||||
return this.GetDBConn().BeginTransaction()
|
||||
// BeginTransaction 开启事物
|
||||
func (this Resource) BeginTransaction() (*db.TxDatabase, error) {
|
||||
return this.DB().Begin()
|
||||
}
|
||||
|
||||
// 获取资源链接
|
||||
func (this Resource) GetDBConn() *db.Connection {
|
||||
db := do.MustInvoke[db.DB](this.container)
|
||||
// DB 获取资源链接
|
||||
func (this Resource) DB() *db.Database {
|
||||
dbEngine := do.MustInvoke[engine.Engine](this.container)
|
||||
conn := dbEngine.Connection(this.Conn)
|
||||
|
||||
if this.IsSystem() {
|
||||
return db.Connection("service-support")
|
||||
}
|
||||
|
||||
return db.Connection("default")
|
||||
}
|
||||
|
||||
// 获取资源对应的数据库连接
|
||||
func (this Resource) GetDBBuilder() *db.Builder {
|
||||
return this.GetDBConn().Query()
|
||||
}
|
||||
|
||||
// 获取资源对应的数据表
|
||||
func (this Resource) GetTable() db.Expression {
|
||||
if this.IsVirtual() {
|
||||
return db.Raw("(" + this.Table + ")")
|
||||
}
|
||||
|
||||
return db.Raw(this.Table)
|
||||
}
|
||||
|
||||
func (this Resource) GetDBDriver() string {
|
||||
return this.GetDBConn().GetConfig().Driver
|
||||
}
|
||||
|
||||
func (this Resource) GetAuthDBTable(u req.User, params ...any) *db.Builder {
|
||||
builder := this.GetDBTable(append(params, u)...)
|
||||
|
||||
// 数据权限过滤
|
||||
builder.Before(func(b *db.Builder, t string, data ...map[string]any) {
|
||||
if t == db.TYPE_SELECT || t == db.TYPE_UPDATE || t == db.TYPE_DELETE {
|
||||
this.WithRolesCondition(b, t, u)
|
||||
}
|
||||
})
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
// GetDBTable("Test", contracts.User)
|
||||
func (this Resource) GetDBTable(params ...any) *db.Builder {
|
||||
builder := this.GetDBBuilder()
|
||||
|
||||
var user req.User
|
||||
alias := this.Code
|
||||
|
||||
for _, param := range params {
|
||||
switch v := param.(type) {
|
||||
case *db.Transaction:
|
||||
builder.Tx = v
|
||||
case string:
|
||||
alias = v
|
||||
case req.User:
|
||||
user = v
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化数据库存储数据
|
||||
builder.Before(func(b *db.Builder, t string, data ...map[string]any) {
|
||||
if t == db.TYPE_UPDATE {
|
||||
// 格式化保存数据
|
||||
this.formatSaveValue(data[0])
|
||||
}
|
||||
if t == db.TYPE_INSERT {
|
||||
// 移除 table alias
|
||||
b.Table(string(this.GetTable()))
|
||||
for i := 0; i < len(data); i++ {
|
||||
// 格式化保存数据
|
||||
this.formatSaveValue(data[i])
|
||||
// 填充保存数据
|
||||
this.fillSaveValue(data[i], user, db.TYPE_INSERT)
|
||||
if isLocalDB(conn.Dialect()) && !this.IsResVirtual && this.Table != "" && this.initOnce != nil {
|
||||
this.initOnce.Do(func() {
|
||||
if err := this.autoCreateTable(conn); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 资源事件
|
||||
this.onResEvent(builder)
|
||||
|
||||
// 用户事件
|
||||
if this.IsHistoryRecord {
|
||||
this.onUserEvent(builder, user)
|
||||
})
|
||||
}
|
||||
|
||||
// 虚拟资源暂时不考虑鉴权
|
||||
if !this.IsVirtual() {
|
||||
// 返回鉴权后的 DB Builder
|
||||
// return
|
||||
// vtable 资源:查询走 engine 中的 :memory: 连接("__"+Conn),
|
||||
// 原文件连接仅用于 _vtab_cache 持久化,两者互不阻塞。
|
||||
if conn.Dialect() == "vtable" && this.Table != "" && this.initOnce != nil {
|
||||
memConn := dbEngine.Connection("__" + this.Conn)
|
||||
this.initOnce.Do(func() {
|
||||
if err := this.autoSetupVtab(memConn); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
return memConn
|
||||
}
|
||||
|
||||
return builder.Table(string(this.GetTable()), alias)
|
||||
return conn
|
||||
}
|
||||
|
||||
func (this Resource) WithRolesCondition(b *db.Builder, t string, u req.User) error {
|
||||
// autoSetupVtab 通过容器获取 ResVirtualTableSetup(由 framework-v2 在 sqlite_vtable tag 下注册),
|
||||
// 再从 App 取出当前资源对应的 ResVirtualTable 实现,完成 vtab 注册和 CREATE VIRTUAL TABLE。
|
||||
func (this Resource) autoSetupVtab(conn *db.Database) error {
|
||||
_, err := conn.Exec(fmt.Sprintf(`CREATE VIRTUAL TABLE IF NOT EXISTS %s USING %s_mod()`, this.Table, this.Table))
|
||||
return err
|
||||
}
|
||||
|
||||
// autoCreateTable 自动创建数据表
|
||||
func (this Resource) autoCreateTable(conn *db.Database) error {
|
||||
sb := schema.New(conn)
|
||||
return sb.Create(this.Table, func(bp *schema.Blueprint) {
|
||||
bp.Comment = this.Name
|
||||
for _, field := range this.Fields {
|
||||
field.ToBlueprint(bp)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetTable 获取资源对应的数据表
|
||||
func (this Resource) GetTable() exp.LiteralExpression {
|
||||
if this.IsResVirtual {
|
||||
return db.L("(" + this.Table + ")")
|
||||
}
|
||||
return db.V(db.T(this.Table))
|
||||
}
|
||||
|
||||
// GetDBTable 获取资源对应的数据库连接
|
||||
func (this Resource) GetDBTable(u req.User, opts ...req.ResOption) (sd *db.SelectDataset) {
|
||||
if u == nil {
|
||||
panic("GetDBTable: user cannot be nil")
|
||||
}
|
||||
|
||||
op := &req.ResOptions{}
|
||||
|
||||
for _, cb := range opts {
|
||||
cb(op)
|
||||
}
|
||||
|
||||
alias := this.GetCode()
|
||||
|
||||
if op.Alias != "" {
|
||||
alias = op.Alias
|
||||
}
|
||||
|
||||
sd = this.DB().From(this.GetTable().As(alias))
|
||||
|
||||
if op.Tx != nil {
|
||||
sd = op.Tx.From(this.GetTable().As(alias))
|
||||
}
|
||||
|
||||
sd.WithHook(&ResourceHooks{res: this, u: u, tx: op.Tx})
|
||||
|
||||
return sd
|
||||
}
|
||||
|
||||
func (this Resource) GetRolesCondition(u req.User) (*db.SelectDataset, db.Expression) {
|
||||
isFullRight := false
|
||||
isFullNot := false
|
||||
|
||||
if u == nil {
|
||||
u = GetAnonymous()
|
||||
}
|
||||
|
||||
// 如果是系统用户, 则不做校验
|
||||
if u.Uuid() == "ffffffff-ffff-ffff-ffff-ffffffffffff" {
|
||||
return nil, nil
|
||||
}
|
||||
// 这里不能放在 u.Uuid() == "ffffffff-ffff-ffff-ffff-ffffffffffff" 前面
|
||||
// 会导致初始化数据时循环依赖
|
||||
GetResRoles, err := do.Invoke[GetResRoles](this.container)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
items := GetResRoles(this.GetUuid(), u.Roles()...)
|
||||
|
||||
if len(items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
GetResource := do.MustInvoke[contracts.GetResource](this.container)
|
||||
NewOrm := do.MustInvoke[contracts.NewOrm](this.container)
|
||||
NewOrmModel := do.MustInvoke[contracts.NewOrmModel](this.container)
|
||||
NewOrmJoin := do.MustInvoke[contracts.NewOrmJoin](this.container)
|
||||
|
||||
GetResRelationResource := do.MustInvoke[GetResRelationResource](this.container)
|
||||
GetResRelations := do.MustInvoke[GetResRelations](this.container)
|
||||
GetResource := do.MustInvoke[contracts.GetResource](this.container)
|
||||
GetOrmConditions := do.MustInvoke[contracts.GetOrmConditions](this.container)
|
||||
|
||||
items := do.MustInvoke[GetResRoles](this.container)(this.GetUuid(), u.Roles()...)
|
||||
|
||||
subTables := lo.Reduce(items, func(carry string, item ResRole, _ int) string {
|
||||
db := this.GetDBBuilder().Table(string(this.GetTable()), this.GetCode()).Select(db.Raw("distinct `" + this.GetCode() + "`.*"))
|
||||
sub := lo.Reduce(items, func(carry *db.SelectDataset, item ResRole, _ int) *db.SelectDataset {
|
||||
sd := this.DB().From(this.GetTable().As(this.GetCode())).Select(db.T(this.GetCode()).All())
|
||||
|
||||
joins := lo.Filter(GetResRelations(item.Uuid), func(item ResRelation, _ int) bool {
|
||||
return item.Type == "inner" || item.Type == "left" || item.Type == "right"
|
||||
@@ -249,106 +273,52 @@ func (this Resource) WithRolesCondition(b *db.Builder, t string, u req.User) err
|
||||
// 关联扩展条件
|
||||
join.SetCondition(GetOrmConditions(joins[i].Uuid, condition.Describe("关联扩展条件")))
|
||||
|
||||
join.Inject(db, NewOrmModel(rResource, rResource.GetCode(), rResource.GetName()))
|
||||
join.Inject(sd, NewOrmModel(rResource, rResource.GetCode(), rResource.GetName()))
|
||||
}
|
||||
|
||||
conditions := GetOrmConditions(item.Uuid, condition.Describe("关联扩展条件"))
|
||||
|
||||
if len(joins) == 0 && conditions.IsEmpty() {
|
||||
// 无权限, 直接跳过这个 unoin 语句
|
||||
if carry != "" {
|
||||
if carry != nil {
|
||||
return carry
|
||||
}
|
||||
// 第一个无权限除外, 避免所有用户所属角色都是无权限
|
||||
db.WhereRaw("false")
|
||||
sd = sd.Where(db.V(false))
|
||||
isFullNot = true
|
||||
} else if len(joins) == 0 && conditions.IsNotEmpty() && conditions.IsAlwaysRight() /* 1=1 的这种条件*/ {
|
||||
// 只要有1个满权限, 直接返回单条语句
|
||||
isFullRight = true
|
||||
return db.ToSql()
|
||||
return sd
|
||||
} else if conditions.IsNotEmpty() {
|
||||
oOrm := NewOrm(this, nil)
|
||||
oOrm.SetGlobalParams(req.NewGlobalParam("{}", u))
|
||||
oOrm.SetGlobalParams(req.NewGlobalParams("{}", u))
|
||||
|
||||
db.Where(conditions.ToSql(oOrm.GetModel()))
|
||||
sd = sd.Where(conditions.ToSql(oOrm.GetModel()))
|
||||
|
||||
// 如果前面是无权限的sql查看, 这直接返回本次查询
|
||||
if isFullNot {
|
||||
isFullNot = false
|
||||
return db.ToSql()
|
||||
return sd
|
||||
}
|
||||
}
|
||||
|
||||
if carry != "" {
|
||||
carry += " UNION "
|
||||
if carry != nil {
|
||||
return carry.Union(sd)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s(%s)", carry, db.ToSql())
|
||||
}, "")
|
||||
return sd
|
||||
}, nil)
|
||||
|
||||
if isFullRight {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if isFullNot {
|
||||
b.WhereRaw("false")
|
||||
} else if subTables != "" {
|
||||
if t == db.TYPE_SELECT {
|
||||
b.FromSub(subTables, b.TableAlias)
|
||||
} else {
|
||||
b.WhereRaw(fmt.Sprintf(
|
||||
"`%s`.`id` in (SELECT `temp`.`id` FROM (%s) as `temp`)",
|
||||
lo.Ternary(b.TableAlias != "", b.TableAlias, this.GetCode()),
|
||||
subTables,
|
||||
))
|
||||
}
|
||||
return nil, db.V(false)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 格式化保存数据
|
||||
func (this Resource) formatSaveValue(data map[string]any) {
|
||||
//
|
||||
for k, v := range data {
|
||||
if k == "id" || k == "created_user" || k == "created_at" || k == "deleted_at" || k == "updated_at" {
|
||||
delete(data, k)
|
||||
} else if val, ok := v.(db.Expression); ok {
|
||||
data[k] = val
|
||||
} else if field, ok := this.GetField(k); ok {
|
||||
data[k] = field.ToValue(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 填充保存数据
|
||||
func (this Resource) fillSaveValue(data map[string]any, u req.User, t string) {
|
||||
for _, field := range this.GetFields() {
|
||||
fCode := field.GetCode()
|
||||
|
||||
if fCode == "id" || fCode == "created_user" || fCode == "created_at" || fCode == "deleted_at" || fCode == "updated_at" || fCode == "owned_user" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 只有新增默认字段
|
||||
if _, ok := data[fCode]; !ok {
|
||||
data[fCode] = field.GetRawDefault(this.GetDBDriver())
|
||||
}
|
||||
}
|
||||
|
||||
// 拥有者
|
||||
if _, ok := data["owned_user"]; !ok {
|
||||
data["owned_user"] = u.Uuid()
|
||||
}
|
||||
// 创建者
|
||||
data["created_user"] = u.Uuid()
|
||||
if this.GetDBDriver() == "sqlite" {
|
||||
// 更新时间
|
||||
// sqlite 不能自动更新时间, "DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
|
||||
data["updated_at"] = db.Raw("CURRENT_TIMESTAMP")
|
||||
} else {
|
||||
|
||||
}
|
||||
return sub, nil
|
||||
}
|
||||
|
||||
func (this Resource) GetStruct(extends ...reflect.StructField) any {
|
||||
@@ -379,67 +349,66 @@ func (this Resource) GetSliceStruct(extends ...reflect.StructField) any {
|
||||
return reflect.New(st).Interface()
|
||||
}
|
||||
|
||||
// 资源事件
|
||||
func (this Resource) onResEvent(builder *db.Builder) {
|
||||
builder.After(func(b *db.Builder, t string, result sql.Result, err error, data ...map[string]any) {
|
||||
if err != nil || t == db.TYPE_SELECT {
|
||||
return
|
||||
} else if num, err := result.RowsAffected(); num == 0 || err != nil {
|
||||
return
|
||||
}
|
||||
// onResEvent 资源事件
|
||||
func (this Resource) onResEvent(t string, result sql.Result) {
|
||||
if num, err := result.RowsAffected(); num == 0 || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 有些地方可能没有注册 contracts.Queue
|
||||
if qu, err := do.Invoke[contracts.Queue](this.container); err == nil {
|
||||
// 全局触发器
|
||||
// 1. 清除系统缓存
|
||||
if err := do.MustInvoke[contracts.Queue](this.container).Publish(ResChangeEventTopic, ResChangeEventTopicPayload{
|
||||
if err := qu.Publish(ResChangeEventTopic, ResChangeEventTopicPayload{
|
||||
Type: t,
|
||||
Res: this,
|
||||
Result: result,
|
||||
}); err != nil {
|
||||
log.Println("Queue Publish Err:", ResChangeEventTopic, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 用户事件
|
||||
func (this Resource) onUserEvent(builder *db.Builder, user req.User) {
|
||||
old := []map[string]any{}
|
||||
// onUserEvent 用户事件
|
||||
func (this Resource) onUserEvent(builder exec.QueryExecutor, user req.User) {
|
||||
// old := []map[string]any{}
|
||||
|
||||
builder.Before(func(b *db.Builder, t string, data ...map[string]any) {
|
||||
if t != db.TYPE_UPDATE && t != db.TYPE_DELETE {
|
||||
return
|
||||
}
|
||||
// 查询保存之前的数据
|
||||
if _, err := b.Get(&old); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
// builder.Before(func(b *db.Builder, t string, data ...map[string]any) {
|
||||
// if t != db.TYPE_UPDATE && t != db.TYPE_DELETE {
|
||||
// return
|
||||
// }
|
||||
// // 查询保存之前的数据
|
||||
// if _, err := b.Get(&old); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// })
|
||||
|
||||
builder.After(func(b *db.Builder, t string, result sql.Result, err error, data ...map[string]any) {
|
||||
if err != nil || t == db.TYPE_SELECT {
|
||||
return
|
||||
} else if num, err := result.RowsAffected(); num == 0 || err != nil {
|
||||
return
|
||||
}
|
||||
// builder.After(func(b *db.Builder, t string, result sql.Result, err error, data ...map[string]any) {
|
||||
// if err != nil || t == db.TYPE_SELECT {
|
||||
// return
|
||||
// } else if num, err := result.RowsAffected(); num == 0 || err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
if user == nil {
|
||||
user = GetAnonymous()
|
||||
}
|
||||
// if user == nil {
|
||||
// user = GetAnonymous()
|
||||
// }
|
||||
|
||||
// 触发消息队列
|
||||
if err := do.MustInvoke[contracts.Queue](this.container).Publish(ResChangeRecordTopic, ResChangeRecordTopicPayload{
|
||||
Type: t,
|
||||
User: user,
|
||||
Res: this,
|
||||
Old: old,
|
||||
New: data,
|
||||
Result: result,
|
||||
}); err != nil {
|
||||
log.Println("Queue Publish Err:", ResChangeRecordTopic, err)
|
||||
}
|
||||
})
|
||||
// // 触发消息队列
|
||||
// if err := do.MustInvoke[contracts.Queue](this.container).Publish(ResChangeRecordTopic, ResChangeRecordTopicPayload{
|
||||
// Type: t,
|
||||
// User: user,
|
||||
// Res: this,
|
||||
// Old: old,
|
||||
// New: data,
|
||||
// Result: result,
|
||||
// }); err != nil {
|
||||
// log.Println("Queue Publish Err:", ResChangeRecordTopic, err)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
func NewVirtualResource(pRes req.Resource, code, name, sql string, fields []ResField) req.Resource {
|
||||
func NewVirtualResource(pRes req.Resource, code, name string, sd *db.SelectDataset, fields []ResField) req.Resource {
|
||||
fieldsCopy := make([]ResField, len(fields))
|
||||
|
||||
copy(fieldsCopy, fields)
|
||||
@@ -448,17 +417,21 @@ func NewVirtualResource(pRes req.Resource, code, name, sql string, fields []ResF
|
||||
fieldsCopy[i].CodeResource = code
|
||||
}
|
||||
|
||||
sql, _, err := sd.WithDialect(pRes.DB().Dialect()).ToSQL()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Resource{
|
||||
Uuid: code,
|
||||
PUuid: pRes.GetUuid(),
|
||||
Code: code,
|
||||
Name: name,
|
||||
IsResVirtual: true,
|
||||
IsResSystem: pRes.IsSystem(),
|
||||
Table: sql,
|
||||
IsHistoryRecord: false,
|
||||
HistoryCacheMax: 0,
|
||||
Fields: fieldsCopy,
|
||||
Roles: nil,
|
||||
Uuid: code,
|
||||
PUuid: pRes.GetUuid(),
|
||||
Code: code,
|
||||
Name: name,
|
||||
IsResVirtual: true,
|
||||
Conn: pRes.GetConn(),
|
||||
Table: sql,
|
||||
Fields: fieldsCopy,
|
||||
Roles: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/exp"
|
||||
"git.fsdpf.net/go/db/schema"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
var changeLogOnce sync.Map // key: Conn,每个数据库连接只建一次 _change_log 表
|
||||
|
||||
// isLocalDB 判断是否为本地文件型数据库(LastInsertId 返回最后一条而非第一条)
|
||||
func isLocalDB(dialect string) bool {
|
||||
return dialect == "sqlite3" || dialect == "duckdb"
|
||||
}
|
||||
|
||||
type ResourceHooks struct {
|
||||
res Resource
|
||||
u req.User
|
||||
tx *db.TxDatabase
|
||||
snapshots []map[string]any // UPDATE/DELETE 前预查的行数据
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) isTracked() bool {
|
||||
if len(rh.res.HistoryRoles) == 0 || rh.res.IsVirtual() {
|
||||
return false
|
||||
}
|
||||
if lo.Contains(rh.res.HistoryRoles, "00000000-0000-0000-0000-000000000000") {
|
||||
return true
|
||||
}
|
||||
return lo.Some(rh.res.HistoryRoles, rh.u.Roles())
|
||||
}
|
||||
|
||||
// autoCreateChangeLog 在当前连接上建 _change_log 表,每个连接只执行一次
|
||||
func (rh *ResourceHooks) autoCreateChangeLog() error {
|
||||
conn := rh.res.DB()
|
||||
v, _ := changeLogOnce.LoadOrStore(rh.res.Conn, &sync.Once{})
|
||||
var err error
|
||||
v.(*sync.Once).Do(func() {
|
||||
sb := schema.New(conn)
|
||||
err = sb.Create("_change_log", func(bp *schema.Blueprint) {
|
||||
bp.Comment = "资源数据变更记录"
|
||||
bp.BigIncrements("id").AutoIncrement().Comment("ID")
|
||||
bp.Boolean("enabled").Default("1").Comment("是否有效")
|
||||
bp.Char("created_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("操作用户")
|
||||
bp.Char("owned_user", 36).Default("00000000-0000-0000-0000-000000000000").Comment("拥有者")
|
||||
bp.Timestamp("created_at").UseCurrent().Comment("操作时间")
|
||||
bp.Timestamp("updated_at").UseCurrent().Default(db.L("ON UPDATE CURRENT_TIMESTAMP")).Comment("更新时间")
|
||||
bp.DateTime("deleted_at").Nullable().Comment("删除时间")
|
||||
bp.Char("resource_uuid", 36).Default("").Comment("资源UUID")
|
||||
bp.String("category", 10).Default("").Comment("操作类型 INSERT/UPDATE/DELETE")
|
||||
bp.Char("trace_id", 36).Default("").Comment("请求追踪ID")
|
||||
bp.Integer("row_id").Default("0").Comment("受影响行ID")
|
||||
bp.Json("snapshot").Nullable().Comment("操作前数据快照")
|
||||
})
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// captureSnapshot 在写操作执行前,查出受影响的行存入 snapshots
|
||||
func (rh *ResourceHooks) captureSnapshot(where exp.ExpressionList) {
|
||||
sd := rh.res.DB().From(rh.res.GetTable())
|
||||
if where != nil && len(where.Expressions()) > 0 {
|
||||
sd = sd.Where(where.Expressions()...)
|
||||
}
|
||||
rows, err := sd.Executor().GetRecords()
|
||||
if err != nil {
|
||||
log.Printf("_change_log captureSnapshot err: %v", err)
|
||||
return
|
||||
}
|
||||
rh.snapshots = rows
|
||||
}
|
||||
|
||||
// writeChangeLogs 将变更写入 _change_log
|
||||
func (rh *ResourceHooks) writeChangeLogs(category string, result sql.Result) {
|
||||
if err := rh.autoCreateChangeLog(); err != nil {
|
||||
log.Printf("_change_log init err: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
traceId := rh.u.Runtime().TraceId()
|
||||
opUser := rh.u.Uuid()
|
||||
|
||||
var rows []any
|
||||
|
||||
switch category {
|
||||
case "INSERT":
|
||||
lastId, _ := result.LastInsertId()
|
||||
count, _ := result.RowsAffected()
|
||||
for i := int64(0); i < count; i++ {
|
||||
rowId := lastId + i // MySQL: lastId 是第一条
|
||||
if isLocalDB(rh.res.DB().Dialect()) {
|
||||
rowId = lastId - count + 1 + i // SQLite: lastId 是最后一条
|
||||
}
|
||||
rows = append(rows, map[string]any{
|
||||
"enabled": true,
|
||||
"created_user": opUser,
|
||||
"owned_user": opUser,
|
||||
"resource_uuid": rh.res.Uuid,
|
||||
"category": category,
|
||||
"trace_id": traceId,
|
||||
"row_id": rowId,
|
||||
"snapshot": nil,
|
||||
})
|
||||
}
|
||||
|
||||
case "UPDATE", "DELETE":
|
||||
for _, snap := range rh.snapshots {
|
||||
rowId := int64(0)
|
||||
if id, ok := snap["id"]; ok {
|
||||
rowId = toChangeLogInt64(id)
|
||||
}
|
||||
snapJSON, _ := json.Marshal(snap)
|
||||
rows = append(rows, map[string]any{
|
||||
"enabled": true,
|
||||
"created_user": opUser,
|
||||
"owned_user": opUser,
|
||||
"resource_uuid": rh.res.Uuid,
|
||||
"category": category,
|
||||
"trace_id": traceId,
|
||||
"row_id": rowId,
|
||||
"snapshot": string(snapJSON),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(rows) == 0 {
|
||||
return
|
||||
}
|
||||
inserter := rh.res.DB().Insert("_change_log")
|
||||
if rh.tx != nil {
|
||||
inserter = rh.tx.Insert("_change_log")
|
||||
}
|
||||
if _, err := inserter.Rows(rows...).Executor().Exec(); err != nil {
|
||||
log.Printf("_change_log write err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func toChangeLogInt64(v any) int64 {
|
||||
switch val := v.(type) {
|
||||
case int64:
|
||||
return val
|
||||
case int:
|
||||
return int64(val)
|
||||
case int32:
|
||||
return int64(val)
|
||||
case float64:
|
||||
return int64(val)
|
||||
case string:
|
||||
n, _ := strconv.ParseInt(val, 10, 64)
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) Before(dataset interface{}) error {
|
||||
switch d := dataset.(type) {
|
||||
case *db.SelectDataset:
|
||||
return rh.beforeSelectDataset(d)
|
||||
case *db.InsertDataset:
|
||||
return rh.beforeInsertDataset(d)
|
||||
case *db.UpdateDataset:
|
||||
return rh.beforeUpdateDataset(d)
|
||||
case *db.DeleteDataset:
|
||||
return rh.beforeDeleteDataset(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) After(dataset interface{}, result interface{}) error {
|
||||
if rh.res.IsVirtual() {
|
||||
// 返回鉴权后的 DB Builder
|
||||
return nil
|
||||
}
|
||||
|
||||
// 用户事件
|
||||
// if rh.res.IsHistoryRecord {
|
||||
// rh.res.onUserEvent(builder, user)
|
||||
// }
|
||||
|
||||
switch dataset.(type) {
|
||||
case *db.SelectDataset:
|
||||
case *db.InsertDataset:
|
||||
r := result.(sql.Result)
|
||||
rh.res.onResEvent("INSERT", r)
|
||||
if rh.isTracked() {
|
||||
rh.writeChangeLogs("INSERT", r)
|
||||
}
|
||||
case *db.UpdateDataset:
|
||||
r := result.(sql.Result)
|
||||
rh.res.onResEvent("UPDATE", r)
|
||||
if rh.isTracked() {
|
||||
rh.writeChangeLogs("UPDATE", r)
|
||||
}
|
||||
case *db.DeleteDataset:
|
||||
r := result.(sql.Result)
|
||||
rh.res.onResEvent("DELETE", r)
|
||||
if rh.isTracked() {
|
||||
rh.writeChangeLogs("DELETE", r)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeInsertDataset(id *db.InsertDataset) error {
|
||||
switch true {
|
||||
case id.GetClauses().HasRows():
|
||||
return rh.beforeInsertRows(id)
|
||||
case id.GetClauses().HasVals():
|
||||
return rh.beforeInsertColsVals(id)
|
||||
case id.GetClauses().HasFrom():
|
||||
return rh.beforeInsertFromQuery(id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeInsertRows(id *db.InsertDataset) error {
|
||||
rows := id.GetClauses().Rows()
|
||||
|
||||
for i := range rows {
|
||||
rowValue := reflect.ValueOf(rows[i])
|
||||
if rowValue.Kind() == reflect.Ptr {
|
||||
rowValue = rowValue.Elem()
|
||||
}
|
||||
if rowValue.Kind() == reflect.Struct {
|
||||
if row, err := exp.NewRecordFromStruct(rowValue.Interface(), true, false); err != nil {
|
||||
return err
|
||||
} else {
|
||||
rows[i] = row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
row := rows[i].(map[string]any)
|
||||
// 格式化保存数据
|
||||
if err := rh.normalizeSaveValue(row); err != nil {
|
||||
return err
|
||||
}
|
||||
// 填充默认数据
|
||||
if err := rh.applyDefaultValue(row, true, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeInsertColsVals(id *db.InsertDataset) error {
|
||||
cols := id.GetClauses().Cols()
|
||||
vals := id.GetClauses().Vals()
|
||||
|
||||
colsName := []string{}
|
||||
for _, col := range cols.Columns() {
|
||||
colsName = append(colsName, col.(exp.IdentifierExpression).GetCol().(string))
|
||||
}
|
||||
|
||||
colsVal := []any{}
|
||||
for k, v := range lo.OmitByKeys(rh.getFieldsDefaultValue(true, false), colsName) {
|
||||
cols = cols.Append(db.C(k))
|
||||
colsVal = append(colsVal, v)
|
||||
}
|
||||
|
||||
for i := 0; i < len(vals); i++ {
|
||||
for j := 0; j < len(vals[i]); j++ {
|
||||
if filed, ok := rh.res.GetField(colsName[j]); ok {
|
||||
vals[i][j] = filed.ToValue(vals[i][j])
|
||||
}
|
||||
}
|
||||
vals[i] = append(vals[i], colsVal...)
|
||||
}
|
||||
|
||||
*id = *id.Cols(cols)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeInsertFromQuery(id *db.InsertDataset) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 规范化保存数据
|
||||
// 1. 移除系统缺省字段, 如果是 insert 需要调用 applyDefaultValue
|
||||
// 2. 移除系统中没有的字段
|
||||
func (rh *ResourceHooks) normalizeSaveValue(row db.Record) error {
|
||||
for k, v := range row {
|
||||
if k == "id" || k == "created_user" || k == "created_at" || k == "deleted_at" || k == "updated_at" {
|
||||
delete(row, k)
|
||||
} else if val, ok := v.(db.Expression); ok {
|
||||
row[k] = val
|
||||
} else if field, ok := rh.res.GetField(k); ok {
|
||||
row[k] = field.ToValue(v)
|
||||
} else {
|
||||
delete(row, k)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 填充默认数据
|
||||
func (rh *ResourceHooks) applyDefaultValue(row db.Record, forInsert, forUpdate bool) error {
|
||||
// 填充默认数据
|
||||
for k, v := range rh.getFieldsDefaultValue(forInsert, forUpdate) {
|
||||
if _, ok := row[k]; !ok {
|
||||
row[k] = v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) getFieldsDefaultValue(forInsert, forUpdate bool) map[string]db.Expression {
|
||||
defs := map[string]db.Expression{}
|
||||
|
||||
if forInsert {
|
||||
for _, item := range rh.res.Fields {
|
||||
if item.GetCode() == "updated_at" || item.GetCode() == "created_user" || item.GetCode() == "owned_user" {
|
||||
continue
|
||||
}
|
||||
if len(item.Default) > 4 && strings.ToLower(item.Default[0:4]) == "sql:" {
|
||||
defs[item.GetCode()] = item.GetRawDefault()
|
||||
} else if item.DataType == req.ResJson {
|
||||
defs[item.GetCode()] = item.GetRawDefault()
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := defs["owned_user"]; !ok {
|
||||
defs["owned_user"] = db.V(rh.u.Uuid())
|
||||
}
|
||||
|
||||
defs["created_user"] = db.V(rh.u.Uuid())
|
||||
}
|
||||
|
||||
if forUpdate && isLocalDB(rh.res.DB().Dialect()) {
|
||||
defs["updated_at"] = db.L("CURRENT_TIMESTAMP")
|
||||
}
|
||||
|
||||
return defs
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeSelectDataset(sd *db.SelectDataset) error {
|
||||
sub, ex := rh.res.GetRolesCondition(rh.u)
|
||||
if ex != nil {
|
||||
*sd = *sd.Where(ex)
|
||||
} else if sub != nil {
|
||||
table := sd.GetClauses().From().Columns()[0]
|
||||
if alias, ok := table.(exp.AliasedExpression); ok {
|
||||
*sd = *sd.From(db.V(sub.Expression()).As(alias.GetAs()))
|
||||
} else {
|
||||
*sd = *sd.From(db.V(sub.Expression()).As(table))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rh *ResourceHooks) beforeUpdateDataset(ud *db.UpdateDataset) error {
|
||||
// 虚拟资源暂时不考虑鉴权
|
||||
if rh.res.IsVirtual() {
|
||||
return nil
|
||||
}
|
||||
sub, ex := rh.res.GetRolesCondition(rh.u)
|
||||
if ex != nil {
|
||||
*ud = *ud.Where(ex)
|
||||
} else if sub != nil {
|
||||
table := ud.GetClauses().Table()
|
||||
if alias, ok := table.(exp.AliasedExpression); ok {
|
||||
*ud = *ud.Where(alias.GetAs().Col("id").In(sub.Select(db.T(rh.res.GetCode()).Col("id"))))
|
||||
} else {
|
||||
*ud = *ud.Where(db.L("?.id", table).In(sub.Select(db.T(rh.res.GetCode()).Col("id"))))
|
||||
}
|
||||
}
|
||||
|
||||
// 记录变更前快照(role 条件已应用,WHERE 与实际 UPDATE 一致)
|
||||
if rh.isTracked() {
|
||||
rh.captureSnapshot(ud.GetClauses().Where())
|
||||
}
|
||||
|
||||
// 格式化保存数据
|
||||
if ud.GetClauses().HasSetValues() {
|
||||
udv := ud.GetClauses().SetValues()
|
||||
|
||||
switch data := udv.(type) {
|
||||
case map[string]any:
|
||||
return rh.normalizeSaveValue(data)
|
||||
case db.Record:
|
||||
return rh.normalizeSaveValue(data)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type: %T", udv)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (rh *ResourceHooks) beforeDeleteDataset(dd *db.DeleteDataset) error {
|
||||
// 虚拟资源暂时不考虑鉴权
|
||||
if rh.res.IsVirtual() {
|
||||
return nil
|
||||
}
|
||||
sub, ex := rh.res.GetRolesCondition(rh.u)
|
||||
if ex != nil {
|
||||
*dd = *dd.Where(ex)
|
||||
} else if sub != nil {
|
||||
table := dd.GetClauses().From().Columns()[0]
|
||||
if alias, ok := table.(exp.AliasedExpression); ok {
|
||||
*dd = *dd.Where(alias.GetAs().Col("id").In(sub.Select(db.T(rh.res.GetCode()).Col("id"))))
|
||||
} else {
|
||||
*dd = *dd.Where(db.L("?.id", table).In(sub.Select(db.T(rh.res.GetCode()).Col("id"))))
|
||||
}
|
||||
}
|
||||
|
||||
// 记录变更前快照(role 条件已应用,WHERE 与实际 DELETE 一致)
|
||||
if rh.isTracked() {
|
||||
rh.captureSnapshot(dd.GetClauses().Where())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package base_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.fsdpf.net/go/contracts/base"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/engine"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
_ "git.fsdpf.net/go/db/schema/dialect/mysql"
|
||||
_ "git.fsdpf.net/go/db/schema/dialect/sqlite3"
|
||||
)
|
||||
|
||||
type resourceTest struct {
|
||||
suite.Suite
|
||||
res *base.Resource
|
||||
}
|
||||
|
||||
func TestResource(t *testing.T) {
|
||||
suite.Run(t, new(resourceTest))
|
||||
}
|
||||
|
||||
func (t *resourceTest) SetupSuite() {
|
||||
app := do.New()
|
||||
|
||||
do.Provide(app, func(container do.Injector) (engine.Engine, error) {
|
||||
return engine.Open(map[string]engine.DBConfig{
|
||||
"default": engine.NewDBConfig("mysql",
|
||||
engine.WithHost(os.Getenv("MYSQL_HOST")),
|
||||
engine.WithPort(os.Getenv("MYSQL_PORT")),
|
||||
engine.WithDatabase(os.Getenv("MYSQL_DB")),
|
||||
engine.WithUsername(os.Getenv("MYSQL_USER")),
|
||||
engine.WithPassword(os.Getenv("MYSQL_PASSWD")),
|
||||
),
|
||||
}), nil
|
||||
})
|
||||
|
||||
fields := []base.ResField{
|
||||
{Name: "字段", Code: "fields", DataType: req.ResJson, Default: "{}"},
|
||||
}
|
||||
|
||||
res := &base.Resource{
|
||||
Uuid: "4bf3a311-cbe1-4236-bdda-c64ab04ae9b1",
|
||||
Code: "User",
|
||||
Name: "用户",
|
||||
Table: "users",
|
||||
Primarykey: "id",
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
res.InitContainer(app)
|
||||
|
||||
t.res = res
|
||||
}
|
||||
|
||||
func (t *resourceTest) TestInsertRows() {
|
||||
type Temp struct {
|
||||
Name string `db:"name"`
|
||||
Fields map[string]any `db:"fields"`
|
||||
}
|
||||
|
||||
result := "INSERT INTO `users` AS `User` (`created_user`, `fields`, `name`, `owned_user`) " +
|
||||
"VALUES ('00000000-0000-0000-0000-000000000000', '{\\\"aa\\\":1}', '张三', '00000000-0000-0000-0000-000000000000')"
|
||||
|
||||
sql, _, _ := t.res.GetDBTable(base.GetAnonymous()).Insert().Rows(
|
||||
db.Record{"name": "张三", "id": 1, "fields": map[string]any{"aa": 1}},
|
||||
).Executor().ToSQL()
|
||||
t.Equal(result, sql)
|
||||
|
||||
sql, _, _ = t.res.GetDBTable(base.GetAnonymous()).Insert().Rows(
|
||||
&Temp{Name: "张三", Fields: map[string]any{"aa": 1}},
|
||||
).Executor().ToSQL()
|
||||
t.Equal(result, sql)
|
||||
|
||||
sql, _, _ = t.res.GetDBTable(base.GetAnonymous()).Insert().Rows(
|
||||
Temp{Name: "张三", Fields: map[string]any{"aa": 1}},
|
||||
).Executor().ToSQL()
|
||||
t.Equal(result, sql)
|
||||
}
|
||||
func (t *resourceTest) TestInsertColsVals() {
|
||||
result := "INSERT INTO `users` AS `User` (`name`, `fields`, `created_user`, `owned_user`) VALUES " +
|
||||
"('李四', '{\\\"aa\\\":1}', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000'), " +
|
||||
"('王五', '{\\\"aa\\\":3}', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000')"
|
||||
|
||||
sql, _, _ := t.res.GetDBTable(base.GetAnonymous()).Insert().Cols("name", "fields").Vals(
|
||||
db.Vals{"李四", map[string]any{"aa": 1}},
|
||||
db.Vals{"王五", map[string]any{"aa": 3}},
|
||||
).Executor().ToSQL()
|
||||
|
||||
_ = result
|
||||
_ = sql
|
||||
|
||||
// t.Equal(result, sql)
|
||||
|
||||
}
|
||||
func (*resourceTest) TestInsertColsFromQuery() {}
|
||||
@@ -15,6 +15,7 @@ type Route struct {
|
||||
Exact bool `db:"exact"`
|
||||
Platform string `db:"platform"`
|
||||
Extra map[string]any `db:"extra"`
|
||||
Roles []string `db:"roles"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ func (this user) Runtime() req.UserRuntime {
|
||||
return this.runtime
|
||||
}
|
||||
|
||||
// 获取匿名用户
|
||||
// GetAnonymous 获取匿名用户
|
||||
func GetAnonymous(opts ...req.UserRuntimeOption) req.User {
|
||||
u := user{
|
||||
id: 0,
|
||||
@@ -69,7 +69,7 @@ func GetAnonymous(opts ...req.UserRuntimeOption) req.User {
|
||||
return u
|
||||
}
|
||||
|
||||
// 系统用户
|
||||
// GetSystemUser 系统用户
|
||||
func GetSystemUser(opts ...req.UserRuntimeOption) req.User {
|
||||
u := user{
|
||||
id: -1,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
)
|
||||
|
||||
type UserAsset struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
FileName string `db:"filename"`
|
||||
File string `db:"file"`
|
||||
Mime string `db:"mime"`
|
||||
Size int64 `db:"size"`
|
||||
Ext string `db:"ext"`
|
||||
Hit int64 `db:"hit"`
|
||||
IsPrivate bool `db:"isPrivate"`
|
||||
UpdatedAt string `db:"updated_at"`
|
||||
CreatedAt string `db:"created_at"`
|
||||
}
|
||||
|
||||
type GetUserAsset func(user req.User, code string) (UserAsset, bool)
|
||||
|
||||
type UploadUserAsset func(user req.User, file string, name string) (UserAsset, error)
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/reflux"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type CondflowService interface {
|
||||
Run(name string, input reflux.R, u req.User) error
|
||||
}
|
||||
|
||||
type FlowDecision interface {
|
||||
Controller
|
||||
Res() req.Resource
|
||||
}
|
||||
|
||||
type BaseFlowDecision struct {
|
||||
Controller
|
||||
res string
|
||||
}
|
||||
|
||||
func (be BaseFlowDecision) Res() req.Resource {
|
||||
return do.MustInvoke[MustResource](be.Container())(be.res)
|
||||
}
|
||||
|
||||
func NewBaseFlowDecision(container do.Injector, res string) *BaseFlowDecision {
|
||||
return &BaseFlowDecision{
|
||||
Controller: &BaseController{container},
|
||||
res: res,
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -2,23 +2,23 @@ package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type Controller interface {
|
||||
// 初始化
|
||||
Init() error
|
||||
// 获取 Container
|
||||
Container() *do.Injector
|
||||
Container() do.Injector
|
||||
// 派遣一个任务
|
||||
Dispatch(job string, payload any, u req.User) error
|
||||
}
|
||||
|
||||
type BaseController struct {
|
||||
container *do.Injector
|
||||
container do.Injector
|
||||
}
|
||||
|
||||
func (this BaseController) Container() *do.Injector {
|
||||
func (this BaseController) Container() do.Injector {
|
||||
return this.container
|
||||
}
|
||||
|
||||
@@ -30,6 +30,6 @@ func (this BaseController) Dispatch(job string, payload any, u req.User) error {
|
||||
return do.MustInvoke[JobService](this.Container()).Dispatch(job, payload, u)
|
||||
}
|
||||
|
||||
func NewBaseController(container *do.Injector) Controller {
|
||||
func NewBaseController(container do.Injector) Controller {
|
||||
return &BaseController{container}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type EventStream interface {
|
||||
Controller
|
||||
// 获取请求信息
|
||||
Request() *http.Request
|
||||
// 请求处理
|
||||
Execute(req.GlobalParams) error
|
||||
// 获取用户信息
|
||||
User() req.User
|
||||
// 路由信息
|
||||
Route() req.Route
|
||||
// 发送数据
|
||||
Sent(data, event string) error
|
||||
}
|
||||
|
||||
type EsHandleController struct {
|
||||
Controller
|
||||
w http.ResponseWriter
|
||||
flusher http.Flusher
|
||||
request *http.Request
|
||||
}
|
||||
|
||||
func (this EsHandleController) Request() *http.Request {
|
||||
return this.request
|
||||
}
|
||||
|
||||
func (this EsHandleController) Route() req.Route {
|
||||
return this.Request().Context().Value(req.RouteCtx{Name: "Route"}).(req.Route)
|
||||
}
|
||||
|
||||
func (this EsHandleController) User() req.User {
|
||||
return this.Request().Context().Value(req.RouteCtx{Name: "User"}).(req.User)
|
||||
}
|
||||
|
||||
func (this EsHandleController) Execute(req.GlobalParams) error {
|
||||
return ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func (this EsHandleController) Sent(data, event string) error {
|
||||
if _, err := io.WriteString(this.w, "event: "+event+"\ndata: "+data+"\n\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
this.flusher.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewEventStreamController(container do.Injector, request *http.Request, w http.ResponseWriter) EventStream {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
return &EsHandleController{
|
||||
Controller: &BaseController{container},
|
||||
request: request,
|
||||
w: w,
|
||||
flusher: w.(http.Flusher),
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type Executor interface {
|
||||
Controller
|
||||
Res() req.Resource
|
||||
}
|
||||
|
||||
type BaseExecutor struct {
|
||||
Controller
|
||||
res string
|
||||
}
|
||||
|
||||
func (be BaseExecutor) Res() req.Resource {
|
||||
return do.MustInvoke[MustResource](be.Container())(be.res)
|
||||
}
|
||||
|
||||
func NewBaseExecutor(container do.Injector, res string) *BaseExecutor {
|
||||
return &BaseExecutor{
|
||||
Controller: &BaseController{container},
|
||||
res: res,
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,71 @@
|
||||
module git.fsdpf.net/go/contracts
|
||||
|
||||
go 1.21
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.10
|
||||
|
||||
replace (
|
||||
git.fsdpf.net/go/condition => ../condition-v2
|
||||
git.fsdpf.net/go/db => ../db-v2
|
||||
git.fsdpf.net/go/reflux => ../reflux
|
||||
git.fsdpf.net/go/req => ../req-v2
|
||||
)
|
||||
|
||||
require (
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126093638-50e1b3630e6b
|
||||
git.fsdpf.net/go/db v0.0.0-20241125095839-f186c88c5c2a
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jhump/protoreflect v1.15.1
|
||||
github.com/lestrrat-go/jwx v1.2.25
|
||||
github.com/samber/do v1.6.0
|
||||
github.com/samber/lo v1.39.0
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/tidwall/gjson v1.17.1
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
google.golang.org/grpc v1.52.0
|
||||
git.fsdpf.net/go/condition v0.0.0-00010101000000-000000000000
|
||||
git.fsdpf.net/go/db v0.0.0-00010101000000-000000000000
|
||||
git.fsdpf.net/go/reflux v0.0.0-00010101000000-000000000000
|
||||
git.fsdpf.net/go/req v0.0.0-00010101000000-000000000000
|
||||
git.fsdpf.net/go/utils v0.0.0-20240509025914-c03a9cb48aff
|
||||
github.com/samber/do/v2 v2.0.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.42 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/samber/go-type-to-string v1.8.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
git.fsdpf.net/go/req v0.0.0-20241126093511-f1774e5ca2f0 // indirect
|
||||
git.fsdpf.net/go/utils v0.0.0-20240509025914-c03a9cb48aff // indirect
|
||||
github.com/bufbuild/protocompile v0.4.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jhump/protoreflect v1.15.1
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.1 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.25
|
||||
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/spf13/afero v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.15.0
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
|
||||
google.golang.org/grpc v1.52.0
|
||||
google.golang.org/protobuf v1.28.2-0.20230222093303-bc1253ad3743 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -36,50 +36,12 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509022115-37018837d1b0 h1:MHjkN+zrSCqb7Qswn9fFrHT4XaoDnGbXz0ssSKnWnQk=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509022115-37018837d1b0/go.mod h1:Zj9yrMa/A3WICTc9zn3W55ociuoBompEsVPo6EUXW5Q=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509023514-a871ccfa75ac h1:pJQ0sFjPtv/RCy8k0mC/Kpn/j6As5bSqXyG0MWaylhc=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509023514-a871ccfa75ac/go.mod h1:Zj9yrMa/A3WICTc9zn3W55ociuoBompEsVPo6EUXW5Q=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509024751-9694d9033266 h1:s5On5PKF1ZiIGgq6R2Nk/2GcURuZxDAVK1Sru54Ybn0=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509024751-9694d9033266/go.mod h1:Zj9yrMa/A3WICTc9zn3W55ociuoBompEsVPo6EUXW5Q=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509030058-b0e337133e44 h1:XHJxWJuofEL7TLMDBsywi48nciaon8bEFOJLMYkOrNk=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509030058-b0e337133e44/go.mod h1:xgX0fc3LEgrPk9tihGQqoQND4EWlNgYbLCi70TuCvvM=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509034445-afc243ea916e h1:ZfwjqZS5URF4Y0JfQeytSdmXimJ4l74ooWRppza+3Tk=
|
||||
git.fsdpf.net/go/condition v0.0.0-20240509034445-afc243ea916e/go.mod h1:K5ocyKVxgC5mI1k/crgrEozK0r51qAavWg0hweXo6PU=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126084857-bb134581146e h1:GR7OmszSIcutwYfYkL0wGJXKlLzOHCszjN7Oy2jAcRU=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126084857-bb134581146e/go.mod h1:cQhj0R7Q1GP4qxOvqRAwpGPSdYCX/c6dN3WSHhjmMvw=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126090116-c3471706f4d4 h1:BMO+OujDukHDKD0RLUEu7w1ZZyHvtz8k69pHUiP7yPE=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126090116-c3471706f4d4/go.mod h1:uKIEYgBdHFFnNQs6y7cvwsi4b/0Jk1QDneDgemo3Oa0=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126093638-50e1b3630e6b h1:0Ez7ny8HNlZTMhYdlsGbSDcc9vkWt/V/OWlPi+sDw/s=
|
||||
git.fsdpf.net/go/condition v0.0.0-20241126093638-50e1b3630e6b/go.mod h1:5N2xtaawQLquSsF8jJYPvOXhYNplBK+Iib783hTRg8c=
|
||||
git.fsdpf.net/go/db v0.0.0-20230621051209-5740d112407f h1:H+/H6j+hyFMgSy+tUNmymRy7/PWQRjjp8qY/DrhwrsY=
|
||||
git.fsdpf.net/go/db v0.0.0-20230621051209-5740d112407f/go.mod h1:397Sdx1cJS0OlHtTX1bVl//9k3Xn0Klnc6jC4MAkb6w=
|
||||
git.fsdpf.net/go/db v0.0.0-20230731125324-11651ea6640b h1:fRgWNOQ9dAYuUZHQ24oi1XqRbJIcJvZGbnQDaAKI7IY=
|
||||
git.fsdpf.net/go/db v0.0.0-20230731125324-11651ea6640b/go.mod h1:397Sdx1cJS0OlHtTX1bVl//9k3Xn0Klnc6jC4MAkb6w=
|
||||
git.fsdpf.net/go/db v0.0.0-20241125095839-f186c88c5c2a h1:upXLpdDk7Oz7rNoMLx4uDRx50P3tLvwwlslywSkdygk=
|
||||
git.fsdpf.net/go/db v0.0.0-20241125095839-f186c88c5c2a/go.mod h1:397Sdx1cJS0OlHtTX1bVl//9k3Xn0Klnc6jC4MAkb6w=
|
||||
git.fsdpf.net/go/req v0.0.0-20240508133526-672fc634ef20 h1:PhjRtac2r0Vn1WBBzhRhz4//bJhHgkf/SHZe3mQYPto=
|
||||
git.fsdpf.net/go/req v0.0.0-20240508133526-672fc634ef20/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509031332-f00b993f9c7d h1:TKNhQWDKyL50eVC/ptur1YuRkjRnKOlMsE1Y9tFCVbk=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509031332-f00b993f9c7d/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509031626-6501b5ed0f2b h1:KlVBAuWnFt1lcRTCcBU165MIldJqleO/LahWrd3FvUA=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509031626-6501b5ed0f2b/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509033523-c8cda2d8db56 h1:7ElgXUzt75brzOr44mItipTQTk++kZNoGOyTWHLxkTU=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509033523-c8cda2d8db56/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509033936-f8a11598db60 h1:hHrEcUSbtajC2LAo2fvEo7yrSSDeM/s5FAuQluP7yZQ=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509033936-f8a11598db60/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509034400-4ef8c130a758 h1:nHys8Bwb29b/FvnE100KVH62aD2j5CEPxTQ/RRHo27k=
|
||||
git.fsdpf.net/go/req v0.0.0-20240509034400-4ef8c130a758/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126084545-fd3625802b40 h1:Ik3m4G4fY0kEHGX7qg01L2Q7OXjXAvRRqg8ErwQd2Hs=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126084545-fd3625802b40/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126090024-bccb0382d886 h1:LOKgXrnJT4xmxf9KkvZBOZDQYEhNz3h0TZl13R2GCtk=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126090024-bccb0382d886/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126093511-f1774e5ca2f0 h1:XevOuJLH63dzRiE6Sni/M0xOUw+bmrqH5Rcum+Aayjo=
|
||||
git.fsdpf.net/go/req v0.0.0-20241126093511-f1774e5ca2f0/go.mod h1:S+p7t3XclKxsvaXQng7AMmqEGq11FSnMzxOtv1z8JUw=
|
||||
git.fsdpf.net/go/utils v0.0.0-20240509025914-c03a9cb48aff h1:1hokQGKGIstoI7WDBir2N24OGgnussZ3GmucK4TQRuI=
|
||||
git.fsdpf.net/go/utils v0.0.0-20240509025914-c03a9cb48aff/go.mod h1:NUoyQtFr905YT+pi850mvSf4YX0WghQIcMQkTvize5o=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
|
||||
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -102,20 +64,20 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -157,6 +119,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -172,8 +135,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
@@ -192,10 +153,12 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
|
||||
@@ -208,12 +171,16 @@ github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0ot
|
||||
github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY=
|
||||
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=
|
||||
github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -223,19 +190,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/samber/do v1.6.0 h1:Jy/N++BXINDB6lAx5wBlbpHlUdl0FKpLWgGEV9YWqaU=
|
||||
github.com/samber/do v1.6.0/go.mod h1:DWqBvumy8dyb2vEnYZE7D7zaVEB64J45B0NjTlY/M4k=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
|
||||
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/samber/do/v2 v2.0.0 h1:tnunwWaoqSfJ9hxVIaJawIo7JXHQlqT9d9YBXlE9Keg=
|
||||
github.com/samber/do/v2 v2.0.0/go.mod h1:ZSBCE7Xr6nTNIOVo4DBrkl2+ydUbIOzJjjdV8En5XO4=
|
||||
github.com/samber/go-type-to-string v1.8.0 h1:5z6tDTjtXxkIAoAuHAZYMYR8mkBZjVgeSH7jcSLqc8w=
|
||||
github.com/samber/go-type-to-string v1.8.0/go.mod h1:jpU77vIDoIxkahknKDoEx9C8bQ1ADnh2sotZ8I4QqBU=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
|
||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -245,6 +211,8 @@ github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jH
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
@@ -252,13 +220,12 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@@ -285,8 +252,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -297,8 +264,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -354,8 +319,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -375,7 +340,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -412,8 +378,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -422,8 +388,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -575,8 +541,9 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.28.2-0.20230222093303-bc1253ad3743 h1:yqElulDvOF26oZ2O+2/aoX7mQ8DY/6+p39neytrycd8=
|
||||
google.golang.org/protobuf v1.28.2-0.20230222093303-bc1253ad3743/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
var defaultWsClientGroup WsClientGroup = "__DEFAULT__"
|
||||
@@ -95,7 +95,7 @@ func (HttpHandleController) AuthDB() req.ResAuthDB {
|
||||
return req.ResAuthOn
|
||||
}
|
||||
|
||||
func NewHttpController(container *do.Injector, request *http.Request) HttpController {
|
||||
func NewHttpController(container do.Injector, request *http.Request) HttpController {
|
||||
return &HttpHandleController{
|
||||
Controller: &BaseController{container},
|
||||
request: request,
|
||||
|
||||
+2
-2
@@ -4,11 +4,11 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type JWTAuth interface {
|
||||
HttpMiddleware(container *do.Injector) func(next http.Handler) http.Handler
|
||||
HttpMiddleware(container do.Injector) func(next http.Handler) http.Handler
|
||||
Encode(claims map[string]interface{}) (token string, err error)
|
||||
Decode(token string) (jwt.Token, error)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package contracts
|
||||
|
||||
import "net/http"
|
||||
|
||||
type MCPService interface {
|
||||
ServeHTTP(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type MqttService interface {
|
||||
@@ -88,7 +88,7 @@ func (this MqttHandle) OnPublish(g req.GlobalParams, topic string, retain bool,
|
||||
return nil, ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func NewMqttController(container *do.Injector) Mqtt {
|
||||
func NewMqttController(container do.Injector) Mqtt {
|
||||
return &MqttHandle{
|
||||
Controller: &BaseController{container},
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"git.fsdpf.net/go/condition"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/exp"
|
||||
"git.fsdpf.net/go/reflux"
|
||||
"git.fsdpf.net/go/req"
|
||||
)
|
||||
|
||||
@@ -12,39 +15,35 @@ type OrmExecute int
|
||||
|
||||
type RelationType string
|
||||
|
||||
type OrderByDirection string
|
||||
|
||||
const (
|
||||
OrmExecute_Query OrmExecute = iota
|
||||
OrmExecute_Show
|
||||
OrmExecute_Store
|
||||
OrmExecute_Destroy
|
||||
OrmQuery OrmExecute = iota
|
||||
OrmShow
|
||||
OrmStore
|
||||
OrmDestroy
|
||||
)
|
||||
|
||||
const (
|
||||
RelationType_HasOne RelationType = "hasOne"
|
||||
RelationType_HasMany RelationType = "hasMany"
|
||||
RelationType_Inner RelationType = "inner"
|
||||
RelationType_Left RelationType = "left"
|
||||
RelationType_Right RelationType = "right"
|
||||
OrmHasOne RelationType = "hasOne"
|
||||
OrmHasMany RelationType = "hasMany"
|
||||
OrmInnerJoin RelationType = "inner"
|
||||
OrmLeftJoin RelationType = "left"
|
||||
OrmRightJoin RelationType = "right"
|
||||
)
|
||||
|
||||
const (
|
||||
OrderByDirection_ASC OrderByDirection = db.ORDER_ASC
|
||||
OrderByDirection_DESC OrderByDirection = db.ORDER_DESC
|
||||
)
|
||||
type ModelDecorator func(Model) Model
|
||||
|
||||
type Orm interface {
|
||||
SetGlobalParams(g req.GlobalParams) Orm
|
||||
SetGlobalParams(g req.UserAccessor) Orm
|
||||
GetModel(params ...string) Model
|
||||
SetRelationModel(m Model) Model
|
||||
SetQueryField(qf req.QueryField) error
|
||||
SetOrderBy(params []OrderBy) error
|
||||
SetCondition(cond *condition.Condition) error
|
||||
SetController(ctr OrmController) error
|
||||
Execute(OrmExecute) (any, error)
|
||||
Clone(tx *db.Transaction) Orm
|
||||
Count() int
|
||||
Controller() reflect.Value
|
||||
Execute(OrmExecute) (reflux.R, error)
|
||||
Clone(tx *db.TxDatabase) Orm
|
||||
Count() int64
|
||||
// Exists() bool
|
||||
}
|
||||
|
||||
@@ -54,7 +53,6 @@ type Relation interface {
|
||||
Type() RelationType
|
||||
SetType(RelationType)
|
||||
SetCondition(*condition.Condition) *condition.Condition
|
||||
SetPrependCondition(*condition.Condition) *condition.Condition
|
||||
GetCondition() *condition.Condition
|
||||
GetRelationResource() string
|
||||
GetRelationField() string
|
||||
@@ -66,8 +64,9 @@ type Join interface {
|
||||
GetCode() string
|
||||
GetResource() req.Resource
|
||||
// GetDependencies(items []ResRelation, dependencies ...string) []string
|
||||
Inject(dbBuilder *db.Builder, m Model)
|
||||
Inject(dbBuilder *db.SelectDataset, m Model)
|
||||
}
|
||||
|
||||
type Model interface {
|
||||
Relation
|
||||
condition.TokenValue
|
||||
@@ -76,28 +75,30 @@ type Model interface {
|
||||
GetPrimaryKey() string
|
||||
GetQueryFieldsStruct(extends ...reflect.StructField) any
|
||||
GetQueryFieldsSliceStruct(fields ...reflect.StructField) any
|
||||
GetAttribute() map[string]any
|
||||
GetResult() reflect.Value
|
||||
GetAttribute() reflux.R
|
||||
SetRelationAttribute(string, reflux.R) error
|
||||
GetResult() reflux.R
|
||||
GetResource() req.Resource
|
||||
QueryBuilder() *db.Builder
|
||||
GetTransaction() *db.Transaction
|
||||
SetQueryField(req.QueryField)
|
||||
SetRelationResult(code string, vItems reflect.Value)
|
||||
GetTransaction() *db.TxDatabase
|
||||
SetQueryField(...req.QueryField)
|
||||
// SetRelationResult(code string, items []reflux.R)
|
||||
GetJoinsCode() []string
|
||||
SetAuthDB(req.ResAuthDB)
|
||||
SetOrderBy(params ...OrderBy) Model
|
||||
SetGroupBy(params ...GroupBy) Model
|
||||
SetJoin(Join) Model
|
||||
SetLimit(int) Model
|
||||
SetOffset(int) Model
|
||||
SetAttribute(data any) Model
|
||||
SetLimit(uint) Model
|
||||
SetOffset(uint) Model
|
||||
// SetAttribute(data any) Model
|
||||
SetPrimaryKey(string) Model
|
||||
SetWithRecursive(QueryWithRecursive) Model
|
||||
DelQueryField(string)
|
||||
SelectDataset() *db.SelectDataset
|
||||
SelectDatasetWithUser(u req.User) *db.SelectDataset
|
||||
Query() error
|
||||
Store() error
|
||||
Destroy() error
|
||||
Count() (int, error)
|
||||
Count() (int64, error)
|
||||
// Exists() (bool, error)
|
||||
}
|
||||
|
||||
@@ -106,18 +107,18 @@ type OrmController interface {
|
||||
}
|
||||
|
||||
type OrderBy interface {
|
||||
ToSql() db.Expression
|
||||
Inject(dbBuilder *db.Builder, m Model)
|
||||
ToSql() exp.OrderedExpression
|
||||
Inject(sd *db.SelectDataset, m Model)
|
||||
}
|
||||
|
||||
type GroupBy interface {
|
||||
ToSql() db.Expression
|
||||
Inject(dbBuilder *db.Builder, m Model)
|
||||
ToSql() exp.ColumnListExpression
|
||||
Inject(sd *db.SelectDataset, m Model)
|
||||
}
|
||||
|
||||
type QueryWithRecursive interface {
|
||||
ToQueryBuilder(*db.Builder, Model) (*db.Builder, any)
|
||||
ToTreeData(any) any
|
||||
ToQueryBuilder(*db.SelectDataset, Model) (*db.SelectDataset, any)
|
||||
ToTreeData([]reflux.R) reflux.R
|
||||
}
|
||||
|
||||
// @title 创建关联模型
|
||||
@@ -130,18 +131,23 @@ type NewOrmRelation func(t RelationType, m Model, rResource, rField, rForeignKey
|
||||
|
||||
type NewOrmJoin func(t RelationType, res req.Resource, alias, rResource, rField, rForeignKey string) Join
|
||||
|
||||
type NewOrmQueryWithRecursive func(pField, cField string, root any, isWithoutCondition bool, depth int) QueryWithRecursive
|
||||
type NewOrmQueryWithRecursive func(pField, cField exp.IdentifierExpression, root any, isWithoutCondition bool, depth uint) QueryWithRecursive
|
||||
|
||||
type NewOrmOrderBy func(sql string, direction OrderByDirection) OrderBy
|
||||
type NewOrmOrderBy func(sql db.Expression, direction exp.SortDirection) OrderBy
|
||||
|
||||
type NewOrmGroupBy func(sql string) GroupBy
|
||||
type NewOrmGroupBy func(db.Expression) GroupBy
|
||||
|
||||
type NewOrmModel func(res req.Resource, code, name string) Model
|
||||
|
||||
type NewOrm func(res req.Resource, tx *db.Transaction) Orm
|
||||
type NewOrm func(res req.Resource, opts ...OrmOption) Orm
|
||||
|
||||
type GetOrmConditions func(categoryUuid string, opts ...condition.Option) *condition.Condition
|
||||
|
||||
type GetOrmOrderBy func(categoryUuid string) []OrderBy
|
||||
|
||||
type GetOrmGroupBy func(categoryUuid string) []GroupBy
|
||||
|
||||
type OrmOption func(Orm)
|
||||
type WithTx func(tx *db.TxDatabase) OrmOption
|
||||
type WithContext func(ctx context.Context) OrmOption
|
||||
type WithUserAccessor func(ua req.UserAccessor) OrmOption
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ type ResListener interface {
|
||||
}
|
||||
|
||||
type BaseListener struct {
|
||||
Container *do.Injector
|
||||
Container do.Injector
|
||||
res req.Resource // 监听资源
|
||||
code string // code
|
||||
events []string // 监听动作
|
||||
@@ -51,6 +51,6 @@ func (BaseListener) Delete(old map[string]any, u req.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBaseListener(code string, res req.Resource, container *do.Injector) *BaseListener {
|
||||
func NewBaseListener(code string, res req.Resource, container do.Injector) *BaseListener {
|
||||
return &BaseListener{code: code, res: res, Container: container}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package res_type
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 解析map ----------------------------------
|
||||
type ResFieldByMap map[string]any
|
||||
|
||||
func (c *ResFieldByMap) Scan(value any) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(value.([]byte), c); err != nil {
|
||||
return fmt.Errorf("ResFieldByMap json.Unmarshal Error, %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ResFieldByMap) Value() (driver.Value, error) {
|
||||
b, err := json.Marshal(c)
|
||||
return string(b), err
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package res_type
|
||||
|
||||
type ResFieldByFloat float64
|
||||
@@ -1,3 +0,0 @@
|
||||
package res_type
|
||||
|
||||
type ResFieldByInteger int64
|
||||
@@ -1,38 +0,0 @@
|
||||
package res_type
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 切片 ------------------------------------
|
||||
type ResFieldByAnys []any
|
||||
|
||||
func (c *ResFieldByAnys) Scan(value any) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rune('[') != rune(v[0]) {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(value.([]byte), c); err != nil {
|
||||
return fmt.Errorf("ResFieldByAnys json.Unmarshal Error, %s, %s", value, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (c ResFieldByAnys) Value() (driver.Value, error) {
|
||||
b, err := json.Marshal(c)
|
||||
return string(b), err
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package res_type
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// Number 类型 --------------------------------
|
||||
type ResFieldByNumber float64
|
||||
|
||||
func (this *ResFieldByNumber) Scan(value any) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch s := value.(type) {
|
||||
case []byte:
|
||||
*this = ResFieldByNumber(cast.ToFloat64(string(s)))
|
||||
default:
|
||||
*this = ResFieldByNumber(cast.ToFloat64(s))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this ResFieldByNumber) Value() (driver.Value, error) {
|
||||
return this, nil
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package res_type
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 字符串 -------------------------------------
|
||||
type ResFieldByString string
|
||||
|
||||
func (this *ResFieldByString) Scan(value any) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch s := value.(type) {
|
||||
case []byte:
|
||||
*this = ResFieldByString(s)
|
||||
default:
|
||||
*this = ResFieldByString(fmt.Sprintf("%v", s))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c ResFieldByString) Value() (driver.Value, error) {
|
||||
return c, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package contracts
|
||||
|
||||
import (
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/reflux"
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
// ResVirtualTable 虚拟表接口,由业务层实现,框架通过 SQLite vtab 机制调用。
|
||||
// Select 提供列表查询,Fetch 提供单条完整查询(含 Detail 补充字段),
|
||||
// Insert/Update/Delete 支持写操作并自动同步缓存。
|
||||
type ResVirtualTable interface {
|
||||
Controller
|
||||
GetResource() req.Resource
|
||||
|
||||
// Fetch 按主键获取单条完整数据,内部负责调用远端接口并补充 Detail 专属字段。
|
||||
// 框架在 item cache miss 时调用,结果写入 item cache 供后续查询复用。
|
||||
Fetch(pk any) (map[string]any, error)
|
||||
|
||||
// Detail 对已有的 item 数据进行补充,填充列表接口不返回的详情字段。
|
||||
// changed=true 时框架会将修改后的数据回写 item cache。
|
||||
Detail(item reflux.R) (changed bool, err error)
|
||||
|
||||
// Select 查询列表数据,filter 为过滤条件,pagesize/page 控制分页。
|
||||
// 返回的 items 每项为 map[string]any,total 为总记录数。
|
||||
Select(filter reflux.R, pagesize, page int) (items []any, total int64, err error)
|
||||
|
||||
// Insert 新增一条记录,返回新记录的 rowid。
|
||||
Insert(item reflux.R) (rowid int64, err error)
|
||||
|
||||
// Update 按 rowid 更新记录。
|
||||
Update(rowid any, item reflux.R) error
|
||||
|
||||
// Delete 按 rowid 删除记录。
|
||||
Delete(rowid any) error
|
||||
}
|
||||
|
||||
type BaseResVirtualTable struct {
|
||||
Controller
|
||||
|
||||
res req.Resource
|
||||
cache *db.Database
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Cache() *db.Database {
|
||||
// CREATE TABLE IF NOT EXISTS ` + res.Table + ` (
|
||||
// id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
// kind VARCHAR(8) NOT NULL DEFAULT 'list',
|
||||
// key VARCHAR(255) NOT NULL,
|
||||
// lmt INTEGER NOT NULL DEFAULT 0,
|
||||
// page INTEGER NOT NULL DEFAULT 0,
|
||||
// data TEXT NOT NULL,
|
||||
// total INTEGER NOT NULL DEFAULT 0,
|
||||
// expires_at BIGINT NOT NULL,
|
||||
// UNIQUE(kind, key, lmt, page)
|
||||
// )
|
||||
return b.cache
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) GetResource() req.Resource {
|
||||
return b.res
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) GetExtraFields() []req.ResField {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Fetch(pk any) (map[string]any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Detail(detail reflux.R) (changed bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Select(filter reflux.R, pagesize, page int) (items []any, total int64, err error) {
|
||||
return nil, 0, ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Insert(item reflux.R) (rowid int64, err error) {
|
||||
return 0, ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Update(rowid any, item reflux.R) error {
|
||||
return ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func (b *BaseResVirtualTable) Delete(rowid any) error {
|
||||
return ErrFuncNotImplemented
|
||||
}
|
||||
|
||||
func NewBaseResVirtualTable(container do.Injector, res req.Resource, conn *db.Database) ResVirtualTable {
|
||||
return &BaseResVirtualTable{
|
||||
Controller: &BaseController{container},
|
||||
res: res,
|
||||
cache: conn,
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -1,9 +1,14 @@
|
||||
package contracts
|
||||
|
||||
import "git.fsdpf.net/go/req"
|
||||
import (
|
||||
"git.fsdpf.net/go/req"
|
||||
)
|
||||
|
||||
// 资源
|
||||
// GetResource 资源
|
||||
type GetResource func(code string) (req.Resource, bool)
|
||||
|
||||
// 获取用户
|
||||
// MustResource 资源, 没有则抛出错误
|
||||
type MustResource func(code string) req.Resource
|
||||
|
||||
// GetUser 获取用户
|
||||
type GetUser func(uuid string, options ...req.UserRuntimeOption) (req.User, error)
|
||||
|
||||
+5
-4
@@ -8,8 +8,9 @@ import (
|
||||
"git.fsdpf.net/go/condition"
|
||||
"git.fsdpf.net/go/contracts"
|
||||
"git.fsdpf.net/go/db"
|
||||
"git.fsdpf.net/go/db/exp"
|
||||
"git.fsdpf.net/go/utils"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -37,7 +38,7 @@ type OperationAccess struct {
|
||||
data map[string][]any
|
||||
}
|
||||
|
||||
func (o Operation) ToDBColumn(model contracts.Model, container *do.Injector) (column db.Expression, field reflect.StructField) {
|
||||
func (o Operation) ToDBColumn(model contracts.Model, container do.Injector) (column db.Expression, field reflect.StructField) {
|
||||
GetOrmConditions := do.MustInvoke[contracts.GetOrmConditions](container)
|
||||
|
||||
uuid := strings.ReplaceAll(o.Uuid, "-", "_")
|
||||
@@ -52,9 +53,9 @@ func (o Operation) ToDBColumn(model contracts.Model, container *do.Injector) (co
|
||||
}
|
||||
|
||||
if cond.IsEmpty() {
|
||||
column = db.Raw(pk + " as `u_" + uuid + "`")
|
||||
column = db.I(pk).As("u_" + uuid)
|
||||
} else {
|
||||
column = db.Raw("if(" + string(cond.ToSql(model)) + ", " + pk + ", 0) as `u_" + uuid + "`")
|
||||
column = exp.NewSQLFunctionExpression("IF", cond.ToSql(model), db.I(pk), 0).As("u_" + uuid)
|
||||
}
|
||||
|
||||
// 主键类型
|
||||
|
||||
+3
-3
@@ -37,7 +37,7 @@ var wsUpgrader = websocket.Upgrader{
|
||||
|
||||
func (this HttpStatusResponse) Get(path ...string) req.GlobalParams {
|
||||
if this.body == nil {
|
||||
return req.NewGlobalParam("", nil)
|
||||
return req.NewGlobalParams("", nil)
|
||||
}
|
||||
return this.body.Get(path...)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func (this HttpStatusResponse) Send(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (this RawResponse) Get(path ...string) req.GlobalParams {
|
||||
return lo.Ternary(len(path) == 0, req.NewGlobalParam(string(this.raw), nil), req.NewGlobalParam(string(this.raw), nil).Get(strings.Join(path, ".")))
|
||||
return lo.Ternary(len(path) == 0, req.NewGlobalParams(string(this.raw), nil), req.NewGlobalParams(string(this.raw), nil).Get(strings.Join(path, ".")))
|
||||
}
|
||||
|
||||
func (this RawResponse) Send(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -72,7 +72,7 @@ func (this FileResponse) Get(path ...string) req.GlobalParams {
|
||||
"name": this.name,
|
||||
"disposition": this.disposition,
|
||||
}); err == nil {
|
||||
return lo.Ternary(len(path) == 0, req.NewGlobalParam(string(resp), nil), req.NewGlobalParam(string(resp), nil).Get(strings.Join(path, ".")))
|
||||
return lo.Ternary(len(path) == 0, req.NewGlobalParams(string(resp), nil), req.NewGlobalParams(string(resp), nil).Get(strings.Join(path, ".")))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ type UserApproval interface {
|
||||
// 判断发起审批请求权限
|
||||
HasUserRoles(req.User) bool
|
||||
// 获取审批列表标记字段值
|
||||
Execute(id int64, status UserApprovalExecute, r *http.Request, tx *db.Transaction) (current, next UserApprovalFlow, err error)
|
||||
Execute(id int64, status UserApprovalExecute, r *http.Request, tx *db.TxDatabase) (current, next UserApprovalFlow, err error)
|
||||
}
|
||||
|
||||
type UserApprovalFlow interface {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"git.fsdpf.net/go/req"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/samber/do"
|
||||
"github.com/samber/do/v2"
|
||||
)
|
||||
|
||||
type WsClientGroup string
|
||||
@@ -90,7 +90,7 @@ func (WsHandleController) ClientGroup(req.GlobalParams) WsClientGroup {
|
||||
return defaultWsClientGroup
|
||||
}
|
||||
|
||||
func NewWsController(container *do.Injector, request *http.Request, ws WsClient) WsController {
|
||||
func NewWsController(container do.Injector, request *http.Request, ws WsClient) WsController {
|
||||
return &WsHandleController{
|
||||
Controller: &BaseController{container},
|
||||
request: request,
|
||||
|
||||
Reference in New Issue
Block a user