版本更新
This commit is contained in:
parent
975c1990f6
commit
bf5867c7b7
@ -1,3 +1,10 @@
|
||||
生成一个方法
|
||||
## 方法名
|
||||
ToTmplStr
|
||||
|
||||
## 参数
|
||||
string
|
||||
|
||||
## 逻辑
|
||||
将字符串转换成一行的字符串格式,并再进行一次转义输出
|
||||
|
||||
|
||||
42
Type/c.go
Normal file
42
Type/c.go
Normal file
@ -0,0 +1,42 @@
|
||||
package Type
|
||||
|
||||
type CardMdData struct {
|
||||
Date string
|
||||
Register int
|
||||
Recharge float64
|
||||
RechargeUserNum int
|
||||
ChurnRate float64
|
||||
ARPU float64
|
||||
YRegister int
|
||||
YRecharge float64
|
||||
YLogin int
|
||||
PerOnlineTime float64
|
||||
PerOrderNum float64
|
||||
C1 string
|
||||
C2 string
|
||||
}
|
||||
|
||||
type MarkDown struct {
|
||||
Markdown string
|
||||
}
|
||||
|
||||
type RowData struct {
|
||||
Date string
|
||||
Login int
|
||||
Register int
|
||||
Recharge float64
|
||||
SecondRemain string
|
||||
ThirdRemain string
|
||||
SeventhRemain string
|
||||
}
|
||||
|
||||
type CardTable struct {
|
||||
Rows string
|
||||
}
|
||||
|
||||
type Card struct {
|
||||
Title string
|
||||
Subtitle string
|
||||
Elements string
|
||||
Tag1 string
|
||||
}
|
||||
34
Type/t.go
34
Type/t.go
@ -28,24 +28,28 @@ type User struct {
|
||||
}
|
||||
|
||||
type Operation struct {
|
||||
Retain []*Retain
|
||||
Register int
|
||||
Recharge float64
|
||||
ChurnRate float64
|
||||
Date string
|
||||
Retain []*Retain
|
||||
Register int
|
||||
Recharge float64
|
||||
RechargeUserNum int
|
||||
ChurnRate float64
|
||||
ARPU float64
|
||||
}
|
||||
|
||||
type Retain struct {
|
||||
Date string `db:"Date"`
|
||||
Register int `db:"Register"`
|
||||
SecondRemain int `db:"SecondRemain"`
|
||||
ThirdRemain int `db:"ThirdRemain"`
|
||||
SeventhRemain int `db:"SeventhRemain"`
|
||||
ThirtiethRemain int `db:"ThirtiethRemain"`
|
||||
Recharge float64 `db:"Recharge"`
|
||||
Login int `db:"Login"`
|
||||
Ext string `db:"Ext"`
|
||||
PerOnlineTime float64 // 每日在线时长
|
||||
PerOrderNum float64 // 每日完成订单数
|
||||
Date string `db:"Date"`
|
||||
Register int `db:"Register"`
|
||||
SecondRemain int `db:"SecondRemain"`
|
||||
ThirdRemain int `db:"ThirdRemain"`
|
||||
SeventhRemain int `db:"SeventhRemain"`
|
||||
FourteenthRemain int `db:"FourteenthRemain"`
|
||||
ThirtiethRemain int `db:"ThirtiethRemain"`
|
||||
Recharge float64 `db:"Recharge"`
|
||||
Login int `db:"Login"`
|
||||
Ext string `db:"Ext"`
|
||||
PerOnlineTime float64 // 每日在线时长
|
||||
PerOrderNum float64 // 每日完成订单数
|
||||
}
|
||||
|
||||
type ServerInfo struct {
|
||||
|
||||
@ -4,3 +4,8 @@ const (
|
||||
FONT_COLOR_UP = "red"
|
||||
FONT_COLOR_DOWN = "green"
|
||||
)
|
||||
|
||||
const (
|
||||
FEISHU_INFO_TYPE = "text"
|
||||
FEISHU_CART_TYPE = "interactive"
|
||||
)
|
||||
|
||||
@ -26,10 +26,11 @@ type MysqlConfig struct {
|
||||
}
|
||||
|
||||
type SystemConfig struct {
|
||||
NMap bool `yaml:"nmap"` // 是否开启端口扫描
|
||||
FeishUrl string `yaml:"feishu_url"` // 飞书机器人url
|
||||
NoticeUrl string `yaml:"notice_url"` // 通知url
|
||||
OperationUrl string `yaml:"operation_url"` // 运营url
|
||||
NMap bool `yaml:"nmap"` // 是否开启端口扫描
|
||||
FeishUrl string `yaml:"feishu_url"` // 飞书机器人url
|
||||
NoticeUrl string `yaml:"notice_url"` // 通知url
|
||||
OperationUrl string `yaml:"operation_url"` // 运营url
|
||||
OperationChatId string `yaml:"operation_chat_id"` // 运营群id
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -97,3 +98,7 @@ func GetOperationUrl() string {
|
||||
func GetNMap() bool {
|
||||
return config.System.NMap
|
||||
}
|
||||
|
||||
func GetOperationChatId() string {
|
||||
return config.System.OperationChatId
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ system:
|
||||
feishu_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
|
||||
notice_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/64bad1f3-3a41-4dca-9037-399067ffb252'
|
||||
operation_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/64bad1f3-3a41-4dca-9037-399067ffb252'
|
||||
operation_chat_id: 'oc_f6e10a55f28f31e2a5677bdcb6aed599'
|
||||
mysqls:
|
||||
- host: '127.0.0.1'
|
||||
name: 'merge_pet_test'
|
||||
|
||||
@ -3,6 +3,7 @@ system:
|
||||
feishu_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/70e24a79-b019-434a-b4d1-4592bbf7c311'
|
||||
notice_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/48944500-477a-4647-a7e0-c56c43bee263'
|
||||
operation_url: 'https://open.feishu.cn/open-apis/bot/v2/hook/e3122bc9-99ca-46b4-9634-862d3c8cdc7e'
|
||||
operation_chat_id: 'oc_967a93dcade6d55c3db434f23767a414'
|
||||
mysqls:
|
||||
- host: '127.0.0.1'
|
||||
name: 'merge_pet_test'
|
||||
|
||||
@ -2,14 +2,11 @@ package controller
|
||||
|
||||
import (
|
||||
"backend/Type"
|
||||
"backend/common"
|
||||
"backend/feishu"
|
||||
"backend/model"
|
||||
"backend/msg"
|
||||
"backend/util"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -18,68 +15,12 @@ import (
|
||||
|
||||
func FeishuSendInfo(c *gin.Context) {
|
||||
// TODO
|
||||
AppConfig, err := util.GetAppConfig(3)
|
||||
Result, err := util.GetOperation(3)
|
||||
if err != nil {
|
||||
log.Printf("failed to get app config: %v", err)
|
||||
log.Printf("failed to get operation: %v", err)
|
||||
return
|
||||
}
|
||||
Db := util.MPool.GetTopicDB(AppConfig.Topic)
|
||||
defer Db.Close()
|
||||
Retain := []*Type.Retain{}
|
||||
|
||||
ZeroTimestamp := util.ZeroTimestampByTz("Europe/London") - 86400
|
||||
ZeroTime := time.Unix(ZeroTimestamp, 0).In(time.UTC)
|
||||
StartDate := ZeroTime.AddDate(0, 0, -14).Format("2006-01-02")
|
||||
EndDate := ZeroTime.Format("2006-01-02")
|
||||
err = Db.Select(&Retain, "SELECT `Date`, `Register`, `SecondRemain`, `ThirdRemain`, `SeventhRemain`, `ThirtiethRemain`, `Recharge`, `Login`, `Ext` FROM remain where `Date` >= ? and `Date` <= ? order by `Date` desc", StartDate, EndDate)
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var Register int
|
||||
err = Db.Get(&Register, "SELECT count(`Uid`) as count FROM log_login WHERE Event = 'register'")
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var Recharge float64
|
||||
err = Db.Get(&Recharge, "SELECT IFNULL(SUM(Price), 0) as sum FROM log_order")
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var InactiveUsers int
|
||||
err = Db.Get(&InactiveUsers, "SELECT count(distinct Uid) as count from (SELECT Uid, MAX(Timestamp) as LastLogin FROM log_login WHERE Event = 'Login_log' GROUP BY Uid) as lt where lastlogin < ?", ZeroTimestamp-7*86400)
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
type ExtStruct struct {
|
||||
PerOnlineTime string `json:"PerOnlineTime"`
|
||||
PerOrderNum string `json:"PerOrderNum"`
|
||||
}
|
||||
for _, v := range Retain {
|
||||
if v.Ext == "" {
|
||||
continue
|
||||
}
|
||||
var d ExtStruct
|
||||
err := json.Unmarshal([]byte(v.Ext), &d)
|
||||
if err != nil {
|
||||
fmt.Printf("err :%s", err.Error())
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseFloat(d.PerOnlineTime, 64)
|
||||
if err == nil {
|
||||
v.PerOnlineTime = value
|
||||
}
|
||||
value, err = strconv.ParseFloat(d.PerOrderNum, 64)
|
||||
if err == nil {
|
||||
v.PerOrderNum = value
|
||||
}
|
||||
}
|
||||
ChurnRate := 100 * float64(InactiveUsers) / float64(Register)
|
||||
Result := &Type.Operation{Retain: Retain, Register: Register, Recharge: Recharge, ChurnRate: ChurnRate}
|
||||
err = common.SendOperationMsg(Result)
|
||||
err = feishu.SendOperationMsg(Result)
|
||||
if err != nil {
|
||||
log.Printf("failed to send operation message: %v", err)
|
||||
}
|
||||
@ -87,45 +28,12 @@ func FeishuSendInfo(c *gin.Context) {
|
||||
|
||||
func FeishuSendWeekInfo(c *gin.Context) {
|
||||
// TODO
|
||||
AppConfig, err := util.GetAppConfig(3)
|
||||
Result, err := util.GetOperation(3)
|
||||
if err != nil {
|
||||
log.Printf("failed to get app config: %v", err)
|
||||
log.Printf("failed to get operation: %v", err)
|
||||
return
|
||||
}
|
||||
Db := util.MPool.GetTopicDB(AppConfig.Topic)
|
||||
defer Db.Close()
|
||||
Retain := []*Type.Retain{}
|
||||
|
||||
ZeroTimestamp := util.ZeroTimestampByTz("Europe/London") - 86400
|
||||
ZeroTime := time.Unix(ZeroTimestamp, 0).In(time.UTC)
|
||||
StartDate := ZeroTime.AddDate(0, 0, -30).Format("2006-01-02")
|
||||
EndDate := ZeroTime.Format("2006-01-02")
|
||||
err = Db.Select(&Retain, "SELECT `Date`, `Register`, `SecondRemain`, `ThirdRemain`, `SeventhRemain`, `ThirtiethRemain`, `Recharge`, `Login` FROM remain where `Date` >= ? and `Date` <= ? order by `Date` desc", StartDate, EndDate)
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var Register int
|
||||
err = Db.Get(&Register, "SELECT count(`Uid`) as count FROM log_login WHERE Event = 'register'")
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var Recharge float64
|
||||
err = Db.Get(&Recharge, "SELECT IFNULL(SUM(Price), 0) as sum FROM log_order")
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
var InactiveUsers int
|
||||
err = Db.Get(&InactiveUsers, "SELECT count(distinct Uid) as count from (SELECT Uid, MAX(Timestamp) as LastLogin FROM log_login WHERE Event = 'Login_log' GROUP BY Uid) as lt where lastlogin < ?", ZeroTimestamp-7*86400)
|
||||
if err != nil {
|
||||
log.Printf("GetOperation Select error: %v", err)
|
||||
return
|
||||
}
|
||||
ChurnRate := 100 * float64(InactiveUsers) / float64(Register)
|
||||
Result := &Type.Operation{Retain: Retain, Register: Register, Recharge: Recharge, ChurnRate: ChurnRate}
|
||||
err = common.SendOperationMsg(Result)
|
||||
err = feishu.SendOperationMsg(Result)
|
||||
if err != nil {
|
||||
log.Printf("failed to send operation message: %v", err)
|
||||
}
|
||||
@ -144,7 +52,7 @@ func FeishuUpdateApp(c *gin.Context) {
|
||||
}
|
||||
|
||||
if r.Step == 0 {
|
||||
err := common.SendUpdateCard()
|
||||
err := feishu.SendUpdateCard()
|
||||
if err != nil {
|
||||
log.Printf("failed to send update card: %v", err)
|
||||
}
|
||||
@ -219,7 +127,7 @@ func FeishuServerInfo(c *gin.Context) {
|
||||
}(v)
|
||||
}
|
||||
w.Wait()
|
||||
err = common.SendServerInfoCard(result)
|
||||
err = feishu.SendServerInfoCard(result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package controller
|
||||
import (
|
||||
"backend/Type"
|
||||
"backend/common"
|
||||
"backend/feishu"
|
||||
"backend/model"
|
||||
"backend/util"
|
||||
"fmt"
|
||||
@ -119,6 +120,6 @@ func AppPortNmap() {
|
||||
func AppPortNmap_(App *Type.AppStruct, server *model.ServerInfo) {
|
||||
err := model.PortMap(App.WsHost, fmt.Sprintf("%d", server.ServerId+App.WsPort))
|
||||
if err != nil {
|
||||
common.SendNoticeMsg(App.AppName, server.ServerName, err.Error())
|
||||
feishu.SendNoticeMsg(App.AppName, server.ServerName, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package client
|
||||
import (
|
||||
"backend/feishu/data"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -26,16 +25,15 @@ func GetClient() *client {
|
||||
return C
|
||||
}
|
||||
|
||||
func (c *client) SendMsg(open_id, content string) error {
|
||||
func (c *client) SendMsg(open_id, msg_type, content string) error {
|
||||
// 创建 Client
|
||||
b, _ := json.Marshal(map[string]string{"text": content})
|
||||
uuidVal := uuid.New().String()
|
||||
req := larkim.NewCreateMessageReqBuilder().
|
||||
ReceiveIdType(`open_id`).
|
||||
Body(larkim.NewCreateMessageReqBodyBuilder().
|
||||
ReceiveId(open_id).
|
||||
MsgType(`text`).
|
||||
Content(string(b)).
|
||||
MsgType(msg_type).
|
||||
Content(content).
|
||||
Uuid(uuidVal).
|
||||
Build()).
|
||||
Build()
|
||||
@ -56,16 +54,15 @@ func (c *client) SendMsg(open_id, content string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) SendGroupMsg(chat_id, content string) error {
|
||||
func (c *client) SendGroupMsg(chat_id, msg_type, content string) error {
|
||||
// 创建 Client
|
||||
b, _ := json.Marshal(map[string]string{"text": content})
|
||||
uuidVal := uuid.New().String()
|
||||
req := larkim.NewCreateMessageReqBuilder().
|
||||
ReceiveIdType(`chat_id`).
|
||||
Body(larkim.NewCreateMessageReqBodyBuilder().
|
||||
ReceiveId(chat_id).
|
||||
MsgType(`text`).
|
||||
Content(string(b)).
|
||||
MsgType(msg_type).
|
||||
Content(content).
|
||||
Uuid(uuidVal).
|
||||
Build()).
|
||||
Build()
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
package common
|
||||
package feishu
|
||||
|
||||
import (
|
||||
"backend/Type"
|
||||
"backend/common"
|
||||
"backend/feishu/client"
|
||||
"backend/util"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -23,7 +27,7 @@ func SendFeishuMsg(msg string) error {
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
|
||||
req, err := http.NewRequest("POST", common.GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -76,7 +80,7 @@ func SendNoticeMsg(AppName, ServerName, notice string) error {
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetNoticeUrl(), bytes.NewBuffer(payloadBytes))
|
||||
req, err := http.NewRequest("POST", common.GetNoticeUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -99,110 +103,64 @@ func SendNoticeMsg(AppName, ServerName, notice string) error {
|
||||
}
|
||||
|
||||
// table_raw_array_1
|
||||
func SendOperationMsg(data *Type.Operation) error {
|
||||
// retainStr := ""
|
||||
// for _, v := range data.Retain {
|
||||
// retainStr += fmt.Sprintf("| %s | %d | %d | %.2f | %.2f%% | %.2f%% | %.2f%% | %.2f%% |\n", v.Date, v.Register, v.Login, v.Recharge, 100*float64(v.SecondRemain)/float64(v.Register), 100*float64(v.ThirdRemain)/float64(v.Register), 100*float64(v.SeventhRemain)/float64(v.Register), 100*float64(v.ThirtiethRemain)/float64(v.Register))
|
||||
// }
|
||||
RetainData := make([]map[string]interface{}, 0)
|
||||
for _, v := range data.Retain {
|
||||
var retain2, retain3, retain7 string
|
||||
if v.Register == 0 {
|
||||
retain2 = "0.00%"
|
||||
retain3 = "0.00%"
|
||||
retain7 = "0.00%"
|
||||
} else {
|
||||
retain2 = fmt.Sprintf("%.2f%%", 100*float64(v.SecondRemain)/float64(v.Register))
|
||||
retain3 = fmt.Sprintf("%.2f%%", 100*float64(v.ThirdRemain)/float64(v.Register))
|
||||
retain7 = fmt.Sprintf("%.2f%%", 100*float64(v.SeventhRemain)/float64(v.Register))
|
||||
func SendOperationMsg(Operation *Type.Operation) error {
|
||||
card_md_data := Type.CardMdData{
|
||||
Date: Operation.Date,
|
||||
Register: Operation.Register,
|
||||
Recharge: Operation.Recharge,
|
||||
RechargeUserNum: Operation.RechargeUserNum,
|
||||
ChurnRate: Operation.ChurnRate,
|
||||
ARPU: Operation.ARPU,
|
||||
YRegister: Operation.Retain[0].Register,
|
||||
YRecharge: Operation.Retain[0].Recharge,
|
||||
YLogin: Operation.Retain[0].Login,
|
||||
PerOnlineTime: Operation.Retain[0].PerOnlineTime,
|
||||
PerOrderNum: Operation.Retain[0].PerOrderNum,
|
||||
C1: util.Ternary(Operation.Retain[0].PerOnlineTime > Operation.Retain[1].PerOnlineTime, common.FONT_COLOR_UP, common.FONT_COLOR_DOWN).(string),
|
||||
C2: util.Ternary(Operation.Retain[0].PerOrderNum > Operation.Retain[1].PerOrderNum, common.FONT_COLOR_UP, common.FONT_COLOR_DOWN).(string),
|
||||
}
|
||||
data1 := Type.MarkDown{
|
||||
Markdown: util.ToTmplStr(util.ParseTmpl("./template/card_md_1.tmpl", card_md_data)),
|
||||
}
|
||||
MarkDown := util.ParseTmpl("./template/card_md.tmpl", data1)
|
||||
|
||||
var table_tmpl string
|
||||
var table_tmpl_rows []string
|
||||
for _, v := range Operation.Retain {
|
||||
row_data := Type.RowData{
|
||||
Date: v.Date,
|
||||
Login: v.Login,
|
||||
Register: v.Register,
|
||||
Recharge: v.Recharge,
|
||||
SecondRemain: fmt.Sprintf("%.2f%%", 100*util.FloatDiv(v.SecondRemain, v.Register, 5)),
|
||||
ThirdRemain: fmt.Sprintf("%.2f%%", 100*util.FloatDiv(v.ThirdRemain, v.Register, 5)),
|
||||
SeventhRemain: fmt.Sprintf("%.2f%%", 100*util.FloatDiv(v.SeventhRemain, v.Register, 5)),
|
||||
}
|
||||
RetainData = append(RetainData, map[string]interface{}{
|
||||
"Date": v.Date,
|
||||
"Reg": v.Register,
|
||||
"Login": v.Login,
|
||||
"Pay": v.Recharge,
|
||||
"Retain2": retain2,
|
||||
"Retain3": retain3,
|
||||
"Retain7": retain7,
|
||||
// "Retain14": fmt.Sprintf("%.2f%%", 100*float64(v.ThirtiethRemain)/float64(v.Register)),
|
||||
})
|
||||
}
|
||||
var c1, c2 string
|
||||
if data.Retain[0].PerOnlineTime > data.Retain[1].PerOnlineTime {
|
||||
c1 = FONT_COLOR_UP
|
||||
} else {
|
||||
c1 = FONT_COLOR_DOWN
|
||||
}
|
||||
if data.Retain[0].PerOrderNum > data.Retain[1].PerOrderNum {
|
||||
c2 = FONT_COLOR_UP
|
||||
} else {
|
||||
c2 = FONT_COLOR_DOWN
|
||||
}
|
||||
str := fmt.Sprintf(`
|
||||
# 日期 %s
|
||||
-----------------------------
|
||||
## 总体数据
|
||||
|
||||
- **注册**:%d
|
||||
- **充值**:%.2f
|
||||
- **流失率**:%.2f%%
|
||||
> 超过7天未登录视为流失
|
||||
|
||||
## 昨日数据
|
||||
|
||||
- **注册**:%d
|
||||
- **充值**:%.2f
|
||||
- **登录**:%d
|
||||
- **平均在线时长**:<font color="%s">%.2f</font>分钟
|
||||
- **平均完成订单数**:<font color="%s">%.2f</font>
|
||||
----------------------
|
||||
|
||||
## 留存数据
|
||||
|
||||
`, time.Now().In(time.UTC).Format("2006-01-02"),
|
||||
data.Register, data.Recharge, data.ChurnRate, data.Retain[0].Register, data.Retain[0].Recharge, data.Retain[0].Login,
|
||||
c1, data.Retain[0].PerOnlineTime, c2, data.Retain[0].PerOrderNum)
|
||||
// 创建请求体
|
||||
payload := map[string]interface{}{
|
||||
"msg_type": "interactive",
|
||||
"card": map[string]interface{}{
|
||||
"type": "template",
|
||||
"data": map[string]interface{}{
|
||||
"template_id": "AAqBcfmUwQya1",
|
||||
"template_version_name": "1.0.8",
|
||||
"template_variable": map[string]interface{}{
|
||||
"msg": str,
|
||||
"appName": "merge_pet_london",
|
||||
"table_raw_array_1": RetainData,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
table_tmpl_rows = append(table_tmpl_rows, util.ParseTmpl("./template/card_table_row.tmpl", row_data))
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetOperationUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
table_tmpl_data := Type.CardTable{
|
||||
Rows: strings.Join(table_tmpl_rows, ","),
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
table_tmpl = util.ParseTmpl("./template/card_table.tmpl", table_tmpl_data)
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
elements := []string{
|
||||
MarkDown,
|
||||
table_tmpl,
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
|
||||
elementsStr := strings.Join(elements, ",")
|
||||
data := Type.Card{
|
||||
Title: "运营日报",
|
||||
Elements: elementsStr,
|
||||
Tag1: "UK",
|
||||
}
|
||||
|
||||
s := util.ParseTmpl("./template/card.tmpl", data)
|
||||
if !json.Valid([]byte(s)) {
|
||||
return fmt.Errorf("invalid JSON format")
|
||||
}
|
||||
c := client.GetClient()
|
||||
c.SendGroupMsg(common.GetOperationChatId(), common.FEISHU_CART_TYPE, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -224,7 +182,7 @@ func SendUpdateCard() error {
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetOperationUrl(), bytes.NewBuffer(payloadBytes))
|
||||
req, err := http.NewRequest("POST", common.GetOperationUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -279,7 +237,7 @@ func SendServerInfoCard(data *Type.ServerInfo) error {
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
|
||||
req, err := http.NewRequest("POST", common.GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -364,7 +322,7 @@ func SendWeekOperationMsg(data *Type.Operation) error {
|
||||
}
|
||||
|
||||
// 创建HTTP请求
|
||||
req, err := http.NewRequest("POST", GetOperationUrl(), bytes.NewBuffer(payloadBytes))
|
||||
req, err := http.NewRequest("POST", common.GetOperationUrl(), bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -20,7 +20,7 @@ func Server() {
|
||||
OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
|
||||
log.Printf("[ OnP2MessageReceiveV1 access ], data: %s\n", larkcore.Prettify(event))
|
||||
chat_id := *event.Event.Message.ChatId
|
||||
client.C.SendGroupMsg(chat_id, "hello")
|
||||
client.C.SendGroupMsg(chat_id, "text", "hello")
|
||||
return nil
|
||||
})
|
||||
eventHandler.OnP2BotMenuV6(func(ctx context.Context, event *larkapplication.P2BotMenuV6) error {
|
||||
|
||||
239
log/t.json
Normal file
239
log/t.json
Normal file
@ -0,0 +1,239 @@
|
||||
{
|
||||
"schema": "2.0",
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": "运营周报"
|
||||
},
|
||||
"subtitle": {
|
||||
"tag": "plain_text",
|
||||
"content": ""
|
||||
},
|
||||
"text_tag_list": [
|
||||
{
|
||||
"tag": "text_tag",
|
||||
"element_id": "custom_id_psd1",
|
||||
"text": {
|
||||
"tag": "plain_text",
|
||||
"content": "UK"
|
||||
},
|
||||
"color": "orange"
|
||||
}
|
||||
],
|
||||
"template": "green",
|
||||
"icon": {
|
||||
"tag": "standard_icon",
|
||||
"token": "calendar_colorful",
|
||||
"color": "green"
|
||||
},
|
||||
"padding": "12px 8px 12px 8px"
|
||||
},
|
||||
"body":{
|
||||
"elements": [
|
||||
{
|
||||
"tag": "markdown",
|
||||
"element_id": "custom_md_id_s12",
|
||||
"margin": "0px 0px 0px 0px",
|
||||
"content": "# 日期 2025-03-05 \n-----------------------------\n## 总体数据\n\n- **注册**:1312\n- **充值**:68.87\n- **充值人数**:35\n- **流失率**:77.74%\n- **ARPU**:0.052\n\n> 超过7天未登录视为流失 \n\n---------------------------\n## 昨日数据\n\n- **注册**:4\n- **充值**:0\n- **登录**:64\n- **平均在线时长**:<font color="red">41.08%</font>\n- **平均完成订单数**:<font color="red">8.27</font>\n\n## 留存数据",
|
||||
"text_size": "normal",
|
||||
"text_align": "left"
|
||||
},{
|
||||
"tag": "table",
|
||||
"element_id": "custom_table_id_x12",
|
||||
"margin": "0px 0px 0px 0px",
|
||||
"page_size": 10,
|
||||
"row_height": "low",
|
||||
"row_max_height": "50px",
|
||||
"freeze_first_column": true,
|
||||
"header_style": {
|
||||
"text_align": "center",
|
||||
"text_size": "normal",
|
||||
"background_style": "grey",
|
||||
"text_color": "grey",
|
||||
"bold": true,
|
||||
"lines": 1
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"name": "customer_date",
|
||||
"display_name": "Date",
|
||||
"width": "auto",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center" ,
|
||||
"width": "105px"
|
||||
},
|
||||
{
|
||||
"name": "customer_reg",
|
||||
"display_name": "Reg",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_login",
|
||||
"display_name": "Login",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_pay",
|
||||
"display_name": "Pay",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"format": {
|
||||
"symbol": "$",
|
||||
"precision": 2,
|
||||
"separator": true
|
||||
},
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain2",
|
||||
"display_name": "Retain2",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain3",
|
||||
"display_name": "Retain3",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain7",
|
||||
"display_name": "Retain7",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"customer_date": "2025-03-04",
|
||||
"customer_reg": 4,
|
||||
"customer_login": 64,
|
||||
"customer_pay": 0,
|
||||
"customer_retain2": "0.00%",
|
||||
"customer_retain3": "0.00%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-03-03",
|
||||
"customer_reg": 0,
|
||||
"customer_login": 69,
|
||||
"customer_pay": 0,
|
||||
"customer_retain2": "0.00%",
|
||||
"customer_retain3": "0.00%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-03-02",
|
||||
"customer_reg": 1,
|
||||
"customer_login": 61,
|
||||
"customer_pay": 0.49,
|
||||
"customer_retain2": "0.00%",
|
||||
"customer_retain3": "0.00%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-03-01",
|
||||
"customer_reg": 10,
|
||||
"customer_login": 76,
|
||||
"customer_pay": 2.98,
|
||||
"customer_retain2": "10.00%",
|
||||
"customer_retain3": "0.00%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-02-28",
|
||||
"customer_reg": 12,
|
||||
"customer_login": 59,
|
||||
"customer_pay": 0,
|
||||
"customer_retain2": "33.33%",
|
||||
"customer_retain3": "25.00%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-02-27",
|
||||
"customer_reg": 29,
|
||||
"customer_login": 123,
|
||||
"customer_pay": 3.99,
|
||||
"customer_retain2": "3.45%",
|
||||
"customer_retain3": "3.45%",
|
||||
"customer_retain7": "0.00%"
|
||||
},{
|
||||
"customer_date": "2025-02-26",
|
||||
"customer_reg": 28,
|
||||
"customer_login": 112,
|
||||
"customer_pay": 3.47,
|
||||
"customer_retain2": "25.00%",
|
||||
"customer_retain3": "14.29%",
|
||||
"customer_retain7": "7.14%"
|
||||
},{
|
||||
"customer_date": "2025-02-25",
|
||||
"customer_reg": 38,
|
||||
"customer_login": 136,
|
||||
"customer_pay": 0,
|
||||
"customer_retain2": "21.05%",
|
||||
"customer_retain3": "15.79%",
|
||||
"customer_retain7": "7.90%"
|
||||
},{
|
||||
"customer_date": "2025-02-24",
|
||||
"customer_reg": 23,
|
||||
"customer_login": 111,
|
||||
"customer_pay": 1.47,
|
||||
"customer_retain2": "34.78%",
|
||||
"customer_retain3": "21.74%",
|
||||
"customer_retain7": "17.39%"
|
||||
},{
|
||||
"customer_date": "2025-02-23",
|
||||
"customer_reg": 35,
|
||||
"customer_login": 125,
|
||||
"customer_pay": 0.99,
|
||||
"customer_retain2": "28.57%",
|
||||
"customer_retain3": "25.71%",
|
||||
"customer_retain7": "20.00%"
|
||||
},{
|
||||
"customer_date": "2025-02-22",
|
||||
"customer_reg": 39,
|
||||
"customer_login": 126,
|
||||
"customer_pay": 1.47,
|
||||
"customer_retain2": "30.77%",
|
||||
"customer_retain3": "17.95%",
|
||||
"customer_retain7": "2.56%"
|
||||
},{
|
||||
"customer_date": "2025-02-21",
|
||||
"customer_reg": 62,
|
||||
"customer_login": 163,
|
||||
"customer_pay": 4.45,
|
||||
"customer_retain2": "24.19%",
|
||||
"customer_retain3": "9.68%",
|
||||
"customer_retain7": "9.68%"
|
||||
},{
|
||||
"customer_date": "2025-02-20",
|
||||
"customer_reg": 50,
|
||||
"customer_login": 133,
|
||||
"customer_pay": 0,
|
||||
"customer_retain2": "36.00%",
|
||||
"customer_retain3": "16.00%",
|
||||
"customer_retain7": "16.00%"
|
||||
},{
|
||||
"customer_date": "2025-02-19",
|
||||
"customer_reg": 86,
|
||||
"customer_login": 167,
|
||||
"customer_pay": 7.47,
|
||||
"customer_retain2": "19.77%",
|
||||
"customer_retain3": "13.95%",
|
||||
"customer_retain7": "8.14%"
|
||||
},{
|
||||
"customer_date": "2025-02-18",
|
||||
"customer_reg": 74,
|
||||
"customer_login": 149,
|
||||
"customer_pay": 11.9,
|
||||
"customer_retain2": "35.13%",
|
||||
"customer_retain3": "25.68%",
|
||||
"customer_retain7": "17.57%"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"backend/common"
|
||||
"backend/feishu"
|
||||
"backend/msg"
|
||||
util "backend/util"
|
||||
"fmt"
|
||||
@ -134,7 +135,7 @@ func (s *Server) UpdateApp() (string, error) {
|
||||
DB := util.MPool.GetGameDB()
|
||||
defer DB.Close()
|
||||
DB.Exec("UPDATE app SET `Update` = ? WHERE `AppId` = ?", util.Now(), s.AppId)
|
||||
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
|
||||
feishu.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@ -164,7 +165,7 @@ func (s *Server) UpdateAppFeishu() (string, error) {
|
||||
DB := util.MPool.GetGameDB()
|
||||
defer DB.Close()
|
||||
DB.Exec("UPDATE app SET `Update` = ? WHERE `AppId` = ?", util.Now(), s.AppId)
|
||||
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
|
||||
feishu.SendFeishuMsg(fmt.Sprintf("AppName: %s, 执行文件更新完成", AppConfig.AppName))
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@ -187,7 +188,7 @@ func (s *Server) RestartServer() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 重启完成", AppConfig.AppName, s.ServerName))
|
||||
feishu.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 重启完成", AppConfig.AppName, s.ServerName))
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@ -207,7 +208,7 @@ func (s *Server) ReloadServer() (string, error) {
|
||||
if err != nil {
|
||||
log.Printf("failed to send admin message: %v", err)
|
||||
}
|
||||
common.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 配置重载完成", AppConfig.AppName, s.ServerName))
|
||||
feishu.SendFeishuMsg(fmt.Sprintf("AppName: %s, ServerName: %s, 配置重载完成", AppConfig.AppName, s.ServerName))
|
||||
return "success", nil
|
||||
}
|
||||
|
||||
|
||||
BIN
release/backend
BIN
release/backend
Binary file not shown.
36
template/card.tmpl
Normal file
36
template/card.tmpl
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"schema": "2.0",
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": "{{.Title}}"
|
||||
},
|
||||
"subtitle": {
|
||||
"tag": "plain_text",
|
||||
"content": "{{.Subtitle}}"
|
||||
},
|
||||
"text_tag_list": [
|
||||
{
|
||||
"tag": "text_tag",
|
||||
"element_id": "custom_id_psd1",
|
||||
"text": {
|
||||
"tag": "plain_text",
|
||||
"content": "{{.Tag1}}"
|
||||
},
|
||||
"color": "orange"
|
||||
}
|
||||
],
|
||||
"template": "green",
|
||||
"icon": {
|
||||
"tag": "standard_icon",
|
||||
"token": "calendar_colorful",
|
||||
"color": "green"
|
||||
},
|
||||
"padding": "12px 8px 12px 8px"
|
||||
},
|
||||
"body":{
|
||||
"elements": [
|
||||
{{.Elements}}
|
||||
]
|
||||
}
|
||||
}
|
||||
8
template/card_md.tmpl
Normal file
8
template/card_md.tmpl
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"tag": "markdown",
|
||||
"element_id": "custom_md_id_s12",
|
||||
"margin": "0px 0px 0px 0px",
|
||||
"content": "{{.Markdown}}",
|
||||
"text_size": "normal",
|
||||
"text_align": "left"
|
||||
}
|
||||
22
template/card_md_1.tmpl
Normal file
22
template/card_md_1.tmpl
Normal file
@ -0,0 +1,22 @@
|
||||
# 日期 {{.Date}}
|
||||
-----------------------------
|
||||
## 总体数据
|
||||
|
||||
- **注册**:{{.Register}}
|
||||
- **充值**:{{.Recharge}}
|
||||
- **充值人数**:{{.RechargeUserNum}}
|
||||
- **流失率**:{{.ChurnRate}}%
|
||||
- **ARPU**:{{.ARPU}}
|
||||
|
||||
> 超过7天未登录视为流失
|
||||
|
||||
---------------------------
|
||||
## 昨日数据
|
||||
|
||||
- **注册**:{{.YRegister}}
|
||||
- **充值**:{{.YRecharge}}
|
||||
- **登录**:{{.YLogin}}
|
||||
- **平均在线时长**:<font color="{{.C1}}">{{.PerOnlineTime}}%</font>
|
||||
- **平均完成订单数**:<font color="{{.C2}}">{{.PerOrderNum}}</font>
|
||||
|
||||
## 留存数据
|
||||
77
template/card_table.tmpl
Normal file
77
template/card_table.tmpl
Normal file
@ -0,0 +1,77 @@
|
||||
{
|
||||
"tag": "table",
|
||||
"element_id": "custom_table_id_x12",
|
||||
"margin": "0px 0px 0px 0px",
|
||||
"page_size": 10,
|
||||
"row_height": "low",
|
||||
"row_max_height": "50px",
|
||||
"freeze_first_column": true,
|
||||
"header_style": {
|
||||
"text_align": "center",
|
||||
"text_size": "normal",
|
||||
"background_style": "grey",
|
||||
"text_color": "grey",
|
||||
"bold": true,
|
||||
"lines": 1
|
||||
},
|
||||
"columns": [
|
||||
{
|
||||
"name": "customer_date",
|
||||
"display_name": "Date",
|
||||
"width": "auto",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center" ,
|
||||
"width": "105px"
|
||||
},
|
||||
{
|
||||
"name": "customer_reg",
|
||||
"display_name": "Reg",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_login",
|
||||
"display_name": "Login",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_pay",
|
||||
"display_name": "Pay",
|
||||
"data_type": "number",
|
||||
"horizontal_align": "center",
|
||||
"format": {
|
||||
"symbol": "$",
|
||||
"precision": 2,
|
||||
"separator": true
|
||||
},
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain2",
|
||||
"display_name": "Retain2",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain3",
|
||||
"display_name": "Retain3",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
},
|
||||
{
|
||||
"name": "customer_retain7",
|
||||
"display_name": "Retain7",
|
||||
"data_type": "text",
|
||||
"horizontal_align": "center",
|
||||
"width": "80px"
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{{.Rows}}
|
||||
]
|
||||
}
|
||||
9
template/card_table_row.tmpl
Normal file
9
template/card_table_row.tmpl
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"customer_date": "{{.Date}}",
|
||||
"customer_reg": {{.Register}},
|
||||
"customer_login": {{.Login}},
|
||||
"customer_pay": {{.Recharge}},
|
||||
"customer_retain2": "{{.SecondRemain}}",
|
||||
"customer_retain3": "{{.ThirdRemain}}",
|
||||
"customer_retain7": "{{.SeventhRemain}}"
|
||||
}
|
||||
93
unit_test.go
93
unit_test.go
@ -1,89 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"backend/Type"
|
||||
"backend/common"
|
||||
"backend/controller"
|
||||
"backend/model"
|
||||
"backend/msg"
|
||||
"backend/util"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestXxx1(t *testing.T) {
|
||||
// controller.FeishuSendInfo(nil)
|
||||
str := `
|
||||
## 日期:2021-09-01
|
||||
## 代办事项
|
||||
- [ ] 任务1
|
||||
- [ ] 任务2
|
||||
- [ ] 任务3
|
||||
`
|
||||
str = util.ToTmplStr(str)
|
||||
fmt.Println(str)
|
||||
}
|
||||
func TestXxx(t *testing.T) {
|
||||
controller.FeishuSendInfo(nil)
|
||||
// AppConfig, err := util.GetAppConfig(3)
|
||||
// if err != nil {
|
||||
// log.Printf("failed to get app config: %v", err)
|
||||
// return
|
||||
// }
|
||||
// Db := util.MPool.GetTopicDB(AppConfig.Topic)
|
||||
// defer Db.Close()
|
||||
// ZeroTimestamp := util.ZeroTimestampByTz("Europe/London") - 86400
|
||||
// p := model.GetPerOnlineTime(Db, ZeroTimestamp)
|
||||
// fmt.Println(p)
|
||||
}
|
||||
|
||||
func TestFeishu(t *testing.T) {
|
||||
Db := util.MPool.GetGameDB()
|
||||
var server []*model.ServerInfo
|
||||
defer Db.Close()
|
||||
err := Db.Select(&server, "SELECT `AppId`, `ServerId`, `ServerName`, `Status`, `CreateTime`, `OpenServerTime` FROM server where AppId = ?", 1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var w sync.WaitGroup
|
||||
result := &Type.ServerInfo{}
|
||||
result.ServerList = make([]*Type.Server, 0)
|
||||
for _, v := range server {
|
||||
ws, err := util.GetWebsocket(v.AppId, v.ServerId)
|
||||
if err != nil {
|
||||
v.Status = 0
|
||||
continue
|
||||
}
|
||||
ws.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
w.Add(1)
|
||||
go func(v *model.ServerInfo) {
|
||||
defer w.Done()
|
||||
req := &msg.ReqServerInfo{}
|
||||
r, err := util.SendAdminMsg(ws, req)
|
||||
rs := &Type.Server{}
|
||||
rs.ServerName = v.ServerName
|
||||
if err != nil {
|
||||
log.Printf("failed to send admin message: %v", err)
|
||||
rs.Status = "Inactive"
|
||||
return
|
||||
}
|
||||
rs.Status = "Active"
|
||||
if r["StartTime"] != nil {
|
||||
rs.StartTime = time.Unix(int64(r["StartTime"].(float64)), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if r["PlayerNum"] != nil {
|
||||
rs.PlayerNum = int(r["PlayerNum"].(float64))
|
||||
}
|
||||
if r["TotalAlloc"] != nil {
|
||||
rs.Mem = r["TotalAlloc"].(string)
|
||||
}
|
||||
v.Status = 1
|
||||
result.ServerList = append(result.ServerList, rs)
|
||||
if r["Sys"] != nil {
|
||||
result.Mem = r["Sys"].(string)
|
||||
}
|
||||
if r["CPU"] != nil {
|
||||
result.Cpu = r["CPU"].(string)
|
||||
}
|
||||
ws.Close()
|
||||
}(v)
|
||||
}
|
||||
w.Wait()
|
||||
err = common.SendServerInfoCard(result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
data := struct {
|
||||
Title string
|
||||
Subtitle string
|
||||
Elements string
|
||||
}{
|
||||
Title: "模拟测试",
|
||||
Subtitle: "模拟测试副标题",
|
||||
Elements: "",
|
||||
}
|
||||
s := util.ParseTmpl("./template/card.tmpl", data)
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -205,3 +206,76 @@ func GetPerOnlineTime(db *Db, Zero int64) float64 {
|
||||
}
|
||||
return float64(sum/int64(len(d2))) / 60
|
||||
}
|
||||
|
||||
func GetOperation(AppId int) (*Type.Operation, error) {
|
||||
AppConfig, err := GetAppConfig(AppId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get app config: %v", err)
|
||||
}
|
||||
Db := MPool.GetTopicDB(AppConfig.Topic)
|
||||
defer Db.Close()
|
||||
Retain := []*Type.Retain{}
|
||||
|
||||
ZeroTimestamp := ZeroTimestampByTz("Europe/London") - 86400
|
||||
ZeroTime := time.Unix(ZeroTimestamp, 0).In(time.UTC)
|
||||
StartDate := ZeroTime.AddDate(0, 0, -14).Format("2006-01-02")
|
||||
EndDate := ZeroTime.Format("2006-01-02")
|
||||
err = Db.Select(&Retain, "SELECT `Date`, `Register`, `SecondRemain`, `ThirdRemain`, `SeventhRemain`, `ThirtiethRemain`, `Recharge`, `Login`, `Ext` FROM remain where `Date` >= ? and `Date` <= ? order by `Date` desc", StartDate, EndDate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select data: %v", err)
|
||||
}
|
||||
var Register int
|
||||
err = Db.Get(&Register, "SELECT count(`Uid`) as count FROM log_login WHERE Event = 'register'")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select data: %v", err)
|
||||
}
|
||||
var Recharge float64
|
||||
err = Db.Get(&Recharge, "SELECT IFNULL(SUM(Price), 0) as sum FROM log_order")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select data: %v", err)
|
||||
}
|
||||
var InactiveUsers int
|
||||
err = Db.Get(&InactiveUsers, "SELECT count(distinct Uid) as count from (SELECT Uid, MAX(Timestamp) as LastLogin FROM log_login WHERE Event = 'Login_log' GROUP BY Uid) as lt where lastlogin < ?", ZeroTimestamp-7*86400)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select data: %v", err)
|
||||
}
|
||||
var RechargeUserNum int
|
||||
err = Db.Get(&RechargeUserNum, "SELECT count(distinct Uid) as count FROM log_order")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select data: %v", err)
|
||||
}
|
||||
type ExtStruct struct {
|
||||
PerOnlineTime string `json:"PerOnlineTime"`
|
||||
PerOrderNum string `json:"PerOrderNum"`
|
||||
}
|
||||
for _, v := range Retain {
|
||||
if v.Ext == "" {
|
||||
continue
|
||||
}
|
||||
var d ExtStruct
|
||||
err := json.Unmarshal([]byte(v.Ext), &d)
|
||||
if err != nil {
|
||||
fmt.Printf("err :%s", err.Error())
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseFloat(d.PerOnlineTime, 64)
|
||||
if err == nil {
|
||||
v.PerOnlineTime = value
|
||||
}
|
||||
value, err = strconv.ParseFloat(d.PerOrderNum, 64)
|
||||
if err == nil {
|
||||
v.PerOrderNum = value
|
||||
}
|
||||
}
|
||||
ChurnRate := 100 * float64(InactiveUsers) / float64(Register)
|
||||
R := &Type.Operation{
|
||||
Retain: Retain,
|
||||
Register: Register,
|
||||
Recharge: Decimal(Recharge, 2),
|
||||
ChurnRate: Decimal(ChurnRate, 2),
|
||||
RechargeUserNum: RechargeUserNum,
|
||||
Date: time.Now().In(time.UTC).Format("2006-01-02"),
|
||||
ARPU: FloatDiv(Recharge, Register, 3),
|
||||
}
|
||||
return R, nil
|
||||
}
|
||||
|
||||
79
util/util.go
79
util/util.go
@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"backend/msg"
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
crand "crypto/rand"
|
||||
@ -9,8 +10,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
@ -106,6 +110,49 @@ func FloatDecimals(f float64, n int) float64 {
|
||||
return r
|
||||
}
|
||||
|
||||
// 将两个参数转换成浮点数,如果无法转换则为0,实现两个浮点数的除法,除数为0的话,返回0
|
||||
func FloatDiv(a, b interface{}, Decimal int) float64 {
|
||||
af := toFloat64(a)
|
||||
bf := toFloat64(b)
|
||||
if bf == 0 {
|
||||
return 0
|
||||
}
|
||||
result := af / bf
|
||||
format := fmt.Sprintf("%%.%df", Decimal)
|
||||
r, _ := strconv.ParseFloat(fmt.Sprintf(format, result), 64)
|
||||
return r
|
||||
}
|
||||
|
||||
func Decimal(f float64, n int) float64 {
|
||||
format := fmt.Sprintf("%%.%df", n)
|
||||
r, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
|
||||
return r
|
||||
}
|
||||
|
||||
// 辅助函数:将interface{}转换为float64
|
||||
func toFloat64(v interface{}) float64 {
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
return float64(val)
|
||||
case int32:
|
||||
return float64(val)
|
||||
case int64:
|
||||
return float64(val)
|
||||
case float32:
|
||||
return float64(val)
|
||||
case float64:
|
||||
return val
|
||||
case string:
|
||||
r, err := strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return r
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
SECRET_KEY = ")VQbB(vpy=U(wcp)"
|
||||
)
|
||||
@ -153,3 +200,35 @@ func Decrypt(cipherText string) (string, error) {
|
||||
|
||||
return string(cipherTextBytes), nil
|
||||
}
|
||||
|
||||
func ParseTmpl(file string, data interface{}) string {
|
||||
t, err := template.ParseFiles(file)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse template file: %v", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err = t.Execute(&buf, data)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to execute template: %v", err)
|
||||
}
|
||||
s := buf.String()
|
||||
return s
|
||||
}
|
||||
|
||||
// 将字符串转换成一行的字符串格式,并再进行一次转义输出
|
||||
func ToTmplStr(input string) string {
|
||||
// 转义换行符
|
||||
escapedStr := strings.ReplaceAll(input, "\r", "")
|
||||
escapedStr = strings.ReplaceAll(escapedStr, "\n", "\\n")
|
||||
escapedStr = strings.ReplaceAll(escapedStr, "\"", "\\\"")
|
||||
// 转义输出
|
||||
// return template.HTMLEscapeString(escapedStr)
|
||||
return escapedStr
|
||||
}
|
||||
|
||||
func Ternary(condition bool, trueVal, falseVal interface{}) interface{} {
|
||||
if condition {
|
||||
return trueVal
|
||||
}
|
||||
return falseVal
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user