Skip to content

Commit

Permalink
Merge branch 'dev.categories'
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Dec 31, 2024
2 parents cc2cdba + 78daa18 commit 0034ee4
Show file tree
Hide file tree
Showing 14 changed files with 343 additions and 25 deletions.
6 changes: 6 additions & 0 deletions cmfx/categories/categories.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2025 caixw
//
// SPDX-License-Identifier: MIT

// Package categories 提供了几种用于分类的方式
package categories
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package linkage
package linkages

import (
"github.com/issue9/orm/v6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package linkage
package linkages

import (
"testing"
Expand All @@ -29,5 +29,5 @@ func TestInstall(t *testing.T) {
})
a.NotNil(l)

s.TableExists("mod_linkage_lk")
s.TableExists("mod_linkages_lk")
}
16 changes: 16 additions & 0 deletions cmfx/categories/linkages/linkages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2024 caixw
//
// SPDX-License-Identifier: MIT

// Package linkages 提供简单的级联链表管理
package linkages

import (
"github.com/issue9/orm/v6"

"github.com/issue9/cmfx/cmfx"
)

func buildDB(mod *cmfx.Module, tableName string) *orm.DB {
return mod.DB().New(mod.DB().TablePrefix() + "_linkages_" + tableName)
}
10 changes: 9 additions & 1 deletion cmfx/linkage/models.go → cmfx/categories/linkages/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package linkage
package linkages

import "database/sql"

Expand All @@ -11,22 +11,30 @@ type linkagePO struct {
Title string `orm:"name(title);len(20)"` // 此分类的简要说明
Icon string `orm:"name(icon);len(1024);nullable"` // 图标
Deleted sql.NullTime `orm:"name(deleted);nullable"` // 删除时间
Order int `orm:"name(order)"` // 在同级中的顺序
Parent int64 `orm:"name(parent)"` // 表示某一项的上一级项目,如果为零,表示该值是顶级项目。
Count int `orm:"name(count)"` // 关联内容的数量
}

func (l *linkagePO) TableName() string { return `` }

type LinkageVO struct {
XMLName struct{} `json:"-" yaml:"-" cbor:"-" xml:"linkage"`

ID int64 `json:"id" yaml:"id" cbor:"id" xml:"id,attr"`
Title string `json:"title" yaml:"title" cbor:"title" xml:"title"`
Order int `json:"order,omitempty" yaml:"order,omitempty" cbor:"order,omitempty" xml:"order,attr,omitempty"` // 在同级中的顺序
Icon string `json:"icon,omitempty" yaml:"icon,omitempty" cbor:"icon,omitempty" xml:"icon,omitempty"`
Items []*LinkageVO `json:"items,omitempty" yaml:"items,omitempty" cbor:"items,omitempty" xml:"items>item,omitempty"`
Count int `json:"count,omitempty" yaml:"count,omitempty" cbor:"count,omitempty" xml:"count,attr,omitempty"` // 关联内容的数量
}

func (vo *LinkageVO) toPO(parent int64) *linkagePO {
return &linkagePO{
Parent: parent,
Title: vo.Title,
Icon: vo.Icon,
Order: vo.Order,
Count: vo.Count,
}
}
53 changes: 44 additions & 9 deletions cmfx/linkage/module.go → cmfx/categories/linkages/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package linkage
package linkages

import (
"database/sql"
Expand Down Expand Up @@ -63,6 +63,10 @@ func buildItems(v *LinkageVO, all []*linkagePO) []*linkagePO {
return all
}

func sort(items []*LinkageVO) {
slices.SortFunc(items, func(a, b *LinkageVO) int { return a.Order - b.Order })
}

func getItems(p int64, all []*linkagePO) ([]*LinkageVO, []*linkagePO) {
items := make([]*LinkageVO, 0, 10)
for _, item := range all {
Expand All @@ -71,9 +75,12 @@ func getItems(p int64, all []*linkagePO) ([]*LinkageVO, []*linkagePO) {
ID: item.ID,
Title: item.Title,
Icon: item.Icon,
Order: item.Order,
Count: item.Count,
})
}
}
sort(items)

