Apply .gitignore rules

This commit is contained in:
皮蛋13361098506
2025-01-06 16:21:36 +08:00
parent 1b77f62820
commit ccd2c530cf
580 changed files with 69806 additions and 0 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
}
}

View File

@@ -0,0 +1,6 @@
package reloadMgr
// 重新加载包,提供重新加载的功能
// 使用方法:
// 1、先调用RegisterReloadFunc方法将重新加载时需要调用的方法进行注册。
// 2、在需要重新加载时调用Reload()方法

View File

@@ -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
}
}

View File

@@ -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
}
```