Преглед на файлове

实现access_token获取

wenzl преди 9 години
родител
ревизия
d9075933c1
променени са 7 файла, в които са добавени 195 реда и са изтрити 4 реда
  1. 11 0
      cache/cache.go
  2. 51 0
      cache/memcache.go
  3. 28 0
      cache/memcache_test.go
  4. 71 0
      context/access_token.go
  5. 10 4
      context/context.go
  6. 19 0
      util/http.go
  7. 5 0
      wechat.go

+ 11 - 0
cache/cache.go

@@ -0,0 +1,11 @@
+package cache
+
+import "time"
+
+//Cache interface
+type Cache interface {
+	Get(key string) interface{}
+	Set(key string, val interface{}, timeput time.Duration) error
+	IsExist(key string) bool
+	Delete(key string) error
+}

+ 51 - 0
cache/memcache.go

@@ -0,0 +1,51 @@
+package cache
+
+import (
+	"errors"
+	"time"
+
+	"github.com/bradfitz/gomemcache/memcache"
+)
+
+//Memcache struct contains *memcache.Client
+type Memcache struct {
+	conn *memcache.Client
+}
+
+//NewMemcache create new memcache
+func NewMemcache(server ...string) *Memcache {
+	mc := memcache.New(server...)
+	return &Memcache{mc}
+}
+
+//Get return cached value
+func (mem *Memcache) Get(key string) interface{} {
+	if item, err := mem.conn.Get(key); err == nil {
+		return string(item.Value)
+	}
+	return nil
+}
+
+// IsExist check value exists in memcache.
+func (mem *Memcache) IsExist(key string) bool {
+	_, err := mem.conn.Get(key)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+//Set cached value with key and expire time.
+func (mem *Memcache) Set(key string, val interface{}, timeout time.Duration) error {
+	v, ok := val.(string)
+	if !ok {
+		return errors.New("val must string")
+	}
+	item := &memcache.Item{Key: key, Value: []byte(v), Expiration: int32(timeout / time.Second)}
+	return mem.conn.Set(item)
+}
+
+//Delete delete value in memcache.
+func (mem *Memcache) Delete(key string) error {
+	return mem.conn.Delete(key)
+}

+ 28 - 0
cache/memcache_test.go

@@ -0,0 +1,28 @@
+package cache
+
+import (
+	"testing"
+	"time"
+)
+
+func TestMemcache(t *testing.T) {
+	mem := NewMemcache("127.0.0.1:11211")
+	var err error
+	timeoutDuration := 10 * time.Second
+	if err = mem.Set("username", "silenceper", timeoutDuration); err != nil {
+		t.Error("set Error", err)
+	}
+
+	if !mem.IsExist("username") {
+		t.Error("IsExist Error")
+	}
+
+	name := mem.Get("username").(string)
+	if name != "silenceper" {
+		t.Error("get Error")
+	}
+
+	if err = mem.Delete("username"); err != nil {
+		t.Errorf("delete Error , err=%v", err)
+	}
+}

+ 71 - 0
context/access_token.go

@@ -0,0 +1,71 @@
+package context
+
+import (
+	"encoding/json"
+	"fmt"
+	"sync"
+	"time"
+
+	"github.com/silenceper/wechat/util"
+)
+
+const (
+	//AccessTokenURL 获取access_token的接口
+	AccessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
+)
+
+//ResAccessToken struct
+type ResAccessToken struct {
+	ErrorCode int32  `json:"errcode"`
+	ErrorMsg  string `json:"errmsg"`
+
+	AccessToken string `json:"access_token"`
+	ExpiresIn   int32  `json:"expires_in"`
+}
+
+//SetAccessTokenLock 设置读写锁(一个appID一个读写锁)
+func (ctx *Context) SetAccessTokenLock(l *sync.RWMutex) {
+	ctx.accessTokenLock = l
+}
+
+//GetAccessToken 获取access_token
+func (ctx *Context) GetAccessToken() (accessToken string, err error) {
+	ctx.accessTokenLock.Lock()
+	defer ctx.accessTokenLock.Unlock()
+
+	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
+	val := ctx.Cache.Get(accessTokenCacheKey)
+	if val != nil {
+		accessToken = val.(string)
+		return
+	}
+
+	//从微信服务器获取
+	var resAccessToken ResAccessToken
+	resAccessToken, err = ctx.GetAccessTokenFromServer()
+	if err != nil {
+		return
+	}
+
+	accessToken = resAccessToken.AccessToken
+	return
+}
+
+//GetAccessTokenFromServer 强制从微信服务器获取token
+func (ctx *Context) GetAccessTokenFromServer() (resAccessToken ResAccessToken, err error) {
+	url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", AccessTokenURL, ctx.AppID, ctx.AppSecret)
+	var body []byte
+	body, err = util.HTTPGet(url)
+	err = json.Unmarshal(body, &resAccessToken)
+	if err != nil {
+		return
+	}
+	if resAccessToken.ErrorMsg != "" {
+		err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrorCode, resAccessToken.ErrorMsg)
+		return
+	}
+
+	accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
+	err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(resAccessToken.ExpiresIn)-1500/time.Second)
+	return
+}

+ 10 - 4
context/context.go

@@ -1,6 +1,11 @@
 package context
 
-import "net/http"
+import (
+	"net/http"
+	"sync"
+
+	"github.com/silenceper/wechat/cache"
+)
 
 //Context struct
 type Context struct {
@@ -9,12 +14,13 @@ type Context struct {
 	Token          string
 	EncodingAESKey string
 
+	Cache cache.Cache
+
 	Writer  http.ResponseWriter
 	Request *http.Request
-}
-
-func (ctx *Context) getAccessToken() {
 
+	//accessTokenLock 读写锁 同一个AppID一个
+	accessTokenLock *sync.RWMutex
 }
 
 // Query returns the keyed url query value if it exists

+ 19 - 0
util/http.go

@@ -0,0 +1,19 @@
+package util
+
+import (
+	"io/ioutil"
+	"net/http"
+)
+
+//HTTPGet get 请求
+func HTTPGet(url string) ([]byte, error) {
+	response, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+	return ioutil.ReadAll(response.Body)
+}
+
+//HTTPPost post 请求
+func HTTPPost() {
+}

+ 5 - 0
wechat.go

@@ -2,7 +2,9 @@ package wechat
 
 import (
 	"net/http"
+	"sync"
 
+	"github.com/silenceper/wechat/cache"
 	"github.com/silenceper/wechat/context"
 	"github.com/silenceper/wechat/server"
 )
@@ -18,6 +20,7 @@ type Config struct {
 	AppSecret      string
 	Token          string
 	EncodingAESKey string
+	Cache          cache.Cache
 }
 
 //NewWechat init
@@ -32,6 +35,8 @@ func copyConfigToContext(cfg *Config, context *context.Context) {
 	context.AppSecret = cfg.AppSecret
 	context.Token = cfg.Token
 	context.EncodingAESKey = cfg.EncodingAESKey
+	context.Cache = cfg.Cache
+	context.SetAccessTokenLock(new(sync.RWMutex))
 }
 
 //GetServer init