From f59a6597f7b318f6ef7d4c67325249349697e34f Mon Sep 17 00:00:00 2001 From: JustSong Date: Sat, 5 Nov 2022 15:49:39 +0800 Subject: [PATCH] Able to set menu now --- common/access-token-store.go | 9 +++++- common/constants.go | 9 ++++++ controller/misc.go | 32 --------------------- controller/option.go | 29 +++++++++++++++++++ controller/wechat.go | 44 +++++++++++++++++++++++++++++ model/option.go | 3 ++ web/src/components/WeChatSetting.js | 34 +++++++++++++++++++++- 7 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 controller/wechat.go diff --git a/common/access-token-store.go b/common/access-token-store.go index ba01f95..022e5d9 100644 --- a/common/access-token-store.go +++ b/common/access-token-store.go @@ -48,6 +48,7 @@ func RefreshAccessToken() { SysError("failed to refresh access token: " + err.Error()) return } + defer responseData.Body.Close() var res response err = json.NewDecoder(responseData.Body).Decode(&res) if err != nil { @@ -61,8 +62,14 @@ func RefreshAccessToken() { SysLog("access token refreshed") } -func GetAccessToken() (string, int) { +func GetAccessTokenAndExpirationSeconds() (string, int) { s.Mutex.RLock() defer s.Mutex.RUnlock() return s.AccessToken, s.ExpirationSeconds } + +func GetAccessToken() string { + s.Mutex.RLock() + defer s.Mutex.RUnlock() + return s.AccessToken +} diff --git a/common/constants.go b/common/constants.go index b20fe13..35b8220 100644 --- a/common/constants.go +++ b/common/constants.go @@ -19,6 +19,15 @@ var WeChatAppID = "" var WeChatAppSecret = "" var WeChatEncodingAESKey = "" var WeChatOwnerID = "" +var WeChatMenu = `{ + "button": [ + { + "type": "click", + "name": "登录验证", + "key": "USER_VERIFICATION" + } + ] +}` var SessionSecret = uuid.New().String() var SQLitePath = ".wechat-server.db" diff --git a/controller/misc.go b/controller/misc.go index fb7c5ed..3d40e28 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -1,13 +1,9 @@ package controller import ( - "crypto/sha1" - "encoding/hex" "fmt" "github.com/gin-gonic/gin" "net/http" - "sort" - "strings" "wechat-server/common" "wechat-server/model" ) @@ -40,34 +36,6 @@ func GetNotice(c *gin.Context) { return } -func WeChatVerification(c *gin.Context) { - // https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html - signature := c.Query("signature") - timestamp := c.Query("timestamp") - nonce := c.Query("nonce") - echoStr := c.Query("echostr") - arr := []string{common.WeChatToken, timestamp, nonce} - sort.Strings(arr) - str := strings.Join(arr, "") - hash := sha1.Sum([]byte(str)) - hexStr := hex.EncodeToString(hash[:]) - if signature == hexStr { - c.String(http.StatusOK, echoStr) - } else { - c.Status(http.StatusForbidden) - } -} - -func GetAccessToken(c *gin.Context) { - accessToken, expiration := common.GetAccessToken() - c.JSON(http.StatusOK, gin.H{ - "success": true, - "message": "", - "access_token": accessToken, - "expiration": expiration, - }) -} - func SendEmailVerification(c *gin.Context) { email := c.Query("email") if err := common.Validate.Var(email, "required,email"); err != nil { diff --git a/controller/option.go b/controller/option.go index d4e7729..5fd0e27 100644 --- a/controller/option.go +++ b/controller/option.go @@ -1,7 +1,9 @@ package controller import ( + "bytes" "encoding/json" + "fmt" "github.com/gin-gonic/gin" "net/http" "strings" @@ -55,6 +57,33 @@ func UpdateOption(c *gin.Context) { }) return } + if option.Key == "WeChatMenu" { + httpResponse, err := http.Post(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s", common.GetAccessToken()), "application/json", bytes.NewBuffer([]byte(option.Value))) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + defer httpResponse.Body.Close() + var res wechatResponse + err = json.NewDecoder(httpResponse.Body).Decode(&res) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } + if res.ErrCode != 0 { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": res.ErrMsg, + }) + return + } + } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", diff --git a/controller/wechat.go b/controller/wechat.go new file mode 100644 index 0000000..211f7d4 --- /dev/null +++ b/controller/wechat.go @@ -0,0 +1,44 @@ +package controller + +import ( + "crypto/sha1" + "encoding/hex" + "github.com/gin-gonic/gin" + "net/http" + "sort" + "strings" + "wechat-server/common" +) + +type wechatResponse struct { + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` +} + +func WeChatVerification(c *gin.Context) { + // https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html + signature := c.Query("signature") + timestamp := c.Query("timestamp") + nonce := c.Query("nonce") + echoStr := c.Query("echostr") + arr := []string{common.WeChatToken, timestamp, nonce} + sort.Strings(arr) + str := strings.Join(arr, "") + hash := sha1.Sum([]byte(str)) + hexStr := hex.EncodeToString(hash[:]) + if signature == hexStr { + c.String(http.StatusOK, echoStr) + } else { + c.Status(http.StatusForbidden) + } +} + +func GetAccessToken(c *gin.Context) { + accessToken, expiration := common.GetAccessTokenAndExpirationSeconds() + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + "access_token": accessToken, + "expiration": expiration, + }) +} diff --git a/model/option.go b/model/option.go index 51c895f..99695e7 100644 --- a/model/option.go +++ b/model/option.go @@ -43,6 +43,7 @@ func InitOptionMap() { common.OptionMap["WeChatAppSecret"] = "" common.OptionMap["WeChatEncodingAESKey"] = "" common.OptionMap["WeChatOwnerID"] = "" + common.OptionMap["WeChatMenu"] = common.WeChatMenu common.OptionMapRWMutex.Unlock() options, _ := AllOption() for _, option := range options { @@ -121,5 +122,7 @@ func updateOptionMap(key string, value string) { common.WeChatEncodingAESKey = value case "WeChatOwnerID": common.WeChatOwnerID = value + case "WeChatMenu": + common.WeChatMenu = value } } diff --git a/web/src/components/WeChatSetting.js b/web/src/components/WeChatSetting.js index f1cd7ff..3b412f0 100644 --- a/web/src/components/WeChatSetting.js +++ b/web/src/components/WeChatSetting.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Form, Grid } from 'semantic-ui-react'; import { API, showError } from '../helpers'; +import { Link } from 'react-router-dom'; const WeChatSetting = () => { let [inputs, setInputs] = useState({ @@ -9,6 +10,7 @@ const WeChatSetting = () => { WeChatAppSecret: '', WeChatEncodingAESKey: '', WeChatOwnerID: '', + WeChatMenu: '', }); let [loading, setLoading] = useState(false); @@ -48,7 +50,15 @@ const WeChatSetting = () => { }; const handleInputChange = async (e, { name, value }) => { - await updateOption(name, value); + if (name === 'WeChatMenu') { + setInputs((inputs) => ({ ...inputs, [name]: value })); + } else { + await updateOption(name, value); + } + }; + + const submitWeChatMenu = async () => { + await updateOption('WeChatMenu', inputs.WeChatMenu); }; return ( @@ -100,6 +110,28 @@ const WeChatSetting = () => { onChange={handleInputChange} /> + + + 公众号菜单( + + 格式请参考此处 + + ) +

+ } + placeholder="JSON 格式" + value={inputs.WeChatMenu} + name="WeChatMenu" + onChange={handleInputChange} + style={{ minHeight: 150, fontFamily: 'JetBrains Mono, Consolas' }} + /> +
+ 更新公众号菜单