فهرست منبع

支持公众号账号迁移,获取openID变化接口 (#370)

* * 公众号菜单管理,set相关函数,返回btn本身,方便以字面量的方式创建多个菜单,更直观,方便管理

* * golangci-lint fix

* * 获取二维码ticket接口没有往上抛接口错误

* * 增加GetOpenID方法,以获取消息的生产用户openID

* * 支持公众号账号迁移,获取openID变化接口

* * bugfix

* * golint fix

* * golint fix
GargantuaX 5 سال پیش
والد
کامیت
e7fdcf9534
4فایلهای تغییر یافته به همراه147 افزوده شده و 1 حذف شده
  1. 87 0
      officialaccount/user/migrate.go
  2. 1 1
      officialaccount/user/user.go
  3. 41 0
      util/util.go
  4. 18 0
      util/util_test.go

+ 87 - 0
officialaccount/user/migrate.go

@@ -0,0 +1,87 @@
+//Package user migrate 用于微信公众号账号迁移,获取openID变化
+//参考文档:https://kf.qq.com/faq/1901177NrqMr190117nqYJze.html
+package user
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/silenceper/wechat/v2/util"
+)
+
+const (
+	changeOpenIDURL = "https://api.weixin.qq.com/cgi-bin/changeopenid"
+)
+
+// ChangeOpenIDResult OpenID迁移变化
+type ChangeOpenIDResult struct {
+	OriOpenID string `json:"ori_openid"`
+	NewOpenID string `json:"new_openid"`
+	ErrMsg    string `json:"err_msg,omitempty"`
+}
+
+// ChangeOpenIDResultList OpenID迁移变化列表
+type ChangeOpenIDResultList struct {
+	util.CommonError
+	List []ChangeOpenIDResult `json:"result_list"`
+}
+
+// ListChangeOpenIDs 返回指定OpenID变化列表
+// fromAppID 为老账号AppID
+// openIDs 为老账号的openID,openIDs限100个以内
+// AccessToken 为新账号的AccessToken
+func (user *User) ListChangeOpenIDs(fromAppID string, openIDs ...string) (list *ChangeOpenIDResultList, err error) {
+	list = &ChangeOpenIDResultList{}
+	//list.List = make([]ChangeOpenIDResult, 0)
+	if len(openIDs) > 100 {
+		err = errors.New("openIDs length must be lt 100")
+		return
+	}
+
+	if fromAppID == "" {
+		err = errors.New("fromAppID is required")
+		return
+	}
+
+	accessToken, err := user.GetAccessToken()
+	if err != nil {
+		return
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", changeOpenIDURL, accessToken)
+	var resp []byte
+	var req struct {
+		FromAppID  string   `json:"from_appid"`
+		OpenidList []string `json:"openid_list"`
+	}
+	req.FromAppID = fromAppID
+	req.OpenidList = append(req.OpenidList, openIDs...)
+	resp, err = util.PostJSON(uri, req)
+	if err != nil {
+		return
+	}
+
+	err = util.DecodeWithError(resp, list, "ListChangeOpenIDs")
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// ListAllChangeOpenIDs  返回所有用户OpenID列表
+// fromAppID 为老账号AppID
+// openIDs 为老账号的openID
+// AccessToken 为新账号的AccessToken
+func (user *User) ListAllChangeOpenIDs(fromAppID string, openIDs ...string) (list []ChangeOpenIDResult, err error) {
+	list = make([]ChangeOpenIDResult, 0)
+	chunks := util.SliceChunk(openIDs, 100)
+	for _, chunk := range chunks {
+		result, err := user.ListChangeOpenIDs(fromAppID, chunk...)
+		if err != nil {
+			return list, err
+		}
+		list = append(list, result.List...)
+	}
+	return
+}

+ 1 - 1
officialaccount/user/user.go

@@ -139,7 +139,7 @@ func (user *User) ListUserOpenIDs(nextOpenid ...string) (*OpenidList, error) {
 // ListAllUserOpenIDs 返回所有用户OpenID列表
 func (user *User) ListAllUserOpenIDs() ([]string, error) {
 	nextOpenid := ""
-	openids := []string{}
+	openids := make([]string, 0)
 	count := 0
 	for {
 		ul, err := user.ListUserOpenIDs(nextOpenid)

+ 41 - 0
util/util.go

@@ -0,0 +1,41 @@
+package util
+
+//SliceChunk 用于将字符串切片分块
+func SliceChunk(src []string, chunkSize int) (chunks [][]string) {
+	total := len(src)
+	chunks = make([][]string, 0)
+	if chunkSize < 1 {
+		chunkSize = 1
+	}
+	if total == 0 {
+		return
+	}
+
+	chunkNum := total / chunkSize
+	if total%chunkSize != 0 {
+		chunkNum++
+	}
+
+	chunks = make([][]string, chunkNum)
+
+	for i := 0; i < chunkNum; i++ {
+		for j := 0; j < chunkSize; j++ {
+			offset := i*chunkSize + j
+			if offset >= total {
+				return
+			}
+
+			if chunks[i] == nil {
+				actualChunkSize := chunkSize
+				if i == chunkNum-1 && total%chunkSize != 0 {
+					actualChunkSize = total % chunkSize
+				}
+				chunks[i] = make([]string, actualChunkSize)
+			}
+
+			chunks[i][j] = src[offset]
+		}
+	}
+
+	return
+}

+ 18 - 0
util/util_test.go

@@ -0,0 +1,18 @@
+package util
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSliceChunk(t *testing.T) {
+	src1 := []string{"1", "2", "3", "4", "5"}
+	assert.Equal(t, [][]string{{"1", "2"}, {"3", "4"}, {"5"}}, SliceChunk(src1, 2))
+	assert.Equal(t, [][]string{{"1", "2", "3", "4", "5"}}, SliceChunk(src1, 5))
+	assert.Equal(t, [][]string{{"1", "2", "3", "4", "5"}}, SliceChunk(src1, 6))
+	assert.Equal(t, [][]string{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}, SliceChunk(src1, 1))
+	assert.Equal(t, [][]string{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}, SliceChunk(src1, 0))
+	assert.Equal(t, [][]string{{"1"}, {"2"}, {"3"}, {"4"}, {"5"}}, SliceChunk(src1, -100))
+	assert.Equal(t, [][]string{}, SliceChunk(nil, 5))
+}