Apply .gitignore rules
This commit is contained in:
parent
1b77f62820
commit
ccd2c530cf
9
.idea/goProject.iml
Normal file
9
.idea/goProject.iml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/goProject.iml" filepath="$PROJECT_DIR$/.idea/goProject.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
49
.idea/workspace.xml
Normal file
49
.idea/workspace.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="1cbe937e-e3b5-473c-ae0b-961313bb7e86" name="更改" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="GOROOT" url="file://$PROJECT_DIR$/../../../Program Files/Go" />
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"associatedIndex": 2
|
||||
}]]></component>
|
||||
<component name="ProjectId" id="2qn71YfCvRaIgWjkDctgPm123Ac" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
||||
"go.import.settings.migrated": "true",
|
||||
"go.sdk.automatically.set": "true",
|
||||
"last_opened_file_path": "D:/workspace/e2023/goProject/trunk",
|
||||
"nodejs_package_manager_path": "npm"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-gosdk-5df93f7ad4aa-df9ad98b711f-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-242.22855.85" />
|
||||
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-GO-242.22855.85" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<settings-migrated>true</settings-migrated>
|
||||
</component>
|
||||
</project>
|
||||
1
.svn/entries
Normal file
1
.svn/entries
Normal file
@ -0,0 +1 @@
|
||||
12
|
||||
1
.svn/format
Normal file
1
.svn/format
Normal file
@ -0,0 +1 @@
|
||||
12
|
||||
@ -0,0 +1,34 @@
|
||||
package webServer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// methodAndInOutTypes
|
||||
// @description: 反射的方法和输入、输出参数类型组合类型
|
||||
type methodAndInOutTypes struct {
|
||||
// 反射出来的对应方法对象
|
||||
Method reflect.Value
|
||||
|
||||
// 反射出来的方法的输入参数的类型集合
|
||||
InTypes []reflect.Type
|
||||
|
||||
// 反射出来的方法的输出参数的类型集合
|
||||
OutTypes []reflect.Type
|
||||
}
|
||||
|
||||
// newmethodAndInOutTypes
|
||||
// @description: newmethodAndInOutTypes
|
||||
// parameter:
|
||||
// @_method: _method
|
||||
// @_inTypes: _inTypes
|
||||
// @_outTypes: _outTypes
|
||||
// return:
|
||||
// @*methodAndInOutTypes: methodAndInOutTypes
|
||||
func newmethodAndInOutTypes(_method reflect.Value, _inTypes []reflect.Type, _outTypes []reflect.Type) *methodAndInOutTypes {
|
||||
return &methodAndInOutTypes{
|
||||
Method: _method,
|
||||
InTypes: _inTypes,
|
||||
OutTypes: _outTypes,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
gxpath
|
||||
====
|
||||
gxpath is XPath packages for the Go, that lets you extract data from the custom documents using XPath expression.
|
||||
|
||||
**[XQuery](https://github.com/antchfx/xquery)** : gxpath implemented, lets you extract data from HTML/XML documents using XPath.
|
||||
|
||||
### Features
|
||||
|
||||
#### The basic XPath patterns.
|
||||
|
||||
> The basic XPath patterns cover 90% of the cases that most stylesheets will need.
|
||||
|
||||
- `node` : Selects all child elements with nodeName of node.
|
||||
|
||||
- `*` : Selects all child elements.
|
||||
|
||||
- `@attr` : Selects the attribute attr.
|
||||
|
||||
- `@*` : Selects all attributes.
|
||||
|
||||
- `node()` : Matches an org.w3c.dom.Node.
|
||||
|
||||
- `text()` : Matches a org.w3c.dom.Text node.
|
||||
|
||||
- `comment()` : Matches a comment.
|
||||
|
||||
- `.` : Selects the current node.
|
||||
|
||||
- `..` : Selects the parent of current node.
|
||||
|
||||
- `/` : Selects the document node.
|
||||
|
||||
- `a[expr]` : Select only those nodes matching a which also satisfy the expression expr.
|
||||
|
||||
- `a[n]` : Selects the nth matching node matching a When a filter's expression is a number, XPath selects based on position.
|
||||
|
||||
- `a/b` : For each node matching a, add the nodes matching b to the result.
|
||||
|
||||
- `a//b` : For each node matching a, add the descendant nodes matching b to the result.
|
||||
|
||||
- `//b` : Returns elements in the entire document matching b.
|
||||
|
||||
- `a|b` : All nodes matching a or b.
|
||||
|
||||
#### Node Axes
|
||||
|
||||
- `child::*` : The child axis selects children of the current node.
|
||||
|
||||
- `descendant::*` : The descendant axis selects descendants of the current node. It is equivalent to '//'.
|
||||
|
||||
- `descendant-or-self::*` : Selects descendants including the current node.
|
||||
|
||||
- `attribute::*` : Selects attributes of the current element. It is equivalent to @*
|
||||
|
||||
- `following-sibling::*` : Selects nodes after the current node.
|
||||
|
||||
- `preceding-sibling::*` : Selects nodes before the current node.
|
||||
|
||||
- `following::*` : Selects the first matching node following in document order, excluding descendants.
|
||||
|
||||
- `preceding::*` : Selects the first matching node preceding in document order, excluding ancestors.
|
||||
|
||||
- `parent::*` : Selects the parent if it matches. The '..' pattern from the core is equivalent to 'parent::node()'.
|
||||
|
||||
- `ancestor::*` : Selects matching ancestors.
|
||||
|
||||
- `ancestor-or-self::*` : Selects ancestors including the current node.
|
||||
|
||||
- `self::*` : Selects the current node. '.' is equivalent to 'self::node()'.
|
||||
|
||||
#### Expressions
|
||||
|
||||
The gxpath supported three types: number, boolean, string.
|
||||
|
||||
- `path` : Selects nodes based on the path.
|
||||
|
||||
- `a = b` : Standard comparisons.
|
||||
|
||||
* a = b True if a equals b.
|
||||
* a != b True if a is not equal to b.
|
||||
* a < b True if a is less than b.
|
||||
* a <= b True if a is less than or equal to b.
|
||||
* a > b True if a is greater than b.
|
||||
* a >= b True if a is greater than or equal to b.
|
||||
|
||||
- `a + b` : Arithmetic expressions.
|
||||
|
||||
* `- a` Unary minus
|
||||
* a + b Add
|
||||
* a - b Substract
|
||||
* a * b Multiply
|
||||
* a div b Divide
|
||||
* a mod b Floating point mod, like Java.
|
||||
|
||||
- `(expr)` : Parenthesized expressions.
|
||||
|
||||
- `fun(arg1, ..., argn)` : Function calls.
|
||||
|
||||
* position()
|
||||
* last()
|
||||
* count(node-set)
|
||||
* name()
|
||||
* starts-with(string,string)
|
||||
* normalize-space(string)
|
||||
* substring(string,start[,length])
|
||||
* come more
|
||||
|
||||
- `a or b` : Boolean or.
|
||||
|
||||
- `a and b` : Boolean and.
|
||||
@ -0,0 +1,29 @@
|
||||
package ensureSendUtil
|
||||
|
||||
/*
|
||||
ensureSendUtil 用于推送数据
|
||||
支持TCP和HTTP两种形式,在发送失败时会缓存数据,并在一定时间间隔后重试
|
||||
|
||||
通过NewTCPSender和NewHTTPSender两个接口分别创建TCP和HTTP模式的EnsureSender
|
||||
|
||||
type EnsureSender interface {
|
||||
// 用于发送数据
|
||||
Write(string) error
|
||||
|
||||
// 用于停止发送,此时会自动保存未发送数据
|
||||
Close() error
|
||||
}
|
||||
|
||||
// 创建一个tcp数据发送器
|
||||
// 参数:
|
||||
// _dataFolder 数据存放目录
|
||||
// _address 连接地址
|
||||
func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) {
|
||||
|
||||
|
||||
// 创建一个http数据发送器
|
||||
// 参数:
|
||||
// _dataFolder 数据存放目录
|
||||
// _url 发送地址
|
||||
func NewHTTPSender(_dataFolder, _url string) (EnsureSender, error) {
|
||||
*/
|
||||
@ -0,0 +1,129 @@
|
||||
/*
|
||||
url的格式如下:https://cmq-{$type}-{$region}.api.{$network}.com
|
||||
其最终内容受到以下因素的影响:地域、网络、消息队列模型
|
||||
|
||||
地域
|
||||
gz(广州)、sh(上海)、bj(北京)、shjr(上海金融)、szjr(深圳金融)、hk(中国香港)、cd(成都)、ca(北美)、usw(美西)、use(美东)、in(印度)、th(泰国)、sg(新加坡)
|
||||
|
||||
网络
|
||||
外网接口请求域名后缀:api.qcloud.com
|
||||
内网接口请求域名后缀:api.tencentyun.com
|
||||
|
||||
队列模型
|
||||
请参照下面说明将域名中的 {$region} 替换成相应地域:
|
||||
外网接口请求域名:https://cmq-queue-{$region}.api.qcloud.com
|
||||
内网接口请求域名:http://cmq-queue-{$region}.api.tencentyun.com
|
||||
|
||||
主题模型
|
||||
请参照下面说明将域名中的 {$region} 替换成相应地域:
|
||||
外网接口请求域名:https://cmq-topic-{$region}.api.qcloud.com
|
||||
内网接口请求域名:http://cmq-topic-{$region}.api.tencentyun.com
|
||||
*/
|
||||
package mqMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"goutil/securityUtil"
|
||||
"goutil/stringUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
func getPrefix(network string) string {
|
||||
if network == MQ_NETWORK_INTERNAL {
|
||||
return "http://"
|
||||
}
|
||||
|
||||
return "https://"
|
||||
}
|
||||
|
||||
// // 获取请求url
|
||||
// // region:地域
|
||||
// // network:网络类型:内网、外网
|
||||
// // _type:消息队列类型:消息队列、消息主题
|
||||
// // 返回:
|
||||
// // 请求url
|
||||
// func getHost(region, network, _type string) string {
|
||||
// url := "cmq-{$type}-{$region}.api.{$network}.com"
|
||||
// url = strings.Replace(url, "{$region}", region, 1)
|
||||
// url = strings.Replace(url, "{$network}", network, 1)
|
||||
// url = strings.Replace(url, "{$type}", _type, 1)
|
||||
|
||||
// return url
|
||||
// }
|
||||
|
||||
// 获取请求url todo:切换成tdmq之后需要用这个方法
|
||||
// region:地域
|
||||
// network:网络类型:内网、外网
|
||||
// _type:消息队列类型:消息队列、消息主题
|
||||
// 返回:
|
||||
// 请求url
|
||||
func getHost(region, network, _type string) string {
|
||||
var url string = ""
|
||||
if network == MQ_NETWORK_INTERNAL {
|
||||
url = "{$region}.mqadapter.cmq.{$network}.com"
|
||||
} else {
|
||||
url = "cmq-{$region}.public.{$network}.com"
|
||||
}
|
||||
|
||||
url = strings.Replace(url, "{$region}", region, 1)
|
||||
url = strings.Replace(url, "{$network}", network, 1)
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
func getPath() string {
|
||||
return "/v2/index.php"
|
||||
}
|
||||
|
||||
func getMethod() string {
|
||||
return "POST"
|
||||
}
|
||||
|
||||
// AssembleUrl 组装请求url
|
||||
// 参数
|
||||
// region:地域
|
||||
// network:网络类型:内网、外网
|
||||
// _type:消息队列类型:消息队列、消息主题
|
||||
// secretKey:密钥的key
|
||||
// paramMap:参数字典
|
||||
// 返回值
|
||||
// string:组装好的请求url
|
||||
// string:签名
|
||||
// error:错误
|
||||
func AssembleUrl(region, network, _type, secretKey string, paramMap map[string]string) (url, signature string, err error) {
|
||||
// 1. 申请安全凭证(已经得到)
|
||||
|
||||
// 2. 生成签名串
|
||||
// 2.1、对参数排序
|
||||
// 2.2、拼接请求字符串
|
||||
// 注意:
|
||||
// “参数值”为原始值而非 url 编码后的值。
|
||||
// 若输入参数中包含下划线,则需要将其转换为“.”。(指的是参数的名称,不是参数的值)
|
||||
paramStr := webUtil.AssembleRequestParamSort(paramMap, true)
|
||||
|
||||
// 2.3、拼接签名原文字符串
|
||||
host := getHost(region, network, _type)
|
||||
path := getPath()
|
||||
signatureSource := fmt.Sprintf("%s%s%s?%s", getMethod(), host, path, paramStr)
|
||||
|
||||
// 2.4、生成签名串
|
||||
data, err := securityUtil.HmacSha256(signatureSource, secretKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signature = string(stringUtil.Base64Encode2(data))
|
||||
|
||||
// 3. 签名串编码
|
||||
// 注意:
|
||||
// 生成的签名串并不能直接作为请求参数,需要对其进行 URL 编码。
|
||||
// 如果用户的请求方法是 GET,则对所有请求参数值均需要做 URL 编码。
|
||||
// 如果是POST,则不用进行URL编码
|
||||
// signature = url.QueryEscape(signature)
|
||||
|
||||
// 将签名添加到参数集合中
|
||||
url = fmt.Sprintf("%s%s%s", getPrefix(network), host, path)
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package timeUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetTime(t *testing.T) {
|
||||
timeVal := time.Date(2018, 4, 25, 9, 36, 1, 0, time.Local)
|
||||
timeStr1 := ToDateTimeString2(timeVal)
|
||||
|
||||
utcTime := GetUTCTime(timeVal)
|
||||
timeStr2 := ToDateTimeString2(utcTime)
|
||||
|
||||
if timeStr1 != timeStr2 {
|
||||
t.Errorf("获取UTC时间出错,两个时间不对等")
|
||||
}
|
||||
|
||||
utcTime2 := GetUTCTime(utcTime)
|
||||
timeStr3 := ToDateTimeString2(utcTime2)
|
||||
if timeStr1 != timeStr3 {
|
||||
t.Errorf("两次的UTC时间不对等")
|
||||
}
|
||||
|
||||
utcTime4 := GetLocalTime(utcTime)
|
||||
timeStr4 := ToDateTimeString2(utcTime4)
|
||||
if timeStr4 != timeStr1 {
|
||||
t.Errorf("local变更了时间 time1:%v time4:%v", timeStr1, timeStr4)
|
||||
}
|
||||
|
||||
utcTime5 := GetLocalTime(utcTime)
|
||||
timeStr5 := ToDateTimeString2(utcTime5)
|
||||
if timeStr4 != timeStr5 {
|
||||
t.Errorf("两次的local时间不对等")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,658 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"goutil/xmlUtil/gxpath/xpath"
|
||||
)
|
||||
|
||||
// An XPath query interface.
|
||||
type Query interface {
|
||||
// Select traversing Iterator returns a query matched node xpath.NodeNavigator.
|
||||
Select(Iterator) xpath.NodeNavigator
|
||||
|
||||
// Evaluate evaluates query and returns values of the current query.
|
||||
Evaluate(Iterator) interface{}
|
||||
|
||||
// Test checks a specified xpath.NodeNavigator can passed by the current query.
|
||||
//Test(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
// ContextQuery is returns current node on the Iterator object query.
|
||||
type ContextQuery struct {
|
||||
count int
|
||||
Root bool // Moving to root-level node in the current context Iterator.
|
||||
}
|
||||
|
||||
func (c *ContextQuery) Select(t Iterator) (n xpath.NodeNavigator) {
|
||||
if c.count == 0 {
|
||||
c.count++
|
||||
n = t.Current().Copy()
|
||||
if c.Root {
|
||||
n.MoveToRoot()
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (c *ContextQuery) Evaluate(Iterator) interface{} {
|
||||
c.count = 0
|
||||
return c
|
||||
}
|
||||
|
||||
// AncestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*)
|
||||
type AncestorQuery struct {
|
||||
iterator func() xpath.NodeNavigator
|
||||
|
||||
Self bool
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (a *AncestorQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if a.iterator == nil {
|
||||
node := a.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
first := true
|
||||
a.iterator = func() xpath.NodeNavigator {
|
||||
if first && a.Self {
|
||||
first = false
|
||||
if a.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
for node.MoveToParent() {
|
||||
if !a.Predicate(node) {
|
||||
break
|
||||
}
|
||||
return node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if node := a.iterator(); node != nil {
|
||||
return node
|
||||
}
|
||||
a.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AncestorQuery) Evaluate(t Iterator) interface{} {
|
||||
a.Input.Evaluate(t)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AncestorQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return a.Predicate(n)
|
||||
}
|
||||
|
||||
// AttributeQuery is an XPath attribute node query.(@*)
|
||||
type AttributeQuery struct {
|
||||
iterator func() xpath.NodeNavigator
|
||||
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (a *AttributeQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if a.iterator == nil {
|
||||
node := a.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
a.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
onAttr := node.MoveToNextAttribute()
|
||||
if !onAttr {
|
||||
return nil
|
||||
}
|
||||
if a.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node := a.iterator(); node != nil {
|
||||
return node
|
||||
}
|
||||
a.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AttributeQuery) Evaluate(t Iterator) interface{} {
|
||||
a.Input.Evaluate(t)
|
||||
a.iterator = nil
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AttributeQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return a.Predicate(n)
|
||||
}
|
||||
|
||||
// ChildQuery is an XPath child node query.(child::*)
|
||||
type ChildQuery struct {
|
||||
posit int
|
||||
iterator func() xpath.NodeNavigator
|
||||
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (c *ChildQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if c.iterator == nil {
|
||||
c.posit = 0
|
||||
node := c.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
first := true
|
||||
c.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
if (first && !node.MoveToChild()) || (!first && !node.MoveToNext()) {
|
||||
return nil
|
||||
}
|
||||
first = false
|
||||
if c.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node := c.iterator(); node != nil {
|
||||
c.posit++
|
||||
return node
|
||||
}
|
||||
c.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChildQuery) Evaluate(t Iterator) interface{} {
|
||||
c.Input.Evaluate(t)
|
||||
c.iterator = nil
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ChildQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return c.Predicate(n)
|
||||
}
|
||||
|
||||
// position returns a position of current xpath.NodeNavigator.
|
||||
func (c *ChildQuery) position() int {
|
||||
return c.posit
|
||||
}
|
||||
|
||||
// DescendantQuery is an XPath descendant node query.(descendant::* | descendant-or-self::*)
|
||||
type DescendantQuery struct {
|
||||
iterator func() xpath.NodeNavigator
|
||||
|
||||
Self bool
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (d *DescendantQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if d.iterator == nil {
|
||||
node := d.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
level := 0
|
||||
first := true
|
||||
d.iterator = func() xpath.NodeNavigator {
|
||||
if first && d.Self {
|
||||
first = false
|
||||
if d.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if node.MoveToChild() {
|
||||
level++
|
||||
} else {
|
||||
for {
|
||||
if level == 0 {
|
||||
return nil
|
||||
}
|
||||
if node.MoveToNext() {
|
||||
break
|
||||
}
|
||||
node.MoveToParent()
|
||||
level--
|
||||
}
|
||||
}
|
||||
if d.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node := d.iterator(); node != nil {
|
||||
return node
|
||||
}
|
||||
d.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DescendantQuery) Evaluate(t Iterator) interface{} {
|
||||
d.Input.Evaluate(t)
|
||||
d.iterator = nil
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *DescendantQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return d.Predicate(n)
|
||||
}
|
||||
|
||||
// FollowingQuery is an XPath following node query.(following::*|following-sibling::*)
|
||||
type FollowingQuery struct {
|
||||
iterator func() xpath.NodeNavigator
|
||||
|
||||
Input Query
|
||||
Sibling bool // The matching sibling node of current node.
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (f *FollowingQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if f.iterator == nil {
|
||||
node := f.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
if f.Sibling {
|
||||
f.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
if !node.MoveToNext() {
|
||||
return nil
|
||||
}
|
||||
if f.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var q Query // descendant query
|
||||
f.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
if q == nil {
|
||||
for !node.MoveToNext() {
|
||||
if !node.MoveToParent() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
q = &DescendantQuery{
|
||||
Self: true,
|
||||
Input: &ContextQuery{},
|
||||
Predicate: f.Predicate,
|
||||
}
|
||||
t.Current().MoveTo(node)
|
||||
}
|
||||
if node := q.Select(t); node != nil {
|
||||
return node
|
||||
}
|
||||
q = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node := f.iterator(); node != nil {
|
||||
return node
|
||||
}
|
||||
f.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FollowingQuery) Evaluate(t Iterator) interface{} {
|
||||
f.Input.Evaluate(t)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *FollowingQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return f.Predicate(n)
|
||||
}
|
||||
|
||||
// PrecedingQuery is an XPath preceding node query.(preceding::*)
|
||||
type PrecedingQuery struct {
|
||||
iterator func() xpath.NodeNavigator
|
||||
Input Query
|
||||
Sibling bool // The matching sibling node of current node.
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (p *PrecedingQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
if p.iterator == nil {
|
||||
node := p.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
if p.Sibling {
|
||||
p.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
for !node.MoveToPrevious() {
|
||||
return nil
|
||||
}
|
||||
if p.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var q Query
|
||||
p.iterator = func() xpath.NodeNavigator {
|
||||
for {
|
||||
if q == nil {
|
||||
for !node.MoveToPrevious() {
|
||||
if !node.MoveToParent() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
q = &DescendantQuery{
|
||||
Self: true,
|
||||
Input: &ContextQuery{},
|
||||
Predicate: p.Predicate,
|
||||
}
|
||||
t.Current().MoveTo(node)
|
||||
}
|
||||
if node := q.Select(t); node != nil {
|
||||
return node
|
||||
}
|
||||
q = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if node := p.iterator(); node != nil {
|
||||
return node
|
||||
}
|
||||
p.iterator = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PrecedingQuery) Evaluate(t Iterator) interface{} {
|
||||
p.Input.Evaluate(t)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *PrecedingQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return p.Predicate(n)
|
||||
}
|
||||
|
||||
// ParentQuery is an XPath parent node query.(parent::*)
|
||||
type ParentQuery struct {
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (p *ParentQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
node := p.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
node = node.Copy()
|
||||
if node.MoveToParent() && p.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ParentQuery) Evaluate(t Iterator) interface{} {
|
||||
p.Input.Evaluate(t)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *ParentQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return p.Predicate(n)
|
||||
}
|
||||
|
||||
// SelfQuery is an Self node query.(self::*)
|
||||
type SelfQuery struct {
|
||||
Input Query
|
||||
Predicate func(xpath.NodeNavigator) bool
|
||||
}
|
||||
|
||||
func (s *SelfQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
node := s.Input.Select(t)
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.Predicate(node) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SelfQuery) Evaluate(t Iterator) interface{} {
|
||||
s.Input.Evaluate(t)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SelfQuery) Test(n xpath.NodeNavigator) bool {
|
||||
return s.Predicate(n)
|
||||
}
|
||||
|
||||
// FilterQuery is an XPath query for predicate filter.
|
||||
type FilterQuery struct {
|
||||
Input Query
|
||||
Predicate Query
|
||||
}
|
||||
|
||||
func (f *FilterQuery) do(t Iterator) bool {
|
||||
val := reflect.ValueOf(f.Predicate.Evaluate(t))
|
||||
switch val.Kind() {
|
||||
case reflect.Bool:
|
||||
return val.Bool()
|
||||
case reflect.String:
|
||||
return len(val.String()) > 0
|
||||
case reflect.Float64:
|
||||
pt := float64(getNodePosition(f.Input))
|
||||
return int(val.Float()) == int(pt)
|
||||
default:
|
||||
if q, ok := f.Predicate.(Query); ok {
|
||||
return q.Select(t) != nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FilterQuery) Select(t Iterator) xpath.NodeNavigator {
|
||||
for {
|
||||
node := f.Input.Select(t)
|
||||
if node == nil {
|
||||
return node
|
||||
}
|
||||
node = node.Copy()
|
||||
//fmt.Println(node.LocalName())
|
||||
|
||||
t.Current().MoveTo(node)
|
||||
if f.do(t) {
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FilterQuery) Evaluate(t Iterator) interface{} {
|
||||
f.Input.Evaluate(t)
|
||||
return f
|
||||
}
|
||||
|
||||
// FunctionQuery is an XPath function that call a function to returns
|
||||
// value of current xpath.NodeNavigator node.
|
||||
type XPathFunction struct {
|
||||
Input Query // Node Set
|
||||
Func func(Query, Iterator) interface{} // The xpath function.
|
||||
}
|
||||
|
||||
func (f *XPathFunction) Select(t Iterator) xpath.NodeNavigator {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Evaluate call a specified function that will returns the
|
||||
// following value type: number,string,boolean.
|
||||
func (f *XPathFunction) Evaluate(t Iterator) interface{} {
|
||||
return f.Func(f.Input, t)
|
||||
}
|
||||
|
||||
// XPathConstant is an XPath constant operand.
|
||||
type XPathConstant struct {
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
func (c *XPathConstant) Select(t Iterator) xpath.NodeNavigator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *XPathConstant) Evaluate(t Iterator) interface{} {
|
||||
return c.Val
|
||||
}
|
||||
|
||||
// LogicalExpr is an XPath logical expression.
|
||||
type LogicalExpr struct {
|
||||
Left, Right Query
|
||||
|
||||
Do func(Iterator, interface{}, interface{}) interface{}
|
||||
}
|
||||
|
||||
func (l *LogicalExpr) Select(t Iterator) xpath.NodeNavigator {
|
||||
// When a XPath expr is logical expression.
|
||||
node := t.Current().Copy()
|
||||
val := l.Evaluate(t)
|
||||
switch val.(type) {
|
||||
case bool:
|
||||
if val.(bool) == true {
|
||||
return node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LogicalExpr) Evaluate(t Iterator) interface{} {
|
||||
m := l.Left.Evaluate(t)
|
||||
n := l.Right.Evaluate(t)
|
||||
return l.Do(t, m, n)
|
||||
}
|
||||
|
||||
// NumericExpr is an XPath numeric operator expression.
|
||||
type NumericExpr struct {
|
||||
Left, Right Query
|
||||
|
||||
Do func(interface{}, interface{}) interface{}
|
||||
}
|
||||
|
||||
func (n *NumericExpr) Select(t Iterator) xpath.NodeNavigator {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NumericExpr) Evaluate(t Iterator) interface{} {
|
||||
m := n.Left.Evaluate(t)
|
||||
k := n.Right.Evaluate(t)
|
||||
return n.Do(m, k)
|
||||
}
|
||||
|
||||
type BooleanExpr struct {
|
||||
IsOr bool
|
||||
Left, Right Query
|
||||
iterator func() xpath.NodeNavigator
|
||||
}
|
||||
|
||||
func (b *BooleanExpr) Select(t Iterator) xpath.NodeNavigator {
|
||||
if b.iterator == nil {
|
||||
var list []xpath.NodeNavigator
|
||||
i := 0
|
||||
root := t.Current().Copy()
|
||||
if b.IsOr {
|
||||
for {
|
||||
node := b.Left.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
node = node.Copy()
|
||||
list = append(list, node)
|
||||
}
|
||||
t.Current().MoveTo(root)
|
||||
for {
|
||||
node := b.Right.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
node = node.Copy()
|
||||
list = append(list, node)
|
||||
}
|
||||
} else {
|
||||
var m []xpath.NodeNavigator
|
||||
var n []xpath.NodeNavigator
|
||||
for {
|
||||
node := b.Left.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
node = node.Copy()
|
||||
list = append(m, node)
|
||||
}
|
||||
t.Current().MoveTo(root)
|
||||
for {
|
||||
node := b.Right.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
node = node.Copy()
|
||||
list = append(n, node)
|
||||
}
|
||||
for _, k := range m {
|
||||
for _, j := range n {
|
||||
if k == j {
|
||||
list = append(list, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.iterator = func() xpath.NodeNavigator {
|
||||
if i >= len(list) {
|
||||
return nil
|
||||
}
|
||||
node := list[i]
|
||||
i++
|
||||
return node
|
||||
}
|
||||
}
|
||||
return b.iterator()
|
||||
}
|
||||
|
||||
func (b *BooleanExpr) Evaluate(t Iterator) interface{} {
|
||||
m := b.Left.Evaluate(t)
|
||||
if m.(bool) == b.IsOr {
|
||||
return m
|
||||
}
|
||||
return b.Right.Evaluate(t)
|
||||
}
|
||||
|
||||
func getNodePosition(q Query) int {
|
||||
type Position interface {
|
||||
position() int
|
||||
}
|
||||
if count, ok := q.(Position); ok {
|
||||
return count.position()
|
||||
}
|
||||
return 1
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package fileUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTar(t *testing.T) {
|
||||
path := GetCurrentPath()
|
||||
fmt.Printf("CurrPath:%s\n", path)
|
||||
|
||||
fileName1 := fmt.Sprintf("%s/%s", path, "test1.txt")
|
||||
fileName2 := fmt.Sprintf("%s/%s", path, "test2.txt")
|
||||
|
||||
if err := WriteFile(path, "test1.txt", true, "first line"); err != nil {
|
||||
t.Errorf("there should be no error, but now it is:%s", err)
|
||||
}
|
||||
if err := WriteFile(path, "test2.txt", true, "first line"); err != nil {
|
||||
t.Errorf("there should be no error, but now it is:%s", err)
|
||||
}
|
||||
|
||||
sourceList := make([]string, 0, 2)
|
||||
sourceList = append(sourceList, fileName1)
|
||||
sourceList = append(sourceList, fileName2)
|
||||
target := fmt.Sprintf("%s/%s", path, "test.tar")
|
||||
if err := Tar(sourceList, target); err != nil {
|
||||
t.Errorf("There should be no error, but now it has:%s", err)
|
||||
}
|
||||
|
||||
if fileList, err := GetFileList(path); err != nil {
|
||||
t.Errorf("There should be no error, but now it has:%s", err)
|
||||
} else {
|
||||
for _, item := range fileList {
|
||||
fmt.Printf("item:%s\n", item)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteFile(fileName1)
|
||||
DeleteFile(fileName2)
|
||||
}
|
||||
|
||||
func TestUntar(t *testing.T) {
|
||||
path := GetCurrentPath()
|
||||
fmt.Printf("CurrPath:%s\n", path)
|
||||
|
||||
source := fmt.Sprintf("%s/%s", path, "test.tar")
|
||||
// target := path
|
||||
target := ""
|
||||
if err := Untar(source, target); err != nil {
|
||||
t.Errorf("There should be no error, but now it has:%s", err)
|
||||
}
|
||||
|
||||
if fileList, err := GetFileList(path); err != nil {
|
||||
t.Errorf("There should be no error, but now it has:%s", err)
|
||||
} else {
|
||||
for _, item := range fileList {
|
||||
fmt.Printf("item:%s\n", item)
|
||||
|
||||
if strings.HasSuffix(item, "txt") {
|
||||
if content, err := ReadFileContent(item); err != nil {
|
||||
t.Errorf("There should be no error, but now it has:%s", err)
|
||||
} else {
|
||||
fmt.Printf("content:%s\n", content)
|
||||
}
|
||||
|
||||
DeleteFile(item)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteFile(source)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package logUtilPlus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
Start("http://10.254.0.242:9200", "20008_gs_log", 20008)
|
||||
|
||||
InfoLog("日志测试")
|
||||
WarnLog("日志测试")
|
||||
DebugLog("日志测试")
|
||||
ErrorLog("日志测试")
|
||||
FatalLog("日志测试")
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
Stop()
|
||||
}
|
||||
|
||||
func BenchmarkWrite(b *testing.B) {
|
||||
Start("http://10.254.0.242:9200", "20008_gs_log", 20008)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
InfoLog("日志测试%d", i)
|
||||
WarnLog("日志测试%d", i)
|
||||
DebugLog("日志测试%d", i)
|
||||
ErrorLog("日志测试%d", i)
|
||||
FatalLog("日志测试%d", i)
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
Stop()
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package ini_config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_File(t *testing.T) {
|
||||
foder, _ := os.Getwd()
|
||||
filePath := path.Join(foder, "ini.conf")
|
||||
|
||||
kvmap, err := ParseFile(filePath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(kvmap) != 12 {
|
||||
t.Error("读取的内容数量不正确")
|
||||
return
|
||||
}
|
||||
|
||||
if v, exists := kvmap["log.es.enable"]; exists == false || v != "false" {
|
||||
t.Error("log.es.enable读取的值不正确")
|
||||
}
|
||||
|
||||
if v, exists := kvmap["log.es.url"]; exists == false || v != "" {
|
||||
t.Error("log.es.url读取的值不正确")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_String(t *testing.T) {
|
||||
k, v, err := Parse("log.es.enable")
|
||||
if err == nil {
|
||||
t.Error(fmt.Errorf("解析格式错误"))
|
||||
return
|
||||
}
|
||||
|
||||
k, v, err = Parse("log.es.enable=false #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if k != "log.es.enable" || v != "false" {
|
||||
t.Error("解析的值不正确")
|
||||
}
|
||||
|
||||
k, v, err = Parse("dbconnection=root:moqikaka3309!#@tcp(10.252.0.62:3309)/liangjian2_groupserver_auto_master?charset=utf8&parseTime=true&loc=Local&timeout=30s#数据库连接")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if k != "dbconnection" || v != "root:moqikaka3309!#@tcp(10.252.0.62:3309)/liangjian2_groupserver_auto_master?charset=utf8&parseTime=true&loc=Local&timeout=30s" {
|
||||
t.Error("解析的值不正确")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_Content(t *testing.T) {
|
||||
content := "#配置项说明\nlog.es.enable #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)\nlog.es.url= #(es服务地址)\nlog.es.indexName=1 #(es服务中Index名)\nlog.es.level=info #(debug|info|warn|error|fatal等级,等于或高于配置项则记录)\n\nlog.file.enable=false #(默认false)\nlog.file.path=log #(运行目录下log目录,默认logs)\nlog.file.pre=log #(文件名前缀,默认log)\nlog.file.enableHour=true #(文件以小时划分,格式:yyyyMMddHH,默认true,false 一天一个文件,格式:yyyyMMdd)\nlog.file.level=info\n\nlog.console.enable=false #(默认false)\nlog.console.level=info"
|
||||
kvmap, err := ParseMultipleLines(content)
|
||||
if err == nil {
|
||||
t.Error(fmt.Errorf("配置格式不正确"))
|
||||
return
|
||||
}
|
||||
|
||||
content = "#配置项说明\nlog.es.enable=false #(false,默认值,关闭es日志记录,后续配置可不填写; true 打开日志记录)\nlog.es.url= #(es服务地址)\nlog.es.indexName=1 #(es服务中Index名)\nlog.es.level=info #(debug|info|warn|error|fatal等级,等于或高于配置项则记录)\n\nlog.file.enable=false #(默认false)\nlog.file.path=log #(运行目录下log目录,默认logs)\nlog.file.pre=log #(文件名前缀,默认log)\nlog.file.enableHour=true #(文件以小时划分,格式:yyyyMMddHH,默认true,false 一天一个文件,格式:yyyyMMdd)\nlog.file.level=info\n\nlog.console.enable=false #(默认false)\nlog.console.level=info"
|
||||
kvmap, err = ParseMultipleLines(content)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(kvmap) != 11 {
|
||||
t.Error("读取的内容数量不正确")
|
||||
return
|
||||
}
|
||||
|
||||
if v, exists := kvmap["log.es.enable"]; exists == false || v != "false" {
|
||||
t.Error("log.es.enable读取的值不正确")
|
||||
}
|
||||
|
||||
if v, exists := kvmap["log.es.url"]; exists == false || v != "" {
|
||||
t.Error("log.es.url读取的值不正确")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package ipMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
IP_SERVICE_URL = "http://ipip.7qule.com/query"
|
||||
|
||||
appId := "unittest"
|
||||
appId_wrong := "wrong"
|
||||
appSecret := "c5746980-5d52-4ba9-834f-13d0066ad1d0"
|
||||
appSecret_wrong := "wrong"
|
||||
ip := "117.139.247.210"
|
||||
ip_wrong := "wrong"
|
||||
isDomestic := true
|
||||
continent := ""
|
||||
country := "中国"
|
||||
region := "四川"
|
||||
city := "成都"
|
||||
timeout := 3
|
||||
|
||||
// Test with wrong AppId
|
||||
ipInfoObj, err := Query(appId_wrong, appSecret, ip, isDomestic, timeout)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there is no error")
|
||||
return
|
||||
}
|
||||
|
||||
// Test with wrong AppSecret
|
||||
ipInfoObj, err = Query(appId, appSecret_wrong, ip, isDomestic, timeout)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there is no error")
|
||||
return
|
||||
}
|
||||
|
||||
// Test with wrong IP
|
||||
ipInfoObj, err = Query(appId, appSecret, ip_wrong, isDomestic, timeout)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there is no error")
|
||||
return
|
||||
}
|
||||
|
||||
// Test with correct information and domestic ip
|
||||
ipInfoObj, err = Query(appId, appSecret, ip, isDomestic, timeout)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is one:%s", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("ipInfoObj:%v\n", ipInfoObj)
|
||||
|
||||
if ipInfoObj.Continent != continent {
|
||||
t.Errorf("Expected continent %s, but got %s", continent, ipInfoObj.Continent)
|
||||
}
|
||||
|
||||
if ipInfoObj.Country != country {
|
||||
t.Errorf("Expected country %s, but got %s", country, ipInfoObj.Country)
|
||||
}
|
||||
|
||||
if ipInfoObj.Region != region {
|
||||
t.Errorf("Expected region %s, but got %s", region, ipInfoObj.Region)
|
||||
}
|
||||
|
||||
if ipInfoObj.City != city {
|
||||
t.Errorf("Expected city %s, but got %s", city, ipInfoObj.City)
|
||||
}
|
||||
|
||||
// Test with correct information and foreign ip
|
||||
isDomestic = false
|
||||
continent = "亚洲"
|
||||
country = "中国"
|
||||
region = "四川省"
|
||||
city = "成都"
|
||||
|
||||
ipInfoObj, err = Query(appId, appSecret, ip, isDomestic, timeout)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is one:%s", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("ipInfoObj:%v\n", ipInfoObj)
|
||||
|
||||
if ipInfoObj.Continent != continent {
|
||||
t.Errorf("Expected continent %s, but got %s", continent, ipInfoObj.Continent)
|
||||
}
|
||||
|
||||
if ipInfoObj.Country != country {
|
||||
t.Errorf("Expected country %s, but got %s", country, ipInfoObj.Country)
|
||||
}
|
||||
|
||||
if ipInfoObj.Region != region {
|
||||
t.Errorf("Expected region %s, but got %s", region, ipInfoObj.Region)
|
||||
}
|
||||
|
||||
if ipInfoObj.City != city {
|
||||
t.Errorf("Expected city %s, but got %s", city, ipInfoObj.City)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package fileUtil
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// 对文件进行gzip压缩
|
||||
// source:源文件完整路径
|
||||
// target:目标文件文件夹(如果传空字符串,则为当前文件夹)
|
||||
// 返回值
|
||||
// 错误对象
|
||||
func Gzip(source, target string) error {
|
||||
reader, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// 给目标文件夹赋值,如果传空,则默认为当前文件夹
|
||||
if target == "" {
|
||||
target = filepath.Dir(source)
|
||||
}
|
||||
fileName := filepath.Base(source)
|
||||
|
||||
targetFilePath := filepath.Join(target, fmt.Sprintf("%s.gz", fileName))
|
||||
writer, err := os.Create(targetFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
archiver := gzip.NewWriter(writer)
|
||||
archiver.Name = fileName
|
||||
defer archiver.Close()
|
||||
|
||||
_, err = io.Copy(archiver, reader)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 对文件进行gzip解压缩
|
||||
// source:源文件完整路径
|
||||
// target:目标文件文件夹(解压缩文件的名字是内部自动赋值)
|
||||
// 返回值
|
||||
// 错误对象
|
||||
func UnGzip(source, target string) error {
|
||||
reader, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
archive, err := gzip.NewReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
// 给目标文件夹赋值,如果传空,则默认为当前文件夹
|
||||
if target == "" {
|
||||
target = filepath.Dir(source)
|
||||
}
|
||||
|
||||
targetFilePath := filepath.Join(target, archive.Name)
|
||||
writer, err := os.Create(targetFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
_, err = io.Copy(writer, archive)
|
||||
|
||||
return err
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package logUtil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
impl_console "goutil/logUtil/impl-console"
|
||||
impl_es "goutil/logUtil/impl-es"
|
||||
impl_localfile "goutil/logUtil/impl-localfile"
|
||||
)
|
||||
|
||||
func TestAllLog(t *testing.T) {
|
||||
file, _ := exec.LookPath(os.Args[0])
|
||||
path, _ := filepath.Abs(file)
|
||||
logPath := filepath.Dir(path)
|
||||
|
||||
GetLocalFileLog().SetLogPath(logPath)
|
||||
|
||||
//添加控制台日志
|
||||
consoleLog := impl_console.NewLogger()
|
||||
|
||||
//添加es日志
|
||||
urls := []string{"http://10.1.0.71:9101/"}
|
||||
eslog, err := impl_es.NewLogger(urls, "", "", "es_log_test", "les_log_test_innerid", nil)
|
||||
if err != nil {
|
||||
t.Error("esLog 创建失败")
|
||||
}
|
||||
SettingLogs([]ILog{consoleLog, eslog, impl_localfile.NewLogger()})
|
||||
|
||||
for i := 1; i < 10; i++ {
|
||||
InfoLog("Info记录")
|
||||
InfoLog("Info记录2:%v %v", i, time.Now())
|
||||
|
||||
DebugLog("Debug记录")
|
||||
DebugLog("Debug记录2:%v %v", i, time.Now())
|
||||
|
||||
WarnLog("Warn记录")
|
||||
WarnLog("Warn记录2:%v %v", i, time.Now())
|
||||
|
||||
ErrorLog("Error记录")
|
||||
ErrorLog("ErrorLog记录2:%v %v", i, time.Now())
|
||||
|
||||
FatalLog("Fatal记录")
|
||||
FatalLog("Fatal记录2:%v %v", i, time.Now())
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
Close(true)
|
||||
}
|
||||
|
||||
func BenchmarkInfoLog(b *testing.B) {
|
||||
file, _ := exec.LookPath(os.Args[0])
|
||||
path, _ := filepath.Abs(file)
|
||||
logPath := filepath.Dir(path)
|
||||
|
||||
GetLocalFileLog().SetLogPath(logPath)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
DebugLog("Debug 记录")
|
||||
InfoLog("info记录 :%v", time.Now())
|
||||
}
|
||||
Close(true)
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package managecenterMgr
|
||||
|
||||
// ManageCenter数据获取开关(每一类数据一个开关)
|
||||
type ManageCenterDataSwitch struct {
|
||||
// 获取所有数据开关(只要这个开关值为true,则不论各类数据的开关是否打开,都获取数据)
|
||||
AllDataSwitch bool
|
||||
|
||||
// 获取合作商数据开关
|
||||
PartnerDataSwitch bool
|
||||
|
||||
// 获取服务器数据开关
|
||||
ServerDataSwitch bool
|
||||
|
||||
// 获取服务器组数开关
|
||||
ServerGroupDataSwitch bool
|
||||
|
||||
// 获取资源包版本数据开关
|
||||
ResourceVersionDataSwitch bool
|
||||
|
||||
// 获取白名单数据开关
|
||||
WhiteListDataSwitch bool
|
||||
|
||||
// 获取大区数据开关
|
||||
AreaDataSwitch bool
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
module admincenter
|
||||
|
||||
go 1.22.2
|
||||
|
||||
replace (
|
||||
common => ../common
|
||||
framework => ../../framework
|
||||
goutil => ../../goutil
|
||||
)
|
||||
|
||||
require (
|
||||
common v0.0.0-00010101000000-000000000000
|
||||
goutil v0.0.0-20230425160006-b2d0b0a0b0b0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
framework v0.0.0-20230425160006-b2d0b0a0b0b0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gomodule/redigo v1.8.9 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/jinzhu/gorm v1.9.12 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
gorm.io/gorm v1.25.12 // indirect
|
||||
)
|
||||
@ -0,0 +1,259 @@
|
||||
package timeUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
// format time like java, such as: yyyy-MM-dd HH:mm:ss
|
||||
// t:时间
|
||||
// format:格式化字符串
|
||||
// 返回值:
|
||||
// 格式化后的字符串
|
||||
func Format(t time.Time, format string) string {
|
||||
//year
|
||||
if strings.ContainsAny(format, "y") {
|
||||
year := strconv.Itoa(t.Year())
|
||||
|
||||
if strings.Count(format, "yy") == 1 && strings.Count(format, "y") == 2 {
|
||||
format = strings.Replace(format, "yy", year[2:], 1)
|
||||
} else if strings.Count(format, "yyyy") == 1 && strings.Count(format, "y") == 4 {
|
||||
format = strings.Replace(format, "yyyy", year, 1)
|
||||
} else {
|
||||
panic("format year error! please 'yyyy' or 'yy'")
|
||||
}
|
||||
}
|
||||
|
||||
//month
|
||||
if strings.ContainsAny(format, "M") {
|
||||
var month string
|
||||
|
||||
if int(t.Month()) < 10 {
|
||||
month = "0" + strconv.Itoa(int(t.Month()))
|
||||
} else {
|
||||
month = strconv.Itoa(int(t.Month()))
|
||||
}
|
||||
|
||||
if strings.Count(format, "MM") == 1 && strings.Count(format, "M") == 2 {
|
||||
format = strings.Replace(format, "MM", month, 1)
|
||||
} else {
|
||||
panic("format month error! please 'MM'")
|
||||
}
|
||||
}
|
||||
|
||||
//day
|
||||
if strings.ContainsAny(format, "d") {
|
||||
var day string
|
||||
|
||||
if t.Day() < 10 {
|
||||
day = "0" + strconv.Itoa(t.Day())
|
||||
} else {
|
||||
day = strconv.Itoa(t.Day())
|
||||
}
|
||||
|
||||
if strings.Count(format, "dd") == 1 && strings.Count(format, "d") == 2 {
|
||||
format = strings.Replace(format, "dd", day, 1)
|
||||
} else {
|
||||
panic("format day error! please 'dd'")
|
||||
}
|
||||
}
|
||||
|
||||
//hour
|
||||
if strings.ContainsAny(format, "H") {
|
||||
var hour string
|
||||
|
||||
if t.Hour() < 10 {
|
||||
hour = "0" + strconv.Itoa(t.Hour())
|
||||
} else {
|
||||
hour = strconv.Itoa(t.Hour())
|
||||
}
|
||||
|
||||
if strings.Count(format, "HH") == 1 && strings.Count(format, "H") == 2 {
|
||||
format = strings.Replace(format, "HH", hour, 1)
|
||||
} else {
|
||||
panic("format hour error! please 'HH'")
|
||||
}
|
||||
}
|
||||
|
||||
//minute
|
||||
if strings.ContainsAny(format, "m") {
|
||||
var minute string
|
||||
|
||||
if t.Minute() < 10 {
|
||||
minute = "0" + strconv.Itoa(t.Minute())
|
||||
} else {
|
||||
minute = strconv.Itoa(t.Minute())
|
||||
}
|
||||
if strings.Count(format, "mm") == 1 && strings.Count(format, "m") == 2 {
|
||||
format = strings.Replace(format, "mm", minute, 1)
|
||||
} else {
|
||||
panic("format minute error! please 'mm'")
|
||||
}
|
||||
}
|
||||
|
||||
//second
|
||||
if strings.ContainsAny(format, "s") {
|
||||
var second string
|
||||
|
||||
if t.Second() < 10 {
|
||||
second = "0" + strconv.Itoa(t.Second())
|
||||
} else {
|
||||
second = strconv.Itoa(t.Second())
|
||||
}
|
||||
|
||||
if strings.Count(format, "ss") == 1 && strings.Count(format, "s") == 2 {
|
||||
format = strings.Replace(format, "ss", second, 1)
|
||||
} else {
|
||||
panic("format second error! please 'ss'")
|
||||
}
|
||||
}
|
||||
|
||||
return format
|
||||
}
|
||||
|
||||
// 转换成日期字符串
|
||||
// timeVal:待转换的时间
|
||||
// 返回值:
|
||||
// string:格式形如:2016-10-10
|
||||
/*
|
||||
前面是含义,后面是 go 的表示值,多种表示,逗号","分割
|
||||
月份 1,01,Jan,January
|
||||
日 2,02,_2
|
||||
时 3,03,15,PM,pm,AM,am
|
||||
分 4,04
|
||||
秒 5,05
|
||||
年 06,2006
|
||||
时区 -07,-0700,Z0700,Z07:00,-07:00,MST
|
||||
周几 Mon,Monday
|
||||
*/
|
||||
func ToDateString(timeVal time.Time) string {
|
||||
return timeVal.Local().Format("2006-01-02")
|
||||
}
|
||||
|
||||
// 忽略时区,转换成日期字符串
|
||||
// timeVal:待转换的时间
|
||||
// 返回值:
|
||||
// string:格式形如:2016-10-10
|
||||
/*
|
||||
前面是含义,后面是 go 的表示值,多种表示,逗号","分割
|
||||
月份 1,01,Jan,January
|
||||
日 2,02,_2
|
||||
时 3,03,15,PM,pm,AM,am
|
||||
分 4,04
|
||||
秒 5,05
|
||||
年 06,2006
|
||||
时区 -07,-0700,Z0700,Z07:00,-07:00,MST
|
||||
周几 Mon,Monday
|
||||
*/
|
||||
func ToDateString2(timeVal time.Time) string {
|
||||
return timeVal.Format("2006-01-02")
|
||||
}
|
||||
|
||||
// 以本地时区为准,转换成时间字符串
|
||||
// timeVal:待转换的时间
|
||||
// 返回值:
|
||||
// string:格式形如:2016-10-10 10:10:10
|
||||
/*
|
||||
前面是含义,后面是 go 的表示值,多种表示,逗号","分割
|
||||
月份 1,01,Jan,January
|
||||
日 2,02,_2
|
||||
时 3,03,15,PM,pm,AM,am
|
||||
分 4,04
|
||||
秒 5,05
|
||||
年 06,2006
|
||||
时区 -07,-0700,Z0700,Z07:00,-07:00,MST
|
||||
周几 Mon,Monday
|
||||
*/
|
||||
func ToDateTimeString(timeVal time.Time) string {
|
||||
return ToDateTimeStringEx(timeVal, false)
|
||||
}
|
||||
|
||||
func ToDateTimeStringEx(timeVal time.Time, flagT bool) string {
|
||||
if flagT {
|
||||
val := timeVal.Local().Format("2006-01-02 15:04:05")
|
||||
return strings.Replace(val, " ", "T", -1)
|
||||
}
|
||||
|
||||
return timeVal.Local().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// 忽略时区,转换成时间字符串
|
||||
// timeVal:待转换的时间
|
||||
// 返回值:
|
||||
// string:格式形如:2016-10-10 10:10:10
|
||||
/*
|
||||
前面是含义,后面是 go 的表示值,多种表示,逗号","分割
|
||||
月份 1,01,Jan,January
|
||||
日 2,02,_2
|
||||
时 3,03,15,PM,pm,AM,am
|
||||
分 4,04
|
||||
秒 5,05
|
||||
年 06,2006
|
||||
时区 -07,-0700,Z0700,Z07:00,-07:00,MST
|
||||
周几 Mon,Monday
|
||||
*/
|
||||
func ToDateTimeString2(timeVal time.Time) string {
|
||||
return ToDateTimeStringEx2(timeVal, false)
|
||||
}
|
||||
|
||||
// 日期和时间中间带T方式
|
||||
func ToDateTimeStringEx2(timeVal time.Time, flagT bool) string {
|
||||
if flagT {
|
||||
val := timeVal.Format("2006-01-02 15:04:05")
|
||||
return strings.Replace(val, " ", "T", -1)
|
||||
}
|
||||
|
||||
return timeVal.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
// 转换成日期格式
|
||||
func ToDateTime(timeVal string) (time.Time, error) {
|
||||
if stringUtil.IsEmpty(timeVal) {
|
||||
return time.Time{}, fmt.Errorf("timeval is empty")
|
||||
}
|
||||
|
||||
return time.ParseInLocation("2006-01-02 15:04:05", timeVal, time.Local)
|
||||
}
|
||||
|
||||
// 以指定时区,转换成日期格式
|
||||
func ToDateTime2(timeVal string, location *time.Location) (time.Time, error) {
|
||||
if stringUtil.IsEmpty(timeVal) {
|
||||
return time.Time{}, fmt.Errorf("timeval is empty")
|
||||
}
|
||||
|
||||
return time.ParseInLocation("2006-01-02 15:04:05", timeVal, location)
|
||||
}
|
||||
|
||||
// 转换成时间格式
|
||||
func ToDate(timeVal string) (time.Time, error) {
|
||||
if stringUtil.IsEmpty(timeVal) {
|
||||
return time.Time{}, fmt.Errorf("timeval is empty")
|
||||
}
|
||||
|
||||
return time.ParseInLocation("2006-01-02", timeVal, time.Local)
|
||||
}
|
||||
|
||||
// 转换成时间格式
|
||||
func ToDate2(timeVal string, location *time.Location) (time.Time, error) {
|
||||
if stringUtil.IsEmpty(timeVal) {
|
||||
return time.Time{}, fmt.Errorf("timeval is empty")
|
||||
}
|
||||
|
||||
return time.ParseInLocation("2006-01-02", timeVal, location)
|
||||
}
|
||||
|
||||
// 转换成yyyyMMddHHmmssms的格式
|
||||
func ToInt64(timeVal time.Time) int64 {
|
||||
year := timeVal.Year()
|
||||
month := int(timeVal.Month())
|
||||
day := timeVal.Day()
|
||||
hour := timeVal.Hour()
|
||||
minute := timeVal.Minute()
|
||||
second := timeVal.Second()
|
||||
|
||||
return int64(int64(year)*1e10) + int64(month*1e8) + int64(day*1e6) + int64(hour*1e4) + int64(minute*1e2) + int64(second)
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package game
|
||||
|
||||
import "common/connection"
|
||||
|
||||
func init() {
|
||||
//注册数据库
|
||||
connection.RegisterDBModel(&Game{})
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
GameID int64 `gorm:"column:game_id;primary_key;comment:用户id;autoIncrementIncrement" json:"gameid"`
|
||||
|
||||
//账号
|
||||
Account string `gorm:"column:account;comment:账号" json:"account"`
|
||||
}
|
||||
|
||||
func (Game) TableName() string {
|
||||
return "user"
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package gameLogMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Shopify/sarama"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtilPlus"
|
||||
)
|
||||
|
||||
var (
|
||||
producer sarama.AsyncProducer
|
||||
)
|
||||
|
||||
// 启动生产者
|
||||
// 参数:
|
||||
// brokerList:Broker地址
|
||||
// userId:用户名(可默认为空字符串)
|
||||
// passWard:密码(可默认为空字符串)
|
||||
// 返回值:
|
||||
// 无
|
||||
func Start(brokerList []string, userId string, passWard string) {
|
||||
/*
|
||||
设置 acks = all。acks 是 Producer 的一个参数,代表了你对“已提交”消息的定义。如果设置成 all,则表明所有副本 Broker 都要接收到消息,该消息才算是“已提交”。这是最高等级的“已提交”定义。
|
||||
对于游戏日志,设置为WaitForLocal即可;如果是游戏数据,则应设置为WaitForAll
|
||||
设置 retries 为一个较大的值。这里的 retries 同样是 Producer 的参数,对应前面提到的 Producer 自动重试。当出现网络的瞬时抖动时,消息发送可能会失败,此时配置了 retries > 0 的 Producer 能够自动重试消息发送,避免消息丢失。
|
||||
*/
|
||||
var err error
|
||||
config := sarama.NewConfig()
|
||||
|
||||
config.Net.SASL.User = userId
|
||||
config.Net.SASL.Password = passWard
|
||||
config.Producer.Return.Successes = false
|
||||
config.Producer.Return.Errors = true
|
||||
config.Producer.Retry.Max = 10
|
||||
config.Producer.RequiredAcks = sarama.WaitForLocal
|
||||
producer, err = sarama.NewAsyncProducer(brokerList, config)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Kafka Start failed. Error: %v\n", err))
|
||||
}
|
||||
|
||||
go func() {
|
||||
for err := range producer.Errors() {
|
||||
debugUtil.Printf("Send message to kafka failed. Error: %v\n", err.Err)
|
||||
logUtilPlus.ErrorLog("Send message to kafka failed. Error: %v\n", err.Err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
if producer != nil {
|
||||
err := producer.Close()
|
||||
if err != nil {
|
||||
debugUtil.Printf("Stop kafka failed. Error: %v\n", err)
|
||||
logUtilPlus.ErrorLog("Stop kafka failed. Error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 写入游戏日志
|
||||
// 参数:
|
||||
// serverGroupId: 游戏服务器组Id
|
||||
// key: 标识
|
||||
// message: 日志
|
||||
// 返回值: 无
|
||||
func Write(topic string, serverGroupId int32, message string) {
|
||||
if producer == nil {
|
||||
debugUtil.Printf("Send message to kafka failed. producer is nil")
|
||||
logUtilPlus.ErrorLog("Send message to kafka failed. producer is nil")
|
||||
return
|
||||
}
|
||||
|
||||
msg := &sarama.ProducerMessage{}
|
||||
msg.Topic = topic
|
||||
msg.Key = sarama.StringEncoder(fmt.Sprintf("%d", serverGroupId))
|
||||
msg.Value = sarama.ByteEncoder(message)
|
||||
|
||||
// Send to kafka
|
||||
producer.Input() <- msg
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package gameLogMgr
|
||||
|
||||
// 游戏日志对象
|
||||
type GameLog struct {
|
||||
ServerGroupId int32 // 服务器组Id
|
||||
LogSql string // 日志Sql
|
||||
}
|
||||
|
||||
func newGameLog(serverGroupId int32, logSql string) *GameLog {
|
||||
return &GameLog{
|
||||
ServerGroupId: serverGroupId,
|
||||
LogSql: logSql,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package mysqlSync
|
||||
|
||||
/*
|
||||
提供数据同步到mysql的方法。基本逻辑如下:
|
||||
1、对外接收数据,以追加的方式保存到大文件中。数据的格式为:header(4bytes)+content。
|
||||
2、启动独立的goroutine来从大文件中读取数据,并保存到数据库中。
|
||||
3、使用syncInfo.txt文件保存当前已经处理的文件的路径,以及下一次将要读取的文件的Offset。为了降低向syncInfo.txt文件中写入失败,
|
||||
导致需要从头开始同步数据,所以采用了在指定数目的范围内以追加形式来写入数据的方式;只有达到了指定数量才会将整个文件清空。
|
||||
|
||||
对于错误的处理方式,分为以下两种:
|
||||
1、文件错误:由于文件系统是本系统的核心,所以如果出现文件的读写出错,则需要终止整个进程,所以需要抛出panic。
|
||||
2、数据库错误:当数据库不可访问时,为了不影响整个外部进程的运行,故而不抛出panic,而只是通过monitorNewMgr.Report的方式来报告故障。
|
||||
*/
|
||||
@ -0,0 +1,18 @@
|
||||
package mqMgr
|
||||
|
||||
// 地域
|
||||
const (
|
||||
MQ_REGION_GUANGZHOU = "gz"
|
||||
MQ_REGION_SAHNGHAI = "sh"
|
||||
MQ_REGION_BEIJING = "bj"
|
||||
MQ_REGION_SHANGHAIJINRONG = "shjr"
|
||||
MQ_REGION_SHENZHENJINRONG = "szjr"
|
||||
MQ_REGION_HONGKONG = "hk"
|
||||
MQ_REGION_CHENGDU = "cd"
|
||||
MQ_REGION_CANADA = "ca"
|
||||
MQ_REGION_UNITED_STATES_EAST = "use"
|
||||
MQ_REGION_UNITED_STATES_WEST = "usw"
|
||||
MQ_REGION_INDIA = "in"
|
||||
MQ_REGION_THILAND = "th"
|
||||
MQ_REGION_SINGAPORE = "sg"
|
||||
)
|
||||
@ -0,0 +1,265 @@
|
||||
package typeUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// KeyValue数据集合
|
||||
type MapData map[string]interface{}
|
||||
|
||||
// 创建新的MapData
|
||||
// mapData:原有的map数据
|
||||
// 返回
|
||||
// 新的Map对象
|
||||
func NewMapData(mapData map[string]interface{}) MapData {
|
||||
return MapData(mapData)
|
||||
}
|
||||
|
||||
// 类型转换为byte
|
||||
// 返回值:
|
||||
// byte:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Byte(key string) (value byte, err error) {
|
||||
return this.Uint8(key)
|
||||
}
|
||||
|
||||
// 类型转换为int
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Int(key string) (value int, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Int(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为int8
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Int8(key string) (value int8, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Int8(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为int16
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Int16(key string) (value int16, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Int16(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为int32
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Int32(key string) (value int32, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Int32(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为int64
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Int64(key string) (value int64, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Int64(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为uint
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Uint(key string) (value uint, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Uint(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为uint8
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Uint8(key string) (value uint8, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Uint8(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为uint16
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Uint16(key string) (value uint16, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Uint16(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为uint32
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Uint32(key string) (value uint32, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Uint32(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为uint64
|
||||
// 返回值:
|
||||
// int:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Uint64(key string) (value uint64, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Uint64(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为float32
|
||||
// 返回值:
|
||||
// float64:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Float32(key string) (value float32, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Float32(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为float64
|
||||
// 返回值:
|
||||
// float64:结果
|
||||
// error:错误数据
|
||||
func (this MapData) Float64(key string) (value float64, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Float64(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为bool
|
||||
// 返回值:
|
||||
// bool:结果
|
||||
// error:错误信息
|
||||
func (this MapData) Bool(key string) (value bool, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = Bool(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 类型转换为字符串
|
||||
// 返回值:
|
||||
// string:结果
|
||||
// error:错误信息
|
||||
func (this MapData) String(key string) (value string, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = String(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为时间格式,如果是字符串,则要求内容格式形如:2017-02-14 05:20:00
|
||||
// 返回值:
|
||||
// bool:结果
|
||||
// error:错误信息
|
||||
func (this MapData) DateTime(key string) (value time.Time, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value, err = DateTime(val)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取指定的值
|
||||
// 返回值:
|
||||
// interface{}:结果
|
||||
// error:错误信息
|
||||
func (this MapData) Interface(key string) (value interface{}, err error) {
|
||||
val, exist := this[key]
|
||||
if exist == false || val == nil {
|
||||
err = fmt.Errorf("Target key: [%s] doesn't exist", key)
|
||||
return
|
||||
}
|
||||
|
||||
value = val
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package shortUrlMgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/securityUtil"
|
||||
"goutil/stringUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
ShortUrl_SERVICE_URL = "http://a.app366.com/get"
|
||||
)
|
||||
|
||||
// 服务器的响应对象
|
||||
type QueryResponse struct {
|
||||
// 响应结果的状态值
|
||||
ResultStatus string
|
||||
|
||||
// 响应结果的数据
|
||||
Data string
|
||||
}
|
||||
|
||||
// 获取短链
|
||||
// appId: 为应用分配的唯一标识
|
||||
// appSecret: 为应用分配的密钥
|
||||
// fullUrl: 完整的Url
|
||||
// timeout:超时时间(单位:秒)
|
||||
// 返回值:
|
||||
// ipInfoObj: IP地址信息对象
|
||||
// err: 错误对象
|
||||
func GetShortUrl(appId, appSecret, fullUrl string, timeout int) (shortUrl string, err error) {
|
||||
timeStamp := fmt.Sprintf("%d", time.Now().Unix())
|
||||
fullUrl = stringUtil.Base64Encode(fullUrl)
|
||||
rawString := fmt.Sprintf("AppId=%s&FullUrl=%s&Timestamp=%s&AppSecret=%s", appId, fullUrl, timeStamp, appSecret)
|
||||
sign := securityUtil.Md5String(rawString, true)
|
||||
|
||||
postData := make(map[string]string, 5)
|
||||
postData["AppId"] = appId
|
||||
postData["FullUrl"] = fullUrl
|
||||
postData["Timestamp"] = timeStamp
|
||||
postData["Sign"] = sign
|
||||
|
||||
header := webUtil.GetFormHeader()
|
||||
transport := webUtil.NewTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport = webUtil.GetTimeoutTransport(transport, timeout)
|
||||
statusCode, result, err := webUtil.PostMapData(ShortUrl_SERVICE_URL, postData, header, transport)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if statusCode != 200 {
|
||||
err = fmt.Errorf("StatusCode:%d is wrong.", statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
var queryResponseObj *QueryResponse
|
||||
err = json.Unmarshal(result, &queryResponseObj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if queryResponseObj.ResultStatus != "" {
|
||||
err = fmt.Errorf("Query result:%s", queryResponseObj.ResultStatus)
|
||||
return
|
||||
}
|
||||
|
||||
shortUrl = queryResponseObj.Data
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package reloadMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
reloadFuncMap = make(map[string]func() error)
|
||||
)
|
||||
|
||||
// RegisterReloadFunc ...注册Reload方法
|
||||
// funcName:方法名称
|
||||
// reloadFunc:reload方法
|
||||
func RegisterReloadFunc(funcName string, reloadFunc func() error) {
|
||||
if _, exists := reloadFuncMap[funcName]; exists {
|
||||
panic(fmt.Sprintf("%s已经存在,请重新取名", funcName))
|
||||
}
|
||||
|
||||
reloadFuncMap[funcName] = reloadFunc
|
||||
logUtil.InfoLog(fmt.Sprintf("RegisterReloadFunc funcName:%s,当前共有%d个注册", funcName, len(reloadFuncMap)))
|
||||
}
|
||||
|
||||
// Reload ...重新加载
|
||||
// 返回值:
|
||||
// 错误列表
|
||||
func Reload() (errList []error) {
|
||||
for funcName, reloadFunc := range reloadFuncMap {
|
||||
if err := reloadFunc(); err == nil {
|
||||
logUtil.InfoLog(fmt.Sprintf("Call ReloadFunc:%s Success.", funcName))
|
||||
} else {
|
||||
logUtil.ErrorLog(fmt.Sprintf("Call ReloadFunc:%s Fail, Error:%s", funcName, err))
|
||||
errList = append(errList, err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package stringUtil
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
const (
|
||||
base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
// const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
// const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
)
|
||||
|
||||
var coder = base64.NewEncoding(base64Table)
|
||||
|
||||
// 对字符串进行Base64编码
|
||||
func Base64Encode(src string) string {
|
||||
if src == "" {
|
||||
return src
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString([]byte(src))
|
||||
}
|
||||
|
||||
// 对字符串进行Base64解码
|
||||
func Base64Encode2(src []byte) []byte {
|
||||
if len(src) == 0 {
|
||||
return src
|
||||
}
|
||||
|
||||
return []byte(base64.StdEncoding.EncodeToString(src))
|
||||
}
|
||||
|
||||
// 对字符数组进行Base64编码
|
||||
func Base64Decode(src string) (string, error) {
|
||||
if src == "" {
|
||||
return src, nil
|
||||
}
|
||||
|
||||
bytes, err := coder.DecodeString(src)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// 对字符数组进行Base64解码
|
||||
func Base64Decode2(src []byte) ([]byte, error) {
|
||||
if len(src) == 0 {
|
||||
return src, nil
|
||||
}
|
||||
|
||||
return coder.DecodeString(string(src))
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
// ************************************
|
||||
// @package: handleMgr
|
||||
// @description: 反射类-反射的方法和输入、输出参数类型组合类型
|
||||
// @author:
|
||||
// @revision history:
|
||||
// @create date: 2022-02-23 16:33:43
|
||||
// ************************************
|
||||
|
||||
package handleMgr
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ReflectMethod 反射的方法和输入、输出参数类型组合类型
|
||||
type ReflectMethod struct {
|
||||
// 反射出来的对应方法对象
|
||||
Method reflect.Value
|
||||
|
||||
// 反射出来的方法的输入参数的类型集合
|
||||
InTypes []reflect.Type
|
||||
|
||||
// 反射出来的方法的输出参数的类型集合
|
||||
OutTypes []reflect.Type
|
||||
}
|
||||
|
||||
// NewReflectMethod
|
||||
// @description:创建反射的方法和输入、输出参数类型组合类型
|
||||
// parameter:
|
||||
// @_method:反射出来的对应方法对象
|
||||
// @_inTypes:反射出来的方法的输入参数的类型集合
|
||||
// @_outTypes:反射出来的方法的输出参数的类型集合
|
||||
// return:
|
||||
// @*ReflectMethod:
|
||||
func NewReflectMethod(_method reflect.Value, _inTypes []reflect.Type, _outTypes []reflect.Type) *ReflectMethod {
|
||||
return &ReflectMethod{
|
||||
Method: _method,
|
||||
InTypes: _inTypes,
|
||||
OutTypes: _outTypes,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
/*
|
||||
提供时间相关的一些助手方法
|
||||
*/
|
||||
package timeUtil
|
||||
@ -0,0 +1,33 @@
|
||||
package exitMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
exitFuncMap = make(map[string]func())
|
||||
)
|
||||
|
||||
// RegisterExitFunc ...注册Exit方法
|
||||
// funcName:方法名称
|
||||
// exitFunc:exit方法
|
||||
func RegisterExitFunc(funcName string, exitFunc func()) {
|
||||
if _, exists := exitFuncMap[funcName]; exists {
|
||||
panic(fmt.Sprintf("%s已经存在,请重新取名", funcName))
|
||||
}
|
||||
|
||||
exitFuncMap[funcName] = exitFunc
|
||||
logUtil.InfoLog("RegisterExitFunc funcName:%s,当前共有%d个注册", funcName, len(exitFuncMap))
|
||||
}
|
||||
|
||||
// Exit ...退出程序
|
||||
// 返回值:
|
||||
// 无
|
||||
func Exit() {
|
||||
for funcName, exitFunc := range exitFuncMap {
|
||||
exitFunc()
|
||||
logUtil.InfoLog("Call ExitFunc:%s Finish.", funcName)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package mqMgr
|
||||
|
||||
// 消息队列类型:消息队列、消息主题
|
||||
const (
|
||||
MQ_TYPE_QUEUE = "queue"
|
||||
MQ_TYPE_TOPIC = "topic"
|
||||
)
|
||||
@ -0,0 +1,44 @@
|
||||
package intUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ShuffleIntDigits 打乱整数的各个数字位置并返回新的整数
|
||||
func ShuffleIntDigits(num int64) (int64, error) {
|
||||
if num < 0 {
|
||||
return 0, fmt.Errorf("number must be non-negative")
|
||||
}
|
||||
|
||||
var digits []int64
|
||||
for num > 0 {
|
||||
digits = append(digits, num%10)
|
||||
num /= 10
|
||||
}
|
||||
|
||||
// 如果原始数字是0,直接返回
|
||||
if len(digits) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// 反转切片以保持原来的数字顺序
|
||||
for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
|
||||
digits[i], digits[j] = digits[j], digits[i]
|
||||
}
|
||||
|
||||
// 打乱数字切片
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
rand.Shuffle(len(digits), func(i, j int) {
|
||||
digits[i], digits[j] = digits[j], digits[i]
|
||||
})
|
||||
|
||||
// 重新组合为新的整数
|
||||
result := int64(0)
|
||||
for _, digit := range digits {
|
||||
result = result*10 + digit
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package qcloud
|
||||
|
||||
// 发送模板短信字段
|
||||
type tmplSmsField struct {
|
||||
// 签名 (前缀)
|
||||
Sign string `json:"sign,omitempty"`
|
||||
// 模板id
|
||||
Tpl_id int `json:"tpl_id,omitempty"`
|
||||
// 模板参数
|
||||
Params []string `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
func newTmplSmsField(sign string, id int, params []string) *tmplSmsField {
|
||||
return &tmplSmsField{
|
||||
Sign: sign,
|
||||
Tpl_id: id,
|
||||
Params: params,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package monitorNewMgr
|
||||
|
||||
// 监控信息传输对象
|
||||
type MonitorModel struct {
|
||||
//状态码 0 是心跳,非零为错误信息
|
||||
Code int `json:"Code"`
|
||||
|
||||
//组Id
|
||||
GroupId string `json:"GroupId"`
|
||||
|
||||
//组密钥
|
||||
ProjectId string `json:"ProjectId"`
|
||||
|
||||
//项目Id
|
||||
ServerIp string `json:"ServerIp"`
|
||||
|
||||
// 监控使用的服务器IP
|
||||
ServerName string `json:"ServerName"`
|
||||
|
||||
// 监控使用的服务器名称
|
||||
Content string `json:"Content"`
|
||||
|
||||
// 消息产生时的时间戳
|
||||
Timestamp int64 `json:"Timestamp"`
|
||||
|
||||
// 签名
|
||||
Sign string `json:"Sign"`
|
||||
}
|
||||
|
||||
func newMonitorModel(code int, groupId, projectId, serverIp, serverName, content string, timestamp int64, sign string) *MonitorModel {
|
||||
return &MonitorModel{
|
||||
Code: code,
|
||||
GroupId: groupId,
|
||||
ProjectId: projectId,
|
||||
ServerIp: serverIp,
|
||||
ServerName: serverName,
|
||||
Content: content,
|
||||
Timestamp: timestamp,
|
||||
Sign: sign,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"common/connection"
|
||||
"sync"
|
||||
|
||||
_ "common/resultStatus"
|
||||
"common/webServer"
|
||||
_ "logincenter/internal/user"
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 设置WaitGroup需要等待的数量,只要有一个服务器出现错误都停止服务器
|
||||
wg.Add(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
//加载配置
|
||||
loadConfig()
|
||||
|
||||
// 启动webserver
|
||||
go webServer.Start(&wg)
|
||||
|
||||
// 阻塞等待,以免main线程退出
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// loadConfig 用于加载配置信息。
|
||||
// 该函数会读取配置文件或环境变量中的设置,并根据这些设置初始化程序所需的配置。
|
||||
// 目前函数的实现为空,需要根据实际的配置加载逻辑进行填充。
|
||||
func loadConfig() {
|
||||
|
||||
//设置数据类型
|
||||
connection.SetModelDB(connection.GetUserDB())
|
||||
|
||||
//构建数据库
|
||||
connection.BuildDB()
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,155 @@
|
||||
package gameServerMgr
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
. "Framework/managecenterModel"
|
||||
"goutil/webUtil"
|
||||
"goutil/zlibUtil"
|
||||
)
|
||||
|
||||
// 区服激活地址后缀
|
||||
const ActivateServer_URL_SUFFIX string = "/API/ServerActivate.ashx"
|
||||
|
||||
var (
|
||||
mManageCenterServerAPIUrl string
|
||||
mIsInit bool = true
|
||||
)
|
||||
|
||||
// 解析从ManagecenterServer中获取的服务器相关数据
|
||||
func ParseInfoFromManageCenterServer(serverGroupId int32, data string) {
|
||||
var deserializedData map[string]interface{}
|
||||
err := json.Unmarshal([]byte(data), &deserializedData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//解析服务器组
|
||||
var serverGroup *ServerGroup
|
||||
err = json.Unmarshal([]byte(deserializedData["ServerGroupInfo"].(string)), &serverGroup)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//解析合作商
|
||||
var partnerList []*Partner
|
||||
err = json.Unmarshal([]byte(deserializedData["PartnerList"].(string)), &partnerList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//解析服务器列表
|
||||
var serverList []*Server
|
||||
err = json.Unmarshal([]byte(deserializedData["ServerList"].(string)), &serverList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//解析资源包
|
||||
var resourceList []*ResourceVersion
|
||||
err = json.Unmarshal([]byte(deserializedData["ResourceVersionList"].(string)), &resourceList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//解析大区
|
||||
var areaList []*Area
|
||||
err = json.Unmarshal([]byte(deserializedData["AreaList"].(string)), &areaList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//判断是否需要更新数据(如果ServerGroupId不匹配,则不解析数据)
|
||||
if serverGroupId != serverGroup.Id {
|
||||
return
|
||||
}
|
||||
|
||||
//缓存服务器组
|
||||
ParseServerGroupInfo(serverGroup)
|
||||
|
||||
//缓存合作商
|
||||
ParsePartnerInfo(partnerList)
|
||||
|
||||
//缓存合作商对应的充值配置
|
||||
ParseChargeConfigInfo(partnerList)
|
||||
|
||||
//缓存服务器
|
||||
ParseServerInfo(serverList)
|
||||
|
||||
//缓存资源包
|
||||
ParseResourceVersionInfo(resourceList)
|
||||
|
||||
//缓存大区
|
||||
ParseAreaInfo(areaList)
|
||||
}
|
||||
|
||||
// 激活服务器
|
||||
func ActiveServer(manageCenterServerAPIUrl string, serverGroupId int32) error {
|
||||
if len(manageCenterServerAPIUrl) == 0 {
|
||||
return fmt.Errorf("ManageCenterServerAPI地址不能为空")
|
||||
}
|
||||
mManageCenterServerAPIUrl = manageCenterServerAPIUrl
|
||||
|
||||
//定义参数
|
||||
requestParamMap := make(map[string]string, 0)
|
||||
requestParamMap["ServerGroupID"] = strconv.Itoa(int(serverGroupId))
|
||||
|
||||
//构造请求url
|
||||
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, ActivateServer_URL_SUFFIX)
|
||||
|
||||
//请求url,请求头
|
||||
header := webUtil.GetFormHeader()
|
||||
transport := webUtil.NewTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true, //关闭证书校验
|
||||
}
|
||||
transport = webUtil.GetTimeoutTransport(transport, 30)
|
||||
|
||||
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
|
||||
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if statusCode != 200 {
|
||||
return fmt.Errorf("StatusCode:%d", statusCode)
|
||||
}
|
||||
|
||||
//解压缩
|
||||
retBytes, err1 := zlibUtil.Decompress(returnBytes)
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
// 解析返回值
|
||||
returnObj := new(ReturnObject)
|
||||
if err = json.Unmarshal(retBytes, &returnObj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 判断返回状态是否为成功
|
||||
if returnObj.Code != 0 {
|
||||
return fmt.Errorf("code:%d,Message:%s", returnObj.Code, returnObj.Message)
|
||||
}
|
||||
|
||||
//解析得到的数据
|
||||
ParseInfoFromManageCenterServer(serverGroupId, returnObj.Data.(string))
|
||||
|
||||
//获取白名单
|
||||
GetWhiteListFromManageCenterServer()
|
||||
|
||||
//如果是初始化,则开启白名单刷新线程。避免游戏客户端刷新数据的时候重复开启线程
|
||||
if mIsInit {
|
||||
//启动白名单数据刷新线程
|
||||
StartRefreshWhiteListTread()
|
||||
|
||||
//启动刷新MC系统配置
|
||||
StartRefreshSysConfigTread()
|
||||
//初始化修改为fasle
|
||||
mIsInit = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type player struct {
|
||||
// 玩家id
|
||||
Id string `gorm:"column:Id;primary_key"`
|
||||
|
||||
// 玩家名称
|
||||
Name string `gorm:"column:Name"`
|
||||
}
|
||||
|
||||
func (this *player) resetName(name string) {
|
||||
this.Name = name
|
||||
}
|
||||
|
||||
func (this *player) tableName() string {
|
||||
return "player"
|
||||
}
|
||||
|
||||
func newPlayer(id, name string) *player {
|
||||
return &player{
|
||||
Id: id,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
type playerMgr struct {
|
||||
playerMap map[string]*player
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (this *playerMgr) insert(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
this.playerMap[obj.Id] = obj
|
||||
}
|
||||
|
||||
func (this *playerMgr) delete(obj *player) {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
delete(this.playerMap, obj.Id)
|
||||
}
|
||||
|
||||
func (this *playerMgr) randomSelect() *player {
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
for _, obj := range this.playerMap {
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newPlayerMgr() *playerMgr {
|
||||
return &playerMgr{
|
||||
playerMap: make(map[string]*player),
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package bytesSendUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"goutil/debugUtil"
|
||||
)
|
||||
|
||||
// 保存接收的数据用于校验
|
||||
var http_recv_msg = make([]byte, 0)
|
||||
|
||||
func init() {
|
||||
debugUtil.SetDebug(true)
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
cnt int
|
||||
}
|
||||
|
||||
func (ctx *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
defer r.Body.Close()
|
||||
result, _ := ioutil.ReadAll(r.Body)
|
||||
|
||||
if string(result) == "http-msg-failed" {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
ctx.cnt++
|
||||
// 模拟一次失败
|
||||
if ctx.cnt == 2 {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
http_recv_msg = append(http_recv_msg, result...)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_http(t *testing.T) {
|
||||
http.Handle("/test", new(httpHandler))
|
||||
go http.ListenAndServe("127.0.0.1:9560", nil)
|
||||
|
||||
httpSender, err := NewHTTPSender("./test_http", "http://127.0.0.1:9560/test")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
|
||||
// 第一次应该成功
|
||||
httpSender.Write([]byte("http-msg-1"))
|
||||
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// 发送消息,此数据会多次失败,被丢弃到giveup目录
|
||||
httpSender.Write([]byte("http-msg-failed"))
|
||||
|
||||
time.Sleep(time.Second * 4)
|
||||
|
||||
// 第二次应该失败
|
||||
httpSender.Write([]byte("http-msg-2"))
|
||||
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
// 保存数据
|
||||
httpSender.Close()
|
||||
|
||||
// 重启之后应该会重发数据
|
||||
httpSender, err = NewHTTPSender("./test_http", "http://127.0.0.1:9560/test")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
httpSender.Close()
|
||||
|
||||
if string(http_recv_msg) != "http-msg-1http-msg-2" {
|
||||
t.Error("message error. got " + string(http_recv_msg))
|
||||
} else {
|
||||
fmt.Println("HTTP OK")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
v1.0版本,支持以下功能:
|
||||
1、项目中的各种基础功能;
|
||||
2、其中的managecenterMgr兼容旧版本的ManageCenter(2019-12-01之前)。
|
||||
3、新增日志记录功能(LogMgr 2020-03-09)
|
||||
4、新增监控功能(MonitorNewMgr 2020-03-09)
|
||||
5、新增短链功能(ShortUrlMgr 2020-03-09)
|
||||
|
||||
v2.0版本,支持以下功能:
|
||||
1、新的ManageCenter版本(2019-12-01之后)
|
||||
|
||||
v2.0.0.1
|
||||
LogMgr里面Log日志消息先进行base64编码之后再发送到mq,因为原消息有特殊符号,
|
||||
直接发送消息会导致消息返送之后,在腾讯mq收到消息之后数据会丢失,导致验签失败
|
||||
|
||||
v2.0.1.1
|
||||
新增屏蔽字处理forbidWordsMgr
|
||||
新增gameServerMgr
|
||||
@ -0,0 +1,114 @@
|
||||
package coroutine_timer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"goutil/mathUtil"
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
||||
func Test_Method1(t *testing.T) {
|
||||
imap := make(map[int]struct{})
|
||||
var lockObj sync.Mutex
|
||||
|
||||
cb := func(obj interface{}) {
|
||||
i := obj.(int)
|
||||
|
||||
lockObj.Lock()
|
||||
defer lockObj.Unlock()
|
||||
|
||||
if _, exist := imap[i]; exist == false {
|
||||
t.Error(i, "应该删除,不应该回调 Test_Method1")
|
||||
}
|
||||
|
||||
delete(imap, i)
|
||||
}
|
||||
|
||||
for i := 0; i < 20000; i++ {
|
||||
tick := i % 20
|
||||
isdel := false
|
||||
if tick > 1 {
|
||||
isdel = mathUtil.GetRand().GetRandInt(100) < 50
|
||||
}
|
||||
if isdel == false {
|
||||
lockObj.Lock()
|
||||
imap[i] = struct{}{}
|
||||
lockObj.Unlock()
|
||||
}
|
||||
id := AddTimer(tick, cb, i)
|
||||
if isdel {
|
||||
DeleteTimer(id)
|
||||
}
|
||||
}
|
||||
|
||||
newN := 10000000
|
||||
newId := stringUtil.GetNewUUID()
|
||||
|
||||
lockObj.Lock()
|
||||
imap[newN] = struct{}{}
|
||||
lockObj.Unlock()
|
||||
|
||||
err := AddTimer4(newId, 3, cb, newN)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
err = AddTimer4(newId, 3, cb, newN)
|
||||
if err == nil {
|
||||
t.Error("未检测到重复id")
|
||||
}
|
||||
|
||||
for {
|
||||
if len(imap) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
t.Log("剩余回调次数:", len(imap))
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Method2(t *testing.T) {
|
||||
imap := make(map[int64]struct{})
|
||||
var lockObj sync.Mutex
|
||||
|
||||
cb := func(obj interface{}) {
|
||||
i := obj.(int64)
|
||||
n := time.Now().Unix()
|
||||
x := n - i
|
||||
// 此处因为启动有暂停5s,所以启动后最近的执行偏差在5s内
|
||||
if x > 6 || x < -6 {
|
||||
t.Errorf("错误的时间执行了回调函数 tick:%v now:%v", i, n)
|
||||
}
|
||||
|
||||
lockObj.Lock()
|
||||
defer lockObj.Unlock()
|
||||
|
||||
if _, exist := imap[i]; exist == false {
|
||||
t.Error(i, "应该删除,不应该回调 Test_Method2")
|
||||
}
|
||||
|
||||
delete(imap, i)
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
tick := time.Now().Unix() + int64(i)
|
||||
imap[tick] = struct{}{}
|
||||
AddTimer3(tick, cb, tick)
|
||||
}
|
||||
|
||||
for {
|
||||
if len(imap) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
t.Log("剩余回调次数:", len(imap))
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -0,0 +1,141 @@
|
||||
package appChargeUtil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"goutil/typeUtil"
|
||||
)
|
||||
|
||||
// APP Store充值收据对象
|
||||
type Receipt struct {
|
||||
// Bvrs
|
||||
Bvrs string
|
||||
|
||||
// BundleIdentifier
|
||||
BundleIdentifier string
|
||||
|
||||
// 产品Id
|
||||
ProductId string
|
||||
|
||||
// 交易Id
|
||||
TransactionId string
|
||||
|
||||
// 数量
|
||||
Quantity int
|
||||
|
||||
// 状态
|
||||
Status int
|
||||
}
|
||||
|
||||
// BundleIdentifier是否有效
|
||||
// bundleIdentifierList:配置的BundleIdentifier列表
|
||||
// 返回值:
|
||||
// 是否有效
|
||||
func (this *Receipt) IsBundleIdentifierValid(bundleIdentifierList []string) bool {
|
||||
for _, item := range bundleIdentifierList {
|
||||
if this.BundleIdentifier == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ProductId是否有效
|
||||
// productId:输入的ProductId
|
||||
// 返回值:
|
||||
// 是否有效
|
||||
func (this *Receipt) IsProductIdValid(productId string) bool {
|
||||
return this.ProductId == productId
|
||||
}
|
||||
|
||||
// 转换为字符串
|
||||
// 返回值:
|
||||
// 字符串
|
||||
func (this *Receipt) String() string {
|
||||
return fmt.Sprintf("{Bvrs=%s,BundleIdentifier=%s,ProductId=%s,TransactionId=%s,Quantity=%d,Status=%d}", this.Bvrs, this.BundleIdentifier, this.ProductId, this.TransactionId, this.Quantity, this.Status)
|
||||
}
|
||||
|
||||
// 创建新的收据对象
|
||||
// receiptInfo:收据信息
|
||||
// 返回值:
|
||||
// 收据对象
|
||||
// 错误对象
|
||||
/*
|
||||
{
|
||||
"receipt":
|
||||
{
|
||||
"original_purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles", //购买时间,太平洋标准时间
|
||||
"purchase_date_ms":"1435031794826", //购买时间毫秒
|
||||
"unique_identifier":"5bcc5503dbcc886d10d09bef079dc9ab08ac11bb",//唯一标识符
|
||||
"original_transaction_id":"1000000160390314", //原始交易ID
|
||||
"bvrs":"1.0",//iPhone程序的版本号
|
||||
"transaction_id":"1000000160390314", //交易的标识
|
||||
"quantity":"1", //购买商品的数量
|
||||
"unique_vendor_identifier":"AEEC55C0-FA41-426A-B9FC-324128342652", //开发商交易ID
|
||||
"item_id":"1008526677",//App Store用来标识程序的字符串
|
||||
"product_id":"cosmosbox.strikehero.gems60",//商品的标识
|
||||
"purchase_date":"2015-06-23 03:56:34 Etc/GMT",//购买时间
|
||||
"original_purchase_date":"2015-06-23 03:56:34 Etc/GMT", //原始购买时间
|
||||
"purchase_date_pst":"2015-06-22 20:56:34 America/Los_Angeles",//太平洋标准时间
|
||||
"bid":"com.cosmosbox.StrikeHero",//iPhone程序的bundle标识
|
||||
"original_purchase_date_ms":"1435031794826"//毫秒
|
||||
},
|
||||
"status":0 //状态码,0为成功
|
||||
}
|
||||
*/
|
||||
func newReceipt(receiptInfo string) (receiptObj *Receipt, err error) {
|
||||
// 创建空对象
|
||||
receiptObj = &Receipt{}
|
||||
|
||||
// 将接收的数据转化为map类型的对象
|
||||
receiptDataMap := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(receiptInfo), &receiptDataMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mapData := typeUtil.NewMapData(receiptDataMap)
|
||||
|
||||
// 定义、并判断返回状态
|
||||
receiptObj.Status, err = mapData.Int("status")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if receiptObj.Status != 0 {
|
||||
err = fmt.Errorf("状态:%d不正确", receiptObj.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// Receipt is actually a child
|
||||
receiptDataMap, ok := mapData["receipt"].(map[string]interface{})
|
||||
if !ok {
|
||||
err = fmt.Errorf("receipt错误")
|
||||
return
|
||||
}
|
||||
mapData = typeUtil.NewMapData(receiptDataMap)
|
||||
|
||||
// 用返回值对本对象的属性进行赋值
|
||||
receiptObj.Bvrs, err = mapData.String("bvrs")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
receiptObj.BundleIdentifier, err = mapData.String("bid")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
receiptObj.ProductId, err = mapData.String("product_id")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
receiptObj.TransactionId, err = mapData.String("transaction_id")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
receiptObj.Quantity, err = mapData.Int("quantity")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,330 @@
|
||||
package stringUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"goutil/mathUtil"
|
||||
)
|
||||
|
||||
// 使用多分隔符来进行分割(默认分隔符为:",", ";", ":", "|", "||")
|
||||
// eg:1,2;3|4||5,6;7|8||9
|
||||
// 返回值:
|
||||
// []string
|
||||
func Split(s string, seps []string) []string {
|
||||
retList := make([]string, 0, 32)
|
||||
|
||||
// 如果seps为nil,则使用默认值
|
||||
if seps == nil {
|
||||
seps = []string{",", ";", ":", "|", "||"}
|
||||
}
|
||||
|
||||
// 根据所有的分隔符来一点一点地切割字符串,直到不可切割为止
|
||||
for {
|
||||
startIndex := len(s) - 1
|
||||
endIndex := 0
|
||||
exists := false
|
||||
|
||||
// 遍历,找到第一个分割的位置
|
||||
for _, sep := range seps {
|
||||
index := strings.Index(s, sep)
|
||||
|
||||
// 如果找到有匹配项,则寻找最小的pos,如果有多个相同的pos,则使用长度最长的分隔符
|
||||
if index > -1 {
|
||||
exists = true
|
||||
|
||||
// 说明有多个有效的分隔符,如|和||
|
||||
if index < startIndex {
|
||||
startIndex = index
|
||||
endIndex = startIndex + len(sep) - 1
|
||||
} else if index == startIndex {
|
||||
if startIndex+len(sep)-1 > endIndex {
|
||||
endIndex = startIndex + len(sep) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到匹配的pos,则分割过程结束
|
||||
if !exists {
|
||||
retList = append(retList, s)
|
||||
break
|
||||
}
|
||||
|
||||
// 切割字符串
|
||||
sub := s[:startIndex]
|
||||
if sub != "" {
|
||||
retList = append(retList, sub)
|
||||
}
|
||||
s = s[endIndex+1:]
|
||||
}
|
||||
|
||||
return retList
|
||||
}
|
||||
|
||||
// 将字符串切割为[]int
|
||||
// str:输入字符串
|
||||
// 返回值:
|
||||
// []int
|
||||
// error
|
||||
func SplitToIntSlice(s, sep string) ([]int, error) {
|
||||
// 先按照分隔符进行切割
|
||||
strSlice := strings.Split(s, sep)
|
||||
|
||||
// 定义int slice
|
||||
intSlice := make([]int, 0, len(strSlice))
|
||||
for _, value := range strSlice {
|
||||
// 去除空格
|
||||
if value = strings.TrimSpace(value); value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if value_int, err := strconv.Atoi(value); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
intSlice = append(intSlice, value_int)
|
||||
}
|
||||
}
|
||||
|
||||
return intSlice, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为[]int32
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// []int
|
||||
// error
|
||||
func SplitToInt32Slice(s, sep string) ([]int32, error) {
|
||||
// 先获得int slice
|
||||
count := 0
|
||||
intSlice, err := SplitToIntSlice(s, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
count = len(intSlice)
|
||||
}
|
||||
|
||||
// 定义int32 slice
|
||||
int32Slice := make([]int32, 0, count)
|
||||
for _, item := range intSlice {
|
||||
int32Slice = append(int32Slice, int32(item))
|
||||
}
|
||||
|
||||
return int32Slice, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为[]int64
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// []int64
|
||||
// error
|
||||
func SplitToInt64Slice(s, sep string) ([]int64, error) {
|
||||
// 先获得int slice
|
||||
count := 0
|
||||
intSlice, err := SplitToIntSlice(s, sep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
count = len(intSlice)
|
||||
}
|
||||
|
||||
// 定义int32 slice
|
||||
int64Slice := make([]int64, 0, count)
|
||||
for _, item := range intSlice {
|
||||
int64Slice = append(int64Slice, int64(item))
|
||||
}
|
||||
|
||||
return int64Slice, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为[]float64
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// []float64
|
||||
// error
|
||||
func SplitToFloat64Slice(s, sep string) ([]float64, error) {
|
||||
// 先按照分隔符进行切割
|
||||
strSlice := strings.Split(s, sep)
|
||||
|
||||
// 定义float64 slice
|
||||
floatSlice := make([]float64, 0, len(strSlice))
|
||||
for _, value := range strSlice {
|
||||
// 去除空格
|
||||
if value = strings.TrimSpace(value); value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if value_float, err := strconv.ParseFloat(value, 64); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
floatSlice = append(floatSlice, value_float)
|
||||
}
|
||||
}
|
||||
|
||||
return floatSlice, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为map[int32]int32
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// map[int32]float32
|
||||
// error
|
||||
func SplitToDict_KintVint(s, outerSep, innerSep string) (map[int32]int32, error) {
|
||||
// 先按照分隔符进行切割
|
||||
outerSlice := strings.Split(s, outerSep)
|
||||
|
||||
// 定义map[int32]float32
|
||||
floatMap := make(map[int32]int32, len(outerSlice))
|
||||
for _, itemStr := range outerSlice {
|
||||
innerSlice := strings.Split(strings.TrimSpace(itemStr), innerSep)
|
||||
key := strings.TrimSpace(innerSlice[0])
|
||||
value := strings.TrimSpace(innerSlice[1])
|
||||
|
||||
key_int, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_int, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
floatMap[int32(key_int)] = int32(value_int)
|
||||
}
|
||||
return floatMap, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为map[int32]string
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// map[int32]string
|
||||
// error
|
||||
func SplitToDict_KintVstring(s, outerSep, innerSep string) (map[int32]string, error) {
|
||||
// 先按照分隔符进行切割
|
||||
outerSlice := strings.Split(s, outerSep)
|
||||
|
||||
// 定义map[int32]string
|
||||
resultMap := make(map[int32]string, len(outerSlice))
|
||||
for _, itemStr := range outerSlice {
|
||||
innerSlice := strings.Split(strings.TrimSpace(itemStr), innerSep)
|
||||
key := strings.TrimSpace(innerSlice[0])
|
||||
value := strings.TrimSpace(innerSlice[1])
|
||||
|
||||
key_int, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultMap[int32(key_int)] = value
|
||||
}
|
||||
return resultMap, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为map[int32]float32
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// map[int32]float32
|
||||
// error
|
||||
func SplitToDict_KintVfloat(s, outerSep, innerSep string) (map[int32]float32, error) {
|
||||
// 先按照分隔符进行切割
|
||||
outerSlice := strings.Split(s, outerSep)
|
||||
|
||||
// 定义map[int32]float32
|
||||
floatMap := make(map[int32]float32, len(outerSlice))
|
||||
for _, itemStr := range outerSlice {
|
||||
innerSlice := strings.Split(strings.TrimSpace(itemStr), innerSep)
|
||||
key := strings.TrimSpace(innerSlice[0])
|
||||
value := strings.TrimSpace(innerSlice[1])
|
||||
|
||||
key_int, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_float, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
floatMap[int32(key_int)] = float32(value_float)
|
||||
}
|
||||
return floatMap, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为map[int32]float32
|
||||
// s:输入字符串
|
||||
// 返回值:
|
||||
// map[int32]float32
|
||||
// error
|
||||
func SplitToDict_KintVfloat64(s, outerSep, innerSep string) (map[int32]float64, error) {
|
||||
// 先按照分隔符进行切割
|
||||
outerSlice := strings.Split(s, outerSep)
|
||||
|
||||
// 定义map[int32]float32
|
||||
floatMap := make(map[int32]float64, len(outerSlice))
|
||||
for _, itemStr := range outerSlice {
|
||||
innerSlice := strings.Split(strings.TrimSpace(itemStr), innerSep)
|
||||
key := strings.TrimSpace(innerSlice[0])
|
||||
value := strings.TrimSpace(innerSlice[1])
|
||||
|
||||
key_int, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value_float, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
floatMap[int32(key_int)] = float64(value_float)
|
||||
}
|
||||
return floatMap, nil
|
||||
}
|
||||
|
||||
// 将字符串切割为IntRegion列表
|
||||
// s:输入字符串,形如:1-200,201-400,401-1000
|
||||
// outerSep:外部分隔符
|
||||
// innerSep:内部分隔符
|
||||
// 返回值:
|
||||
// IntRegion列表
|
||||
// 错误对象
|
||||
func SplitToIntRegion(s, outerSep, innerSep string) (intRegionList []*mathUtil.IntRegion, err error) {
|
||||
if s == "" {
|
||||
err = fmt.Errorf("Input is empty")
|
||||
return
|
||||
}
|
||||
|
||||
outerRegionList := make([]string, 0, 4)
|
||||
outerRegionList = strings.Split(s, outerSep)
|
||||
if len(outerRegionList) == 0 {
|
||||
err = fmt.Errorf("%s:Format invalid. Such as:1-100,101-200", s)
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range outerRegionList {
|
||||
innerRegionList := make([]string, 0, 2)
|
||||
innerRegionList = strings.Split(item, innerSep)
|
||||
if len(innerRegionList) != 2 {
|
||||
err = fmt.Errorf("%s:Format invalid. Such as:1-100", item)
|
||||
return
|
||||
}
|
||||
|
||||
var lower, upper int
|
||||
lower, err = strconv.Atoi(innerRegionList[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
upper, err = strconv.Atoi(innerRegionList[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if lower > upper {
|
||||
err = fmt.Errorf("lower:%d should less than upper:%d", lower, upper)
|
||||
return
|
||||
}
|
||||
|
||||
intRegionList = append(intRegionList, mathUtil.NewIntRegion(lower, upper))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
// ************************************
|
||||
// @package: websocketServer
|
||||
// @description:
|
||||
// @author:
|
||||
// @revision history:
|
||||
// @create date: 2022-02-16 18:13:45
|
||||
// ************************************
|
||||
package websocketServer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
webServer "Framework/webServer"
|
||||
logUtil "goutil/logUtil"
|
||||
)
|
||||
|
||||
// 事件回调函数
|
||||
type EventCallbackFuncs struct {
|
||||
// websocket连接事件
|
||||
OnConnFunc func(ctx *Context)
|
||||
|
||||
// websocket关闭事件
|
||||
OnCloseFunc func(ctx *Context)
|
||||
|
||||
// websocket接收事件
|
||||
OnMsgFunc func(ctx *Context, msgType int, msgData []byte)
|
||||
}
|
||||
|
||||
// 用户自定义数据结构
|
||||
type userDatas struct {
|
||||
// WsServer 或 WssServer 指针
|
||||
server interface{}
|
||||
|
||||
// 事件回调函数
|
||||
eventCallback *EventCallbackFuncs
|
||||
}
|
||||
|
||||
// iServerMgr
|
||||
// @description: WsServer/WssServer内部管理接口,以便hookHandler中统一访问
|
||||
type iServerMgr interface {
|
||||
// 升级为websocket
|
||||
upgrade(ctx *Context) (conn *websocket.Conn, err error)
|
||||
|
||||
// 将连接从连接池删除
|
||||
delConn(conn *websocket.Conn)
|
||||
|
||||
// 获取接收到Ping消息时,是否自动回复Pong信息
|
||||
GetAutoPong() bool
|
||||
}
|
||||
|
||||
// 回调勾子,将http/https升级为websocket
|
||||
func hookHandler(webServerCtx *webServer.Context) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logUtil.LogUnknownError(r)
|
||||
}
|
||||
}()
|
||||
|
||||
userDataI := webServerCtx.GetUserData()
|
||||
if userDataI == nil {
|
||||
return
|
||||
}
|
||||
|
||||
userData, ok := userDataI.(*userDatas)
|
||||
if !ok {
|
||||
logUtil.ErrorLog("userData type error")
|
||||
return
|
||||
}
|
||||
|
||||
// 通信结束信号
|
||||
cls := make(chan struct{})
|
||||
ctx := &Context{
|
||||
webServerCtx: webServerCtx, // web_server环境
|
||||
cls: cls, // 关闭连接信号
|
||||
}
|
||||
|
||||
var serverMgr iServerMgr
|
||||
var conn *websocket.Conn
|
||||
var err error
|
||||
|
||||
// 转为iServerMgr
|
||||
switch userData.server.(type) {
|
||||
case *WsServer:
|
||||
if svr, ok := userData.server.(*WsServer); ok {
|
||||
serverMgr = svr
|
||||
} else {
|
||||
logUtil.ErrorLog("server type not WsServer")
|
||||
return
|
||||
}
|
||||
case *WssServer:
|
||||
if svr, ok := userData.server.(*WssServer); ok {
|
||||
serverMgr = svr
|
||||
} else {
|
||||
logUtil.ErrorLog("server type not WssServer")
|
||||
return
|
||||
}
|
||||
default:
|
||||
logUtil.ErrorLog("server type not WsServer or WssServer")
|
||||
return
|
||||
}
|
||||
|
||||
// 升级为websocket
|
||||
conn, err = serverMgr.upgrade(ctx)
|
||||
if err != nil {
|
||||
if err.Error() == "connManager(disableNewConn)" {
|
||||
// 禁用新连接。正常功能,直接返回
|
||||
return
|
||||
}
|
||||
|
||||
logUtil.ErrorLog("websocket Upgrade failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 将连接从连接池删除
|
||||
defer serverMgr.delConn(conn)
|
||||
|
||||
// 关闭连接
|
||||
defer conn.Close()
|
||||
|
||||
// 默认情况下,ReadMessage不会读取到ping/pong/close消息(内部有专门的处理函数)
|
||||
// 设置心跳包处理函数
|
||||
conn.SetPingHandler(func(msg string) error {
|
||||
// 只要收到消息,都需要更新最近一次收到心跳包的时间
|
||||
ctx.heartbeat = time.Now()
|
||||
|
||||
if serverMgr.GetAutoPong() {
|
||||
// 自动回应一个Pong心跳
|
||||
go ctx.SendMessage(websocket.PongMessage, []byte(msg))
|
||||
}
|
||||
|
||||
// 接收消息回调
|
||||
if userData.eventCallback.OnMsgFunc != nil {
|
||||
userData.eventCallback.OnMsgFunc(ctx, websocket.PingMessage, []byte(msg))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
// 设置关闭包处理函数
|
||||
conn.SetCloseHandler(func(code int, text string) error {
|
||||
// 所有向cls写入,都使用select超时结构,以保证这儿不会一直阻塞,确保此协程能退出
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
case cls <- struct{}{}:
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// 设置最近一次收到心跳包的时间
|
||||
ctx.heartbeat = time.Now()
|
||||
|
||||
// 新连接回调
|
||||
if userData.eventCallback.OnConnFunc != nil {
|
||||
userData.eventCallback.OnConnFunc(ctx)
|
||||
}
|
||||
|
||||
// 开启读协程
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logUtil.LogUnknownError(r)
|
||||
|
||||
// 有异常出现(可能是用户回调中出现异常);执行到这儿,需要关闭连接
|
||||
// 所有向cls写入,都使用select超时结构,以保证这儿不会一直阻塞,确保此协程能退出
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
case cls <- struct{}{}:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
// 注意:ReadMessage不会读取到心跳包和关闭包数据;心跳包/关闭包需要设置专门的处理函数
|
||||
// 但内部对心跳包/关闭包的处理,也是由ReadMessage函数触发的(也就是不调用ReadMessage函数,可能也不会触发对心跳包/关闭包的处理);
|
||||
// 经测试和内部代码确认:调用心跳包/关闭包处理函数时,ReadMessage不会返回;心跳包/关闭包处理函数调用完毕后ReadMessage才可能返回
|
||||
mt, msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
// 所有向cls写入,都使用select超时结构,以保证这儿不会一直阻塞,确保此协程能退出
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
case cls <- struct{}{}:
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// 只要收到消息,都需要更新最近一次收到心跳包的时间
|
||||
ctx.heartbeat = time.Now()
|
||||
|
||||
// 接收消息回调
|
||||
if userData.eventCallback.OnMsgFunc != nil {
|
||||
userData.eventCallback.OnMsgFunc(ctx, mt, msg)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待退出
|
||||
<-cls
|
||||
|
||||
// 设置已关闭标志
|
||||
ctx.isClosed = true
|
||||
|
||||
// 关闭回调
|
||||
if userData.eventCallback.OnCloseFunc != nil {
|
||||
userData.eventCallback.OnCloseFunc(ctx)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
/*
|
||||
请求结构简介
|
||||
最近更新时间:2019-08-01 19:14:44
|
||||
|
||||
编辑 查看pdf
|
||||
在这篇文章中:
|
||||
服务地址
|
||||
通信协议
|
||||
请求方法
|
||||
请求参数
|
||||
字符编码
|
||||
对腾讯云的 API 接口的调用是通过向腾讯云 API 的服务端地址发送请求,并按照接口说明在请求中加入相应的请求参数来完成的。腾讯云 API 的请求结构由:服务地址、通信协议、请求方法、请求参数和字符编码组成。具体描述如下:
|
||||
|
||||
服务地址
|
||||
腾讯云 API 的服务接入地址与具体模块相关,详细请参见各接口相关描述。
|
||||
|
||||
通信协议
|
||||
腾讯云 API 的大部分接口都通过 HTTPS 进行通信,为您提供高安全性的通信通道。
|
||||
|
||||
请求方法
|
||||
腾讯云 API 同时支持 POST 和 GET 请求。
|
||||
|
||||
注意:
|
||||
|
||||
POST 和 GET 请求不能混合使用,若使用 GET 方式,则参数均从 Querystring 取得;
|
||||
若使用 POST 方式,则参数均从 Request Body 中取得,而 Querystring 中的参数将忽略。
|
||||
两种请求方式的参数格式规则相同,一般情况下使用 GET 请求,当参数字符串过长时推荐使用 POST。
|
||||
如果用户的请求方法是 GET,则对所有请求参数值均需要做 URL 编码,若为 POST,则无需对参数编码。
|
||||
GET 请求的最大长度根据不同的浏览器和服务器设置有所不同,例如,传统 IE 浏览器限制为 2K,Firefox 限制为 8K;对于一些参数较多、长度较长的 API 请求,建议您使用 POST 方法以免在请求过程中会由于字符串超过最大长度而导致请求失败。
|
||||
对于 POST 请求,您需要使用 x-www-form-urlencoded 的形式传参,因为云 API 侧是从 $_POST 中取出请求参数的。
|
||||
请求参数
|
||||
腾讯云 API 的每个请求都需要指定两类参数:公共请求参数以及接口请求参数。其中公共请求参数是每个接口都要用到的请求参数,具体可参见 公共请求参数,而接口请求参数是各个接口所特有的,具体见各个接口的“请求参数”描述。
|
||||
|
||||
字符编码
|
||||
腾讯云 API 的请求及返回结果均使用 UTF-8 字符集进行编码。
|
||||
*/
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/mathUtil"
|
||||
)
|
||||
|
||||
// CommonRequest 公共请求参数对象
|
||||
type CommonRequest struct {
|
||||
// Action 指令接口名称(必须)
|
||||
Action string
|
||||
|
||||
// 地域参数(必须)
|
||||
Region string
|
||||
|
||||
// Timestamp 当前UNIX时间戳(必须)
|
||||
Timestamp uint64
|
||||
|
||||
// Nonce 随机正整数(必须)
|
||||
Nonce uint32
|
||||
|
||||
// SecretId 在云API密钥上申请的标识身份的SecretId(必须)
|
||||
SecretId string
|
||||
|
||||
// 请求签名,用来验证此次请求的合法性,需要用户根据实际的输入参数计算得出。
|
||||
Signature string
|
||||
|
||||
// 签名方式,目前支持 HmacSHA256 和 HmacSHA1。只有指定此参数为 HmacSHA256 时,才使用 HmacSHA256 算法验证签名,其他情况均使用 HmacSHA1 验证签名。
|
||||
SignatureMethod string
|
||||
|
||||
// 队列名称(此属性虽然不是API文档中的公共属性,但是在队列模型中确实事实上的公共属性,所以将其转移到此处)
|
||||
queueName string
|
||||
}
|
||||
|
||||
// AssembleParamMap 组装请求参数字典
|
||||
// 返回值
|
||||
// map[string]interface{}:请求参数字
|
||||
func (this *CommonRequest) AssembleParamMap() map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
// 组装参数
|
||||
result["Action"] = this.Action
|
||||
result["Region"] = this.Region
|
||||
result["Timestamp"] = fmt.Sprintf("%d", this.Timestamp)
|
||||
result["Nonce"] = fmt.Sprintf("%d", this.Nonce)
|
||||
result["SecretId"] = this.SecretId
|
||||
result["SignatureMethod"] = this.SignatureMethod
|
||||
result["queueName"] = this.queueName
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// NewCommonRequest 新建公共请求参数对象
|
||||
// 参数
|
||||
// action:指令接口名称
|
||||
// region:地域
|
||||
// secretId:在云API密钥上申请的标识身份的SecretId
|
||||
// queueName:队列名称
|
||||
// 返回值
|
||||
// *CommonRequest:公共请求参数对象
|
||||
func NewCommonRequest(action, region, secretId, queueName string) *CommonRequest {
|
||||
return &CommonRequest{
|
||||
Action: action,
|
||||
Region: region,
|
||||
Timestamp: uint64(time.Now().Unix()),
|
||||
Nonce: mathUtil.GetRand().Uint32(),
|
||||
SecretId: secretId,
|
||||
SignatureMethod: "HmacSHA256",
|
||||
queueName: queueName,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package configYaml
|
||||
|
||||
import (
|
||||
"framework/configMgr"
|
||||
"gopkg.in/yaml.v3"
|
||||
"goutil/logUtil"
|
||||
"goutil/yamlUtil"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
// 配置对象
|
||||
|
||||
configManager = configMgr.NewConfigManager()
|
||||
)
|
||||
|
||||
// init
|
||||
//
|
||||
// @description: init
|
||||
//
|
||||
// parameter:
|
||||
// return:
|
||||
func init() {
|
||||
// 设置日志文件的存储目录
|
||||
logUtil.SetLogPath("LOG")
|
||||
|
||||
if err := reloadConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//加载配置
|
||||
initBaseConfig()
|
||||
initDbConfig()
|
||||
initFunctionConfig()
|
||||
initLogMgrConfig()
|
||||
}
|
||||
|
||||
// reloadConfig
|
||||
//
|
||||
// @description: reloadConfig
|
||||
//
|
||||
// parameter:
|
||||
// return:
|
||||
//
|
||||
// @error: 错误信息
|
||||
func reloadConfig() error {
|
||||
|
||||
yamlFile, err := yamlUtil.LoadFromFile("config.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析 YAML 文件
|
||||
err = yaml.Unmarshal(yamlFile, &ConfigYaml)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshalling config file: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package webUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
client := NewClient(nil)
|
||||
|
||||
result, err := client.Get("https://www.baidu.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("测试错误,返回的结果为:%s", err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
t.Errorf("返回的数据为空,期望不为空")
|
||||
}
|
||||
|
||||
//t.Log(string(result))
|
||||
}
|
||||
|
||||
func TestGetTimeout(t *testing.T) {
|
||||
transportOPT := &TransportOPT{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
opt := make(map[string]interface{})
|
||||
opt["Timeout"] = 3 * time.Second
|
||||
|
||||
client := NewClient(transportOPT)
|
||||
_, err := client.Get("https://www.google.com", nil)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Errorf("测试异常")
|
||||
}
|
||||
|
||||
func TestPostWithMap(t *testing.T) {
|
||||
client := NewClient(nil)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["test1"] = "value1"
|
||||
data["test2"] = "value2"
|
||||
result, err := client.PostWithMap("http://www.baidu.com", data, nil)
|
||||
if err != nil {
|
||||
t.Errorf("测试错误,返回的结果为:%s", err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
t.Errorf("返回的数据为空,期望不为空")
|
||||
}
|
||||
|
||||
//t.Log(string(result))
|
||||
}
|
||||
|
||||
func TestPostWithByte(t *testing.T) {
|
||||
client := NewClient(nil)
|
||||
|
||||
result, err := client.PostWithByte("http://www.baidu.com", []byte("test=abc"), nil)
|
||||
if err != nil {
|
||||
t.Errorf("测试错误,返回的结果为:%s", err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
t.Errorf("返回的数据为空,期望不为空")
|
||||
}
|
||||
|
||||
//t.Log(string(result))
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package qcloud
|
||||
|
||||
type telField struct {
|
||||
Nationcode string `json:"nationcode"`
|
||||
Mobile string `json:"mobile"`
|
||||
}
|
||||
|
||||
func newTelField(nation, mobile string) *telField {
|
||||
return &telField{
|
||||
Nationcode: nation,
|
||||
Mobile: mobile,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package gameServerMgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"Framework/goroutineMgr"
|
||||
. "Framework/managecenterModel"
|
||||
"goutil/logUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
const SYSCONF_URL_SUFFIX string = "/API/SysConfig.ashx"
|
||||
|
||||
var (
|
||||
mSysConfig *SysConfig
|
||||
)
|
||||
|
||||
// 获取MC系统配置
|
||||
func GetSysConfigFromManageCenterServer() error {
|
||||
//定义参数
|
||||
requestParamMap := make(map[string]string, 0)
|
||||
requestParamMap["IsResultCompressed"] = "false"
|
||||
|
||||
//构造url
|
||||
url := fmt.Sprintf("%s/%s", mManageCenterServerAPIUrl, SYSCONF_URL_SUFFIX)
|
||||
|
||||
//请求url,请求头
|
||||
header := webUtil.GetFormHeader()
|
||||
transport := webUtil.NewTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport = webUtil.GetTimeoutTransport(transport, 30)
|
||||
|
||||
statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, transport)
|
||||
//statusCode, returnBytes, err := webUtil.PostMapData(url, requestParamMap, header, nil)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错,url:%s,错误信息为:%s", url, err))
|
||||
return err
|
||||
}
|
||||
if statusCode != 200 {
|
||||
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错,url:%s,错误码为:%d", url, statusCode))
|
||||
return err
|
||||
}
|
||||
// 解析返回值
|
||||
returnObj := new(ReturnObject)
|
||||
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错,反序列化返回值出错,错误信息为:%s, str:%s", err, string(returnBytes)))
|
||||
return err
|
||||
}
|
||||
|
||||
// 判断返回状态是否为成功
|
||||
if returnObj.Code != 0 {
|
||||
// 数据没有变化,所以没有获取到新的数据,不能算错误。
|
||||
if returnObj.Code == 47 || returnObj.Message == "DataNotChanged" {
|
||||
return nil
|
||||
} else {
|
||||
msg := fmt.Sprintf("获取MC系统配置出错,返回状态:%d,信息为:%s", returnObj.Code, returnObj.Message)
|
||||
logUtil.ErrorLog(msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析Data
|
||||
var tmpSysConfig *SysConfig
|
||||
if data, ok := returnObj.Data.(string); !ok {
|
||||
msg := "获取MC系统配置出错,返回的数据不是string类型"
|
||||
logUtil.ErrorLog(msg)
|
||||
return errors.New(msg)
|
||||
} else {
|
||||
if err = json.Unmarshal([]byte(data), &tmpSysConfig); err != nil {
|
||||
logUtil.ErrorLog(fmt.Sprintf("获取MC系统配置出错出错,反序列化数据出错,错误信息为:%s", err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 赋值给最终的sysconfig
|
||||
mSysConfig = tmpSysConfig
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 定时刷新MC系统配置
|
||||
func StartRefreshSysConfigTread() {
|
||||
// 定时刷新数据
|
||||
go func() {
|
||||
goroutineName := "gameServerMgr.StartRefreshSysConfigTread"
|
||||
goroutineMgr.Monitor(goroutineName)
|
||||
defer goroutineMgr.ReleaseMonitor(goroutineName)
|
||||
|
||||
for {
|
||||
// 每30秒刷新一次
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
// MC系统配置
|
||||
GetSysConfigFromManageCenterServer()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
func GetSysConfig() *SysConfig {
|
||||
return mSysConfig
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package initMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
Register("first", 1, first)
|
||||
Register("second", 2, second)
|
||||
Register("third", 3, third)
|
||||
Register("fourth", 4, fourth)
|
||||
}
|
||||
|
||||
func TestCallOne(t *testing.T) {
|
||||
name := "first"
|
||||
if err := CallOne(name); err != nil {
|
||||
t.Errorf("there should be no error, but now it has:%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallAny(t *testing.T) {
|
||||
errList := CallAny("second", "third")
|
||||
if len(errList) != 1 {
|
||||
t.Errorf("there should be 1 error, but now:%d", len(errList))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallAll(t *testing.T) {
|
||||
errList := CallAll()
|
||||
if len(errList) != 2 {
|
||||
t.Errorf("there should be 1 error, but now:%d", len(errList))
|
||||
}
|
||||
}
|
||||
|
||||
func first() error {
|
||||
fmt.Println("first")
|
||||
return nil
|
||||
}
|
||||
|
||||
func second() error {
|
||||
fmt.Println("second")
|
||||
return fmt.Errorf("the second error")
|
||||
}
|
||||
|
||||
func third() error {
|
||||
fmt.Println("third")
|
||||
return nil
|
||||
}
|
||||
|
||||
func fourth() error {
|
||||
fmt.Println("fourth")
|
||||
return fmt.Errorf("the fourth error")
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"common/cache"
|
||||
"common/connection"
|
||||
"goutil/logUtilPlus"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 用户缓存对象
|
||||
var userMap = make(map[int64]*User)
|
||||
var rwmu sync.RWMutex
|
||||
|
||||
// GetUserByID 根据用户id获取用户信息
|
||||
func GetUserByID(UserID int64) (*User, error) {
|
||||
|
||||
//判断缓存是否存在
|
||||
var user *User
|
||||
|
||||
func() *User {
|
||||
rwmu.RLock()
|
||||
defer rwmu.RUnlock()
|
||||
ok := true
|
||||
if user, ok = userMap[UserID]; ok {
|
||||
return user
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if user != nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
result := connection.GetUserDB().First(&user, UserID)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
//添加缓存
|
||||
func() {
|
||||
rwmu.Lock()
|
||||
defer rwmu.Unlock()
|
||||
user.Cache = cache.NewCache()
|
||||
userMap[user.ID] = user
|
||||
}()
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// AddUserCache 添加用户缓存
|
||||
func AddUserCache(user *User) {
|
||||
rwmu.Lock()
|
||||
defer rwmu.Unlock()
|
||||
user.Cache = cache.NewCache()
|
||||
userMap[user.ID] = user
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
// AddUser 添加新的用户到数据库中。
|
||||
// 参数 User: 包含用户信息的对象。
|
||||
// 返回值: 插入操作影响的行数和可能发生的错误。
|
||||
func AddUser(User *User) (int64, error) {
|
||||
|
||||
//处理一些验证
|
||||
|
||||
//写入缓存
|
||||
AddUserCache(User)
|
||||
|
||||
// 写入到数据库
|
||||
result := connection.GetUserDB().Create(&User) // 通过数据的指针来创建
|
||||
|
||||
if result.Error != nil {
|
||||
logUtilPlus.ErrorLog("添加用户失败 错误信息:", result.Error.Error())
|
||||
}
|
||||
return User.ID, nil
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func Login(account string, password string) (*User, error) {
|
||||
|
||||
//这里优先验证缓存数据
|
||||
|
||||
var User User
|
||||
result := connection.GetUserDB().Where("account = ? AND password = ?", account, password).First(&User)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
return &User, nil
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title> <!-- 阿斯大三大四的 -->
|
||||
这里有中文哦</title>
|
||||
<meta name="language" content="en"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1> This is a H1 </h1>
|
||||
<ul>
|
||||
<li><a id="1" href="/">阿萨德</a></li>
|
||||
<li><a id="2" href="/about">哈哈哈</a></li>
|
||||
<li><a id="3" href="/account">他写的</a></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<p>
|
||||
Hello,This is an example for gxpath.
|
||||
</p>
|
||||
<footer>footer script</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,76 @@
|
||||
package monitorNewMgr
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//服务器节点信息
|
||||
type ServerNodeMessage struct {
|
||||
//通用信息
|
||||
CommInfo CommInfoModel
|
||||
|
||||
// indexMap 指标对象
|
||||
IndexSlice []Index
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
// IndexName 指标名字(eg:可以为cpu,mem,numGoroutine,proxy)
|
||||
IndexName string
|
||||
|
||||
// moduleName 模块名字
|
||||
ModuleName string
|
||||
|
||||
// methodName 方法名字
|
||||
MethodName string
|
||||
|
||||
// value 指标值(eg:indexName为cpu时,value 为cpu使用率,indexName为内存时,value为内存使用率)
|
||||
Value float64
|
||||
}
|
||||
|
||||
//服务监控中心信息
|
||||
type MonitorCenterMessage struct {
|
||||
//通用信息
|
||||
CommInfo CommInfoModel
|
||||
|
||||
// Cpu 核数
|
||||
Cpu int32
|
||||
|
||||
// Mem 内存大小
|
||||
Mem int32
|
||||
|
||||
// Status
|
||||
Status int32
|
||||
}
|
||||
|
||||
// 通用信息
|
||||
type CommInfoModel struct {
|
||||
// 项目组ID
|
||||
GroupId string
|
||||
|
||||
// projectId 项目Id (eg:迪士尼)
|
||||
ProjectId string
|
||||
|
||||
// clusterId 集群Id(一个集群相当于一个大区)
|
||||
ClusterId string
|
||||
|
||||
// 组密钥
|
||||
ProjectSecret string
|
||||
|
||||
// 服务器IP
|
||||
IP string
|
||||
|
||||
// Port 服务端口
|
||||
Port int32
|
||||
|
||||
// 服务名(eg:玩家服务player,城市服务:city,代理服务:proxy)
|
||||
ServiceName string
|
||||
|
||||
// instanceId 服务实例Id
|
||||
InstanceId string
|
||||
|
||||
// tll 时间戳
|
||||
Tll time.Duration
|
||||
|
||||
// 签名
|
||||
Sign string
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package configUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
config map[string]interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
func TestReadJsonConfig(t *testing.T) {
|
||||
config, err = ReadJsonConfig("testdata/jsonConfig.ini")
|
||||
if err != nil {
|
||||
t.Errorf("读取JSON配置失败,错误信息为:%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadIntJsonValue(t *testing.T) {
|
||||
actualValue, err := ReadIntJsonValue(config, "ServerGroupId")
|
||||
if err != nil {
|
||||
t.Errorf("读取JSON配置失败,错误信息为:%s", err)
|
||||
}
|
||||
|
||||
expectedValue := 1
|
||||
if actualValue != expectedValue {
|
||||
t.Errorf("期望的值为%d,实际的值为%d", expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadStringJsonValue(t *testing.T) {
|
||||
actualValue, err := ReadStringJsonValue(config, "ChatDBConnection")
|
||||
if err != nil {
|
||||
t.Errorf("读取JSON配置失败,错误信息为:%s", err)
|
||||
}
|
||||
|
||||
expectedValue := "root:moqikaka@tcp(192.168.1.226:3306)/chatserver?charset=utf8&parseTime=true&loc=Local&timeout=30s"
|
||||
if actualValue != expectedValue {
|
||||
t.Errorf("期望的值为%s,实际的值为%s", expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBoolJsonValue(t *testing.T) {
|
||||
actualValue, err := ReadBoolJsonValue(config, "IfRecordMessage")
|
||||
if err != nil {
|
||||
t.Errorf("读取JSON配置失败,错误信息为:%s", err)
|
||||
}
|
||||
|
||||
expectedValue := true
|
||||
if actualValue != expectedValue {
|
||||
t.Errorf("期望的值为%v,实际的值为%v", expectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package logUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
con_MIN_SKIP = 1
|
||||
con_MAX_SKIP = 10
|
||||
)
|
||||
|
||||
// InfoLog 信息日志记录
|
||||
// format:日志格式
|
||||
// args:参数列表
|
||||
func InfoLog(format string, args ...interface{}) {
|
||||
for _, log := range logs {
|
||||
log.InfoLog(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// WarnLog 警告日志记录
|
||||
// format:日志格式
|
||||
// args:参数列表
|
||||
func WarnLog(format string, args ...interface{}) {
|
||||
for _, log := range logs {
|
||||
log.WarnLog(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// DebugLog 调试日志记录
|
||||
// format:日志格式
|
||||
// args:参数列表
|
||||
func DebugLog(format string, args ...interface{}) {
|
||||
for _, log := range logs {
|
||||
log.DebugLog(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorLog 错误日志记录
|
||||
// format:日志格式
|
||||
// args:参数列表
|
||||
func ErrorLog(format string, args ...interface{}) {
|
||||
for _, log := range logs {
|
||||
log.ErrorLog(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalLog 致命错误日志记录
|
||||
// format:日志格式
|
||||
// args:参数列表
|
||||
func FatalLog(format string, args ...interface{}) {
|
||||
for _, log := range logs {
|
||||
log.FatalLog(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Close
|
||||
// @description: 关闭日志
|
||||
// parameter:
|
||||
// @waitFinish:是否等待关闭完成
|
||||
// return:
|
||||
func Close(waitFinish bool) {
|
||||
for _, log := range logs {
|
||||
log.CloseLog(waitFinish)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------Deprecated methods start----------------------------
|
||||
|
||||
// Log 日志记录
|
||||
// Deprecated: use XXXLog api instead
|
||||
func Log(logInfo string, lv LogType, ifIncludeHour bool) {
|
||||
switch lv {
|
||||
case Info:
|
||||
InfoLog(logInfo)
|
||||
case Warn:
|
||||
WarnLog(logInfo)
|
||||
case Debug:
|
||||
DebugLog(logInfo)
|
||||
case Error:
|
||||
ErrorLog(logInfo)
|
||||
case Fatal:
|
||||
FatalLog(logInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// NormalLog 日志记录
|
||||
// Deprecated: use XXXLog api instead
|
||||
func NormalLog(logInfo string, level LogType) {
|
||||
Log(logInfo, level, true)
|
||||
}
|
||||
|
||||
// LogAndPrint 日志记录
|
||||
// Deprecated: use XXXLog api instead
|
||||
func LogAndPrint(logInfo string, level LogType) {
|
||||
NormalLog(logInfo, level)
|
||||
fmt.Println(logInfo)
|
||||
}
|
||||
|
||||
// LogUnknownError 日志记录
|
||||
func LogUnknownError(r interface{}, args ...string) {
|
||||
buf := strings.Builder{}
|
||||
buf.WriteString(fmt.Sprintf("通过recover捕捉到的未处理异常:%v \n", r))
|
||||
|
||||
// 获取附加信息
|
||||
if len(args) > 0 {
|
||||
buf.WriteString("附加信息:")
|
||||
buf.WriteString(strings.Join(args, "-"))
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
// 获取堆栈信息
|
||||
for skip := con_MIN_SKIP; skip <= con_MAX_SKIP; skip++ {
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("skip = %d, file = %s, line = %d \n", skip, file, line))
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
ErrorLog(buf.String())
|
||||
}
|
||||
|
||||
//--------------------------Deprecated methods end----------------------------
|
||||
@ -0,0 +1,76 @@
|
||||
// ************************************
|
||||
// @package: websocketServer
|
||||
// @description: WsServer/WssServer接口,以便统一调用
|
||||
// @author:
|
||||
// @revision history:
|
||||
// @create date: 2022-02-22 16:07:27
|
||||
// ************************************
|
||||
package websocketServer
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
webServer "Framework/webServer"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IServer
|
||||
// @description: WsServer/WssServer接口,以便统一调用
|
||||
type IServer interface {
|
||||
//-------------------------------------
|
||||
// HttpServer方法
|
||||
|
||||
// HttpServer接口
|
||||
webServer.IWebServer
|
||||
|
||||
// 设置地址
|
||||
SetAddr(addr string)
|
||||
|
||||
// 启动HttpServer
|
||||
Start(wg *sync.WaitGroup)
|
||||
|
||||
//-------------------------------------
|
||||
// websocket方法
|
||||
|
||||
// 注册websocket回调
|
||||
RegisterWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig)
|
||||
|
||||
// 注册正则websocket回调
|
||||
RegisterRegexWebsocketHandler(path string, eventCallback *EventCallbackFuncs, configObj *webServer.HandlerConfig)
|
||||
|
||||
// 设置websocket参数结构
|
||||
SetUpgrader(upgrader *websocket.Upgrader)
|
||||
|
||||
// 获取websocket参数结构
|
||||
GetUpgrader() *websocket.Upgrader
|
||||
|
||||
// 设置接收到Ping消息时,是否自动回复Pong信息
|
||||
SetAutoPong(autuPong bool)
|
||||
|
||||
// 获取接收到Ping消息时,是否自动回复Pong信息
|
||||
GetAutoPong() bool
|
||||
|
||||
// 设置心跳检测信息
|
||||
SetHeartbeatDetectInfo(heartbeatCloseCount int, heartbeatCycle time.Duration)
|
||||
|
||||
// 获取心跳检测信息
|
||||
GetHeartbeatDetectInfo() (heartbeatCloseCount int, heartbeatCycle time.Duration)
|
||||
|
||||
// 设置广播并发数
|
||||
SetBroadcastConcurrent(n int)
|
||||
|
||||
// 允许新连接
|
||||
EnableNewConn()
|
||||
|
||||
// 禁用新连接
|
||||
DisableNewConn()
|
||||
|
||||
// 多播消息(给指定多用户发送消息)
|
||||
MulticastMessage(ctxs []*Context, messageType int, data []byte) (err error)
|
||||
|
||||
// 消息广播
|
||||
BroadcastMessage(messageType int, data []byte) (err error)
|
||||
|
||||
// 关闭所有连接
|
||||
CloseAll()
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>go websocket</title>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var wsUri ="ws://127.0.0.1:22222/websocket";
|
||||
var output;
|
||||
|
||||
function init() {
|
||||
output = document.getElementById("output");
|
||||
testWebSocket();
|
||||
}
|
||||
|
||||
function testWebSocket() {
|
||||
websocket = new WebSocket(wsUri);
|
||||
websocket.onopen = function(evt) {
|
||||
onOpen(evt)
|
||||
};
|
||||
websocket.onclose = function(evt) {
|
||||
onClose(evt)
|
||||
};
|
||||
websocket.onmessage = function(evt) {
|
||||
onMessage(evt)
|
||||
};
|
||||
websocket.onerror = function(evt) {
|
||||
onError(evt)
|
||||
};
|
||||
}
|
||||
|
||||
function onOpen(evt) {
|
||||
writeToScreen("CONNECTED");
|
||||
// doSend("WebSocket rocks");
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
writeToScreen("DISCONNECTED");
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
writeToScreen('<span style="color: blue;">RESPONSE: '+ evt.data+'</span>');
|
||||
// websocket.close();
|
||||
}
|
||||
|
||||
function onError(evt) {
|
||||
writeToScreen('<span style="color: red;">ERROR:</span> '+ evt.data);
|
||||
}
|
||||
|
||||
function doSend(message) {
|
||||
writeToScreen("SENT: " + message);
|
||||
websocket.send(message);
|
||||
}
|
||||
|
||||
function writeToScreen(message) {
|
||||
var pre = document.createElement("p");
|
||||
pre.style.wordWrap = "break-word";
|
||||
pre.innerHTML = message;
|
||||
output.appendChild(pre);
|
||||
}
|
||||
|
||||
window.addEventListener("load", init, false);
|
||||
function sendBtnClick(){
|
||||
var msg = document.getElementById("input").value;
|
||||
doSend(msg);
|
||||
document.getElementById("input").value = '';
|
||||
}
|
||||
function closeBtnClick(){
|
||||
websocket.close();
|
||||
}
|
||||
</script>
|
||||
<h2>WebSocket Test</h2>
|
||||
<input type="text" id="input"></input>
|
||||
<button onclick="sendBtnClick()" >send</button>
|
||||
<button onclick="closeBtnClick()" >close</button>
|
||||
<div id="output"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,15 @@
|
||||
package dfaExUtil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHandleWord(t *testing.T) {
|
||||
strs := []string{"ABC", "1234", "测试", "测试代码", "测试一下"}
|
||||
|
||||
dfaEx1 := NewDFAEx(strs)
|
||||
str := dfaEx1.HandleWord("abc按了数字12345来测试代码是否正常,结果测试出了bug", '*')
|
||||
t.Log(str)
|
||||
|
||||
dfaEx2 := NewDFAEx(strs, true)
|
||||
str = dfaEx2.HandleWord("abc按了数字12345来测试代码是否正常,结果测试出了bug", '*')
|
||||
t.Log(str)
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
提供统一的ip验证的逻辑;包括两部分:
|
||||
1、ManageCenter中配置到ServerGroup中的IP;系统内部自动处理,外部无需关注(暂时不用);
|
||||
2、各个应用程序中配置的ip;通过调用Init或InitString方法进行初始化;
|
||||
*/
|
||||
package ipMgr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
ipMap = make(map[string]struct{}, 32) // 本地ip集合
|
||||
ipCheckFuncList = make([]func(string) bool, 0, 16) // ip检查函数列表
|
||||
mutex sync.RWMutex
|
||||
)
|
||||
|
||||
// 初始化IP列表
|
||||
func Init(ipList []string) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
// 先清空再初始化
|
||||
ipMap = make(map[string]struct{}, 32)
|
||||
for _, item := range ipList {
|
||||
ipMap[item] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化ip字符串(以分隔符分割的,分隔符为:",", ";", ":", "|", "||")
|
||||
func InitString(ipStr string) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
// 先清空再初始化
|
||||
ipMap = make(map[string]struct{}, 32)
|
||||
for _, item := range stringUtil.Split(ipStr, nil) {
|
||||
ipMap[item] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func AddString(ipStr string) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
// 先清空再初始化
|
||||
if ipMap == nil {
|
||||
ipMap = make(map[string]struct{}, 32)
|
||||
}
|
||||
|
||||
for _, item := range stringUtil.Split(ipStr, nil) {
|
||||
ipMap[item] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterIpCheckFunc 注册Ip检查函数
|
||||
func RegisterIpCheckFunc(funcName string, funcItem func(string) bool) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
ipCheckFuncList = append(ipCheckFuncList, funcItem)
|
||||
}
|
||||
|
||||
func isLocalValid(ip string) bool {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
||||
_, exist := ipMap[ip]
|
||||
return exist
|
||||
}
|
||||
|
||||
func isCheckFuncValid(ip string) bool {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
||||
for _, funcItem := range ipCheckFuncList {
|
||||
if funcItem(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断传入的Ip是否有效
|
||||
// ip:ip
|
||||
// 返回值:
|
||||
// 是否有效
|
||||
func IsIpValid(ip string) bool {
|
||||
return isLocalValid(ip) || isCheckFuncValid(ip)
|
||||
}
|
||||
@ -0,0 +1,210 @@
|
||||
package managecenterModel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"goutil/stringUtil"
|
||||
"goutil/typeUtil"
|
||||
)
|
||||
|
||||
// 游戏版本
|
||||
type ResourceVersion struct {
|
||||
// 资源版本唯一标识
|
||||
Id int32 `json:"ResourceVersionID"`
|
||||
|
||||
// 资源版本名称
|
||||
Name string `json:"ResourceVersionName"`
|
||||
|
||||
// 资源版本的url地址
|
||||
Url string `json:"ResourceVersionUrl"`
|
||||
|
||||
// 资源大小
|
||||
Size int32 `json:"Size"`
|
||||
|
||||
// 资源文件MD5加密的结果
|
||||
MD5 string `json:"MD5"`
|
||||
|
||||
//大区Id
|
||||
AreaID int32 `json:"AreaID"`
|
||||
|
||||
// 资源生效时间
|
||||
StartTime string `json:"StartTime"`
|
||||
StartTimeTick int64 `json:"StartTimeTick"`
|
||||
|
||||
// 资源失效时间
|
||||
EndTime string `json:"EndTime"`
|
||||
EndTimeTick int64 `json:"EndTimeTick"`
|
||||
|
||||
// 添加时间
|
||||
Crdate string `json:"Crdate"`
|
||||
CrdateTick int64 `json:"CrdateTick"`
|
||||
|
||||
// 更新时间
|
||||
UpdateTime string `json:"UpdateTime"`
|
||||
UpdateTimeTick int64 `json:"UpdateTimeTick"`
|
||||
|
||||
// 是否重启客户端
|
||||
IfRestart int32 `json:"IfRestart"`
|
||||
|
||||
// 是否禁用
|
||||
IfDelete int32 `json:"IfDelete"`
|
||||
|
||||
// 是否审核服下载
|
||||
IfAuditServiceDownload int32 `json:"IfAuditServiceDownload"`
|
||||
|
||||
// 资源所属的合作商ID集合
|
||||
PartnerIds string `json:"PartnerIDs"`
|
||||
|
||||
// 资源所属的游戏版本ID集合
|
||||
GameVersionIds string `json:"GameVersionIDs"`
|
||||
}
|
||||
|
||||
// 判断资源是否包含指定合作商
|
||||
// partnerId:合作商Id
|
||||
// 返回值
|
||||
// 是否包含
|
||||
func (this *ResourceVersion) ContainsPartner(partnerId int32) bool {
|
||||
partnerIdList, _ := stringUtil.SplitToInt32Slice(this.PartnerIds, ",")
|
||||
for _, item := range partnerIdList {
|
||||
if item == partnerId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断资源是否包含指定游戏版本
|
||||
// gameVersionId:游戏版本Id
|
||||
// 返回值
|
||||
// 是否包含
|
||||
func (this *ResourceVersion) ContainsGameVersion(gameVersionId int32) bool {
|
||||
gameVersionIdList, _ := stringUtil.SplitToInt32Slice(this.GameVersionIds, ",")
|
||||
for _, item := range gameVersionIdList {
|
||||
if item == gameVersionId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取有效资源包
|
||||
func GetAvailableResource(resourceVersionList []*ResourceVersion, partnerId, gameVersionId int32, resourceVersionName string, offTest OfficialOrTest) (availableResourceVersion map[string]interface{}) {
|
||||
|
||||
if len(resourceVersionList) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
//判断资源是否有效
|
||||
_, hashCode, isVaild := IsResourceVersionNameValid(resourceVersionName)
|
||||
if !isVaild {
|
||||
return
|
||||
}
|
||||
|
||||
//根据合作商Id和游戏版本Id和开始时间来过滤
|
||||
var targetResourceVersionList []*ResourceVersion
|
||||
for _, resourceVersion := range resourceVersionList {
|
||||
startime, err := typeUtil.DateTime(resourceVersion.StartTimeTick)
|
||||
if resourceVersion.ContainsPartner(partnerId) && resourceVersion.ContainsGameVersion(gameVersionId) && err == nil && startime.Before(time.Now()) {
|
||||
targetResourceVersionList = append(targetResourceVersionList, resourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if len(targetResourceVersionList) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
//组装数据
|
||||
|
||||
//按照资源Id进行降序排列
|
||||
sort.Slice(targetResourceVersionList, func(i, j int) bool {
|
||||
return targetResourceVersionList[i].SortByIdDesc(targetResourceVersionList[j])
|
||||
})
|
||||
|
||||
//取出资源号最大的资源,如果与传入的资源名称相等,则表示没有新资源
|
||||
availableResourceVersion = make(map[string]interface{}, 0)
|
||||
newResource := targetResourceVersionList[0]
|
||||
availableResourceVersion["ResourceVersionName"] = newResource.Name
|
||||
availableResourceVersion["Url"] = strings.Replace(newResource.Url, ".zip", "", -1)
|
||||
|
||||
if newResource.Name == resourceVersionName {
|
||||
|
||||
//是否有新资源,如果为fasle,也需要返回project.manifest.temp.zip 用于子包验证
|
||||
availableResourceVersion["IsNewResource"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//判断资源号中的HashCode是否相等,如果相等,则表示没有新资源;如果传入的timeTick>最新的timeTick说明服务器没有被刷新,表示没有新资源
|
||||
_, newHashCode, newIsVaild := IsResourceVersionNameValid(newResource.Name)
|
||||
if !newIsVaild {
|
||||
return
|
||||
}
|
||||
|
||||
if compareRes := strings.Compare(hashCode, newHashCode); compareRes == 0 {
|
||||
//是否有新资源,如果为fasle,也需要返回project.manifest.temp.zip 用于子包验证
|
||||
availableResourceVersion["IsNewResource"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if offTest == Con_Test && newResource.IfAuditServiceDownload == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
availableResourceVersion["IsNewResource"] = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 按照Id进行升序排序
|
||||
// target:另一个资源对象
|
||||
// 是否是小于
|
||||
func (this *ResourceVersion) SortByIdAsc(target *ResourceVersion) bool {
|
||||
return this.Id < target.Id
|
||||
}
|
||||
|
||||
// 按照Id进行降序排序
|
||||
// target:另一个资源对象
|
||||
// 是否是大于
|
||||
func (this *ResourceVersion) SortByIdDesc(target *ResourceVersion) bool {
|
||||
return this.Id > target.Id
|
||||
}
|
||||
|
||||
//判断资源版本是否有效
|
||||
func IsResourceVersionNameValid(resourceVersionName string) (timeTick int64, hashCode string, isValid bool) {
|
||||
if len(resourceVersionName) == 0 {
|
||||
isValid = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if index := strings.Index(resourceVersionName, "_"); index == -1 {
|
||||
resourceVersionName = fmt.Sprintf("0_%s", resourceVersionName)
|
||||
}
|
||||
|
||||
resourceList := stringUtil.Split(resourceVersionName, []string{"_"})
|
||||
if len(resourceList) != 2 {
|
||||
isValid = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//解析timeTick
|
||||
timeTick, err := strconv.ParseInt(resourceList[0], 10, 64)
|
||||
if err != nil {
|
||||
isValid = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
hashCode = resourceList[1]
|
||||
isValid = true
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
func (this *RedisPool) Int(reply interface{}) (int, error) {
|
||||
return redis.Int(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64(reply interface{}) (int64, error) {
|
||||
return redis.Int64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Uint64(reply interface{}) (uint64, error) {
|
||||
return redis.Uint64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Float64(reply interface{}) (float64, error) {
|
||||
return redis.Float64(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) String(reply interface{}) (string, error) {
|
||||
return redis.String(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Bytes(reply interface{}) ([]byte, error) {
|
||||
return redis.Bytes(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Bool(reply interface{}) (bool, error) {
|
||||
return redis.Bool(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Values(reply interface{}) ([]interface{}, error) {
|
||||
return redis.Values(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Ints(reply interface{}) ([]int, error) {
|
||||
return redis.Ints(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64s(reply interface{}) ([]int64, error) {
|
||||
return redis.Int64s(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Float64s(reply interface{}) ([]float64, error) {
|
||||
return redis.Float64s(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Strings(reply interface{}) ([]string, error) {
|
||||
return redis.Strings(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) ByteSlices(reply interface{}) ([][]byte, error) {
|
||||
return redis.ByteSlices(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) IntMap(reply interface{}) (map[string]int, error) {
|
||||
return redis.IntMap(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Int64Map(reply interface{}) (map[string]int64, error) {
|
||||
return redis.Int64Map(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) StringMap(reply interface{}) (map[string]string, error) {
|
||||
return redis.StringMap(reply, nil)
|
||||
}
|
||||
|
||||
func (this *RedisPool) Positions(reply interface{}) ([]*[2]float64, error) {
|
||||
return redis.Positions(reply, nil)
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package intAndBytesUtil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInt16ToBytes(t *testing.T) {
|
||||
var expectedBigEndian []byte = []byte{1, 0}
|
||||
var expectedLittleEndian []byte = []byte{0, 1}
|
||||
var givenInt int16 = 256
|
||||
|
||||
result := Int16ToBytes(givenInt, binary.BigEndian)
|
||||
if equal(result, expectedBigEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian)
|
||||
}
|
||||
|
||||
result = Int16ToBytes(givenInt, binary.LittleEndian)
|
||||
if equal(result, expectedLittleEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32ToBytes(t *testing.T) {
|
||||
var expectedBigEndian []byte = []byte{0, 0, 1, 0}
|
||||
var expectedLittleEndian []byte = []byte{0, 1, 0, 0}
|
||||
var givenInt int32 = 256
|
||||
|
||||
result := Int32ToBytes(givenInt, binary.BigEndian)
|
||||
if equal(result, expectedBigEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian)
|
||||
}
|
||||
|
||||
result = Int32ToBytes(givenInt, binary.LittleEndian)
|
||||
if equal(result, expectedLittleEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64ToBytes(t *testing.T) {
|
||||
var expectedBigEndian []byte = []byte{0, 0, 0, 0, 0, 0, 1, 0}
|
||||
var expectedLittleEndian []byte = []byte{0, 1, 0, 0, 0, 0, 0, 0}
|
||||
var givenInt int64 = 256
|
||||
|
||||
result := Int64ToBytes(givenInt, binary.BigEndian)
|
||||
if equal(result, expectedBigEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedBigEndian)
|
||||
}
|
||||
|
||||
result = Int64ToBytes(givenInt, binary.LittleEndian)
|
||||
if equal(result, expectedLittleEndian) == false {
|
||||
t.Errorf("IntToBytes(%v) failed.Got %v, expected %v", givenInt, result, expectedLittleEndian)
|
||||
}
|
||||
}
|
||||
|
||||
func equal(b1, b2 []byte) bool {
|
||||
if len(b1) != len(b2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(b1); i++ {
|
||||
if b1[i] != b2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
Log/*
|
||||
logs/*
|
||||
1501
.svn/pristine/19/19cd06f67349e079d14ba9d6e284cd8a2cbd2ca4.svn-base
Normal file
1501
.svn/pristine/19/19cd06f67349e079d14ba9d6e284cd8a2cbd2ca4.svn-base
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,114 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
redisPoolObj *RedisPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
redisPoolObj = NewRedisPool("testPool", "10.1.0.21:6379", "redis_pwd", 5, 500, 200, 10*time.Second, 5*time.Second)
|
||||
}
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
expected := "testPool"
|
||||
got := redisPoolObj.GetName()
|
||||
if expected != got {
|
||||
t.Errorf("Expected to get %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddress(t *testing.T) {
|
||||
expected := "10.1.0.21:6379"
|
||||
got := redisPoolObj.GetAddress()
|
||||
if expected != got {
|
||||
t.Errorf("Expected to get %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func converInterfaceSliceToStringSlice(sourceList []interface{}) []string {
|
||||
targetList := make([]string, 0, len(sourceList))
|
||||
for _, item := range sourceList {
|
||||
if item == nil {
|
||||
targetList = append(targetList, "")
|
||||
} else if item_str, ok := item.(string); ok {
|
||||
targetList = append(targetList, item_str)
|
||||
} else if item_bytes, ok2 := item.([]byte); ok2 {
|
||||
targetList = append(targetList, string(item_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
return targetList
|
||||
}
|
||||
|
||||
func isTwoOrderedSliceEqual(list1, list2 []string) bool {
|
||||
if list1 == nil && list2 == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if list1 == nil || list2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(list1) != len(list2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(list1); i++ {
|
||||
if list1[i] != list2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isTwoUnorderedSliceEqual(list1, list2 []string) bool {
|
||||
if list1 == nil && list2 == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if list1 == nil || list2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(list1) != len(list2) {
|
||||
return false
|
||||
}
|
||||
|
||||
map1 := make(map[string]struct{})
|
||||
map2 := make(map[string]struct{})
|
||||
|
||||
for _, item := range list1 {
|
||||
map1[item] = struct{}{}
|
||||
}
|
||||
for _, item := range list2 {
|
||||
map2[item] = struct{}{}
|
||||
}
|
||||
|
||||
for k := range map1 {
|
||||
if _, exist := map2[k]; !exist {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getDistinctKeyList(keyList []string) []string {
|
||||
distinctKeyList := make([]string, 0, len(keyList))
|
||||
keyMap := make(map[string]struct{})
|
||||
for _, key := range keyList {
|
||||
if _, exist := keyMap[key]; !exist {
|
||||
distinctKeyList = append(distinctKeyList, key)
|
||||
keyMap[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return distinctKeyList
|
||||
}
|
||||
@ -0,0 +1,587 @@
|
||||
package typeUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMapDataByte(t *testing.T) {
|
||||
TestMapDataUint8(t)
|
||||
}
|
||||
|
||||
func TestMapDataInt(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected int = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Int(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Int(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Int(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataInt8(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected int8 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Int8(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Int8(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Int8(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataInt16(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected int16 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Int16(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Int16(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Int16(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataInt32(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected int32 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Int32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Int32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Int32(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataInt64(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected int64 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Int64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Int64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Int64(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataUint(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected uint = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Uint(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Uint(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Uint(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataUint8(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected uint8 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Uint8(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Uint8(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Uint8(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataUint16(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected uint16 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Uint16(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Uint16(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Uint16(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataUint32(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected uint32 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Uint32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Uint32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Uint32(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataUint64(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected uint64 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Uint64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Uint64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Uint64(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but got %d", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataFloat32(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected float32 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Float32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Float32(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Float32(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %f, but got %f", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataFloat64(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected float64 = 0
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Float64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Float64(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = 1
|
||||
expected = 1
|
||||
got, err = mapData.Float64(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %f, but got %f", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataBool(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected bool = true
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Bool(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = "abc"
|
||||
got, err = mapData.Bool(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = true
|
||||
expected = true
|
||||
got, err = mapData.Bool(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %t, but got %t", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataString(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected string = ""
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.String(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist, but type doesn't match
|
||||
mapData[key] = 123
|
||||
expected = "123"
|
||||
got, err = mapData.String(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = "hello"
|
||||
expected = "hello"
|
||||
got, err = mapData.String(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
|
||||
func TestMapDataDateTime(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected time.Time = time.Date(2019, time.December, 25, 12, 0, 0, 0, time.UTC)
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.DateTime(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist but value doesn't match
|
||||
mapData[key] = "123"
|
||||
expected = time.Date(2019, time.December, 25, 12, 0, 0, 0, time.UTC)
|
||||
got, err = mapData.DateTime(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = time.Date(2019, time.December, 25, 12, 0, 0, 0, time.UTC)
|
||||
expected = time.Date(2019, time.December, 25, 12, 0, 0, 0, time.UTC)
|
||||
got, err = mapData.DateTime(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("Expected %s, but got %s", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDataInterface(t *testing.T) {
|
||||
data := make(map[string]interface{})
|
||||
mapData := NewMapData(data)
|
||||
key := "key"
|
||||
var expected *Person
|
||||
|
||||
// Test when key doesn't exist
|
||||
got, err := mapData.Interface(key)
|
||||
if err == nil {
|
||||
t.Errorf("There should be an error, but now there isn't.")
|
||||
return
|
||||
}
|
||||
|
||||
// Test when key exist and value matches
|
||||
mapData[key] = NewPerson("Jordan", 34)
|
||||
expected = NewPerson("Jordan", 34)
|
||||
got, err = mapData.Interface(key)
|
||||
if err != nil {
|
||||
t.Errorf("There should be no error, but now there is:%s", err)
|
||||
return
|
||||
}
|
||||
if gotPersonObj, ok := got.(*Person); !ok {
|
||||
t.Errorf("Expected type *Person")
|
||||
} else if gotPersonObj.SameAs(expected) == false {
|
||||
t.Errorf("Expected %v, but got %v", expected, got)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
func (this *Person) SameAs(other *Person) bool {
|
||||
return this.Name == other.Name && this.Age == other.Age
|
||||
}
|
||||
|
||||
func NewPerson(name string, age int) *Person {
|
||||
return &Person{
|
||||
Name: name,
|
||||
Age: age,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package bytesSendUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
实现sender接口
|
||||
*/
|
||||
|
||||
type baseSender struct {
|
||||
// 待发送的数据channel
|
||||
waitingDataChan chan dataItem
|
||||
|
||||
// 失败数据缓存
|
||||
cachedDataChan chan dataItem
|
||||
|
||||
// 用于停止协程
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newBaseSender() *baseSender {
|
||||
return &baseSender{
|
||||
waitingDataChan: make(chan dataItem, 1024),
|
||||
cachedDataChan: make(chan dataItem, 1024000),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Sender接口
|
||||
// Send:
|
||||
func (this *baseSender) Send() error {
|
||||
// baseSender不实现发送
|
||||
// 由tcpSender和httpSender实现发送
|
||||
return fmt.Errorf("baseSender dose not have Send Method")
|
||||
}
|
||||
|
||||
// Sender接口
|
||||
// Data: 返回待发送的数据channel
|
||||
func (this *baseSender) Data() <-chan dataItem {
|
||||
return this.waitingDataChan
|
||||
}
|
||||
|
||||
// Sender接口
|
||||
// Cache:返回失败数据缓存channel
|
||||
func (this *baseSender) Cache() chan dataItem {
|
||||
return this.cachedDataChan
|
||||
}
|
||||
|
||||
// Sender接口
|
||||
// Done:返回channel用于判断是否关闭
|
||||
func (this *baseSender) Done() <-chan struct{} {
|
||||
return this.done
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
package notify_util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"goutil/syncUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
// key:初始化成功的标志名称 val:占位符
|
||||
registerNC = make(map[string]*notifyCenter)
|
||||
mutex = syncUtil.NewRWLocker()
|
||||
)
|
||||
|
||||
// getItemOrAdd
|
||||
// @description: 获取注册的通知对象
|
||||
// parameter:
|
||||
// @chanGroup:
|
||||
// return:
|
||||
// @*notifyCenter:
|
||||
func getItemOrAdd(chanGroup string) *notifyCenter {
|
||||
if isOk, prevStack, currStack := mutex.Lock(deathLockTime); isOk == false {
|
||||
//记日志
|
||||
errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s \n当前堆栈:\n%s", prevStack, currStack)
|
||||
panic(errMsg)
|
||||
}
|
||||
defer mutex.Unlock()
|
||||
|
||||
nc, exists := registerNC[chanGroup]
|
||||
if exists {
|
||||
return nc
|
||||
}
|
||||
|
||||
nc = newNotifyCenter()
|
||||
registerNC[chanGroup] = nc
|
||||
|
||||
return nc
|
||||
}
|
||||
|
||||
// Register
|
||||
// @description: 注册需要被通知的对象
|
||||
// parameter:
|
||||
// @chanGroup:通知的分组标识
|
||||
// @chanName:唯一标识
|
||||
// @cf:回调方法
|
||||
// return:
|
||||
func Register(chanGroup string, chanName string, cf func()) {
|
||||
nc := getItemOrAdd(chanGroup)
|
||||
nc.register(chanName, cf)
|
||||
}
|
||||
|
||||
// Unregister
|
||||
// @description: 取消启动成功通知注册
|
||||
// parameter:
|
||||
// @chanGroup:分组标识
|
||||
// @name:唯一标识
|
||||
// return:
|
||||
func Unregister(chanGroup string, name string) {
|
||||
nc := getItemOrAdd(chanGroup)
|
||||
nc.unregister(name)
|
||||
}
|
||||
|
||||
// Notify
|
||||
// @description: 通知分组所有已注册的对象
|
||||
// parameter:
|
||||
// @chanGroup:分组标识
|
||||
// return:
|
||||
func Notify(chanGroup string) {
|
||||
nc := getItemOrAdd(chanGroup)
|
||||
nc.notify()
|
||||
}
|
||||
|
||||
// Notify2
|
||||
// @description: 通知所有已注册的对象,该方法会在捕获第一个err的时候停止后续的通知。多用于系统启动的判定
|
||||
// parameter:
|
||||
// @chanGroup:分组标识
|
||||
// return:
|
||||
// @err:
|
||||
func Notify2(chanGroup string) (err error) {
|
||||
nc := getItemOrAdd(chanGroup)
|
||||
err = nc.notify2()
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
### 窗口周期计数器
|
||||
窗口周期计数类,用于记录一个窗口周期数量,并且触发某个操作的场景。
|
||||
在下一个窗口周期会自动重置次数
|
||||
|
||||
#### =======================>使用方法说明<=========================
|
||||
|
||||
1.引入包
|
||||
2.构造对象并次有
|
||||
3.调用对象的增加次数方法
|
||||
|
||||
```go
|
||||
package demo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"goutil/counter_util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 构造名字叫test的,窗口间隔为1s,计数达到2就会触发警告的窗口计数器
|
||||
c := counter_util.NewCounterUtil("test", 2, checkId, func(tag string, num int, ti time.Time) {
|
||||
//自定义触发动作
|
||||
})
|
||||
|
||||
c.AddNum(1)
|
||||
c.AddNum(10)
|
||||
}
|
||||
|
||||
// 窗口周期设定为1s
|
||||
func checkId(t1, t2 time.Time) bool {
|
||||
return t1.Second() == t2.Second()
|
||||
}
|
||||
|
||||
```
|
||||
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"common/connection"
|
||||
"sync"
|
||||
|
||||
_ "common/resultStatus"
|
||||
"common/webServer"
|
||||
_ "logincenter/internal/user"
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 设置WaitGroup需要等待的数量,只要有一个服务器出现错误都停止服务器
|
||||
wg.Add(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
//加载配置
|
||||
loadConfig()
|
||||
|
||||
// 启动webserver
|
||||
go webServer.Start(&wg)
|
||||
|
||||
// 阻塞等待,以免main线程退出
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// loadConfig 用于加载配置信息。
|
||||
// 该函数会读取配置文件或环境变量中的设置,并根据这些设置初始化程序所需的配置。
|
||||
// 目前函数的实现为空,需要根据实际的配置加载逻辑进行填充。
|
||||
func loadConfig() {
|
||||
|
||||
//构建数据库
|
||||
connection.BuildDB()
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
module logincenter
|
||||
|
||||
go 1.22.10
|
||||
|
||||
replace (
|
||||
common => ../common
|
||||
framework => ../../framework
|
||||
goutil => ../../goutil
|
||||
)
|
||||
|
||||
require common v0.0.0-00010101000000-000000000000
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
framework v0.0.0-20230425160006-b2d0b0a0b0b0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/elastic/go-elasticsearch/v8 v8.0.0-20210916085751-c2fb55d91ba4 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gomodule/redigo v1.8.9 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/jinzhu/gorm v1.9.12 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
gorm.io/gorm v1.25.12 // indirect
|
||||
goutil v0.0.0-20230425160006-b2d0b0a0b0b0 // indirect
|
||||
)
|
||||
@ -0,0 +1,6 @@
|
||||
package verifyMgr
|
||||
|
||||
/*
|
||||
此包用于对指定url进行访问验证,以便于在程序启动时可以提前知道目标地址的可访问性,
|
||||
而不用等到实际需要时再验证;从而造成既定的影响。
|
||||
*/
|
||||
@ -0,0 +1,22 @@
|
||||
package sensitiveWordsMgr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// type Persion struct {
|
||||
// name string
|
||||
// }
|
||||
|
||||
// 屏蔽字详细信息
|
||||
func Test1(t *testing.T) {
|
||||
//启动获取敏感字
|
||||
refreshSensitiveWord()
|
||||
|
||||
words, pos, exist := SensitiveWords("测试,测试")
|
||||
if exist {
|
||||
t.Log(words, pos)
|
||||
}
|
||||
|
||||
t.Log("END")
|
||||
}
|
||||
@ -0,0 +1,377 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"goutil/logUtilPlus"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定义常量
|
||||
var MinDateTime = time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
var MaxDateTime = time.Date(3000, 1, 1, 0, 0, 0, 0, time.Local)
|
||||
var GuidEmpty = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
// IsTrue
|
||||
// @description: 是否为true
|
||||
// parameter:
|
||||
// @data: []byte格式的bool信息
|
||||
// return:
|
||||
// @bool: 返回布尔值
|
||||
func IsTrue(data []byte) bool {
|
||||
|
||||
if data == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if data[0] == 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertBooleanToBytes
|
||||
// @description: bool型转换成byte数组
|
||||
// parameter:
|
||||
// @status: 状态
|
||||
// return:
|
||||
// @[]byte: 返回字节数组
|
||||
func ConvertBooleanToBytes(status bool) []byte {
|
||||
|
||||
if status == false {
|
||||
return []byte{0}
|
||||
} else {
|
||||
return []byte{1}
|
||||
}
|
||||
}
|
||||
|
||||
// StrSliceJoinToStr
|
||||
// @description: 将[]string组装成字符串
|
||||
// parameter:
|
||||
// @numArray: 源int32数组
|
||||
// @sep: 分割字符串
|
||||
// return:
|
||||
// @string: 组成的字符串
|
||||
func StrSliceJoinToStr(numArray []string, sep string) string {
|
||||
str := ""
|
||||
if numArray == nil || len(numArray) == 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
for _, n := range numArray {
|
||||
str += fmt.Sprintf("%s%s", n, sep)
|
||||
}
|
||||
|
||||
str = strings.TrimSuffix(str, sep)
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// Int32SliceJoinToStr
|
||||
// @description: 将[]int32转换成字符串
|
||||
// parameter:
|
||||
// @source: 资源
|
||||
// @sep: 分隔符
|
||||
// return:
|
||||
// @result: 字符串
|
||||
func Int32SliceJoinToStr(source []int32, sep string) (result string) {
|
||||
if source == nil || len(source) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, s := range source {
|
||||
result += fmt.Sprintf("%d%s", s, sep)
|
||||
}
|
||||
|
||||
result = strings.TrimRight(result, sep)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SplitToStrSlice
|
||||
// @description: 将字符串切割为[]string
|
||||
// parameter:
|
||||
// @s: 输入字符串
|
||||
// @sep: 分割字符串
|
||||
// @removeEmpty: 是否去除空字符串
|
||||
// return:
|
||||
// @resultSlice: 字符串列表
|
||||
func SplitToStrSlice(s, sep string, removeEmpty bool) (resultSlice []string) {
|
||||
if len(s) == 0 {
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
// 先按照分隔符进行切割
|
||||
strSlice := strings.Split(s, sep)
|
||||
|
||||
for _, value := range strSlice {
|
||||
if removeEmpty {
|
||||
// 去除空格
|
||||
if value = strings.TrimSpace(value); value == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
resultSlice = append(resultSlice, value)
|
||||
}
|
||||
|
||||
return resultSlice
|
||||
}
|
||||
|
||||
// ParseTimeString
|
||||
// @description: 解析时间字符串
|
||||
// parameter:
|
||||
// @timeStr: 时间字符串,例:12:33:12
|
||||
// return:
|
||||
// @hour: 小时
|
||||
// @minute: 分钟
|
||||
// @second: 秒数
|
||||
func ParseTimeString(timeStr string) (hour int, minute int, second int) {
|
||||
timeSlice := strings.Split(timeStr, ":")
|
||||
if len(timeSlice) != 3 {
|
||||
return
|
||||
}
|
||||
|
||||
hour, _ = strconv.Atoi(timeSlice[0])
|
||||
minute, _ = strconv.Atoi(timeSlice[1])
|
||||
second, _ = strconv.Atoi(timeSlice[2])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Rm_duplicate_string
|
||||
// @description: 去重
|
||||
// parameter:
|
||||
// @list: 列表
|
||||
// return:
|
||||
// @[]string: 去重后的数据
|
||||
func Rm_duplicate_string(list []string) []string {
|
||||
var x []string = []string{}
|
||||
for _, i := range list {
|
||||
if len(x) == 0 {
|
||||
x = append(x, i)
|
||||
} else {
|
||||
for k, v := range x {
|
||||
if i == v {
|
||||
break
|
||||
}
|
||||
if k == len(x)-1 {
|
||||
x = append(x, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Rm_duplicate_int32
|
||||
// @description: 去重
|
||||
// parameter:
|
||||
// @list: 列表数据
|
||||
// return:
|
||||
// @[]int32: 去重后的结果
|
||||
func Rm_duplicate_int32(list []int32) []int32 {
|
||||
var x []int32 = []int32{}
|
||||
for _, i := range list {
|
||||
if len(x) == 0 {
|
||||
x = append(x, i)
|
||||
} else {
|
||||
for k, v := range x {
|
||||
if i == v {
|
||||
break
|
||||
}
|
||||
if k == len(x)-1 {
|
||||
x = append(x, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// RandomAarrayOfInt32
|
||||
// @description: int32数组乱序
|
||||
// parameter:
|
||||
// @arr: 数组
|
||||
// return:
|
||||
func RandomAarrayOfInt32(arr []int32) {
|
||||
|
||||
if arr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(arr) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
for i := len(arr) - 1; i > 0; i-- {
|
||||
num := rand.Intn(i + 1)
|
||||
arr[i], arr[num] = arr[num], arr[i]
|
||||
}
|
||||
}
|
||||
|
||||
// RandomAarrayOfString
|
||||
// @description: string数组乱序
|
||||
// parameter:
|
||||
// @arr: 数组
|
||||
// return:
|
||||
func RandomAarrayOfString(arr []string) {
|
||||
|
||||
if arr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(arr) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
for i := len(arr) - 1; i > 0; i-- {
|
||||
num := rand.Intn(i + 1)
|
||||
arr[i], arr[num] = arr[num], arr[i]
|
||||
}
|
||||
}
|
||||
|
||||
// LogErrorRecover
|
||||
// @description: 记录错误
|
||||
// parameter:
|
||||
// return:
|
||||
func LogErrorRecover() {
|
||||
if err := recover(); err != nil {
|
||||
tmsg := fmt.Sprintf("err msg:%s stack:%s", err, debug.Stack())
|
||||
logUtilPlus.ErrorLog(tmsg)
|
||||
}
|
||||
}
|
||||
|
||||
// LogReqErrorRecover
|
||||
// @description: 记录错误
|
||||
// parameter:
|
||||
// @r:
|
||||
// return:
|
||||
func LogReqErrorRecover(r *http.Request) {
|
||||
if err := recover(); err != nil {
|
||||
b, err := json.Marshal(r)
|
||||
reqStr := ""
|
||||
if err == nil {
|
||||
reqStr = string(b)
|
||||
}
|
||||
tmsg := fmt.Sprintf("RequestInfo:%s .err msg:%s stack:%s", reqStr, err, debug.Stack())
|
||||
logUtilPlus.ErrorLog(tmsg)
|
||||
}
|
||||
}
|
||||
|
||||
// StringSliceIsExists
|
||||
// @description: StringSliceIsExists
|
||||
// parameter:
|
||||
// @strList: strList
|
||||
// @val: val
|
||||
// return:
|
||||
// @bool: 是否存在
|
||||
func StringSliceIsExists(strList []string, val string) bool {
|
||||
|
||||
if strList == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range strList {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Int64SliceIsExists
|
||||
// @description: Int64SliceIsExists
|
||||
// parameter:
|
||||
// @strList: strList
|
||||
// @val: val
|
||||
// return:
|
||||
// @bool: 是否存在
|
||||
func Int64SliceIsExists(strList []int64, val int64) bool {
|
||||
|
||||
if strList == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range strList {
|
||||
if v == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SliceIsExists
|
||||
// @description: SliceIsExists
|
||||
// parameter:
|
||||
// @n: n
|
||||
// @f: f
|
||||
// return:
|
||||
// @bool: 是否存在
|
||||
func SliceIsExists(n int, f func(int) bool) bool {
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
if f(i) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// FlateEncode
|
||||
// @description: 压缩字符串
|
||||
// parameter:
|
||||
// @input: 输入字符列表
|
||||
// return:
|
||||
// @result: 结果字符列表
|
||||
// @err: 错误信息
|
||||
func FlateEncode(input []byte) (result []byte, err error) {
|
||||
var buf bytes.Buffer
|
||||
w, err := flate.NewWriter(&buf, flate.DefaultCompression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 无法使用defer close使用无法拿到结果
|
||||
_, err = w.Write(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.Close()
|
||||
|
||||
result = buf.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
// FlateDecode
|
||||
// @description: FlateDecode
|
||||
// parameter:
|
||||
// @input: 输入字符列表
|
||||
// return:
|
||||
// @result: 结果字符列表
|
||||
// @err: 错误信息
|
||||
func FlateDecode(input []byte) (result []byte, err error) {
|
||||
result, err = ioutil.ReadAll(flate.NewReader(bytes.NewReader(input)))
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"common/connection"
|
||||
)
|
||||
|
||||
func init() {
|
||||
//注册数据库
|
||||
connection.RegisterDBModel(&Admin{})
|
||||
}
|
||||
|
||||
type Admin struct {
|
||||
ID int64 `gorm:"column:id;primary_key;comment:管理员id;autoIncrementIncrement" json:"id"`
|
||||
//账号
|
||||
Account string `gorm:"column:account;comment:账号" json:"account"`
|
||||
Name string `gorm:"column:name;comment:管理员名称" json:"name"`
|
||||
Password string `gorm:"column:password;comment:管理员密码" json:"password"`
|
||||
//性别
|
||||
Sex int32 `gorm:"column:sex;comment:性别" json:"sex"`
|
||||
//生日
|
||||
Birthday string `gorm:"column:birthday;comment:生日" json:"birthday"`
|
||||
//手机
|
||||
Phone int64 `gorm:"column:phone;comment:手机" json:"phone"`
|
||||
//邮箱
|
||||
Email string `gorm:"column:email;comment:邮箱" json:"email"`
|
||||
//微信群【方便发送通知】
|
||||
WechatGroup string `gorm:"column:wechat_group;comment:微信群" json:"wechat_group"`
|
||||
//备注
|
||||
Describe string `gorm:"column:describe;comment:备注" json:"describe"`
|
||||
}
|
||||
|
||||
func (Admin) TableName() string {
|
||||
return "admin"
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package validationUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 身份证测试
|
||||
func TestIdCard(t *testing.T) {
|
||||
idno := "450325197410077393"
|
||||
if IsValideIdno(idno) == false {
|
||||
t.Error("身份证验证出错:", idno)
|
||||
t.Fail()
|
||||
}
|
||||
idno = "36062219701120774X"
|
||||
if IsValideIdno(idno) == false {
|
||||
t.Error("身份证验证出错:", idno)
|
||||
t.Fail()
|
||||
}
|
||||
idno = "450325197410071111"
|
||||
if IsValideIdno(idno) == false {
|
||||
t.Error("身份证验证出错:", idno)
|
||||
t.Fail()
|
||||
}
|
||||
idno = "3123123123"
|
||||
if IsValideIdno(idno) == true {
|
||||
t.Error("身份证验证出错:", idno)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// 邮箱测试
|
||||
func TestMail(t *testing.T) {
|
||||
mail := "nihao@qq.com"
|
||||
if IsValideEmail(mail) == false {
|
||||
t.Error("邮箱验证出错:", mail)
|
||||
t.Fail()
|
||||
}
|
||||
mail = "111@qq.com"
|
||||
if IsValideEmail(mail) == false {
|
||||
t.Error("邮箱验证出错:", mail)
|
||||
t.Fail()
|
||||
}
|
||||
mail = "111_@qq.com"
|
||||
if IsValideEmail(mail) == false {
|
||||
t.Error("邮箱验证出错:", mail)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// 验证中国的手机号
|
||||
func TestChinesePhone(t *testing.T) {
|
||||
phoneNum := "15111111111"
|
||||
if IsValideChinesePhoneNum(phoneNum) == false {
|
||||
t.Error("手机号验证出错:", phoneNum)
|
||||
t.Fail()
|
||||
}
|
||||
phoneNum = "11111"
|
||||
if IsValideChinesePhoneNum(phoneNum) == true {
|
||||
t.Error("手机号验证出错:", phoneNum)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
package mysqlUtil
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// 打开数据库连接
|
||||
// connectionString:数据库连接字符串,格式:root:moqikaka3306@tcp(10.1.0.10:3306)/gameserver_data?charset=utf8&parseTime=true&loc=Local&timeout=60s||MaxOpenConns=10||MaxIdleConns=5
|
||||
// 返回值:
|
||||
// 数据库对象
|
||||
// 错误对象
|
||||
func OpenMysqlConnection(connectionString string) (dbObj *sql.DB, err error) {
|
||||
dbConfigObj, err1 := NewDBConfig2(connectionString)
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
dbObj, err = OpenMysqlConnection3(dbConfigObj)
|
||||
return
|
||||
}
|
||||
|
||||
// 打开数据库连接
|
||||
// connectionString:数据库连接字符串
|
||||
// maxOpenConns:最大打开的连接数
|
||||
// maxIdleConns:最大处于闲置状态的连接数
|
||||
// 返回值:
|
||||
// 数据库对象
|
||||
// 错误对象
|
||||
func OpenMysqlConnection2(connectionString string, maxOpenConns, maxIdleConns int) (dbObj *sql.DB, err error) {
|
||||
dbConfigObj := NewDBConfig(connectionString, maxOpenConns, maxIdleConns)
|
||||
dbObj, err = OpenMysqlConnection3(dbConfigObj)
|
||||
return
|
||||
}
|
||||
|
||||
// 建立Mysql数据库连接
|
||||
// dbConfigObj:数据库配置对象
|
||||
// 返回值:
|
||||
// 数据库对象
|
||||
// 错误对象
|
||||
func OpenMysqlConnection3(dbConfigObj *DBConfig) (dbObj *sql.DB, err error) {
|
||||
// 建立数据库连接
|
||||
logUtil.DebugLog("开始连接Mysql数据库")
|
||||
dbObj, err = sql.Open("mysql", dbConfigObj.ConnectionString)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("打开游戏数据库失败,连接字符串为:%s", dbConfigObj.ConnectionString)
|
||||
return
|
||||
}
|
||||
logUtil.DebugLog("连接Mysql数据库成功")
|
||||
|
||||
if dbConfigObj.MaxOpenConns > 0 && dbConfigObj.MaxIdleConns > 0 {
|
||||
dbObj.SetMaxOpenConns(dbConfigObj.MaxOpenConns)
|
||||
dbObj.SetMaxIdleConns(dbConfigObj.MaxIdleConns)
|
||||
}
|
||||
|
||||
if err = dbObj.Ping(); err != nil {
|
||||
err = fmt.Errorf("Ping数据库失败,连接字符串为:%s,错误信息为:%s", dbConfigObj.ConnectionString, err)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 测试数据库连接
|
||||
// dbObj:数据库连对象
|
||||
// 返回值:
|
||||
// 错误对象
|
||||
func TestConnection(dbObj *sql.DB) error {
|
||||
command := "SHOW DATABASES;"
|
||||
rows, err := dbObj.Query(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
// db:数据库对象
|
||||
// 返回值:
|
||||
// 事务对象
|
||||
// 错误对象
|
||||
func BeginTransaction(dbObj *sql.DB) (*sql.Tx, error) {
|
||||
tx, err := dbObj.Begin()
|
||||
if err != nil {
|
||||
logUtil.Log(fmt.Sprintf("开启事务失败,错误信息:%s", err), logUtil.Error, true)
|
||||
}
|
||||
|
||||
return tx, err
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
// tx:事务对象
|
||||
// 返回值:
|
||||
// 错误对象
|
||||
func CommitTransaction(tx *sql.Tx) error {
|
||||
err := tx.Commit()
|
||||
if err != nil {
|
||||
logUtil.Log(fmt.Sprintf("提交事务失败,错误信息:%s", err), logUtil.Error, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录Prepare错误
|
||||
// command:执行的SQL语句
|
||||
// err:错误对象
|
||||
func WritePrepareError(command string, err error) {
|
||||
logUtil.Log(fmt.Sprintf("Prepare失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
|
||||
}
|
||||
|
||||
// 记录Query错误
|
||||
// command:执行的SQL语句
|
||||
// err:错误对象
|
||||
func WriteQueryError(command string, err error) {
|
||||
logUtil.Log(fmt.Sprintf("Query失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
|
||||
}
|
||||
|
||||
// 记录Exec错误
|
||||
// command:执行的SQL语句
|
||||
// err:错误对象
|
||||
func WriteExecError(command string, err error) {
|
||||
logUtil.Log(fmt.Sprintf("Exec失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
|
||||
}
|
||||
|
||||
// 记录Scan错误
|
||||
// command:执行的SQL语句
|
||||
// err:错误对象
|
||||
func WriteScanError(command string, err error) {
|
||||
logUtil.Log(fmt.Sprintf("Scan失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
coroutine-timer支持如下工作:
|
||||
定时触发设定的回调,最小精度秒级
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 增加回调
|
||||
|
||||
> 1. 导入包
|
||||
> 2. 调用AddTimerx添加定时回调,传入相关参数
|
||||
|
||||
ps:
|
||||
> 1. AddTimer1,AddTimer2,AddTimer3是内部自动生成的id,内部保证唯一性。外部如果后续要删除该添加的timer,需要持有返回的id信息
|
||||
> 2. AddTimer4 需要外部传入id,外部需要保证id的唯一性。并且这个方法会在内部校验id是否已经存在,所以性能上会比其他AddTimer方法慢
|
||||
|
||||
### 删除回调
|
||||
|
||||
```go
|
||||
DeleteTimer(id)
|
||||
```
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"goutil/mathUtil"
|
||||
"goutil/stringUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
func init() {
|
||||
wg.Add(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
playerMgr := newPlayerMgr()
|
||||
|
||||
// insert
|
||||
go func() {
|
||||
for {
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s", id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
|
||||
insert(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// update
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
suffix := mathUtil.GetRand().GetRandInt(1000)
|
||||
newName := fmt.Sprintf("Hero_%d", suffix)
|
||||
obj.resetName(newName)
|
||||
|
||||
update(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// delete
|
||||
go func() {
|
||||
for {
|
||||
obj := playerMgr.randomSelect()
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
playerMgr.delete(obj)
|
||||
|
||||
clear(obj)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// errorFile
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(1 * time.Hour)
|
||||
id := stringUtil.GetNewGUID()
|
||||
name := fmt.Sprintf("Hero_%s%s", id, id)
|
||||
obj := newPlayer(id, name)
|
||||
playerMgr.insert(obj)
|
||||
print("errorFile")
|
||||
|
||||
insert(obj)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
package dfaUtil
|
||||
|
||||
import "strings"
|
||||
|
||||
/*
|
||||
DFA util, is used to verify whether a sentence has invalid words.
|
||||
The underlying data structure is trie.
|
||||
https://en.wikipedia.org/wiki/Trie
|
||||
*/
|
||||
|
||||
// dfa util
|
||||
type DFAUtil struct {
|
||||
// The root node
|
||||
root *trieNode
|
||||
}
|
||||
|
||||
// 搜索语句
|
||||
// 由于go不支持tuple,所以为了避免定义多余的struct,特别使用两个list来分别返回匹配的索引的上界和下界
|
||||
// 在处理此方法的返回值时,需要两者配合使用
|
||||
// 参数:
|
||||
//
|
||||
// sentence:语句字符串
|
||||
//
|
||||
// 返回:
|
||||
//
|
||||
// 搜索到的开始位置列表
|
||||
// 搜索到的结束位置列表
|
||||
func (this *DFAUtil) SearchSentence(sentence string) (startIndexList, endIndexList []int) {
|
||||
sentenceRuneList := []rune(sentence)
|
||||
for i := 0; i < len(sentenceRuneList); {
|
||||
//按序匹配每个字
|
||||
end := this.searchSentenceByStart(i, sentenceRuneList)
|
||||
if end < 0 {
|
||||
//匹配失败,继续匹配下一个字
|
||||
i++
|
||||
} else {
|
||||
//匹配成功,记录索引位置
|
||||
startIndexList = append(startIndexList, i)
|
||||
endIndexList = append(endIndexList, end)
|
||||
|
||||
//从匹配到的字后面开始找
|
||||
i = end + 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 从指定的开始位置搜索语句
|
||||
// 参数:
|
||||
//
|
||||
// start:开始匹配的位置
|
||||
// sentenceRuneList:语句字列表
|
||||
//
|
||||
// 返回:
|
||||
//
|
||||
// 匹配到的结束位置,未匹配到返回-1
|
||||
func (this *DFAUtil) searchSentenceByStart(start int, sentenceRuneList []rune) (endIndex int) {
|
||||
//当前节点,从根节点开始找
|
||||
currNode := this.root
|
||||
//是否匹配到
|
||||
var isMatched bool
|
||||
|
||||
//按顺序匹配字
|
||||
for i := start; i < len(sentenceRuneList); {
|
||||
child, exists := currNode.children[sentenceRuneList[i]]
|
||||
|
||||
//未匹配到则结束,跳出循环(可能匹配到过词结尾)
|
||||
if !exists {
|
||||
break
|
||||
}
|
||||
|
||||
//是否是词末尾,如果是则先记录下来,因为还可能匹配到更长的词
|
||||
//比如["金鳞"、"金鳞岂是池中物"] => 匹配"金鳞岂是池中物",匹配到"金鳞"不应该停下来,应继续匹配更长的词
|
||||
if child.isEndOfWord {
|
||||
endIndex = i
|
||||
isMatched = true
|
||||
}
|
||||
|
||||
//是否已经到词末尾
|
||||
if len(child.children) == 0 {
|
||||
return endIndex
|
||||
} else {
|
||||
//继续与后面的字匹配
|
||||
currNode = child
|
||||
}
|
||||
|
||||
//增加索引匹配下一个位置
|
||||
i++
|
||||
}
|
||||
|
||||
//匹配结束,若曾经匹配到词末尾,则直接返回匹配到的位置
|
||||
if isMatched {
|
||||
return endIndex
|
||||
} else {
|
||||
//没有匹配到词末尾,则返回匹配失败
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new word into object
|
||||
func (this *DFAUtil) InsertWord(word []rune) {
|
||||
currNode := this.root
|
||||
for _, c := range word {
|
||||
if cildNode, exist := currNode.children[c]; !exist {
|
||||
cildNode = newtrieNode()
|
||||
currNode.children[c] = cildNode
|
||||
currNode = cildNode
|
||||
} else {
|
||||
currNode = cildNode
|
||||
}
|
||||
}
|
||||
|
||||
currNode.isEndOfWord = true
|
||||
}
|
||||
|
||||
// Check if there is any word in the trie that starts with the given prefix.
|
||||
func (this *DFAUtil) StartsWith(prefix []rune) bool {
|
||||
currNode := this.root
|
||||
for _, c := range prefix {
|
||||
if childNode, exist := currNode.children[c]; !exist {
|
||||
return false
|
||||
} else {
|
||||
currNode = childNode
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Judge if input sentence contains some special caracter
|
||||
// Return:
|
||||
// Matc or not
|
||||
func (this *DFAUtil) IsMatch(sentence string) bool {
|
||||
startIndexList, _ := this.SearchSentence(sentence)
|
||||
return len(startIndexList) > 0
|
||||
}
|
||||
|
||||
// Handle sentence. Use specified caracter to replace those sensitive caracters.
|
||||
// input: Input sentence
|
||||
// replaceCh: candidate
|
||||
// Return:
|
||||
// Sentence after manipulation
|
||||
func (this *DFAUtil) HandleWord(sentence string, replaceCh rune) string {
|
||||
startIndexList, endIndexList := this.SearchSentence(sentence)
|
||||
if len(startIndexList) == 0 {
|
||||
return sentence
|
||||
}
|
||||
|
||||
// Manipulate
|
||||
sentenceList := []rune(sentence)
|
||||
for i := 0; i < len(startIndexList); i++ {
|
||||
for index := startIndexList[i]; index <= endIndexList[i]; index++ {
|
||||
sentenceList[index] = replaceCh
|
||||
}
|
||||
}
|
||||
|
||||
return string(sentenceList)
|
||||
}
|
||||
|
||||
// Handle sentence. Use specified caracter to replace those sensitive caracters.
|
||||
// input: Input sentence
|
||||
// replaceCh: candidate
|
||||
// Return:
|
||||
// Sentence after manipulation
|
||||
func (this *DFAUtil) HandleWordUseStr(input string, replaceCh string) string {
|
||||
input2 := strings.ToUpper(input)
|
||||
|
||||
startIndexList, endIndexList := this.SearchSentence(input2)
|
||||
if len(startIndexList) == 0 {
|
||||
return input
|
||||
}
|
||||
|
||||
// Manipulate
|
||||
inputRune := []rune(input)
|
||||
replaceChList := []rune(replaceCh)
|
||||
|
||||
//上一次替换掉的数量
|
||||
lastReplaceCount := 0
|
||||
|
||||
for i := 0; i < len(startIndexList); i++ {
|
||||
|
||||
//替换字的索引
|
||||
index := len(replaceChList)
|
||||
|
||||
//开始位置--加上替换的词的索引
|
||||
starIndex := startIndexList[i] + (i * index) - lastReplaceCount
|
||||
|
||||
//结束位置
|
||||
endIndex := endIndexList[i] + (i * index) - lastReplaceCount
|
||||
|
||||
//结束字符串
|
||||
sentenceAttr := string(inputRune[endIndex+1:])
|
||||
|
||||
//替换范围字符串
|
||||
inputRune = append(inputRune[:starIndex], replaceChList...)
|
||||
inputRune = append(inputRune, []rune(sentenceAttr)...)
|
||||
lastReplaceCount = endIndex + 1 - starIndex
|
||||
}
|
||||
|
||||
return string(inputRune)
|
||||
}
|
||||
|
||||
// Create new DfaUtil object
|
||||
// wordList:word list
|
||||
func NewDFAUtil(wordList []string) *DFAUtil {
|
||||
this := &DFAUtil{
|
||||
root: newtrieNode(),
|
||||
}
|
||||
|
||||
for _, word := range wordList {
|
||||
wordRuneList := []rune(word)
|
||||
if len(wordRuneList) > 0 {
|
||||
this.InsertWord(wordRuneList)
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
# 配置根节点
|
||||
root:
|
||||
# 是否是调试模式
|
||||
debug: true
|
||||
|
||||
# Web服务监听地址和端口
|
||||
web_server_address: "192.168.50.85:10052"
|
||||
|
||||
# Elasticsearch 地址
|
||||
es_urls: "http://10.252.0.70:18099"
|
||||
|
||||
# 数据库配置
|
||||
db_config:
|
||||
admin_db:
|
||||
# 最大处于开启状态的连接数
|
||||
max_open_conns: 0
|
||||
|
||||
# 最大处于空闲状态的连接数
|
||||
max_idle_conns: 0
|
||||
|
||||
# 数据库连接字符串
|
||||
connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/admin?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true"
|
||||
|
||||
user_db:
|
||||
# 最大处于开启状态的连接数
|
||||
max_open_conns: 0
|
||||
|
||||
# 最大处于空闲状态的连接数
|
||||
max_idle_conns: 0
|
||||
|
||||
# 数据库连接字符串
|
||||
connection_string: "root:Qq5201530300@tcp(192.168.50.110:3306)/user?charset=utf8&parseTime=true&loc=Local&timeout=30s&multiStatements=true"
|
||||
|
||||
redis_config:
|
||||
# 数据库连接字符串
|
||||
connection_string: "192.168.50.110:6379"
|
||||
|
||||
# 密码, 如果要设置用户Id,则密码设置为:"UserId:Password"
|
||||
password: ""
|
||||
|
||||
# 数据库序号
|
||||
database: 5
|
||||
|
||||
# 最大活跃连接数
|
||||
max_active: 500
|
||||
|
||||
# 最大空闲的连接数
|
||||
max_idle: 200
|
||||
|
||||
# 连接空闲超时时间,单位:秒
|
||||
idle_timeout: 300
|
||||
|
||||
# 连接超时时间, 单位:秒
|
||||
dial_connect_timeout: 10
|
||||
@ -0,0 +1,123 @@
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Redis配置对象
|
||||
type RedisConfig struct {
|
||||
// 连接字符串
|
||||
ConnectionString string
|
||||
|
||||
// 密码
|
||||
Password string
|
||||
|
||||
// 数据库编号
|
||||
Database int
|
||||
|
||||
// 最大活跃连接数
|
||||
MaxActive int
|
||||
|
||||
// 最大空闲连接数
|
||||
MaxIdle int
|
||||
|
||||
// 空闲超时
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// 连接超时
|
||||
DialConnectTimeout time.Duration
|
||||
}
|
||||
|
||||
// 将redis连接字符串转化为redis config对象
|
||||
// 格式:ConnectionString=10.1.0.21:6379;Password=redis_pwd;Database=3;MaxActive=50;MaxIdle=20;IdleTimeout=300;DialConnectTimeout=10;
|
||||
// redisConfigStr:redis连接字符串
|
||||
// 返回值:
|
||||
// redis config对象
|
||||
// 错误对象
|
||||
func NewRedisConfig(redisConfigStr string) (redisConfig *RedisConfig, err error) {
|
||||
var connectionString string
|
||||
var password string
|
||||
var database int
|
||||
var maxActive int
|
||||
var maxIdle int
|
||||
var idleTimeout time.Duration
|
||||
var dialConectTimeout time.Duration
|
||||
var count int = 7
|
||||
var subCount int = 2
|
||||
|
||||
itemList := strings.Split(redisConfigStr, ";")
|
||||
// 去掉最后的空数据
|
||||
if itemList[len(itemList)-1] == "" {
|
||||
itemList = itemList[0 : len(itemList)-1]
|
||||
}
|
||||
if len(itemList) != count {
|
||||
err = fmt.Errorf("%s格式不正确,需要包含%d个部分,现在有%d个部分", redisConfigStr, count, len(itemList))
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range itemList {
|
||||
subItemList := strings.Split(item, "=")
|
||||
if len(subItemList) != subCount {
|
||||
err = fmt.Errorf("%s格式不正确,需要包含%d个部分", item, subCount)
|
||||
return
|
||||
}
|
||||
|
||||
// 分别进行判断
|
||||
switch strings.ToLower(subItemList[0]) {
|
||||
case strings.ToLower("ConnectionString"):
|
||||
connectionString = subItemList[1]
|
||||
case strings.ToLower("Password"):
|
||||
password = subItemList[1]
|
||||
case strings.ToLower("Database"):
|
||||
if database, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("MaxActive"):
|
||||
if maxActive, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("MaxIdle"):
|
||||
if maxIdle, err = strconv.Atoi(subItemList[1]); err != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
}
|
||||
case strings.ToLower("IdleTimeout"):
|
||||
if idleTimeout_int, err1 := strconv.Atoi(subItemList[1]); err1 != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
} else {
|
||||
idleTimeout = time.Duration(idleTimeout_int) * time.Second
|
||||
}
|
||||
case strings.ToLower("DialConnectTimeout"):
|
||||
if dialConectTimeout_int, err1 := strconv.Atoi(subItemList[1]); err1 != nil {
|
||||
err = fmt.Errorf("%s转化为int型失败", subItemList[1])
|
||||
return
|
||||
} else {
|
||||
dialConectTimeout = time.Duration(dialConectTimeout_int) * time.Second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redisConfig = NewRedisConfig2(connectionString, password, database, maxActive, maxIdle, idleTimeout, dialConectTimeout)
|
||||
return
|
||||
}
|
||||
|
||||
func NewRedisConfig2(connectionString, password string,
|
||||
database, maxActive, maxIdle int,
|
||||
idleTimeout, dialConnectTimeout time.Duration) *RedisConfig {
|
||||
|
||||
return &RedisConfig{
|
||||
ConnectionString: connectionString,
|
||||
Password: password,
|
||||
Database: database,
|
||||
MaxActive: maxActive,
|
||||
MaxIdle: maxIdle,
|
||||
IdleTimeout: idleTimeout,
|
||||
DialConnectTimeout: dialConnectTimeout,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package mqMgr
|
||||
|
||||
// 消息队列配置对象
|
||||
type QueueConfig struct {
|
||||
// 地域
|
||||
Region string
|
||||
|
||||
// 队列名称
|
||||
QueueName string
|
||||
|
||||
// API密钥Id
|
||||
SecretId string
|
||||
|
||||
// API密钥key
|
||||
SecretKey string
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package goroutineMgr
|
||||
|
||||
/*
|
||||
goroutine的管理包,提供了对goroutine的监控机制
|
||||
对外提供的方法为:
|
||||
|
||||
// 监控指定的goroutine
|
||||
Monitor(goroutineName string)
|
||||
|
||||
// 只添加数量,不监控
|
||||
MonitorZero(goroutineName string)
|
||||
|
||||
// 释放监控
|
||||
ReleaseMonitor(goroutineName string)
|
||||
|
||||
*/
|
||||
@ -0,0 +1,133 @@
|
||||
package handleMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ---------------申请示例实例--------------
|
||||
var (
|
||||
data = make(map[int]int, 0)
|
||||
)
|
||||
|
||||
type testBll struct {
|
||||
}
|
||||
|
||||
// TestAdd
|
||||
// @description:测试添加
|
||||
// parameter:
|
||||
// @receiver t:
|
||||
// @x:
|
||||
// @y:
|
||||
// return:
|
||||
// @*ResponseObject:
|
||||
func (t testBll) TestAdd(x, y int) *ResponseObject {
|
||||
responseObj := GetInitResponseObj()
|
||||
data[x] = y
|
||||
z := x + y
|
||||
|
||||
print(x)
|
||||
|
||||
responseObj.SetData(z)
|
||||
responseObj.SetResultStatus(-11111, "错误码")
|
||||
return responseObj
|
||||
}
|
||||
|
||||
// See1
|
||||
// @description:测试添加
|
||||
// parameter:
|
||||
// @receiver t:
|
||||
// @x:
|
||||
// return:
|
||||
// @*ResponseObject:
|
||||
func (t testBll) See1(x int) *ResponseObject {
|
||||
responseObj := GetInitResponseObj()
|
||||
fmt.Print(x)
|
||||
return responseObj
|
||||
}
|
||||
|
||||
// See2
|
||||
// @description:测试添加
|
||||
// parameter:
|
||||
// @receiver t:
|
||||
// @x:
|
||||
// return:
|
||||
// @*ResponseObject:
|
||||
func (t testBll) See2(x int) *ResponseObject {
|
||||
responseObj := GetInitResponseObj()
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
fmt.Print(x)
|
||||
return responseObj
|
||||
}
|
||||
|
||||
func (t testBll) ReMove(x int) *ResponseObject {
|
||||
responseObj := GetInitResponseObj()
|
||||
delete(data, x)
|
||||
return responseObj
|
||||
}
|
||||
|
||||
// ---------------测试方法--------------
|
||||
func TestNew(t *testing.T) {
|
||||
|
||||
var paramItem interface{}
|
||||
paramItem = 123
|
||||
paramFloat64, ok := paramItem.(int)
|
||||
if ok {
|
||||
x := reflect.ValueOf(int(paramFloat64))
|
||||
t.Log(x)
|
||||
}
|
||||
t.Log(paramFloat64)
|
||||
t.Log(ok)
|
||||
|
||||
//t.Log("A")
|
||||
//RegisterNewModule("test", new(testBll), 10)
|
||||
|
||||
//parameter := []interface{}{"1", 2}
|
||||
//x, y, mes := Done("test", "testBll", "TestAdd", parameter, false)
|
||||
//for key, item := range data {
|
||||
// t.Log(key, item)
|
||||
//}
|
||||
|
||||
//t.Log("B2")
|
||||
//parameter1 := []interface{}{2, 2}
|
||||
//x, y, mes = Done("test", "testBll", "TestAdd", parameter1, true)
|
||||
//for key, item := range data {
|
||||
// t.Log(key, item)
|
||||
//}
|
||||
//t.Log(x)
|
||||
//t.Log(y)
|
||||
//t.Log(mes)
|
||||
//t.Log("B3")
|
||||
//
|
||||
//t.Log("C")
|
||||
//parameter2 := []interface{}{2}
|
||||
//Done("test", "testBll", "ReMove", parameter2, true)
|
||||
//for key, item := range data {
|
||||
// t.Log(key, item)
|
||||
//}
|
||||
//t.Log("D")
|
||||
//
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//parameter3 := []interface{}{1}
|
||||
//Done("test", "testBll", "See2", parameter3, false)
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//parameter4 := []interface{}{2}
|
||||
//Done("test", "testBll", "See1", parameter4, true)
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//parameter5 := []interface{}{1}
|
||||
//Done("test", "testBll", "See1", parameter5, true)
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//
|
||||
//t.Log("E")
|
||||
//
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//for i := 0; i < 1000; i++ {
|
||||
// Done("test", "testBll", "See1", parameter5, true)
|
||||
//}
|
||||
//t.Log(time.Now().UnixMilli())
|
||||
//t.Log("F")
|
||||
|
||||
t.Error("G")
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package qcloud
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"goutil/mathUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
const (
|
||||
VOICE_CAPTCHA_URL = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendcvoice"
|
||||
VOICE_NOTIFICATION_URL = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendvoiceprompt"
|
||||
VOICE_TEMPLATE_NOTIFICATION_URL = "https://cloud.tim.qq.com/v5/tlsvoicesvr/sendtvoice"
|
||||
)
|
||||
|
||||
// calculate sign-string for phone numbers
|
||||
func calcSig(appKey string, rand int, timeStamp int64, mobile string) string {
|
||||
sum := sha256.Sum256([]byte(fmt.Sprintf("appkey=%s&random=%d&time=%d&mobile=%s", appKey, rand, timeStamp, mobile)))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// do the http request and parse the response
|
||||
func request(url string, data map[string]interface{}, appId string, rand int) (success bool, err error) {
|
||||
url = fmt.Sprintf("%s?sdkappid=%s&random=%d", url, appId, rand)
|
||||
fmt.Printf("url:%s\n", url)
|
||||
|
||||
contentBytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("data:%v\n", string(contentBytes))
|
||||
|
||||
_, retBytes, err := webUtil.PostByteData2(url, contentBytes, nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var responseObj *response
|
||||
err = json.Unmarshal(retBytes, &responseObj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if responseObj.Result != 0 {
|
||||
err = fmt.Errorf(responseObj.ErrMsg)
|
||||
return
|
||||
}
|
||||
|
||||
success = true
|
||||
return
|
||||
}
|
||||
|
||||
func SendVoiceCaptcha(appId, appKey, nation, mobile, captcha string, playTimes int) (success bool, err error) {
|
||||
rand := mathUtil.GetRand().GetRandRangeInt(100000, 999999)
|
||||
timeStamp := time.Now().Unix()
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["playtimes"] = playTimes
|
||||
data["sig"] = calcSig(appKey, rand, timeStamp, mobile)
|
||||
data["tel"] = newTelField(nation, mobile)
|
||||
data["time"] = timeStamp
|
||||
data["ext"] = ""
|
||||
// dedicated param
|
||||
data["msg"] = captcha
|
||||
|
||||
success, err = request(VOICE_CAPTCHA_URL, data, appId, rand)
|
||||
return
|
||||
}
|
||||
|
||||
func SendVoiceNotification(appId, appKey, nation, mobile, prompt string, playTimes int) (success bool, err error) {
|
||||
rand := mathUtil.GetRand().GetRandRangeInt(100000, 999999)
|
||||
timeStamp := time.Now().Unix()
|
||||
promptType := 2
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["playtimes"] = playTimes
|
||||
data["sig"] = calcSig(appKey, rand, timeStamp, mobile)
|
||||
data["tel"] = newTelField(nation, mobile)
|
||||
data["time"] = timeStamp
|
||||
data["ext"] = ""
|
||||
// dedicated param
|
||||
data["promptfile"] = prompt
|
||||
data["prompttype"] = promptType
|
||||
|
||||
success, err = request(VOICE_NOTIFICATION_URL, data, appId, rand)
|
||||
return
|
||||
}
|
||||
|
||||
func SendVoiceTemplateNotification(appId, appKey, nation, mobile string, templateId int, params []string, playTimes int) (success bool, err error) {
|
||||
rand := mathUtil.GetRand().GetRandRangeInt(100000, 999999)
|
||||
timeStamp := time.Now().Unix()
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["playtimes"] = playTimes
|
||||
data["sig"] = calcSig(appKey, rand, timeStamp, mobile)
|
||||
data["tel"] = newTelField(nation, mobile)
|
||||
data["time"] = timeStamp
|
||||
data["ext"] = ""
|
||||
// dedicated param
|
||||
data["tpl_id"] = templateId
|
||||
data["params"] = params
|
||||
|
||||
success, err = request(VOICE_TEMPLATE_NOTIFICATION_URL, data, appId, rand)
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package gameServerMgr
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
. "Framework/managecenterModel"
|
||||
"goutil/timeUtil"
|
||||
"goutil/typeUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
mServerGroupObj *ServerGroup
|
||||
)
|
||||
|
||||
//解析服务器组信息
|
||||
func ParseServerGroupInfo(serverGroupObj *ServerGroup) {
|
||||
mServerGroupObj = serverGroupObj
|
||||
}
|
||||
|
||||
//获取服务器组对象
|
||||
func GetServerGroup() (serverGroupObj *ServerGroup) {
|
||||
serverGroupObj = mServerGroupObj
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//检查服务器是否在维护
|
||||
func CheckMaintainStatus() (maintainMessage string, isMaintaining bool) {
|
||||
serverGroupObj := GetServerGroup()
|
||||
nowTick := time.Now().Unix()
|
||||
if serverGroupObj.GroupState == int32(Con_GroupState_Maintain) || (serverGroupObj.MaintainBeginTimeTick <= nowTick && nowTick <= serverGroupObj.MaintainBeginTimeTick+int64(60*serverGroupObj.MaintainMinutes)) {
|
||||
maintainMessage = serverGroupObj.MaintainMessage
|
||||
isMaintaining = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//获取服务器维护开始时间时间戳
|
||||
func GetMaintainBeginTime() (maintainBeginTimeTick int64) {
|
||||
serverGroupObj := GetServerGroup()
|
||||
maintainBeginTimeTick = serverGroupObj.MaintainBeginTimeTick
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//获取服务器维护持续时间 单位分钟
|
||||
func GetMaintainMinutes() (maintainMinutes int32) {
|
||||
serverGroupObj := GetServerGroup()
|
||||
maintainMinutes = serverGroupObj.MaintainMinutes
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//获取服务器开服日期 时间戳
|
||||
func GetServerOpenDate() (openTimeTick int64) {
|
||||
serverGroupObj := GetServerGroup()
|
||||
openTimeTick = serverGroupObj.OpenTimeTick
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//当前服务器已开服天数(开服第几天)
|
||||
//当前开服天数 计算公式:(当前日期 - 开服日期)的总天数 + 1
|
||||
func ServerOpenDays() (days int32) {
|
||||
serverGroupObj := GetServerGroup()
|
||||
if serverGroupObj.IsOpen() == false {
|
||||
return 0
|
||||
}
|
||||
|
||||
//(当前日期 - 开服日期)的总天数 + 1
|
||||
openTimeTick := serverGroupObj.OpenTimeTick
|
||||
openDate, _ := typeUtil.DateTime(openTimeTick)
|
||||
days = int32(timeUtil.SubDay(time.Now(), openDate) + 1)
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,225 @@
|
||||
package syncUtil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMutex(t *testing.T) {
|
||||
mu := NewMutex()
|
||||
mu.Lock()
|
||||
defer mu.UnLock()
|
||||
if mu.TryLock() {
|
||||
t.Errorf("cannot fetch mutex !!!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutexTryLockTimeout(t *testing.T) {
|
||||
fmt.Println("start")
|
||||
mu := NewMutex()
|
||||
mu.Lock()
|
||||
go func() {
|
||||
time.Sleep(20 * time.Second)
|
||||
mu.UnLock()
|
||||
}()
|
||||
// if !mu.TryLockTimeout(500 * time.Microsecond) {
|
||||
// t.Errorf("cannot fetch mutex in 500us !!!")
|
||||
// }
|
||||
if !mu.TryLockTimeout(15 * time.Second) {
|
||||
t.Errorf("should fetch mutex in 5ms !!!")
|
||||
}
|
||||
mu.UnLock()
|
||||
}
|
||||
|
||||
func TestMutexUnlockTwice(t *testing.T) {
|
||||
mu := NewMutex()
|
||||
mu.Lock()
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if x != "unlock of unlocked mutex" {
|
||||
t.Errorf("unexpect panic")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("should panic after unlock twice")
|
||||
}
|
||||
}()
|
||||
mu.UnLock()
|
||||
mu.UnLock()
|
||||
}
|
||||
|
||||
func TestMutexTryLockContext(t *testing.T) {
|
||||
mu := NewMutex()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
mu.Lock()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cancel()
|
||||
}()
|
||||
if mu.TryLockContext(ctx) {
|
||||
t.Errorf("cannot fetch mutex !!!")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMutex(b *testing.B) {
|
||||
mu := NewMutex()
|
||||
a := 0
|
||||
c := 0
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
mu.Lock()
|
||||
a++
|
||||
mu.UnLock()
|
||||
mu.Lock()
|
||||
c = a
|
||||
mu.UnLock()
|
||||
}
|
||||
})
|
||||
_ = a
|
||||
_ = c
|
||||
}
|
||||
|
||||
func TestMutexGroup(t *testing.T) {
|
||||
mu := NewMutexGroup()
|
||||
mu.Lock("g")
|
||||
defer mu.UnLock("g")
|
||||
if mu.TryLock("g") {
|
||||
t.Errorf("cannot fetch mutex !!!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutexGroupMutliWaitLock(t *testing.T) {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu = NewMutexGroup()
|
||||
cn = 3
|
||||
)
|
||||
|
||||
for i := 0; i < cn; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
mu.Lock("h")
|
||||
time.Sleep(1e7)
|
||||
mu.UnLock("h")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for i := 0; i < cn; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
mu.Lock("g")
|
||||
time.Sleep(1e7)
|
||||
mu.UnLockAndFree("g")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestMutexGroupUnLockAndFree(t *testing.T) {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu = NewMutexGroup()
|
||||
mg = mu.(*mutexGroup)
|
||||
)
|
||||
|
||||
for j := 1; j < 5; j++ {
|
||||
for i := 0; i < j; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
mu.Lock("h")
|
||||
time.Sleep(1e6)
|
||||
mu.UnLockAndFree("h")
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
mg.mu.Lock()
|
||||
if _, ok := mg.group["h"]; ok {
|
||||
t.Error("h mutex exist after UnLockAndFree")
|
||||
}
|
||||
mg.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutexGroupTryLockFailedAndUnLockAndFree(t *testing.T) {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
mu = NewMutexGroup()
|
||||
mg = mu.(*mutexGroup)
|
||||
)
|
||||
|
||||
for j := 1; j < 5; j++ {
|
||||
for i := 0; i < j; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if mu.TryLock("h") {
|
||||
time.Sleep(1e6)
|
||||
mu.UnLockAndFree("h")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
mg.mu.Lock()
|
||||
if _, ok := mg.group["h"]; ok {
|
||||
t.Error("h mutex exist after UnLockAndFree")
|
||||
}
|
||||
mg.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutexGroupTryLockTimeout(t *testing.T) {
|
||||
mu := NewMutexGroup()
|
||||
mu.Lock("g")
|
||||
go func() {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
mu.UnLock("g")
|
||||
}()
|
||||
if mu.TryLockTimeout("g", 500*time.Microsecond) {
|
||||
t.Errorf("cannot fetch mutex in 500us !!!")
|
||||
}
|
||||
if !mu.TryLockTimeout("g", 5*time.Millisecond) {
|
||||
t.Errorf("should fetch mutex in 5ms !!!")
|
||||
}
|
||||
mu.UnLock("g")
|
||||
}
|
||||
|
||||
func TestMutexGroupTryLockContext(t *testing.T) {
|
||||
mu := NewMutexGroup()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
mu.Lock("g")
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cancel()
|
||||
}()
|
||||
if mu.TryLockContext("g", ctx) {
|
||||
t.Errorf("cannot fetch mutex !!!")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMutexGroup(b *testing.B) {
|
||||
mu := NewMutexGroup()
|
||||
a := 0
|
||||
c := 0
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
mu.Lock("g")
|
||||
a++
|
||||
mu.UnLock("g")
|
||||
mu.Lock("g")
|
||||
c = a
|
||||
mu.UnLock("g")
|
||||
}
|
||||
})
|
||||
_ = a
|
||||
_ = c
|
||||
}
|
||||
1575
.svn/pristine/25/253842466dd2f7a93da426d59d796617815b5714.svn-base
Normal file
1575
.svn/pristine/25/253842466dd2f7a93da426d59d796617815b5714.svn-base
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,378 @@
|
||||
/*
|
||||
未实现的列表方法:
|
||||
BLPOP、BRPOP、BRPOPLPUSH
|
||||
*/
|
||||
package redisUtil
|
||||
|
||||
import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
)
|
||||
|
||||
/*
|
||||
LPUSH key value [value …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将一个或多个值 value 插入到列表 key 的表头
|
||||
|
||||
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。
|
||||
|
||||
如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
|
||||
|
||||
当 key 存在但不是列表类型时,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。
|
||||
|
||||
返回值
|
||||
执行 LPUSH 命令后,列表的长度。
|
||||
*/
|
||||
func (this *RedisPool) LPush(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LPUSH", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LPUSHX key value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(1)
|
||||
将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。
|
||||
|
||||
和 LPUSH key value [value …] 命令相反,当 key 不存在时, LPUSHX 命令什么也不做。
|
||||
|
||||
返回值
|
||||
LPUSHX 命令执行之后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) LPushX(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LPUSHX", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPUSH key value [value …]
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
将一个或多个值 value 插入到列表 key 的表尾(最右边)。
|
||||
|
||||
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。
|
||||
|
||||
如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。
|
||||
|
||||
当 key 存在但不是列表类型时,返回一个错误。
|
||||
|
||||
Note
|
||||
|
||||
在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。
|
||||
|
||||
返回值
|
||||
执行 RPUSH 操作后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) RPush(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("RPUSH", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPUSHX key value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(1)
|
||||
将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
|
||||
|
||||
和 RPUSH key value [value …] 命令相反,当 key 不存在时, RPUSHX 命令什么也不做。
|
||||
|
||||
返回值
|
||||
RPUSHX 命令执行之后,表的长度。
|
||||
*/
|
||||
func (this *RedisPool) RPushX(key string, values ...interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("RPUSHX", redis.Args{}.Add(key).AddFlat(values)...))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LPOP key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
移除并返回列表 key 的头元素。
|
||||
|
||||
返回值
|
||||
列表的头元素。 当 key 不存在时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) LPop(key string) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("LPOP", key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPOP key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
移除并返回列表 key 的尾元素。
|
||||
|
||||
返回值
|
||||
列表的尾元素。 当 key 不存在时,返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) RPop(key string) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("RPOP", key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RPOPLPUSH source destination
|
||||
可用版本: >= 1.2.0
|
||||
时间复杂度: O(1)
|
||||
命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
|
||||
|
||||
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
|
||||
|
||||
将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
|
||||
|
||||
举个例子,你有两个列表 source 和 destination , source 列表有元素 a, b, c , destination 列表有元素 x, y, z ,执行 RPOPLPUSH source destination 之后, source 列表包含元素 a, b , destination 列表包含元素 c, x, y, z ,并且元素 c 会被返回给客户端。
|
||||
|
||||
如果 source 不存在,值 nil 被返回,并且不执行其他动作。
|
||||
|
||||
如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。
|
||||
|
||||
返回值
|
||||
被弹出的元素。
|
||||
*/
|
||||
func (this *RedisPool) RPopLPush(source, destination string) (item interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("RPOPLPUSH", source, destination)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LREM key count value
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为列表的长度。
|
||||
根据参数 count 的值,移除列表中与参数 value 相等的元素。
|
||||
|
||||
count 的值可以是以下几种:
|
||||
|
||||
count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
|
||||
|
||||
count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
|
||||
|
||||
count = 0 : 移除表中所有与 value 相等的值。
|
||||
|
||||
返回值
|
||||
被移除元素的数量。 因为不存在的 key 被视作空表(empty list),所以当 key 不存在时, LREM 命令总是返回 0 。
|
||||
*/
|
||||
// 错误对象
|
||||
func (this *RedisPool) LRem(key string, count int, value string) (removeCount int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
removeCount, err = redis.Int(conn.Do("LREM", key, count, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LLEN key
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(1)
|
||||
返回列表 key 的长度。
|
||||
|
||||
如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
列表 key 的长度。
|
||||
*/
|
||||
func (this *RedisPool) LLen(key string) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LLEN", key))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LINDEX key index
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度:O(N), N 为到达下标 index 过程中经过的元素数量。因此,对列表的头元素和尾元素执行 LINDEX 命令,复杂度为O(1)。
|
||||
返回列表 key 中,下标为 index 的元素。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
列表中下标为 index 的元素。 如果 index 参数的值不在列表的区间范围内(out of range),返回 nil 。
|
||||
*/
|
||||
func (this *RedisPool) LIndex(key string, index int) (item interface{}, exist bool, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
item, err = conn.Do("LINDEX", key, index)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
|
||||
exist = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LINSERT key BEFORE|AFTER pivot value
|
||||
可用版本: >= 2.2.0
|
||||
时间复杂度: O(N), N 为寻找 pivot 过程中经过的元素数量。
|
||||
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
|
||||
|
||||
当 pivot 不存在于列表 key 时,不执行任何操作。
|
||||
|
||||
当 key 不存在时, key 被视为空列表,不执行任何操作。
|
||||
|
||||
如果 key 不是列表类型,返回一个错误。
|
||||
|
||||
返回值
|
||||
如果命令执行成功,返回插入操作完成之后,列表的长度。 如果没有找到 pivot ,返回 -1 。 如果 key 不存在或为空列表,返回 0 。
|
||||
*/
|
||||
func (this *RedisPool) LInsert(key, beforeOrAfter string, pivot, value interface{}) (length int, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
length, err = redis.Int(conn.Do("LINSERT", key, beforeOrAfter, pivot, value))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LSET key index value
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度:对头元素或尾元素进行 LSET 操作,复杂度为 O(1)。其他情况下,为 O(N), N 为列表的长度。
|
||||
将列表 key 下标为 index 的元素的值设置为 value 。
|
||||
|
||||
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
|
||||
|
||||
关于列表下标的更多信息,请参考 LINDEX key index 命令。
|
||||
|
||||
返回值
|
||||
操作成功返回 ok ,否则返回错误信息。
|
||||
*/
|
||||
func (this *RedisPool) LSet(key string, index int, value interface{}) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("LSet", key, index, value)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LRANGE key start stop
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。
|
||||
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
注意LRANGE命令和编程语言区间函数的区别
|
||||
假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在 LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range() 函数。
|
||||
|
||||
超出范围的下标
|
||||
超出范围的下标值不会引起错误。
|
||||
|
||||
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,那么 LRANGE 返回一个空列表。
|
||||
|
||||
如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。
|
||||
|
||||
返回值
|
||||
一个列表,包含指定区间内的元素。
|
||||
*/
|
||||
func (this *RedisPool) LRange(key string, start, stop int) (reply interface{}, err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
reply, err = conn.Do("LRANGE", key, start, stop)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LTRIM key start stop
|
||||
可用版本: >= 1.0.0
|
||||
时间复杂度: O(N), N 为被移除的元素的数量。
|
||||
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
|
||||
|
||||
举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。
|
||||
|
||||
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
|
||||
|
||||
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
|
||||
|
||||
当 key 不是列表类型时,返回一个错误。
|
||||
|
||||
LTRIM 命令通常和 LPUSH key value [value …] 命令或 RPUSH key value [value …] 命令配合使用,举个例子:
|
||||
|
||||
LPUSH log newest_log
|
||||
LTRIM log 0 99
|
||||
这个例子模拟了一个日志程序,每次将最新日志 newest_log 放到 log 列表中,并且只保留最新的 100 项。注意当这样使用 LTRIM 命令时,时间复杂度是O(1),因为平均情况下,每次只有一个元素被移除。
|
||||
|
||||
注意LTRIM命令和编程语言区间函数的区别
|
||||
假如你有一个包含一百个元素的列表 list ,对该列表执行 LTRIM list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在 LTRIM 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range() 函数。
|
||||
|
||||
超出范围的下标
|
||||
超出范围的下标值不会引起错误。
|
||||
|
||||
如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)。
|
||||
|
||||
如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。
|
||||
|
||||
返回值
|
||||
命令执行成功时,返回 ok 。
|
||||
*/
|
||||
func (this *RedisPool) LTrim(key string, start, stop int) (err error) {
|
||||
conn := this.GetConnection()
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Do("LTRIM", key, start, stop)
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
package gameServerMgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "Framework/managecenterModel"
|
||||
"goutil/logUtil"
|
||||
"goutil/securityUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
// 登陆助手类
|
||||
type LoginUtil struct{}
|
||||
|
||||
// 验证登陆信息
|
||||
// partnerId:合作商Id
|
||||
// userId:合作商用户Id
|
||||
// loginInfo:登陆信息
|
||||
// isIntranet:是否是内网:true,内网;false:外网
|
||||
// 返回值:
|
||||
// 成功与否
|
||||
// 错误对象
|
||||
func (this *LoginUtil) CheckLoginInfo(partnerId int32, userId, loginInfo string, isIntranet bool) (success bool, err error) {
|
||||
// 验证用户合法性
|
||||
loginItemList := strings.Split(loginInfo, "_")
|
||||
if len(loginItemList) != 2 {
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s", partnerId, userId, loginInfo)
|
||||
return
|
||||
}
|
||||
|
||||
//将requestUrl地址进行拆分
|
||||
requestDomainList := strings.Split(loginItemList[1], ";")
|
||||
|
||||
//请求的主域名
|
||||
requestDomain := ""
|
||||
if isIntranet || len(requestDomainList) == 1 {
|
||||
requestDomain = requestDomainList[0]
|
||||
} else {
|
||||
requestDomain = requestDomainList[1]
|
||||
}
|
||||
|
||||
//构造请求url
|
||||
requestUrl := fmt.Sprintf("http://%s/API/CheckDynamicLoginKey.ashx", requestDomain)
|
||||
|
||||
// 定义请求参数
|
||||
postDict := make(map[string]string)
|
||||
postDict["UserId"] = userId
|
||||
postDict["LoginKey"] = loginItemList[0]
|
||||
|
||||
//请求url,请求头
|
||||
header := webUtil.GetFormHeader()
|
||||
transport := webUtil.NewTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport = webUtil.GetTimeoutTransport(transport, 30)
|
||||
|
||||
statusCode, returnBytes, err1 := webUtil.PostMapData(requestUrl, postDict, header, transport)
|
||||
if err1 != nil {
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, err:%s", partnerId, userId, loginInfo, err1)
|
||||
return
|
||||
}
|
||||
if statusCode != 200 {
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, statusCode:%d", partnerId, userId, loginInfo, statusCode)
|
||||
return
|
||||
}
|
||||
|
||||
// 解析返回值
|
||||
returnObj := new(ReturnObject)
|
||||
if err = json.Unmarshal(returnBytes, &returnObj); err != nil {
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, err:%s", partnerId, userId, loginInfo, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断返回状态是否为成功
|
||||
if returnObj.Code != 0 {
|
||||
logUtil.ErrorLog(fmt.Sprintf("CheckLoginInfo Failed. partnerId:%d, userId:%s, loginInfo:%s, Code:%d, Message:%s", partnerId, userId, loginInfo, returnObj.Code, returnObj.Message))
|
||||
return
|
||||
}
|
||||
|
||||
success = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 本地验证登陆信息
|
||||
// partnerId:合作商Id
|
||||
// userId:合作商用户Id
|
||||
// loginInfo:登陆信息
|
||||
// 返回值:
|
||||
// 成功与否
|
||||
// 错误对象
|
||||
func CheckDynamicTokenLocal(partnerId int32, userId, loginInfo string) (success bool, err error) {
|
||||
//1001直接返回true
|
||||
if partnerId == 1001 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
//非1001渠道验证
|
||||
if len(loginInfo) == 0 {
|
||||
success = false
|
||||
err = fmt.Errorf("Err:%s", "LoginInfo is null!")
|
||||
return
|
||||
}
|
||||
// 验证用户合法性
|
||||
loginItemList := strings.Split(loginInfo, "_")
|
||||
if len(loginItemList) != 2 {
|
||||
success = false
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. userId:%s, loginInfo:%s", userId, loginInfo)
|
||||
return
|
||||
}
|
||||
|
||||
//生成key
|
||||
localSign := securityUtil.Md5String(userId+GetSysConfig().DynamicLoginKey+loginItemList[1], true)
|
||||
|
||||
//判断签名是佛正确
|
||||
if localSign != loginItemList[0] {
|
||||
success = false
|
||||
err = fmt.Errorf("CheckLoginInfo Failed. Sign Check Failed! userId:%s,LocalSign:%s,loginInfo:%s", userId, localSign, loginInfo)
|
||||
return
|
||||
}
|
||||
|
||||
success = true
|
||||
return
|
||||
}
|
||||
|
||||
// ------------------类型定义和业务逻辑的分隔符-------------------------
|
||||
|
||||
var (
|
||||
LoginUtilObj = new(LoginUtil)
|
||||
)
|
||||
@ -0,0 +1,112 @@
|
||||
package webServer
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"common/resultStatus"
|
||||
)
|
||||
|
||||
// 处理函数
|
||||
type HandleFunc func(context *ApiContext) *ResponseObject
|
||||
|
||||
// ApiHandler
|
||||
//
|
||||
// @description: API处理结构
|
||||
type ApiHandler struct {
|
||||
// API完整路径名称
|
||||
apiFullName string
|
||||
|
||||
// 方法定义
|
||||
handleFun HandleFunc
|
||||
|
||||
// 方法参数名称集合
|
||||
funcParamNames []string
|
||||
}
|
||||
|
||||
// ApiFullName
|
||||
//
|
||||
// @description: API完整路径名称
|
||||
//
|
||||
// parameter:
|
||||
//
|
||||
// @receiver this: this
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @string:
|
||||
func (this *ApiHandler) ApiFullName() string {
|
||||
return this.apiFullName
|
||||
}
|
||||
|
||||
// HandleFun
|
||||
//
|
||||
// @description: 方法定义
|
||||
//
|
||||
// parameter:
|
||||
//
|
||||
// @receiver this: this
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @HandleFunc: 方法
|
||||
func (this *ApiHandler) HandleFun() HandleFunc {
|
||||
return this.handleFun
|
||||
}
|
||||
|
||||
// FuncParamNames
|
||||
//
|
||||
// @description: 方法参数名称集合
|
||||
//
|
||||
// parameter:
|
||||
//
|
||||
// @receiver this: this
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @[]string: 方法参数名称集合
|
||||
func (this *ApiHandler) FuncParamNames() []string {
|
||||
return this.funcParamNames
|
||||
}
|
||||
|
||||
// CheckParam
|
||||
//
|
||||
// @description: 检测参数数量
|
||||
//
|
||||
// parameter:
|
||||
//
|
||||
// @receiver this: this
|
||||
// @r:
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @resultStatus.ResultStatus: 状态码数据
|
||||
func (this *ApiHandler) CheckParam(r *http.Request) resultStatus.ResultStatus {
|
||||
for _, name := range this.funcParamNames {
|
||||
if r.Form[name] == nil || len(r.Form[name]) == 0 {
|
||||
return resultStatus.APIParamError
|
||||
}
|
||||
}
|
||||
|
||||
return resultStatus.Success
|
||||
}
|
||||
|
||||
// newApiHandler
|
||||
//
|
||||
// @description: 创建新的请求方法对象
|
||||
//
|
||||
// parameter:
|
||||
//
|
||||
// @_apiFullName: API完整路径名称
|
||||
// @_funcDefinition: 方法定义
|
||||
// @_funcParamNames: 方法参数名称集合
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @*ApiHandler: 请求方法对象
|
||||
func newApiHandler(_apiFullName string, _funcDefinition HandleFunc, _funcParamNames ...string) *ApiHandler {
|
||||
return &ApiHandler{
|
||||
apiFullName: _apiFullName,
|
||||
handleFun: _funcDefinition,
|
||||
funcParamNames: _funcParamNames,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
module Framework
|
||||
|
||||
go 1.22.2
|
||||
|
||||
replace goutil => ../goutil
|
||||
|
||||
require (
|
||||
github.com/Shopify/sarama v1.29.1
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jinzhu/gorm v1.9.12
|
||||
github.com/rabbitmq/amqp091-go v1.8.1
|
||||
github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.230
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vms v1.0.230
|
||||
goutil v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/goutil.iml" filepath="$PROJECT_DIR$/.idea/goutil.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,130 @@
|
||||
package notify_util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"Framework/goroutineMgr"
|
||||
"goutil/logUtil"
|
||||
"goutil/syncUtil"
|
||||
)
|
||||
|
||||
const (
|
||||
deathLockTime = 0
|
||||
)
|
||||
|
||||
// notifyCenter
|
||||
// @description: 玩家信息务逻辑类
|
||||
type notifyCenter struct {
|
||||
// key:初始化成功的标志名称 val:占位符
|
||||
registerMap map[string]func()
|
||||
mutex *syncUtil.RWLocker
|
||||
}
|
||||
|
||||
// newNotifyCenter
|
||||
// @description: 构造对象
|
||||
// parameter:
|
||||
// return:
|
||||
// @*notifyCenter:
|
||||
func newNotifyCenter() *notifyCenter {
|
||||
return ¬ifyCenter{
|
||||
registerMap: make(map[string]func()),
|
||||
mutex: syncUtil.NewRWLocker(),
|
||||
}
|
||||
}
|
||||
|
||||
// register
|
||||
// @description: 注册需要被通知的对象
|
||||
// parameter:
|
||||
// @receiver nc:
|
||||
// @chanName:唯一标识
|
||||
// @cf:回调方法
|
||||
// return:
|
||||
func (nc *notifyCenter) register(chanName string, cf func()) {
|
||||
if isOk, prevStack, currStack := nc.mutex.Lock(deathLockTime); isOk == false {
|
||||
//记日志
|
||||
errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s \n当前堆栈:\n%s", prevStack, currStack)
|
||||
panic(errMsg)
|
||||
}
|
||||
defer nc.mutex.Unlock()
|
||||
if _, exists := nc.registerMap[chanName]; exists {
|
||||
panic(fmt.Errorf("registerMap.Register-%s已经存在,请检查", chanName))
|
||||
}
|
||||
|
||||
nc.registerMap[chanName] = cf
|
||||
}
|
||||
|
||||
// @description: 取消启动成功通知注册
|
||||
// parameter:
|
||||
// @receiver nc:
|
||||
// @name:唯一标识
|
||||
// return:
|
||||
func (nc *notifyCenter) unregister(name string) {
|
||||
if isOk, prevStack, currStack := nc.mutex.Lock(deathLockTime); isOk == false {
|
||||
//记日志
|
||||
errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s \n当前堆栈:\n%s", prevStack, currStack)
|
||||
panic(errMsg)
|
||||
}
|
||||
defer nc.mutex.Unlock()
|
||||
|
||||
delete(nc.registerMap, name)
|
||||
}
|
||||
|
||||
// notify
|
||||
// @description: 通知所有已注册的对象
|
||||
// parameter:
|
||||
// @receiver nc:
|
||||
// return:
|
||||
func (nc *notifyCenter) notify() {
|
||||
// 处理goroutine数量
|
||||
goroutineName := "notifyCenter.Notify"
|
||||
goroutineMgr.MonitorZero(goroutineName)
|
||||
defer goroutineMgr.ReleaseMonitor(goroutineName)
|
||||
|
||||
if isOk, prevStack, currStack := nc.mutex.RLock(deathLockTime); isOk == false {
|
||||
//记日志
|
||||
errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s \n当前堆栈:\n%s", prevStack, currStack)
|
||||
panic(errMsg)
|
||||
}
|
||||
defer nc.mutex.RUnlock()
|
||||
|
||||
for name, cf := range nc.registerMap {
|
||||
cf()
|
||||
msg := fmt.Sprintf("通知:%s初始化成功", name)
|
||||
logUtil.DebugLog(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// notify2
|
||||
// @description: 通知所有已注册的对象,该方法会在捕获第一个err的时候停止后续的通知。多用于系统启动的判定
|
||||
// parameter:
|
||||
// @receiver nc:
|
||||
// return:
|
||||
// @error:
|
||||
func (nc *notifyCenter) notify2() (err error) {
|
||||
// 处理goroutine数量
|
||||
goroutineName := "notifyCenter.Notify"
|
||||
goroutineMgr.MonitorZero(goroutineName)
|
||||
defer goroutineMgr.ReleaseMonitor(goroutineName)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("notify2 err:%s", r)
|
||||
logUtil.ErrorLog(err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
if isOk, prevStack, currStack := nc.mutex.RLock(deathLockTime); isOk == false {
|
||||
//记日志
|
||||
errMsg := fmt.Sprintf("Lock timeout! \n上一个堆栈:\n%s \n当前堆栈:\n%s", prevStack, currStack)
|
||||
panic(errMsg)
|
||||
}
|
||||
defer nc.mutex.RUnlock()
|
||||
|
||||
for name, cf := range nc.registerMap {
|
||||
cf()
|
||||
msg := fmt.Sprintf("通知:%s初始化成功", name)
|
||||
logUtil.DebugLog(msg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package timeUtil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConverToStandardFormat(t *testing.T) {
|
||||
str := "2018-10-10T10:10:10"
|
||||
expected := time.Date(2018, 10, 10, 10, 10, 10, 0, time.Local)
|
||||
|
||||
got, err := ConverToStandardFormat(str)
|
||||
if err != nil {
|
||||
t.Errorf("发生错误,错误信息为:%s", err)
|
||||
}
|
||||
|
||||
if got != expected {
|
||||
t.Errorf("转换不正确,期待:%s, 实际:%s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToInt(t *testing.T) {
|
||||
date := time.Date(2018, 10, 10, 10, 10, 10, 0, time.Local)
|
||||
finalInt := ConvertToInt(date)
|
||||
expecteInt := 20181010
|
||||
|
||||
if finalInt != expecteInt {
|
||||
t.Errorf("转换不正确,期待:%d, 实际:%d", expecteInt, finalInt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubDay(t *testing.T) {
|
||||
time1 := time.Now().AddDate(0, 0, 5)
|
||||
time2 := time.Now()
|
||||
expected := 5
|
||||
|
||||
got := SubDay(time1, time2)
|
||||
if got != expected {
|
||||
t.Errorf("Expected %d, but now got %d.", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTimeString(t *testing.T) {
|
||||
val := "12:13:14"
|
||||
expectedHour := 12
|
||||
expectedMinute := 13
|
||||
expectedSecond := 14
|
||||
|
||||
err, hour, miniute, second := ParseTimeString(val)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if expectedHour != hour || expectedMinute != miniute || expectedSecond != second {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
_ "logincenter/internal/game"
|
||||
_ "logincenter/internal/user"
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user