first commit

This commit is contained in:
2023-04-12 16:56:55 +08:00
commit e82ca51b08
53 changed files with 2762 additions and 0 deletions

42
base/data_list.go Normal file
View File

@@ -0,0 +1,42 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataList struct {
Uuid string `db:"uuid"`
ResourceUuid string `db:"resource_uuid"` // 所属资源UUID
Code string `db:"code"` // 列表标识
Name string `db:"name"` // 列表名称
Title string `db:"title"` // 布局标题
PrimaryKey string `db:"primaryKey"` // 主键标识
Layout string `db:"layout"` // 主键标识
IsOperationColumn bool `db:"isOperationColumn"` // 是否显示操作栏,1是,0否
OperationColumnWidth int `db:"operationColumnWidth"` // 操作列宽度
IsPaginate bool `db:"isPaginate"` // 是否分页
IsTreeData bool `db:"isTreeData"` // 是否使用树形列表
TreeDataConfig res_type.ResFieldByMap `db:"treeDataConfig"` // 树形列表配置
IsExpandedRow string `db:"isExpandedRow"` // 列表明细控件
ExpandedRowConfig res_type.ResFieldByMap `db:"expandedRowConfig"` // 列表明细控件配置
IsTreeSider bool `db:"isTreeSider"` // 是否打开侧边栏侧边栏
TreeSiderConfig res_type.ResFieldByMap `db:"treeSiderConfig"` // 侧边栏设置
IsDynamicRefresh bool `db:"isDynamicRefresh"` // 是否启用动态刷新
PageSize int `db:"pagesize"` // 分页大小
IsAuxiliaryPanel bool `db:"isAuxiliaryPanel"` // 是否使用附属面板
AuxiliaryPanelConfig res_type.ResFieldByMap `db:"auxiliaryPanelConfig"` // 附属面板配置
SortConfig res_type.ResFieldByAnys `db:"sortConfig"` // 排序设置
FieldsSort res_type.ResFieldByAnys `db:"fieldsSort"` // 字段排序
GroupConfig res_type.ResFieldByAnys `db:"groupConfig"` // 分组设置
XlsxFieldsSort res_type.ResFieldByAnys `db:"xlsxFieldsSort"` // 字段排序
XlsxImportFieldsSort res_type.ResFieldByAnys `db:"xlsxImportFieldsSort"` // 字段排序
XlsxImportConfig res_type.ResFieldByMap `db:"xlsxImportConfig"` // 导入设置
TabsSort res_type.ResFieldByAnys `db:"tabsSort"` // 标签排序
OperationsSort res_type.ResFieldByAnys `db:"operationsSort"` // 按钮排序
FsmConfig res_type.ResFieldByAnys `db:"fsmConfig"` // FSM设置
ApprovalConfig res_type.ResFieldByAnys `db:"approvalConfig"` // 审批设置
UniqueImportFields res_type.ResFieldByAnys `db:"uniqueImportFields"` // 导入字段唯一效验
Roles res_type.ResFieldByAnys `db:"roles"` // 列表权限
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

34
base/data_list_field.go Normal file
View File

@@ -0,0 +1,34 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataListField struct {
Uuid string `db:"uuid"`
DataListUuid string `db:"data_list_uuid"` // 所属列表 UUID
Title res_type.ResFieldByString `db:"title"` // 列表标题
Code string `db:"code"` // 字段名称
CodeResource string `db:"codeResource"` // 字段所属资源
Alias string `db:"alias"` // 字段别名
IsExpr bool `db:"isExpr"` // 字段名是否为SQL表达式
FieldDataType string `db:"fieldDataType"` // 字段数据类型
Width int `db:"width"` // 列宽
IsWidthFixed bool `db:"isWidthFixed"` // 列宽固定
Fixed bool `db:"fixed"` // 字段冻结
Align string `db:"align"` // 对齐方式
IsShow int `db:"isShow"` // 是否显示
WidgetByJs string `db:"widgetByJs"` // 前端数据渲染控件
WidgetByJsSetting res_type.ResFieldByMap `db:"widgetByJsSetting"` // 前端数据渲染控件设置参数
IsSearch bool `db:"isSearch"` // 允许关键字搜索
IsVirtual bool `db:"isVirtual"` // 是否虚拟字段
IsSort bool `db:"isSort"` // 允许字段排序
EditableByJs string `db:"editableByJs"` // 字段更新组件
EditableByJsSetting res_type.ResFieldByMap `db:"editableByJsSetting"` // 字段更新组件设置
FilterByJs string `db:"filterByJs"` // 字段筛选组件
FilterByJsSetting res_type.ResFieldByMap `db:"filterByJsSetting"` // 字段筛选组件设置参数
FilterOperator string `db:"filterOperator"` // 字段筛选符号
EditableRoles res_type.ResFieldByAnys `db:"editableRoles"` // 字段编辑权限
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

View File

@@ -0,0 +1,24 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataListImportField struct {
Uuid string `db:"uuid"`
DataListUuid string `db:"data_list_uuid"` // 所属列表 UUID
Title res_type.ResFieldByString `db:"title"` // 列表标题
Code string `db:"code"` // 字段名称
CodeResource string `db:"codeResource"` // 字段所属资源
FieldDataType string `db:"fieldDataType"` // 字段数据类型
Width int `db:"width"` // 列宽
Align string `db:"align"` // 对齐方式
IsWidthFixed bool `db:"isWidthFixed"` // 列宽固定
ConvertByJs string `db:"convertByJs"` // 前端数据转换控件
ConvertByJsSetting res_type.ResFieldByMap `db:"convertByJsSetting"` // 前端数据转换控件设置参数
IsVirtual bool `db:"isVirtual"` // 是否虚拟字段
IsValidator int `db:"isValidator"` // 导入字段验证
Validators res_type.ResFieldByMap `db:"validators"` // 导入字段验证规则
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

View File

@@ -0,0 +1,36 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataListOperation struct {
Uuid string `db:"uuid"`
Puuid string `db:"pUuid"`
DataListUuid string `db:"data_list_uuid"` // 所属列表 UUID
Name string `db:"name"` // 按钮名称
Code string `db:"code"` // 按钮标识
Category string `db:"category"` // 按钮类型
Icon string `db:"icon"` // 按钮图标
Size string `db:"size"` // 按钮大小
Block bool `db:"block"` // 固定宽度
Ghost bool `db:"ghost"` // 背景透明
Shape string `db:"shape"` // 按钮形状
Type string `db:"type"` // 按钮样式
IsPopConfirm bool `db:"isPopConfirm"` // 按钮提示
PopTitle string `db:"popTitle"` // 按钮提示
PopOkText string `db:"popOkText"` // 按钮提示
PopCancelText string `db:"popCancelText"` // 按钮提示
PopOkType string `db:"popOkType"` // 按钮提示
PopPlacement string `db:"popPlacement"` // 按钮提示
Widget string `db:"widget"` // 按钮组件
WidgetType string `db:"widgetType"` // 组件类型
WidgetProps res_type.ResFieldByMap `db:"widgetProps"` // 组件PROPS
WidgetSetting res_type.ResFieldByMap `db:"widgetSetting"` // 组件设置
WidgetContainerSetting res_type.ResFieldByMap `db:"widgetContainerSetting"` // 组件容器设置
IsRefresh int `db:"isRefresh"` // 0不刷新, 1刷新所有数据, 2刷新一条数据
NoAuthType string `db:"noAuthType"` // 无按钮权限处理方式
Roles res_type.ResFieldByAnys `db:"roles"` // 按钮权限
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

17
base/data_list_tab.go Normal file
View File

@@ -0,0 +1,17 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataListTab struct {
Uuid string `db:"uuid"`
DataListUuid string `db:"data_list_uuid"` // 所属列表 UUID
Label string `db:"label"` // 标签名
Code string `db:"code"` // 标签标识
Order res_type.ResFieldByAnys `db:"order"` // 排序
UseOrderMode int `db:"useOrderMode"` // 排序模式
IsDefault bool `db:"isDefault"` // 是否默认
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

View File

@@ -0,0 +1,19 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type DataListXlsxField struct {
Uuid string `db:"uuid"`
DataListUuid string `db:"data_list_uuid"` // 所属列表 UUID
Title res_type.ResFieldByString `db:"title"` // 列表标题
Code string `db:"code"` // 字段名称
CodeResource string `db:"codeResource"` // 字段所属资源
Alias string `db:"alias"` // 字段别名
FieldDataType string `db:"fieldDataType"` // 字段数据类型
IsExpr bool `db:"isExpr"` // 字段名是否为SQL表达式
IsShow bool `db:"isShow"` // 字段名是否为SQL表达式
ConvertByJs string `db:"convertByJs"` // 字段数据转换组件
ConvertByJsSetting res_type.ResFieldByMap `db:"convertByJsSetting"` // 字段数据转换组件参数
}

31
base/fsm_transition.go Normal file
View File

@@ -0,0 +1,31 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type FsmTransition struct {
Uuid string `db:"uuid"`
FsmUuid string `db:"fsm_uuid"`
Name string `db:"name"`
Code string `db:"code"`
From string `db:"from"`
To string `db:"to"`
ResourceUuid string `db:"resource_uuid"`
FsmName string `db:"fsm_name"`
PrimaryKey string `db:"primaryKey"`
StatusField string `db:"statusField"`
StatusOptions res_type.ResFieldByAnys `db:"statusOptions"`
IsPopConfirm bool `db:"isPopConfirm"`
PopTitle string `db:"popTitle"`
PopOkText string `db:"popOkText"`
PopCancelText string `db:"popCancelText"`
PopOkType string `db:"popOkType"`
PopPlacement string `db:"popPlacement"`
WidgetContainerSetting res_type.ResFieldByMap `db:"widgetContainerSetting"`
Roles res_type.ResFieldByAnys `db:"roles"`
OperationRoles res_type.ResFieldByAnys `db:"operationRoles"`
IsWithLayoutForm bool `db:"isWithLayoutForm"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

25
base/layout.go Normal file
View File

@@ -0,0 +1,25 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type Layout struct {
Uuid string `db:"uuid"`
ResourceUuid string `db:"resource_uuid"` // 所属资源UUID
Code string `db:"code"` // 布局标识
Name string `db:"name"` // 布局名称
Type int `db:"type"` // 布局类型
Align string `db:"align"` // 方向
PrimaryKey string `db:"primaryKey"` // 主键
AutoComplete string `db:"autoComplete"` // 自动填充表单, off | on
RowHeight int `db:"rowHeight"` // 栅格行高
MarginX int `db:"marginX"` // 栅格 x 间距
MarginY int `db:"marginY"` // 栅格 y 间距
FormProps res_type.ResFieldByAnys `db:"formProps"` // 表单PROPS
ListenChangeFields res_type.ResFieldByAnys `db:"listenChangeFields"` // 监听字段
ListenChangeFieldsFunc res_type.ResFieldByString `db:"listenChangeFieldsFunc"` // 监听字段回调方法
Roles res_type.ResFieldByAnys `db:"roles"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

35
base/layout_field.go Normal file
View File

@@ -0,0 +1,35 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type LayoutField struct {
Uuid string `db:"uuid"`
LayoutUuid string `db:"layout_uuid"` // 所属布局 UUID
Type string `db:"type"`
X int `db:"x"`
Y int `db:"y"`
W int `db:"w"`
H int `db:"h"`
MinH int `db:"minH"`
MinW int `db:"minW"`
MaxH int `db:"maxH"`
MaxW int `db:"maxW"`
Code string `db:"code"`
Label string `db:"label"`
Widget string `db:"widget"`
WidgetPerfix string `db:"widgetPerfix"`
WidgetDecorator res_type.ResFieldByString `db:"widgetDecorator"`
SubWidgets res_type.ResFieldByAnys `db:"subWidgets"`
Placeholder string `db:"placeholder"`
Help string `db:"help"`
Disabled int `db:"disabled"`
InitialValue string `db:"initialValue"`
Extras res_type.ResFieldByMap `db:"extras"`
Validators res_type.ResFieldByMap `db:"validators"`
Hidden bool `db:"hidden"`
IsVirtual bool `db:"isVirtual"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

23
base/menu.go Normal file
View File

@@ -0,0 +1,23 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type Menu struct {
Uuid string `db:"uuid"`
Id int `db:"id"`
Pid int `db:"pid"`
Type string `db:"type"`
Name string `db:"name"`
Icon string `db:"icon"`
Widget string `db:"widget"`
WidgetType string `db:"widgetType"`
WidgetProps res_type.ResFieldByMap `db:"widgetProps"`
WidgetSetting res_type.ResFieldByMap `db:"widgetSetting"`
WidgetContainerSetting res_type.ResFieldByMap `db:"widgetContainerSetting"`
Roles res_type.ResFieldByAnys `db:"roles"`
Platform string `db:"platform"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

17
base/print_template.go Normal file
View File

@@ -0,0 +1,17 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type PrintTemplate struct {
Uuid string `db:"uuid"`
ResourceUuid string `db:"resource_uuid"` // 所属资源UUID
Code string `db:"code"` // 打印模板标识
Name string `db:"name"` // 打印模板名称
PrimaryKey string `db:"primaryKey"` // 主键标识
Roles res_type.ResFieldByAnys `db:"roles"` // 打印权限
Template res_type.ResFieldByMap `db:"template"` // 打印权限
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

View File

@@ -0,0 +1,21 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type PrintTemplateField struct {
Uuid string `db:"uuid"`
PrintTemplateUuid string `db:"print_template_uuid"` // 所属打印模板UUID
Label res_type.ResFieldByString `db:"label"` // 字段标签
Code string `db:"code"` // 字段名称
CodeResource string `db:"codeResource"` // 字段所属资源
Category string `db:"category"` // 字段控件类型
Alias string `db:"alias"` // 字段别名
IsExpr bool `db:"isExpr"` // 字段名是否为SQL表达式
FieldDataType string `db:"fieldDataType"` // 字段数据类型
WidgetByJs string `db:"widgetByJs"` // 前端数据渲染控件
WidgetByJsSetting res_type.ResFieldByMap `db:"widgetByJsSetting"` // 前端数据渲染控件设置参数
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

19
base/res_api.go Normal file
View File

@@ -0,0 +1,19 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type ResApi struct {
Uuid string `db:"uuid"`
Code string `db:"code"`
Name string `db:"name"`
PrimaryKey string `db:"primaryKey"`
ResourceUuid string `db:"resource_uuid"`
Method string `db:"method"`
Category string `db:"action"`
Params res_type.ResFieldByAnys `db:"params"`
Roles res_type.ResFieldByAnys `db:"roles"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

18
base/res_api_approval.go Normal file
View File

@@ -0,0 +1,18 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type ResApiApproval struct {
Uuid string `db:"uuid"`
ApiUuid string `db:"api_uuid"`
Code string `db:"code"`
Name string `db:"name"`
MarkField string `db:"markField"`
RolesUuid res_type.ResFieldByAnys `db:"rolesUuid"`
Flows res_type.ResFieldByAnys `db:"flows"`
Roles res_type.ResFieldByAnys `db:"roles"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

13
base/res_api_field.go Normal file
View File

@@ -0,0 +1,13 @@
package base
type ResApiField struct {
Uuid string `db:"id"`
ApiUuid string `db:"api_uuid"`
Code string `db:"code"` // 字段名称
CodeResource string `db:"resource"` // 字段所属资源
Alias string `db:"alias"` // 字段别名
IsExpr bool `db:"isExpr"` // 字段名是否为SQL表达式
FieldDataType string `db:"fieldDataType"` // 字段数据类型
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

19
base/res_condition.go Normal file
View File

@@ -0,0 +1,19 @@
package base
type ResCondition struct {
Uuid string `db:"id"`
Id int `db:"id"`
Pid int `db:"pid"`
Type string `db:"type"`
CategoryUuid string `db:"category_uuid"`
Column string `db:"column"`
ColumnResource string `db:"columnResource"`
Operator string `db:"operator"`
ColumnSqlFunc string `db:"columnSqlFunc"`
ColumnSqlFuncParam string `db:"columnSqlFuncParam"`
IgnoreEmptyParma bool `db:"ignoreEmptyParma"`
ValueType string `db:"valueType"`
Value string `db:"value"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

14
base/res_configure.go Normal file
View File

@@ -0,0 +1,14 @@
package base
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"`
}

288
base/res_field.go Normal file
View File

@@ -0,0 +1,288 @@
package base
import (
"database/sql/driver"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/spf13/cast"
"git.fsdpf.net/go/contracts"
"git.fsdpf.net/go/contracts/res_type"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/db/schema"
)
// 资源字段
type ResField struct {
Uuid string `db:"uuid"`
Name string `db:"name"`
Code string `db:"code"`
CodeResource string `db:"codeResource"`
DataType res_type.ResDataType `db:"table_type"`
Length string `db:"length"`
Comment string `db:"comment"`
Default string `db:"default"`
}
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
}
func (this ResField) ToStructField(tags ...string) reflect.StructField {
var typ reflect.Type
fCode := this.Code
fTag := `db:"` + fCode + `" json:"` + fCode + `"`
if len(tags) > 0 {
fTag = strings.Join(tags, " ") + " " + fTag
}
switch this.DataType {
case contracts.ResDataType_String, contracts.ResDataType_Text, contracts.ResDataType_Enum,
contracts.ResDataType_Timestamp, contracts.ResDataType_Date, contracts.ResDataType_Datetime:
typ = reflect.TypeOf(res_type.ResFieldByString(""))
case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger:
typ = reflect.TypeOf(res_type.ResFieldByInteger(0))
case contracts.ResDataType_Decimal:
typ = reflect.TypeOf(float64(0))
case contracts.ResDataType_Boolean:
typ = reflect.TypeOf(true)
case contracts.ResDataType_Json:
if this.Default != "" && this.Default[0:1] == "[" {
typ = reflect.TypeOf(res_type.ResFieldByAnys{})
} else {
typ = reflect.TypeOf(res_type.ResFieldByMap{})
}
}
return reflect.StructField{
Name: strings.ToUpper(fCode[:1]) + fCode[1:],
Tag: reflect.StructTag(fTag),
Type: typ,
}
}
func (this ResField) GetCode() string {
return this.Code
}
func (this ResField) GetCodeResource() string {
return this.CodeResource
}
func (this ResField) GetName() string {
return this.Name
}
func (this ResField) GetDataType() contracts.ResDataType {
return this.DataType
}
func (this ResField) GetQueryDataType() contracts.QueryDataType {
switch this.GetDataType() {
case contracts.ResDataType_Enum,
contracts.ResDataType_Timestamp, contracts.ResDataType_Date,
contracts.ResDataType_Datetime, contracts.ResDataType_String,
contracts.ResDataType_Text:
return contracts.QueryDataType_String
case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger:
return contracts.QueryDataType_Integer
case contracts.ResDataType_Decimal:
return contracts.QueryDataType_Float
case contracts.ResDataType_Boolean:
return contracts.QueryDataType_Bool
case contracts.ResDataType_Json:
if this.Default != "" && this.Default[0:1] == "[" {
return contracts.QueryDataType_Array
}
return contracts.QueryDataType_Json
}
return contracts.QueryDataType_String
}
func (this ResField) ToValue(v any) any {
// 类型转换, 填补 cast 未知的类型
switch data := v.(type) {
case res_type.ResFieldByInteger:
v = int64(data)
case res_type.ResFieldByFloat:
v = float64(data)
case res_type.ResFieldByNumber:
v = float64(data)
case res_type.ResFieldByString:
v = strings.Trim(string(data), " ")
case res_type.ResFieldByAnys:
if v != nil {
b, _ := json.Marshal(v)
return string(b)
}
v = []any(data)
case res_type.ResFieldByMap:
if v != nil {
b, _ := json.Marshal(v)
return string(b)
}
v = map[string]any(data)
case []any, []string, []int, []float64, []float32, []int64:
if v != nil {
b, _ := json.Marshal(v)
return string(b)
}
v = data
case map[string]any, map[string]string, map[string]int:
if v != nil {
b, _ := json.Marshal(v)
return string(b)
}
v = data
}
switch this.DataType {
case contracts.ResDataType_String, contracts.ResDataType_Text, contracts.ResDataType_Enum,
contracts.ResDataType_Timestamp, contracts.ResDataType_Date, contracts.ResDataType_Datetime:
return strings.Trim(cast.ToString(v), " ")
case contracts.ResDataType_Integer, contracts.ResDataType_SmallInteger:
return cast.ToInt(v)
case contracts.ResDataType_Decimal:
return strings.Trim(cast.ToString(v), " ")
case contracts.ResDataType_Boolean:
v, _ := strconv.ParseBool(fmt.Sprintf("%v", v))
return v
case contracts.ResDataType_Json:
if v == nil {
if this.Default != "" && this.Default[0:1] == "[" {
return "[]"
} else if this.Default != "" && this.Default[0:1] == "{" {
return "{}"
} else if this.Default == "" {
return "{}"
}
return this.Default
}
b, _ := json.Marshal(v)
return string(b)
}
return strings.Trim(cast.ToString(v), " ")
}
func (this ResField) GetRawDefault(driver string) db.Expression {
if this.DataType == "json" && this.Default == "" {
return db.Raw("'{}'")
}
if len(this.Default) > 4 && strings.ToLower(this.Default[0:4]) == "sql:" {
sql := strings.ToLower(this.Default[4:])
if sql == "uuid()" {
return Uuid(driver)
}
return db.Raw(sql)
}
if this.Default == "" {
if this.GetDataType() == contracts.ResDataType_Date || this.GetDataType() == contracts.ResDataType_Datetime {
return db.Raw("NULL")
}
return db.Raw("''")
}
if strings.ToUpper(this.Default) == "CURRENT_TIMESTAMP" {
return db.Raw(this.Default)
}
return db.Raw("'" + this.Default + "'")
}
func (this ResField) ToBlueprint(table *schema.Blueprint) (temp *schema.ColumnDefinition) {
isNull := false
comment := this.Name
def := any(this.Default)
if this.Comment != "" {
comment += " [ " + strings.Trim(this.Comment, `' "`) + " ]"
}
switch this.DataType {
case "string":
len := 255
if v, err := strconv.Atoi(strings.Trim(this.Length, `' "`)); err == nil {
len = v
}
temp = table.String(this.Code, len)
case "smallInteger":
// integer 默认长度 4
temp = table.SmallInteger(this.Code)
case "boolean":
// integer 默认长度 1
temp = table.Boolean(this.Code)
case "integer":
// integer 默认长度 11
temp = table.Integer(this.Code)
case "date", "dateTime":
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)
} else if def == "" {
isNull = true
}
case "decimal":
allowed := strings.SplitN(this.Length, ",", 2)
total := 8
places := 2
if v, err := strconv.Atoi(strings.Trim(allowed[0], `' "`)); err == nil {
total = v
}
if v, err := strconv.Atoi(strings.Trim(allowed[1], `' "`)); err == nil {
places = v
}
temp = table.Decimal(this.Code, total, places)
case "enum":
allowed := []string{}
for _, v := range strings.Split(this.Length, ",") {
allowed = append(allowed, strings.Trim(v, `' "`))
}
temp = table.Enum(this.Code, allowed)
case "json":
temp = table.Json(this.Code)
isNull = true
case "text":
temp = table.Text(this.Code)
isNull = true
}
if isNull {
temp.Nullable()
} else {
temp.Default(def)
}
temp.Comment(comment)
return temp
}

View File

@@ -0,0 +1,14 @@
package base
import (
"github.com/google/uuid"
"git.fsdpf.net/go/db"
)
func Uuid(driver string) db.Expression {
if driver == "sqlite" {
return db.Raw("'" + uuid.NewString() + "'")
}
return db.Raw("uuid()")
}

15
base/res_listener.go Normal file
View File

@@ -0,0 +1,15 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type ResListener struct {
Uuid string `db:"uuid"`
Code string `db:"code"`
Name string `db:"name"`
ResourceUuid string `db:"resource_uuid"`
Event res_type.ResFieldByAnys `db:"event"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

15
base/res_relation.go Normal file
View File

@@ -0,0 +1,15 @@
package base
type ResRelation struct {
Uuid string `db:"uuid"`
CategoryUuid string `db:"category_uuid"` // 所属 UUID
Name string `db:"name"` // 关联名称
Code string `db:"code"` // 关联标识
Type string `db:"type"` // 关联类型
ResourceCode string `db:"actuallyResource"` // 实际资源资源
RelationForeignKey string `db:"actuallyField"` // 实际资源资源字段
RelationResource string `db:"relationResource"` // 被关联的资源
RelationField string `db:"relationField"` // 被关联的字段
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

9
base/res_role.go Normal file
View File

@@ -0,0 +1,9 @@
package base
type ResRole struct {
Uuid string `db:"id"`
ResUuid string `db:"resource_uuid"`
RoleUuid string `db:"role_uuid"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

View File

@@ -0,0 +1,17 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type ResWidgetOperation struct {
Uuid string `db:"uuid"`
WidgetUuid string `db:"widget_uuid"`
Name string `db:"name"`
NoAuthType string `db:"noAuthType"`
PropsAuthRule res_type.ResFieldByAnys `db:"propsAuthRule"`
ExtraAuthRule res_type.ResFieldByAnys `db:"extraAuthRule"`
Roles res_type.ResFieldByAnys `db:"roles"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

340
base/resource.go Normal file
View File

@@ -0,0 +1,340 @@
package base
import (
"database/sql"
"log"
"reflect"
"unicode"
"github.com/samber/do"
"github.com/samber/lo"
"git.fsdpf.net/go/contracts"
"git.fsdpf.net/go/contracts/res_type"
"git.fsdpf.net/go/db"
"git.fsdpf.net/go/queue"
)
// 资源
type Resource struct {
container *do.Injector
Uuid string `db:"uuid"`
PUuid string `db:"pUuid"`
Code string `db:"code"`
Name string `db:"name"`
IsResVirtual bool `db:"isVirtual"`
Table string `db:"table"`
Namespace string `db:"namespace"`
Workspace string `db:"workspace"`
Primarykey string `db:"primaryKey"`
IsHistoryRecord bool `db:"isHistoryRecord"`
HistoryCacheMax int `db:"historyCacheMax"`
Fields ResFields `db:"fields"`
Roles res_type.ResFieldByMap `db:"roles"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}
// 资源UUID
func (this Resource) GetUuid() string {
return this.Uuid
}
// 资源CODE
func (this Resource) GetCode() string {
return this.Code
}
// 资源名
func (this Resource) GetName() string {
return this.Name
}
// 主键
func (this Resource) GetPrimarykey() string {
return this.Primarykey
}
// 是否虚拟资源
func (this Resource) IsVirtual() bool {
return this.IsResVirtual
}
// 是否系统资源
func (this Resource) IsSystem() bool {
return this.Namespace == "Framework\\Service"
}
// 资源字段
func (this Resource) GetFields() (result []contracts.ResField) {
for _, item := range this.Fields {
result = append(result, item)
}
return result
}
// 资源字段
func (this Resource) GetField(code string) (contracts.ResField, bool) {
return lo.Find(this.GetFields(), func(v contracts.ResField) bool {
return v.GetCode() == code
})
}
// 判断资源字段
func (this Resource) HasField(code string) bool {
return lo.SomeBy(this.GetFields(), func(v contracts.ResField) bool {
return v.GetCode() == code
})
}
// 开启事物
func (this Resource) BeginTransaction() (*db.Transaction, error) {
return this.GetDB().Connection.BeginTransaction()
}
// 获取资源对应的数据库连接
func (this Resource) GetDB() *db.Builder {
db := do.MustInvoke[db.DB](this.container)
if this.IsSystem() {
return db.Connection("service-support").Query()
}
return db.Connection("default").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.GetDB().Connection.GetConfig().Driver
}
func (this Resource) GetAuthDBTable(u contracts.User, params ...any) *db.Builder {
this.GetRolesCondition(u)
return this.GetDBTable(append(params, u)...)
}
// GetDBTable("Test", contracts.User)
func (this Resource) GetDBTable(params ...any) *db.Builder {
builder := this.GetDB()
var user contracts.User
alias := this.Code
for _, param := range params {
switch v := param.(type) {
case *db.Transaction:
builder.Tx = v
case string:
alias = v
case contracts.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)
}
}
})
// 资源事件
this.onResEvent(builder)
// 用户事件
if this.IsHistoryRecord {
this.onUserEvent(builder, user)
}
// 虚拟资源暂时不考虑鉴权
if !this.IsVirtual() {
// 返回鉴权后的 DB Builder
// return
}
return builder.Table(string(this.GetTable()), alias)
}
func (this Resource) WithRolesCondition(b *db.Builder, roles ...string) {
}
// 获取鉴权条件
func (this Resource) GetRolesCondition(u contracts.User) {
// isFullRight := false
// isFullNot := false
// roles := do.MustInvoke[GetResRoles](this.container)(this.GetUuid())
// GetResRelations := do.MustInvoke[GetResRelations](this.container)
// subTables := lo.Reduce(roles, func(carry *db.Builder, item ResRole, _ int) *db.Builder {
// db := this.GetDB().Table(string(this.GetTable()), this.GetCode()).Select("`" + this.GetCode() + "`.*")
// joins := lo.Filter(GetResRelations(item.Uuid), func(item ResRelation, _ int) bool {
// return item.Type == "inner" || item.Type == "left" || item.Type == "right"
// })
// join := orm.NewJoin(contracts.RelationType(item.Type), item.Code, oResource, item.RelationResource, item.RelationField, item.RelationForeignKey)
// // 关联扩展条件
// join.SetCondition(orm.NewConditionByRes(GetResConditions(item.Uuid)))
// return carry
// }, nil)
// fmt.Println(subTables.ToSql())
}
// 格式化保存数据
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 contracts.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 {
}
}
func (this Resource) GetStruct(extends ...reflect.StructField) any {
fields := []reflect.StructField{}
for _, field := range this.Fields {
if unicode.IsLetter(rune(field.Code[0])) {
fields = append(fields, field.ToStructField())
} else {
log.Printf("资源字段错误, 必须以字母开头 <- %s", field.Code)
}
}
fields = lo.UniqBy(append(fields, extends...), func(v reflect.StructField) string {
return v.Name
})
t := reflect.StructOf(fields)
return reflect.New(t).Interface()
}
func (this Resource) GetSliceStruct(extends ...reflect.StructField) any {
t := reflect.TypeOf(this.GetStruct(extends...))
st := reflect.SliceOf(t.Elem())
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
}
// 全局触发器
// 1. 清除系统缓存
if err := do.MustInvoke[contracts.Queue](this.container).Publish(contracts.ResQueueTopic, contracts.ResQueuePayload{
Type: t,
Res: this,
Result: result,
}); err != nil {
log.Println("Queue Publish Err:", contracts.ResQueueTopic, err)
}
})
}
// 用户事件
func (this Resource) onUserEvent(builder *db.Builder, user contracts.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.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 err := do.MustInvoke[contracts.Queue](this.container).Publish(contracts.UserQueuetTopic, contracts.UserQueuePayload{
Type: t,
User: user,
Res: this,
Old: old,
New: data,
Result: result,
}); err != nil {
log.Println("Queue Publish Err:", contracts.UserQueuetTopic, err)
}
})
}

