admin_backend/middleware/feishu/feishu.go
2026-04-15 14:48:13 +08:00

545 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package feishu
import (
"backend/Type"
"backend/common"
"backend/middleware/feishu/client"
"backend/util"
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
)
var NOTIFY_TITLE_COLOR = map[string]string{
"Not classified": "grey",
"Information": "blue",
"Warning": "yellow",
"Average": "orange",
"High": "red",
"Disaster": "carmine",
}
func SendFeishuMsg(msg string) error {
// 创建请求体
payload := map[string]interface{}{
"msg_type": "text",
"content": map[string]string{
"text": msg,
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
// 创建HTTP请求
req, err := http.NewRequest("POST", common.GetFeishuUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
func SendNoticeMsg(AppName, ServerName, notice string) error {
str := fmt.Sprintf(`
## **游戏数据监控异常**
- 项目名称: %s
- 监控项名称: %s
- 异常信息: %s
<at id="all"></at>
`, AppName, ServerName, notice)
// 创建请求体
payload := map[string]interface{}{
"msg_type": "interactive",
"card": map[string]interface{}{
"type": "template",
"data": map[string]interface{}{
"template_id": "AAqBcOkbPmfUZ",
"template_version_name": "1.0.3",
"template_variable": map[string]interface{}{
"appName": AppName,
"msg": str,
},
},
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
// 创建HTTP请求
req, err := http.NewRequest("POST", common.GetNoticeUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
// table_raw_array_1
func SendOperationMsg(Operation *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 Operation.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))
}
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 Operation.Retain[0].PerOnlineTime > Operation.Retain[1].PerOnlineTime {
c1 = common.FONT_COLOR_UP
} else {
c1 = common.FONT_COLOR_DOWN
}
if Operation.Retain[0].PerOrderNum > Operation.Retain[1].PerOrderNum {
c2 = common.FONT_COLOR_UP
} else {
c2 = common.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"),
Operation.Register, Operation.Recharge, Operation.ChurnRate, Operation.Retain[0].Register, Operation.Retain[0].Recharge, Operation.Retain[0].Login,
c1, Operation.Retain[0].PerOnlineTime, c2, Operation.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.10",
"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
}
// 创建HTTP请求
req, err := http.NewRequest("POST", common.GetOperationUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
func SendUpdateCard() error {
payload := map[string]interface{}{
"msg_type": "interactive",
"card": map[string]interface{}{
"type": "template",
"data": map[string]interface{}{
"template_id": "AAqBmWrjPeHVp",
"template_version_name": "1.0.3",
// "template_variable": map[string]interface{}{},
},
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
// 创建HTTP请求
req, err := http.NewRequest("POST", common.GetOperationUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
func SendServerInfoCard(data *Type.ServerInfo) error {
return nil
}
// table_raw_array_1
func SendWeekOperationMsg(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 {
RetainData = append(RetainData, map[string]interface{}{
"Date": v.Date,
"Reg": v.Register,
"Login": v.Login,
"Pay": v.Recharge,
"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)),
// "Retain14": fmt.Sprintf("%.2f%%", 100*float64(v.ThirtiethRemain)/float64(v.Register)),
})
}
str := fmt.Sprintf(`
# 日期 %s
-----------------------------
## 总体数据
- **注册**%d
- **充值**%.2f
- **流失率**%.2f%%
> 超过7天未登录视为流失
## 昨日数据
- **注册**%d
- **充值**%.2f
- **登录**%d
- **平均在线时长**%.2f分钟
- **平均完成订单数**%.2f
----------------------
## 留存数据
`, 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, data.Retain[0].PerOnlineTime, 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.5",
"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
}
// 创建HTTP请求
req, err := http.NewRequest("POST", common.GetOperationUrl(), bytes.NewBuffer(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send message, status code: %d", resp.StatusCode)
}
return nil
}
func SendOperationMsg2(AppId int) error {
App, _ := util.GetAppConfig(AppId)
Operation, err := util.GetOperation(App)
if err != nil {
return err
}
register := 0
remain := 0
for i := 1; i <= 7; i++ {
register += Operation.Retain[i].Register
remain += Operation.Retain[i].SecondRemain
}
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),
RemainRate: fmt.Sprintf("%.2f", float64(remain)/float64(register)),
}
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)),
}
table_tmpl_rows = append(table_tmpl_rows, util.ParseTmpl("./template/card_table_row.tmpl", row_data))
}
table_tmpl_data := Type.CardTable{
Rows: strings.Join(table_tmpl_rows, ","),
}
table_tmpl = util.ParseTmpl("./template/card_table.tmpl", table_tmpl_data)
HeatArr := make([]string, 0)
statisticsData, err := util.GetDailyHeat(AppId)
for k, v := range statisticsData.Key {
HeatArr = append(HeatArr, fmt.Sprintf(`{"time":"%s","value":%d}`, v, statisticsData.Value[k]))
}
if err != nil {
log.Printf("failed to get statistics data: %v", err)
}
VchartArea := util.ParseTmpl("./template/card_vchart_area.tmpl", Type.CardVchartData{Values: strings.Join(HeatArr, ",")})
LevelArr := make([]string, 0)
levelData, _ := util.GetLevelData(AppId)
for k, v := range levelData.Key {
LevelArr = append(LevelArr, fmt.Sprintf(`{"level":"%s","value":%d, "type":"all"}`, v, levelData.Value[k]))
LevelArr = append(LevelArr, fmt.Sprintf(`{"level":"%s","value":%d, "type":"churn"}`, v, levelData.Value2[k]))
}
VchartBar := util.ParseTmpl("./template/card_vchart_bar.tmpl", Type.CardVchartData{Values: strings.Join(LevelArr, ",")})
DecoArr := make([]string, 0)
decoData, _ := util.GetDecorateData(AppId)
for k, v := range decoData.Key {
DecoArr = append(DecoArr, fmt.Sprintf(`{"deco_step":"%s","value":%d, "type":"all"}`, v, decoData.Value[k]))
DecoArr = append(DecoArr, fmt.Sprintf(`{"deco_step":"%s","value":%d, "type":"churn"}`, v, decoData.Value2[k]))
}
VchartBarDeco := util.ParseTmpl("./template/card_vchart_bar_d.tmpl", Type.CardVchartData{Values: strings.Join(DecoArr, ",")})
hr := util.ParseTmpl("./template/card_hr.tmpl", nil)
elements := []string{
MarkDown,
table_tmpl,
hr,
VchartArea,
hr,
VchartBar,
hr,
VchartBarDeco,
}
elementsStr := strings.Join(elements, ",")
data := Type.Card{
Title: "运营周报",
Elements: elementsStr,
Tag1: "US",
}
s := util.ParseTmpl("./template/card.tmpl", data)
if !json.Valid([]byte(s)) {
fmt.Println(s)
return fmt.Errorf("invalid JSON format")
}
c := client.GetClient()
c.SendGroupMsg(common.GetUSOperationChatId(), common.FEISHU_CART_TYPE, s)
return nil
}
func SendNotifyMsg(data *Type.NotifyData) error {
TitleColor := NOTIFY_TITLE_COLOR[data.Severity]
payload := map[string]interface{}{
"type": "template",
"data": map[string]interface{}{
"template_id": "AAqB4RTngW8Wt",
"template_version_name": "1.0.7",
"template_variable": map[string]interface{}{
"Title": "待处理",
"TitleColor": TitleColor,
"InputMsg": "处理情况说明,选填",
"HostName": data.Host,
"EventName": data.EventName,
"EventSeverity": data.Severity,
"alarm_time": data.AlarmTime,
"NotifyMsg": data.NotifyMsg,
},
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
c := client.GetClient()
err = c.SendGroupMsg(common.GetOperationChatId(), common.FEISHU_CART_TYPE, string(payloadBytes))
if err != nil {
log.Printf("sendNotifyMsg error %v", err)
}
return nil
}
// AAq9lf37SuuDP
func SendNotifyOrderMsg(data *Type.OrderData) error {
payload := map[string]interface{}{
"type": "template",
"data": map[string]interface{}{
"template_id": "AAq9lf37SuuDP",
"template_version_name": "1.0.1",
"template_variable": map[string]interface{}{
"UID": data.UID,
"OrderId": data.OrderId,
"Product": data.Product,
"ProductName": data.ProductName,
"EventRecovery": data.EventRecovery,
"EventAge": data.EventAge,
},
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
c := client.GetClient()
err = c.SendGroupMsg(common.GetUSOperationChatId(), common.FEISHU_CART_TYPE, string(payloadBytes))
if err != nil {
log.Printf("sendNotifyMsg error %v", err)
}
return nil
}
func SendNotifyClientMsg(data *Type.NotifyClientData) error {
payload := map[string]interface{}{
"type": "template",
"data": map[string]interface{}{
"template_id": "AAqR0eWBmiN6z",
"template_version_name": "1.0.7",
"template_variable": map[string]interface{}{
"log": data.Log,
"stackTrace": data.StackTrace,
"deviceModel": data.DeviceModel,
"operatingSystem": data.OperatingSystem,
"AlarmTime": data.AlarmTime,
"deviceUniqueIdentifier": data.DeviceUniqueIdentifier,
},
},
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}
c := client.GetClient()
err = c.SendGroupMsg(common.GetClientChatId(), common.FEISHU_CART_TYPE, string(payloadBytes))
if err != nil {
log.Printf("sendNotifyMsg error %v", err)
}
return nil
}