Skip to content

Commit

Permalink
Merge branch 'main' into notice
Browse files Browse the repository at this point in the history
  • Loading branch information
SchwarzSail committed Jan 11, 2025
2 parents c1c61d5 + 401ad9f commit 31502ea
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 0 deletions.
6 changes: 6 additions & 0 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ upyuns:
token-timeout: 1800
path: "/statistic/html/"

umeng:
app_key: ""
message_secret: ""
app_master_secret: ""
package_name: ""

elasticsearch:
addr: 127.0.0.1:9200
host: 127.0.0.1
Expand Down
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var (
Elasticsearch *elasticsearch
Kafka *kafka
UpYun *upyun
Umeng *umeng
VersionUploadService *url
runtimeViper = viper.New()
)
Expand Down Expand Up @@ -103,6 +104,7 @@ func configMapping(srv string) {
Kafka = &c.Kafka
DefaultUser = &c.DefaultUser
VersionUploadService = &c.Url
Umeng = &c.Umeng
if upy, ok := c.UpYuns[srv]; ok {
UpYun = &upy
}
Expand Down
8 changes: 8 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ type upyun struct {
Path string
}

type umeng struct {
AppKey string `mapstructure:"app_key"`
MessageSecret string `mapstructure:"message_secret"`
AppMasterSecret string `mapstructure:"app_master_secret"`
PackageName string `mapstructure:"package_name"`
}

type config struct {
Server server
Snowflake snowflake
Expand All @@ -133,5 +140,6 @@ type config struct {
Kafka kafka
DefaultUser defaultUser
UpYuns map[string]upyun
Umeng umeng
Url url
}
2 changes: 2 additions & 0 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const (

// ValidateCodeURL 获取验证码结果的本地python服务url,需要保证 login-verify 和 api 处于同一个 dokcer 网络中
ValidateCodeURL = "http://login-verify:8081/api/v1/jwch/user/validateCode"
// UmengURL 友盟推送 API
UmengURL = "https://msgapi.umeng.com/api/send"
)

const (
Expand Down
129 changes: 129 additions & 0 deletions pkg/umeng/broadcast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
Copyright 2024 The west2-online Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package umeng

import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/west2-online/fzuhelper-server/pkg/constants"
"github.com/west2-online/fzuhelper-server/pkg/errno"
"github.com/west2-online/fzuhelper-server/pkg/logger"
)

// Android广播函数
func SendAndroidBroadcast(appKey, appMasterSecret, ticker, title, text, expireTime string) error {
message := AndroidBroadcastMessage{
AppKey: appKey,
Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
Type: "broadcast",
Payload: AndroidPayload{
DisplayType: "notification",
Body: AndroidBody{
Ticker: ticker,
Title: title,
Text: text,
AfterOpen: "go_app",
},
},
Policy: Policy{
ExpireTime: expireTime,
},
ChannelProperties: map[string]string{
"channel_activity": "xxx",
},
Description: "测试广播通知-Android",
}

return sendBroadcast(appMasterSecret, message)
}

// iOS广播函数
func SendIOSBroadcast(appKey, appMasterSecret, title, subtitle, body, expireTime string) error {
message := IOSBroadcastMessage{
AppKey: appKey,
Timestamp: fmt.Sprintf("%d", time.Now().Unix()),
Type: "broadcast",
Payload: IOSPayload{
Aps: IOSAps{
Alert: IOSAlert{
Title: title,
Subtitle: subtitle,
Body: body,
},
},
},
Policy: Policy{
ExpireTime: expireTime,
},
Description: "测试广播通知-iOS",
}

return sendBroadcast(appMasterSecret, message)
}

// 通用广播发送逻辑
func sendBroadcast(appMasterSecret string, message interface{}) error {
postBody, err := json.Marshal(message)
if err != nil {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : failed to marshal JSON: %v", err)
}

sign := generateSign("POST", constants.UmengURL, string(postBody), appMasterSecret)

req, err := http.NewRequest("POST", constants.UmengURL+"?sign="+sign, bytes.NewBuffer(postBody))
if err != nil {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : failed to create request: %v", err)
}
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : failed to send request: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : unexpected response code: %v", resp.StatusCode)
}

var response UmengResponse
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : failed to decode response: %v", err)
}

if response.Ret != "SUCCESS" {
return errno.Errorf(errno.InternalServiceErrorCode, "umeng.sendBroadcast : broadcast failed: %s (%s)", response.Data.ErrorMsg, response.Data.ErrorCode)
}

logger.Infof("Broadcast sent successfully! MsgID: %s\n", response.Data.MsgID)
return nil
}

// 生成MD5签名
func generateSign(method, url, postBody, appMasterSecret string) string {
data := fmt.Sprintf("%s%s%s%s", method, url, postBody, appMasterSecret)
hash := md5.Sum([]byte(data))
return hex.EncodeToString(hash[:])
}
79 changes: 79 additions & 0 deletions pkg/umeng/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2024 The west2-online Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package umeng

// 公共返回结构
type UmengResponse struct {
Ret string `json:"ret"`
Data struct {
MsgID string `json:"msg_id,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
ErrorMsg string `json:"error_msg,omitempty"`
} `json:"data"`
}

// Android广播消息结构
type AndroidBroadcastMessage struct {
AppKey string `json:"appkey"`
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Payload AndroidPayload `json:"payload"`
Policy Policy `json:"policy"`
ChannelProperties map[string]string `json:"channel_properties"`
Description string `json:"description"`
}

type AndroidPayload struct {
DisplayType string `json:"display_type"`
Body AndroidBody `json:"body"`
}

type AndroidBody struct {
Ticker string `json:"ticker"`
Title string `json:"title"`
Text string `json:"text"`
AfterOpen string `json:"after_open"`
}

// iOS广播消息结构
type IOSBroadcastMessage struct {
AppKey string `json:"appkey"`
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Payload IOSPayload `json:"payload"`
Policy Policy `json:"policy"`
Description string `json:"description"`
}

type IOSPayload struct {
Aps IOSAps `json:"aps"`
}

type IOSAps struct {
Alert IOSAlert `json:"alert"`
}

type IOSAlert struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Body string `json:"body"`
}

// 公共策略结构
type Policy struct {
ExpireTime string `json:"expire_time"`
}

0 comments on commit 31502ea

Please sign in to comment.