优化操作系统和浏览器统计相关程序

This commit is contained in:
GoEdgeLab
2022-11-11 17:48:30 +08:00
parent b02ca0c840
commit 1a1b937a63
24 changed files with 1055 additions and 127 deletions

View File

@@ -1,11 +1,15 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"strconv"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
const (
@@ -13,7 +17,20 @@ const (
ClientBrowserStateDisabled = 0 // 已禁用
)
var clientBrowserNameAndIdCacheMap = map[string]int64{}
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedClientBrowserDAO.Clean(nil, 7) // 只保留N天
if err != nil {
remotelogs.Error("SharedClientBrowserDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ClientBrowserDAO dbs.DAO
@@ -74,65 +91,64 @@ func (this *ClientBrowserDAO) FindClientBrowserName(tx *dbs.Tx, id uint32) (stri
FindStringCol("")
}
// FindBrowserIdWithNameCacheable 根据浏览器名称查找浏览器ID
func (this *ClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browserName string) (int64, error) {
SharedCacheLocker.RLock()
browserId, ok := clientBrowserNameAndIdCacheMap[browserName]
if ok {
SharedCacheLocker.RUnlock()
return browserId, nil
}
SharedCacheLocker.RUnlock()
browserId, err := this.Query(tx).
Where("JSON_CONTAINS(codes, :browserName)").
Param("browserName", strconv.Quote(browserName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if browserId > 0 {
// 只有找到的时候才放入缓存,以便于我们可以在不存在的时候创建一条新的记录
SharedCacheLocker.Lock()
clientBrowserNameAndIdCacheMap[browserName] = browserId
SharedCacheLocker.Unlock()
}
return browserId, nil
}
// CreateBrowser 创建浏览器
func (this *ClientBrowserDAO) CreateBrowser(tx *dbs.Tx, browserName string) (int64, error) {
var maxlength = 50
// CreateBrowserIfNotExists 创建浏览器信息
func (this *ClientBrowserDAO) CreateBrowserIfNotExists(tx *dbs.Tx, browserName string) error {
const maxlength = 50
if len(browserName) > maxlength {
browserName = browserName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查缓存
var cacheKey = "clientBrowser:" + browserName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return nil
}
// 检查是否已经创建
// 检查是否已经存在
// 不需要加状态条件
browserId, err := this.Query(tx).
Attr("name", browserName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
return err
}
if browserId > 0 {
return browserId, nil
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
ttlcache.SharedCache.Write(cacheKey, browserId, time.Now().Unix()+3600)
return this.Query(tx).
Pk(browserId).
Set("createdDay", timeutil.Format("Ymd")).
UpdateQuickly()
}
// 如果不存在,则创建之
var op = NewClientBrowserOperator()
op.Name = browserName
codes := []string{browserName}
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
op.CreatedDay = timeutil.Format("Ymd")
op.State = ClientBrowserStateEnabled
return this.SaveInt64(tx, op)
browserId, err = this.SaveInt64(tx, op)
if err != nil && CheckSQLErrCode(err, 1062 /** duplicate entry **/) {
return nil
}
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
if browserId > 0 {
ttlcache.SharedCache.Write(cacheKey, browserId, time.Now().Unix()+3600)
}
return err
}
// Clean 清理
func (this *ClientBrowserDAO) Clean(tx *dbs.Tx, days int) error {
if days <= 0 {
days = 30
}
return this.Query(tx).
Lt("createdDay", timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))).
DeleteQuickly()
}

View File

@@ -1,5 +1,33 @@
package models
package models_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestClientBrowserDAO_CreateBrowser(t *testing.T) {
var dao = models.NewClientBrowserDAO()
err := dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
err = dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
err = dao.CreateBrowserIfNotExists(nil, "Hello")
if err != nil {
t.Fatal(err)
}
}
func TestClientBrowserDAO_Clean(t *testing.T) {
var dao = models.NewClientBrowserDAO()
err := dao.Clean(nil, 30)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -4,17 +4,19 @@ import "github.com/iwind/TeaGo/dbs"
// ClientBrowser 终端浏览器信息
type ClientBrowser struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 浏览器名称
Codes dbs.JSON `field:"codes"` // 代号
State uint8 `field:"state"` // 状态
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 浏览器名称
Codes dbs.JSON `field:"codes"` // 代号
CreatedDay string `field:"createdDay"` // 创建日期YYYYMMDD
State uint8 `field:"state"` // 状态
}
type ClientBrowserOperator struct {
Id interface{} // ID
Name interface{} // 浏览器名称
Codes interface{} // 代号
State interface{} // 状态
Id any // ID
Name any // 浏览器名称
Codes any // 代号
CreatedDay any // 创建日期YYYYMMDD
State any // 状态
}
func NewClientBrowserOperator() *ClientBrowserOperator {

View File

@@ -1,11 +1,15 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"strconv"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
const (
@@ -13,7 +17,20 @@ const (
ClientSystemStateDisabled = 0 // 已禁用
)
var clientSystemNameAndIdCacheMap = map[string]int64{} // system name => id
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedClientSystemDAO.Clean(nil, 7) // 只保留N天
if err != nil {
remotelogs.Error("SharedClientSystemDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ClientSystemDAO dbs.DAO
@@ -74,67 +91,63 @@ func (this *ClientSystemDAO) FindClientSystemName(tx *dbs.Tx, id uint32) (string
FindStringCol("")
}
// FindSystemIdWithNameCacheable 根据操作系统名称查找系统ID
func (this *ClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemName string) (int64, error) {
SharedCacheLocker.RLock()
systemId, ok := clientSystemNameAndIdCacheMap[systemName]
if ok {
SharedCacheLocker.RUnlock()
return systemId, nil
}
SharedCacheLocker.RUnlock()
systemId, err := this.Query(tx).
Where("JSON_CONTAINS(codes, :systemName)").
Param("systemName", strconv.Quote(systemName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if systemId > 0 {
// 只有找到的时候才放入缓存,以便于我们可以在不存在的时候创建一条新的记录
SharedCacheLocker.Lock()
clientSystemNameAndIdCacheMap[systemName] = systemId
SharedCacheLocker.Unlock()
}
return systemId, nil
}
// CreateSystem 创建浏览器
func (this *ClientSystemDAO) CreateSystem(tx *dbs.Tx, systemName string) (int64, error) {
var maxlength = 50
// CreateSystemIfNotExists 创建系统信息
func (this *ClientSystemDAO) CreateSystemIfNotExists(tx *dbs.Tx, systemName string) error {
const maxlength = 50
if len(systemName) > maxlength {
systemName = systemName[:50]
}
SharedCacheLocker.Lock()
defer SharedCacheLocker.Unlock()
// 检查缓存
var cacheKey = "clientSystem:" + systemName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return nil
}
// 检查是否已经创建
// 检查是否已经存在
// 不需要加状态条件
systemId, err := this.Query(tx).
Attr("name", systemName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
return err
}
if systemId > 0 {
return systemId, nil
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
ttlcache.SharedCache.Write(cacheKey, systemId, time.Now().Unix()+3600)
return this.Query(tx).
Pk(systemId).
Set("createdDay", timeutil.Format("Ymd")).
UpdateQuickly()
}
var op = NewClientSystemOperator()
op.Name = systemName
codes := []string{systemName}
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
op.CreatedDay = timeutil.Format("Ymd")
op.State = ClientSystemStateEnabled
return this.SaveInt64(tx, op)
systemId, err = this.SaveInt64(tx, op)
if err != nil && CheckSQLErrCode(err, 1062 /** duplicate entry **/) {
return nil
}
// 加入缓存,但缓存时间不要过长,因为有别的操作在更新数据
if systemId > 0 {
ttlcache.SharedCache.Write(cacheKey, systemId, time.Now().Unix()+3600)
}
return err
}
// Clean 清理
func (this *ClientSystemDAO) Clean(tx *dbs.Tx, days int) error {
if days <= 0 {
days = 30
}
return this.Query(tx).
Lt("createdDay", timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))).
DeleteQuickly()
}

View File

@@ -1,5 +1,31 @@
package models
package models_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestClientSystemDAO_CreateSystemIfNotExists(t *testing.T) {
var dao = models.NewClientSystemDAO()
{
err := dao.CreateSystemIfNotExists(nil, "Mac OS X")
if err != nil {
t.Fatal(err)
}
}
{
err := dao.CreateSystemIfNotExists(nil, "Mac OS X 2")
if err != nil {
t.Fatal(err)
}
}
}
func TestClientSystemDAO_Clean(t *testing.T) {
var dao = models.NewClientSystemDAO()
err := dao.Clean(nil, 30)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -4,17 +4,19 @@ import "github.com/iwind/TeaGo/dbs"
// ClientSystem 终端操作系统信息
type ClientSystem struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 系统名称
Codes dbs.JSON `field:"codes"` // 代号
State uint8 `field:"state"` //
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 系统名称
Codes dbs.JSON `field:"codes"` // 代号
CreatedDay string `field:"createdDay"` // 创建日期YYYYMMDD
State uint8 `field:"state"` // 状态
}
type ClientSystemOperator struct {
Id interface{} // ID
Name interface{} // 系统名称
Codes interface{} // 代号
State interface{} //
Id any // ID
Name any // 系统名称
Codes any // 代号
CreatedDay any // 创建日期YYYYMMDD
State any // 状态
}
func NewClientSystemOperator() *ClientSystemOperator {

View File

@@ -0,0 +1,207 @@
package models
import (
"encoding/json"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"strconv"
"strings"
"time"
)
const (
FormalClientBrowserStateEnabled = 1 // 已启用
FormalClientBrowserStateDisabled = 0 // 已禁用
)
type FormalClientBrowserDAO dbs.DAO
func NewFormalClientBrowserDAO() *FormalClientBrowserDAO {
return dbs.NewDAO(&FormalClientBrowserDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeFormalClientBrowsers",
Model: new(FormalClientBrowser),
PkName: "id",
},
}).(*FormalClientBrowserDAO)
}
var SharedFormalClientBrowserDAO *FormalClientBrowserDAO
func init() {
dbs.OnReady(func() {
SharedFormalClientBrowserDAO = NewFormalClientBrowserDAO()
})
}
// EnableFormalClientBrowser 启用条目
func (this *FormalClientBrowserDAO) EnableFormalClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", FormalClientBrowserStateEnabled).
Update()
return err
}
// DisableFormalClientBrowser 禁用条目
func (this *FormalClientBrowserDAO) DisableFormalClientBrowser(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", FormalClientBrowserStateDisabled).
Update()
return err
}
// FindEnabledFormalClientBrowser 查找启用中的条目
func (this *FormalClientBrowserDAO) FindEnabledFormalClientBrowser(tx *dbs.Tx, id int64) (*FormalClientBrowser, error) {
result, err := this.Query(tx).
Pk(id).
State(FormalClientBrowserStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*FormalClientBrowser), err
}
// FindFormalClientBrowserName 根据主键查找名称
func (this *FormalClientBrowserDAO) FindFormalClientBrowserName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindBrowserIdWithNameCacheable 根据浏览器名称查找系统ID
func (this *FormalClientBrowserDAO) FindBrowserIdWithNameCacheable(tx *dbs.Tx, browserName string) (int64, error) {
var cacheKey = "formalClientBrowser:" + browserName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return types.Int64(cacheItem.Value), nil
}
// 先使用 name 查找,因为有索引,所以会快一些
browserId, err := this.Query(tx).
Attr("name", browserName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if browserId == 0 {
browserId, err = this.Query(tx).
Where("JSON_CONTAINS(codes, :browserName)").
Param("browserName", strconv.Quote(browserName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
}
// 即使找不到也要放入到缓存中
ttlcache.SharedCache.Write(cacheKey, browserId, time.Now().Unix()+3600)
return browserId, nil
}
// CountBrowsers 计算浏览器数量
func (this *FormalClientBrowserDAO) CountBrowsers(tx *dbs.Tx, keyword string) (int64, error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("LOWER(codes)", dbutils.QuoteLikeKeyword(strings.ToLower(keyword)))
}
return query.Count()
}
// ListBrowsers 列出单页浏览器信息
func (this *FormalClientBrowserDAO) ListBrowsers(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*FormalClientBrowser, err error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("LOWER(codes)", dbutils.QuoteLikeKeyword(strings.ToLower(keyword)))
}
_, err = query.
Offset(offset).
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// FindBrowserWithDataId 根据dataId查找浏览器信息
func (this *FormalClientBrowserDAO) FindBrowserWithDataId(tx *dbs.Tx, dataId string) (*FormalClientBrowser, error) {
one, err := this.Query(tx).
Attr("dataId", dataId).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*FormalClientBrowser), nil
}
// CreateBrowser 创建浏览器信息
func (this *FormalClientBrowserDAO) CreateBrowser(tx *dbs.Tx, name string, codes []string, dataId string) (int64, error) {
if len(dataId) == 0 {
return 0, errors.New("invalid dataId")
}
// 检查 dataId 是否已经存在
exists, err := this.Query(tx).
Attr("dataId", dataId).
Exist()
if err != nil {
return 0, err
}
if exists {
return 0, errors.New("dataId '" + dataId + "' already exists")
}
var op = NewFormalClientBrowserOperator()
op.Name = name
if len(codes) == 0 {
op.Codes = "[]"
} else {
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
}
op.DataId = dataId
op.State = FormalClientBrowserStateEnabled
return this.SaveInt64(tx, op)
}
// UpdateBrowser 修改浏览器信息
func (this *FormalClientBrowserDAO) UpdateBrowser(tx *dbs.Tx, browserId int64, name string, codes []string, dataId string) error {
if browserId <= 0 {
return errors.New("invalid browserId '" + types.String(browserId) + "'")
}
if len(dataId) == 0 {
return errors.New("invalid dataId")
}
var op = NewFormalClientBrowserOperator()
op.Id = browserId
op.Name = name
if len(codes) == 0 {
op.Codes = "[]"
} else {
codesJSON, err := json.Marshal(codes)
if err != nil {
return err
}
op.Codes = codesJSON
}
op.DataId = dataId
return this.Save(tx, op)
}

View File

@@ -0,0 +1,6 @@
package models_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,24 @@
package models
import "github.com/iwind/TeaGo/dbs"
// FormalClientBrowser 终端浏览器信息
type FormalClientBrowser struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 浏览器名称
Codes dbs.JSON `field:"codes"` // 代号
DataId string `field:"dataId"` // 数据ID
State uint8 `field:"state"` // 状态
}
type FormalClientBrowserOperator struct {
Id any // ID
Name any // 浏览器名称
Codes any // 代号
DataId any // 数据ID
State any // 状态
}
func NewFormalClientBrowserOperator() *FormalClientBrowserOperator {
return &FormalClientBrowserOperator{}
}

View File

@@ -0,0 +1,21 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
)
// DecodeCodes 解析代号
func (this *FormalClientBrowser) DecodeCodes() []string {
if IsNull(this.Codes) {
return nil
}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
remotelogs.Error("FormalClientBrowser.DecodeCodes", err.Error())
}
return result
}

View File

@@ -0,0 +1,207 @@
package models
import (
"encoding/json"
dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/utils/ttlcache"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"strconv"
"strings"
"time"
)
const (
FormalClientSystemStateEnabled = 1 // 已启用
FormalClientSystemStateDisabled = 0 // 已禁用
)
type FormalClientSystemDAO dbs.DAO
func NewFormalClientSystemDAO() *FormalClientSystemDAO {
return dbs.NewDAO(&FormalClientSystemDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeFormalClientSystems",
Model: new(FormalClientSystem),
PkName: "id",
},
}).(*FormalClientSystemDAO)
}
var SharedFormalClientSystemDAO *FormalClientSystemDAO
func init() {
dbs.OnReady(func() {
SharedFormalClientSystemDAO = NewFormalClientSystemDAO()
})
}
// EnableFormalClientSystem 启用条目
func (this *FormalClientSystemDAO) EnableFormalClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", FormalClientSystemStateEnabled).
Update()
return err
}
// DisableFormalClientSystem 禁用条目
func (this *FormalClientSystemDAO) DisableFormalClientSystem(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", FormalClientSystemStateDisabled).
Update()
return err
}
// FindEnabledFormalClientSystem 查找启用中的条目
func (this *FormalClientSystemDAO) FindEnabledFormalClientSystem(tx *dbs.Tx, id int64) (*FormalClientSystem, error) {
result, err := this.Query(tx).
Pk(id).
State(FormalClientSystemStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*FormalClientSystem), err
}
// FindFormalClientSystemName 根据主键查找名称
func (this *FormalClientSystemDAO) FindFormalClientSystemName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// FindSystemIdWithNameCacheable 根据操作系统名称查找系统ID
func (this *FormalClientSystemDAO) FindSystemIdWithNameCacheable(tx *dbs.Tx, systemName string) (int64, error) {
var cacheKey = "formalClientSystem:" + systemName
var cacheItem = ttlcache.SharedCache.Read(cacheKey)
if cacheItem != nil {
return types.Int64(cacheItem.Value), nil
}
// 先使用 name 查找,因为有索引,所以会快一些
systemId, err := this.Query(tx).
Attr("name", systemName).
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if systemId == 0 {
systemId, err = this.Query(tx).
Where("JSON_CONTAINS(codes, :systemName)").
Param("systemName", strconv.Quote(systemName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
}
// 即使找不到也要放入到缓存中
ttlcache.SharedCache.Write(cacheKey, systemId, time.Now().Unix()+3600)
return systemId, nil
}
// CreateSystem 创建操作系统信息
func (this *FormalClientSystemDAO) CreateSystem(tx *dbs.Tx, name string, codes []string, dataId string) (int64, error) {
if len(dataId) == 0 {
return 0, errors.New("invalid dataId")
}
// 检查 dataId 是否已经存在
exists, err := this.Query(tx).
Attr("dataId", dataId).
Exist()
if err != nil {
return 0, err
}
if exists {
return 0, errors.New("dataId '" + dataId + "' already exists")
}
var op = NewFormalClientSystemOperator()
op.Name = name
if len(codes) == 0 {
op.Codes = "[]"
} else {
codesJSON, err := json.Marshal(codes)
if err != nil {
return 0, err
}
op.Codes = codesJSON
}
op.DataId = dataId
op.State = FormalClientSystemStateEnabled
return this.SaveInt64(tx, op)
}
// UpdateSystem 修改操作系统信息
func (this *FormalClientSystemDAO) UpdateSystem(tx *dbs.Tx, systemId int64, name string, codes []string, dataId string) error {
if systemId <= 0 {
return errors.New("invalid systemId '" + types.String(systemId) + "'")
}
if len(dataId) == 0 {
return errors.New("invalid dataId")
}
var op = NewFormalClientSystemOperator()
op.Id = systemId
op.Name = name
if len(codes) == 0 {
op.Codes = "[]"
} else {
codesJSON, err := json.Marshal(codes)
if err != nil {
return err
}
op.Codes = codesJSON
}
op.DataId = dataId
return this.Save(tx, op)
}
// CountSystems 计算操作系统数量
func (this *FormalClientSystemDAO) CountSystems(tx *dbs.Tx, keyword string) (int64, error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("LOWER(codes)", dbutils.QuoteLikeKeyword(strings.ToLower(keyword)))
}
return query.Count()
}
// ListSystems 列出单页操作系统信息
func (this *FormalClientSystemDAO) ListSystems(tx *dbs.Tx, keyword string, offset int64, size int64) (result []*FormalClientSystem, err error) {
var query = this.Query(tx)
if len(keyword) > 0 {
query.Like("LOWER(codes)", dbutils.QuoteLikeKeyword(strings.ToLower(keyword)))
}
_, err = query.
Offset(offset).
Limit(size).
DescPk().
Slice(&result).
FindAll()
return
}
// FindSystemWithDataId 根据dataId查找操作系统信息
func (this *FormalClientSystemDAO) FindSystemWithDataId(tx *dbs.Tx, dataId string) (*FormalClientSystem, error) {
one, err := this.Query(tx).
Attr("dataId", dataId).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*FormalClientSystem), nil
}

