新版IP库管理阶段性提交(未完成)

This commit is contained in:
GoEdgeLab
2022-08-13 23:55:48 +08:00
parent 0388d6c557
commit 0e411ff59f
42 changed files with 2628 additions and 172 deletions

View File

@@ -88,6 +88,7 @@ function build() {
mkdir "$DIST"/bin
mkdir "$DIST"/configs
mkdir "$DIST"/logs
mkdir "$DIST"/data
fi
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs/
cp "$ROOT"/configs/db.template.yaml "$DIST"/configs/

View File

@@ -14,23 +14,23 @@ type Admin struct {
UpdatedAt uint64 `field:"updatedAt"` // 修改时间
State uint8 `field:"state"` // 状态
Modules dbs.JSON `field:"modules"` // 允许的模块
CanLogin uint8 `field:"canLogin"` // 是否可以登录
CanLogin bool `field:"canLogin"` // 是否可以登录
Theme string `field:"theme"` // 模板设置
}
type AdminOperator struct {
Id interface{} // ID
IsOn interface{} // 是否启用
Username interface{} // 用户名
Password interface{} // 密码
Fullname interface{} // 全名
IsSuper interface{} // 是否为超级管理员
CreatedAt interface{} // 创建时间
UpdatedAt interface{} // 修改时间
State interface{} // 状态
Modules interface{} // 允许的模块
CanLogin interface{} // 是否可以登录
Theme interface{} // 模板设置
Id any // ID
IsOn any // 是否启用
Username any // 用户名
Password any // 密码
Fullname any // 全名
IsSuper any // 是否为超级管理员
CreatedAt any // 创建时间
UpdatedAt any // 修改时间
State any // 状态
Modules any // 允许的模块
CanLogin any // 是否可以登录
Theme any // 模板设置
}
func NewAdminOperator() *AdminOperator {

View File

@@ -12,23 +12,23 @@ type HTTPCacheTask struct {
Day string `field:"day"` // 创建日期YYYYMMDD
IsDone bool `field:"isDone"` // 是否已完成
IsOk bool `field:"isOk"` // 是否完全成功
IsReady uint8 `field:"isReady"` // 是否已准备好
IsReady bool `field:"isReady"` // 是否已准备好
Description string `field:"description"` // 描述
}
type HTTPCacheTaskOperator struct {
Id interface{} // ID
UserId interface{} // 用户ID
Type interface{} // 任务类型purge|fetch
KeyType interface{} // Key类型
State interface{} // 状态
CreatedAt interface{} // 创建时间
DoneAt interface{} // 完成时间
Day interface{} // 创建日期YYYYMMDD
IsDone interface{} // 是否已完成
IsOk interface{} // 是否完全成功
IsReady interface{} // 是否已准备好
Description interface{} // 描述
Id any // ID
UserId any // 用户ID
Type any // 任务类型purge|fetch
KeyType any // Key类型
State any // 状态
CreatedAt any // 创建时间
DoneAt any // 完成时间
Day any // 创建日期YYYYMMDD
IsDone any // 是否已完成
IsOk any // 是否完全成功
IsReady any // 是否已准备好
Description any // 描述
}
func NewHTTPCacheTaskOperator() *HTTPCacheTaskOperator {

View File

@@ -33,7 +33,7 @@ func init() {
})
}
// 启用条目
// EnableIPLibrary 启用条目
func (this *IPLibraryDAO) EnableIPLibrary(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -42,7 +42,7 @@ func (this *IPLibraryDAO) EnableIPLibrary(tx *dbs.Tx, id int64) error {
return err
}
// 禁用条目
// DisableIPLibrary 禁用条目
func (this *IPLibraryDAO) DisableIPLibrary(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -51,7 +51,7 @@ func (this *IPLibraryDAO) DisableIPLibrary(tx *dbs.Tx, id int64) error {
return err
}
// 查找启用中的条目
// FindEnabledIPLibrary 查找启用中的条目
func (this *IPLibraryDAO) FindEnabledIPLibrary(tx *dbs.Tx, id int64) (*IPLibrary, error) {
result, err := this.Query(tx).
Pk(id).
@@ -63,7 +63,7 @@ func (this *IPLibraryDAO) FindEnabledIPLibrary(tx *dbs.Tx, id int64) (*IPLibrary
return result.(*IPLibrary), err
}
// 查找某个类型的IP库列表
// FindAllEnabledIPLibrariesWithType 查找某个类型的IP库列表
func (this *IPLibraryDAO) FindAllEnabledIPLibrariesWithType(tx *dbs.Tx, libraryType string) (result []*IPLibrary, err error) {
_, err = this.Query(tx).
State(IPLibraryStateEnabled).
@@ -74,7 +74,7 @@ func (this *IPLibraryDAO) FindAllEnabledIPLibrariesWithType(tx *dbs.Tx, libraryT
return
}
// 查找某个类型的最新的IP库
// FindLatestIPLibraryWithType 查找某个类型的最新的IP库
func (this *IPLibraryDAO) FindLatestIPLibraryWithType(tx *dbs.Tx, libraryType string) (*IPLibrary, error) {
one, err := this.Query(tx).
State(IPLibraryStateEnabled).
@@ -90,7 +90,7 @@ func (this *IPLibraryDAO) FindLatestIPLibraryWithType(tx *dbs.Tx, libraryType st
return one.(*IPLibrary), nil
}
// 创建新的IP库
// CreateIPLibrary 创建新的IP库
func (this *IPLibraryDAO) CreateIPLibrary(tx *dbs.Tx, libraryType string, fileId int64) (int64, error) {
var op = NewIPLibraryOperator()
op.Type = libraryType

View File

@@ -0,0 +1,469 @@
package models
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/types"
"os"
"time"
)
const (
IPLibraryFileStateEnabled = 1 // 已启用
IPLibraryFileStateDisabled = 0 // 已禁用
)
type IPLibraryFileDAO dbs.DAO
func NewIPLibraryFileDAO() *IPLibraryFileDAO {
return dbs.NewDAO(&IPLibraryFileDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeIPLibraryFiles",
Model: new(IPLibraryFile),
PkName: "id",
},
}).(*IPLibraryFileDAO)
}
var SharedIPLibraryFileDAO *IPLibraryFileDAO
func init() {
dbs.OnReady(func() {
SharedIPLibraryFileDAO = NewIPLibraryFileDAO()
})
}
// EnableIPLibraryFile 启用条目
func (this *IPLibraryFileDAO) EnableIPLibraryFile(tx *dbs.Tx, id uint64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", IPLibraryFileStateEnabled).
Update()
return err
}
// DisableIPLibraryFile 禁用条目
func (this *IPLibraryFileDAO) DisableIPLibraryFile(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
Set("state", IPLibraryFileStateDisabled).
Update()
return err
}
// FindEnabledIPLibraryFile 查找启用中的条目
func (this *IPLibraryFileDAO) FindEnabledIPLibraryFile(tx *dbs.Tx, id int64) (*IPLibraryFile, error) {
result, err := this.Query(tx).
Pk(id).
State(IPLibraryFileStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*IPLibraryFile), err
}
// CreateLibraryFile 创建文件
func (this *IPLibraryFileDAO) CreateLibraryFile(tx *dbs.Tx, template string, emptyValues []string, fileId int64, countries []string, provinces [][2]string, cities [][3]string, towns [][4]string, providers []string) (int64, error) {
var op = NewIPLibraryFileOperator()
op.Template = template
if emptyValues == nil {
emptyValues = []string{}
}
emptyValuesJSON, err := json.Marshal(emptyValues)
if err != nil {
return 0, err
}
op.EmptyValues = emptyValuesJSON
op.FileId = fileId
if countries == nil {
countries = []string{}
}
countriesJSON, err := json.Marshal(countries)
if err != nil {
return 0, err
}
op.Countries = countriesJSON
if provinces == nil {
provinces = [][2]string{}
}
provincesJSON, err := json.Marshal(provinces)
if err != nil {
return 0, err
}
op.Provinces = provincesJSON
if cities == nil {
cities = [][3]string{}
}
citiesJSON, err := json.Marshal(cities)
if err != nil {
return 0, err
}
op.Cities = citiesJSON
if towns == nil {
towns = [][4]string{}
}
townsJSON, err := json.Marshal(towns)
if err != nil {
return 0, err
}
op.Towns = townsJSON
if providers == nil {
providers = []string{}
}
providersJSON, err := json.Marshal(providers)
if err != nil {
return 0, err
}
op.Providers = providersJSON
op.IsFinished = false
op.State = IPLibraryFileStateEnabled
return this.SaveInt64(tx, op)
}
// FindAllUnfinishedLibraryFiles 查找所有未完成的文件
func (this *IPLibraryFileDAO) FindAllUnfinishedLibraryFiles(tx *dbs.Tx) (result []*IPLibraryFile, err error) {
_, err = this.Query(tx).
State(IPLibraryFileStateEnabled).
Result("id", "fileId", "createdAt"). // 这里不需要其他信息
Attr("isFinished", false).
DescPk().
Slice(&result).
FindAll()
return
}
// UpdateLibraryFileIsFinished 设置文件为已完成
func (this *IPLibraryFileDAO) UpdateLibraryFileIsFinished(tx *dbs.Tx, fileId int64) error {
return this.Query(tx).
Pk(fileId).
Set("isFinished", true).
UpdateQuickly()
}
// FindLibraryFileCountries 获取IP库中的国家/地区
func (this *IPLibraryFileDAO) FindLibraryFileCountries(tx *dbs.Tx, fileId int64) ([]string, error) {
countriesJSON, err := this.Query(tx).
Result("countries").
Pk(fileId).
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(countriesJSON) {
return nil, nil
}
var result = []string{}
err = json.Unmarshal(countriesJSON, &result)
if err != nil {
return nil, err
}
return result, nil
}
// FindLibraryFileProvinces 获取IP库中的省份
func (this *IPLibraryFileDAO) FindLibraryFileProvinces(tx *dbs.Tx, fileId int64) ([][2]string, error) {
provincesJSON, err := this.Query(tx).
Result("provinces").
Pk(fileId).
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(provincesJSON) {
return nil, nil
}
var result = [][2]string{}
err = json.Unmarshal(provincesJSON, &result)
if err != nil {
return nil, err
}
return result, nil
}
// FindLibraryFileCities 获取IP库中的城市
func (this *IPLibraryFileDAO) FindLibraryFileCities(tx *dbs.Tx, fileId int64) ([][3]string, error) {
citiesJSON, err := this.Query(tx).
Result("cities").
Pk(fileId).
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(citiesJSON) {
return nil, nil
}
var result = [][3]string{}
err = json.Unmarshal(citiesJSON, &result)
if err != nil {
return nil, err
}
return result, nil
}
// FindLibraryFileProviders 获取IP库中的ISP运营商
func (this *IPLibraryFileDAO) FindLibraryFileProviders(tx *dbs.Tx, fileId int64) ([]string, error) {
providersJSON, err := this.Query(tx).
Result("providers").
Pk(fileId).
FindJSONCol()
if err != nil {
return nil, err
}
if IsNull(providersJSON) {
return nil, nil
}
var result = []string{}
err = json.Unmarshal(providersJSON, &result)
if err != nil {
return nil, err
}
return result, nil
}
func (this *IPLibraryFileDAO) GenerateIPLibrary(tx *dbs.Tx, libraryFileId int64) error {
one, err := this.Query(tx).Pk(libraryFileId).Find()
if err != nil {
return err
}
if one == nil {
return errors.New("the library file not found")
}
var libraryFile = one.(*IPLibraryFile)
template, err := iplibrary.NewTemplate(libraryFile.Template)
if err != nil {
return errors.New("create template from '" + libraryFile.Template + "' failed: " + err.Error())
}
var fileId = int64(libraryFile.FileId)
if fileId == 0 {
return errors.New("the library file has not been uploaded yet")
}
var dir = Tea.Root + "/data"
stat, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
err = os.Mkdir(dir, 0777)
if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
}
} else {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
}
} else if !stat.IsDir() {
_ = os.Remove(dir)
err = os.Mkdir(dir, 0777)
if err != nil {
return errors.New("can not open dir '" + dir + "' to write: " + err.Error())
}
}
// TODO 删除以往生成的文件,但要考虑到文件正在被别的任务所使用
// 国家
var countries = []*iplibrary.Country{}
// TODO
// 省份
var provinces = []*iplibrary.Province{}
// TODO
// 城市
var cities = []*iplibrary.City{}
// TODO
// 区县
var towns = []*iplibrary.Town{}
// TODO
// ISP运营商
var providers = []*iplibrary.Provider{}
// TODO
var libraryCode = utils.Sha1RandomString() // 每次都生成新的code
var filePath = dir + "/" + this.composeFilename(libraryFileId, libraryCode)
writer, err := iplibrary.NewFileWriter(filePath, &iplibrary.Meta{
Author: "", // 将来用户可以自行填写
CreatedAt: time.Now().Unix(),
Countries: countries,
Provinces: provinces,
Cities: cities,
Towns: towns,
Providers: providers,
})
if err != nil {
return err
}
defer func() {
_ = writer.Close()
}()
err = writer.WriteMeta()
if err != nil {
return errors.New("write meta failed: " + err.Error())
}
chunkIds, err := SharedFileChunkDAO.FindAllFileChunkIds(tx, fileId)
if err != nil {
return err
}
// countries etc ...
var countryMap = map[string]int64{} // countryName => countryId
dbCountries, err := regions.SharedRegionCountryDAO.FindAllCountries(tx)
if err != nil {
return err
}
for _, country := range dbCountries {
for _, code := range country.AllCodes() {
countryMap[code] = int64(country.Id)
}
}
var provinceMap = map[string]int64{} // countryId_provinceName => provinceId
dbProvinces, err := regions.SharedRegionProvinceDAO.FindAllEnabledProvinces(tx)
if err != nil {
return err
}
for _, province := range dbProvinces {
for _, code := range province.AllCodes() {
provinceMap[types.String(province.CountryId)+"_"+code] = int64(province.Id)
}
}
var cityMap = map[string]int64{} // provinceId_cityName => cityId
dbCities, err := regions.SharedRegionCityDAO.FindAllEnabledCities(tx)
if err != nil {
return err
}
for _, city := range dbCities {
for _, code := range city.AllCodes() {
cityMap[types.String(city.ProvinceId)+"_"+code] = int64(city.Id)
}
}
var townMap = map[string]int64{} // cityId_townName => townId
// TODO 将来实现区县
var providerMap = map[string]int64{} // providerName => providerId
dbProviders, err := regions.SharedRegionProviderDAO.FindAllEnabledProviders(tx)
if err != nil {
return err
}
for _, provider := range dbProviders {
for _, code := range provider.AllCodes() {
providerMap[code] = int64(provider.Id)
}
}
dataParser, err := iplibrary.NewParser(&iplibrary.ParserConfig{
Template: template,
EmptyValues: libraryFile.DecodeEmptyValues(),
Iterator: func(values map[string]string) error {
var ipFrom = values["ipFrom"]
var ipTo = values["ipTo"]
var countryName = values["country"]
var provinceName = values["province"]
var cityName = values["city"]
var townName = values["town"]
var providerName = values["provider"]
var countryId = countryMap[countryName]
var provinceId int64
var cityId int64 = 0
var townId int64 = 0
var providerId = providerMap[providerName]
if countryId > 0 {
provinceId = provinceMap[types.String(countryId)+"_"+provinceName]
if provinceId > 0 {
cityId = cityMap[types.String(provinceId)+"_"+cityName]
if cityId > 0 {
townId = townMap[types.String(cityId)+"_"+townName]
}
}
}
err = writer.Write(ipFrom, ipTo, countryId, provinceId, cityId, townId, providerId)
if err != nil {
return errors.New("write failed: " + err.Error())
}
return nil
},
})
if err != nil {
return err
}
for _, chunkId := range chunkIds {
chunk, err := SharedFileChunkDAO.FindFileChunk(tx, chunkId)
if err != nil {
return err
}
if chunk == nil {
return errors.New("invalid chunk file, please upload again")
}
dataParser.Write(chunk.Data)
err = dataParser.Parse()
if err != nil {
return err
}
}
err = writer.Close()
if err != nil {
return err
}
// 设置code
err = this.Query(tx).
Pk(libraryFileId).
Set("code", libraryCode).
Set("isFinished", true).
Set("generatedAt", time.Now().Unix()).
UpdateQuickly()
if err != nil {
return err
}
return nil
}
// 组合IP库文件名
func (this *IPLibraryFileDAO) composeFilename(libraryFileId int64, code string) string {
return "ip-library-" + types.String(libraryFileId) + "-" + code + ".db"
}

View File

@@ -0,0 +1,19 @@
package models_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestIPLibraryFileDAO_GenerateIPLibrary(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := models.SharedIPLibraryFileDAO.GenerateIPLibrary(tx, 3)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,44 @@
package models
import "github.com/iwind/TeaGo/dbs"
// IPLibraryFile IP库上传的文件
type IPLibraryFile struct {
Id uint64 `field:"id"` // ID
FileId uint64 `field:"fileId"` // 原始文件ID
Template string `field:"template"` // 模板
EmptyValues dbs.JSON `field:"emptyValues"` // 空值列表
GeneratedFileId uint64 `field:"generatedFileId"` // 生成的文件ID
GeneratedAt uint64 `field:"generatedAt"` // 生成时间
IsFinished bool `field:"isFinished"` // 是否已经完成
Countries dbs.JSON `field:"countries"` // 国家/地区
Provinces dbs.JSON `field:"provinces"` // 省份
Cities dbs.JSON `field:"cities"` // 城市
Towns dbs.JSON `field:"towns"` // 区县
Providers dbs.JSON `field:"providers"` // ISP服务商
Code string `field:"code"` // 文件代号
CreatedAt uint64 `field:"createdAt"` // 上传时间
State uint8 `field:"state"` // 状态
}
type IPLibraryFileOperator struct {
Id any // ID
FileId any // 原始文件ID
Template any // 模板
EmptyValues any // 空值列表
GeneratedFileId any // 生成的文件ID
GeneratedAt any // 生成时间
IsFinished any // 是否已经完成
Countries any // 国家/地区
Provinces any // 省份
Cities any // 城市
Towns any // 区县
Providers any // ISP服务商
Code any // 文件代号
CreatedAt any // 上传时间
State any // 状态
}
func NewIPLibraryFileOperator() *IPLibraryFileOperator {
return &IPLibraryFileOperator{}
}

View File

@@ -0,0 +1,69 @@
package models
import "encoding/json"
func (this *IPLibraryFile) DecodeCountries() []string {
var countries = []string{}
if IsNotNull(this.Countries) {
err := json.Unmarshal(this.Countries, &countries)
if err != nil {
// ignore error
}
}
return countries
}
func (this *IPLibraryFile) DecodeProvinces() [][2]string {
var provinces = [][2]string{}
if IsNotNull(this.Provinces) {
err := json.Unmarshal(this.Provinces, &provinces)
if err != nil {
// ignore error
}
}
return provinces
}
func (this *IPLibraryFile) DecodeCities() [][3]string {
var cities = [][3]string{}
if IsNotNull(this.Cities) {
err := json.Unmarshal(this.Cities, &cities)
if err != nil {
// ignore error
}
}
return cities
}
func (this *IPLibraryFile) DecodeTowns() [][4]string {
var towns = [][4]string{}
if IsNotNull(this.Towns) {
err := json.Unmarshal(this.Towns, &towns)
if err != nil {
// ignore error
}
}
return towns
}
func (this *IPLibraryFile) DecodeProviders() []string {
var providers = []string{}
if IsNotNull(this.Providers) {
err := json.Unmarshal(this.Providers, &providers)
if err != nil {
// ignore error
}
}
return providers
}
func (this *IPLibraryFile) DecodeEmptyValues() []string {
var result = []string{}
if IsNotNull(this.EmptyValues) {
err := json.Unmarshal(this.EmptyValues, &result)
if err != nil {
// ignore error
}
}
return result
}

View File

@@ -1,22 +1,26 @@
package models
// IP库
// IPLibrary IP库
type IPLibrary struct {
Id uint32 `field:"id"` // ID
AdminId uint32 `field:"adminId"` // 管理员ID
FileId uint32 `field:"fileId"` // 文件ID
Type string `field:"type"` // 类型
Name string `field:"name"` // 名称
IsPublic bool `field:"isPublic"` // 是否公用
State uint8 `field:"state"` // 状态
CreatedAt uint64 `field:"createdAt"` // 创建时间
}
type IPLibraryOperator struct {
Id interface{} // ID
AdminId interface{} // 管理员ID
FileId interface{} // 文件ID
Type interface{} // 类型
State interface{} // 状态
CreatedAt interface{} // 创建时间
Id any // ID
AdminId any // 管理员ID
FileId any // 文件ID
Type any // 类型
Name any // 名称
IsPublic any // 是否公用
State any // 状态
CreatedAt any // 创建时间
}
func NewIPLibraryOperator() *IPLibraryOperator {

View File

@@ -2,11 +2,14 @@ package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"sort"
"strconv"
)
@@ -105,7 +108,18 @@ func (this *RegionCityDAO) CreateCity(tx *dbs.Tx, provinceId int64, name string,
return types.Int64(op.Id), nil
}
// FindCityIdWithNameCacheable 根据城市名查找城市ID
// FindCityIdWithName 根据城市名查找城市ID
func (this *RegionCityDAO) FindCityIdWithName(tx *dbs.Tx, provinceId int64, cityName string) (int64, error) {
return this.Query(tx).
Attr("provinceId", provinceId).
Where("(name=:cityName OR customName=:cityName OR JSON_CONTAINS(codes, :cityNameJSON) OR JSON_CONTAINS(customCodes, :cityNameJSON))").
Param("cityName", cityName).
Param("cityNameJSON", strconv.Quote(cityName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
}
// FindCityIdWithNameCacheable 根据城市名查找城市ID并加入缓存
func (this *RegionCityDAO) FindCityIdWithNameCacheable(tx *dbs.Tx, provinceId int64, cityName string) (int64, error) {
key := cityName + "@" + numberutils.FormatInt64(provinceId)
@@ -119,16 +133,19 @@ func (this *RegionCityDAO) FindCityIdWithNameCacheable(tx *dbs.Tx, provinceId in
cityId, err := this.Query(tx).
Attr("provinceId", provinceId).
Where("JSON_CONTAINS(codes, :cityName)").
Param("cityName", strconv.Quote(cityName)). // 查询的需要是个JSON字符串所以这里加双引号
Where("(name=:cityName OR customName=:cityName OR JSON_CONTAINS(codes, :cityNameJSON) OR JSON_CONTAINS(customCodes, :cityNameJSON))").
Param("cityName", cityName).
Param("cityNameJSON", strconv.Quote(cityName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if cityId > 0 {
SharedCacheLocker.Lock()
regionCityNameAndIdCacheMap[key] = cityId
SharedCacheLocker.Unlock()
}
return cityId, nil
}
@@ -141,3 +158,75 @@ func (this *RegionCityDAO) FindAllEnabledCities(tx *dbs.Tx) (result []*RegionCit
FindAll()
return
}
// FindAllEnabledCitiesWithProvinceId 获取某个省份下的所有城市
func (this *RegionCityDAO) FindAllEnabledCitiesWithProvinceId(tx *dbs.Tx, provinceId int64) (result []*RegionCity, err error) {
_, err = this.Query(tx).
Attr("provinceId", provinceId).
State(RegionCityStateEnabled).
Slice(&result).
FindAll()
return
}
// UpdateCityCustom 自定义城市信息
func (this *RegionCityDAO) UpdateCityCustom(tx *dbs.Tx, cityId int64, customName string, customCodes []string) error {
if customCodes == nil {
customCodes = []string{}
}
customCodesJSON, err := json.Marshal(customCodes)
if err != nil {
return err
}
defer func() {
SharedCacheLocker.Lock()
regionCityNameAndIdCacheMap = map[string]int64{}
SharedCacheLocker.Unlock()
}()
return this.Query(tx).
Pk(cityId).
Set("customName", customName).
Set("customCodes", customCodesJSON).
UpdateQuickly()
}
// FindSimilarCities 查找类似城市名
func (this *RegionCityDAO) FindSimilarCities(cities []*RegionCity, cityName string, size int) (result []*RegionCity) {
if len(cities) == 0 {
return
}
var similarResult = []maps.Map{}
for _, city := range cities {
var similarityList = []float32{}
for _, code := range city.AllCodes() {
var similarity = utils.Similar(cityName, code)
if similarity > 0 {
similarityList = append(similarityList, similarity)
}
}
if len(similarityList) > 0 {
similarResult = append(similarResult, maps.Map{
"similarity": numberutils.Max(similarityList...),
"city": city,
})
}
}
sort.Slice(similarResult, func(i, j int) bool {
return similarResult[i].GetFloat32("similarity") > similarResult[j].GetFloat32("similarity")
})
if len(similarResult) > size {
similarResult = similarResult[:size]
}
for _, r := range similarResult {
result = append(result, r.Get("city").(*RegionCity))
}
return
}

View File

@@ -2,12 +2,14 @@ package regions
import "github.com/iwind/TeaGo/dbs"
// RegionCity 区域城市
// RegionCity 区域-城市
type RegionCity struct {
Id uint32 `field:"id"` // ID
ProvinceId uint32 `field:"provinceId"` // 省份ID
Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称
CustomCodes dbs.JSON `field:"customCodes"` // 自定义代号
State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 原始数据ID
}
@@ -17,6 +19,8 @@ type RegionCityOperator struct {
ProvinceId interface{} // 省份ID
Name interface{} // 名称
Codes interface{} // 代号
CustomName interface{} // 自定义名称
CustomCodes interface{} // 自定义代号
State interface{} // 状态
DataId interface{} // 原始数据ID
}

View File

@@ -2,17 +2,56 @@ package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/lists"
)
func (this *RegionCity) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
logs.Error(err)
remotelogs.Error("RegionCity.DecodeCodes", err.Error())
}
return result
}
func (this *RegionCity) DecodeCustomCodes() []string {
if len(this.CustomCodes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.CustomCodes, &result)
if err != nil {
remotelogs.Error("RegionCity.DecodeCustomCodes", err.Error())
}
return result
}
func (this *RegionCity) DisplayName() string {
if len(this.CustomName) > 0 {
return this.CustomName
}
return this.Name
}
func (this *RegionCity) AllCodes() []string {
var codes = this.DecodeCodes()
if len(this.Name) > 0 && !lists.ContainsString(codes, this.Name) {
codes = append(codes, this.Name)
}
if len(this.CustomName) > 0 && !lists.ContainsString(codes, this.CustomName) {
codes = append(codes, this.CustomName)
}
for _, code := range this.DecodeCustomCodes() {
if !lists.ContainsString(codes, code) {
codes = append(codes, code)
}
}
return codes
}

View File

@@ -2,11 +2,15 @@ package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/mozillazg/go-pinyin"
"sort"
"strconv"
"strings"
)
@@ -16,6 +20,10 @@ const (
RegionCountryStateDisabled = 0 // 已禁用
)
const (
CountryChinaId = 1
)
var regionCountryNameAndIdCacheMap = map[string]int64{} // country name => id
var regionCountryIdAndNameCacheMap = map[int64]string{} // country id => name
@@ -88,7 +96,9 @@ func (this *RegionCountryDAO) FindRegionCountryName(tx *dbs.Tx, id int64) (strin
return "", err
}
if len(name) > 0 {
regionCountryIdAndNameCacheMap[id] = name
}
return name, nil
}
@@ -103,8 +113,9 @@ func (this *RegionCountryDAO) FindCountryIdWithDataId(tx *dbs.Tx, dataId string)
// FindCountryIdWithName 根据国家名查找国家ID
func (this *RegionCountryDAO) FindCountryIdWithName(tx *dbs.Tx, countryName string) (int64, error) {
return this.Query(tx).
Where("JSON_CONTAINS(codes, :countryName)").
Param("countryName", strconv.Quote(countryName)). // 查询的需要是个JSON字符串所以这里加双引号
Where("(name=:countryName OR JSON_CONTAINS(codes, :countryNameJSON) OR customName=:countryName OR JSON_CONTAINS(customCodes, :countryNameJSON))").
Param("countryName", countryName).
Param("countryNameJSON", strconv.Quote(countryName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
}
@@ -124,9 +135,11 @@ func (this *RegionCountryDAO) FindCountryIdWithNameCacheable(tx *dbs.Tx, country
return 0, err
}
if countryId > 0 {
SharedCacheLocker.Lock()
regionCountryNameAndIdCacheMap[countryName] = countryId
SharedCacheLocker.Unlock()
}
return countryId, nil
}
@@ -160,7 +173,7 @@ func (this *RegionCountryDAO) CreateCountry(tx *dbs.Tx, name string, dataId stri
return types.Int64(op.Id), nil
}
// FindAllEnabledCountriesOrderByPinyin 查找所有可用的国家
// FindAllEnabledCountriesOrderByPinyin 查找所有可用的国家并按拼音排序
func (this *RegionCountryDAO) FindAllEnabledCountriesOrderByPinyin(tx *dbs.Tx) (result []*RegionCountry, err error) {
_, err = this.Query(tx).
State(RegionCountryStateEnabled).
@@ -169,3 +182,76 @@ func (this *RegionCountryDAO) FindAllEnabledCountriesOrderByPinyin(tx *dbs.Tx) (
FindAll()
return
}
// FindAllCountries 查找所有可用的国家
func (this *RegionCountryDAO) FindAllCountries(tx *dbs.Tx) (result []*RegionCountry, err error) {
_, err = this.Query(tx).
State(RegionCountryStateEnabled).
Slice(&result).
AscPk().
FindAll()
return
}
// UpdateCountryCustom 修改国家/地区自定义
func (this *RegionCountryDAO) UpdateCountryCustom(tx *dbs.Tx, countryId int64, customName string, customCodes []string) error {
if customCodes == nil {
customCodes = []string{}
}
customCodesJSON, err := json.Marshal(customCodes)
if err != nil {
return err
}
defer func() {
SharedCacheLocker.Lock()
regionCountryNameAndIdCacheMap = map[string]int64{}
regionCountryIdAndNameCacheMap = map[int64]string{}
SharedCacheLocker.Unlock()
}()
return this.Query(tx).
Pk(countryId).
Set("customName", customName).
Set("customCodes", customCodesJSON).
UpdateQuickly()
}
// FindSimilarCountries 查找类似国家/地区名
func (this *RegionCountryDAO) FindSimilarCountries(countries []*RegionCountry, countryName string, size int) (result []*RegionCountry) {
if len(countries) == 0 {
return
}
var similarResult = []maps.Map{}
for _, country := range countries {
var similarityList = []float32{}
for _, code := range country.AllCodes() {
var similarity = utils.Similar(countryName, code)
if similarity > 0 {
similarityList = append(similarityList, similarity)
}
}
if len(similarityList) > 0 {
similarResult = append(similarResult, maps.Map{
"similarity": numberutils.Max(similarityList...),
"country": country,
})
}
}
sort.Slice(similarResult, func(i, j int) bool {
return similarResult[i].GetFloat32("similarity") > similarResult[j].GetFloat32("similarity")
})
if len(similarResult) > size {
similarResult = similarResult[:size]
}
for _, r := range similarResult {
result = append(result, r.Get("country").(*RegionCountry))
}
return
}

View File

@@ -8,11 +8,29 @@ import (
"time"
)
func TestRegionCountryDAO_FindCountryIdWithName(t *testing.T) {
dbs.NotifyReady()
for _, name := range []string{
"中国",
"中华人民共和国",
"美国",
"美利坚合众国",
"美利坚",
} {
countryId, err := SharedRegionCountryDAO.FindCountryIdWithName(nil, name)
if err != nil {
t.Fatal(err)
}
t.Log(name, ":", countryId)
}
}
func TestRegionCountryDAO_FindCountryIdWithCountryNameCacheable(t *testing.T) {
dbs.NotifyReady()
for i := 0; i < 5; i++ {
now := time.Now()
var now = time.Now()
countryId, err := SharedRegionCountryDAO.FindCountryIdWithNameCacheable(nil, "中国")
if err != nil {
t.Fatal(err)
@@ -20,3 +38,24 @@ func TestRegionCountryDAO_FindCountryIdWithCountryNameCacheable(t *testing.T) {
t.Log("countryId", countryId, time.Since(now).Seconds()*1000, "ms")
}
}
func TestRegionCountryDAO_FindSimilarCountries(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
countries, err := SharedRegionCountryDAO.FindAllCountries(tx)
if err != nil {
t.Fatal(err)
}
for _, countryName := range []string{"中国", "布基纳法索", "哥伦比亚", "德意志共和国", "美利坚", "刚果金"} {
t.Log("====" + countryName + "====")
var countries = SharedRegionCountryDAO.FindSimilarCountries(countries, countryName, 5)
if err != nil {
t.Fatal(err)
}
for _, country := range countries {
t.Log(country.Name, country.AllCodes())
}
}
}

View File

@@ -2,11 +2,13 @@ package regions
import "github.com/iwind/TeaGo/dbs"
// RegionCountry 区域国家|地区
// RegionCountry 区域-国家/地区
type RegionCountry struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称
CustomCodes dbs.JSON `field:"customCodes"` // 自定义代号
State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 原始数据ID
Pinyin dbs.JSON `field:"pinyin"` // 拼音
@@ -16,6 +18,8 @@ type RegionCountryOperator struct {
Id interface{} // ID
Name interface{} // 名称
Codes interface{} // 代号
CustomName interface{} // 自定义名称
CustomCodes interface{} // 自定义代号
State interface{} // 状态
DataId interface{} // 原始数据ID
Pinyin interface{} // 拼音

View File

@@ -2,17 +2,56 @@ package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/lists"
)
func (this *RegionCountry) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
logs.Error(err)
remotelogs.Error("RegionCountry.DecodeCodes", err.Error())
}
return result
}
func (this *RegionCountry) DecodeCustomCodes() []string {
if len(this.CustomCodes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.CustomCodes, &result)
if err != nil {
remotelogs.Error("RegionCountry.DecodeCustomCodes", err.Error())
}
return result
}
func (this *RegionCountry) DisplayName() string {
if len(this.CustomName) > 0 {
return this.CustomName
}
return this.Name
}
func (this *RegionCountry) AllCodes() []string {
var codes = this.DecodeCodes()
if len(this.Name) > 0 && !lists.ContainsString(codes, this.Name) {
codes = append(codes, this.Name)
}
if len(this.CustomName) > 0 && !lists.ContainsString(codes, this.CustomName) {
codes = append(codes, this.CustomName)
}
for _, code := range this.DecodeCustomCodes() {
if !lists.ContainsString(codes, code) {
codes = append(codes, code)
}
}
return codes
}

View File

@@ -2,9 +2,13 @@ package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"sort"
"strconv"
)
@@ -74,7 +78,17 @@ func (this *RegionProviderDAO) FindRegionProviderName(tx *dbs.Tx, id uint32) (st
FindStringCol("")
}
// FindProviderIdWithNameCacheable 根据服务商名称查找服务商ID
// FindProviderIdWithName 根据服务商名称查找服务商ID
func (this *RegionProviderDAO) FindProviderIdWithName(tx *dbs.Tx, providerName string) (int64, error) {
return this.Query(tx).
Where("(name=:providerName OR customName=:providerName OR JSON_CONTAINS(codes, :providerNameJSON) OR JSON_CONTAINS(customCodes, :providerNameJSON))").
Param("providerName", providerName).
Param("providerNameJSON", strconv.Quote(providerName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
}
// FindProviderIdWithNameCacheable 根据服务商名称查找服务商ID并保存进缓存
func (this *RegionProviderDAO) FindProviderIdWithNameCacheable(tx *dbs.Tx, providerName string) (int64, error) {
SharedCacheLocker.RLock()
providerId, ok := regionProviderNameAndIdCacheMap[providerName]
@@ -85,17 +99,20 @@ func (this *RegionProviderDAO) FindProviderIdWithNameCacheable(tx *dbs.Tx, provi
SharedCacheLocker.RUnlock()
providerId, err := this.Query(tx).
Where("JSON_CONTAINS(codes, :providerName)").
Param("providerName", strconv.Quote(providerName)). // 查询的需要是个JSON字符串所以这里加双引号
Where("(name=:providerName OR customName=:providerName OR JSON_CONTAINS(codes, :providerNameJSON) OR JSON_CONTAINS(customCodes, :providerNameJSON))").
Param("providerName", providerName).
Param("providerNameJSON", strconv.Quote(providerName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
if err != nil {
return 0, err
}
if providerId > 0 {
SharedCacheLocker.Lock()
regionProviderNameAndIdCacheMap[providerName] = providerId
SharedCacheLocker.Unlock()
}
return providerId, nil
}
@@ -121,3 +138,65 @@ func (this *RegionProviderDAO) FindAllEnabledProviders(tx *dbs.Tx) (result []*Re
FindAll()
return
}
// UpdateProviderCustom 修改ISP自定义信息
func (this *RegionProviderDAO) UpdateProviderCustom(tx *dbs.Tx, providerId int64, customName string, customCodes []string) error {
if customCodes == nil {
customCodes = []string{}
}
customCodesJSON, err := json.Marshal(customCodes)
if err != nil {
return err
}
defer func() {
SharedCacheLocker.Lock()
regionProviderNameAndIdCacheMap = map[string]int64{}
SharedCacheLocker.Unlock()
}()
return this.Query(tx).
Pk(providerId).
Set("customName", customName).
Set("customCodes", customCodesJSON).
UpdateQuickly()
}
// FindSimilarProviders 查找类似ISP运营商
func (this *RegionProviderDAO) FindSimilarProviders(providers []*RegionProvider, providerName string, size int) (result []*RegionProvider) {
if len(providers) == 0 {
return
}
var similarResult = []maps.Map{}
for _, provider := range providers {
var similarityList = []float32{}
for _, code := range provider.AllCodes() {
var similarity = utils.Similar(providerName, code)
if similarity > 0 {
similarityList = append(similarityList, similarity)
}
}
if len(similarityList) > 0 {
similarResult = append(similarResult, maps.Map{
"similarity": numberutils.Max(similarityList...),
"provider": provider,
})
}
}
sort.Slice(similarResult, func(i, j int) bool {
return similarResult[i].GetFloat32("similarity") > similarResult[j].GetFloat32("similarity")
})
if len(similarResult) > size {
similarResult = similarResult[:size]
}
for _, r := range similarResult {
result = append(result, r.Get("provider").(*RegionProvider))
}
return
}

View File

@@ -2,11 +2,13 @@ package regions
import "github.com/iwind/TeaGo/dbs"
// RegionProvider 区域ISP
// RegionProvider 区域-运营商
type RegionProvider struct {
Id uint32 `field:"id"` // ID
Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称
CustomCodes dbs.JSON `field:"customCodes"` // 自定义代号
State uint8 `field:"state"` // 状态
}
@@ -14,6 +16,8 @@ type RegionProviderOperator struct {
Id interface{} // ID
Name interface{} // 名称
Codes interface{} // 代号
CustomName interface{} // 自定义名称
CustomCodes interface{} // 自定义代号
State interface{} // 状态
}

View File

@@ -2,17 +2,56 @@ package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/lists"
)
func (this *RegionProvider) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
logs.Error(err)
remotelogs.Error("RegionProvider.DecodeCodes", err.Error())
}
return result
}
func (this *RegionProvider) DecodeCustomCodes() []string {
if len(this.CustomCodes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.CustomCodes, &result)
if err != nil {
remotelogs.Error("RegionProvider.DecodeCustomCodes", err.Error())
}
return result
}
func (this *RegionProvider) DisplayName() string {
if len(this.CustomName) > 0 {
return this.CustomName
}
return this.Name
}
func (this *RegionProvider) AllCodes() []string {
var codes = this.DecodeCodes()
if len(this.Name) > 0 && !lists.ContainsString(codes, this.Name) {
codes = append(codes, this.Name)
}
if len(this.CustomName) > 0 && !lists.ContainsString(codes, this.CustomName) {
codes = append(codes, this.CustomName)
}
for _, code := range this.DecodeCustomCodes() {
if !lists.ContainsString(codes, code) {
codes = append(codes, code)
}
}
return codes
}

View File

@@ -2,11 +2,14 @@ package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"sort"
"strconv"
)
@@ -38,7 +41,7 @@ func init() {
})
}
// 启用条目
// EnableRegionProvince 启用条目
func (this *RegionProvinceDAO) EnableRegionProvince(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -47,7 +50,7 @@ func (this *RegionProvinceDAO) EnableRegionProvince(tx *dbs.Tx, id int64) error
return err
}
// 禁用条目
// DisableRegionProvince 禁用条目
func (this *RegionProvinceDAO) DisableRegionProvince(tx *dbs.Tx, id int64) error {
_, err := this.Query(tx).
Pk(id).
@@ -56,7 +59,7 @@ func (this *RegionProvinceDAO) DisableRegionProvince(tx *dbs.Tx, id int64) error
return err
}
// 查找启用中的条目
// FindEnabledRegionProvince 查找启用中的条目
func (this *RegionProvinceDAO) FindEnabledRegionProvince(tx *dbs.Tx, id int64) (*RegionProvince, error) {
result, err := this.Query(tx).
Pk(id).
@@ -68,7 +71,7 @@ func (this *RegionProvinceDAO) FindEnabledRegionProvince(tx *dbs.Tx, id int64) (
return result.(*RegionProvince), err
}
// 根据主键查找名称
// FindRegionProvinceName 根据主键查找名称
func (this *RegionProvinceDAO) FindRegionProvinceName(tx *dbs.Tx, id int64) (string, error) {
return this.Query(tx).
Pk(id).
@@ -76,7 +79,7 @@ func (this *RegionProvinceDAO) FindRegionProvinceName(tx *dbs.Tx, id int64) (str
FindStringCol("")
}
// 根据数据ID查找省份
// FindProvinceIdWithDataId 根据数据ID查找省份
func (this *RegionProvinceDAO) FindProvinceIdWithDataId(tx *dbs.Tx, dataId string) (int64, error) {
return this.Query(tx).
Attr("dataId", dataId).
@@ -84,17 +87,18 @@ func (this *RegionProvinceDAO) FindProvinceIdWithDataId(tx *dbs.Tx, dataId strin
FindInt64Col(0)
}
// 根据省份名查找省份ID
// FindProvinceIdWithName 根据省份名查找省份ID
func (this *RegionProvinceDAO) FindProvinceIdWithName(tx *dbs.Tx, countryId int64, provinceName string) (int64, error) {
return this.Query(tx).
Attr("countryId", countryId).
Where("JSON_CONTAINS(codes, :provinceName)").
Param("provinceName", strconv.Quote(provinceName)). // 查询的需要是个JSON字符串所以这里加双引号
Where("(name=:provinceName OR customName=:provinceName OR JSON_CONTAINS(codes, :provinceNameJSON) OR JSON_CONTAINS(customCodes, :provinceNameJSON))").
Param("provinceName", provinceName).
Param("provinceNameJSON", strconv.Quote(provinceName)). // 查询的需要是个JSON字符串所以这里加双引号
ResultPk().
FindInt64Col(0)
}
// 根据省份名查找省份ID并可使用缓存
// FindProvinceIdWithNameCacheable 根据省份名查找省份ID并可使用缓存
func (this *RegionProvinceDAO) FindProvinceIdWithNameCacheable(tx *dbs.Tx, countryId int64, provinceName string) (int64, error) {
var key = provinceName + "@" + numberutils.FormatInt64(countryId)
@@ -110,14 +114,16 @@ func (this *RegionProvinceDAO) FindProvinceIdWithNameCacheable(tx *dbs.Tx, count
if err != nil {
return 0, err
}
if provinceId > 0 {
SharedCacheLocker.Lock()
regionProvinceNameAndIdCacheMap[key] = provinceId
SharedCacheLocker.Unlock()
}
return provinceId, nil
}
// 创建省份
// CreateProvince 创建省份
func (this *RegionProvinceDAO) CreateProvince(tx *dbs.Tx, countryId int64, name string, dataId string) (int64, error) {
var op = NewRegionProvinceOperator()
op.CountryId = countryId
@@ -138,7 +144,7 @@ func (this *RegionProvinceDAO) CreateProvince(tx *dbs.Tx, countryId int64, name
return types.Int64(op.Id), nil
}
// 查找所有省份
// FindAllEnabledProvincesWithCountryId 查找某个国家/地区的所有省份
func (this *RegionProvinceDAO) FindAllEnabledProvincesWithCountryId(tx *dbs.Tx, countryId int64) (result []*RegionProvince, err error) {
_, err = this.Query(tx).
State(RegionProvinceStateEnabled).
@@ -148,3 +154,76 @@ func (this *RegionProvinceDAO) FindAllEnabledProvincesWithCountryId(tx *dbs.Tx,
FindAll()
return
}
// FindAllEnabledProvinces 查找所有省份
func (this *RegionProvinceDAO) FindAllEnabledProvinces(tx *dbs.Tx) (result []*RegionProvince, err error) {
_, err = this.Query(tx).
State(RegionProvinceStateEnabled).
Asc().
Slice(&result).
FindAll()
return
}
// UpdateProvinceCustom 修改自定义省份信息
func (this *RegionProvinceDAO) UpdateProvinceCustom(tx *dbs.Tx, provinceId int64, customName string, customCodes []string) error {
if customCodes == nil {
customCodes = []string{}
}
customCodesJSON, err := json.Marshal(customCodes)
if err != nil {
return err
}
// 清空缓存
defer func() {
SharedCacheLocker.Lock()
regionProvinceNameAndIdCacheMap = map[string]int64{}
SharedCacheLocker.Unlock()
}()
return this.Query(tx).
Pk(provinceId).
Set("customName", customName).
Set("customCodes", customCodesJSON).
UpdateQuickly()
}
// FindSimilarProvinces 查找类似省份名
func (this *RegionProvinceDAO) FindSimilarProvinces(provinces []*RegionProvince, provinceName string, size int) (result []*RegionProvince) {
if len(provinces) == 0 {
return
}
var similarResult = []maps.Map{}
for _, province := range provinces {
var similarityList = []float32{}
for _, code := range province.AllCodes() {
var similarity = utils.Similar(provinceName, code)
if similarity > 0 {
similarityList = append(similarityList, similarity)
}
}
if len(similarityList) > 0 {
similarResult = append(similarResult, maps.Map{
"similarity": numberutils.Max(similarityList...),
"province": province,
})
}
}
sort.Slice(similarResult, func(i, j int) bool {
return similarResult[i].GetFloat32("similarity") > similarResult[j].GetFloat32("similarity")
})
if len(similarResult) > size {
similarResult = similarResult[:size]
}
for _, r := range similarResult {
result = append(result, r.Get("province").(*RegionProvince))
}
return
}

View File

@@ -7,7 +7,7 @@ import (
"time"
)
func TestRegionProvinceDAO_FindProvinceIdWithProvinceName(t *testing.T) {
func TestRegionProvinceDAO_FindProvinceIdWithNameCacheable(t *testing.T) {
dbs.NotifyReady()
for i := 0; i < 5; i++ {
@@ -29,3 +29,51 @@ func TestRegionProvinceDAO_FindProvinceIdWithProvinceName(t *testing.T) {
t.Log(provinceId, time.Since(now).Seconds()*1000, "ms")
}
}
func TestRegionProvinceDAO_FindProvinceIdWithName(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
for _, name := range []string{
"安徽",
"安徽省",
"广西",
"广西省",
"广西壮族自治区",
"皖",
} {
provinceId, err := SharedRegionProvinceDAO.FindProvinceIdWithName(tx, 1, name)
if err != nil {
t.Fatal(err)
}
t.Log(name, "=>", provinceId)
}
}
func TestRegionProvinceDAO_FindSimilarProvinces(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
var countryId int64 = 1
provinces, err := SharedRegionProvinceDAO.FindAllEnabledProvincesWithCountryId(tx, countryId)
if err != nil {
t.Fatal(err)
}
for _, provinceName := range []string{
"北京",
"北京市",
"安徽",
"安徽省",
"大北京",
} {
t.Log("====" + provinceName + "====")
var provinces = SharedRegionProvinceDAO.FindSimilarProvinces(provinces, provinceName, 5)
if err != nil {
t.Fatal(err)
}
for _, province := range provinces {
t.Log(province.Name, province.AllCodes())
}
}
}

View File

@@ -2,12 +2,14 @@ package regions
import "github.com/iwind/TeaGo/dbs"
// RegionProvince 区域省份
// RegionProvince 区域-省份
type RegionProvince struct {
Id uint32 `field:"id"` // ID
CountryId uint32 `field:"countryId"` // 国家ID
Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称
CustomCodes dbs.JSON `field:"customCodes"` // 自定义代号
State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 原始数据ID
}
@@ -17,6 +19,8 @@ type RegionProvinceOperator struct {
CountryId interface{} // 国家ID
Name interface{} // 名称
Codes interface{} // 代号
CustomName interface{} // 自定义名称
CustomCodes interface{} // 自定义代号
State interface{} // 状态
DataId interface{} // 原始数据ID
}

View File

@@ -2,17 +2,56 @@ package regions
import (
"encoding/json"
"github.com/iwind/TeaGo/logs"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/lists"
)
func (this *RegionProvince) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
result := []string{}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
logs.Error(err)
remotelogs.Error("RegionProvince.DecodeCodes", err.Error())
}
return result
}
func (this *RegionProvince) DecodeCustomCodes() []string {
if len(this.CustomCodes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.CustomCodes, &result)
if err != nil {
remotelogs.Error("RegionProvince.DecodeCustomCodes", err.Error())
}
return result
}
func (this *RegionProvince) DisplayName() string {
if len(this.CustomName) > 0 {
return this.CustomName
}
return this.Name
}
func (this *RegionProvince) AllCodes() []string {
var codes = this.DecodeCodes()
if len(this.Name) > 0 && !lists.ContainsString(codes, this.Name) {
codes = append(codes, this.Name)
}
if len(this.CustomName) > 0 && !lists.ContainsString(codes, this.CustomName) {
codes = append(codes, this.CustomName)
}
for _, code := range this.DecodeCustomCodes() {
if !lists.ContainsString(codes, code) {
codes = append(codes, code)
}
}
return codes
}

View File

@@ -0,0 +1,131 @@
package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"sort"
)
const (
RegionTownStateEnabled = 1 // 已启用
RegionTownStateDisabled = 0 // 已禁用
)
type RegionTownDAO dbs.DAO
func NewRegionTownDAO() *RegionTownDAO {
return dbs.NewDAO(&RegionTownDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeRegionTowns",
Model: new(RegionTown),
PkName: "id",
},
}).(*RegionTownDAO)
}
var SharedRegionTownDAO *RegionTownDAO
func init() {
dbs.OnReady(func() {
SharedRegionTownDAO = NewRegionTownDAO()
})
}
// EnableRegionTown 启用条目
func (this *RegionTownDAO) EnableRegionTown(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", RegionTownStateEnabled).
Update()
return err
}
// DisableRegionTown 禁用条目
func (this *RegionTownDAO) DisableRegionTown(tx *dbs.Tx, id uint32) error {
_, err := this.Query(tx).
Pk(id).
Set("state", RegionTownStateDisabled).
Update()
return err
}
// FindEnabledRegionTown 查找启用中的条目
func (this *RegionTownDAO) FindEnabledRegionTown(tx *dbs.Tx, id uint32) (*RegionTown, error) {
result, err := this.Query(tx).
Pk(id).
Attr("state", RegionTownStateEnabled).
Find()
if result == nil {
return nil, err
}
return result.(*RegionTown), err
}
// FindRegionTownName 根据主键查找名称
func (this *RegionTownDAO) FindRegionTownName(tx *dbs.Tx, id uint32) (string, error) {
return this.Query(tx).
Pk(id).
Result("name").
FindStringCol("")
}
// UpdateTownCustom 修改自定义县级信息
func (this *RegionTownDAO) UpdateTownCustom(tx *dbs.Tx, townId int64, customName string, customCodes []string) error {
if customCodes == nil {
customCodes = []string{}
}
customCodesJSON, err := json.Marshal(customCodes)
if err != nil {
return err
}
return this.Query(tx).
Pk(townId).
Set("customName", customName).
Set("customCodes", customCodesJSON).
UpdateQuickly()
}
// FindSimilarTowns 查找类似区县
func (this *RegionTownDAO) FindSimilarTowns(towns []*RegionTown, townName string, size int) (result []*RegionTown) {
if len(towns) == 0 {
return
}
var similarResult = []maps.Map{}
for _, town := range towns {
var similarityList = []float32{}
for _, code := range town.AllCodes() {
var similarity = utils.Similar(townName, code)
if similarity > 0 {
similarityList = append(similarityList, similarity)
}
}
if len(similarityList) > 0 {
similarResult = append(similarResult, maps.Map{
"similarity": numberutils.Max(similarityList...),
"town": town,
})
}
}
sort.Slice(similarResult, func(i, j int) bool {
return similarResult[i].GetFloat32("similarity") > similarResult[j].GetFloat32("similarity")
})
if len(similarResult) > size {
similarResult = similarResult[:size]
}
for _, r := range similarResult {
result = append(result, r.Get("town").(*RegionTown))
}
return
}

View File

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

View File

@@ -0,0 +1,30 @@
package regions
import "github.com/iwind/TeaGo/dbs"
// RegionTown 区域-省份
type RegionTown struct {
Id uint32 `field:"id"` // ID
CityId uint32 `field:"cityId"` // 城市ID
Name string `field:"name"` // 名称
Codes dbs.JSON `field:"codes"` // 代号
CustomName string `field:"customName"` // 自定义名称
CustomCodes dbs.JSON `field:"customCodes"` // 自定义代号
State uint8 `field:"state"` // 状态
DataId string `field:"dataId"` // 原始数据ID
}
type RegionTownOperator struct {
Id interface{} // ID
CityId interface{} // 城市ID
Name interface{} // 名称
Codes interface{} // 代号
CustomName interface{} // 自定义名称
CustomCodes interface{} // 自定义代号
State interface{} // 状态
DataId interface{} // 原始数据ID
}
func NewRegionTownOperator() *RegionTownOperator {
return &RegionTownOperator{}
}

View File

@@ -0,0 +1,57 @@
package regions
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/iwind/TeaGo/lists"
)
func (this *RegionTown) DecodeCodes() []string {
if len(this.Codes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.Codes, &result)
if err != nil {
remotelogs.Error("RegionTown.DecodeCodes", err.Error())
}
return result
}
func (this *RegionTown) DecodeCustomCodes() []string {
if len(this.CustomCodes) == 0 {
return []string{}
}
var result = []string{}
err := json.Unmarshal(this.CustomCodes, &result)
if err != nil {
remotelogs.Error("RegionTown.DecodeCustomCodes", err.Error())
}
return result
}
func (this *RegionTown) DisplayName() string {
if len(this.CustomName) > 0 {
return this.CustomName
}
return this.Name
}
func (this *RegionTown) AllCodes() []string {
var codes = this.DecodeCodes()
if len(this.Name) > 0 && !lists.ContainsString(codes, this.Name) {
codes = append(codes, this.Name)
}
if len(this.CustomName) > 0 && !lists.ContainsString(codes, this.CustomName) {
codes = append(codes, this.CustomName)
}
for _, code := range this.DecodeCustomCodes() {
if !lists.ContainsString(codes, code) {
codes = append(codes, code)
}
}
return codes
}

View File

@@ -242,6 +242,11 @@ func (this *APINode) registerServices(server *grpc.Server) {
pb.RegisterIPLibraryServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.IPLibraryFileService{}).(*services.IPLibraryFileService)
pb.RegisterIPLibraryFileServiceServer(server, instance)
this.rest(instance)
}
{
var instance = this.serviceInstance(&services.FileChunkService{}).(*services.FileChunkService)
pb.RegisterFileChunkServiceServer(server, instance)

View File

@@ -187,7 +187,7 @@ func (this *AdminService) FindEnabledAdmin(ctx context.Context, req *pb.FindEnab
IsSuper: admin.IsSuper,
Modules: pbModules,
OtpLogin: pbOtpAuth,
CanLogin: admin.CanLogin == 1,
CanLogin: admin.CanLogin,
}
return &pb.FindEnabledAdminResponse{Admin: result}, nil
}
@@ -407,7 +407,7 @@ func (this *AdminService) ListEnabledAdmins(ctx context.Context, req *pb.ListEna
IsSuper: admin.IsSuper,
CreatedAt: int64(admin.CreatedAt),
OtpLogin: pbOtpAuth,
CanLogin: admin.CanLogin == 1,
CanLogin: admin.CanLogin,
})
}

View File

@@ -0,0 +1,495 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package services
import (
"context"
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
)
// IPLibraryFileService IP库文件管理
type IPLibraryFileService struct {
BaseService
}
// FindAllUnfinishedIPLibraryFiles 查找所有未完成的IP库文件
func (this *IPLibraryFileService) FindAllUnfinishedIPLibraryFiles(ctx context.Context, req *pb.FindAllUnfinishedIPLibraryFilesRequest) (*pb.FindAllUnfinishedIPLibraryFilesResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
libraryFiles, err := models.SharedIPLibraryFileDAO.FindAllUnfinishedLibraryFiles(tx)
if err != nil {
return nil, err
}
var pbLibraryFiles = []*pb.IPLibraryFile{}
for _, libraryFile := range libraryFiles {
var pbCountryNames = libraryFile.DecodeCountries()
var pbProviderNames = libraryFile.DecodeProviders()
var pbProvinces = []*pb.IPLibraryFile_Province{}
for _, province := range libraryFile.DecodeProvinces() {
pbProvinces = append(pbProvinces, &pb.IPLibraryFile_Province{
CountryName: province[0],
ProvinceName: province[1],
})
}
var pbCities = []*pb.IPLibraryFile_City{}
for _, city := range libraryFile.DecodeCities() {
pbCities = append(pbCities, &pb.IPLibraryFile_City{
CountryName: city[0],
ProvinceName: city[1],
CityName: city[2],
})
}
var pbTowns = []*pb.IPLibraryFile_Town{}
for _, town := range libraryFile.DecodeTowns() {
pbTowns = append(pbTowns, &pb.IPLibraryFile_Town{
CountryName: town[0],
ProvinceName: town[1],
CityName: town[2],
TownName: town[3],
})
}
pbLibraryFiles = append(pbLibraryFiles, &pb.IPLibraryFile{
Id: int64(libraryFile.Id),
FileId: int64(libraryFile.FileId),
IsFinished: libraryFile.IsFinished,
CreatedAt: int64(libraryFile.CreatedAt),
CountryNames: pbCountryNames,
Provinces: pbProvinces,
Cities: pbCities,
Towns: pbTowns,
ProviderNames: pbProviderNames,
})
}
return &pb.FindAllUnfinishedIPLibraryFilesResponse{
IpLibraryFiles: pbLibraryFiles,
}, nil
}
// FindIPLibraryFile 查找单个IP库文件
func (this *IPLibraryFileService) FindIPLibraryFile(ctx context.Context, req *pb.FindIPLibraryFileRequest) (*pb.FindIPLibraryFileResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
libraryFile, err := models.SharedIPLibraryFileDAO.FindEnabledIPLibraryFile(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
if libraryFile == nil {
return &pb.FindIPLibraryFileResponse{
IpLibraryFile: nil,
}, nil
}
var pbCountryNames = libraryFile.DecodeCountries()
var pbProviderNames = libraryFile.DecodeProviders()
var pbProvinces = []*pb.IPLibraryFile_Province{}
for _, province := range libraryFile.DecodeProvinces() {
pbProvinces = append(pbProvinces, &pb.IPLibraryFile_Province{
CountryName: province[0],
ProvinceName: province[1],
})
}
var pbCities = []*pb.IPLibraryFile_City{}
for _, city := range libraryFile.DecodeCities() {
pbCities = append(pbCities, &pb.IPLibraryFile_City{
CountryName: city[0],
ProvinceName: city[1],
CityName: city[2],
})
}
var pbTowns = []*pb.IPLibraryFile_Town{}
for _, town := range libraryFile.DecodeTowns() {
pbTowns = append(pbTowns, &pb.IPLibraryFile_Town{
CountryName: town[0],
ProvinceName: town[1],
CityName: town[2],
TownName: town[3],
})
}
return &pb.FindIPLibraryFileResponse{IpLibraryFile: &pb.IPLibraryFile{
Id: int64(libraryFile.Id),
FileId: int64(libraryFile.FileId),
IsFinished: libraryFile.IsFinished,
CreatedAt: int64(libraryFile.CreatedAt),
CountryNames: pbCountryNames,
Provinces: pbProvinces,
Cities: pbCities,
Towns: pbTowns,
ProviderNames: pbProviderNames,
}}, nil
}
// CreateIPLibraryFile 创建IP库文件
func (this *IPLibraryFileService) CreateIPLibraryFile(ctx context.Context, req *pb.CreateIPLibraryFileRequest) (*pb.CreateIPLibraryFileResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var countries = []string{}
var provinces = [][2]string{}
var cities = [][3]string{}
var towns = [][4]string{}
var providers = []string{}
err = json.Unmarshal(req.CountriesJSON, &countries)
if err != nil {
return nil, errors.New("decode countries failed: " + err.Error())
}
err = json.Unmarshal(req.ProvincesJSON, &provinces)
if err != nil {
return nil, errors.New("decode provinces failed: " + err.Error())
}
err = json.Unmarshal(req.CitiesJSON, &cities)
if err != nil {
return nil, errors.New("decode cities failed: " + err.Error())
}
err = json.Unmarshal(req.TownsJSON, &towns)
if err != nil {
return nil, errors.New("decode towns failed: " + err.Error())
}
err = json.Unmarshal(req.ProvidersJSON, &providers)
if err != nil {
return nil, errors.New("decode providers failed: " + err.Error())
}
var tx = this.NullTx()
libraryFileId, err := models.SharedIPLibraryFileDAO.CreateLibraryFile(tx, req.Template, req.EmptyValues, req.FileId, countries, provinces, cities, towns, providers)
if err != nil {
return nil, err
}
return &pb.CreateIPLibraryFileResponse{
IpLibraryFileId: libraryFileId,
}, nil
}
// CheckCountriesWithIPLibraryFileId 检查国家/地区
func (this *IPLibraryFileService) CheckCountriesWithIPLibraryFileId(ctx context.Context, req *pb.CheckCountriesWithIPLibraryFileIdRequest) (*pb.CheckCountriesWithIPLibraryFileIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
allCountries, err := regions.SharedRegionCountryDAO.FindAllCountries(tx)
if err != nil {
return nil, err
}
countryNames, err := models.SharedIPLibraryFileDAO.FindLibraryFileCountries(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
var pbMissingCountries = []*pb.CheckCountriesWithIPLibraryFileIdResponse_MissingCountry{}
for _, countryName := range countryNames {
if len(countryName) == 0 {
continue
}
// 检查是否存在
countryId, err := regions.SharedRegionCountryDAO.FindCountryIdWithName(tx, countryName)
if err != nil {
return nil, err
}
if countryId > 0 {
continue
}
var pbMissingCountry = &pb.CheckCountriesWithIPLibraryFileIdResponse_MissingCountry{
CountryName: countryName,
SimilarCountries: nil,
}
// 查找相似
var similarCountries = regions.SharedRegionCountryDAO.FindSimilarCountries(allCountries, countryName, 5)
if err != nil {
return nil, err
}
for _, similarCountry := range similarCountries {
pbMissingCountry.SimilarCountries = append(pbMissingCountry.SimilarCountries, &pb.RegionCountry{
Id: int64(similarCountry.Id),
Name: similarCountry.Name,
DisplayName: similarCountry.DisplayName(),
})
}
pbMissingCountries = append(pbMissingCountries, pbMissingCountry)
}
return &pb.CheckCountriesWithIPLibraryFileIdResponse{
MissingCountries: pbMissingCountries,
}, nil
}
// CheckProvincesWithIPLibraryFileId 检查省份/州
func (this *IPLibraryFileService) CheckProvincesWithIPLibraryFileId(ctx context.Context, req *pb.CheckProvincesWithIPLibraryFileIdRequest) (*pb.CheckProvincesWithIPLibraryFileIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
provinces, err := models.SharedIPLibraryFileDAO.FindLibraryFileProvinces(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
var countryMap = map[string]int64{} // countryName => countryId
var provinceNamesMap = map[int64][][2]string{} // countryId => [][2]{countryName, provinceName}
var countryIds = []int64{}
for _, province := range provinces {
var countryName = province[0]
var provinceName = province[1]
countryId, ok := countryMap[countryName]
if ok {
provinceNamesMap[countryId] = append(provinceNamesMap[countryId], [2]string{countryName, provinceName})
continue
}
countryId, err := regions.SharedRegionCountryDAO.FindCountryIdWithName(tx, countryName)
if err != nil {
return nil, err
}
countryMap[countryName] = countryId
provinceNamesMap[countryId] = append(provinceNamesMap[countryId], [2]string{countryName, provinceName})
if countryId > 0 && !lists.ContainsInt64(countryIds, countryId) {
countryIds = append(countryIds, countryId)
}
}
var pbMissingProvinces = []*pb.CheckProvincesWithIPLibraryFileIdResponse_MissingProvince{}
for _, countryId := range countryIds {
allProvinces, err := regions.SharedRegionProvinceDAO.FindAllEnabledProvincesWithCountryId(tx, countryId)
if err != nil {
return nil, err
}
for _, province := range provinceNamesMap[countryId] {
var countryName = province[0]
var provinceName = province[1]
provinceId, err := regions.SharedRegionProvinceDAO.FindProvinceIdWithName(tx, countryId, provinceName)
if err != nil {
return nil, err
}
if provinceId > 0 {
continue
}
var similarProvinces = regions.SharedRegionProvinceDAO.FindSimilarProvinces(allProvinces, provinceName, 5)
if err != nil {
return nil, err
}
var pbMissingProvince = &pb.CheckProvincesWithIPLibraryFileIdResponse_MissingProvince{}
pbMissingProvince.CountryName = countryName
pbMissingProvince.ProvinceName = provinceName
for _, similarProvince := range similarProvinces {
pbMissingProvince.SimilarProvinces = append(pbMissingProvince.SimilarProvinces, &pb.RegionProvince{
Id: int64(similarProvince.Id),
Name: similarProvince.Name,
DisplayName: similarProvince.DisplayName(),
})
}
pbMissingProvinces = append(pbMissingProvinces, pbMissingProvince)
}
}
return &pb.CheckProvincesWithIPLibraryFileIdResponse{MissingProvinces: pbMissingProvinces}, nil
}
// CheckCitiesWithIPLibraryFileId 检查城市/市
func (this *IPLibraryFileService) CheckCitiesWithIPLibraryFileId(ctx context.Context, req *pb.CheckCitiesWithIPLibraryFileIdRequest) (*pb.CheckCitiesWithIPLibraryFileIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
cities, err := models.SharedIPLibraryFileDAO.FindLibraryFileCities(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
var countryMap = map[string]int64{} // countryName => countryId
var provinceMap = map[string]int64{} // countryId_provinceName => provinceId
var provinceNamesMap = map[int64][][3]string{} // provinceId => [][3]{countryName, provinceName, cityName}
var provinceIds = []int64{}
for _, city := range cities {
var countryName = city[0]
var provinceName = city[1]
var cityName = city[2]
countryId, ok := countryMap[countryName]
if !ok {
countryId, err = regions.SharedRegionCountryDAO.FindCountryIdWithName(tx, countryName)
if err != nil {
return nil, err
}
}
countryMap[countryName] = countryId
var key = types.String(countryId) + "_" + provinceName
provinceId, ok := provinceMap[key]
if ok {
provinceNamesMap[provinceId] = append(provinceNamesMap[provinceId], [3]string{countryName, provinceName, cityName})
} else {
provinceId, err := regions.SharedRegionProvinceDAO.FindProvinceIdWithName(tx, countryId, provinceName)
if err != nil {
return nil, err
}
provinceMap[key] = provinceId
provinceNamesMap[provinceId] = append(provinceNamesMap[provinceId], [3]string{countryName, provinceName, cityName})
if provinceId > 0 {
provinceIds = append(provinceIds, provinceId)
}
}
}
var pbMissingCities = []*pb.CheckCitiesWithIPLibraryFileIdResponse_MissingCity{}
for _, provinceId := range provinceIds {
allCities, err := regions.SharedRegionCityDAO.FindAllEnabledCitiesWithProvinceId(tx, provinceId)
if err != nil {
return nil, err
}
for _, city := range provinceNamesMap[provinceId] {
var countryName = city[0]
var provinceName = city[1]
var cityName = city[2]
cityId, err := regions.SharedRegionCityDAO.FindCityIdWithName(tx, provinceId, cityName)
if err != nil {
return nil, err
}
if cityId > 0 {
continue
}
var similarCities = regions.SharedRegionCityDAO.FindSimilarCities(allCities, cityName, 5)
if err != nil {
return nil, err
}
var pbMissingCity = &pb.CheckCitiesWithIPLibraryFileIdResponse_MissingCity{}
pbMissingCity.CountryName = countryName
pbMissingCity.ProvinceName = provinceName
pbMissingCity.CityName = cityName
for _, similarCity := range similarCities {
pbMissingCity.SimilarCities = append(pbMissingCity.SimilarCities, &pb.RegionCity{
Id: int64(similarCity.Id),
Name: similarCity.Name,
DisplayName: similarCity.DisplayName(),
})
}
pbMissingCities = append(pbMissingCities, pbMissingCity)
}
}
return &pb.CheckCitiesWithIPLibraryFileIdResponse{MissingCities: pbMissingCities}, nil
}
// CheckProvidersWithIPLibraryFileId 检查ISP运营商
func (this *IPLibraryFileService) CheckProvidersWithIPLibraryFileId(ctx context.Context, req *pb.CheckProvidersWithIPLibraryFileIdRequest) (*pb.CheckProvidersWithIPLibraryFileIdResponse, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
allProviders, err := regions.SharedRegionProviderDAO.FindAllEnabledProviders(tx)
if err != nil {
return nil, err
}
providerNames, err := models.SharedIPLibraryFileDAO.FindLibraryFileProviders(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
var pbMissingProviders = []*pb.CheckProvidersWithIPLibraryFileIdResponse_MissingProvider{}
for _, providerName := range providerNames {
if len(providerName) == 0 {
continue
}
// 检查是否存在
providerId, err := regions.SharedRegionProviderDAO.FindProviderIdWithName(tx, providerName)
if err != nil {
return nil, err
}
if providerId > 0 {
continue
}
var pbMissingProvider = &pb.CheckProvidersWithIPLibraryFileIdResponse_MissingProvider{
ProviderName: providerName,
SimilarProviders: nil,
}
// 查找相似
var similarProviders = regions.SharedRegionProviderDAO.FindSimilarProviders(allProviders, providerName, 5)
if err != nil {
return nil, err
}
for _, similarProvider := range similarProviders {
pbMissingProvider.SimilarProviders = append(pbMissingProvider.SimilarProviders, &pb.RegionProvider{
Id: int64(similarProvider.Id),
Name: similarProvider.Name,
DisplayName: similarProvider.DisplayName(),
})
}
pbMissingProviders = append(pbMissingProviders, pbMissingProvider)
}
return &pb.CheckProvidersWithIPLibraryFileIdResponse{
MissingProviders: pbMissingProviders,
}, nil
}
// GenerateIPLibraryFile 生成IP库文件
func (this *IPLibraryFileService) GenerateIPLibraryFile(ctx context.Context, req *pb.GenerateIPLibraryFileRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = models.SharedIPLibraryFileDAO.GenerateIPLibrary(tx, req.IpLibraryFileId)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -14,6 +14,7 @@ type RegionCityService struct {
}
// FindAllEnabledRegionCities 查找所有城市
// Deprecated
func (this *RegionCityService) FindAllEnabledRegionCities(ctx context.Context, req *pb.FindAllEnabledRegionCitiesRequest) (*pb.FindAllEnabledRegionCitiesResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
@@ -51,6 +52,7 @@ func (this *RegionCityService) FindAllEnabledRegionCities(ctx context.Context, r
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
DisplayName: province.DisplayName(),
}
}
@@ -60,6 +62,9 @@ func (this *RegionCityService) FindAllEnabledRegionCities(ctx context.Context, r
Codes: city.DecodeCodes(),
RegionProvinceId: int64(city.ProvinceId),
RegionProvince: pbProvince,
CustomName: city.CustomName,
CustomCodes: city.DecodeCustomCodes(),
DisplayName: city.DisplayName(),
})
}
@@ -68,7 +73,106 @@ func (this *RegionCityService) FindAllEnabledRegionCities(ctx context.Context, r
}, nil
}
// FindAllRegionCities 查找所有城市
func (this *RegionCityService) FindAllRegionCities(ctx context.Context, req *pb.FindAllRegionCitiesRequest) (*pb.FindAllRegionCitiesResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
cities, err := regions.SharedRegionCityDAO.FindAllEnabledCities(tx)
if err != nil {
return nil, err
}
var pbCities = []*pb.RegionCity{}
var provincesMap = map[int64]*regions.RegionProvince{} // provinceId => RegionProvince
for _, city := range cities {
var provinceId = int64(city.ProvinceId)
var pbProvince = &pb.RegionProvince{Id: provinceId}
if req.IncludeRegionProvince {
province, ok := provincesMap[provinceId]
if !ok {
province, err = regions.SharedRegionProvinceDAO.FindEnabledRegionProvince(tx, provinceId)
if err != nil {
return nil, err
}
if province == nil {
continue
}
provincesMap[provinceId] = province
}
pbProvince = &pb.RegionProvince{
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
CustomName: province.CustomName,
CustomCodes: province.DecodeCustomCodes(),
DisplayName: province.DisplayName(),
}
}
pbCities = append(pbCities, &pb.RegionCity{
Id: int64(city.Id),
Name: city.Name,
Codes: city.DecodeCodes(),
RegionProvinceId: int64(city.ProvinceId),
RegionProvince: pbProvince,
CustomName: city.CustomName,
CustomCodes: city.DecodeCustomCodes(),
DisplayName: city.DisplayName(),
})
}
return &pb.FindAllRegionCitiesResponse{
RegionCities: pbCities,
}, nil
}
// FindAllRegionCitiesWithRegionProvinceId 查找某个省份的所有城市
func (this *RegionCityService) FindAllRegionCitiesWithRegionProvinceId(ctx context.Context, req *pb.FindAllRegionCitiesWithRegionProvinceIdRequest) (*pb.FindAllRegionCitiesWithRegionProvinceIdResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
cities, err := regions.SharedRegionCityDAO.FindAllEnabledCitiesWithProvinceId(tx, req.RegionProvinceId)
if err != nil {
return nil, err
}
var pbCities = []*pb.RegionCity{}
for _, city := range cities {
var provinceId = int64(city.ProvinceId)
var pbProvince = &pb.RegionProvince{Id: provinceId}
pbCities = append(pbCities, &pb.RegionCity{
Id: int64(city.Id),
Name: city.Name,
Codes: city.DecodeCodes(),
RegionProvinceId: int64(city.ProvinceId),
RegionProvince: pbProvince,
CustomName: city.CustomName,
CustomCodes: city.DecodeCustomCodes(),
DisplayName: city.DisplayName(),
})
}
return &pb.FindAllRegionCitiesWithRegionProvinceIdResponse{
RegionCities: pbCities,
}, nil
}
// FindEnabledRegionCity 查找单个城市信息
// Deprecated
func (this *RegionCityService) FindEnabledRegionCity(ctx context.Context, req *pb.FindEnabledRegionCityRequest) (*pb.FindEnabledRegionCityResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
@@ -92,6 +196,55 @@ func (this *RegionCityService) FindEnabledRegionCity(ctx context.Context, req *p
Name: city.Name,
Codes: city.DecodeCodes(),
RegionProvinceId: int64(city.ProvinceId),
CustomName: city.CustomName,
CustomCodes: city.DecodeCustomCodes(),
DisplayName: city.DisplayName(),
},
}, nil
}
// FindRegionCity 查找单个城市信息
func (this *RegionCityService) FindRegionCity(ctx context.Context, req *pb.FindRegionCityRequest) (*pb.FindRegionCityResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
city, err := regions.SharedRegionCityDAO.FindEnabledRegionCity(tx, req.RegionCityId)
if err != nil {
return nil, err
}
if city == nil {
return &pb.FindRegionCityResponse{
RegionCity: nil,
}, nil
}
return &pb.FindRegionCityResponse{
RegionCity: &pb.RegionCity{
Id: int64(city.Id),
Name: city.Name,
Codes: city.DecodeCodes(),
RegionProvinceId: int64(city.ProvinceId),
CustomName: city.CustomName,
CustomCodes: city.DecodeCustomCodes(),
DisplayName: city.DisplayName(),
},
}, nil
}
// UpdateRegionCityCustom 修改城市定制信息
func (this *RegionCityService) UpdateRegionCityCustom(ctx context.Context, req *pb.UpdateRegionCityCustomRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = regions.SharedRegionCityDAO.UpdateCityCustom(tx, req.RegionCityId, req.CustomName, req.CustomCodes)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -13,6 +13,7 @@ type RegionCountryService struct {
}
// FindAllEnabledRegionCountries 查找所有的国家列表
// Deprecated
func (this *RegionCountryService) FindAllEnabledRegionCountries(ctx context.Context, req *pb.FindAllEnabledRegionCountriesRequest) (*pb.FindAllEnabledRegionCountriesResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
@@ -43,6 +44,9 @@ func (this *RegionCountryService) FindAllEnabledRegionCountries(ctx context.Cont
Name: country.Name,
Codes: country.DecodeCodes(),
Pinyin: pinyinStrings,
CustomName: country.CustomName,
CustomCodes: country.DecodeCustomCodes(),
DisplayName: country.DisplayName(),
})
}
return &pb.FindAllEnabledRegionCountriesResponse{
@@ -51,6 +55,7 @@ func (this *RegionCountryService) FindAllEnabledRegionCountries(ctx context.Cont
}
// FindEnabledRegionCountry 查找单个国家信息
// Deprecated
func (this *RegionCountryService) FindEnabledRegionCountry(ctx context.Context, req *pb.FindEnabledRegionCountryRequest) (*pb.FindEnabledRegionCountryResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
@@ -72,5 +77,92 @@ func (this *RegionCountryService) FindEnabledRegionCountry(ctx context.Context,
Id: int64(country.Id),
Name: country.Name,
Codes: country.DecodeCodes(),
CustomName: country.CustomName,
CustomCodes: country.DecodeCustomCodes(),
DisplayName: country.DisplayName(),
}}, nil
}
// FindAllRegionCountries 查找所有的国家列表
func (this *RegionCountryService) FindAllRegionCountries(ctx context.Context, req *pb.FindAllRegionCountriesRequest) (*pb.FindAllRegionCountriesResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
countries, err := regions.SharedRegionCountryDAO.FindAllEnabledCountriesOrderByPinyin(tx)
if err != nil {
return nil, err
}
var result = []*pb.RegionCountry{}
for _, country := range countries {
pinyinStrings := []string{}
err = json.Unmarshal(country.Pinyin, &pinyinStrings)
if err != nil {
return nil, err
}
if len(pinyinStrings) == 0 {
continue
}
result = append(result, &pb.RegionCountry{
Id: int64(country.Id),
Name: country.Name,
Codes: country.DecodeCodes(),
Pinyin: pinyinStrings,
CustomName: country.CustomName,
CustomCodes: country.DecodeCustomCodes(),
DisplayName: country.DisplayName(),
})
}
return &pb.FindAllRegionCountriesResponse{
RegionCountries: result,
}, nil
}
// FindRegionCountry 查找单个国家信息
func (this *RegionCountryService) FindRegionCountry(ctx context.Context, req *pb.FindRegionCountryRequest) (*pb.FindRegionCountryResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
country, err := regions.SharedRegionCountryDAO.FindEnabledRegionCountry(tx, req.RegionCountryId)
if err != nil {
return nil, err
}
if country == nil {
return &pb.FindRegionCountryResponse{RegionCountry: nil}, nil
}
return &pb.FindRegionCountryResponse{RegionCountry: &pb.RegionCountry{
Id: int64(country.Id),
Name: country.Name,
Codes: country.DecodeCodes(),
CustomName: country.CustomName,
CustomCodes: country.DecodeCustomCodes(),
DisplayName: country.DisplayName(),
}}, nil
}
// UpdateRegionCountryCustom 修改城市定制信息
func (this *RegionCountryService) UpdateRegionCountryCustom(ctx context.Context, req *pb.UpdateRegionCountryCustomRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = regions.SharedRegionCountryDAO.UpdateCountryCustom(tx, req.RegionCountryId, req.CustomName, req.CustomCodes)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -14,6 +14,7 @@ type RegionProviderService struct {
}
// FindAllEnabledRegionProviders 查找所有ISP
// Deprecated
func (this *RegionProviderService) FindAllEnabledRegionProviders(ctx context.Context, req *pb.FindAllEnabledRegionProvidersRequest) (*pb.FindAllEnabledRegionProvidersResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
@@ -32,6 +33,9 @@ func (this *RegionProviderService) FindAllEnabledRegionProviders(ctx context.Con
Id: int64(provider.Id),
Name: provider.Name,
Codes: provider.DecodeCodes(),
CustomName: provider.CustomName,
CustomCodes: provider.DecodeCustomCodes(),
DisplayName: provider.DisplayName(),
})
}
@@ -41,6 +45,7 @@ func (this *RegionProviderService) FindAllEnabledRegionProviders(ctx context.Con
}
// FindEnabledRegionProvider 查找单个ISP信息
// Deprecated
func (this *RegionProviderService) FindEnabledRegionProvider(ctx context.Context, req *pb.FindEnabledRegionProviderRequest) (*pb.FindEnabledRegionProviderResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
@@ -63,6 +68,84 @@ func (this *RegionProviderService) FindEnabledRegionProvider(ctx context.Context
Id: int64(provider.Id),
Name: provider.Name,
Codes: provider.DecodeCodes(),
CustomName: provider.CustomName,
CustomCodes: provider.DecodeCustomCodes(),
DisplayName: provider.DisplayName(),
},
}, nil
}
// FindAllRegionProviders 查找所有ISP
func (this *RegionProviderService) FindAllRegionProviders(ctx context.Context, req *pb.FindAllRegionProvidersRequest) (*pb.FindAllRegionProvidersResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
providers, err := regions.SharedRegionProviderDAO.FindAllEnabledProviders(tx)
if err != nil {
return nil, err
}
var pbProviders = []*pb.RegionProvider{}
for _, provider := range providers {
pbProviders = append(pbProviders, &pb.RegionProvider{
Id: int64(provider.Id),
Name: provider.Name,
Codes: provider.DecodeCodes(),
CustomName: provider.CustomName,
CustomCodes: provider.DecodeCustomCodes(),
DisplayName: provider.DisplayName(),
})
}
return &pb.FindAllRegionProvidersResponse{
RegionProviders: pbProviders,
}, nil
}
// FindRegionProvider 查找单个ISP信息
func (this *RegionProviderService) FindRegionProvider(ctx context.Context, req *pb.FindRegionProviderRequest) (*pb.FindRegionProviderResponse, error) {
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
provider, err := regions.SharedRegionProviderDAO.FindEnabledRegionProvider(tx, req.RegionProviderId)
if err != nil {
return nil, err
}
if provider == nil {
return &pb.FindRegionProviderResponse{
RegionProvider: nil,
}, nil
}
return &pb.FindRegionProviderResponse{
RegionProvider: &pb.RegionProvider{
Id: int64(provider.Id),
Name: provider.Name,
Codes: provider.DecodeCodes(),
CustomName: provider.CustomName,
CustomCodes: provider.DecodeCustomCodes(),
DisplayName: provider.DisplayName(),
},
}, nil
}
// UpdateRegionProviderCustom 修改城市定制信息
func (this *RegionProviderService) UpdateRegionProviderCustom(ctx context.Context, req *pb.UpdateRegionProviderCustomRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = regions.SharedRegionProviderDAO.UpdateProviderCustom(tx, req.RegionProviderId, req.CustomName, req.CustomCodes)
if err != nil {
return nil, err
}
return this.Success()
}

View File

@@ -12,6 +12,7 @@ type RegionProvinceService struct {
}
// FindAllEnabledRegionProvincesWithCountryId 查找所有省份
// Deprecated
func (this *RegionProvinceService) FindAllEnabledRegionProvincesWithCountryId(ctx context.Context, req *pb.FindAllEnabledRegionProvincesWithCountryIdRequest) (*pb.FindAllEnabledRegionProvincesWithCountryIdResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
@@ -31,6 +32,9 @@ func (this *RegionProvinceService) FindAllEnabledRegionProvincesWithCountryId(ct
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
CustomName: province.CustomName,
CustomCodes: province.DecodeCustomCodes(),
DisplayName: province.DisplayName(),
})
}
@@ -40,6 +44,7 @@ func (this *RegionProvinceService) FindAllEnabledRegionProvincesWithCountryId(ct
}
// FindEnabledRegionProvince 查找单个省份信息
// Deprecated
func (this *RegionProvinceService) FindEnabledRegionProvince(ctx context.Context, req *pb.FindEnabledRegionProvinceRequest) (*pb.FindEnabledRegionProvinceResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
@@ -62,6 +67,85 @@ func (this *RegionProvinceService) FindEnabledRegionProvince(ctx context.Context
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
CustomName: province.CustomName,
CustomCodes: province.DecodeCustomCodes(),
DisplayName: province.DisplayName(),
},
}, nil
}
// FindAllRegionProvincesWithRegionCountryId 查找所有省份
func (this *RegionProvinceService) FindAllRegionProvincesWithRegionCountryId(ctx context.Context, req *pb.FindAllRegionProvincesWithRegionCountryIdRequest) (*pb.FindAllRegionProvincesWithRegionCountryIdResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
provinces, err := regions.SharedRegionProvinceDAO.FindAllEnabledProvincesWithCountryId(tx, req.RegionCountryId)
if err != nil {
return nil, err
}
result := []*pb.RegionProvince{}
for _, province := range provinces {
result = append(result, &pb.RegionProvince{
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
CustomName: province.CustomName,
CustomCodes: province.DecodeCustomCodes(),
DisplayName: province.DisplayName(),
})
}
return &pb.FindAllRegionProvincesWithRegionCountryIdResponse{
RegionProvinces: result,
}, nil
}
// FindRegionProvince 查找单个省份信息
func (this *RegionProvinceService) FindRegionProvince(ctx context.Context, req *pb.FindRegionProvinceRequest) (*pb.FindRegionProvinceResponse, error) {
// 校验请求
_, _, err := this.ValidateNodeId(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
province, err := regions.SharedRegionProvinceDAO.FindEnabledRegionProvince(tx, req.RegionProvinceId)
if err != nil {
return nil, err
}
if province == nil {
return &pb.FindRegionProvinceResponse{RegionProvince: nil}, nil
}
return &pb.FindRegionProvinceResponse{
RegionProvince: &pb.RegionProvince{
Id: int64(province.Id),
Name: province.Name,
Codes: province.DecodeCodes(),
CustomName: province.CustomName,
CustomCodes: province.DecodeCustomCodes(),
DisplayName: province.DisplayName(),
},
}, nil
}
// UpdateRegionProvinceCustom 修改城市定制信息
func (this *RegionProvinceService) UpdateRegionProvinceCustom(ctx context.Context, req *pb.UpdateRegionProvinceCustomRequest) (*pb.RPCSuccess, error) {
_, err := this.ValidateAdmin(ctx)
if err != nil {
return nil, err
}
var tx = this.NullTx()
err = regions.SharedRegionProvinceDAO.UpdateProvinceCustom(tx, req.RegionProvinceId, req.CustomName, req.CustomCodes)
if err != nil {
return nil, err
}
return this.Success()
}

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types"
"regexp"
"strings"
@@ -13,18 +14,22 @@ var recordsTables = []*SQLRecordsTable{
{
TableName: "edgeRegionCities",
UniqueFields: []string{"name", "provinceId"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionCountries",
UniqueFields: []string{"name"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionProvinces",
UniqueFields: []string{"name", "countryId"},
ExceptFields: []string{"customName", "customCodes"},
},
{
TableName: "edgeRegionProviders",
UniqueFields: []string{"name"},
ExceptFields: []string{"customName", "customCodes"},
},
}
@@ -96,8 +101,14 @@ func (this *SQLDump) Dump(db *dbs.DB) (result *SQLDumpResult, err error) {
Id: one.GetInt64("id"),
Values: map[string]string{},
UniqueFields: recordsTable.UniqueFields,
ExceptFields: recordsTable.ExceptFields,
}
for k, v := range one {
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
record.Values[k] = types.String(v)
}
records = append(records, record)
@@ -233,9 +244,9 @@ func (this *SQLDump) Apply(db *dbs.DB, newResult *SQLDumpResult, showLog bool) (
// 对比记录
// +
for _, record := range newTable.Records {
queryArgs := []string{}
queryValues := []interface{}{}
valueStrings := []string{}
var queryArgs = []string{}
var queryValues = []interface{}{}
var valueStrings = []string{}
for _, field := range record.UniqueFields {
queryArgs = append(queryArgs, field+"=?")
queryValues = append(queryValues, record.Values[field])
@@ -254,6 +265,11 @@ func (this *SQLDump) Apply(db *dbs.DB, newResult *SQLDumpResult, showLog bool) (
args := []string{}
values := []interface{}{}
for k, v := range record.Values {
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
// ID需要保留因为各个表格之间需要有对应关系
params = append(params, "`"+k+"`")
args = append(args, "?")
@@ -274,6 +290,12 @@ func (this *SQLDump) Apply(db *dbs.DB, newResult *SQLDumpResult, showLog bool) (
if k == "id" {
continue
}
// 需要排除的字段
if lists.ContainsString(record.ExceptFields, k) {
continue
}
args = append(args, k+"=?")
values = append(values, v)
}

View File

@@ -1,6 +1,7 @@
package setup
import (
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
@@ -9,14 +10,22 @@ type SQLRecord struct {
Id int64 `json:"id"`
Values map[string]string `json:"values"`
UniqueFields []string `json:"uniqueFields"`
ExceptFields []string `json:"exceptFields"`
}
func (this *SQLRecord) ValuesEquals(values maps.Map) bool {
for k, v := range values {
// 跳过ID
if k == "id" {
continue
}
vString := types.String(v)
// 需要排除的字段
if lists.ContainsString(this.ExceptFields, k) {
continue
}
var vString = types.String(v)
if this.Values[k] != vString {
return false
}

View File

@@ -3,4 +3,5 @@ package setup
type SQLRecordsTable struct {
TableName string
UniqueFields []string
ExceptFields []string
}

View File

@@ -9,3 +9,33 @@ func FormatInt64(value int64) string {
func FormatInt(value int) string {
return strconv.Itoa(value)
}
func Max[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64](values ...T) T {
if len(values) == 0 {
return 0
}
var max T
for index, value := range values {
if index == 0 {
max = value
} else if value > max {
max = value
}
}
return max
}
func Min[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64](values ...T) T {
if len(values) == 0 {
return 0
}
var min T
for index, value := range values {
if index == 0 {
min = value
} else if value < min {
min = value
}
}
return min
}

View File

@@ -0,0 +1,20 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package numberutils_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
"testing"
)
func TestMax(t *testing.T) {
t.Log(numberutils.Max[int](1, 2, 3))
t.Log(numberutils.Max[int32](1, 2, 3))
t.Log(numberutils.Max[float32](1.2, 2.3, 3.4))
}
func TestMin(t *testing.T) {
t.Log(numberutils.Min[int](1, 2, 3))
t.Log(numberutils.Min[int32](1, 2, 3))
t.Log(numberutils.Min[float32](1.2, 2.3, 3.4))
}

View File

@@ -2,7 +2,9 @@
package utils
import "strings"
import (
"strings"
)
// SplitStrings 分隔字符串
// 忽略其中为空的片段
@@ -30,3 +32,33 @@ func ContainsStringInsensitive(list []string, search string) bool {
}
return false
}
// Similar 计算相似度
// between 0-1
func Similar(s1 string, s2 string) float32 {
var r1s = []rune(s1)
var r2s = []rune(s2)
var l1 = len(r1s)
var l2 = len(r2s)
if l1 > l2 {
r1s, r2s = r2s, r1s
}
if len(r1s) == 0 {
return 0
}
var count = 0
for _, r := range r1s {
for index, r2 := range r2s {
if r == r2 {
count++
r2s = r2s[index+1:]
break
}
}
}
return (float32(count)/float32(l1) + float32(count)/float32(l2)) / 2
}

View File

@@ -1,22 +1,30 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package utils
package utils_test
import (
"github.com/TeaOSLab/EdgeAPI/internal/utils"
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestSplitStrings(t *testing.T) {
t.Log(SplitStrings("a, b, c", ","))
t.Log(SplitStrings("a, b, c, ", ","))
t.Log(utils.SplitStrings("a, b, c", ","))
t.Log(utils.SplitStrings("a, b, c, ", ","))
}
func TestContainsStringInsensitive(t *testing.T) {
var a = assert.NewAssertion(t)
a.IsTrue(ContainsStringInsensitive([]string{"a", "b", "C"}, "A"))
a.IsTrue(ContainsStringInsensitive([]string{"a", "b", "C"}, "b"))
a.IsTrue(ContainsStringInsensitive([]string{"a", "b", "C"}, "c"))
a.IsFalse(ContainsStringInsensitive([]string{"a", "b", "C"}, "d"))
a.IsTrue(utils.ContainsStringInsensitive([]string{"a", "b", "C"}, "A"))
a.IsTrue(utils.ContainsStringInsensitive([]string{"a", "b", "C"}, "b"))
a.IsTrue(utils.ContainsStringInsensitive([]string{"a", "b", "C"}, "c"))
a.IsFalse(utils.ContainsStringInsensitive([]string{"a", "b", "C"}, "d"))
}
func TestSimilar(t *testing.T) {
t.Log(utils.Similar("", ""))
t.Log(utils.Similar("", "a"))
t.Log(utils.Similar("abc", "bcd"))
t.Log(utils.Similar("efgj", "hijk"))
t.Log(utils.Similar("efgj", "klmn"))
}