mirror of
https://github.com/veops/oneterm.git
synced 2025-09-26 19:31:14 +08:00
refactor: remove cte
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -18,53 +19,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
assetPostHooks = []postHook[*model.Asset]{
|
||||
func(ctx *gin.Context, data []*model.Asset) {
|
||||
post := make([]*model.AssetNodeChain, 0)
|
||||
if err := mysql.DB.
|
||||
Model(&model.Node{}).
|
||||
Raw(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT id, name AS chain
|
||||
FROM node
|
||||
WHERE
|
||||
deleted_at = 0
|
||||
AND parent_id = 0
|
||||
UNION ALL
|
||||
SELECT
|
||||
t.id,
|
||||
CONCAT(cte.chain, '/', t.name)
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.id = t.parent_id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT *
|
||||
FROM cte
|
||||
`).
|
||||
Find(&post).
|
||||
Error; err != nil {
|
||||
logger.L.Error("asset posthookfailed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
m := lo.SliceToMap(post, func(p *model.AssetNodeChain) (int, string) { return p.NodeId, p.Chain })
|
||||
for _, d := range data {
|
||||
d.NodeChain = m[d.ParentId]
|
||||
}
|
||||
}, func(ctx *gin.Context, data []*model.Asset) {
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if acl.IsAdmin(currentUser) {
|
||||
return
|
||||
}
|
||||
for _, a := range data {
|
||||
for k, v := range a.Authorization {
|
||||
if lo.Contains(v, currentUser.GetRid()) {
|
||||
continue
|
||||
}
|
||||
delete(a.Authorization, k)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
assetPostHooks = []postHook[*model.Asset]{assetPostHookCount, assetPostHookAuth}
|
||||
)
|
||||
|
||||
// CreateAsset godoc
|
||||
@@ -127,30 +82,11 @@ func (c *Controller) GetAssets(ctx *gin.Context) {
|
||||
db = db.Where("id IN ?", lo.Map(strings.Split(q, ","), func(s string, _ int) int { return cast.ToInt(s) }))
|
||||
}
|
||||
if q, ok := ctx.GetQuery("parent_id"); ok {
|
||||
parentIds := make([]int, 0)
|
||||
if err := mysql.DB.
|
||||
Model(&model.Node{}).
|
||||
Raw(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT id
|
||||
FROM node
|
||||
WHERE deleted_at = 0
|
||||
AND parent_id = ?
|
||||
UNION ALL
|
||||
SELECT t.id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.id = t.parent_id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT id
|
||||
FROM cte;
|
||||
`, q).
|
||||
Find(&parentIds).
|
||||
Error; err != nil {
|
||||
parentIds, err := handleParentId(cast.ToInt(q))
|
||||
if err != nil {
|
||||
logger.L.Error("parent id found failed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
parentIds = append(parentIds, cast.ToInt(q))
|
||||
db = db.Where("parent_id IN ?", parentIds)
|
||||
}
|
||||
|
||||
@@ -220,3 +156,67 @@ func (c *Controller) UpdateByServer(ctx *gin.Context) {
|
||||
|
||||
ctx.JSON(http.StatusOK, defaultHttpResponse)
|
||||
}
|
||||
|
||||
func assetPostHookCount(ctx *gin.Context, data []*model.Asset) {
|
||||
nodes := make([]*model.NodeIdPidName, 0)
|
||||
if err := mysql.DB.
|
||||
Model(nodes).
|
||||
Find(&nodes).
|
||||
Error; err != nil {
|
||||
logger.L.Error("asset posthookfailed", zap.Error(err))
|
||||
return
|
||||
}
|
||||
g := make(map[int][]model.Pair[int, string])
|
||||
for _, n := range nodes {
|
||||
g[n.ParentId] = append(g[n.ParentId], model.Pair[int, string]{First: n.Id, Second: n.Name})
|
||||
}
|
||||
m := make(map[int]string)
|
||||
var dfs func(int, string)
|
||||
dfs = func(x int, s string) {
|
||||
m[x] = s
|
||||
for _, node := range g[x] {
|
||||
dfs(node.First, fmt.Sprintf("%s/%s", s, node.Second))
|
||||
}
|
||||
}
|
||||
dfs(0, "")
|
||||
|
||||
for _, d := range data {
|
||||
d.NodeChain = m[d.ParentId]
|
||||
}
|
||||
}
|
||||
|
||||
func assetPostHookAuth(ctx *gin.Context, data []*model.Asset) {
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if acl.IsAdmin(currentUser) {
|
||||
return
|
||||
}
|
||||
for _, a := range data {
|
||||
for k, v := range a.Authorization {
|
||||
if lo.Contains(v, currentUser.GetRid()) {
|
||||
continue
|
||||
}
|
||||
delete(a.Authorization, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleParentId(parentId int) (pids []int, err error) {
|
||||
nodes := make([]*model.NodeIdPid, 0)
|
||||
if err = mysql.DB.Model(nodes).Find(&nodes).Error; err != nil {
|
||||
return
|
||||
}
|
||||
g := make(map[int][]int)
|
||||
for _, n := range nodes {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int)
|
||||
dfs = func(x int) {
|
||||
pids = append(pids, x)
|
||||
for _, y := range g[x] {
|
||||
dfs(y)
|
||||
}
|
||||
}
|
||||
dfs(parentId)
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -19,103 +18,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
nodePreHooks = []preHook[*model.Node]{
|
||||
func(ctx *gin.Context, data *model.Node) {
|
||||
ids := make([]int, 0)
|
||||
if err := mysql.DB.Raw(fmt.Sprintf(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT id
|
||||
FROM node
|
||||
WHERE id=%s AND deleted_at = 0
|
||||
UNION ALL
|
||||
SELECT t.id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.id = t.parent_id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT
|
||||
id
|
||||
FROM cte
|
||||
`, ctx.Param("id"))).
|
||||
Find(&ids).
|
||||
Error; err != nil || lo.Contains(ids, data.ParentId) {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInvalidArgument})
|
||||
}
|
||||
},
|
||||
}
|
||||
nodePostHooks = []postHook[*model.Node]{
|
||||
func(ctx *gin.Context, data []*model.Node) {
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
isAdmin := acl.IsAdmin(currentUser)
|
||||
post := make([]*model.NodeCount, 0)
|
||||
sql := fmt.Sprintf(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT parent_id
|
||||
FROM asset
|
||||
%s
|
||||
UNION ALL
|
||||
SELECT t.parent_id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.parent_id = t.id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT
|
||||
parent_id,
|
||||
COUNT(*) AS count
|
||||
FROM cte
|
||||
GROUP BY parent_id
|
||||
`, lo.Ternary(isAdmin, "WHERE deleted_at = 0", "WHERE deleted_at = 0 AND id IN (?)"))
|
||||
db := mysql.DB.
|
||||
Model(&model.Asset{})
|
||||
if isAdmin {
|
||||
db = db.Raw(sql)
|
||||
} else {
|
||||
authorizationResourceIds, err := GetAutorizationResourceIds(ctx)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
db = db.Raw(sql, mysql.DB.Model(&model.Authorization{}).Select("asset_id").Where("resource_id IN ?", authorizationResourceIds))
|
||||
}
|
||||
if err := db.
|
||||
Find(&post).
|
||||
Error; err != nil {
|
||||
logger.L.Error("node posthookfailed asset count", zap.Error(err))
|
||||
return
|
||||
}
|
||||
m := lo.SliceToMap(post, func(p *model.NodeCount) (int, int64) { return p.ParentId, p.Count })
|
||||
for _, d := range data {
|
||||
d.AssetCount = m[d.Id]
|
||||
}
|
||||
}, func(ctx *gin.Context, data []*model.Node) {
|
||||
ps := make([]int, 0)
|
||||
if err := mysql.DB.
|
||||
Model(&model.Node{}).
|
||||
Where("parent_id IN ?", lo.Map(data, func(n *model.Node, _ int) int { return n.Id })).
|
||||
Pluck("parent_id", &ps).
|
||||
Error; err != nil {
|
||||
logger.L.Error("node posthookfailed has child", zap.Error(err))
|
||||
return
|
||||
}
|
||||
pm := lo.SliceToMap(ps, func(pid int) (int, bool) { return pid, true })
|
||||
for _, n := range data {
|
||||
n.HasChild = pm[n.Id]
|
||||
}
|
||||
},
|
||||
}
|
||||
nodeDcs = []deleteCheck{
|
||||
func(ctx *gin.Context, id int) {
|
||||
noChild := true
|
||||
noChild = noChild && errors.Is(mysql.DB.Model(&model.Node{}).Select("id").Where("parent_id = ?", id).First(map[string]any{}).Error, gorm.ErrRecordNotFound)
|
||||
noChild = noChild && errors.Is(mysql.DB.Model(&model.Asset{}).Select("id").Where("parent_id = ?", id).First(map[string]any{}).Error, gorm.ErrRecordNotFound)
|
||||
if noChild {
|
||||
return
|
||||
}
|
||||
|
||||
err := &ApiError{Code: ErrHasChild, Data: nil}
|
||||
ctx.AbortWithError(http.StatusBadRequest, err)
|
||||
},
|
||||
}
|
||||
nodePreHooks = []preHook[*model.Node]{nodePreHookCheckCycle}
|
||||
nodePostHooks = []postHook[*model.Node]{nodePostHookCountAsset, nodePostHookHasChild}
|
||||
nodeDcs = []deleteCheck{nodeDelHook}
|
||||
)
|
||||
|
||||
// CreateNode godoc
|
||||
@@ -172,46 +77,151 @@ func (c *Controller) GetNodes(ctx *gin.Context) {
|
||||
db = db.Where("id IN ?", lo.Map(strings.Split(q, ","), func(s string, _ int) int { return cast.ToInt(s) }))
|
||||
}
|
||||
if id, ok := ctx.GetQuery("no_self_child"); ok {
|
||||
sql := fmt.Sprintf(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT id
|
||||
FROM node
|
||||
WHERE id=%s AND deleted_at = 0
|
||||
UNION ALL
|
||||
SELECT t.id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.id = t.parent_id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT
|
||||
id
|
||||
FROM cte
|
||||
`, id)
|
||||
sub := mysql.DB.Raw(sql)
|
||||
db = db.Where("id NOT IN (?)", sub)
|
||||
ids, err := handleNoSelfChild(cast.ToInt(id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
db = db.Where("id NOT IN ?", ids)
|
||||
}
|
||||
|
||||
if id, ok := ctx.GetQuery("self_parent"); ok {
|
||||
sql := fmt.Sprintf(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT id,parent_id
|
||||
FROM node
|
||||
WHERE id=%s AND deleted_at = 0
|
||||
UNION ALL
|
||||
SELECT t.id,t.parent_id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.parent_id = t.id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT
|
||||
id
|
||||
FROM cte
|
||||
`, id)
|
||||
sub := mysql.DB.Raw(sql)
|
||||
db = db.Where("id IN (?)", sub)
|
||||
ids, err := handleSelfParent(cast.ToInt(id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
db = db.Where("id IN ?", ids)
|
||||
}
|
||||
|
||||
db = db.Order("name DESC")
|
||||
|
||||
doGet[*model.Node](ctx, false, db, "", nodePostHooks...)
|
||||
}
|
||||
|
||||
func nodePreHookCheckCycle(ctx *gin.Context, data *model.Node) {
|
||||
nodes := make([]*model.NodeIdPid, 0)
|
||||
err := mysql.DB.Model(nodes).Find(&nodes).Error
|
||||
g := make(map[int][]int)
|
||||
for _, n := range nodes {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int) bool
|
||||
dfs = func(x int) bool {
|
||||
b := x == data.ParentId
|
||||
for _, y := range g[x] {
|
||||
b = b || dfs(y)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
if err != nil || dfs(cast.ToInt(ctx.Param("id"))) {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInvalidArgument})
|
||||
}
|
||||
}
|
||||
|
||||
func nodePostHookCountAsset(ctx *gin.Context, data []*model.Node) {
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
isAdmin := acl.IsAdmin(currentUser)
|
||||
assets := make([]*model.AssetIdPid, 0)
|
||||
db := mysql.DB.Model(assets)
|
||||
if !isAdmin {
|
||||
authorizationResourceIds, err := GetAutorizationResourceIds(ctx)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
db = db.Where("resource_id IN ?", authorizationResourceIds)
|
||||
}
|
||||
if err := db.Find(&assets).Error; err != nil {
|
||||
logger.L.Error("node posthookfailed asset count", zap.Error(err))
|
||||
return
|
||||
}
|
||||
m := make(map[int]int64)
|
||||
g := make(map[int][]int)
|
||||
for _, n := range assets {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int) int64
|
||||
dfs = func(x int) int64 {
|
||||
m[x] += 1
|
||||
for _, y := range g[x] {
|
||||
m[x] += dfs(y)
|
||||
}
|
||||
return m[x]
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
d.AssetCount = m[d.Id]
|
||||
}
|
||||
}
|
||||
|
||||
func nodePostHookHasChild(ctx *gin.Context, data []*model.Node) {
|
||||
ps := make([]int, 0)
|
||||
if err := mysql.DB.
|
||||
Model(&model.Node{}).
|
||||
Where("parent_id IN ?", lo.Map(data, func(n *model.Node, _ int) int { return n.Id })).
|
||||
Pluck("parent_id", &ps).
|
||||
Error; err != nil {
|
||||
logger.L.Error("node posthookfailed has child", zap.Error(err))
|
||||
return
|
||||
}
|
||||
pm := lo.SliceToMap(ps, func(pid int) (int, bool) { return pid, true })
|
||||
for _, n := range data {
|
||||
n.HasChild = pm[n.Id]
|
||||
}
|
||||
}
|
||||
|
||||
func nodeDelHook(ctx *gin.Context, id int) {
|
||||
noChild := true
|
||||
noChild = noChild && errors.Is(mysql.DB.Model(&model.Node{}).Select("id").Where("parent_id = ?", id).First(map[string]any{}).Error, gorm.ErrRecordNotFound)
|
||||
noChild = noChild && errors.Is(mysql.DB.Model(&model.Asset{}).Select("id").Where("parent_id = ?", id).First(map[string]any{}).Error, gorm.ErrRecordNotFound)
|
||||
if noChild {
|
||||
return
|
||||
}
|
||||
|
||||
err := &ApiError{Code: ErrHasChild, Data: nil}
|
||||
ctx.AbortWithError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
func handleNoSelfChild(id int) (ids []int, err error) {
|
||||
nodes := make([]*model.NodeIdPid, 0)
|
||||
if err = mysql.DB.Model(nodes).Find(&nodes).Error; err != nil {
|
||||
return
|
||||
}
|
||||
g := make(map[int][]int)
|
||||
for _, n := range nodes {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int)
|
||||
dfs = func(x int) {
|
||||
ids = append(ids, x)
|
||||
for _, y := range g[x] {
|
||||
dfs(y)
|
||||
}
|
||||
}
|
||||
dfs(id)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleSelfParent(id int) (ids []int, err error) {
|
||||
nodes := make([]*model.NodeIdPid, 0)
|
||||
if err = mysql.DB.Model(nodes).Find(&nodes).Error; err != nil {
|
||||
return
|
||||
}
|
||||
g := make(map[int][]int)
|
||||
for _, n := range nodes {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int)
|
||||
dfs = func(x int) {
|
||||
ids = append(ids, x)
|
||||
for _, y := range g[x] {
|
||||
dfs(y)
|
||||
}
|
||||
}
|
||||
dfs(id)
|
||||
|
||||
ids = append(lo.Without(lo.Keys(g), ids...), id)
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -29,37 +29,22 @@ func (c *Controller) StatAssetType(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
t := mysql.DB.
|
||||
Model(&model.Asset{}).
|
||||
Raw(`
|
||||
WITH RECURSIVE cte AS(
|
||||
SELECT parent_id
|
||||
FROM asset
|
||||
WHERE deleted_at = 0
|
||||
UNION ALL
|
||||
SELECT t.parent_id
|
||||
FROM cte
|
||||
INNER JOIN node t on cte.parent_id = t.id
|
||||
WHERE deleted_at = 0
|
||||
)
|
||||
SELECT
|
||||
parent_id,
|
||||
COUNT(*) AS count
|
||||
FROM cte
|
||||
GROUP BY parent_id
|
||||
`)
|
||||
|
||||
err := mysql.DB.
|
||||
Model(&model.Node{}).
|
||||
Select("node.name, t.count").
|
||||
Joins("LEFT JOIN (?) t ON node.id = t.parent_id", t).
|
||||
Where("node.parent_id = 0").
|
||||
Find(&stat).
|
||||
Error
|
||||
m, err := nodeCountAsset()
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if err = mysql.DB.
|
||||
Model(stat).
|
||||
Where("parent_id = 0").
|
||||
Find(&stat).
|
||||
Error; err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
for _, s := range stat {
|
||||
s.Count = m[s.Id]
|
||||
}
|
||||
|
||||
redis.SetEx(ctx, key, stat, time.Minute)
|
||||
|
||||
@@ -298,3 +283,24 @@ func toListData[T any](data []T) *ListData {
|
||||
List: lo.Map(data, func(d T, _ int) any { return d }),
|
||||
}
|
||||
}
|
||||
|
||||
func nodeCountAsset() (res map[int]int64, err error) {
|
||||
assets := make([]*model.AssetIdPid, 0)
|
||||
if err = mysql.DB.Model(assets).Find(&assets).Error; err != nil {
|
||||
return
|
||||
}
|
||||
g := make(map[int][]int)
|
||||
for _, n := range assets {
|
||||
g[n.ParentId] = append(g[n.ParentId], n.Id)
|
||||
}
|
||||
var dfs func(int) int64
|
||||
dfs = func(x int) int64 {
|
||||
res[x] += 1
|
||||
for _, y := range g[x] {
|
||||
res[x] += dfs(y)
|
||||
}
|
||||
return res[x]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -6,6 +6,10 @@ import (
|
||||
"gorm.io/plugin/soft_delete"
|
||||
)
|
||||
|
||||
const (
|
||||
TABLE_NAME_ASSET = "asset"
|
||||
)
|
||||
|
||||
type Asset struct {
|
||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||
Ciid int `json:"ci_id" gorm:"column:ci_id"`
|
||||
@@ -42,7 +46,7 @@ type Range struct {
|
||||
}
|
||||
|
||||
func (m *Asset) TableName() string {
|
||||
return "asset"
|
||||
return TABLE_NAME_ASSET
|
||||
}
|
||||
func (m *Asset) SetId(id int) {
|
||||
m.Id = id
|
||||
@@ -66,7 +70,11 @@ func (m *Asset) GetId() int {
|
||||
return m.Id
|
||||
}
|
||||
|
||||
type AssetNodeChain struct {
|
||||
NodeId int `gorm:"column:id"`
|
||||
Chain string `gorm:"column:chain"`
|
||||
type AssetIdPid struct {
|
||||
Id int `gorm:"column:id"`
|
||||
ParentId int `gorm:"column:parent_id"`
|
||||
}
|
||||
|
||||
func (m *AssetIdPid) TableName() string {
|
||||
return TABLE_NAME_ASSET
|
||||
}
|
||||
|
@@ -42,3 +42,8 @@ type Model interface {
|
||||
GetId() int
|
||||
GetName() string
|
||||
}
|
||||
|
||||
type Pair[T1, T2 any] struct {
|
||||
First T1
|
||||
Second T2
|
||||
}
|
||||
|
@@ -6,6 +6,10 @@ import (
|
||||
"gorm.io/plugin/soft_delete"
|
||||
)
|
||||
|
||||
const (
|
||||
TABLE_NAME_NODE = "node"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Id int `json:"id" gorm:"column:id;primarykey"`
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
@@ -37,7 +41,7 @@ type Sync struct {
|
||||
}
|
||||
|
||||
func (m *Node) TableName() string {
|
||||
return "node"
|
||||
return TABLE_NAME_NODE
|
||||
}
|
||||
func (m *Node) SetId(id int) {
|
||||
m.Id = id
|
||||
@@ -61,7 +65,21 @@ func (m *Node) GetId() int {
|
||||
return m.Id
|
||||
}
|
||||
|
||||
type NodeCount struct {
|
||||
ParentId int `gorm:"column:parent_id"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
type NodeIdPid struct {
|
||||
Id int `gorm:"column:id"`
|
||||
ParentId int `gorm:"column:parent_id"`
|
||||
}
|
||||
|
||||
func (m *NodeIdPid) TableName() string {
|
||||
return TABLE_NAME_NODE
|
||||
}
|
||||
|
||||
type NodeIdPidName struct {
|
||||
Id int `gorm:"column:id"`
|
||||
ParentId int `gorm:"column:parent_id"`
|
||||
Name string `gorm:"column:name"`
|
||||
}
|
||||
|
||||
func (m *NodeIdPidName) TableName() string {
|
||||
return TABLE_NAME_NODE
|
||||
}
|
||||
|
Reference in New Issue
Block a user