View File

@@ -0,0 +1,6 @@
package models_test
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,24 @@
package models
import "github.com/iwind/TeaGo/dbs"
// FormalClientSystem 终端操作系统信息
type FormalClientSystem struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 系统名称
Codes dbs.JSON `field:"codes"` // 代号
State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 数据ID
}
type FormalClientSystemOperator struct {
Id any // ID
Name any // 系统名称
Codes any // 代号
State any // 状态
DataId any // 数据ID
}
func NewFormalClientSystemOperator() *FormalClientSystemOperator {
return &FormalClientSystemOperator{}
}

View File

@@ -0,0 +1,21 @@
package models
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
)
// DecodeCodes 解析代号
func (this *FormalClientSystem) DecodeCodes() []string {
if IsNull(this.Codes) {
return nil
}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
remotelogs.Error("FormalClientSystem.DecodeCodes", err.Error())
}
return result
}

View File

@@ -402,6 +402,16 @@ func (this *APINode) registerServices(server *grpc.Server) {
pb.RegisterServerRegionProviderMonthlyStatServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FormalClientSystemService{}).(*services.FormalClientSystemService)
pb.RegisterFormalClientSystemServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FormalClientBrowserService{}).(*services.FormalClientBrowserService)
pb.RegisterFormalClientBrowserServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.ServerClientSystemMonthlyStatService{}).(*services.ServerClientSystemMonthlyStatService)
pb.RegisterServerClientSystemMonthlyStatServiceServer(server, instance)

