Apply .gitignore rules
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package voicemsgMgr
|
||||
|
||||
//语音配置参数
|
||||
type VoiceMessageConfig struct {
|
||||
//API密钥Id
|
||||
SecretId string
|
||||
|
||||
//API密钥key
|
||||
SecretKey string
|
||||
|
||||
//地域
|
||||
Region string
|
||||
|
||||
//模板ID
|
||||
TemplateId string
|
||||
|
||||
//应用后生成的实际SdkAppid
|
||||
VoiceSdkAppid string
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package mailUtil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SMTPClient实现
|
||||
type simpleClient struct {
|
||||
host string
|
||||
port int
|
||||
isSSL bool
|
||||
|
||||
senderName string
|
||||
senderAddr string
|
||||
senderPwd string
|
||||
}
|
||||
|
||||
// 返回一个simpleClient作为SMTPClient接口
|
||||
func SimpleSMTPClient(_host string, _port int, _isSSL bool,
|
||||
name, address, password string) SMTPClient {
|
||||
|
||||
return &simpleClient{
|
||||
host: _host,
|
||||
port: _port,
|
||||
isSSL: _isSSL,
|
||||
|
||||
senderName: name,
|
||||
senderAddr: address,
|
||||
senderPwd: password,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *simpleClient) SetServer(_host string, _port int, _isSSL bool) {
|
||||
this.host = _host
|
||||
this.port = _port
|
||||
this.isSSL = _isSSL
|
||||
}
|
||||
|
||||
func (this *simpleClient) SetSender(name, address, password string) {
|
||||
this.senderName = name
|
||||
this.senderAddr = address
|
||||
this.senderPwd = password
|
||||
}
|
||||
|
||||
//发送邮件:
|
||||
// mailTo 接收方列表
|
||||
// subject 主题
|
||||
// body 正文
|
||||
// isHtmlBody 正文是否html格式
|
||||
// attachFiles 附件
|
||||
func (this *simpleClient) SendMail(
|
||||
mailTo []string,
|
||||
subject, body string, isHtmlBody bool,
|
||||
attachFiles []string) (err error) {
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
// 创建连接
|
||||
var conn net.Conn
|
||||
|
||||
if this.isSSL {
|
||||
// TLS config
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: this.host,
|
||||
}
|
||||
conn, err = tls.Dial("tcp", fmt.Sprintf("%s:%d", this.host, this.port), tlsconfig)
|
||||
} else {
|
||||
conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", this.host, this.port))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// 创建smtp.Client
|
||||
c, err := smtp.NewClient(conn, this.host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证信息
|
||||
auth := smtp.PlainAuth("", this.senderAddr, this.senderPwd, this.host)
|
||||
if err = c.Auth(auth); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送方
|
||||
from := mail.Address{this.senderName, this.senderAddr}
|
||||
// 接收方
|
||||
to := make([]string, 0, len(mailTo))
|
||||
for _, v := range mailTo {
|
||||
to = append(to, "<"+v+">")
|
||||
}
|
||||
|
||||
// To && From
|
||||
if err = c.Mail(from.Address); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range mailTo {
|
||||
if err = c.Rcpt(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 边界
|
||||
boundary := "a40acf3c8b7200fc6b04c2f1b3da"
|
||||
|
||||
buff := bytes.NewBuffer(nil)
|
||||
|
||||
// 写入基本信息
|
||||
buff.WriteString(fmt.Sprintf("From: %s\r\n", from.String()))
|
||||
buff.WriteString(fmt.Sprintf("To: %s\r\n", strings.Join(to, ", ")))
|
||||
buff.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
|
||||
|
||||
// 写入邮件头部信息
|
||||
if len(attachFiles) > 0 {
|
||||
buff.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", boundary))
|
||||
|
||||
// 写入正文的边界信息
|
||||
buff.WriteString(fmt.Sprintf("\r\n--%s\r\n", boundary))
|
||||
}
|
||||
|
||||
// 写入正文头部
|
||||
if isHtmlBody {
|
||||
buff.WriteString(fmt.Sprintf("Content-Type: text/html; charset=\"utf-8\"\r\n"))
|
||||
} else {
|
||||
buff.WriteString(fmt.Sprintf("Content-Type: text/plain; charset=\"utf-8\"\r\n"))
|
||||
}
|
||||
buff.WriteString("\r\n")
|
||||
// 写入正文内容
|
||||
buff.WriteString(body)
|
||||
|
||||
if len(attachFiles) > 0 {
|
||||
for _, file := range attachFiles {
|
||||
fileBytes, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, fileName := filepath.Split(file)
|
||||
|
||||
// 写入文件信息
|
||||
buff.WriteString(fmt.Sprintf("\r\n\r\n--%s\r\n", boundary))
|
||||
buff.WriteString("Content-Type: application/octet-stream\r\n")
|
||||
buff.WriteString("Content-Transfer-Encoding: base64\r\n")
|
||||
buff.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", fileName))
|
||||
|
||||
// 写入文件数据
|
||||
b := make([]byte, base64.StdEncoding.EncodedLen(len(fileBytes)))
|
||||
base64.StdEncoding.Encode(b, fileBytes)
|
||||
buff.Write(b)
|
||||
}
|
||||
|
||||
// 写入结束标识
|
||||
buff.WriteString(fmt.Sprintf("\r\n--%s--", boundary))
|
||||
}
|
||||
|
||||
// Data
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 写入邮件数据
|
||||
_, err = w.Write(buff.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Quit()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package xmlUtil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func findNode(root *Node, name string) *Node {
|
||||
node := root.FirstChild
|
||||
for {
|
||||
if node == nil || node.NodeName == name {
|
||||
break
|
||||
}
|
||||
node = node.NextSibling
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func childNodes(root *Node, name string) []*Node {
|
||||
var list []*Node
|
||||
node := root.FirstChild
|
||||
for {
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
if node.NodeName == name {
|
||||
list = append(list, node)
|
||||
}
|
||||
node = node.NextSibling
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func testNode(t *testing.T, n *Node, expected string) {
|
||||
if n.NodeName != expected {
|
||||
t.Fatalf("expected node name is %s,but got %s", expected, n.NodeName)
|
||||
}
|
||||
}
|
||||
|
||||
func testAttr(t *testing.T, n *Node, name, expected string) {
|
||||
for _, attr := range n.Attr {
|
||||
if attr.Name.Local == name && attr.Value == expected {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("not found attribute %s in the node %s", name, n.NodeName)
|
||||
}
|
||||
|
||||
func testValue(t *testing.T, val, expected string) {
|
||||
if val != expected {
|
||||
t.Fatalf("expected value is %s,but got %s", expected, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
s := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bookstore>
|
||||
<book>
|
||||
<title lang="en">Harry Potter</title>
|
||||
<price>29.99</price>
|
||||
</book>
|
||||
<book>
|
||||
<title lang="en">Learning XML</title>
|
||||
<price>39.95</price>
|
||||
</book>
|
||||
</bookstore>`
|
||||
root, err := LoadFromReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if root.Type != DocumentNode {
|
||||
t.Fatal("top node of tree is not DocumentNode")
|
||||
}
|
||||
|
||||
declarNode := root.FirstChild
|
||||
if declarNode.Type != DeclarationNode {
|
||||
t.Fatal("first child node of tree is not DeclarationNode")
|
||||
}
|
||||
|
||||
if declarNode.Attr[0].Name.Local != "version" && declarNode.Attr[0].Value != "1.0" {
|
||||
t.Fatal("version attribute not expected")
|
||||
}
|
||||
|
||||
bookstore := root.LastChild
|
||||
if bookstore.NodeName != "bookstore" {
|
||||
t.Fatal("bookstore elem not found")
|
||||
}
|
||||
if bookstore.FirstChild.NodeName != "\n" {
|
||||
t.Fatal("first child node of bookstore is not empty node(\n)")
|
||||
}
|
||||
books := childNodes(bookstore, "book")
|
||||
if len(books) != 2 {
|
||||
t.Fatalf("expected book element count is 2, but got %d", len(books))
|
||||
}
|
||||
// first book element
|
||||
testNode(t, findNode(books[0], "title"), "title")
|
||||
testAttr(t, findNode(books[0], "title"), "lang", "en")
|
||||
testValue(t, findNode(books[0], "price").InnerText(), "29.99")
|
||||
testValue(t, findNode(books[0], "title").InnerText(), "Harry Potter")
|
||||
|
||||
// second book element
|
||||
testNode(t, findNode(books[1], "title"), "title")
|
||||
testAttr(t, findNode(books[1], "title"), "lang", "en")
|
||||
testValue(t, findNode(books[1], "price").InnerText(), "39.95")
|
||||
|
||||
testValue(t, books[0].OutputXML(), `<book><title lang="en">Harry Potter</title><price>29.99</price></book>`)
|
||||
}
|
||||
|
||||
func TestTooNested(t *testing.T) {
|
||||
s := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AAA>
|
||||
<BBB>
|
||||
<DDD>
|
||||
<CCC>
|
||||
<DDD/>
|
||||
<EEE/>
|
||||
</CCC>
|
||||
</DDD>
|
||||
</BBB>
|
||||
<CCC>
|
||||
<DDD>
|
||||
<EEE>
|
||||
<DDD>
|
||||
<FFF/>
|
||||
</DDD>
|
||||
</EEE>
|
||||
</DDD>
|
||||
</CCC>
|
||||
</AAA>`
|
||||
root, err := LoadFromReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
aaa := findNode(root, "AAA")
|
||||
if aaa == nil {
|
||||
t.Fatal("AAA node not exists")
|
||||
}
|
||||
ccc := aaa.LastChild
|
||||
if ccc.NodeName != "CCC" {
|
||||
t.Fatalf("expected node is CCC,but got %s", ccc.NodeName)
|
||||
}
|
||||
bbb := ccc.PrevSibling
|
||||
if bbb.NodeName != "BBB" {
|
||||
t.Fatalf("expected node is bbb,but got %s", bbb.NodeName)
|
||||
}
|
||||
ddd := findNode(bbb, "DDD")
|
||||
testNode(t, ddd, "DDD")
|
||||
testNode(t, ddd.LastChild, "CCC")
|
||||
}
|
||||
|
||||
func TestSelectElement(t *testing.T) {
|
||||
s := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AAA>
|
||||
<BBB id="1"/>
|
||||
<CCC id="2">
|
||||
<DDD/>
|
||||
</CCC>
|
||||
<CCC id="3">
|
||||
<DDD/>
|
||||
</CCC>
|
||||
</AAA>`
|
||||
root, err := LoadFromReader(strings.NewReader(s))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
version, _ := root.FirstChild.SelectAttr("version")
|
||||
if version != "1.0" {
|
||||
t.Fatal("version!=1.0")
|
||||
}
|
||||
aaa := findNode(root, "AAA")
|
||||
var n *Node
|
||||
n = aaa.SelectElement("BBB")
|
||||
if n == nil {
|
||||
t.Fatalf("n is nil")
|
||||
}
|
||||
n = aaa.SelectElement("CCC")
|
||||
if n == nil {
|
||||
t.Fatalf("n is nil")
|
||||
}
|
||||
|
||||
var ns []*Node
|
||||
ns = aaa.SelectElements("CCC")
|
||||
if len(ns) != 2 {
|
||||
t.Fatalf("len(ns)!=2")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package reloadMgr
|
||||
|
||||
// 重新加载包,提供重新加载的功能
|
||||
// 使用方法:
|
||||
// 1、先调用RegisterReloadFunc方法,将重新加载时需要调用的方法进行注册。
|
||||
// 2、在需要重新加载时调用Reload()方法
|
||||
@@ -0,0 +1,208 @@
|
||||
package ensureSendUtil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"goutil/intAndBytesUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
errConnectEmpty = fmt.Errorf("scoket reconnecting...")
|
||||
byterOrder = binary.LittleEndian
|
||||
)
|
||||
|
||||
// 实现 EnsureSender和sender接口
|
||||
type tcpSender struct {
|
||||
// 需要实现的接口
|
||||
EnsureSender
|
||||
|
||||
// 包含sender接口部分实现
|
||||
*baseSender
|
||||
|
||||
// 数据目录
|
||||
dataFolder string
|
||||
|
||||
// 服务器地址
|
||||
address string
|
||||
|
||||
// 连接
|
||||
conn net.Conn
|
||||
|
||||
// 用于重连时互斥
|
||||
mutex sync.Mutex
|
||||
|
||||
// 用于sendLoop和resendLoop发送退出信号
|
||||
closeSignal chan struct{}
|
||||
}
|
||||
|
||||
// 创建一个tcp数据发送器
|
||||
// 参数:
|
||||
//
|
||||
// _dataFolder 数据存放目录
|
||||
// _address 连接地址
|
||||
func NewTCPSender(_dataFolder, _address string) (EnsureSender, error) {
|
||||
// 连接服务器
|
||||
conn, err := net.DialTimeout("tcp", _address, 5*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
this := &tcpSender{
|
||||
dataFolder: _dataFolder,
|
||||
baseSender: newBaseSender(),
|
||||
address: _address,
|
||||
conn: conn,
|
||||
closeSignal: make(chan struct{}),
|
||||
}
|
||||
|
||||
// 新开协程发送数据
|
||||
go sendLoop(this, this.closeSignal)
|
||||
|
||||
// 定时重发
|
||||
go resendLoop(this, _dataFolder, this.closeSignal)
|
||||
|
||||
// 发送心跳包
|
||||
go this.heartBeat()
|
||||
|
||||
return this, nil
|
||||
}
|
||||
|
||||
// 每隔15秒发送心跳包
|
||||
func (this *tcpSender) heartBeat() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logUtil.LogUnknownError(r)
|
||||
}
|
||||
}()
|
||||
|
||||
tick := time.Tick(time.Second * 15)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-this.Done():
|
||||
return
|
||||
case <-tick:
|
||||
this.sendBytes([]byte{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureSender接口
|
||||
// Write:写入数据
|
||||
func (this *tcpSender) Write(data string) error {
|
||||
item, err := newTCPDataItem(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.waitingDataChan <- item
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnsureSender接口
|
||||
// Close:关闭
|
||||
func (this *tcpSender) Close() error {
|
||||
close(this.done)
|
||||
|
||||
// 关闭socket连接
|
||||
conn := this.conn
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
// 等待sendLoop和resendLoop退出
|
||||
<-this.closeSignal
|
||||
<-this.closeSignal
|
||||
|
||||
// 保存数据
|
||||
_, e1 := saveData(this.Cache(), this.dataFolder)
|
||||
_, e2 := saveData(this.Data(), this.dataFolder)
|
||||
|
||||
if e2 != nil {
|
||||
if e1 != nil {
|
||||
return fmt.Errorf("%s %s", e1, e2)
|
||||
}
|
||||
return e2
|
||||
} else {
|
||||
return e1
|
||||
}
|
||||
}
|
||||
|
||||
// Sender接口
|
||||
// Send:发送dataItem
|
||||
func (this *tcpSender) Send(item dataItem) error {
|
||||
err := this.sendBytes(item.Bytes())
|
||||
if err != nil && err != errConnectEmpty {
|
||||
// 发送失败时发送次数+1
|
||||
item.SetCount(item.Count() + 1)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送字节数据
|
||||
// 发送格式:[lenght+data]
|
||||
func (this *tcpSender) sendBytes(data []byte) error {
|
||||
conn := this.conn
|
||||
if conn == nil {
|
||||
return errConnectEmpty
|
||||
}
|
||||
|
||||
// 将长度转化为字节数组
|
||||
header := intAndBytesUtil.Int32ToBytes(int32(len(data)), byterOrder)
|
||||
|
||||
if len(data) > 0 {
|
||||
data = append(header, data...)
|
||||
} else {
|
||||
data = header
|
||||
}
|
||||
|
||||
_, err := conn.Write(data)
|
||||
if err != nil {
|
||||
this.mutex.Lock()
|
||||
// 发送失败
|
||||
// 检查失败的conn是否this.conn(避免多个线程失败后均调用reconnect)
|
||||
// 是则关闭并重连
|
||||
if conn == this.conn {
|
||||
this.conn.Close()
|
||||
this.conn = nil
|
||||
this.mutex.Unlock()
|
||||
|
||||
// 重连
|
||||
go this.reconnect()
|
||||
} else {
|
||||
this.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 重连服务器
|
||||
func (this *tcpSender) reconnect() error {
|
||||
// lock-it
|
||||
this.mutex.Lock()
|
||||
defer this.mutex.Unlock()
|
||||
|
||||
for {
|
||||
// 检查是否已经重连
|
||||
if this.conn != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("tcp", this.address, 5*time.Second)
|
||||
if err != nil {
|
||||
// 连接失败,5秒后重试
|
||||
<-time.After(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
|
||||
this.conn = conn
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
提供游戏内的实时排行榜功能,建议最大长度设定<=200
|
||||
使用方式:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
rank_util "goutil/rank-util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 构造对象
|
||||
r := rank_util.NewRankUtil(20, compar)
|
||||
|
||||
// 刷新排行榜
|
||||
m := &rmodel{k: "byrontest", Fap: 110}
|
||||
ifChangeRank, dm := r.Refresh(m.k, m, true)
|
||||
|
||||
// 获取全部排行榜
|
||||
tempList := r.GetAll()
|
||||
|
||||
// 删除某个key
|
||||
isok := r.Delete("byrontest")
|
||||
}
|
||||
|
||||
// compar
|
||||
// @description: 判断对象大小,返回含义 -1:a<b 0:a=b 1:a>b
|
||||
// parameter:
|
||||
// @a:对象a
|
||||
// @b:对象b
|
||||
// return:
|
||||
// @int:
|
||||
func compar(a, b interface{}) int {
|
||||
af := a.(*rmodel).Fap
|
||||
bf := b.(*rmodel).Fap
|
||||
if af > bf {
|
||||
return 1
|
||||
} else if af == bf {
|
||||
return 0
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
type rmodel struct {
|
||||
k string
|
||||
Fap int
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user