return items, slices.DeleteFunc(all, func(item *linkagePO) bool { // 删除所有已经在 items 中的项
return slices.IndexFunc(items, func(e *LinkageVO) bool { return e.ID == item.ID }) >= 0
Expand All @@ -90,23 +97,29 @@ func (m *Module) Get() (*LinkageVO, error) {
}

// Set 修改 id 指向的对象
func (m *Module) Set(id int64, title, icon string) error {
func (m *Module) Set(id int64, title, icon string, order int) error {
root, err := m.Get()
if err != nil {
return err
}

item, _ := findRoot(id, root)
item, p := findRoot(id, root)
item.Title = title
item.Icon = icon
item.Order = order

if p != nil {
sort(p.Items) // 重新排序
}

// 保存到数据库
po := &linkagePO{
ID: id,
Title: title,
Icon: icon,
Order: order,
}
if _, err := m.db.Update(po); err != nil {
if _, err := m.db.Update(po, "title", "icon", "order"); err != nil {
return err
}

Expand All @@ -125,6 +138,9 @@ func (m *Module) Delete(id int64) error {

_, p := findRoot(id, root)
p.Items = slices.DeleteFunc(p.Items, func(e *LinkageVO) bool { return e.ID == id })
if p != nil {
sort(p.Items) // 重新排序
}

po := &linkagePO{ID: id, Deleted: sql.NullTime{Valid: true, Time: time.Now()}}
if _, err := m.db.Update(po); err != nil {
Expand All @@ -135,8 +151,28 @@ func (m *Module) Delete(id int64) error {
return m.mod.Server().Cache().Set(m.cacheID, root, cache.Forever)
}

// AddCount 添加计数
//
// delta 增加的数量,可以为负数;
func (m *Module) AddCount(id int64, delta int) error {
root, err := m.Get()
if err != nil {
return err
}

curr, _ := findRoot(id, root)
curr.Count++

if _, err := m.db.Update(&linkagePO{ID: id, Count: curr.Count}, "count"); err != nil {
return err
}

// 保存到缓存
return m.mod.Server().Cache().Set(m.cacheID, root, cache.Forever)
}

// Add 向 parent 添加一个子项
func (m *Module) Add(parent int64, title, icon string) error {
func (m *Module) Add(parent int64, title, icon string, order int) error {
root, err := m.Get()
if err != nil {
return err
Expand All @@ -149,6 +185,7 @@ func (m *Module) Add(parent int64, title, icon string) error {
Title: title,
Icon: icon,
Parent: p.ID,
Order: order,
}
id, err := m.db.LastInsertID(po)
if err != nil {
Expand All @@ -160,7 +197,9 @@ func (m *Module) Add(parent int64, title, icon string) error {
ID: id,
Title: title,
Icon: icon,
Order: order,
})
sort(p.Items)
return m.mod.Server().Cache().Set(m.cacheID, root, cache.Forever)
}

Expand All @@ -183,7 +222,3 @@ func find(id int64, p *LinkageVO) (curr, parent *LinkageVO) {
}
return nil, nil
}

func buildDB(mod *cmfx.Module, tableName string) *orm.DB {
return mod.DB().New(mod.DB().TablePrefix() + "_linkage_" + tableName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package linkage
package linkages

import (
"testing"
Expand All @@ -21,9 +21,10 @@ func TestModule(t *testing.T) {
m := Install(mod, "lk", &LinkageVO{
Title: "t1",
Icon: "icon1",
Order: 5,
Items: []*LinkageVO{
{Title: "t2", Icon: "icon2"},
{Title: "t3", Icon: "icon3"},
{Title: "t2", Icon: "icon2", Order: 5},
{Title: "t3", Icon: "icon3", Order: 5},
},
})

Expand All @@ -34,18 +35,29 @@ func TestModule(t *testing.T) {
Length(root.Items, 2)
})

t.Run("AddCount", func(t *testing.T) {
root, err := m.Get()
a.NotError(err).NotNil(root)

a.NotError(m.AddCount(root.ID, 1))

root, err = m.Get()
a.NotError(err).
Equal(root.Count, 1)
})

t.Run("Add", func(t *testing.T) {
root, err := m.Get()
a.NotError(err).NotNil(root)

a.NotError(m.Add(root.ID, "t4", "icon4"))
a.NotError(m.Add(root.ID, "t4", "icon4", 5))
root, err = m.Get()
a.NotError(err).
NotNil(root).
Length(root.Items, 3)

item := root.Items[0]
a.NotError(m.Add(item.ID, "t5", "icon5"))
a.NotError(m.Add(item.ID, "t5", "icon5", 5))
root, err = m.Get()
a.NotError(err).
NotNil(root).
Expand All @@ -58,7 +70,7 @@ func TestModule(t *testing.T) {
a.NotError(err).NotNil(root)

item := root.Items[0]
a.NotError(m.Set(item.ID, "t55", "icon55"))
a.NotError(m.Set(item.ID, "t55", "icon55", 5))
root, err = m.Get()
a.NotError(err).
NotNil(root).
Expand Down
38 changes: 38 additions & 0 deletions cmfx/categories/tags/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2025 caixw
//
// SPDX-License-Identifier: MIT

package tags

import (
"github.com/issue9/orm/v6"
"github.com/issue9/web"

"github.com/issue9/cmfx/cmfx"
)

// Install 安装数据库以及相关的初始数据
func Install(mod *cmfx.Module, tableName string, tag ...string) *Module {
db := buildDB(mod, tableName)

if err := db.Create(&TagPO{}); err != nil {
panic(web.SprintError(mod.Server().Locale().Printer(), true, err))
}

if len(tag) == 0 {
panic("参数 tag 不能为空")
}

err := db.DoTransaction(func(tx *orm.Tx) error {
tags := make([]orm.TableNamer, 0, len(tag))
for _, t := range tag {
tags = append(tags, &TagPO{Title: t})
}
return tx.InsertMany(50, tags...)
})
if err != nil {
panic(web.SprintError(mod.Server().Locale().Printer(), true, err))
}

return Load(mod, tableName)
}
30 changes: 30 additions & 0 deletions cmfx/categories/tags/install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2025 caixw
//
// SPDX-License-Identifier: MIT

package tags

import (
"testing"

"github.com/issue9/assert/v4"

"github.com/issue9/cmfx/cmfx/initial/test"
)

func TestInstall(t *testing.T) {
a := assert.New(t, false)
s := test.NewSuite(a)
defer s.Close()

mod := s.NewModule("mod")

a.PanicString(func() {
Install(mod, "lk")
}, "参数 tag 不能为空")

l := Install(mod, "lk", "t1", "t2")
a.NotNil(l)

s.TableExists("mod_tags_lk")
}
14 changes: 14 additions & 0 deletions cmfx/categories/tags/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2025 caixw
//
// SPDX-License-Identifier: MIT

package tags

type TagPO struct {
XMLName struct{} `orm:"-" json:"-" yaml:"-" cbor:"-" xml:"tag"`

ID int64 `orm:"name(id);ai" json:"id" yaml:"id" cbor:"id" xml:"id,attr"`
Title string `orm:"name(title);len(20)" json:"title" yaml:"title" cbor:"title" xml:"title"` // 此标签的简要说明
}

func (po *TagPO) TableName() string { return `` }
Loading

0 comments on commit 0034ee4

Please sign in to comment.