diff --git a/officialaccount/user/migrate.go b/officialaccount/user/migrate.go new file mode 100644 index 0000000..97e63f0 --- /dev/null +++ b/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 +} diff --git a/officialaccount/user/user.go b/officialaccount/user/user.go index d802019..8cffa36 100644 --- a/officialaccount/user/user.go +++ b/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) diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..f18d6fb --- /dev/null +++ b/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 +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 0000000..ac4d844 --- /dev/null +++ b/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)) +}