23
base/route.go Normal file
View File

@@ -0,0 +1,23 @@
package base
import (
"git.fsdpf.net/go/contracts/res_type"
)
type Route struct {
Uuid string `db:"uuid"`
Name string `db:"name"`
Uri string `db:"uri"`
Layout string `db:"layout"`
IsLogin bool `db:"isLogin"`
IsPhone bool `db:"isPhone"`
Type string `db:"type"`
Component string `db:"component"`
WidgetProps res_type.ResFieldByMap `db:"widgetProps"`
WidgetSetting res_type.ResFieldByMap `db:"widgetSetting"`
WidgetContainerSetting res_type.ResFieldByMap `db:"widgetContainerSetting"`
Exact bool `db:"exact"`
Platform string `db:"platform"`
UpdatedAt string `db:"updated_at"`
CreatedAt string `db:"created_at"`
}

81
base/user.go Normal file
View File

@@ -0,0 +1,81 @@
package base
import (
"reflect"
"github.com/samber/lo"
"git.fsdpf.net/go/contracts"
)
type user struct {
id int
uuid string
username string
nickname string
platform string
roles []string
}
func (this user) ID() int {
return this.id
}
func (this user) Uuid() string {
return this.uuid
}
func (this user) Username() string {
return this.username
}
func (this user) Nickname() string {
return this.nickname
}
func (this user) GetUserInfo() any {
return nil
}
func (this user) Roles() (roles []string) {
return this.roles
}
func (this user) HasUserRoles(roles ...string) bool {
return lo.Contains(this.Roles(), "ffffffff-ffff-ffff-ffff-ffffffffffff") || len(lo.Intersect(this.Roles(), roles)) > 0
}
func (this user) IsAnonymous() bool {
return reflect.DeepEqual(this.Roles(), []string{"00000000-0000-0000-0000-000000000000"})
}
func (this user) Platform() string {
if this.platform == "" {
return "unknown"
}
return this.platform
}
// 获取匿名用户
func GetAnonymous() contracts.User {
return user{
id: 0,
uuid: "00000000-0000-0000-0000-000000000000",
username: "anonymous",
nickname: "匿名者",
roles: []string{"00000000-0000-0000-0000-000000000000"},
platform: "unknown",
}
}
// 系统用户
func GetSystemUser() contracts.User {
return user{
id: -1,
uuid: "ffffffff-ffff-ffff-ffff-ffffffffffff",
username: "system",
nickname: "系统",
roles: []string{"ffffffff-ffff-ffff-ffff-ffffffffffff"},
platform: "unknown",
}
}