mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-23 13:42:25 +08:00
Compare commits
7 Commits
39dedd6193
...
v2.1.6-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49c4cfaf54 | ||
|
|
ead8a6fadb | ||
|
|
ae40639b56 | ||
|
|
8bb145155e | ||
|
|
85bf989242 | ||
|
|
b4f2d1793c | ||
|
|
4a2c44c7c8 |
@@ -66,8 +66,9 @@ func (ak *DefaultAccessToken) GetAccessToken() (accessToken string, err error) {
|
|||||||
func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (accessToken string, err error) {
|
func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (accessToken string, err error) {
|
||||||
// 先从cache中取
|
// 先从cache中取
|
||||||
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID)
|
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID)
|
||||||
if val := ak.cache.Get(accessTokenCacheKey); val != nil {
|
val := ak.cache.Get(accessTokenCacheKey)
|
||||||
return val.(string), nil
|
if accessToken = val.(string); accessToken != "" {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加上lock,是为了防止在并发获取token时,cache刚好失效,导致从微信服务器上获取到不同token
|
// 加上lock,是为了防止在并发获取token时,cache刚好失效,导致从微信服务器上获取到不同token
|
||||||
@@ -75,8 +76,9 @@ func (ak *DefaultAccessToken) GetAccessTokenContext(ctx context.Context) (access
|
|||||||
defer ak.accessTokenLock.Unlock()
|
defer ak.accessTokenLock.Unlock()
|
||||||
|
|
||||||
// 双检,防止重复从微信服务器获取
|
// 双检,防止重复从微信服务器获取
|
||||||
if val := ak.cache.Get(accessTokenCacheKey); val != nil {
|
val = ak.cache.Get(accessTokenCacheKey)
|
||||||
return val.(string), nil
|
if accessToken = val.(string); accessToken != "" {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache失效,从微信服务器获取
|
// cache失效,从微信服务器获取
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
getAccountBasicInfoURL = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"
|
getAccountBasicInfoURL = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"
|
||||||
checkNickNameURL = "https://api.weixin.qq.com/cgi-bin/wxverify/checkwxverifynickname"
|
|
||||||
setNickNameURL = "https://api.weixin.qq.com/wxa/setnickname"
|
|
||||||
setSignatureURL = "https://api.weixin.qq.com/cgi-bin/account/modifysignature"
|
|
||||||
setHeadImageURL = "https://api.weixin.qq.com/cgi-bin/account/modifyheadimage"
|
|
||||||
getSearchStatusURL = "https://api.weixin.qq.com/wxa/getwxasearchstatus"
|
|
||||||
setSearchStatusURL = "https://api.weixin.qq.com/wxa/changewxasearchstatus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic 基础信息设置
|
// Basic 基础信息设置
|
||||||
@@ -57,187 +51,3 @@ func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) {
|
|||||||
// TODO
|
// TODO
|
||||||
// func (encryptor *Basic) modifyDomain() {
|
// func (encryptor *Basic) modifyDomain() {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// CheckNickNameResp 小程序名称检测结果
|
|
||||||
type CheckNickNameResp struct {
|
|
||||||
util.CommonError
|
|
||||||
HitCondition bool `json:"hit_condition"` // 是否命中关键字策略。若命中,可以选填关键字材料
|
|
||||||
Wording string `json:"wording"` // 命中关键字的说明描述
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckNickName 检测微信认证的名称是否符合规则
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/checkNickName.html
|
|
||||||
func (basic *Basic) CheckNickName(nickname string) (*CheckNickNameResp, error) {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", checkNickNameURL, ak)
|
|
||||||
data, err := util.PostJSON(url, map[string]string{
|
|
||||||
"nick_name": nickname,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := &CheckNickNameResp{}
|
|
||||||
if err := util.DecodeWithError(data, result, "wxverify/checkwxverifynickname"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNickNameResp 设置小程序名称结果
|
|
||||||
type SetNickNameResp struct {
|
|
||||||
util.CommonError
|
|
||||||
AuditID int64 `json:"audit_id"` // 审核单Id,通过用于查询改名审核状态
|
|
||||||
Wording string `json:"wording"` // 材料说明
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNickNameParam 设置小程序名称参数
|
|
||||||
type SetNickNameParam struct {
|
|
||||||
NickName string `json:"nick_name"` // 昵称,不支持包含“小程序”关键字的昵称
|
|
||||||
IDCard string `json:"id_card,omitempty"` // 身份证照片 mediaid,个人号必填
|
|
||||||
License string `json:"license,omitempty"` // 组织机构代码证或营业执照 mediaid,组织号必填
|
|
||||||
NameingOtherStuff1 string `json:"naming_other_stuff_1,omitempty"` // 其他证明材料 mediaid,选填
|
|
||||||
NameingOtherStuff2 string `json:"naming_other_stuff_2,omitempty"` // 其他证明材料 mediaid,选填
|
|
||||||
NameingOtherStuff3 string `json:"naming_other_stuff_3,omitempty"` // 其他证明材料 mediaid,选填
|
|
||||||
NameingOtherStuff4 string `json:"naming_other_stuff_4,omitempty"` // 其他证明材料 mediaid,选填
|
|
||||||
NameingOtherStuff5 string `json:"naming_other_stuff_5,omitempty"` // 其他证明材料 mediaid,选填
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNickName 设置小程序名称
|
|
||||||
func (basic *Basic) SetNickName(nickname string) (*SetNickNameResp, error) {
|
|
||||||
return basic.SetNickNameFull(&SetNickNameParam{
|
|
||||||
NickName: nickname,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNickNameFull 设置小程序名称
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/setNickName.html
|
|
||||||
func (basic *Basic) SetNickNameFull(param *SetNickNameParam) (*SetNickNameResp, error) {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", setNickNameURL, ak)
|
|
||||||
data, err := util.PostJSON(url, param)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := &SetNickNameResp{}
|
|
||||||
if err := util.DecodeWithError(data, result, "setnickname"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSignatureResp 小程序功能介绍修改结果
|
|
||||||
type SetSignatureResp struct {
|
|
||||||
util.CommonError
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSignature 小程序修改功能介绍
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/setSignature.html
|
|
||||||
func (basic *Basic) SetSignature(signature string) error {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", setSignatureURL, ak)
|
|
||||||
data, err := util.PostJSON(url, map[string]string{
|
|
||||||
"signature": signature,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return util.DecodeWithError(data, &SetSignatureResp{}, "account/modifysignature")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSearchStatusResp 查询小程序当前是否可被搜索
|
|
||||||
type GetSearchStatusResp struct {
|
|
||||||
util.CommonError
|
|
||||||
Status int `json:"status"` // 1 表示不可搜索,0 表示可搜索
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSearchStatus 查询小程序当前是否可被搜索
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/getSearchStatus.html
|
|
||||||
func (basic *Basic) GetSearchStatus(signature string) (*GetSearchStatusResp, error) {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", getSearchStatusURL, ak)
|
|
||||||
data, err := util.HTTPGet(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result := &GetSearchStatusResp{}
|
|
||||||
if err := util.DecodeWithError(data, result, "getwxasearchstatus"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSearchStatusResp 小程序是否可被搜索修改结果
|
|
||||||
type SetSearchStatusResp struct {
|
|
||||||
util.CommonError
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSearchStatus 修改小程序是否可被搜索
|
|
||||||
// status: 1 表示不可搜索,0 表示可搜索
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/setSearchStatus.html
|
|
||||||
func (basic *Basic) SetSearchStatus(status int) error {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", setSearchStatusURL, ak)
|
|
||||||
data, err := util.PostJSON(url, map[string]int{
|
|
||||||
"status": status,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return util.DecodeWithError(data, &SetSearchStatusResp{}, "changewxasearchstatus")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeadImageResp 小程序头像修改结果
|
|
||||||
type SetHeadImageResp struct {
|
|
||||||
util.CommonError
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeadImageParam 小程序头像修改参数
|
|
||||||
type SetHeadImageParam struct {
|
|
||||||
HeadImageMediaID string `json:"head_img_media_id"` // 头像素材 media_id
|
|
||||||
X1 string `json:"x1"` // 裁剪框左上角 x 坐标(取值范围:[0, 1])
|
|
||||||
Y1 string `json:"y1"` // 裁剪框左上角 y 坐标(取值范围:[0, 1])
|
|
||||||
X2 string `json:"x2"` // 裁剪框右下角 x 坐标(取值范围:[0, 1])
|
|
||||||
Y2 string `json:"y2"` // 裁剪框右下角 y 坐标(取值范围:[0, 1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeadImage 修改小程序头像
|
|
||||||
func (basic *Basic) SetHeadImage(imgMediaID string) error {
|
|
||||||
return basic.SetHeadImageFull(&SetHeadImageParam{
|
|
||||||
HeadImageMediaID: imgMediaID,
|
|
||||||
X1: "0",
|
|
||||||
Y1: "0",
|
|
||||||
X2: "1",
|
|
||||||
Y2: "1",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeadImageFull 修改小程序头像
|
|
||||||
// 新增临时素材: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html
|
|
||||||
// ref: https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/miniprogram-management/basic-info-management/setHeadImage.html
|
|
||||||
func (basic *Basic) SetHeadImageFull(param *SetHeadImageParam) error {
|
|
||||||
ak, err := basic.GetAuthrAccessToken(basic.AppID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
url := fmt.Sprintf("%s?access_token=%s", setHeadImageURL, ak)
|
|
||||||
data, err := util.PostJSON(url, param)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return util.DecodeWithError(data, &SetHeadImageResp{}, "account/modifyheadimage")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/silenceper/wechat/v2/pay/config"
|
"github.com/silenceper/wechat/v2/pay/config"
|
||||||
"github.com/silenceper/wechat/v2/pay/notify"
|
"github.com/silenceper/wechat/v2/pay/notify"
|
||||||
"github.com/silenceper/wechat/v2/pay/order"
|
"github.com/silenceper/wechat/v2/pay/order"
|
||||||
|
"github.com/silenceper/wechat/v2/pay/redpacket"
|
||||||
"github.com/silenceper/wechat/v2/pay/refund"
|
"github.com/silenceper/wechat/v2/pay/refund"
|
||||||
"github.com/silenceper/wechat/v2/pay/transfer"
|
"github.com/silenceper/wechat/v2/pay/transfer"
|
||||||
)
|
)
|
||||||
@@ -37,3 +38,8 @@ func (pay *Pay) GetRefund() *refund.Refund {
|
|||||||
func (pay *Pay) GetTransfer() *transfer.Transfer {
|
func (pay *Pay) GetTransfer() *transfer.Transfer {
|
||||||
return transfer.NewTransfer(pay.cfg)
|
return transfer.NewTransfer(pay.cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRedpacket 红包
|
||||||
|
func (pay *Pay) GetRedpacket() *redpacket.Redpacket {
|
||||||
|
return redpacket.NewRedpacket(pay.cfg)
|
||||||
|
}
|
||||||
|
|||||||
131
pay/redpacket/redpacket.go
Normal file
131
pay/redpacket/redpacket.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package redpacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/pay/config"
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// redpacketGateway 发放红包接口
|
||||||
|
// https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
|
||||||
|
var redpacketGateway = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"
|
||||||
|
|
||||||
|
// Redpacket struct extends context
|
||||||
|
type Redpacket struct {
|
||||||
|
*config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRedpacket return an instance of Redpacket package
|
||||||
|
func NewRedpacket(cfg *config.Config) *Redpacket {
|
||||||
|
return &Redpacket{cfg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params 调用参数
|
||||||
|
type Params struct {
|
||||||
|
MchBillno string // 商户订单号
|
||||||
|
SendName string // 商户名称
|
||||||
|
ReOpenID string
|
||||||
|
TotalAmount int
|
||||||
|
TotalNum int
|
||||||
|
Wishing string
|
||||||
|
ClientIP string
|
||||||
|
ActName string
|
||||||
|
Remark string
|
||||||
|
|
||||||
|
RootCa string // ca证书
|
||||||
|
}
|
||||||
|
|
||||||
|
// request 接口请求参数
|
||||||
|
type request struct {
|
||||||
|
NonceStr string `xml:"nonce_str"`
|
||||||
|
Sign string `xml:"sign"`
|
||||||
|
MchID string `xml:"mch_id"`
|
||||||
|
MchBillno string `xml:"mch_billno"`
|
||||||
|
Wxappid string `xml:"wxappid"`
|
||||||
|
SendName string `xml:"send_name"`
|
||||||
|
ReOpenID string `xml:"re_openid"`
|
||||||
|
TotalAmount int `xml:"total_amount"`
|
||||||
|
TotalNum int `xml:"total_num"`
|
||||||
|
Wishing string `xml:"wishing"`
|
||||||
|
ClientIP string `xml:"client_ip"`
|
||||||
|
ActName string `xml:"act_name"`
|
||||||
|
Remark string `xml:"remark"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response 接口返回
|
||||||
|
type Response struct {
|
||||||
|
ReturnCode string `xml:"return_code"`
|
||||||
|
ReturnMsg string `xml:"return_msg"`
|
||||||
|
ResultCode string `xml:"result_code,omitempty"`
|
||||||
|
ErrCode string `xml:"err_code,omitempty"`
|
||||||
|
ErrCodeDes string `xml:"err_code_des,omitempty"`
|
||||||
|
MchBillno string `xml:"mch_billno,omitempty"`
|
||||||
|
MchID string `xml:"mch_id,omitempty"`
|
||||||
|
Wxappid string `xml:"wxappid"`
|
||||||
|
ReOpenID string `xml:"re_openid"`
|
||||||
|
TotalAmount int `xml:"total_amount"`
|
||||||
|
SendListid string `xml:"send_listid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendRedpacket 发放红包
|
||||||
|
func (redpacket *Redpacket) SendRedpacket(p *Params) (rsp *Response, err error) {
|
||||||
|
nonceStr := util.RandomStr(32)
|
||||||
|
param := make(map[string]string)
|
||||||
|
|
||||||
|
param["nonce_str"] = nonceStr
|
||||||
|
param["mch_id"] = redpacket.MchID
|
||||||
|
param["wxappid"] = redpacket.AppID
|
||||||
|
param["mch_billno"] = p.MchBillno
|
||||||
|
param["send_name"] = p.SendName
|
||||||
|
param["re_openid"] = p.ReOpenID
|
||||||
|
param["total_amount"] = strconv.Itoa(p.TotalAmount)
|
||||||
|
param["total_num"] = strconv.Itoa(p.TotalNum)
|
||||||
|
param["wishing"] = p.Wishing
|
||||||
|
param["client_ip"] = p.ClientIP
|
||||||
|
param["act_name"] = p.ActName
|
||||||
|
param["remark"] = p.Remark
|
||||||
|
//param["scene_id"] = "PRODUCT_2"
|
||||||
|
|
||||||
|
sign, err := util.ParamSign(param, redpacket.Key)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := request{
|
||||||
|
NonceStr: nonceStr,
|
||||||
|
Sign: sign,
|
||||||
|
MchID: redpacket.MchID,
|
||||||
|
Wxappid: redpacket.AppID,
|
||||||
|
MchBillno: p.MchBillno,
|
||||||
|
SendName: p.SendName,
|
||||||
|
ReOpenID: p.ReOpenID,
|
||||||
|
TotalAmount: p.TotalAmount,
|
||||||
|
TotalNum: p.TotalNum,
|
||||||
|
Wishing: p.Wishing,
|
||||||
|
ClientIP: p.ClientIP,
|
||||||
|
ActName: p.ActName,
|
||||||
|
Remark: p.Remark,
|
||||||
|
}
|
||||||
|
|
||||||
|
rawRet, err := util.PostXMLWithTLS(redpacketGateway, req, p.RootCa, redpacket.MchID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = xml.Unmarshal(rawRet, &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rsp.ReturnCode == "SUCCESS" {
|
||||||
|
if rsp.ResultCode == "SUCCESS" {
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("send redpacket error, errcode=%s,errmsg=%s", rsp.ErrCode, rsp.ErrCodeDes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("[msg : xmlUnmarshalError] [rawReturn : %s] [sign : %s]", string(rawRet), sign)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ const (
|
|||||||
departmentSimpleListURL = "https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token=%s&id=%d"
|
departmentSimpleListURL = "https://qyapi.weixin.qq.com/cgi-bin/department/simplelist?access_token=%s&id=%d"
|
||||||
// departmentListURL 获取部门列表
|
// departmentListURL 获取部门列表
|
||||||
departmentListURL = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=%s"
|
departmentListURL = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=%s"
|
||||||
|
// departmentGetURL 获取单个部门详情 https://qyapi.weixin.qq.com/cgi-bin/department/get?access_token=ACCESS_TOKEN&id=ID
|
||||||
|
departmentGetURL = "https://qyapi.weixin.qq.com/cgi-bin/department/get?access_token=%s&id=%d"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -121,3 +123,24 @@ func (r *Client) DepartmentList() ([]*Department, error) {
|
|||||||
// 返回数据
|
// 返回数据
|
||||||
return result.Department, err
|
return result.Department, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DepartmentGet 获取单个部门详情
|
||||||
|
// see https://developer.work.weixin.qq.com/document/path/95351
|
||||||
|
func (r *Client) DepartmentGet(departmentID int) (*Department, error) {
|
||||||
|
var (
|
||||||
|
accessToken string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response []byte
|
||||||
|
if response, err = util.HTTPGet(fmt.Sprintf(departmentGetURL, accessToken, departmentID)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := &Department{}
|
||||||
|
if err = util.DecodeWithError(response, result, "DepartmentGet"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ type UserGetResponse struct {
|
|||||||
} `json:"external_profile"` // 成员对外属性,字段详情见对外属性;代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
|
} `json:"external_profile"` // 成员对外属性,字段详情见对外属性;代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserGet 获取部门成员
|
// UserGet 读取成员
|
||||||
// @see https://developer.work.weixin.qq.com/document/path/90196
|
// @see https://developer.work.weixin.qq.com/document/path/90196
|
||||||
func (r *Client) UserGet(UserID string) (*UserGetResponse, error) {
|
func (r *Client) UserGet(UserID string) (*UserGetResponse, error) {
|
||||||
var (
|
var (
|
||||||
@@ -237,8 +237,8 @@ func (r *Client) UserGet(UserID string) (*UserGetResponse, error) {
|
|||||||
strings.Join([]string{
|
strings.Join([]string{
|
||||||
userGetURL,
|
userGetURL,
|
||||||
util.Query(map[string]interface{}{
|
util.Query(map[string]interface{}{
|
||||||
"access_token": accessToken,
|
"access_token": accessToken,
|
||||||
"department_id": UserID,
|
"userid": UserID,
|
||||||
}),
|
}),
|
||||||
}, "?")); err != nil {
|
}, "?")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
69
work/checkin/checkin.go
Normal file
69
work/checkin/checkin.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package checkin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// getCheckinDataURL 获取打卡记录数据
|
||||||
|
getCheckinDataURL = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata?access_token=%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// GetCheckinDataRequest 获取打卡记录数据请求
|
||||||
|
GetCheckinDataRequest struct {
|
||||||
|
OpenCheckinDataType int64 `json:"opencheckindatatype"`
|
||||||
|
StartTime int64 `json:"starttime"`
|
||||||
|
EndTime int64 `json:"endtime"`
|
||||||
|
UserIDList []string `json:"useridlist"`
|
||||||
|
}
|
||||||
|
// GetCheckinDataResponse 获取打卡记录数据响应
|
||||||
|
GetCheckinDataResponse struct {
|
||||||
|
util.CommonError
|
||||||
|
CheckinData []*GetCheckinDataItem `json:"checkindata"`
|
||||||
|
}
|
||||||
|
// GetCheckinDataItem 打卡记录数据
|
||||||
|
GetCheckinDataItem struct {
|
||||||
|
UserID string `json:"userid"`
|
||||||
|
GroupName string `json:"groupname"`
|
||||||
|
CheckinType string `json:"checkin_type"`
|
||||||
|
ExceptionType string `json:"exception_type"`
|
||||||
|
CheckinTime int64 `json:"checkin_time"`
|
||||||
|
LocationTitle string `json:"location_title"`
|
||||||
|
LocationDetail string `json:"location_detail"`
|
||||||
|
WifiName string `json:"wifiname"`
|
||||||
|
Notes string `json:"notes"`
|
||||||
|
WifiMac string `json:"wifimac"`
|
||||||
|
MediaIDs []string `json:"mediaids"`
|
||||||
|
SchCheckinTime int64 `json:"sch_checkin_time"`
|
||||||
|
GroupID int64 `json:"groupid"`
|
||||||
|
ScheduleID int64 `json:"schedule_id"`
|
||||||
|
TimelineID int64 `json:"timeline_id"`
|
||||||
|
Lat int64 `json:"lat,omitempty"`
|
||||||
|
Lng int64 `json:"lng,omitempty"`
|
||||||
|
DeviceID string `json:"deviceid,omitempty"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetCheckinData 获取打卡记录数据
|
||||||
|
// @see https://developer.work.weixin.qq.com/document/path/90262
|
||||||
|
func (r *Client) GetCheckinData(req *GetCheckinDataRequest) (*GetCheckinDataResponse, error) {
|
||||||
|
var (
|
||||||
|
accessToken string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response []byte
|
||||||
|
if response, err = util.PostJSON(fmt.Sprintf(getCheckinDataURL, accessToken), req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := &GetCheckinDataResponse{}
|
||||||
|
if err = util.DecodeWithError(response, result, "GetCheckinData"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
17
work/checkin/client.go
Normal file
17
work/checkin/client.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package checkin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/silenceper/wechat/v2/work/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client 打卡接口实例
|
||||||
|
type Client struct {
|
||||||
|
*context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient 初始化实例
|
||||||
|
func NewClient(ctx *context.Context) *Client {
|
||||||
|
return &Client{
|
||||||
|
ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,7 @@ type (
|
|||||||
GroupNickname string `json:"group_nickname"` //在群里的昵称
|
GroupNickname string `json:"group_nickname"` //在群里的昵称
|
||||||
Name string `json:"name"` //名字。仅当 need_name = 1 时返回 如果是微信用户,则返回其在微信中设置的名字 如果是企业微信联系人,则返回其设置对外展示的别名或实名
|
Name string `json:"name"` //名字。仅当 need_name = 1 时返回 如果是微信用户,则返回其在微信中设置的名字 如果是企业微信联系人,则返回其设置对外展示的别名或实名
|
||||||
UnionID string `json:"unionid,omitempty"` //外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。仅当群成员类型是微信用户(包括企业成员未添加好友),且企业绑定了微信开发者ID有此字段(查看绑定方法)。第三方不可获取,上游企业不可获取下游企业客户的unionid字段
|
UnionID string `json:"unionid,omitempty"` //外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。仅当群成员类型是微信用户(包括企业成员未添加好友),且企业绑定了微信开发者ID有此字段(查看绑定方法)。第三方不可获取,上游企业不可获取下游企业客户的unionid字段
|
||||||
|
State string `json:"state,omitempty"` //如果在配置入群方式时,配置了state参数,那么在获取客户群详情时,通过该方式入群的成员,会额外获取到相应的state参数
|
||||||
}
|
}
|
||||||
//GroupChatAdmin 群管理员
|
//GroupChatAdmin 群管理员
|
||||||
GroupChatAdmin struct {
|
GroupChatAdmin struct {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ const (
|
|||||||
uploadImgURL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=%s"
|
uploadImgURL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=%s"
|
||||||
// uploadTempFile 上传临时素材
|
// uploadTempFile 上传临时素材
|
||||||
uploadTempFile = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s"
|
uploadTempFile = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s"
|
||||||
|
// uploadAttachment 上传附件资源
|
||||||
|
uploadAttachment = "https://qyapi.weixin.qq.com/cgi-bin/media/upload_attachment?access_token=%s&media_type=%s&attachment_type=%d"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UploadImgResponse 上传图片响应
|
// UploadImgResponse 上传图片响应
|
||||||
@@ -27,6 +29,14 @@ type UploadTempFileResponse struct {
|
|||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadAttachmentResponse 上传资源附件响应
|
||||||
|
type UploadAttachmentResponse struct {
|
||||||
|
util.CommonError
|
||||||
|
MediaID string `json:"media_id"`
|
||||||
|
CreateAt int64 `json:"created_at"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
// UploadImg 上传图片
|
// UploadImg 上传图片
|
||||||
// @see https://developer.work.weixin.qq.com/document/path/90256
|
// @see https://developer.work.weixin.qq.com/document/path/90256
|
||||||
func (r *Client) UploadImg(filename string) (*UploadImgResponse, error) {
|
func (r *Client) UploadImg(filename string) (*UploadImgResponse, error) {
|
||||||
@@ -69,3 +79,26 @@ func (r *Client) UploadTempFile(filename string, mediaType string) (*UploadTempF
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadAttachment 上传附件资源
|
||||||
|
// @see https://developer.work.weixin.qq.com/document/path/95098
|
||||||
|
// @mediaType 媒体文件类型,分别有图片(image)、视频(video)、普通文件(file)
|
||||||
|
// @attachment_type 附件类型,不同的附件类型用于不同的场景。1:朋友圈;2:商品图册
|
||||||
|
func (r *Client) UploadAttachment(filename string, mediaType string, attachmentType int) (*UploadAttachmentResponse, error) {
|
||||||
|
var (
|
||||||
|
accessToken string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var response []byte
|
||||||
|
if response, err = util.PostFile("media", filename, fmt.Sprintf(uploadAttachment, accessToken, mediaType, attachmentType)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := &UploadAttachmentResponse{}
|
||||||
|
if err = util.DecodeWithError(response, result, "UploadAttachment"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/silenceper/wechat/v2/credential"
|
"github.com/silenceper/wechat/v2/credential"
|
||||||
"github.com/silenceper/wechat/v2/work/addresslist"
|
"github.com/silenceper/wechat/v2/work/addresslist"
|
||||||
"github.com/silenceper/wechat/v2/work/appchat"
|
"github.com/silenceper/wechat/v2/work/appchat"
|
||||||
|
"github.com/silenceper/wechat/v2/work/checkin"
|
||||||
"github.com/silenceper/wechat/v2/work/config"
|
"github.com/silenceper/wechat/v2/work/config"
|
||||||
"github.com/silenceper/wechat/v2/work/context"
|
"github.com/silenceper/wechat/v2/work/context"
|
||||||
"github.com/silenceper/wechat/v2/work/externalcontact"
|
"github.com/silenceper/wechat/v2/work/externalcontact"
|
||||||
@@ -85,3 +86,8 @@ func (wk *Work) GetAppChat() *appchat.Client {
|
|||||||
func (wk *Work) GetInvoice() *invoice.Client {
|
func (wk *Work) GetInvoice() *invoice.Client {
|
||||||
return invoice.NewClient(wk.ctx)
|
return invoice.NewClient(wk.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCheckin 获取打卡接口实例
|
||||||
|
func (wk *Work) GetCheckin() *checkin.Client {
|
||||||
|
return checkin.NewClient(wk.ctx)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user