View File

@@ -0,0 +1,143 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package services
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
// FormalClientBrowserService 浏览器信息库服务
type FormalClientBrowserService struct {
BaseService
}
// CreateFormalClientBrowser 创建浏览器信息
func (this *FormalClientBrowserService) CreateFormalClientBrowser(ctx context.Context, req *pb.CreateFormalClientBrowserRequest) (*pb.CreateFormalClientBrowserResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
// 检查dataId是否存在
var tx = this.NullTx()
browser, err := models.SharedFormalClientBrowserDAO.FindBrowserWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if browser != nil {
return nil, errors.New("dataId '" + req.DataId + "' already exists")
}
browserId, err := models.SharedFormalClientBrowserDAO.CreateBrowser(tx, req.Name, req.Codes, req.DataId)
if err != nil {
return nil, err
}
return &pb.CreateFormalClientBrowserResponse{
FormalClientBrowserId: browserId,
}, nil
}
// CountFormalClientBrowsers 计算浏览器信息数量
func (this *FormalClientBrowserService) CountFormalClientBrowsers(ctx context.Context, req *pb.CountFormalClientBrowsersRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := models.SharedFormalClientBrowserDAO.CountBrowsers(tx, req.Keyword)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListFormalClientBrowsers 列出单页浏览器信息
func (this *FormalClientBrowserService) ListFormalClientBrowsers(ctx context.Context, req *pb.ListFormalClientBrowsersRequest) (*pb.ListFormalClientBrowsersResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
browsers, err := models.SharedFormalClientBrowserDAO.ListBrowsers(tx, req.Keyword, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbBrowsers = []*pb.FormalClientBrowser{}
for _, browser := range browsers {
pbBrowsers = append(pbBrowsers, &pb.FormalClientBrowser{
Id: int64(browser.Id),
Name: browser.Name,
Codes: browser.DecodeCodes(),
DataId: browser.DataId,
State: types.Int32(browser.State),
})
}
return &pb.ListFormalClientBrowsersResponse{
FormalClientBrowsers: pbBrowsers,
}, nil
}
// UpdateFormalClientBrowser 修改浏览器信息
func (this *FormalClientBrowserService) UpdateFormalClientBrowser(ctx context.Context, req *pb.UpdateFormalClientBrowserRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
if len(req.DataId) == 0 {
return nil, errors.New("invalid dataId")
}
var tx = this.NullTx()
// 检查dataId是否已经被使用
oldBrowser, err := models.SharedFormalClientBrowserDAO.FindBrowserWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if oldBrowser != nil && int64(oldBrowser.Id) != req.FormalClientBrowserId {
return nil, errors.New("the dataId '" + req.DataId + "' already has been used")
}
err = models.SharedFormalClientBrowserDAO.UpdateBrowser(tx, req.FormalClientBrowserId, req.Name, req.Codes, req.DataId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindFormalClientBrowserWithDataId 通过dataId查询浏览器信息
func (this *FormalClientBrowserService) FindFormalClientBrowserWithDataId(ctx context.Context, req *pb.FindFormalClientBrowserWithDataIdRequest) (*pb.FindFormalClientBrowserWithDataIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
browser, err := models.SharedFormalClientBrowserDAO.FindBrowserWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if browser == nil {
return &pb.FindFormalClientBrowserWithDataIdResponse{
FormalClientBrowser: nil,
}, nil
}
return &pb.FindFormalClientBrowserWithDataIdResponse{
FormalClientBrowser: &pb.FormalClientBrowser{
Id: int64(browser.Id),
Name: browser.Name,
Codes: browser.DecodeCodes(),
DataId: browser.DataId,
State: types.Int32(browser.State),
}}, nil
}

View File

@@ -0,0 +1,143 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package services
import (
"context"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/types"
)
// FormalClientSystemService 操作系统信息库服务
type FormalClientSystemService struct {
BaseService
}
// CreateFormalClientSystem 创建操作系统信息
func (this *FormalClientSystemService) CreateFormalClientSystem(ctx context.Context, req *pb.CreateFormalClientSystemRequest) (*pb.CreateFormalClientSystemResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
// 检查dataId是否存在
var tx = this.NullTx()
system, err := models.SharedFormalClientSystemDAO.FindSystemWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if system != nil {
return nil, errors.New("dataId '" + req.DataId + "' already exists")
}
systemId, err := models.SharedFormalClientSystemDAO.CreateSystem(tx, req.Name, req.Codes, req.DataId)
if err != nil {
return nil, err
}
return &pb.CreateFormalClientSystemResponse{
FormalClientSystemId: systemId,
}, nil
}
// CountFormalClientSystems 计算操作系统信息数量
func (this *FormalClientSystemService) CountFormalClientSystems(ctx context.Context, req *pb.CountFormalClientSystemsRequest) (*pb.RPCCountResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
count, err := models.SharedFormalClientSystemDAO.CountSystems(tx, req.Keyword)
if err != nil {
return nil, err
}
return this.SuccessCount(count)
}
// ListFormalClientSystems 列出单页操作系统信息
func (this *FormalClientSystemService) ListFormalClientSystems(ctx context.Context, req *pb.ListFormalClientSystemsRequest) (*pb.ListFormalClientSystemsResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
systems, err := models.SharedFormalClientSystemDAO.ListSystems(tx, req.Keyword, req.Offset, req.Size)
if err != nil {
return nil, err
}
var pbSystems = []*pb.FormalClientSystem{}
for _, system := range systems {
pbSystems = append(pbSystems, &pb.FormalClientSystem{
Id: int64(system.Id),
Name: system.Name,
Codes: system.DecodeCodes(),
DataId: system.DataId,
State: types.Int32(system.State),
})
}
return &pb.ListFormalClientSystemsResponse{
FormalClientSystems: pbSystems,
}, nil
}
// UpdateFormalClientSystem 修改操作系统信息
func (this *FormalClientSystemService) UpdateFormalClientSystem(ctx context.Context, req *pb.UpdateFormalClientSystemRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
if len(req.DataId) == 0 {
return nil, errors.New("invalid dataId")
}
var tx = this.NullTx()
// 检查dataId是否已经被使用
oldSystem, err := models.SharedFormalClientSystemDAO.FindSystemWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if oldSystem != nil && int64(oldSystem.Id) != req.FormalClientSystemId {
return nil, errors.New("the dataId '" + req.DataId + "' already has been used")
}
err = models.SharedFormalClientSystemDAO.UpdateSystem(tx, req.FormalClientSystemId, req.Name, req.Codes, req.DataId)
if err != nil {
return nil, err
}
return this.Success()
}
// FindFormalClientSystemWithDataId 通过dataId查询操作系统信息
func (this *FormalClientSystemService) FindFormalClientSystemWithDataId(ctx context.Context, req *pb.FindFormalClientSystemWithDataIdRequest) (*pb.FindFormalClientSystemWithDataIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
system, err := models.SharedFormalClientSystemDAO.FindSystemWithDataId(tx, req.DataId)
if err != nil {
return nil, err
}
if system == nil {
return &pb.FindFormalClientSystemWithDataIdResponse{
FormalClientSystem: nil,
}, nil
}
return &pb.FindFormalClientSystemWithDataIdResponse{
FormalClientSystem: &pb.FormalClientSystem{
Id: int64(system.Id),
Name: system.Name,
Codes: system.DecodeCodes(),
DataId: system.DataId,
State: types.Int32(system.State),
}}, nil
}

View File

@@ -1709,17 +1709,20 @@ func (this *ServerService) UploadServerHTTPRequestStat(ctx context.Context, req
return nil
}
systemId, err := models.SharedClientSystemDAO.FindSystemIdWithNameCacheable(tx, result.Name)
systemId, err := models.SharedFormalClientSystemDAO.FindSystemIdWithNameCacheable(tx, result.Name)
if err != nil {
return err
}
if systemId == 0 {
systemId, err = models.SharedClientSystemDAO.CreateSystem(tx, result.Name)
err = models.SharedClientSystemDAO.CreateSystemIfNotExists(tx, result.Name)
if err != nil {
return err
}
// 直接返回不再进行操作
return nil
}
key := fmt.Sprintf("%d@%d@%s@%s", result.ServerId, systemId, result.Version, month)
var key = fmt.Sprintf("%d@%d@%s@%s", result.ServerId, systemId, result.Version, month)
serverStatLocker.Lock()
serverHTTPSystemStatMap[key] += result.Count
serverStatLocker.Unlock()
@@ -1737,15 +1740,18 @@ func (this *ServerService) UploadServerHTTPRequestStat(ctx context.Context, req
return nil
}
browserId, err := models.SharedClientBrowserDAO.FindBrowserIdWithNameCacheable(tx, result.Name)
browserId, err := models.SharedFormalClientBrowserDAO.FindBrowserIdWithNameCacheable(tx, result.Name)
if err != nil {
return err
}
if browserId == 0 {
browserId, err = models.SharedClientBrowserDAO.CreateBrowser(tx, result.Name)
err = models.SharedClientBrowserDAO.CreateBrowserIfNotExists(tx, result.Name)
if err != nil {
return err
}
// 直接返回不再进行操作
return nil
}
key := fmt.Sprintf("%d@%d@%s@%s", result.ServerId, browserId, result.Version, month)
serverStatLocker.Lock()

View File

@@ -31,13 +31,13 @@ func (this *ServerClientBrowserMonthlyStatService) FindTopServerClientBrowserMon
if err != nil {
return nil, err
}
pbStats := []*pb.FindTopServerClientBrowserMonthlyStatsResponse_Stat{}
var pbStats = []*pb.FindTopServerClientBrowserMonthlyStatsResponse_Stat{}
for _, stat := range statList {
pbStat := &pb.FindTopServerClientBrowserMonthlyStatsResponse_Stat{
Count: int64(stat.Count),
Version: stat.Version,
}
browser, err := models.SharedClientBrowserDAO.FindEnabledClientBrowser(tx, int64(stat.BrowserId))
browser, err := models.SharedFormalClientBrowserDAO.FindEnabledFormalClientBrowser(tx, int64(stat.BrowserId))
if err != nil {
return nil, err
}
@@ -48,7 +48,6 @@ func (this *ServerClientBrowserMonthlyStatService) FindTopServerClientBrowserMon
Id: int64(browser.Id),
Name: browser.Name,
}
pbStats = append(pbStats, pbStat)
}
return &pb.FindTopServerClientBrowserMonthlyStatsResponse{Stats: pbStats}, nil

View File

@@ -31,13 +31,13 @@ func (this *ServerClientSystemMonthlyStatService) FindTopServerClientSystemMonth
if err != nil {
return nil, err
}
pbStats := []*pb.FindTopServerClientSystemMonthlyStatsResponse_Stat{}
var pbStats = []*pb.FindTopServerClientSystemMonthlyStatsResponse_Stat{}
for _, stat := range statList {
pbStat := &pb.FindTopServerClientSystemMonthlyStatsResponse_Stat{
Count: int64(stat.Count),
Version: stat.Version,
}
system, err := models.SharedClientSystemDAO.FindEnabledClientSystem(tx, int64(stat.SystemId))
system, err := models.SharedFormalClientSystemDAO.FindEnabledFormalClientSystem(tx, int64(stat.SystemId))
if err != nil {
return nil, err
}

File diff suppressed because one or more lines are too long

View File

@@ -40,6 +40,14 @@ var recordsTables = []*SQLRecordsTable{
UniqueFields: []string{"name"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeFormalClientSystems",
UniqueFields: []string{"dataId"},
},
{
TableName: "edgeFormalClientBrowsers",
UniqueFields: []string{"dataId"},
},
}
type sqlItem struct {

View File

@@ -89,7 +89,7 @@ var upgradeFuncs = []*upgradeVersion{
"0.5.6", upgradeV0_5_6,
},
{
"0.5.7", upgradeV0_5_7,
"0.5.8", upgradeV0_5_8,
},
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"regexp"
)
@@ -178,7 +179,7 @@ func upgradeV0_5_6(db *dbs.DB) error {
}
// v0.5.7
func upgradeV0_5_7(db *dbs.DB) error {
func upgradeV0_5_8(db *dbs.DB) error {
// node task versions
{
_, err := db.Exec("UPDATE edgeNodeTasks SET version=0 WHERE LENGTH(version)=19")
@@ -187,5 +188,20 @@ func upgradeV0_5_7(db *dbs.DB) error {
}
}
// 删除操作系统和浏览器相关统计
// 只删除当前月,避免因为数据过多阻塞
{
_, err := db.Exec("DELETE FROM edgeServerClientSystemMonthlyStats WHERE month=?", timeutil.Format("Ym"))
if err != nil {
return err
}
}
{
_, err := db.Exec("DELETE FROM edgeServerClientBrowserMonthlyStats WHERE month=?", timeutil.Format("Ym"))
if err != nil {
return err
}
}
return nil
}