1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 21:02:25 +08:00

Compare commits

...

91 Commits

Author SHA1 Message Date
silenceper
83e223999b add .goreleaser.yml
Signed-off-by: silenceper <silenceper@gmail.com>
2021-12-06 11:32:49 +08:00
silenceper
e068d53dcb Create release.yml 2021-12-06 11:19:25 +08:00
杨成锴
8c87c49f2a update: 统一消息类型 (#511) 2021-12-03 20:45:11 +08:00
Afeyer
74053fe6ef feat: 微信客服会话变更事件支持查看客服账号ID (#510) 2021-11-15 19:16:12 +08:00
ixugo
566c3c27cb 增加订阅通知事件 (#509) 2021-11-15 17:06:50 +08:00
Afeyer
dc24ad4262 feat: 微信客服会话状态变更时支持发送回复语 (#505) 2021-11-15 17:05:13 +08:00
Afeyer
05ec6a42ae docs:更新接口消息发送限制说明文档 (#504) 2021-11-05 17:37:27 +08:00
Afeyer
efad41bcda pref: 优化全局错误提示 (#503) 2021-11-01 10:07:48 +08:00
Afeyer
3fb288d932 feat: 微信客服支持发送欢迎语 (#496) 2021-10-08 19:37:40 +08:00
Afeyer
f5f401e76c doc: 更新微信客服 API 文档 (#495)
* doc: 更新微信客服 API 文档

* chore: 解决【升级服务】标题显示异常的问题
2021-09-30 22:34:42 +08:00
Afeyer
ab4f427647 feat:微信客服支持获取视频号绑定状态 (#493) 2021-09-30 17:31:30 +08:00
silenceper
a9ae64ef63 add work api doc (#494) 2021-09-30 16:50:29 +08:00
silenceper
83621a38c6 Update README.md 2021-09-30 16:09:55 +08:00
silenceper
4937f019a0 梳理API列表 (#491)
* api梳理

* 完善客服管理api doc
2021-09-30 16:01:47 +08:00
Afeyer
e9489625c6 feat:微信客服支持通过接口转接人工会话 (#492) 2021-09-30 12:13:10 +08:00
houseme
f74869e61c Transfer to wallet returns pointer object (#489)
* [feature] Format the code and improve Mini Program authorization to obtain openid(miniprogram/auth/auth.go Code2Session)

* [feature] CheckEncryptedData (https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/user-info/auth.checkEncryptedData.html)

* upgrade json error

* upgrade json error

* [feature] Wallet Transfer returns the pointer object

Co-authored-by: houseme <houseme@outlook.com>
2021-09-27 19:35:16 +08:00
owen.gxz
fd96154231 增加注册审核事件推送消息 (#487)
* 消息类型增加快速注册企业小程序回调参数

* 代码格式化

* 更改常量名称

* 更改注解

Co-authored-by: 高震 <gaozhen@gaozhendeMacBook-Pro.local>
2021-09-26 14:15:31 +08:00
Afeyer
8621e06a01 feat: 完善微信客服常用的错误类型 (#486)
* feat:微信客服支持向客户发送欢迎语

* chore: go fmt file

* feat:移除空白文件

* doc:完善菜单消息内的注释文档

* feat: 完善微信客服常用的错误类型

* refactor: 优化SDK错误生成函数
2021-09-26 10:38:52 +08:00
okhowang
1e2f909f34 小程序auth增加Context接口 (#483) 2021-09-17 10:11:22 +08:00
okhowang
00b13cda0d 增加iv校验 (#482) 2021-09-16 12:21:40 +08:00
Afeyer
c021336a3c doc:完善菜单消息内的注释文档 (#479)
* feat:微信客服支持向客户发送欢迎语

* chore: go fmt file

* feat:移除空白文件

* doc:完善菜单消息内的注释文档
2021-09-13 19:20:16 +08:00
youkjw
9294950ab5 修复不传sign_type导致request sign_type无默认值的bug (#480)
* 接入关闭订单

* test

* 删除testing,过不了ci

* 避免err覆盖

* 修复不传sign_type导致request sign_type无默认值的bug

* 修复不传sign_type导致request sign_type无默认值的bug

Co-authored-by: liujianwei <liujianwei@linghit.com>
2021-09-13 18:58:56 +08:00
Afeyer
d776f5c400 feat:微信客服支持向客户发送欢迎语 (#478)
* feat:微信客服支持向客户发送欢迎语

* chore: go fmt file

* feat:移除空白文件
2021-09-13 10:07:18 +08:00
Afeyer
bc9f483c8e feat: 菜单消息支持发送结束文本 (#477)
* feat:菜单消息支持发送结束文本

Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-09-10 10:08:49 +08:00
JerryTam
d3d91b8d29 实例化Redis新增dialOpts参数以支持redis更多拨号设置 (#475)
* 实例化Redis新dialOpts参数以支持redis更多拨号设置

* debug

* gofmt file

* remove go.mod empty line

Co-authored-by: Jerry <prc.tzy@gmail.com>
2021-09-09 12:22:02 +08:00
houseme
96c1f98944 [feature] Format the code and improve Mini Program authorization to o… (#473)
* [feature] Format the code and improve Mini Program authorization to obtain openid(miniprogram/auth/auth.go Code2Session)

* [feature] CheckEncryptedData (https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/user-info/auth.checkEncryptedData.html)

* upgrade json error

* upgrade json error

Co-authored-by: houseme <houseme@outlook.com>
2021-09-08 11:03:23 +08:00
George Wang
47adf42208 修复微信支付缺少notify_url的bug (#472) 2021-09-08 10:11:06 +08:00
youkjw
39ed108b11 接入微信关闭订单 (#471)
* 接入关闭订单

* test

* 删除testing,过不了ci

* 避免err覆盖

Co-authored-by: liujianwei <liujianwei@linghit.com>
2021-09-07 16:07:23 +08:00
George Wang
f767b72872 增加urllink (#465)
* 增加urllink

* urllink.Generate返回string作为结果
2021-09-06 20:53:29 +08:00
Daguang
d3f1a83d46 增加shortlink.generate (#467)
* init

* init

* init

* 小程序 shortLink
2021-09-06 12:19:17 +08:00
silenceper
db205405ee 针对会话存档,增加tags条件编译,避免默认编译不通过 (#460) 2021-09-03 16:47:06 +08:00
Afeyer
e1ef3ea160 feat:暴露接待人员ID到消息列表 (#461)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法

* feat:对外暴露SDKApiForbidden等错误

可以通过调用升级服务相关接口然后根据该错误判断微信客服配置来源

* feat:添加无效的open_kfid错误信息

* fix: 添加SDKApiNotOpen 错误信息

目前主要用于判断客户是否关闭了API授权,如果客户关闭了API功能导致服务异常,则可以引导用户执行相应的操作重新开启改功能

* feat:暴露接待人员ID到消息列表

无需对消息进行序列化即可直接获取接待人员ID,便于处理接待人员的相关业务,例如:统计接待人员当天应答次数

* feat: 添加SDKNotUseInWeCom错误信息

如果SDK输出当前错误,则说明用户在企业微信后台关闭了微信客服功能,需引导用户重新开启该功能

Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-09-03 16:46:44 +08:00
silenceper
b9f0e8368d Update README.md (#457) 2021-08-31 20:29:49 +08:00
silenceper
9335b13807 Update README.md (#456) 2021-08-31 20:29:19 +08:00
silenceper
3afe499e70 update release-2.0 (#454)
* merge branch release-2.0 to v2 (#450)

* feat: add/delete subscribe template (#449)

* feat: 添加 SDKApiNotOpen 错误信息 (#448)

* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法

* feat:对外暴露SDKApiForbidden等错误

可以通过调用升级服务相关接口然后根据该错误判断微信客服配置来源

* feat:添加无效的open_kfid错误信息

* fix: 添加SDKApiNotOpen 错误信息

目前主要用于判断客户是否关闭了API授权,如果客户关闭了API功能导致服务异常,则可以引导用户执行相应的操作重新开启改功能

Co-authored-by: Afeyer <afeyer@h5base.cn>

Co-authored-by: ZmJ <wzmmmmj@gmail.com>
Co-authored-by: Afeyer <1500527791@qq.com>
Co-authored-by: Afeyer <afeyer@h5base.cn>

* Update go.yml (#452)

* 修正字段问题 (#451)

fix #443

* fix linux build failed when cgo disable (#453)

Co-authored-by: ZmJ <wzmmmmj@gmail.com>
Co-authored-by: Afeyer <1500527791@qq.com>
Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-08-30 18:39:41 +08:00
Afeyer
beb2f9506d feat: 添加 SDKApiNotOpen 错误信息 (#448)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法

* feat:对外暴露SDKApiForbidden等错误

可以通过调用升级服务相关接口然后根据该错误判断微信客服配置来源

* feat:添加无效的open_kfid错误信息

* fix: 添加SDKApiNotOpen 错误信息

目前主要用于判断客户是否关闭了API授权,如果客户关闭了API功能导致服务异常,则可以引导用户执行相应的操作重新开启改功能

Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-08-30 10:13:04 +08:00
ZmJ
b535cd116a feat: add/delete subscribe template (#449) 2021-08-30 10:12:23 +08:00
silenceper
3cfa9e6c71 企业会话存档只在linux平台支持 (#447) 2021-08-26 18:51:30 +08:00
80d91d8316 使用双检锁优化 Token 获取 (#444) 2021-08-26 10:34:24 +08:00
Afeyer
c61154105b feat:对外暴露SDKApiForbidden等错误 (#445)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法

* feat:对外暴露SDKApiForbidden等错误

可以通过调用升级服务相关接口然后根据该错误判断微信客服配置来源
2021-08-26 10:00:39 +08:00
ZmJ
d392ff776b 补充 添加/删除 公众号模板消息接口 (#440)
* provide add/delete officialaccount template api

* use util.DecodeWithError check errcode
2021-08-25 10:18:49 +08:00
George Wang
df82432f6c 增加订单查询接口中的trade_state参数 (#442) 2021-08-24 18:17:27 +08:00
Afeyer
917f1817e5 feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法 (#439)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容

* feat:支持微信客服回调请求的校验和消息的解析,复用原有的Signature和DecryptMsg方法
2021-08-19 21:33:14 +08:00
Afeyer
6fdb986911 feat:微信客服链接支持自定义参数 (#438)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件

* polish:客服链接支持自定义参数并更新注释文档内容
2021-08-18 10:55:37 +08:00
Afeyer
8ceabc2d0b 添加微信客服SDK (#436)
* 添加微信客服SDK

* polish:优化签名函数

* polish:优化注释内容

* polish:复用已有的Token以及CommonError,移除无用的输出

* polish:复用已有的消息加解密

* fix:修复错误信息被覆盖的问题

* polish:go fmt 文件
2021-08-17 10:19:01 +08:00
ZmJ
fc1fc7e84e 快捷获取公众号订阅消息对象 (#435)
* 快捷获取公众号订阅消息对象

* fix: typo subscrib -> subscribe
2021-08-16 14:39:44 +08:00
notHugh
13611466f3 增加订单失效时间的参数 (#432)
* 增加订单失效时间的参数

* fix error
golangci-lint:
Function 'PrePayOrder' has too many statements

* 增加一个支持 APP 支付的 Bridge 函数
2021-08-16 14:36:57 +08:00
silenceper
08008e1c86 fix #417: CouponFeed0 CouponFeed1 CouponFeed2字段映射失败 (#427) 2021-07-30 14:23:47 +08:00
silenceper
01addecd53 Revert "fix #417: CouponFeed0 CouponFeed1 CouponFeed2字段映射失败 (#424)" (#426)
This reverts commit 4433fc18a6.
2021-07-30 14:19:52 +08:00
silenceper
4433fc18a6 fix #417: CouponFeed0 CouponFeed1 CouponFeed2字段映射失败 (#424) 2021-07-30 14:16:49 +08:00
silenceper
e8c90848e4 Update feature.md 2021-07-27 10:07:39 +08:00
silenceper
6df8453378 Update bug.md 2021-07-23 15:53:11 +08:00
silenceper
2ff99d41dc Update feature.md 2021-07-23 15:51:55 +08:00
silenceper
b1144e3a38 Update feature.md 2021-07-23 15:51:36 +08:00
silenceper
51778fab8f Update question.md 2021-07-23 15:50:37 +08:00
silenceper
6edaa7888a Rename api--.md to feature.md 2021-07-23 15:49:41 +08:00
silenceper
b27dca624f Rename ----.md to question.md 2021-07-23 15:49:30 +08:00
silenceper
3f9aed72cd Rename --bug.md to bug.md 2021-07-23 15:49:10 +08:00
silenceper
dc508f7341 Update issue templates 2021-07-23 15:46:36 +08:00
Afeyer
1005807328 添加企业微信会话存档SDK (#419)
* 添加企业微信会话存档SDK

* 更新说明文档

* 更新包名为msgaudit并更新说明文档

* 迁移会话存档SDK到work目录下

* 移动RSA文件到util并添加动态库文件

* 整合企业微信和会话存档配置文件

* 修复golangcli-lint提示中的错误

* 对整个项目进行gofmt

* 更新会话存档说明文档

* 会话存档消息获取是抛出error

* 更新会话存档说明文档

Co-authored-by: Afeyer <afeyer@h5base.cn>
2021-07-22 17:45:14 +08:00
从小就很酷
c1e4e2c9d0 增加企业微信 企业内部开发模块 (#418)
* 增加小程序内容安全接口

* 内容安全接口 按照golint规范进行优化

* 内容安全接口 按照golint规范进行优化

* 删除CheckImage中的输出代码

* 小程序内容安全接口

* 小程序内容安全接口

* 小程序内容安全接口
1:修改返回值 改为error异常统一返回

* 增加企业微信 企业内部开发模块
1:授权登录

* 增加企业微信 企业内部开发模块

* 修改参数为小写

* 优化参数格式

Co-authored-by: root <admin@example.com>
2021-07-16 10:28:54 +08:00
从小就很酷
c8522f1875 小程序内容安全 (#415)
* 增加小程序内容安全接口

* 内容安全接口 按照golint规范进行优化

* 内容安全接口 按照golint规范进行优化

* 删除CheckImage中的输出代码

* 小程序内容安全接口

* 小程序内容安全接口

* 小程序内容安全接口
1:修改返回值 改为error异常统一返回

Co-authored-by: root <admin@example.com>
2021-07-13 20:49:47 +08:00
silenceper
5d8fd1f5bd fix 恢复直接使用enc.SetEscapeHTML参数 (#412)
Co-authored-by: zhenlinwen <zhenlinwen@tencent.com>
2021-06-23 09:49:41 +08:00
HUCHAOQI
45caf61899 Silenceper release 2.0 (#408)
* feat(miniapp): 增加统一服务消息

* feat(miniapp): 增加获取微信运动数据接口

* refactor(werun): 更改werun的位置

* fix(lint): 更改stuct名称

Co-authored-by: hyperq <hyperq1g@gmail>
2021-06-10 10:28:01 +08:00
silenceper
b42f1e1a7f Update README.md (#407) 2021-06-08 11:52:03 +08:00
Lien Li
2f88fad0bd 支持解密 分享数据 得到 openGId (#406)
* 支持解密 分享数据 

https://developers.weixin.qq.com/miniprogram/dev/api/share/wx.getShareInfo.html

* fix fmt
2021-06-07 10:26:54 +08:00
Lien Li
13e5b3938b Update encryptor.go (#405) 2021-06-07 10:23:49 +08:00
MengYX
828ba7875b fix: incorrect redigo version (#404) 2021-06-07 10:04:45 +08:00
lanbiaowan
7a513080a8 addNews return error (#401)
Co-authored-by: p_biaolan <biao.lan@qingteng.cn>
2021-06-07 10:03:56 +08:00
faith
0e8fc3f88b [付款] pay.go 增加 GetTransfer 方法 (#400) 2021-05-28 09:39:32 +08:00
silenceper
c95b844c83 Update README.md 2021-05-26 15:19:04 +08:00
ForrestSu
34f1335d17 feat: 优化MixMessage采用指针传参,减少2次拷贝 (#394) 2021-04-18 18:55:32 +08:00
ForrestSu
fb1aa60ae8 feat(officialaccount): 补充群发消息event (#393)
* feat(officialaccount): 补充群发消息event

* feat: 解析群发结果通知中的成功失败数
2021-04-18 18:50:24 +08:00
ForrestSu
18abebe4de fix: 微信群发预览接口: 只支持单个用户,默认发给第一个 (#392) 2021-04-18 18:48:56 +08:00
silenceper
5472ac979b fix 修改参数类型为interface{},支持string和number (#390) 2021-04-12 16:06:14 +08:00
HUCHAOQI
5ec4cc2269 feat(miniapp): 增加统一服务消息 (#385)
Co-authored-by: hyperq <hyperq1g@gmail>
2021-04-12 15:56:10 +08:00
水煮牛肉
d1241790cb 增加ocr相关接口 (#388)
Co-authored-by: qiq@pvc123.com <qiq@pvc123.com>
2021-04-12 15:29:14 +08:00
Alfred
813684e555 fix: 微信支付退款请求参数签名类型不可选 (#383)
* fix: 微信支付退款请求参数签名类型不可选

* fix: 修复微信退款请求参数问题,out_trade_no OR transaction_id 不可选

* fix: 误删NotifyURL

* fix: 误删SignType

* fix: 修复 golangci-lint 失败

* refactor: 增加GetSignParam方法

* chore: 调整 go.mod

* refactor: 调整参数
2021-03-10 17:26:01 +08:00
Alfred
7ca0317d84 feat: 新增微信支付查询结果 (#380)
* feat: 增加微信支付查询结果

* feat: 增加微信支付查询结果

* fix: 修复golangci-lint失败

* fix: 修复golangci-lint失败

* refactor: 微信支付结果查询错误友好提示
2021-03-05 15:34:46 +08:00
baiyuxiong
ad3cc913b0 修改退款 bug 及增加付款到零钱的支付 (#373)
* fix refund bug and add transfer to wallet support

* fmt code

* fix golangci-lint

* fix golangci-lint
2021-03-05 11:46:13 +08:00
bugstark
64c2de7ab4 Add subscribe message #371 (#376)
* Fix subscribe #371

* Fix subscribe #371

* 规范struct元素名称
2021-03-05 11:27:05 +08:00
GargantuaX
e7fdcf9534 支持公众号账号迁移,获取openID变化接口 (#370)
* * 公众号菜单管理,set相关函数,返回btn本身,方便以字面量的方式创建多个菜单,更直观,方便管理

* * golangci-lint fix

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

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

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

* * bugfix

* * golint fix

* * golint fix
2021-03-01 15:38:54 +08:00
GargantuaX
05907d152e 消息增加GetOpenID方法 (#368)
* * 公众号菜单管理,set相关函数,返回btn本身,方便以字面量的方式创建多个菜单,更直观,方便管理

* * golangci-lint fix

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

* * 增加GetOpenID方法,以获取消息的生产用户openID
2021-02-19 15:50:02 +08:00
kaiiak
398f2ec6ae long url to short url (#367)
* long url to short url

* Decode by util.DecodeWithError
2021-02-08 09:42:46 +08:00
GargantuaX
e8fb058740 * 公众号菜单管理,增加new相关函数,老的set相关函数,返回btn本身,以便用字面量的方式创建多级菜单,更直观,方便管理 (#365)
* * 公众号菜单管理,set相关函数,返回btn本身,方便以字面量的方式创建多个菜单,更直观,方便管理

* * golangci-lint fix

* * 获取二维码ticket接口没有往上抛接口错误
2021-02-07 09:50:53 +08:00
LouGaZen
d5a67eaf29 微信支付 - 退款通知 (#359)
* 🆕 发起退款 - 添加notify_url参数

* 🆕 退款通知
2021-02-07 09:49:43 +08:00
mlboy
e5f0d5eab7 Update README.md (#358)
fix import
2021-01-26 14:11:38 +08:00
silenceper
2eae660002 修复SetMenuByJSON和AddConditionalByJSON报错 (#353)
* change PostJSON to HTTPPost

* change PostJSON to HTTPPost
2020-12-31 13:52:13 +08:00
silenceper
c0da806e03 update golangci (#349)
* update golangci
2020-11-26 12:25:57 +08:00
Che Kun
185baa5d12 公众号网页授权后获取用户基本信息支持语言入参 #344 (#345) 2020-11-26 12:09:58 +08:00
qufo
bf42c188cb Update README.md (#347)
fix typo
2020-11-21 22:06:01 +08:00
154 changed files with 6417 additions and 886 deletions

21
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,21 @@
---
name: 报告Bug
about: 反馈BUG信息
title: "[BUG]"
labels: bug
assignees: ''
---
**描述**
**如何复现**
步骤:
1、
2、
**关联日志信息**
**使用的版本**
- SDK版本: [比如 v0.0.0]

15
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View File

@@ -0,0 +1,15 @@
---
name: API需求
about: 待实现的API接口SDK的强大离不开社区的帮助欢迎为项目贡献PR
title: "[Feature]"
labels: enhancement
assignees: ''
---
<!--
!!!SDK的强大离不开社区的帮助欢迎为本项目贡献PR!!!
-->
**你想要实现的模块或API**

15
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,15 @@
---
name: 使用咨询
about: 关于SDK使用相关的咨询在使用前请先阅读官方微信文档
title: "[咨询]"
labels: question
assignees: ''
---
<!--
重要:
1、在使用本SDK前请先阅读对应的官方微信API文档https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
2、本SDK部分接口文档 https://silenceper.com/wechat/
-->
**请描述您的问题**

View File

@@ -2,39 +2,43 @@ name: Go
on:
push:
branches: [ master,release-* ]
branches: [ master,release-*,v2 ]
pull_request:
branches: [ master,release-* ]
branches: [ master,release-*,v2 ]
jobs:
golangci:
strategy:
matrix:
go-version: [1.15.x]
name: golangci-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.31
build:
name: Build
name: Test
runs-on: ubuntu-latest
services:
redis:
image: redis
ports:
- 6379:6379
- 6379:6379
options: --entrypoint redis-server
memcached:
image: memcached
ports:
- 11211:11211
steps:
- uses: actions/checkout@v2
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
id: go
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v1
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.26
- name: Test
run: go test -v -race ./...

29
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored
View File

@@ -26,4 +26,5 @@ _testmain.go
.vscode/
vendor
.idea/
example/*
example/*
/test

View File

@@ -11,7 +11,7 @@ linters:
- errcheck
- funlen
- goconst
- gocritic
# - gocritic
- gocyclo
- gofmt
- goimports

29
.goreleaser.yml Normal file
View File

@@ -0,0 +1,29 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod download
# you may remove this if you don't need go generate
- go generate ./...
builds:
- skip: true
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

View File

@@ -2,19 +2,23 @@
![Go](https://github.com/silenceper/wechat/workflows/Go/badge.svg?branch=release-2.0)
[![Go Report Card](https://goreportcard.com/badge/github.com/silenceper/wechat)](https://goreportcard.com/report/github.com/silenceper/wechat)
[![pkg](https://img.shields.io/badge/dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/silenceper/wechat/v2?tab=doc)
![version](https://img.shields.io/badge/version-v2-green)
使用Golang开发的微信SDK简单、易用。
>当前版本为v2版本
>注意:当前版本为v2版本v1版本已废弃
## 文档 && 例子
[Wechat SDK 2.0 文档](http://silenceper.com/wechat)
[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)
[Wechat SDK 2.0 文档](https://silenceper.com/wechat)
[Wechat SDK 2.0 例子](https://github.com/gowechat/example)
## 快速开始
```
import github.com/silenceper/wechat/v2
import "github.com/silenceper/wechat/v2"
```
以下是一个微信公众号处理消息接收以及回复的例子:
@@ -35,7 +39,7 @@ officialAccount := wc.GetOfficialAccount(cfg)
// 传入request和responseWriter
server := officialAccount.GetServer(req, rw)
//设置接收消息的处理方法
server.SetMessageHandler(func(msg message.MixMessage) *message.Reply {
server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply {
//回复消息:演示回复用户发送的消息
text := message.NewText(msg.Content)
@@ -58,11 +62,13 @@ server.Send()
- miniprogram: 小程序API
- minigame:小游戏API
- pay:微信支付API
- opernplatform:开放平台API
- openplatform:开放平台API
- work:企业微信
- aispeech:智能对话
- doc: api文档
## 贡献
- 在[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)中查看哪些API未实现
- 提交issue描述需要贡献的内容
- 完成更改后提交PR

2
cache/cache.go vendored
View File

@@ -2,7 +2,7 @@ package cache
import "time"
//Cache interface
// Cache interface
type Cache interface {
Get(key string) interface{}
Set(key string, val interface{}, timeout time.Duration) error

10
cache/memcache.go vendored
View File

@@ -7,18 +7,18 @@ import (
"github.com/bradfitz/gomemcache/memcache"
)
//Memcache struct contains *memcache.Client
// Memcache struct contains *memcache.Client
type Memcache struct {
conn *memcache.Client
}
//NewMemcache create new memcache
// NewMemcache create new memcache
func NewMemcache(server ...string) *Memcache {
mc := memcache.New(server...)
return &Memcache{mc}
}
//Get return cached value
// Get return cached value
func (mem *Memcache) Get(key string) interface{} {
var err error
var item *memcache.Item
@@ -40,7 +40,7 @@ func (mem *Memcache) IsExist(key string) bool {
return true
}
//Set cached value with key and expire time.
// Set cached value with key and expire time.
func (mem *Memcache) Set(key string, val interface{}, timeout time.Duration) (err error) {
var data []byte
if data, err = json.Marshal(val); err != nil {
@@ -51,7 +51,7 @@ func (mem *Memcache) Set(key string, val interface{}, timeout time.Duration) (er
return mem.conn.Set(item)
}
//Delete delete value in memcache.
// Delete delete value in memcache.
func (mem *Memcache) Delete(key string) error {
return mem.conn.Delete(key)
}

10
cache/memory.go vendored
View File

@@ -5,7 +5,7 @@ import (
"time"
)
//Memory struct contains *memcache.Client
// Memory struct contains *memcache.Client
type Memory struct {
sync.Mutex
@@ -17,14 +17,14 @@ type data struct {
Expired time.Time
}
//NewMemory create new memcache
// NewMemory create new memcache
func NewMemory() *Memory {
return &Memory{
data: map[string]*data{},
}
}
//Get return cached value
// Get return cached value
func (mem *Memory) Get(key string) interface{} {
if ret, ok := mem.data[key]; ok {
if ret.Expired.Before(time.Now()) {
@@ -48,7 +48,7 @@ func (mem *Memory) IsExist(key string) bool {
return false
}
//Set cached value with key and expire time.
// Set cached value with key and expire time.
func (mem *Memory) Set(key string, val interface{}, timeout time.Duration) (err error) {
mem.Lock()
defer mem.Unlock()
@@ -60,7 +60,7 @@ func (mem *Memory) Set(key string, val interface{}, timeout time.Duration) (err
return nil
}
//Delete delete value in memcache.
// Delete delete value in memcache.
func (mem *Memory) Delete(key string) error {
mem.deleteKey(key)
return nil

27
cache/redis.go vendored
View File

@@ -7,32 +7,33 @@ import (
"github.com/gomodule/redigo/redis"
)
//Redis redis cache
// Redis .redis cache
type Redis struct {
conn *redis.Pool
}
//RedisOpts redis 连接属性
// RedisOpts redis 连接属性
type RedisOpts struct {
Host string `yml:"host" json:"host"`
Password string `yml:"password" json:"password"`
Database int `yml:"database" json:"database"`
MaxIdle int `yml:"max_idle" json:"max_idle"`
MaxActive int `yml:"max_active" json:"max_active"`
IdleTimeout int `yml:"idle_timeout" json:"idle_timeout"` //second
IdleTimeout int `yml:"idle_timeout" json:"idle_timeout"` // second
}
//NewRedis 实例化
func NewRedis(opts *RedisOpts) *Redis {
// NewRedis 实例化
func NewRedis(opts *RedisOpts, dialOpts ...redis.DialOption) *Redis {
pool := &redis.Pool{
MaxActive: opts.MaxActive,
MaxIdle: opts.MaxIdle,
IdleTimeout: time.Second * time.Duration(opts.IdleTimeout),
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", opts.Host,
dialOpts = append(dialOpts, []redis.DialOption{
redis.DialDatabase(opts.Database),
redis.DialPassword(opts.Password),
)
}...)
return redis.Dial("tcp", opts.Host, dialOpts...)
},
TestOnBorrow: func(conn redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
@@ -45,17 +46,17 @@ func NewRedis(opts *RedisOpts) *Redis {
return &Redis{pool}
}
//SetRedisPool 设置redis连接池
// SetRedisPool 设置redis连接池
func (r *Redis) SetRedisPool(pool *redis.Pool) {
r.conn = pool
}
//SetConn 设置conn
// SetConn 设置conn
func (r *Redis) SetConn(conn *redis.Pool) {
r.conn = conn
}
//Get 获取一个值
// Get 获取一个值
func (r *Redis) Get(key string) interface{} {
conn := r.conn.Get()
defer conn.Close()
@@ -73,7 +74,7 @@ func (r *Redis) Get(key string) interface{} {
return reply
}
//Set 设置一个值
// Set 设置一个值
func (r *Redis) Set(key string, val interface{}, timeout time.Duration) (err error) {
conn := r.conn.Get()
defer conn.Close()
@@ -88,7 +89,7 @@ func (r *Redis) Set(key string, val interface{}, timeout time.Duration) (err err
return
}
//IsExist 判断key是否存在
// IsExist 判断key是否存在
func (r *Redis) IsExist(key string) bool {
conn := r.conn.Get()
defer conn.Close()
@@ -98,7 +99,7 @@ func (r *Redis) IsExist(key string) bool {
return i > 0
}
//Delete 删除
// Delete 删除
func (r *Redis) Delete(key string) error {
conn := r.conn.Get()
defer conn.Close()

View File

@@ -1,6 +1,6 @@
package credential
//AccessTokenHandle AccessToken 接口
// AccessTokenHandle AccessToken 接口
type AccessTokenHandle interface {
GetAccessToken() (accessToken string, err error)
}

View File

@@ -11,15 +11,19 @@ import (
)
const (
//AccessTokenURL 获取access_token的接口
accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token"
//CacheKeyOfficialAccountPrefix 微信公众号cache key前缀
// AccessTokenURL 获取access_token的接口
accessTokenURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
// AccessTokenURL 企业微信获取access_token的接口
workAccessTokenURL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
// CacheKeyOfficialAccountPrefix 微信公众号cache key前缀
CacheKeyOfficialAccountPrefix = "gowechat_officialaccount_"
//CacheKeyMiniProgramPrefix 小程序cache key前缀
// CacheKeyMiniProgramPrefix 小程序cache key前缀
CacheKeyMiniProgramPrefix = "gowechat_miniprogram_"
// CacheKeyWorkPrefix 企业微信cache key前缀
CacheKeyWorkPrefix = "gowechat_work_"
)
//DefaultAccessToken 默认AccessToken 获取
// DefaultAccessToken 默认AccessToken 获取
type DefaultAccessToken struct {
appID string
appSecret string
@@ -28,7 +32,7 @@ type DefaultAccessToken struct {
accessTokenLock *sync.Mutex
}
//NewDefaultAccessToken new DefaultAccessToken
// NewDefaultAccessToken new DefaultAccessToken
func NewDefaultAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenHandle {
if cache == nil {
panic("cache is ineed")
@@ -42,7 +46,7 @@ func NewDefaultAccessToken(appID, appSecret, cacheKeyPrefix string, cache cache.
}
}
//ResAccessToken struct
// ResAccessToken struct
type ResAccessToken struct {
util.CommonError
@@ -50,22 +54,26 @@ type ResAccessToken struct {
ExpiresIn int64 `json:"expires_in"`
}
//GetAccessToken 获取access_token,先从cache中获取没有则从服务端获取
// GetAccessToken 获取access_token,先从cache中获取没有则从服务端获取
func (ak *DefaultAccessToken) GetAccessToken() (accessToken string, err error) {
//加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
// 先从cache中取
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID)
if val := ak.cache.Get(accessTokenCacheKey); val != nil {
return val.(string), nil
}
// 加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
ak.accessTokenLock.Lock()
defer ak.accessTokenLock.Unlock()
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.appID)
val := ak.cache.Get(accessTokenCacheKey)
if val != nil {
accessToken = val.(string)
return
// 双检,防止重复从微信服务器获取
if val := ak.cache.Get(accessTokenCacheKey); val != nil {
return val.(string), nil
}
//cache失效从微信服务器获取
// cache失效从微信服务器获取
var resAccessToken ResAccessToken
resAccessToken, err = GetTokenFromServer(ak.appID, ak.appSecret)
resAccessToken, err = GetTokenFromServer(fmt.Sprintf(accessTokenURL, ak.appID, ak.appSecret))
if err != nil {
return
}
@@ -79,9 +87,59 @@ func (ak *DefaultAccessToken) GetAccessToken() (accessToken string, err error) {
return
}
//GetTokenFromServer 强制从微信服务器获取token
func GetTokenFromServer(appID, appSecret string) (resAccessToken ResAccessToken, err error) {
url := fmt.Sprintf("%s?grant_type=client_credential&appid=%s&secret=%s", accessTokenURL, appID, appSecret)
// WorkAccessToken 企业微信AccessToken 获取
type WorkAccessToken struct {
CorpID string
CorpSecret string
cacheKeyPrefix string
cache cache.Cache
accessTokenLock *sync.Mutex
}
// NewWorkAccessToken new WorkAccessToken
func NewWorkAccessToken(corpID, corpSecret, cacheKeyPrefix string, cache cache.Cache) AccessTokenHandle {
if cache == nil {
panic("cache the not exist")
}
return &WorkAccessToken{
CorpID: corpID,
CorpSecret: corpSecret,
cache: cache,
cacheKeyPrefix: cacheKeyPrefix,
accessTokenLock: new(sync.Mutex),
}
}
// GetAccessToken 企业微信获取access_token,先从cache中获取没有则从服务端获取
func (ak *WorkAccessToken) GetAccessToken() (accessToken string, err error) {
// 加上lock是为了防止在并发获取token时cache刚好失效导致从微信服务器上获取到不同token
ak.accessTokenLock.Lock()
defer ak.accessTokenLock.Unlock()
accessTokenCacheKey := fmt.Sprintf("%s_access_token_%s", ak.cacheKeyPrefix, ak.CorpID)
val := ak.cache.Get(accessTokenCacheKey)
if val != nil {
accessToken = val.(string)
return
}
// cache失效从微信服务器获取
var resAccessToken ResAccessToken
resAccessToken, err = GetTokenFromServer(fmt.Sprintf(workAccessTokenURL, ak.CorpID, ak.CorpSecret))
if err != nil {
return
}
expires := resAccessToken.ExpiresIn - 1500
err = ak.cache.Set(accessTokenCacheKey, resAccessToken.AccessToken, time.Duration(expires)*time.Second)
if err != nil {
return
}
accessToken = resAccessToken.AccessToken
return
}
// GetTokenFromServer 强制从微信服务器获取token
func GetTokenFromServer(url string) (resAccessToken ResAccessToken, err error) {
var body []byte
body, err = util.HTTPGet(url)
if err != nil {
@@ -91,7 +149,7 @@ func GetTokenFromServer(appID, appSecret string) (resAccessToken ResAccessToken,
if err != nil {
return
}
if resAccessToken.ErrMsg != "" {
if resAccessToken.ErrCode != 0 {
err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.ErrCode, resAccessToken.ErrMsg)
return
}

View File

@@ -7,6 +7,7 @@ import (
"gopkg.in/h2non/gock.v1"
)
// TestGetTicketFromServer .
func TestGetTicketFromServer(t *testing.T) {
defer gock.Off()
gock.New(getTicketURL).Reply(200).JSON(&ResTicket{Ticket: "mock-ticket", ExpiresIn: 10})

View File

@@ -10,19 +10,19 @@ import (
"github.com/silenceper/wechat/v2/util"
)
//获取ticket的url
// getTicketURL 获取ticket的url
const getTicketURL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi"
//DefaultJsTicket 默认获取js ticket方法
// DefaultJsTicket 默认获取js ticket方法
type DefaultJsTicket struct {
appID string
cacheKeyPrefix string
cache cache.Cache
//jsAPITicket 读写锁 同一个AppID一个
// jsAPITicket 读写锁 同一个AppID一个
jsAPITicketLock *sync.Mutex
}
//NewDefaultJsTicket new
// NewDefaultJsTicket new
func NewDefaultJsTicket(appID string, cacheKeyPrefix string, cache cache.Cache) JsTicketHandle {
return &DefaultJsTicket{
appID: appID,
@@ -40,18 +40,22 @@ type ResTicket struct {
ExpiresIn int64 `json:"expires_in"`
}
//GetTicket 获取jsapi_ticket
// GetTicket 获取jsapi_ticket
func (js *DefaultJsTicket) GetTicket(accessToken string) (ticketStr string, err error) {
// 先从cache中取
jsAPITicketCacheKey := fmt.Sprintf("%s_jsapi_ticket_%s", js.cacheKeyPrefix, js.appID)
if val := js.cache.Get(jsAPITicketCacheKey); val != nil {
return val.(string), nil
}
js.jsAPITicketLock.Lock()
defer js.jsAPITicketLock.Unlock()
//先从cache中
jsAPITicketCacheKey := fmt.Sprintf("%s_jsapi_ticket_%s", js.cacheKeyPrefix, js.appID)
val := js.cache.Get(jsAPITicketCacheKey)
if val != nil {
ticketStr = val.(string)
return
// 双检,防止重复从微信服务器获
if val := js.cache.Get(jsAPITicketCacheKey); val != nil {
return val.(string), nil
}
var ticket ResTicket
ticket, err = GetTicketFromServer(accessToken)
if err != nil {
@@ -63,7 +67,7 @@ func (js *DefaultJsTicket) GetTicket(accessToken string) (ticketStr string, err
return
}
//GetTicketFromServer 从服务器中获取ticket
// GetTicketFromServer 从服务器中获取ticket
func GetTicketFromServer(accessToken string) (ticket ResTicket, err error) {
var response []byte
url := fmt.Sprintf(getTicketURL, accessToken)

View File

@@ -1,7 +1,7 @@
package credential
//JsTicketHandle js ticket获取
// JsTicketHandle js ticket获取
type JsTicketHandle interface {
//GetTicket 获取ticket
// GetTicket 获取ticket
GetTicket(accessToken string) (ticket string, err error)
}

18
doc/api/README.md Normal file
View File

@@ -0,0 +1,18 @@
# API 文档
已完成以及未完成API列表汇总
如果有兴趣参与贡献可以在具体的API表格后面标识自己为贡献者以及完成时间例如
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者|完成时间|
| :---------------------: | -------- | :------------------------- | ---------- | -------- |-------- |-------- |
| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | NO | |silenceper| 2021-12-20|
- [微信公众号](./officialaccount.md)
- [小程序](./miniprogram.md)
- [小游戏](./minigame.md)
- [开放平台](./oplatform.md)
- [微信支付](./wxpay.md)
- [企业微信](./work.md)
- [智能对话](./aispeech.md)

2
doc/api/aispeech.md Normal file
View File

@@ -0,0 +1,2 @@
# 智能对话
TODO

2
doc/api/minigame.md Normal file
View File

@@ -0,0 +1,2 @@
# 小游戏
TODO

2
doc/api/miniprogram.md Normal file
View File

@@ -0,0 +1,2 @@
# 小程序
TODO

180
doc/api/officialaccount.md Normal file
View File

@@ -0,0 +1,180 @@
# 微信公众号API列表
## 基础接口
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| :---------------------: | -------- | :------------------------- | ---------- | -------- |
| 获取Access token | GET | /cgi-bin/token | YES | |
| 获取微信服务器IP地址 | GET | /cgi-bin/get_api_domain_ip | YES | |
| 获取微信callback IP地址 | GET | /cgi-bin/getcallbackip | YES | |
| 清理接口调用次数 | POST | /cgi-bin/clear_quota | YES | |
## 订阅通知
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------- | -------- | -------------------------------------- | ---------- | ----------------------- |
| 选用模板 | POST | /wxaapi/newtmpl/addtemplate | YES | (tpl *Subscribe) Add |
| 删除模板 | POST | /wxaapi/newtmpl/deltemplate | YES | (tpl *Subscribe) Delete |
| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | NO | |
| 获取模板中的关键词 | GET | /wxaapi/newtmpl/getpubtemplatekeywords | NO | |
| 获取类目下的公共模板 | GET | /wxaapi/newtmpl/getpubtemplatetitles | NO | |
| 获取私有模板列表 | GET | /wxaapi/newtmpl/gettemplate | YES | (tpl *Subscribe) List() |
| 发送订阅通知 | POST | /cgi-bin/message/subscribe/bizsend | YES | (tpl *Subscribe) Send |
## 客服消息
### PC 客服能力
#### 客服管理
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ---------------- | --------- | -------------------------------------- | ---------- | -------- |
| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | NO | |
| 添加客服帐号 | POST | /customservice/kfaccount/add | NO | |
| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | NO | |
| 设置客服信息 | POST | /customservice/kfaccount/update | NO | |
| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | NO | |
| 删除客服帐号 | GET | /customservice/kfaccount/del | NO | |
#### 会话控制
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Session_control.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | --------------------------------------- | ---------- | -------- |
| 创建会话 | POST | /customservice/kfsession/create | NO | |
| 获取客户会话状态 | GET | /customservice/kfsession/getsession | NO | |
| 获取客服会话列表 | GET | /customservice/kfsession/getsessionlist | NO | |
| 获取未接入会话列表 | POST | /customservice/kfsession/getwaitcase | NO | |
#### 获取聊天记录
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Obtain_chat_transcript.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------ | -------- | ----------------------------------- | ---------- | -------- |
| 获取聊天记录 | POST | /customservice/msgrecord/getmsglist | NO | |
### 对话能力
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/guide.html)
#### 顾问管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------ | -------- | -------------------------------------- | ---------- | -------- |
| 添加顾问 | POST | /cgi-bin/guide/addguideacct | NO | |
| 获取顾问信息 | POST | /cgi-bin/guide/getguideacct | NO | |
| 修改顾问信息 | POST | /cgi-bin/guide/updateguideacct | NO | |
| 删除顾问 | POST | /cgi-bin/guide/delguideacct | NO | |
| 获取服务号顾问列表 | POST | /cgi-bin/guide/getguideacctlist | NO | |
| 生成顾问二维码 | POST | /cgi-bin/guide/guidecreateqrcode | NO | |
| 获取顾问聊天记录 | POST | /cgi-bin/guide/getguidebuyerchatrecord | NO | |
| 设置快捷回复与关注自动回复 | POST | /cgi-bin/guide/setguideconfig | NO | |
| 获取快捷回复与关注自动回复 | POST | /cgi-bin/guide/getguideconfig | NO | |
| 设置敏感词与离线自动回复 | POST | /cgi-bin/guide/setguideacctconfig | NO | |
| 获取离线自动回复与敏感词 | POST | /cgi-bin/guide/getguideacctconfig | NO | |
| 允许微信用户复制小程序页面路径 | POST | /cgi-bin/guide/pushshowwxapathmenu | NO | |
| 新建顾问分组 | POST | /cgi-bin/guide/newguidegroup | NO | |
| 获取顾问分组列表 | POST | /cgi-bin/guide/getguidegrouplist | NO | |
| 获取顾问分组信息 | POST | /cgi-bin/guide/getgroupinfo | NO | |
| 分组内添加顾问 | POST | /cgi-bin/guide/addguide2guidegroup | NO | |
| 分组内删除顾问 | POST | /cgi-bin/guide/delguide2guidegroup | NO | |
| 获取顾问所在分组 | POST | /cgi-bin/guide/getgroupbyguide | NO | |
| 删除指定顾问分组 | POST | /cgi-bin/guide/delguidegroup | NO | |
#### 客户管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------ | -------- | ------------------------------------------- | ---------- | -------- |
| 为顾问分配客户 | POST | /cgi-bin/guide/addguidebuyerrelation | NO | |
| 为顾问移除客户 | POST | /cgi-bin/guide/delguidebuyerrelation | NO | |
| 获取顾问的客户列表 | POST | /cgi-bin/guide/getguidebuyerrelationlist | NO | |
| 为客户更换顾问 | POST | /cgi-bin/guide/rebindguideacctforbuyer | NO | |
| 修改客户昵称 | POST | /cgi-bin/guide/updateguidebuyerrelation | NO | |
| 查询客户所属顾问 | POST | /cgi-bin/guide/getguidebuyerrelationbybuyer | NO | |
| 查询指定顾问和客户的关系 | POST | /cgi-bin/guide/getguidebuyerrelation | NO | |
#### 标签管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | -------------------------------------- | ---------- | -------- |
| 新建标签类型 | POST | /cgi-bin/guide/newguidetagoption | NO | |
| 删除标签类型 | POST | /cgi-bin/guide/delguidetagoption | NO | |
| 为标签添加可选值 | POST | /cgi-bin/guide/addguidetagoption | NO | |
| 获取标签和可选值 | POST | /cgi-bin/guide/getguidetagoption | NO | |
| 为客户设置标签 | POST | /cgi-bin/guide/addguidebuyertag | NO | |
| 查询客户标签 | POST | /cgi-bin/guide/getguidebuyertag | NO | |
| 根据标签值筛选客户 | POST | /cgi-bin/guide/queryguidebuyerbytag | NO | |
| 删除客户标签 | POST | /cgi-bin/guide/delguidebuyertag | NO | |
| 设置自定义客户信息 | POST | /cgi-bin/guide/addguidebuyerdisplaytag | NO | |
| 获取自定义客户信息 | POST | /cgi-bin/guide/getguidebuyerdisplaytag | NO | |
#### 素材管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------ | -------- | ------------------------------------ | ---------- | -------- |
| 添加小程序卡片素材 | POST | /cgi-bin/guide/setguidecardmaterial | NO | |
| 查询小程序卡片素材 | POST | /cgi-bin/guide/getguidecardmaterial | NO | |
| 删除小程序卡片素材 | POST | /cgi-bin/guide/delguidecardmaterial | NO | |
| 添加图片素材 | POST | /cgi-bin/guide/setguideimagematerial | NO | |
| 查询图片素材 | POST | /cgi-bin/guide/getguideimagematerial | NO | |
| 删除图片素材 | POST | /cgi-bin/guide/delguideimagematerial | NO | |
| 添加文字素材 | POST | /cgi-bin/guide/setguidewordmaterial | NO | |
| 查询文字素材 | POST | /cgi-bin/guide/getguidewordmaterial | NO | |
| 删除文字素材 | POST | /cgi-bin/guide/delguidewordmaterial | NO | |
#### 群发任务管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
| 获取群发任务列表 | POST | /cgi-bin/guide/getguidemassendjoblist | NO | |
| 获取指定群发任务信息 | POST | /cgi-bin/guide/getguidemassendjob | NO | |
| 修改群发任务 | POST | /cgi-bin/guide/updateguidemassendjob | NO | |
| 取消群发任务 | POST | /cgi-bin/guide/cancelguidemassendjob | NO | |
## 微信网页开发
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
| ------------------------------------------------------------ | -------- | --------------------------------------------------- | ---------- | ----------------------------------- |
| 获取跳转的url地址 | GET | https://open.weixin.qq.com/connect/oauth2/authorize | YES | (oauth *Oauth) GetRedirectURL |
| 获取网页应用跳转的url地址 | GET | https://open.weixin.qq.com/connect/qrconnect | YES | (oauth *Oauth) GetWebAppRedirectURL |
| 通过网页授权的code 换取access_token(区别于context中的access_token) | GET | /sns/oauth2/access_token | YES | (oauth *Oauth) GetUserAccessToken |
| 刷新access_token | GET | /sns/oauth2/refresh_token? | YES | (oauth *Oauth) RefreshAccessToken |
| 检验access_token是否有效 | GET | /sns/auth | YES | (oauth *Oauth) CheckAccessToken( |
| 拉取用户信息(需scope为 snsapi_userinfo) | GET | /sns/userinfo | YES | (oauth *Oauth) GetUserInfo |
| 获取jssdk需要的配置参数 | GET | /cgi-bin/ticket/getticket | YES | (js *Js) GetConfig |
## 素材管理
## 图文消息留言管理
## 用户管理
## 账号管理
## 数据统计
## 微信卡券
## 微信门店
## 智能接口
## 微信设备功能
## 微信“一物一码”
## 微信发票
## 微信非税缴费

1
doc/api/oplatform.md Normal file
View File

@@ -0,0 +1 @@
# 开放平台

65
doc/api/work.md Normal file
View File

@@ -0,0 +1,65 @@
# 企业微信
host: https://qyapi.weixin.qq.com/
## 微信客服
[官方文档](https://work.weixin.qq.com/api/doc/90000/90135/94638)
### 客服账号管理
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94684)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
| 添加客服帐号 | POST | /cgi-bin/kf/account/add | YES | (r *Client) AccountAdd | NICEXAI |
| 删除客服帐号 | POST | /cgi-bin/kf/account/del | YES | (r *Client) AccountDel | NICEXAI |
| 修改客服帐号 | POST | /cgi-bin/kf/account/update | YES | (r *Client) AccountUpdate | NICEXAI |
| 获取客服帐号列表 | GET | /cgi-bin/kf/account/list | YES | (r *Client) AccountList | NICEXAI |
| 获取客服帐号链接 | GET | /cgi-bin/kf/add_contact_way | YES | (r *Client) AddContactWay | NICEXAI |
### 接待人员列表
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94693)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
| 添加接待人员 | POST | /cgi-bin/kf/servicer/add | YES | (r *Client) ReceptionistAdd | NICEXAI |
| 删除接待人员 | POST | /cgi-bin/kf/servicer/del | YES | (r *Client) ReceptionistDel | NICEXAI |
| 获取接待人员列表 | GET | /cgi-bin/kf/servicer/list | YES | (r *Client) ReceptionistList | NICEXAI |
### 会话分配与消息收发
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94694)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------------| ---------- | ------------------------------- |------------|
| 获取会话状态 | POST | /cgi-bin/kf/service_state/get | YES | (r *Client) ServiceStateGet | NICEXAI |
| 变更会话状态 | POST | /cgi-bin/kf/service_state/trans | YES | (r *Client) ServiceStateTrans | NICEXAI |
| 读取消息 | POST | /cgi-bin/kf/sync_msg | YES | (r *Client) SyncMsg | NICEXAI |
| 发送消息 | POST | /cgi-bin/kf/send_msg | YES | (r *Client) SendMsg | NICEXAI |
| 发送事件响应消息 | POST | /cgi-bin/kf/send_msg_on_event | YES | (r *Client) SendMsgOnEvent | NICEXAI |
### 升级服务配置
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94702)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
| :--------------: | -------- | :-------------------------------------------------| ---------- | ------------------------------- |------------|
| 获取配置的专员与客户群 | POST | /cgi-bin/kf/customer/get_upgrade_service_config | YES | (r *Client) UpgradeServiceConfig | NICEXAI |
| 为客户升级为专员或客户群服务 | POST | /cgi-bin/kf/customer/upgrade_service | YES | (r *Client) UpgradeService | NICEXAI |
| 为客户取消推荐 | POST | /cgi-bin/kf/customer/cancel_upgrade_service | YES | (r *Client) UpgradeServiceCancel | NICEXAI |
### 其他基础信息获取
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/95148)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| :--------------: | -------- | :---------------------------------------| ---------- | ------------------------------- |------------|
| 获取客户基础信息 | POST | /cgi-bin/kf/customer/batchget | YES | (r *Client) CustomerBatchGet | NICEXAI |
| 获取视频号绑定状态 | GET | /cgi-bin/kf/get_corp_qualification | YES | (r *Client) GetCorpQualification | NICEXAI |
## 应用管理
TODO

2
doc/api/wxpay.md Normal file
View File

@@ -0,0 +1,2 @@
# 微信支付
TODO

14
go.mod
View File

@@ -5,10 +5,14 @@ go 1.14
require (
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
github.com/fatih/structs v1.1.0
github.com/gomodule/redigo v1.8.1
github.com/sirupsen/logrus v1.6.0
github.com/gomodule/redigo v1.8.5
github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cast v1.3.1
github.com/stretchr/testify v1.5.1
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/h2non/gock.v1 v1.0.15
)
)

40
go.sum
View File

@@ -1,43 +1,49 @@
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/gomodule/redigo v1.8.1 h1:Abmo0bI7Xf0IhdIPc7HZQzZcShdnmxeoVuDDtIQp8N8=
github.com/gomodule/redigo v1.8.1/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -32,12 +32,12 @@ const (
getAnalysisVisitPageURL = "https://api.weixin.qq.com/datacube/getweanalysisappidvisitpage?access_token=%s"
)
//Analysis analyis 数据分析
// Analysis analyis 数据分析
type Analysis struct {
*context.Context
}
//NewAnalysis new
// NewAnalysis new
func NewAnalysis(ctx *context.Context) *Analysis {
return &Analysis{ctx}
}

View File

@@ -1,6 +1,7 @@
package auth
import (
context2 "context"
"encoding/json"
"fmt"
@@ -10,14 +11,16 @@ import (
const (
code2SessionURL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
checkEncryptedDataURL = "https://api.weixin.qq.com/wxa/business/checkencryptedmsg?access_token=%s"
)
//Auth 登录/用户信息
// Auth 登录/用户信息
type Auth struct {
*context.Context
}
//NewAuth new auth
// NewAuth new auth
func NewAuth(ctx *context.Context) *Auth {
return &Auth{ctx}
}
@@ -31,16 +34,26 @@ type ResCode2Session struct {
UnionID string `json:"unionid"` // 用户在开放平台的唯一标识符在满足UnionID下发条件的情况下会返回
}
//Code2Session 登录凭证校验。
// RspCheckEncryptedData .
type RspCheckEncryptedData struct {
util.CommonError
Vaild bool `json:"vaild"` // 是否是合法的数据
CreateTime uint `json:"create_time"` // 加密数据生成的时间戳
}
// Code2Session 登录凭证校验。
func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) {
urlStr := fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)
return auth.Code2SessionContext(context2.Background(), jsCode)
}
// Code2SessionContext 登录凭证校验。
func (auth *Auth) Code2SessionContext(ctx context2.Context, jsCode string) (result ResCode2Session, err error) {
var response []byte
response, err = util.HTTPGet(urlStr)
if err != nil {
if response, err = util.HTTPGetContext(ctx, fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)); err != nil {
return
}
err = json.Unmarshal(response, &result)
if err != nil {
if err = json.Unmarshal(response, &result); err != nil {
return
}
if result.ErrCode != 0 {
@@ -50,7 +63,30 @@ func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error
return
}
//GetPaidUnionID 用户支付完成后,获取该用户的 UnionId无需用户授权
// GetPaidUnionID 用户支付完成后,获取该用户的 UnionId无需用户授权
func (auth *Auth) GetPaidUnionID() {
//TODO
// TODO
}
// CheckEncryptedData .检查加密信息是否由微信生成当前只支持手机号加密数据只能检测最近3天生成的加密数据
func (auth *Auth) CheckEncryptedData(encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
return auth.CheckEncryptedDataContext(context2.Background(), encryptedMsgHash)
}
// CheckEncryptedDataContext .检查加密信息是否由微信生成当前只支持手机号加密数据只能检测最近3天生成的加密数据
func (auth *Auth) CheckEncryptedDataContext(ctx context2.Context, encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
var response []byte
var (
at string
)
if at, err = auth.GetAccessToken(); err != nil {
return
}
if response, err = util.HTTPPostContext(ctx, fmt.Sprintf(checkEncryptedDataURL, at), "encrypted_msg_hash="+encryptedMsgHash); err != nil {
return
}
if err = util.DecodeWithError(response, &result, "CheckEncryptedDataAuth"); err != nil {
return
}
return
}

View File

@@ -1,13 +1,13 @@
//Package config 小程序config配置
// Package config 小程序config配置
package config
import (
"github.com/silenceper/wechat/v2/cache"
)
//Config config for 小程序
// Config .config for 小程序
type Config struct {
AppID string `json:"app_id"` //appid
AppSecret string `json:"app_secret"` //appsecret
AppID string `json:"app_id"` // appid
AppSecret string `json:"app_secret"` // appsecret
Cache cache.Cache
}

View File

@@ -0,0 +1,61 @@
package content
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
checkTextURL = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=%s"
checkImageURL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s"
)
// Content 内容安全
type Content struct {
*context.Context
}
// NewContent 内容安全接口
func NewContent(ctx *context.Context) *Content {
return &Content{ctx}
}
// CheckText 检测文字
// @text 需要检测的文字
func (content *Content) CheckText(text string) error {
accessToken, err := content.GetAccessToken()
if err != nil {
return err
}
response, err := util.PostJSON(
fmt.Sprintf(checkTextURL, accessToken),
map[string]string{
"content": text,
},
)
if err != nil {
return err
}
return util.DecodeWithCommonError(response, "ContentCheckText")
}
// CheckImage 检测图片
// 所传参数为要检测的图片文件的绝对路径图片格式支持PNG、JPEG、JPG、GIF, 像素不超过 750 x 1334同时文件大小以不超过 300K 为宜,否则可能报错
// @media 图片文件的绝对路径
func (content *Content) CheckImage(media string) error {
accessToken, err := content.GetAccessToken()
if err != nil {
return err
}
response, err := util.PostFile(
"media",
media,
fmt.Sprintf(checkImageURL, accessToken),
)
if err != nil {
return err
}
return util.DecodeWithCommonError(response, "ContentCheckImage")
}

View File

@@ -6,16 +6,17 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
)
//Encryptor struct
// Encryptor struct
type Encryptor struct {
*context.Context
}
//NewEncryptor 实例
// NewEncryptor 实例
func NewEncryptor(context *context.Context) *Encryptor {
basic := new(Encryptor)
basic.Context = context
@@ -45,6 +46,8 @@ type PlainData struct {
AvatarURL string `json:"avatarUrl"`
Language string `json:"language"`
PhoneNumber string `json:"phoneNumber"`
OpenGID string `json:"openGId"`
MsgTicket string `json:"msgTicket"`
PurePhoneNumber string `json:"purePhoneNumber"`
CountryCode string `json:"countryCode"`
Watermark struct {
@@ -74,8 +77,8 @@ func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
return data[:len(data)-n], nil
}
// getCipherText returns slice of the cipher text
func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
// GetCipherText returns slice of the cipher text
func GetCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
if err != nil {
return nil, err
@@ -88,6 +91,9 @@ func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
if err != nil {
return nil, err
}
if len(ivBytes) != aes.BlockSize {
return nil, fmt.Errorf("bad iv length %d", len(ivBytes))
}
block, err := aes.NewCipher(aesKey)
if err != nil {
return nil, err
@@ -103,7 +109,7 @@ func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
// Decrypt 解密数据
func (encryptor *Encryptor) Decrypt(sessionKey, encryptedData, iv string) (*PlainData, error) {
cipherText, err := getCipherText(sessionKey, encryptedData, iv)
cipherText, err := GetCipherText(sessionKey, encryptedData, iv)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,15 @@
package encryptor
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetCipherText_BadIV(t *testing.T) {
keyData := base64.StdEncoding.EncodeToString([]byte("1234567890123456"))
badData := base64.StdEncoding.EncodeToString([]byte("1"))
_, err := GetCipherText(keyData, badData, badData)
assert.Error(t, err)
}

View File

@@ -12,13 +12,13 @@ type EventType string
type InfoType string
const (
//MsgTypeText 文本消息
// MsgTypeText 文本消息
MsgTypeText MsgType = "text"
//MsgTypeImage 图片消息
// MsgTypeImage 图片消息
MsgTypeImage = "image"
//MsgTypeLink 图文链接
// MsgTypeLink 图文链接
MsgTypeLink = "link"
//MsgTypeMiniProgramPage 小程序卡片
// MsgTypeMiniProgramPage 小程序卡片
MsgTypeMiniProgramPage = "miniprogrampage"
)

View File

@@ -11,29 +11,29 @@ const (
customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
)
//Manager 消息管理者,可以发送消息
// Manager 消息管理者,可以发送消息
type Manager struct {
*context.Context
}
//NewCustomerMessageManager 实例化消息管理者
// NewCustomerMessageManager 实例化消息管理者
func NewCustomerMessageManager(context *context.Context) *Manager {
return &Manager{
context,
}
}
//MediaText 文本消息的文字
// MediaText 文本消息的文字
type MediaText struct {
Content string `json:"content"`
}
//MediaResource 消息使用的临时素材id
// MediaResource 消息使用的临时素材id
type MediaResource struct {
MediaID string `json:"media_id"`
}
//MediaMiniprogrampage 小程序卡片
// MediaMiniprogrampage 小程序卡片
type MediaMiniprogrampage struct {
Title string `json:"title"`
Appid string `json:"appid"`
@@ -49,17 +49,17 @@ type MediaLink struct {
ThumbURL string `json:"thumb_url"`
}
//CustomerMessage 客服消息
// CustomerMessage 客服消息
type CustomerMessage struct {
ToUser string `json:"touser"` //接受者OpenID
Msgtype MsgType `json:"msgtype"` //客服消息类型
Text *MediaText `json:"text,omitempty"` //可选
Image *MediaResource `json:"image,omitempty"` //可选
Link *MediaLink `json:"link,omitempty"` //可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` //可选
ToUser string `json:"touser"` // 接受者OpenID
Msgtype MsgType `json:"msgtype"` // 客服消息类型
Text *MediaText `json:"text,omitempty"` // 可选
Image *MediaResource `json:"image,omitempty"` // 可选
Link *MediaLink `json:"link,omitempty"` // 可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` // 可选
}
//NewCustomerTextMessage 文本消息结构体构造方法
// NewCustomerTextMessage 文本消息结构体构造方法
func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -70,7 +70,7 @@ func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
}
}
//NewCustomerImgMessage 图片消息的构造方法
// NewCustomerImgMessage 图片消息的构造方法
func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -81,7 +81,7 @@ func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
}
}
//NewCustomerLinkMessage 图文链接消息的构造方法
// NewCustomerLinkMessage 图文链接消息的构造方法
func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -95,7 +95,7 @@ func NewCustomerLinkMessage(toUser, title, description, url, thumbURL string) *C
}
}
//NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
// NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -108,7 +108,7 @@ func NewCustomerMiniprogrampageMessage(toUser, title, pagepath, thumbMediaID str
}
}
//Send 发送客服消息
// Send 发送客服消息
func (manager *Manager) Send(msg *CustomerMessage) error {
accessToken, err := manager.Context.GetAccessToken()
if err != nil {

View File

@@ -5,20 +5,24 @@ import (
"github.com/silenceper/wechat/v2/miniprogram/analysis"
"github.com/silenceper/wechat/v2/miniprogram/auth"
"github.com/silenceper/wechat/v2/miniprogram/config"
"github.com/silenceper/wechat/v2/miniprogram/content"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
"github.com/silenceper/wechat/v2/miniprogram/message"
"github.com/silenceper/wechat/v2/miniprogram/qrcode"
"github.com/silenceper/wechat/v2/miniprogram/shortlink"
"github.com/silenceper/wechat/v2/miniprogram/subscribe"
"github.com/silenceper/wechat/v2/miniprogram/tcb"
"github.com/silenceper/wechat/v2/miniprogram/urllink"
"github.com/silenceper/wechat/v2/miniprogram/werun"
)
//MiniProgram 微信小程序相关API
// MiniProgram 微信小程序相关API
type MiniProgram struct {
ctx *context.Context
}
//NewMiniProgram 实例化小程序API
// NewMiniProgram 实例化小程序API
func NewMiniProgram(cfg *config.Config) *MiniProgram {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyMiniProgramPrefix, cfg.Cache)
ctx := &context.Context{
@@ -28,7 +32,7 @@ func NewMiniProgram(cfg *config.Config) *MiniProgram {
return &MiniProgram{ctx}
}
//SetAccessTokenHandle 自定义access_token获取方式
// SetAccessTokenHandle 自定义access_token获取方式
func (miniProgram *MiniProgram) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) {
miniProgram.ctx.AccessTokenHandle = accessTokenHandle
}
@@ -43,27 +47,27 @@ func (miniProgram *MiniProgram) GetEncryptor() *encryptor.Encryptor {
return encryptor.NewEncryptor(miniProgram.ctx)
}
//GetAuth 登录/用户信息相关接口
// GetAuth 登录/用户信息相关接口
func (miniProgram *MiniProgram) GetAuth() *auth.Auth {
return auth.NewAuth(miniProgram.ctx)
}
//GetAnalysis 数据分析
// GetAnalysis 数据分析
func (miniProgram *MiniProgram) GetAnalysis() *analysis.Analysis {
return analysis.NewAnalysis(miniProgram.ctx)
}
//GetQRCode 小程序码相关API
// GetQRCode 小程序码相关API
func (miniProgram *MiniProgram) GetQRCode() *qrcode.QRCode {
return qrcode.NewQRCode(miniProgram.ctx)
}
//GetTcb 小程序云开发API
// GetTcb 小程序云开发API
func (miniProgram *MiniProgram) GetTcb() *tcb.Tcb {
return tcb.NewTcb(miniProgram.ctx)
}
//GetSubscribe 小程序订阅消息
// GetSubscribe 小程序订阅消息
func (miniProgram *MiniProgram) GetSubscribe() *subscribe.Subscribe {
return subscribe.NewSubscribe(miniProgram.ctx)
}
@@ -72,3 +76,23 @@ func (miniProgram *MiniProgram) GetSubscribe() *subscribe.Subscribe {
func (miniProgram *MiniProgram) GetCustomerMessage() *message.Manager {
return message.NewCustomerMessageManager(miniProgram.ctx)
}
// GetWeRun 微信运动接口
func (miniProgram *MiniProgram) GetWeRun() *werun.WeRun {
return werun.NewWeRun(miniProgram.ctx)
}
// GetContentSecurity 内容安全接口
func (miniProgram *MiniProgram) GetContentSecurity() *content.Content {
return content.NewContent(miniProgram.ctx)
}
// GetURLLink 小程序URL Link接口
func (miniProgram *MiniProgram) GetURLLink() *urllink.URLLink {
return urllink.NewURLLink(miniProgram.ctx)
}
// GetShortLink 小程序短链接口
func (miniProgram *MiniProgram) GetShortLink() *shortlink.ShortLink {
return shortlink.NewShortLink(miniProgram.ctx)
}

View File

@@ -15,12 +15,12 @@ const (
getWXACodeUnlimitURL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s"
)
//QRCode struct
// QRCode struct
type QRCode struct {
*context.Context
}
//NewQRCode 实例
// NewQRCode 实例
func NewQRCode(context *context.Context) *QRCode {
qrCode := new(QRCode)
qrCode.Context = context

View File

@@ -0,0 +1,86 @@
package shortlink
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
generateShortLinkURL = "https://api.weixin.qq.com/wxa/genwxashortlink?access_token=%s"
)
// ShortLink 短链接
type ShortLink struct {
*context.Context
}
// NewShortLink 实例
func NewShortLink(ctx *context.Context) *ShortLink {
return &ShortLink{ctx}
}
// ShortLinker 请求结构体
type ShortLinker struct {
// pageUrl 通过 Short Link 进入的小程序页面路径,必须是已经发布的小程序存在的页面,可携带 query最大1024个字符
PageURL string `json:"page_url"`
// pageTitle 页面标题不能包含违法信息超过20字符会用... 截断代替
PageTitle string `json:"page_title"`
// isPermanent 生成的 Short Link 类型短期有效false永久有效true
IsPermanent bool `json:"is_permanent,omitempty"`
}
// resShortLinker 返回结构体
type resShortLinker struct {
// 通用错误
*util.CommonError
// 返回的 shortLink
Link string `json:"link"`
}
// Generate 生成 shortLink
func (shortLink *ShortLink) generate(shortLinkParams ShortLinker) (string, error) {
var accessToken string
accessToken, err := shortLink.GetAccessToken()
if err != nil {
return "", err
}
urlStr := fmt.Sprintf(generateShortLinkURL, accessToken)
response, err := util.PostJSON(urlStr, shortLinkParams)
if err != nil {
return "", err
}
// 使用通用方法返回错误
var res resShortLinker
err = util.DecodeWithError(response, &res, "GenerateShortLink")
if err != nil {
return "", err
}
return res.Link, nil
}
// GenerateShortLinkPermanent 生成永久shortLink
func (shortLink *ShortLink) GenerateShortLinkPermanent(PageURL, pageTitle string) (string, error) {
return shortLink.generate(ShortLinker{
PageURL: PageURL,
PageTitle: pageTitle,
IsPermanent: true,
})
}
// GenerateShortLinkTemp 生成临时shortLink
func (shortLink *ShortLink) GenerateShortLinkTemp(PageURL, pageTitle string) (string, error) {
return shortLink.generate(ShortLinker{
PageURL: PageURL,
PageTitle: pageTitle,
IsPermanent: false,
})
}

View File

@@ -8,13 +8,25 @@ import (
)
const (
//发送订阅消息
//https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
// 发送订阅消息
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"
// 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
getTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
// 添加订阅模板
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
addTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
// 删除私有模板
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.deleteTemplate.html
delTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
// 统一服务消息
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
uniformMessageSend = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"
)
// Subscribe 订阅消息
@@ -29,20 +41,21 @@ func NewSubscribe(ctx *context.Context) *Subscribe {
// Message 订阅消息请求参数
type Message struct {
ToUser string `json:"touser"` //必选,接收者(用户)的 openid
TemplateID string `json:"template_id"` //必选所需下发的订阅模板id
Page string `json:"page"` //可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。
Data map[string]*DataItem `json:"data"` //必选, 模板内容
MiniprogramState string `json:"miniprogram_state"` //可选跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版
Lang string `json:"lang"` //入小程序查看”的语言类型支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN
ToUser string `json:"touser"` // 必选,接收者(用户)的 openid
TemplateID string `json:"template_id"` // 必选所需下发的订阅模板id
Page string `json:"page"` // 可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。
Data map[string]*DataItem `json:"data"` // 必选, 模板内容
MiniprogramState string `json:"miniprogram_state"` // 可选跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版
Lang string `json:"lang"` // 入小程序查看”的语言类型支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN
}
//DataItem 模版内某个 .DATA 的值
// DataItem 模版内某个 .DATA 的值
type DataItem struct {
Value string `json:"value"`
Value interface{} `json:"value"`
Color string `json:"color"`
}
//TemplateItem template item
// TemplateItem template item
type TemplateItem struct {
PriTmplID string `json:"priTmplId"`
Title string `json:"title"`
@@ -51,7 +64,7 @@ type TemplateItem struct {
Type int64 `json:"type"`
}
//TemplateList template list
// TemplateList template list
type TemplateList struct {
util.CommonError
Data []TemplateItem `json:"data"`
@@ -72,7 +85,7 @@ func (s *Subscribe) Send(msg *Message) (err error) {
return util.DecodeWithCommonError(response, "Send")
}
//ListTemplates 获取当前帐号下的个人模板列表
// ListTemplates 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
func (s *Subscribe) ListTemplates() (*TemplateList, error) {
accessToken, err := s.GetAccessToken()
@@ -91,3 +104,92 @@ func (s *Subscribe) ListTemplates() (*TemplateList, error) {
}
return &templateList, nil
}
// UniformMessage 统一服务消息
type UniformMessage struct {
ToUser string `json:"touser"`
WeappTemplateMsg struct {
TemplateID string `json:"template_id"`
Page string `json:"page"`
FormID string `json:"form_id"`
Data map[string]*DataItem `json:"data"`
EmphasisKeyword string `json:"emphasis_keyword"`
} `json:"weapp_template_msg"`
MpTemplateMsg struct {
Appid string `json:"appid"`
TemplateID string `json:"template_id"`
URL string `json:"url"`
Miniprogram struct {
Appid string `json:"appid"`
Pagepath string `json:"pagepath"`
} `json:"miniprogram"`
Data map[string]*DataItem `json:"data"`
} `json:"mp_template_msg"`
}
// UniformSend 发送统一服务消息
func (s *Subscribe) UniformSend(msg *UniformMessage) (err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", uniformMessageSend, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "UniformSend")
}
type resSubscribeAdd struct {
util.CommonError
TemplateID string `json:"priTmplId"`
}
// Add 添加订阅消息模板
func (s *Subscribe) Add(ShortID string, kidList []int, sceneDesc string) (templateID string, err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateIDShort string `json:"tid"`
SceneDesc string `json:"sceneDesc"`
KidList []int `json:"kidList"`
}{TemplateIDShort: ShortID, SceneDesc: sceneDesc, KidList: kidList}
uri := fmt.Sprintf("%s?access_token=%s", addTemplateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
var result resSubscribeAdd
err = util.DecodeWithError(response, &result, "AddSubscribe")
if err != nil {
return
}
templateID = result.TemplateID
return
}
// Delete 删除私有模板
func (s *Subscribe) Delete(templateID string) (err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateID string `json:"priTmplId"`
}{TemplateID: templateID}
uri := fmt.Sprintf("%s?access_token=%s", delTemplateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "DeleteSubscribe")
}

View File

@@ -7,17 +7,17 @@ import (
)
const (
//触发云函数
// 触发云函数
invokeCloudFunctionURL = "https://api.weixin.qq.com/tcb/invokecloudfunction"
)
//InvokeCloudFunctionRes 云函数调用返回结果
// InvokeCloudFunctionRes 云函数调用返回结果
type InvokeCloudFunctionRes struct {
util.CommonError
RespData string `json:"resp_data"` //云函数返回的buffer
RespData string `json:"resp_data"` // 云函数返回的buffer
}
//InvokeCloudFunction 云函数调用
// InvokeCloudFunction 云函数调用
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/functions/invokeCloudFunction.html
func (tcb *Tcb) InvokeCloudFunction(env, name, args string) (*InvokeCloudFunctionRes, error) {
accessToken, err := tcb.GetAccessToken()

View File

@@ -7,191 +7,191 @@ import (
)
const (
//数据库导入
// 数据库导入
databaseMigrateImportURL = "https://api.weixin.qq.com/tcb/databasemigrateimport"
//数据库导出
// 数据库导出
databaseMigrateExportURL = "https://api.weixin.qq.com/tcb/databasemigrateexport"
//数据库迁移状态查询
// 数据库迁移状态查询
databaseMigrateQueryInfoURL = "https://api.weixin.qq.com/tcb/databasemigratequeryinfo"
//变更数据库索引
// 变更数据库索引
updateIndexURL = "https://api.weixin.qq.com/tcb/updateindex"
//新增集合
// 新增集合
databaseCollectionAddURL = "https://api.weixin.qq.com/tcb/databasecollectionadd"
//删除集合
// 删除集合
databaseCollectionDeleteURL = "https://api.weixin.qq.com/tcb/databasecollectiondelete"
//获取特定云环境下集合信息
// 获取特定云环境下集合信息
databaseCollectionGetURL = "https://api.weixin.qq.com/tcb/databasecollectionget"
//数据库插入记录
// 数据库插入记录
databaseAddURL = "https://api.weixin.qq.com/tcb/databaseadd"
//数据库删除记录
// 数据库删除记录
databaseDeleteURL = "https://api.weixin.qq.com/tcb/databasedelete"
//数据库更新记录
// 数据库更新记录
databaseUpdateURL = "https://api.weixin.qq.com/tcb/databaseupdate"
//数据库查询记录
// 数据库查询记录
databaseQueryURL = "https://api.weixin.qq.com/tcb/databasequery"
//统计集合记录数或统计查询语句对应的结果记录数
// 统计集合记录数或统计查询语句对应的结果记录数
databaseCountURL = "https://api.weixin.qq.com/tcb/databasecount"
//ConflictModeInster 冲突处理模式 插入
// ConflictModeInster 冲突处理模式 插入
ConflictModeInster ConflictMode = 1
//ConflictModeUpsert 冲突处理模式 更新
// ConflictModeUpsert 冲突处理模式 更新
ConflictModeUpsert ConflictMode = 2
//FileTypeJSON 的合法值 json
// FileTypeJSON 的合法值 json
FileTypeJSON FileType = 1
//FileTypeCsv 的合法值 csv
// FileTypeCsv 的合法值 csv
FileTypeCsv FileType = 2
)
//ConflictMode 冲突处理模式
// ConflictMode 冲突处理模式
type ConflictMode int
//FileType 文件上传和导出的允许文件类型
// FileType 文件上传和导出的允许文件类型
type FileType int
//ValidDirections 合法的direction值
// ValidDirections 合法的direction值
var ValidDirections = []string{"1", "-1", "2dsphere"}
//DatabaseMigrateExportReq 数据库出 请求参数
// DatabaseMigrateExportReq 数据库出 请求参数
type DatabaseMigrateExportReq struct {
Env string `json:"env,omitempty"` //云环境ID
FilePath string `json:"file_path,omitempty"` //导出文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传
FileType FileType `json:"file_type,omitempty"` //导出文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
Query string `json:"query,omitempty"` //导出条件
Env string `json:"env,omitempty"` // 云环境ID
FilePath string `json:"file_path,omitempty"` // 导出文件路径(导入文件需先上传到同环境的存储中,可使用开发者工具或 HTTP API的上传文件 API上传
FileType FileType `json:"file_type,omitempty"` // 导出文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
Query string `json:"query,omitempty"` // 导出条件
}
//DatabaseMigrateExportRes 数据库导出 返回结果
// DatabaseMigrateExportRes 数据库导出 返回结果
type DatabaseMigrateExportRes struct {
util.CommonError
JobID int64 `json:"job_id"` //导出任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
JobID int64 `json:"job_id"` // 导出任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
}
//DatabaseMigrateImportReq 数据库导入 请求参数
// DatabaseMigrateImportReq 数据库导入 请求参数
type DatabaseMigrateImportReq struct {
Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` //集合名称
FilePath string `json:"file_path,omitempty"` //导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接)
FileType FileType `json:"file_type,omitempty"` //导入文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
StopOnError bool `json:"stop_on_error,omitempty"` //是否在遇到错误时停止导入
ConflictMode ConflictMode `json:"conflict_mode,omitempty"` //冲突处理模式 1:inster 2:UPSERT
Env string `json:"env,omitempty"` // 云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称
FilePath string `json:"file_path,omitempty"` // 导出文件路径(文件会导出到同环境的云存储中,可使用获取下载链接 API 获取下载链接)
FileType FileType `json:"file_type,omitempty"` // 导入文件类型,文件格式参考数据库导入指引中的文件格式部分 1:json 2:csv
StopOnError bool `json:"stop_on_error,omitempty"` // 是否在遇到错误时停止导入
ConflictMode ConflictMode `json:"conflict_mode,omitempty"` // 冲突处理模式 1:inster 2:UPSERT
}
//DatabaseMigrateImportRes 数据库导入 返回结果
// DatabaseMigrateImportRes 数据库导入 返回结果
type DatabaseMigrateImportRes struct {
util.CommonError
JobID int64 `json:"job_id"` //导入任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
JobID int64 `json:"job_id"` // 导入任务ID可使用数据库迁移进度查询 API 查询导入进度及结果
}
//DatabaseMigrateQueryInfoRes 数据库迁移状态查询
// DatabaseMigrateQueryInfoRes 数据库迁移状态查询
type DatabaseMigrateQueryInfoRes struct {
util.CommonError
Status string `json:"status"` //导出状态
RecordSuccess int64 `json:"record_success"` //导出成功记录数
RecordFail int64 `json:"record_fail"` //导出失败记录数
ErrMsg string `json:"err_msg"` //导出错误信息
FileURL string `json:"file_url"` //导出文件下载地址
Status string `json:"status"` // 导出状态
RecordSuccess int64 `json:"record_success"` // 导出成功记录数
RecordFail int64 `json:"record_fail"` // 导出失败记录数
ErrMsg string `json:"err_msg"` // 导出错误信息
FileURL string `json:"file_url"` // 导出文件下载地址
}
//UpdateIndexReq 变更数据库索引 请求参数
// UpdateIndexReq 变更数据库索引 请求参数
type UpdateIndexReq struct {
Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` //集合名称
CreateIndexes []CreateIndex `json:"create_indexes,omitempty"` //新增索引
DropIndexes []DropIndex `json:"drop_indexes,omitempty"` //删除索引
Env string `json:"env,omitempty"` // 云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称
CreateIndexes []CreateIndex `json:"create_indexes,omitempty"` // 新增索引
DropIndexes []DropIndex `json:"drop_indexes,omitempty"` // 删除索引
}
//CreateIndex 新增索引
// CreateIndex 新增索引
type CreateIndex struct {
Name string `json:"name,omitempty"` //索引名
Unique bool `json:"unique,omitempty"` //是否唯一
Keys []CreateIndexKey `json:"keys,omitempty"` //索引字段
Name string `json:"name,omitempty"` // 索引名
Unique bool `json:"unique,omitempty"` // 是否唯一
Keys []CreateIndexKey `json:"keys,omitempty"` // 索引字段
}
//CreateIndexKey create index key
// CreateIndexKey create index key
type CreateIndexKey struct {
Name string `json:"name,omitempty"` //字段名
Direction string `json:"direction,omitempty"` //字段排序
Name string `json:"name,omitempty"` // 字段名
Direction string `json:"direction,omitempty"` // 字段排序
}
//DropIndex 删除索引
// DropIndex 删除索引
type DropIndex struct {
Name string `json:"name,omitempty"`
}
//DatabaseCollectionReq 新增/删除集合请求参数
// DatabaseCollectionReq 新增/删除集合请求参数
type DatabaseCollectionReq struct {
Env string `json:"env,omitempty"` //云环境ID
CollectionName string `json:"collection_name,omitempty"` //集合名称
Env string `json:"env,omitempty"` // 云环境ID
CollectionName string `json:"collection_name,omitempty"` // 集合名称
}
//DatabaseCollectionGetReq 获取特定云环境下集合信息请求
// DatabaseCollectionGetReq 获取特定云环境下集合信息请求
type DatabaseCollectionGetReq struct {
Env string `json:"env,omitempty"` //云环境ID
Limit int64 `json:"limit,omitempty"` //获取数量限制
Offset int64 `json:"offset,omitempty"` //偏移量
Env string `json:"env,omitempty"` // 云环境ID
Limit int64 `json:"limit,omitempty"` // 获取数量限制
Offset int64 `json:"offset,omitempty"` // 偏移量
}
//DatabaseCollectionGetRes 获取特定云环境下集合信息结果
// DatabaseCollectionGetRes 获取特定云环境下集合信息结果
type DatabaseCollectionGetRes struct {
util.CommonError
Pager struct {
Limit int64 `json:"limit"` //单次查询限制
Offset int64 `json:"offset"` //偏移量
Total int64 `json:"total"` //符合查询条件的记录总数
Limit int64 `json:"limit"` // 单次查询限制
Offset int64 `json:"offset"` // 偏移量
Total int64 `json:"total"` // 符合查询条件的记录总数
} `json:"pager"`
Collections []struct {
Name string `json:"name"` //集合名
Count int64 `json:"count"` //表中文档数量
Size int64 `json:"size"` //表的大小(即表中文档总大小),单位:字节
IndexCount int64 `json:"index_count"` //索引数量
IndexSize int64 `json:"index_size"` //索引占用大小,单位:字节
Name string `json:"name"` // 集合名
Count int64 `json:"count"` // 表中文档数量
Size int64 `json:"size"` // 表的大小(即表中文档总大小),单位:字节
IndexCount int64 `json:"index_count"` // 索引数量
IndexSize int64 `json:"index_size"` // 索引占用大小,单位:字节
} `json:"collections"`
}
//DatabaseReq 数据库插入/删除/更新/查询/统计记录请求参数
// DatabaseReq 数据库插入/删除/更新/查询/统计记录请求参数
type DatabaseReq struct {
Env string `json:"env,omitempty"` //云环境ID
Query string `json:"query,omitempty"` //数据库操作语句
Env string `json:"env,omitempty"` // 云环境ID
Query string `json:"query,omitempty"` // 数据库操作语句
}
//DatabaseAddRes 数据库插入记录返回结果
// DatabaseAddRes 数据库插入记录返回结果
type DatabaseAddRes struct {
util.CommonError
IDList []string `json:"id_list"` //插入成功的数据集合主键_id。
IDList []string `json:"id_list"` // 插入成功的数据集合主键_id。
}
//DatabaseDeleteRes 数据库删除记录返回结果
// DatabaseDeleteRes 数据库删除记录返回结果
type DatabaseDeleteRes struct {
util.CommonError
Deleted int64 `json:"deleted"` //删除记录数量
Deleted int64 `json:"deleted"` // 删除记录数量
}
//DatabaseUpdateRes 数据库更新记录返回结果
// DatabaseUpdateRes 数据库更新记录返回结果
type DatabaseUpdateRes struct {
util.CommonError
Matched int64 `json:"matched"` //更新条件匹配到的结果数
Modified int64 `json:"modified"` //修改的记录数注意使用set操作新插入的数据不计入修改数目
Matched int64 `json:"matched"` // 更新条件匹配到的结果数
Modified int64 `json:"modified"` // 修改的记录数注意使用set操作新插入的数据不计入修改数目
ID string `json:"id"`
}
//DatabaseQueryRes 数据库查询记录 返回结果
// DatabaseQueryRes 数据库查询记录 返回结果
type DatabaseQueryRes struct {
util.CommonError
Pager struct {
Limit int64 `json:"limit"` //单次查询限制
Offset int64 `json:"offset"` //偏移量
Total int64 `json:"total"` //符合查询条件的记录总数
Limit int64 `json:"limit"` // 单次查询限制
Offset int64 `json:"offset"` // 偏移量
Total int64 `json:"total"` // 符合查询条件的记录总数
} `json:"pager"`
Data []string `json:"data"`
}
//DatabaseCountRes 统计集合记录数或统计查询语句对应的结果记录数 返回结果
// DatabaseCountRes 统计集合记录数或统计查询语句对应的结果记录数 返回结果
type DatabaseCountRes struct {
util.CommonError
Count int64 `json:"count"` //记录数量
Count int64 `json:"count"` // 记录数量
}
//DatabaseMigrateImport 数据库导入
// DatabaseMigrateImport 数据库导入
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateImport.html
func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseMigrateImportRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -208,7 +208,7 @@ func (tcb *Tcb) DatabaseMigrateImport(req *DatabaseMigrateImportReq) (*DatabaseM
return databaseMigrateImportRes, err
}
//DatabaseMigrateExport 数据库导出
// DatabaseMigrateExport 数据库导出
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateExport.html
func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseMigrateExportRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -225,7 +225,7 @@ func (tcb *Tcb) DatabaseMigrateExport(req *DatabaseMigrateExportReq) (*DatabaseM
return databaseMigrateExportRes, err
}
//DatabaseMigrateQueryInfo 数据库迁移状态查询
// DatabaseMigrateQueryInfo 数据库迁移状态查询
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseMigrateQueryInfo.html
func (tcb *Tcb) DatabaseMigrateQueryInfo(env string, jobID int64) (*DatabaseMigrateQueryInfoRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -245,8 +245,8 @@ func (tcb *Tcb) DatabaseMigrateQueryInfo(env string, jobID int64) (*DatabaseMigr
return databaseMigrateQueryInfoRes, err
}
//UpdateIndex 变更数据库索引
//https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html
// UpdateIndex 变更数据库索引
// https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/updateIndex.html
func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error {
accessToken, err := tcb.GetAccessToken()
if err != nil {
@@ -260,7 +260,7 @@ func (tcb *Tcb) UpdateIndex(req *UpdateIndexReq) error {
return util.DecodeWithCommonError(response, "UpdateIndex")
}
//DatabaseCollectionAdd 新增集合
// DatabaseCollectionAdd 新增集合
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionAdd.html
func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error {
accessToken, err := tcb.GetAccessToken()
@@ -278,7 +278,7 @@ func (tcb *Tcb) DatabaseCollectionAdd(env, collectionName string) error {
return util.DecodeWithCommonError(response, "DatabaseCollectionAdd")
}
//DatabaseCollectionDelete 删除集合
// DatabaseCollectionDelete 删除集合
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionDelete.html
func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error {
accessToken, err := tcb.GetAccessToken()
@@ -296,7 +296,7 @@ func (tcb *Tcb) DatabaseCollectionDelete(env, collectionName string) error {
return util.DecodeWithCommonError(response, "DatabaseCollectionDelete")
}
//DatabaseCollectionGet 获取特定云环境下集合信息
// DatabaseCollectionGet 获取特定云环境下集合信息
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCollectionGet.html
func (tcb *Tcb) DatabaseCollectionGet(env string, limit, offset int64) (*DatabaseCollectionGetRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -317,7 +317,7 @@ func (tcb *Tcb) DatabaseCollectionGet(env string, limit, offset int64) (*Databas
return databaseCollectionGetRes, err
}
//DatabaseAdd 数据库插入记录
// DatabaseAdd 数据库插入记录
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseAdd.html
func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -337,7 +337,7 @@ func (tcb *Tcb) DatabaseAdd(env, query string) (*DatabaseAddRes, error) {
return databaseAddRes, err
}
//DatabaseDelete 数据库插入记录
// DatabaseDelete 数据库插入记录
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseDelete.html
func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -357,7 +357,7 @@ func (tcb *Tcb) DatabaseDelete(env, query string) (*DatabaseDeleteRes, error) {
return databaseDeleteRes, err
}
//DatabaseUpdate 数据库插入记录
// DatabaseUpdate 数据库插入记录
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseUpdate.html
func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -377,7 +377,7 @@ func (tcb *Tcb) DatabaseUpdate(env, query string) (*DatabaseUpdateRes, error) {
return databaseUpdateRes, err
}
//DatabaseQuery 数据库查询记录
// DatabaseQuery 数据库查询记录
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html
func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -397,7 +397,7 @@ func (tcb *Tcb) DatabaseQuery(env, query string) (*DatabaseQueryRes, error) {
return databaseQueryRes, err
}
//DatabaseCount 统计集合记录数或统计查询语句对应的结果记录数
// DatabaseCount 统计集合记录数或统计查询语句对应的结果记录数
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseCount.html
func (tcb *Tcb) DatabaseCount(env, query string) (*DatabaseCountRes, error) {
accessToken, err := tcb.GetAccessToken()

View File

@@ -7,60 +7,60 @@ import (
)
const (
//获取文件上传链接
// 获取文件上传链接
uploadFilePathURL = "https://api.weixin.qq.com/tcb/uploadfile"
//获取文件下载链接
// 获取文件下载链接
batchDownloadFileURL = "https://api.weixin.qq.com/tcb/batchdownloadfile"
//删除文件链接
// 删除文件链接
batchDeleteFileURL = "https://api.weixin.qq.com/tcb/batchdeletefile"
)
//UploadFileReq 上传文件请求值
// UploadFileReq 上传文件请求值
type UploadFileReq struct {
Env string `json:"env,omitempty"`
Path string `json:"path,omitempty"`
}
//UploadFileRes 上传文件返回结果
// UploadFileRes 上传文件返回结果
type UploadFileRes struct {
util.CommonError
URL string `json:"url"` //上传url
Token string `json:"token"` //token
Authorization string `json:"authorization"` //authorization
FileID string `json:"file_id"` //文件ID
CosFileID string `json:"cos_file_id"` //cos文件ID
URL string `json:"url"` // 上传url
Token string `json:"token"` // token
Authorization string `json:"authorization"` // authorization
FileID string `json:"file_id"` // 文件ID
CosFileID string `json:"cos_file_id"` // cos文件ID
}
//BatchDownloadFileReq 上传文件请求值
// BatchDownloadFileReq 上传文件请求值
type BatchDownloadFileReq struct {
Env string `json:"env,omitempty"`
FileList []*DownloadFile `json:"file_list,omitempty"`
}
//DownloadFile 文件信息
// DownloadFile 文件信息
type DownloadFile struct {
FileID string `json:"fileid"` //文件ID
MaxAge int64 `json:"max_age"` //下载链接有效期
FileID string `json:"fileid"` // 文件ID
MaxAge int64 `json:"max_age"` // 下载链接有效期
}
//BatchDownloadFileRes 上传文件返回结果
// BatchDownloadFileRes 上传文件返回结果
type BatchDownloadFileRes struct {
util.CommonError
FileList []struct {
FileID string `json:"file_id"` //文件ID
DownloadURL string `json:"download_url"` //下载链接
Status int64 `json:"status"` //状态码
ErrMsg string `json:"errmsg"` //该文件错误信息
FileID string `json:"file_id"` // 文件ID
DownloadURL string `json:"download_url"` // 下载链接
Status int64 `json:"status"` // 状态码
ErrMsg string `json:"errmsg"` // 该文件错误信息
} `json:"file_list"`
}
//BatchDeleteFileReq 批量删除文件请求参数
// BatchDeleteFileReq 批量删除文件请求参数
type BatchDeleteFileReq struct {
Env string `json:"env,omitempty"`
FileIDList []string `json:"fileid_list,omitempty"`
}
//BatchDeleteFileRes 批量删除文件返回结果
// BatchDeleteFileRes 批量删除文件返回结果
type BatchDeleteFileRes struct {
util.CommonError
DeleteList []struct {
@@ -70,7 +70,7 @@ type BatchDeleteFileRes struct {
} `json:"delete_list"`
}
//UploadFile 上传文件
// UploadFile 上传文件
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html
func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -91,7 +91,7 @@ func (tcb *Tcb) UploadFile(env, path string) (*UploadFileRes, error) {
return uploadFileRes, err
}
//BatchDownloadFile 获取文件下载链接
// BatchDownloadFile 获取文件下载链接
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDownloadFile.html
func (tcb *Tcb) BatchDownloadFile(env string, fileList []*DownloadFile) (*BatchDownloadFileRes, error) {
accessToken, err := tcb.GetAccessToken()
@@ -112,7 +112,7 @@ func (tcb *Tcb) BatchDownloadFile(env string, fileList []*DownloadFile) (*BatchD
return batchDownloadFileRes, err
}
//BatchDeleteFile 批量删除文件
// BatchDeleteFile 批量删除文件
//reference:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDeleteFile.html
func (tcb *Tcb) BatchDeleteFile(env string, fileIDList []string) (*BatchDeleteFileRes, error) {
accessToken, err := tcb.GetAccessToken()

View File

@@ -2,12 +2,12 @@ package tcb
import "github.com/silenceper/wechat/v2/miniprogram/context"
//Tcb Tencent Cloud Base
// Tcb Tencent Cloud Base
type Tcb struct {
*context.Context
}
//NewTcb new Tencent Cloud Base
// NewTcb new Tencent Cloud Base
func NewTcb(context *context.Context) *Tcb {
return &Tcb{
context,

View File

@@ -0,0 +1,70 @@
package urllink
import (
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
// URLLink 小程序 URL Link
type URLLink struct {
*context.Context
}
// NewURLLink 实例化
func NewURLLink(ctx *context.Context) *URLLink {
return &URLLink{Context: ctx}
}
const generateURL = "https://api.weixin.qq.com/wxa/generate_urllink"
// TExpireType 失效类型 (指定时间戳/指定间隔)
type TExpireType int
const (
// ExpireTypeTime 指定时间戳后失效
ExpireTypeTime TExpireType = 0
// ExpireTypeInterval 间隔指定天数后失效
ExpireTypeInterval TExpireType = 1
)
// ULParams 请求参数
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#请求参数
type ULParams struct {
Path string `json:"path"`
Query string `json:"query"`
IsExpire bool `json:"is_expire"`
ExpireType TExpireType `json:"expire_type"`
ExpireTime int64 `json:"expire_time"`
ExpireInterval int `json:"expire_interval"`
}
// ULResult 返回的结果
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html#返回值
type ULResult struct {
util.CommonError
URLLink string `json:"url_link"`
}
// Generate 生成url link
func (u *URLLink) Generate(params *ULParams) (string, error) {
accessToken, err := u.GetAccessToken()
if err != nil {
return "", err
}
uri := fmt.Sprintf("%s?access_token=%s", generateURL, accessToken)
response, err := util.PostJSON(uri, params)
if err != nil {
return "", err
}
var resp ULResult
err = util.DecodeWithError(response, &resp, "URLLink.Generate")
if err != nil {
return "", err
}
return resp.URLLink, nil
}

View File

@@ -0,0 +1,40 @@
package werun
import (
"encoding/json"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/miniprogram/encryptor"
)
// WeRun 微信运动
type WeRun struct {
*context.Context
}
// Data 微信运动数据
type Data struct {
StepInfoList []struct {
Timestamp int `json:"timestamp"`
Step int `json:"step"`
} `json:"stepInfoList"`
}
// NewWeRun 实例化
func NewWeRun(ctx *context.Context) *WeRun {
return &WeRun{Context: ctx}
}
// GetWeRunData 解密数据
func (werun *WeRun) GetWeRunData(sessionKey, encryptedData, iv string) (*Data, error) {
cipherText, err := encryptor.GetCipherText(sessionKey, encryptedData, iv)
if err != nil {
return nil, err
}
var weRunData Data
err = json.Unmarshal(cipherText, &weRunData)
if err != nil {
return nil, err
}
return &weRunData, nil
}

View File

@@ -8,34 +8,34 @@ import (
)
var (
//获取微信服务器IP地址
//文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
// 获取微信服务器IP地址
// 文档https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
getCallbackIPURL = "https://api.weixin.qq.com/cgi-bin/getcallbackip"
getAPIDomainIPURL = "https://api.weixin.qq.com/cgi-bin/get_api_domain_ip"
//清理接口调用次数
// 清理接口调用次数
clearQuotaURL = "https://api.weixin.qq.com/cgi-bin/clear_quota"
)
//Basic struct
// Basic struct
type Basic struct {
*context.Context
}
//NewBasic 实例
// NewBasic 实例
func NewBasic(context *context.Context) *Basic {
basic := new(Basic)
basic.Context = context
return basic
}
//IPListRes 获取微信服务器IP地址 返回结果
// IPListRes 获取微信服务器IP地址 返回结果
type IPListRes struct {
util.CommonError
IPList []string `json:"ip_list"`
}
//GetCallbackIP 获取微信callback IP地址
// GetCallbackIP 获取微信callback IP地址
func (basic *Basic) GetCallbackIP() ([]string, error) {
ak, err := basic.GetAccessToken()
if err != nil {
@@ -51,7 +51,7 @@ func (basic *Basic) GetCallbackIP() ([]string, error) {
return ipListRes.IPList, err
}
//GetAPIDomainIP 获取微信API接口 IP地址
// GetAPIDomainIP 获取微信API接口 IP地址
func (basic *Basic) GetAPIDomainIP() ([]string, error) {
ak, err := basic.GetAccessToken()
if err != nil {
@@ -67,7 +67,7 @@ func (basic *Basic) GetAPIDomainIP() ([]string, error) {
return ipListRes.IPList, err
}
//ClearQuota 清理接口调用次数
// ClearQuota 清理接口调用次数
func (basic *Basic) ClearQuota() error {
ak, err := basic.GetAccessToken()
if err != nil {

View File

@@ -62,6 +62,11 @@ func (basic *Basic) GetQRTicket(tq *Request) (t *Ticket, err error) {
return
}
if t.ErrMsg != "" {
err = fmt.Errorf("get qr_ticket error : errcode=%v , errormsg=%v", t.ErrCode, t.ErrMsg)
return
}
return
}

View File

@@ -0,0 +1,52 @@
package basic
import (
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const (
// 将一条长链接转成短链接
// https://developers.weixin.qq.com/doc/offiaccount/Account_Management/URL_Shortener.html
long2shortURL = "https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s"
long2shortAction = "long2short"
)
type (
reqLong2ShortURL struct {
Action string `json:"action"`
LongURL string `json:"long_url"`
}
resplong2ShortURL struct {
ShortURL string `json:"short_url"`
util.CommonError
}
)
// Long2ShortURL 将一条长链接转成短链接
func (basic *Basic) Long2ShortURL(longURL string) (shortURL string, err error) {
var (
req = &reqLong2ShortURL{
Action: long2shortAction,
LongURL: longURL,
}
resp = new(resplong2ShortURL)
ac, uri string
responseBytes []byte
)
ac, err = basic.GetAccessToken()
if err != nil {
return
}
uri = fmt.Sprintf(long2shortURL, ac)
responseBytes, err = util.PostJSON(uri, req)
if err != nil {
return
}
if err = util.DecodeWithError(responseBytes, resp, long2shortAction); err != nil {
return
}
shortURL = resp.ShortURL
return
}

View File

@@ -17,42 +17,42 @@ const (
setSpeedSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set"
)
//MsgType 发送消息类型
// MsgType 发送消息类型
type MsgType string
const (
//MsgTypeNews 图文消息
// MsgTypeNews 图文消息
MsgTypeNews MsgType = "mpnews"
//MsgTypeText 文本
// MsgTypeText 文本
MsgTypeText MsgType = "text"
//MsgTypeVoice 语音/音频
// MsgTypeVoice 语音/音频
MsgTypeVoice MsgType = "voice"
//MsgTypeImage 图片
// MsgTypeImage 图片
MsgTypeImage MsgType = "image"
//MsgTypeVideo 视频
// MsgTypeVideo 视频
MsgTypeVideo MsgType = "mpvideo"
//MsgTypeWxCard 卡券
// MsgTypeWxCard 卡券
MsgTypeWxCard MsgType = "wxcard"
)
//Broadcast 群发消息
// Broadcast 群发消息
type Broadcast struct {
*context.Context
preview bool
}
//NewBroadcast new
// NewBroadcast new
func NewBroadcast(ctx *context.Context) *Broadcast {
return &Broadcast{ctx, false}
}
//User 发送的用户
// User 发送的用户
type User struct {
TagID int64
OpenID []string
}
//Result 群发返回结果
// Result 群发返回结果
type Result struct {
util.CommonError
MsgID int64 `json:"msg_id"`
@@ -60,34 +60,34 @@ type Result struct {
MsgStatus string `json:"msg_status"`
}
//SpeedResult 群发速度返回结果
// SpeedResult 群发速度返回结果
type SpeedResult struct {
util.CommonError
Speed int64 `json:"speed"`
RealSpeed int64 `json:"realspeed"`
}
//sendRequest 发送请求的数据
// sendRequest 发送请求的数据
type sendRequest struct {
//根据tag获全部发送
// 根据tag获全部发送
Filter map[string]interface{} `json:"filter,omitempty"`
//根据OpenID发送
// 根据OpenID发送
ToUser interface{} `json:"touser,omitempty"`
//发送文本
// 发送文本
Text map[string]interface{} `json:"text,omitempty"`
//发送图文消息
// 发送图文消息
Mpnews map[string]interface{} `json:"mpnews,omitempty"`
//发送语音
// 发送语音
Voice map[string]interface{} `json:"voice,omitempty"`
//发送图片
// 发送图片
Images *Image `json:"images,omitempty"`
//发送卡券
// 发送卡券
WxCard map[string]interface{} `json:"wxcard,omitempty"`
MsgType MsgType `json:"msgtype"`
SendIgnoreReprint int32 `json:"send_ignore_reprint,omitempty"`
}
//Image 发送图片
// Image 发送图片
type Image struct {
MediaIDs []string `json:"media_ids"`
Recommend string `json:"recommend"`
@@ -95,10 +95,10 @@ type Image struct {
OnlyFansCanComment int32 `json:"only_fans_can_comment"`
}
//SendText 群发文本
//user 为nil表示全员发送
//&User{TagID:2} 根据tag发送
//&User{OpenID:[]string("xxx","xxx")} 根据openid发送
// SendText 群发文本
// user 为nil表示全员发送
// &User{TagID:2} 根据tag发送
// &User{OpenID:[]string("xxx","xxx")} 根据openid发送
func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -122,7 +122,7 @@ func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error
return res, err
}
//SendNews 发送图文
// SendNews 发送图文
func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -149,7 +149,7 @@ func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint b
return res, err
}
//SendVoice 发送语音
// SendVoice 发送语音
func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -173,7 +173,7 @@ func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, erro
return res, err
}
//SendImage 发送图片
// SendImage 发送图片
func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -195,7 +195,7 @@ func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error
return res, err
}
//SendVideo 发送视频
// SendVideo 发送视频
func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -221,7 +221,7 @@ func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, descrip
return res, err
}
//SendWxCard 发送卡券
// SendWxCard 发送卡券
func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -245,7 +245,7 @@ func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, erro
return res, err
}
//Delete 删除群发消息
// Delete 删除群发消息
func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
ak, err := broadcast.GetAccessToken()
if err != nil {
@@ -333,9 +333,11 @@ func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret
sendURL = sendURLByTag
} else {
if broadcast.preview {
// 预览
req.ToUser = user.OpenID
sendURL = previewSendURL
// 预览 默认发给第一个用户
if len(user.OpenID) != 0 {
req.ToUser = user.OpenID[0]
sendURL = previewSendURL
}
} else {
if user.TagID != 0 {
req.Filter = map[string]interface{}{

View File

@@ -4,11 +4,11 @@ import (
"github.com/silenceper/wechat/v2/cache"
)
//Config config for 微信公众号
// Config .config for 微信公众号
type Config struct {
AppID string `json:"app_id"` //appid
AppSecret string `json:"app_secret"` //appsecret
Token string `json:"token"` //token
EncodingAESKey string `json:"encoding_aes_key"` //EncodingAESKey
AppID string `json:"app_id"` // appid
AppSecret string `json:"app_secret"` // appsecret
Token string `json:"token"` // token
EncodingAESKey string `json:"encoding_aes_key"` // EncodingAESKey
Cache cache.Cache
}

View File

@@ -15,7 +15,7 @@ const (
getUserShareHour = "https://api.weixin.qq.com/datacube/getusersharehour"
)
//ResArticleSummary 获取图文群发每日数据响应
// ResArticleSummary 获取图文群发每日数据响应
type ResArticleSummary struct {
util.CommonError
@@ -34,7 +34,7 @@ type ResArticleSummary struct {
} `json:"list"`
}
//ResArticleTotal 获取图文群发总数据响应
// ResArticleTotal 获取图文群发总数据响应
type ResArticleTotal struct {
util.CommonError
@@ -46,7 +46,7 @@ type ResArticleTotal struct {
} `json:"list"`
}
//ArticleTotalDetails 获取图文群发总数据响应文字详情
// ArticleTotalDetails 获取图文群发总数据响应文字详情
type ArticleTotalDetails struct {
StatDate string `json:"stat_date"`
TargetUser int `json:"target_user"`
@@ -76,7 +76,7 @@ type ArticleTotalDetails struct {
FeedShareFromOtherCnt int `json:"feed_share_from_other_cnt"`
}
//ResUserRead 获取图文统计数据响应
// ResUserRead 获取图文统计数据响应
type ResUserRead struct {
util.CommonError
@@ -94,7 +94,7 @@ type ResUserRead struct {
} `json:"list"`
}
//ResUserReadHour 获取图文统计分时数据
// ResUserReadHour 获取图文统计分时数据
type ResUserReadHour struct {
util.CommonError
@@ -113,7 +113,7 @@ type ResUserReadHour struct {
} `json:"list"`
}
//ResUserShare 获取图文分享转发数据
// ResUserShare 获取图文分享转发数据
type ResUserShare struct {
util.CommonError
@@ -125,7 +125,7 @@ type ResUserShare struct {
} `json:"list"`
}
//ResUserShareHour 获取图文分享转发分时数据
// ResUserShareHour 获取图文分享转发分时数据
type ResUserShareHour struct {
util.CommonError
@@ -138,7 +138,7 @@ type ResUserShareHour struct {
} `json:"list"`
}
//GetArticleSummary 获取图文群发每日数据
// GetArticleSummary 获取图文群发每日数据
func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary ResArticleSummary, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -160,7 +160,7 @@ func (cube *DataCube) GetArticleSummary(s string, e string) (resArticleSummary R
return
}
//GetArticleTotal 获取图文群发总数据
// GetArticleTotal 获取图文群发总数据
func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResArticleTotal, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -182,7 +182,7 @@ func (cube *DataCube) GetArticleTotal(s string, e string) (resArticleTotal ResAr
return
}
//GetUserRead 获取图文统计数据
// GetUserRead 获取图文统计数据
func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -204,7 +204,7 @@ func (cube *DataCube) GetUserRead(s string, e string) (resUserRead ResUserRead,
return
}
//GetUserReadHour 获取图文统计分时数据
// GetUserReadHour 获取图文统计分时数据
func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUserReadHour, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -226,7 +226,7 @@ func (cube *DataCube) GetUserReadHour(s string, e string) (resUserReadHour ResUs
return
}
//GetUserShare 获取图文分享转发数据
// GetUserShare 获取图文分享转发数据
func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShare, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -248,7 +248,7 @@ func (cube *DataCube) GetUserShare(s string, e string) (resUserShare ResUserShar
return
}
//GetUserShareHour 获取图文分享转发分时数据
// GetUserShareHour 获取图文分享转发分时数据
func (cube *DataCube) GetUserShareHour(s string, e string) (resUserShareHour ResUserShareHour, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {

View File

@@ -9,12 +9,12 @@ type reqDate struct {
EndDate string `json:"end_date"`
}
//DataCube 数据统计
// DataCube 数据统计
type DataCube struct {
*context.Context
}
//NewCube 数据统计
// NewCube 数据统计
func NewCube(context *context.Context) *DataCube {
dataCube := new(DataCube)
dataCube.Context = context

View File

@@ -11,7 +11,7 @@ const (
getInterfaceSummaryHour = "https://api.weixin.qq.com/datacube/getinterfacesummaryhour"
)
//ResInterfaceSummary 接口分析数据响应
// ResInterfaceSummary 接口分析数据响应
type ResInterfaceSummary struct {
util.CommonError
@@ -24,7 +24,7 @@ type ResInterfaceSummary struct {
} `json:"list"`
}
//ResInterfaceSummaryHour 接口分析分时数据响应
// ResInterfaceSummaryHour 接口分析分时数据响应
type ResInterfaceSummaryHour struct {
util.CommonError
@@ -38,7 +38,7 @@ type ResInterfaceSummaryHour struct {
} `json:"list"`
}
//GetInterfaceSummary 获取接口分析数据
// GetInterfaceSummary 获取接口分析数据
func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSummary ResInterfaceSummary, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -60,7 +60,7 @@ func (cube *DataCube) GetInterfaceSummary(s string, e string) (resInterfaceSumma
return
}
//GetInterfaceSummaryHour 获取接口分析分时数据
// GetInterfaceSummaryHour 获取接口分析分时数据
func (cube *DataCube) GetInterfaceSummaryHour(s string, e string) (resInterfaceSummaryHour ResInterfaceSummaryHour, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {

View File

@@ -16,7 +16,7 @@ const (
getUpstreamMsgDistMonth = "https://api.weixin.qq.com/datacube/getupstreammsgdistmonth"
)
//ResUpstreamMsg 获取消息发送概况数据响应
// ResUpstreamMsg 获取消息发送概况数据响应
type ResUpstreamMsg struct {
util.CommonError
@@ -28,7 +28,7 @@ type ResUpstreamMsg struct {
} `json:"list"`
}
//ResUpstreamMsgHour 获取消息分送分时数据响应
// ResUpstreamMsgHour 获取消息分送分时数据响应
type ResUpstreamMsgHour struct {
util.CommonError
@@ -41,7 +41,7 @@ type ResUpstreamMsgHour struct {
} `json:"list"`
}
//ResUpstreamMsgWeek 获取消息发送周数据响应
// ResUpstreamMsgWeek 获取消息发送周数据响应
type ResUpstreamMsgWeek struct {
util.CommonError
@@ -53,7 +53,7 @@ type ResUpstreamMsgWeek struct {
} `json:"list"`
}
//ResUpstreamMsgMonth 获取消息发送月数据响应
// ResUpstreamMsgMonth 获取消息发送月数据响应
type ResUpstreamMsgMonth struct {
util.CommonError
@@ -65,7 +65,7 @@ type ResUpstreamMsgMonth struct {
} `json:"list"`
}
//ResUpstreamMsgDist 获取消息发送分布数据响应
// ResUpstreamMsgDist 获取消息发送分布数据响应
type ResUpstreamMsgDist struct {
util.CommonError
@@ -76,7 +76,7 @@ type ResUpstreamMsgDist struct {
} `json:"list"`
}
//ResUpstreamMsgDistWeek 获取消息发送分布周数据响应
// ResUpstreamMsgDistWeek 获取消息发送分布周数据响应
type ResUpstreamMsgDistWeek struct {
util.CommonError
@@ -87,7 +87,7 @@ type ResUpstreamMsgDistWeek struct {
} `json:"list"`
}
//ResUpstreamMsgDistMonth 获取消息发送分布月数据响应
// ResUpstreamMsgDistMonth 获取消息发送分布月数据响应
type ResUpstreamMsgDistMonth struct {
util.CommonError
@@ -98,7 +98,7 @@ type ResUpstreamMsgDistMonth struct {
} `json:"list"`
}
//GetUpstreamMsg 获取消息发送概况数据
// GetUpstreamMsg 获取消息发送概况数据
func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpstreamMsg, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -120,7 +120,7 @@ func (cube *DataCube) GetUpstreamMsg(s string, e string) (resUpstreamMsg ResUpst
return
}
//GetUpstreamMsgHour 获取消息分送分时数据
// GetUpstreamMsgHour 获取消息分送分时数据
func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour ResUpstreamMsgHour, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -142,7 +142,7 @@ func (cube *DataCube) GetUpstreamMsgHour(s string, e string) (resUpstreamMsgHour
return
}
//GetUpstreamMsgWeek 获取消息发送周数据
// GetUpstreamMsgWeek 获取消息发送周数据
func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek ResUpstreamMsgWeek, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -164,7 +164,7 @@ func (cube *DataCube) GetUpstreamMsgWeek(s string, e string) (resUpstreamMsgWeek
return
}
//GetUpstreamMsgMonth 获取消息发送月数据
// GetUpstreamMsgMonth 获取消息发送月数据
func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMonth ResUpstreamMsgMonth, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -186,7 +186,7 @@ func (cube *DataCube) GetUpstreamMsgMonth(s string, e string) (resUpstreamMsgMon
return
}
//GetUpstreamMsgDist 获取消息发送分布数据
// GetUpstreamMsgDist 获取消息发送分布数据
func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist ResUpstreamMsgDist, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -208,7 +208,7 @@ func (cube *DataCube) GetUpstreamMsgDist(s string, e string) (resUpstreamMsgDist
return
}
//GetUpstreamMsgDistWeek 获取消息发送分布周数据
// GetUpstreamMsgDistWeek 获取消息发送分布周数据
func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsgDistWeek ResUpstreamMsgDistWeek, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -230,7 +230,7 @@ func (cube *DataCube) GetUpstreamMsgDistWeek(s string, e string) (resUpstreamMsg
return
}
//GetUpstreamMsgDistMonth 获取消息发送分布月数据
// GetUpstreamMsgDistMonth 获取消息发送分布月数据
func (cube *DataCube) GetUpstreamMsgDistMonth(s string, e string) (resUpstreamMsgDistMonth ResUpstreamMsgDistMonth, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {

View File

@@ -8,31 +8,31 @@ import (
"github.com/silenceper/wechat/v2/util"
)
//AdSlot 广告位类型
// AdSlot 广告位类型
type AdSlot string
const (
//SlotIDBizBottom 公众号底部广告
// SlotIDBizBottom 公众号底部广告
SlotIDBizBottom AdSlot = "SLOT_ID_BIZ_BOTTOM"
//SlotIDBizMidContext 公众号文中广告
// SlotIDBizMidContext 公众号文中广告
SlotIDBizMidContext AdSlot = "SLOT_ID_BIZ_MID_CONTEXT"
//SlotIDBizVideoEnd 公众号视频后贴
// SlotIDBizVideoEnd 公众号视频后贴
SlotIDBizVideoEnd AdSlot = "SLOT_ID_BIZ_VIDEO_END"
//SlotIDBizSponsor 公众号互选广告
// SlotIDBizSponsor 公众号互选广告
SlotIDBizSponsor AdSlot = "SLOT_ID_BIZ_SPONSOR"
//SlotIDBizCps 公众号返佣商品
// SlotIDBizCps 公众号返佣商品
SlotIDBizCps AdSlot = "SLOT_ID_BIZ_CPS"
//SlotIDWeappBanner 小程序banner
// SlotIDWeappBanner 小程序banner
SlotIDWeappBanner AdSlot = "SLOT_ID_WEAPP_BANNER"
//SlotIDWeappRewardVideo 小程序激励视频
// SlotIDWeappRewardVideo 小程序激励视频
SlotIDWeappRewardVideo AdSlot = "SLOT_ID_WEAPP_REWARD_VIDEO"
//SlotIDWeappInterstitial 小程序插屏广告
// SlotIDWeappInterstitial 小程序插屏广告
SlotIDWeappInterstitial AdSlot = "SLOT_ID_WEAPP_INTERSTITIAL"
//SlotIDWeappVideoFeeds 小程序视频广告
// SlotIDWeappVideoFeeds 小程序视频广告
SlotIDWeappVideoFeeds AdSlot = "SLOT_ID_WEAPP_VIDEO_FEEDS"
//SlotIDWeappVideoBegin 小程序视频前贴
// SlotIDWeappVideoBegin 小程序视频前贴
SlotIDWeappVideoBegin AdSlot = "SLOT_ID_WEAPP_VIDEO_BEGIN"
//SlotIDWeappBox 小程序格子广告
// SlotIDWeappBox 小程序格子广告
SlotIDWeappBox AdSlot = "SLOT_ID_WEAPP_BOX"
)
@@ -46,13 +46,13 @@ const (
actionPublisherSettlement = "publisher_settlement"
)
//BaseResp 错误信息
// BaseResp 错误信息
type BaseResp struct {
ErrMsg string `json:"err_msg"`
Ret int `json:"ret"`
}
//ResPublisherAdPos 公众号分广告位数据响应
// ResPublisherAdPos 公众号分广告位数据响应
type ResPublisherAdPos struct {
util.CommonError
@@ -62,7 +62,7 @@ type ResPublisherAdPos struct {
TotalNum int `json:"total_num"`
}
//ResAdPosList 公众号分广告位列表
// ResAdPosList 公众号分广告位列表
type ResAdPosList struct {
SlotID int64 `json:"slot_id"`
AdSlot string `json:"ad_slot"`
@@ -76,7 +76,7 @@ type ResAdPosList struct {
Ecpm float64 `json:"ecpm"`
}
//ResAdPosSummary 公众号分广告位概览
// ResAdPosSummary 公众号分广告位概览
type ResAdPosSummary struct {
ReqSuccCount int `json:"req_succ_count"`
ExposureCount int `json:"exposure_count"`
@@ -87,7 +87,7 @@ type ResAdPosSummary struct {
Ecpm float64 `json:"ecpm"`
}
//ResPublisherCps 公众号返佣商品数据响应
// ResPublisherCps 公众号返佣商品数据响应
type ResPublisherCps struct {
util.CommonError
@@ -97,7 +97,7 @@ type ResPublisherCps struct {
TotalNum int `json:"total_num"`
}
//ResCpsList 公众号返佣商品列表
// ResCpsList 公众号返佣商品列表
type ResCpsList struct {
Date string `json:"date"`
ExposureCount int `json:"exposure_count"`
@@ -109,7 +109,7 @@ type ResCpsList struct {
TotalCommission int `json:"total_commission"`
}
//ResCpsSummary 公众号返佣概览
// ResCpsSummary 公众号返佣概览
type ResCpsSummary struct {
ExposureCount int `json:"exposure_count"`
ClickCount int `json:"click_count"`
@@ -120,7 +120,7 @@ type ResCpsSummary struct {
TotalCommission int `json:"total_commission"`
}
//ResPublisherSettlement 公众号结算收入数据及结算主体信息响应
// ResPublisherSettlement 公众号结算收入数据及结算主体信息响应
type ResPublisherSettlement struct {
util.CommonError
@@ -133,7 +133,7 @@ type ResPublisherSettlement struct {
TotalNum int `json:"total_num"`
}
//SettlementList 结算单列表
// SettlementList 结算单列表
type SettlementList struct {
Date string `json:"date"`
Zone string `json:"zone"`
@@ -146,13 +146,13 @@ type SettlementList struct {
SlotRevenue []SlotRevenue `json:"slot_revenue"`
}
//SlotRevenue 产生收入的广告
// SlotRevenue 产生收入的广告
type SlotRevenue struct {
SlotID string `json:"slot_id"`
SlotSettledRevenue int `json:"slot_settled_revenue"`
}
//ParamsPublisher 拉取数据参数
// ParamsPublisher 拉取数据参数
type ParamsPublisher struct {
Action string `json:"action"`
StartDate string `json:"start_date"`
@@ -189,7 +189,7 @@ func (cube *DataCube) fetchData(params ParamsPublisher) (response []byte, err er
return
}
//GetPublisherAdPosGeneral 获取公众号分广告位数据
// GetPublisherAdPosGeneral 获取公众号分广告位数据
func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page, pageSize int, adSlot AdSlot) (resPublisherAdPos ResPublisherAdPos, err error) {
params := ParamsPublisher{
Action: actionPublisherAdPosGeneral,
@@ -217,7 +217,7 @@ func (cube *DataCube) GetPublisherAdPosGeneral(startDate, endDate string, page,
return
}
//GetPublisherCpsGeneral 获取公众号返佣商品数据
// GetPublisherCpsGeneral 获取公众号返佣商品数据
func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pageSize int) (resPublisherCps ResPublisherCps, err error) {
params := ParamsPublisher{
Action: actionPublisherCpsGeneral,
@@ -244,7 +244,7 @@ func (cube *DataCube) GetPublisherCpsGeneral(startDate, endDate string, page, pa
return
}
//GetPublisherSettlement 获取公众号结算收入数据及结算主体信息
// GetPublisherSettlement 获取公众号结算收入数据及结算主体信息
func (cube *DataCube) GetPublisherSettlement(startDate, endDate string, page, pageSize int) (resPublisherSettlement ResPublisherSettlement, err error) {
params := ParamsPublisher{
Action: actionPublisherSettlement,

View File

@@ -11,7 +11,7 @@ const (
getUserAccumulate = "https://api.weixin.qq.com/datacube/getusercumulate"
)
//ResUserSummary 获取用户增减数据响应
// ResUserSummary 获取用户增减数据响应
type ResUserSummary struct {
util.CommonError
@@ -23,7 +23,7 @@ type ResUserSummary struct {
} `json:"list"`
}
//ResUserAccumulate 获取累计用户数据响应
// ResUserAccumulate 获取累计用户数据响应
type ResUserAccumulate struct {
util.CommonError
@@ -33,7 +33,7 @@ type ResUserAccumulate struct {
} `json:"list"`
}
//GetUserSummary 获取用户增减数据
// GetUserSummary 获取用户增减数据
func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUserSummary, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {
@@ -55,7 +55,7 @@ func (cube *DataCube) GetUserSummary(s string, e string) (resUserSummary ResUser
return
}
//GetUserAccumulate 获取累计用户数据
// GetUserAccumulate 获取累计用户数据
func (cube *DataCube) GetUserAccumulate(s string, e string) (resUserAccumulate ResUserAccumulate, err error) {
accessToken, err := cube.GetAccessToken()
if err != nil {

View File

@@ -1,4 +1,4 @@
//Package device 设备相关接口
// Package device 设备相关接口
package device
import (
@@ -23,13 +23,13 @@ type reqDeviceAuthorize struct {
// 请求操作的类型限定取值为0设备授权缺省值为0 1设备更新更新已授权设备的各属性值
OpType string `json:"op_type,omitempty"`
// 设备的产品编号(由微信硬件平台分配)。可在公众号设备功能管理页面查询。
//当 op_type 为0product_id 为1不要填写 product_id 字段(会引起不必要错误);
//当 op_typy 为0product_id 不为1必须填写 product_id 字段;
//当 op_type 为 1 时,不要填写 product_id 字段。
// 当 op_type 为0product_id 为1不要填写 product_id 字段(会引起不必要错误);
// 当 op_typy 为0product_id 不为1必须填写 product_id 字段;
// 当 op_type 为 1 时,不要填写 product_id 字段。
ProductID string `json:"product_id,omitempty"`
}
//ReqDevice 设备授权实体
// ReqDevice 设备授权实体
type ReqDevice struct {
// 设备的 device id
ID string `json:"id"`
@@ -45,13 +45,13 @@ type ReqDevice struct {
// 1表示设备仅支持andiod classic bluetooth 1|2表示设备支持android 和ios 两种classic bluetooth但是客户端优先选择android classic bluetooth 协议如果android classic bluetooth协议连接失败再选择ios classic bluetooth协议进行连接
// 安卓平台不同时支持BLE和classic类型
ConnectProtocol string `json:"connect_protocol"`
//auth及通信的加密key第三方需要将key烧制在设备上128bit格式采用16进制串的方式长度为32字节不需要0X前缀 1234567890ABCDEF1234567890ABCDEF
// auth及通信的加密key第三方需要将key烧制在设备上128bit格式采用16进制串的方式长度为32字节不需要0X前缀 1234567890ABCDEF1234567890ABCDEF
AuthKey string `json:"auth_key"`
// 断开策略,目前支持: 1退出公众号页面时即断开连接 2退出公众号之后保持连接不断开
CloseStrategy string `json:"close_strategy"`
//连接策略32位整型按bit位置位目前仅第1bit和第3bit位有效bit置0为无效1为有效第2bit已被废弃且bit位可以按或置位如1|4=5各bit置位含义说明如下
//1第1bit置位在公众号对话页面不停的尝试连接设备
//4第3bit置位处于非公众号页面如主界面等微信自动连接。当用户切换微信到前台时可能尝试去连接设备连上后一定时间会断开
// 连接策略32位整型按bit位置位目前仅第1bit和第3bit位有效bit置0为无效1为有效第2bit已被废弃且bit位可以按或置位如1|4=5各bit置位含义说明如下
// 1第1bit置位在公众号对话页面不停的尝试连接设备
// 4第3bit置位处于非公众号页面如主界面等微信自动连接。当用户切换微信到前台时可能尝试去连接设备连上后一定时间会断开
ConnStrategy string `json:"conn_strategy"`
// auth version设备和微信进行auth时会根据该版本号来确认auth buf和auth key的格式各version对应的auth buf及key的具体格式可以参看“客户端蓝牙外设协议”该字段目前支持取值
// 0不加密的version
@@ -69,7 +69,7 @@ type ReqDevice struct {
BleSimpleProtocol string `json:"ble_simple_protocol,omitempty"`
}
//ResBaseInfo 授权回调实体
// ResBaseInfo 授权回调实体
type ResBaseInfo struct {
BaseInfo struct {
DeviceType string `json:"device_type"`

View File

@@ -19,12 +19,12 @@ const (
uriState = "https://api.weixin.qq.com/device/get_stat"
)
//Device struct
// Device struct
type Device struct {
*context.Context
}
//NewDevice 实例
// NewDevice 实例
func NewDevice(context *context.Context) *Device {
device := new(Device)
device.Context = context

View File

@@ -1,6 +1,6 @@
package device
//MsgDevice 设备消息响应
// MsgDevice 设备消息响应
type MsgDevice struct {
DeviceType string
DeviceID string

View File

@@ -7,7 +7,7 @@ import (
"github.com/silenceper/wechat/v2/util"
)
//ResCreateQRCode 获取二维码的返回实体
// ResCreateQRCode 获取二维码的返回实体
type ResCreateQRCode struct {
util.CommonError
DeviceNum int `json:"device_num"`
@@ -42,7 +42,7 @@ func (d *Device) CreateQRCode(devices []string) (res ResCreateQRCode, err error)
return
}
//ResVerifyQRCode 验证授权结果实体
// ResVerifyQRCode 验证授权结果实体
type ResVerifyQRCode struct {
util.CommonError
DeviceType string `json:"device_type"`

View File

@@ -22,7 +22,7 @@ type Config struct {
Signature string `json:"signature"`
}
//NewJs init
// NewJs init
func NewJs(context *context.Context) *Js {
js := new(Js)
js.Context = context
@@ -31,13 +31,13 @@ func NewJs(context *context.Context) *Js {
return js
}
//SetJsTicketHandle 自定义js ticket取值方式
// SetJsTicketHandle 自定义js ticket取值方式
func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) {
js.JsTicketHandle = ticketHandle
}
//GetConfig 获取jssdk需要的配置参数
//uri 为当前网页地址
// GetConfig 获取jssdk需要的配置参数
// uri 为当前网页地址
func (js *Js) GetConfig(uri string) (config *Config, err error) {
config = new(Config)
var accessToken string

View File

@@ -19,33 +19,33 @@ const (
batchGetMaterialURL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"
)
//PermanentMaterialType 永久素材类型
// PermanentMaterialType 永久素材类型
type PermanentMaterialType string
const (
//PermanentMaterialTypeImage 永久素材图片类型image
// PermanentMaterialTypeImage 永久素材图片类型image
PermanentMaterialTypeImage PermanentMaterialType = "image"
//PermanentMaterialTypeVideo 永久素材视频类型video
// PermanentMaterialTypeVideo 永久素材视频类型video
PermanentMaterialTypeVideo PermanentMaterialType = "video"
//PermanentMaterialTypeVoice 永久素材语音类型 voice
// PermanentMaterialTypeVoice 永久素材语音类型 voice
PermanentMaterialTypeVoice PermanentMaterialType = "voice"
//PermanentMaterialTypeNews 永久素材图文类型news
// PermanentMaterialTypeNews 永久素材图文类型news
PermanentMaterialTypeNews PermanentMaterialType = "news"
)
//Material 素材管理
// Material 素材管理
type Material struct {
*context.Context
}
//NewMaterial init
// NewMaterial init
func NewMaterial(context *context.Context) *Material {
material := new(Material)
material.Context = context
return material
}
//Article 永久图文素材
// Article 永久图文素材
type Article struct {
Title string `json:"title"`
ThumbMediaID string `json:"thumb_media_id"`
@@ -87,19 +87,19 @@ func (material *Material) GetNews(id string) ([]*Article, error) {
return res.NewsItem, nil
}
//reqArticles 永久性图文素材请求信息
// reqArticles 永久性图文素材请求信息
type reqArticles struct {
Articles []*Article `json:"articles"`
}
//resArticles 永久性图文素材返回结果
// resArticles 永久性图文素材返回结果
type resArticles struct {
util.CommonError
MediaID string `json:"media_id"`
}
//AddNews 新增永久图文素材
// AddNews 新增永久图文素材
func (material *Material) AddNews(articles []*Article) (mediaID string, err error) {
req := &reqArticles{articles}
@@ -119,11 +119,14 @@ func (material *Material) AddNews(articles []*Article) (mediaID string, err erro
if err != nil {
return
}
if res.ErrCode != 0 {
return "", fmt.Errorf("errcode=%d,errmsg=%s", res.ErrCode, res.ErrMsg)
}
mediaID = res.MediaID
return
}
//reqUpdateArticle 更新永久性图文素材请求信息
// reqUpdateArticle 更新永久性图文素材请求信息
type reqUpdateArticle struct {
MediaID string `json:"media_id"`
Index int64 `json:"index"`
@@ -149,7 +152,7 @@ func (material *Material) UpdateNews(article *Article, mediaID string, index int
return util.DecodeWithCommonError(response, "UpdateNews")
}
//resAddMaterial 永久性素材上传返回的结果
// resAddMaterial 永久性素材上传返回的结果
type resAddMaterial struct {
util.CommonError
@@ -157,7 +160,7 @@ type resAddMaterial struct {
URL string `json:"url"`
}
//AddMaterial 上传永久性素材(处理视频需要单独上传)
// AddMaterial 上传永久性素材(处理视频需要单独上传)
func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) {
if mediaType == MediaTypeVideo {
err = errors.New("永久视频素材上传使用 AddVideo 方法")
@@ -194,7 +197,7 @@ type reqVideo struct {
Introduction string `json:"introduction"`
}
//AddVideo 永久视频素材文件上传
// AddVideo 永久视频素材文件上传
func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) {
var accessToken string
accessToken, err = material.GetAccessToken()
@@ -251,7 +254,7 @@ type reqDeleteMaterial struct {
MediaID string `json:"media_id"`
}
//DeleteMaterial 删除永久素材
// DeleteMaterial 删除永久素材
func (material *Material) DeleteMaterial(mediaID string) error {
accessToken, err := material.GetAccessToken()
if err != nil {
@@ -267,7 +270,7 @@ func (material *Material) DeleteMaterial(mediaID string) error {
return util.DecodeWithCommonError(response, "DeleteMaterial")
}
//ArticleList 永久素材列表
// ArticleList 永久素材列表
type ArticleList struct {
util.CommonError
TotalCount int64 `json:"total_count"`
@@ -275,7 +278,7 @@ type ArticleList struct {
Item []ArticleListItem `json:"item"`
}
//ArticleListItem 用于ArticleList的item节点
// ArticleListItem 用于ArticleList的item节点
type ArticleListItem struct {
MediaID string `json:"media_id"`
Content ArticleListContent `json:"content"`
@@ -284,14 +287,14 @@ type ArticleListItem struct {
UpdateTime int64 `json:"update_time"`
}
//ArticleListContent 用于ArticleListItem的content节点
// ArticleListContent 用于ArticleListItem的content节点
type ArticleListContent struct {
NewsItem []Article `json:"news_item"`
UpdateTime int64 `json:"update_time"`
CreateTime int64 `json:"create_time"`
}
//reqBatchGetMaterial BatchGetMaterial请求参数
// reqBatchGetMaterial BatchGetMaterial请求参数
type reqBatchGetMaterial struct {
Type PermanentMaterialType `json:"type"`
Count int64 `json:"count"`

View File

@@ -7,17 +7,17 @@ import (
"github.com/silenceper/wechat/v2/util"
)
//MediaType 媒体文件类型
// MediaType 媒体文件类型
type MediaType string
const (
//MediaTypeImage 媒体文件:图片
// MediaTypeImage 媒体文件:图片
MediaTypeImage MediaType = "image"
//MediaTypeVoice 媒体文件:声音
// MediaTypeVoice 媒体文件:声音
MediaTypeVoice MediaType = "voice"
//MediaTypeVideo 媒体文件:视频
// MediaTypeVideo 媒体文件:视频
MediaTypeVideo MediaType = "video"
//MediaTypeThumb 媒体文件:缩略图
// MediaTypeThumb 媒体文件:缩略图
MediaTypeThumb MediaType = "thumb"
)
@@ -27,7 +27,7 @@ const (
mediaGetURL = "https://api.weixin.qq.com/cgi-bin/media/get"
)
//Media 临时素材上传返回信息
// Media 临时素材上传返回信息
type Media struct {
util.CommonError
@@ -37,7 +37,7 @@ type Media struct {
CreatedAt int64 `json:"created_at"`
}
//MediaUpload 临时素材上传
// MediaUpload 临时素材上传
func (material *Material) MediaUpload(mediaType MediaType, filename string) (media Media, err error) {
var accessToken string
accessToken, err = material.GetAccessToken()
@@ -62,8 +62,8 @@ func (material *Material) MediaUpload(mediaType MediaType, filename string) (med
return
}
//GetMediaURL 返回临时素材的下载地址供用户自己处理
//NOTICE: URL 不可公开因为含access_token 需要立即另存文件
// GetMediaURL 返回临时素材的下载地址供用户自己处理
// NOTICE: URL 不可公开因为含access_token 需要立即另存文件
func (material *Material) GetMediaURL(mediaID string) (mediaURL string, err error) {
var accessToken string
accessToken, err = material.GetAccessToken()
@@ -74,14 +74,14 @@ func (material *Material) GetMediaURL(mediaID string) (mediaURL string, err erro
return
}
//resMediaImage 图片上传返回结果
// resMediaImage 图片上传返回结果
type resMediaImage struct {
util.CommonError
URL string `json:"url"`
}
//ImageUpload 图片上传
// ImageUpload 图片上传
func (material *Material) ImageUpload(filename string) (url string, err error) {
var accessToken string
accessToken, err = material.GetAccessToken()

View File

@@ -1,6 +1,6 @@
package menu
//Button 菜单按钮
// Button 菜单按钮
type Button struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
@@ -12,48 +12,52 @@ type Button struct {
SubButtons []*Button `json:"sub_button,omitempty"`
}
//SetSubButton 设置二级菜单
func (btn *Button) SetSubButton(name string, subButtons []*Button) {
// SetSubButton 设置二级菜单
func (btn *Button) SetSubButton(name string, subButtons []*Button) *Button {
btn.Name = name
btn.SubButtons = subButtons
btn.Type = ""
btn.Key = ""
btn.URL = ""
btn.MediaID = ""
return btn
}
//SetClickButton btn 为click类型
func (btn *Button) SetClickButton(name, key string) {
// SetClickButton btn 为click类型
func (btn *Button) SetClickButton(name, key string) *Button {
btn.Type = "click"
btn.Name = name
btn.Key = key
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
//SetViewButton view类型
func (btn *Button) SetViewButton(name, url string) {
// SetViewButton view类型
func (btn *Button) SetViewButton(name, url string) *Button {
btn.Type = "view"
btn.Name = name
btn.URL = url
btn.Key = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
// SetScanCodePushButton 扫码推事件
func (btn *Button) SetScanCodePushButton(name, key string) {
func (btn *Button) SetScanCodePushButton(name, key string) *Button {
btn.Type = "scancode_push"
btn.Name = name
btn.Key = key
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
//SetScanCodeWaitMsgButton 设置 扫码推事件且弹出"消息接收中"提示框
func (btn *Button) SetScanCodeWaitMsgButton(name, key string) {
// SetScanCodeWaitMsgButton 设置 扫码推事件且弹出"消息接收中"提示框
func (btn *Button) SetScanCodeWaitMsgButton(name, key string) *Button {
btn.Type = "scancode_waitmsg"
btn.Name = name
btn.Key = key
@@ -61,10 +65,11 @@ func (btn *Button) SetScanCodeWaitMsgButton(name, key string) {
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
//SetPicSysPhotoButton 设置弹出系统拍照发图按钮
func (btn *Button) SetPicSysPhotoButton(name, key string) {
// SetPicSysPhotoButton 设置弹出系统拍照发图按钮
func (btn *Button) SetPicSysPhotoButton(name, key string) *Button {
btn.Type = "pic_sysphoto"
btn.Name = name
btn.Key = key
@@ -72,10 +77,11 @@ func (btn *Button) SetPicSysPhotoButton(name, key string) {
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
//SetPicPhotoOrAlbumButton 设置弹出拍照或者相册发图类型按钮
func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) {
// SetPicPhotoOrAlbumButton 设置弹出拍照或者相册发图类型按钮
func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) *Button {
btn.Type = "pic_photo_or_album"
btn.Name = name
btn.Key = key
@@ -83,10 +89,11 @@ func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) {
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
// SetPicWeixinButton 设置弹出微信相册发图器类型按钮
func (btn *Button) SetPicWeixinButton(name, key string) {
func (btn *Button) SetPicWeixinButton(name, key string) *Button {
btn.Type = "pic_weixin"
btn.Name = name
btn.Key = key
@@ -94,10 +101,11 @@ func (btn *Button) SetPicWeixinButton(name, key string) {
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
// SetLocationSelectButton 设置 弹出地理位置选择器 类型按钮
func (btn *Button) SetLocationSelectButton(name, key string) {
func (btn *Button) SetLocationSelectButton(name, key string) *Button {
btn.Type = "location_select"
btn.Name = name
btn.Key = key
@@ -105,10 +113,11 @@ func (btn *Button) SetLocationSelectButton(name, key string) {
btn.URL = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
//SetMediaIDButton 设置 下发消息(除文本消息) 类型按钮
func (btn *Button) SetMediaIDButton(name, mediaID string) {
// SetMediaIDButton 设置 下发消息(除文本消息) 类型按钮
func (btn *Button) SetMediaIDButton(name, mediaID string) *Button {
btn.Type = "media_id"
btn.Name = name
btn.MediaID = mediaID
@@ -116,10 +125,11 @@ func (btn *Button) SetMediaIDButton(name, mediaID string) {
btn.Key = ""
btn.URL = ""
btn.SubButtons = nil
return btn
}
//SetViewLimitedButton 设置 跳转图文消息URL 类型按钮
func (btn *Button) SetViewLimitedButton(name, mediaID string) {
// SetViewLimitedButton 设置 跳转图文消息URL 类型按钮
func (btn *Button) SetViewLimitedButton(name, mediaID string) *Button {
btn.Type = "view_limited"
btn.Name = name
btn.MediaID = mediaID
@@ -127,10 +137,11 @@ func (btn *Button) SetViewLimitedButton(name, mediaID string) {
btn.Key = ""
btn.URL = ""
btn.SubButtons = nil
return btn
}
//SetMiniprogramButton 设置 跳转小程序 类型按钮 (公众号后台必须已经关联小程序)
func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) {
// SetMiniprogramButton 设置 跳转小程序 类型按钮 (公众号后台必须已经关联小程序)
func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) *Button {
btn.Type = "miniprogram"
btn.Name = name
btn.URL = url
@@ -140,4 +151,65 @@ func (btn *Button) SetMiniprogramButton(name, url, appID, pagePath string) {
btn.Key = ""
btn.MediaID = ""
btn.SubButtons = nil
return btn
}
// NewSubButton 二级菜单
func NewSubButton(name string, subButtons []*Button) *Button {
return (&Button{}).SetSubButton(name, subButtons)
}
// NewClickButton btn 为click类型
func NewClickButton(name, key string) *Button {
return (&Button{}).SetClickButton(name, key)
}
// NewViewButton view类型
func NewViewButton(name, url string) *Button {
return (&Button{}).SetViewButton(name, url)
}
// NewScanCodePushButton 扫码推事件
func NewScanCodePushButton(name, key string) *Button {
return (&Button{}).SetScanCodePushButton(name, key)
}
// NewScanCodeWaitMsgButton 扫码推事件且弹出"消息接收中"提示框
func NewScanCodeWaitMsgButton(name, key string) *Button {
return (&Button{}).SetScanCodeWaitMsgButton(name, key)
}
// NewPicSysPhotoButton 弹出系统拍照发图按钮
func NewPicSysPhotoButton(name, key string) *Button {
return (&Button{}).SetPicSysPhotoButton(name, key)
}
// NewPicPhotoOrAlbumButton 弹出拍照或者相册发图类型按钮
func NewPicPhotoOrAlbumButton(name, key string) *Button {
return (&Button{}).SetPicPhotoOrAlbumButton(name, key)
}
// NewPicWeixinButton 弹出微信相册发图器类型按钮
func NewPicWeixinButton(name, key string) *Button {
return (&Button{}).SetPicWeixinButton(name, key)
}
// NewLocationSelectButton 弹出地理位置选择器 类型按钮
func NewLocationSelectButton(name, key string) *Button {
return (&Button{}).SetLocationSelectButton(name, key)
}
// NewMediaIDButton 下发消息(除文本消息) 类型按钮
func NewMediaIDButton(name, mediaID string) *Button {
return (&Button{}).SetMediaIDButton(name, mediaID)
}
// NewViewLimitedButton 跳转图文消息URL 类型按钮
func NewViewLimitedButton(name, mediaID string) *Button {
return (&Button{}).SetViewLimitedButton(name, mediaID)
}
// NewMiniprogramButton 跳转小程序 类型按钮 (公众号后台必须已经关联小程序)
func NewMiniprogramButton(name, url, appID, pagePath string) *Button {
return (&Button{}).SetMiniprogramButton(name, url, appID, pagePath)
}

View File

@@ -0,0 +1,28 @@
package menu
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewButtonFun(t *testing.T) {
buttons := []*Button{
NewSubButton("1", []*Button{
NewViewButton("1.1", "https://baidu.com"),
NewViewButton("1.2", "https://baidu.com"),
NewViewButton("1.3", "https://baidu.com"),
}),
NewSubButton("2", []*Button{
NewViewButton("2.1", "https://baidu.com"),
NewViewButton("2.2", "https://baidu.com"),
NewViewButton("2.3", "https://baidu.com"),
}),
NewViewButton("3", "https://baidu.com"),
}
data, err := json.Marshal(buttons)
assert.Nil(t, err)
assert.Equal(t, `[{"name":"1","sub_button":[{"type":"view","name":"1.1","url":"https://baidu.com"},{"type":"view","name":"1.2","url":"https://baidu.com"},{"type":"view","name":"1.3","url":"https://baidu.com"}]},{"name":"2","sub_button":[{"type":"view","name":"2.1","url":"https://baidu.com"},{"type":"view","name":"2.2","url":"https://baidu.com"},{"type":"view","name":"2.3","url":"https://baidu.com"}]},{"type":"view","name":"3","url":"https://baidu.com"}]`, string(data))
}

View File

@@ -18,42 +18,42 @@ const (
menuSelfMenuInfoURL = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info"
)
//Menu struct
// Menu struct
type Menu struct {
*context.Context
}
//reqMenu 设置菜单请求数据
// reqMenu 设置菜单请求数据
type reqMenu struct {
Button []*Button `json:"button,omitempty"`
MatchRule *MatchRule `json:"matchrule,omitempty"`
}
//reqDeleteConditional 删除个性化菜单请求数据
// reqDeleteConditional 删除个性化菜单请求数据
type reqDeleteConditional struct {
MenuID int64 `json:"menuid"`
}
//reqMenuTryMatch 菜单匹配请求
// reqMenuTryMatch 菜单匹配请求
type reqMenuTryMatch struct {
UserID string `json:"user_id"`
}
//resConditionalMenu 个性化菜单返回结果
// resConditionalMenu 个性化菜单返回结果
type resConditionalMenu struct {
Button []Button `json:"button"`
MatchRule MatchRule `json:"matchrule"`
MenuID int64 `json:"menuid"`
}
//resMenuTryMatch 菜单匹配请求结果
// resMenuTryMatch 菜单匹配请求结果
type resMenuTryMatch struct {
util.CommonError
Button []Button `json:"button"`
}
//ResMenu 查询菜单的返回数据
// ResMenu 查询菜单的返回数据
type ResMenu struct {
util.CommonError
@@ -64,7 +64,7 @@ type ResMenu struct {
Conditionalmenu []resConditionalMenu `json:"conditionalmenu"`
}
//ResSelfMenuInfo 自定义菜单配置返回结果
// ResSelfMenuInfo 自定义菜单配置返回结果
type ResSelfMenuInfo struct {
util.CommonError
@@ -74,7 +74,7 @@ type ResSelfMenuInfo struct {
} `json:"selfmenu_info"`
}
//SelfMenuButton 自定义菜单配置详情
// SelfMenuButton 自定义菜单配置详情
type SelfMenuButton struct {
Type string `json:"type"`
Name string `json:"name"`
@@ -89,7 +89,7 @@ type SelfMenuButton struct {
} `json:"news_info,omitempty"`
}
//ButtonNew 图文消息菜单
// ButtonNew 图文消息菜单
type ButtonNew struct {
Title string `json:"title"`
Author string `json:"author"`
@@ -100,7 +100,7 @@ type ButtonNew struct {
SourceURL string `json:"source_url"`
}
//MatchRule 个性化菜单规则
// MatchRule 个性化菜单规则
type MatchRule struct {
GroupID string `json:"group_id,omitempty"`
Sex string `json:"sex,omitempty"`
@@ -111,14 +111,14 @@ type MatchRule struct {
Language string `json:"language,omitempty"`
}
//NewMenu 实例
// NewMenu 实例
func NewMenu(context *context.Context) *Menu {
menu := new(Menu)
menu.Context = context
return menu
}
//SetMenu 设置按钮
// SetMenu 设置按钮
func (menu *Menu) SetMenu(buttons []*Button) error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -138,7 +138,7 @@ func (menu *Menu) SetMenu(buttons []*Button) error {
return util.DecodeWithCommonError(response, "SetMenu")
}
//SetMenuByJSON 设置按钮
// SetMenuByJSON 设置按钮
func (menu *Menu) SetMenuByJSON(jsonInfo string) error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -147,15 +147,15 @@ func (menu *Menu) SetMenuByJSON(jsonInfo string) error {
uri := fmt.Sprintf("%s?access_token=%s", menuCreateURL, accessToken)
response, err := util.PostJSON(uri, jsonInfo)
response, err := util.HTTPPost(uri, jsonInfo)
if err != nil {
return err
}
return util.DecodeWithCommonError(response, "SetMenu")
return util.DecodeWithCommonError(response, "SetMenuByJSON")
}
//GetMenu 获取菜单配置
// GetMenu 获取菜单配置
func (menu *Menu) GetMenu() (resMenu ResMenu, err error) {
var accessToken string
accessToken, err = menu.GetAccessToken()
@@ -179,7 +179,7 @@ func (menu *Menu) GetMenu() (resMenu ResMenu, err error) {
return
}
//DeleteMenu 删除菜单
// DeleteMenu 删除菜单
func (menu *Menu) DeleteMenu() error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -194,7 +194,7 @@ func (menu *Menu) DeleteMenu() error {
return util.DecodeWithCommonError(response, "GetMenu")
}
//AddConditional 添加个性化菜单
// AddConditional 添加个性化菜单
func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -215,7 +215,7 @@ func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error
return util.DecodeWithCommonError(response, "AddConditional")
}
//AddConditionalByJSON 添加个性化菜单
// AddConditionalByJSON 添加个性化菜单
func (menu *Menu) AddConditionalByJSON(jsonInfo string) error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -223,7 +223,7 @@ func (menu *Menu) AddConditionalByJSON(jsonInfo string) error {
}
uri := fmt.Sprintf("%s?access_token=%s", menuAddConditionalURL, accessToken)
response, err := util.PostJSON(uri, jsonInfo)
response, err := util.HTTPPost(uri, jsonInfo)
if err != nil {
return err
}
@@ -231,7 +231,7 @@ func (menu *Menu) AddConditionalByJSON(jsonInfo string) error {
return util.DecodeWithCommonError(response, "AddConditional")
}
//DeleteConditional 删除个性化菜单
// DeleteConditional 删除个性化菜单
func (menu *Menu) DeleteConditional(menuID int64) error {
accessToken, err := menu.GetAccessToken()
if err != nil {
@@ -251,7 +251,7 @@ func (menu *Menu) DeleteConditional(menuID int64) error {
return util.DecodeWithCommonError(response, "DeleteConditional")
}
//MenuTryMatch 菜单匹配
// MenuTryMatch 菜单匹配
func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) {
var accessToken string
accessToken, err = menu.GetAccessToken()
@@ -278,7 +278,7 @@ func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) {
return
}
//GetCurrentSelfMenuInfo 获取自定义菜单配置接口
// GetCurrentSelfMenuInfo 获取自定义菜单配置接口
func (menu *Menu) GetCurrentSelfMenuInfo() (resSelfMenuInfo ResSelfMenuInfo, err error) {
var accessToken string
accessToken, err = menu.GetAccessToken()

View File

@@ -12,35 +12,35 @@ const (
customerSendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
)
//Manager 消息管理者,可以发送消息
// Manager 消息管理者,可以发送消息
type Manager struct {
*context.Context
}
//NewMessageManager 实例化消息管理者
// NewMessageManager 实例化消息管理者
func NewMessageManager(context *context.Context) *Manager {
return &Manager{
context,
}
}
//CustomerMessage 客服消息
// CustomerMessage 客服消息
type CustomerMessage struct {
ToUser string `json:"touser"` //接受者OpenID
Msgtype MsgType `json:"msgtype"` //客服消息类型
Text *MediaText `json:"text,omitempty"` //可选
Image *MediaResource `json:"image,omitempty"` //可选
Voice *MediaResource `json:"voice,omitempty"` //可选
Video *MediaVideo `json:"video,omitempty"` //可选
Music *MediaMusic `json:"music,omitempty"` //可选
News *MediaNews `json:"news,omitempty"` //可选
Mpnews *MediaResource `json:"mpnews,omitempty"` //可选
Wxcard *MediaWxcard `json:"wxcard,omitempty"` //可选
Msgmenu *MediaMsgmenu `json:"msgmenu,omitempty"` //可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` //可选
ToUser string `json:"touser"` // 接受者OpenID
Msgtype MsgType `json:"msgtype"` // 客服消息类型
Text *MediaText `json:"text,omitempty"` // 可选
Image *MediaResource `json:"image,omitempty"` // 可选
Voice *MediaResource `json:"voice,omitempty"` // 可选
Video *MediaVideo `json:"video,omitempty"` // 可选
Music *MediaMusic `json:"music,omitempty"` // 可选
News *MediaNews `json:"news,omitempty"` // 可选
Mpnews *MediaResource `json:"mpnews,omitempty"` // 可选
Wxcard *MediaWxcard `json:"wxcard,omitempty"` // 可选
Msgmenu *MediaMsgmenu `json:"msgmenu,omitempty"` // 可选
Miniprogrampage *MediaMiniprogrampage `json:"miniprogrampage,omitempty"` // 可选
}
//NewCustomerTextMessage 文本消息结构体构造方法
// NewCustomerTextMessage 文本消息结构体构造方法
func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -51,7 +51,7 @@ func NewCustomerTextMessage(toUser, text string) *CustomerMessage {
}
}
//NewCustomerImgMessage 图片消息的构造方法
// NewCustomerImgMessage 图片消息的构造方法
func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -62,7 +62,7 @@ func NewCustomerImgMessage(toUser, mediaID string) *CustomerMessage {
}
}
//NewCustomerVoiceMessage 语音消息的构造方法
// NewCustomerVoiceMessage 语音消息的构造方法
func NewCustomerVoiceMessage(toUser, mediaID string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -73,7 +73,7 @@ func NewCustomerVoiceMessage(toUser, mediaID string) *CustomerMessage {
}
}
//NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
// NewCustomerMiniprogrampageMessage 小程序卡片消息的构造方法
func NewCustomerMiniprogrampageMessage(toUser, title, appID, pagePath, thumbMediaID string) *CustomerMessage {
return &CustomerMessage{
ToUser: toUser,
@@ -87,17 +87,17 @@ func NewCustomerMiniprogrampageMessage(toUser, title, appID, pagePath, thumbMedi
}
}
//MediaText 文本消息的文字
// MediaText 文本消息的文字
type MediaText struct {
Content string `json:"content"`
}
//MediaResource 消息使用的永久素材id
// MediaResource 消息使用的永久素材id
type MediaResource struct {
MediaID string `json:"media_id"`
}
//MediaVideo 视频消息包含的内容
// MediaVideo 视频消息包含的内容
type MediaVideo struct {
MediaID string `json:"media_id"`
ThumbMediaID string `json:"thumb_media_id"`
@@ -105,7 +105,7 @@ type MediaVideo struct {
Description string `json:"description"`
}
//MediaMusic 音乐消息包括的内容
// MediaMusic 音乐消息包括的内容
type MediaMusic struct {
Title string `json:"title"`
Description string `json:"description"`
@@ -114,12 +114,12 @@ type MediaMusic struct {
ThumbMediaID string `json:"thumb_media_id"`
}
//MediaNews 图文消息的内容
// MediaNews 图文消息的内容
type MediaNews struct {
Articles []MediaArticles `json:"articles"`
}
//MediaArticles 图文消息的内容的文章列表中的单独一条
// MediaArticles 图文消息的内容的文章列表中的单独一条
type MediaArticles struct {
Title string `json:"title"`
Description string `json:"description"`
@@ -127,25 +127,25 @@ type MediaArticles struct {
Picurl string `json:"picurl"`
}
//MediaMsgmenu 菜单消息的内容
// MediaMsgmenu 菜单消息的内容
type MediaMsgmenu struct {
HeadContent string `json:"head_content"`
List []MsgmenuItem `json:"list"`
TailContent string `json:"tail_content"`
}
//MsgmenuItem 菜单消息的菜单按钮
// MsgmenuItem 菜单消息的菜单按钮
type MsgmenuItem struct {
ID string `json:"id"`
Content string `json:"content"`
}
//MediaWxcard 卡券的id
// MediaWxcard 卡券的id
type MediaWxcard struct {
CardID string `json:"card_id"`
}
//MediaMiniprogrampage 小程序消息
// MediaMiniprogrampage 小程序消息
type MediaMiniprogrampage struct {
Title string `json:"title"`
AppID string `json:"appid"`
@@ -153,7 +153,7 @@ type MediaMiniprogrampage struct {
ThumbMediaID string `json:"thumb_media_id"`
}
//Send 发送客服消息
// Send 发送客服消息
func (manager *Manager) Send(msg *CustomerMessage) error {
accessToken, err := manager.Context.GetAccessToken()
if err != nil {

View File

@@ -1,6 +1,6 @@
package message
//Image 图片消息
// Image 图片消息
type Image struct {
CommonToken
@@ -9,7 +9,7 @@ type Image struct {
} `xml:"Image"`
}
//NewImage 回复图片消息
// NewImage 回复图片消息
func NewImage(mediaID string) *Image {
image := new(Image)
image.Image.MediaID = mediaID

View File

@@ -16,83 +16,89 @@ type EventType string
type InfoType string
const (
//MsgTypeText 表示文本消息
// MsgTypeText 表示文本消息
MsgTypeText MsgType = "text"
//MsgTypeImage 表示图片消息
MsgTypeImage = "image"
//MsgTypeVoice 表示语音消息
MsgTypeVoice = "voice"
//MsgTypeVideo 表示视频消息
MsgTypeVideo = "video"
//MsgTypeMiniprogrampage 表示小程序卡片消息
MsgTypeMiniprogrampage = "miniprogrampage"
//MsgTypeShortVideo 表示短视频消息[限接收]
MsgTypeShortVideo = "shortvideo"
//MsgTypeLocation 表示坐标消息[限接收]
MsgTypeLocation = "location"
//MsgTypeLink 表示链接消息[限接收]
MsgTypeLink = "link"
//MsgTypeMusic 表示音乐消息[限回复]
MsgTypeMusic = "music"
//MsgTypeNews 表示图文消息[限回复]
MsgTypeNews = "news"
//MsgTypeTransfer 表示消息消息转发到客服
MsgTypeTransfer = "transfer_customer_service"
//MsgTypeEvent 表示事件推送消息
MsgTypeEvent = "event"
// MsgTypeImage 表示图片消息
MsgTypeImage MsgType = "image"
// MsgTypeVoice 表示语音消息
MsgTypeVoice MsgType = "voice"
// MsgTypeVideo 表示视频消息
MsgTypeVideo MsgType = "video"
// MsgTypeMiniprogrampage 表示小程序卡片消息
MsgTypeMiniprogrampage MsgType = "miniprogrampage"
// MsgTypeShortVideo 表示短视频消息[限接收]
MsgTypeShortVideo MsgType = "shortvideo"
// MsgTypeLocation 表示坐标消息[限接收]
MsgTypeLocation MsgType = "location"
// MsgTypeLink 表示链接消息[限接收]
MsgTypeLink MsgType = "link"
// MsgTypeMusic 表示音乐消息[限回复]
MsgTypeMusic MsgType = "music"
// MsgTypeNews 表示图文消息[限回复]
MsgTypeNews MsgType = "news"
// MsgTypeTransfer 表示消息消息转发到客服
MsgTypeTransfer MsgType = "transfer_customer_service"
// MsgTypeEvent 表示事件推送消息
MsgTypeEvent MsgType = "event"
)
const (
//EventSubscribe 订阅
// EventSubscribe 订阅
EventSubscribe EventType = "subscribe"
//EventUnsubscribe 取消订阅
EventUnsubscribe = "unsubscribe"
//EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者
EventScan = "SCAN"
//EventLocation 上报地理位置事件
EventLocation = "LOCATION"
//EventClick 点击菜单拉取消息时的事件推送
EventClick = "CLICK"
//EventView 点击菜单跳转链接时的事件推送
EventView = "VIEW"
//EventScancodePush 扫码推事件的事件推送
EventScancodePush = "scancode_push"
//EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
EventScancodeWaitmsg = "scancode_waitmsg"
//EventPicSysphoto 弹出系统拍照发图的事件推送
EventPicSysphoto = "pic_sysphoto"
//EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
EventPicPhotoOrAlbum = "pic_photo_or_album"
//EventPicWeixin 弹出微信相册发图器的事件推送
EventPicWeixin = "pic_weixin"
//EventLocationSelect 弹出地理位置选择器的事件推送
EventLocationSelect = "location_select"
//EventTemplateSendJobFinish 发送模板消息推送通知
EventTemplateSendJobFinish = "TEMPLATESENDJOBFINISH"
//EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
EventWxaMediaCheck = "wxa_media_check"
// EventUnsubscribe 取消订阅
EventUnsubscribe EventType = "unsubscribe"
// EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者
EventScan EventType = "SCAN"
// EventLocation 上报地理位置事件
EventLocation EventType = "LOCATION"
// EventClick 点击菜单拉取消息时的事件推送
EventClick EventType = "CLICK"
// EventView 点击菜单跳转链接时的事件推送
EventView EventType = "VIEW"
// EventScancodePush 扫码推事件的事件推送
EventScancodePush EventType = "scancode_push"
// EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
EventScancodeWaitmsg EventType = "scancode_waitmsg"
// EventPicSysphoto 弹出系统拍照发图的事件推送
EventPicSysphoto EventType = "pic_sysphoto"
// EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
EventPicPhotoOrAlbum EventType = "pic_photo_or_album"
// EventPicWeixin 弹出微信相册发图器的事件推送
EventPicWeixin EventType = "pic_weixin"
// EventLocationSelect 弹出地理位置选择器的事件推送
EventLocationSelect EventType = "location_select"
// EventTemplateSendJobFinish 发送模板消息推送通知
EventTemplateSendJobFinish EventType = "TEMPLATESENDJOBFINISH"
// EventMassSendJobFinish 群发消息推送通知
EventMassSendJobFinish EventType = "MASSSENDJOBFINISH"
// EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
EventWxaMediaCheck EventType = "wxa_media_check"
// EventSubscribeMsgPopupEvent 订阅通知事件推送
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
)
const (
//微信开放平台需要用到
// 微信开放平台需要用到
// InfoTypeVerifyTicket 返回ticket
InfoTypeVerifyTicket InfoType = "component_verify_ticket"
// InfoTypeAuthorized 授权
InfoTypeAuthorized = "authorized"
InfoTypeAuthorized InfoType = "authorized"
// InfoTypeUnauthorized 取消授权
InfoTypeUnauthorized = "unauthorized"
InfoTypeUnauthorized InfoType = "unauthorized"
// InfoTypeUpdateAuthorized 更新授权
InfoTypeUpdateAuthorized = "updateauthorized"
InfoTypeUpdateAuthorized InfoType = "updateauthorized"
// InfoTypeNotifyThirdFasterRegister 注册审核事件推送
InfoTypeNotifyThirdFasterRegister InfoType = "notify_third_fasteregister"
)
//MixMessage 存放所有微信发送过来的消息和事件
// MixMessage 存放所有微信发送过来的消息和事件
type MixMessage struct {
CommonToken
//基本消息
MsgID int64 `xml:"MsgId"` //其他消息推送过来是MsgId
TemplateMsgID int64 `xml:"MsgID"` //模板消息推送成功的消息是MsgID
// 基本消息
MsgID int64 `xml:"MsgId"` // 其他消息推送过来是MsgId
TemplateMsgID int64 `xml:"MsgID"` // 模板消息推送成功的消息是MsgID
Content string `xml:"Content"`
Recognition string `xml:"Recognition"`
PicURL string `xml:"PicUrl"`
@@ -107,7 +113,7 @@ type MixMessage struct {
Description string `xml:"Description"`
URL string `xml:"Url"`
//事件相关
// 事件相关
Event EventType `xml:"Event"`
EventKey string `xml:"EventKey"`
Ticket string `xml:"Ticket"`
@@ -117,6 +123,10 @@ type MixMessage struct {
MenuID string `xml:"MenuId"`
Status string `xml:"Status"`
SessionFrom string `xml:"SessionFrom"`
TotalCount int64 `xml:"TotalCount"`
FilterCount int64 `xml:"FilterCount"`
SentCount int64 `xml:"SentCount"`
ErrorCount int64 `xml:"ErrorCount"`
ScanCodeInfo struct {
ScanType string `xml:"ScanType"`
@@ -136,6 +146,10 @@ type MixMessage struct {
Poiname string `xml:"Poiname"`
}
SubscribeMsgPopupEvent []struct {
List SubscribeMsgPopupEvent `xml:"List"`
} `xml:"SubscribeMsgPopupEvent"`
// 第三方平台相关
InfoType InfoType `xml:"InfoType"`
AppID string `xml:"AppId"`
@@ -144,6 +158,15 @@ type MixMessage struct {
AuthorizationCode string `xml:"AuthorizationCode"`
AuthorizationCodeExpiredTime int64 `xml:"AuthorizationCodeExpiredTime"`
PreAuthCode string `xml:"PreAuthCode"`
AuthCode string `xml:"auth_code"`
Info struct {
Name string `xml:"name"`
Code string `xml:"code"`
CodeType int `xml:"code_type"`
LegalPersonaWechat string `xml:"legal_persona_wechat"`
LegalPersonaName string `xml:"legal_persona_name"`
ComponentPhone string `xml:"component_phone"`
} `xml:"info"`
// 卡券相关
CardID string `xml:"CardId"`
@@ -162,23 +185,30 @@ type MixMessage struct {
TraceID string `xml:"trace_id"`
StatusCode int `xml:"status_code"`
//设备相关
// 设备相关
device.MsgDevice
}
//EventPic 发图事件推送
// SubscribeMsgPopupEvent 订阅通知事件推送的消息体
type SubscribeMsgPopupEvent struct {
TemplateID string `xml:"TemplateId"`
SubscribeStatusString string `xml:"SubscribeStatusString"`
PopupScene int `xml:"PopupScene"`
}
// EventPic 发图事件推送
type EventPic struct {
PicMd5Sum string `xml:"PicMd5Sum"`
}
//EncryptedXMLMsg 安全模式下的消息体
// EncryptedXMLMsg 安全模式下的消息体
type EncryptedXMLMsg struct {
XMLName struct{} `xml:"xml" json:"-"`
ToUserName string `xml:"ToUserName" json:"ToUserName"`
EncryptedMsg string `xml:"Encrypt" json:"Encrypt"`
}
//ResponseEncryptedXMLMsg 需要返回的消息体
// ResponseEncryptedXMLMsg 需要返回的消息体
type ResponseEncryptedXMLMsg struct {
XMLName struct{} `xml:"xml" json:"-"`
EncryptedMsg string `xml:"Encrypt" json:"Encrypt"`
@@ -206,22 +236,27 @@ type CommonToken struct {
MsgType MsgType `xml:"MsgType"`
}
//SetToUserName set ToUserName
// SetToUserName set ToUserName
func (msg *CommonToken) SetToUserName(toUserName CDATA) {
msg.ToUserName = toUserName
}
//SetFromUserName set FromUserName
// SetFromUserName set FromUserName
func (msg *CommonToken) SetFromUserName(fromUserName CDATA) {
msg.FromUserName = fromUserName
}
//SetCreateTime set createTime
// SetCreateTime set createTime
func (msg *CommonToken) SetCreateTime(createTime int64) {
msg.CreateTime = createTime
}
//SetMsgType set MsgType
// SetMsgType set MsgType
func (msg *CommonToken) SetMsgType(msgType MsgType) {
msg.MsgType = msgType
}
// GetOpenID get the FromUserName value
func (msg *CommonToken) GetOpenID() string {
return string(msg.FromUserName)
}

View File

@@ -1,6 +1,6 @@
package message
//Music 音乐消息
// Music 音乐消息
type Music struct {
CommonToken
@@ -13,7 +13,7 @@ type Music struct {
} `xml:"Music"`
}
//NewMusic 回复音乐消息
// NewMusic 回复音乐消息
func NewMusic(title, description, musicURL, hQMusicURL, thumbMediaID string) *Music {
music := new(Music)
music.Music.Title = title

View File

@@ -1,6 +1,6 @@
package message
//News 图文消息
// News 图文消息
type News struct {
CommonToken
@@ -8,7 +8,7 @@ type News struct {
Articles []*Article `xml:"Articles>item,omitempty"`
}
//NewNews 初始化图文消息
// NewNews 初始化图文消息
func NewNews(articles []*Article) *News {
news := new(News)
news.ArticleCount = len(articles)
@@ -16,7 +16,7 @@ func NewNews(articles []*Article) *News {
return news
}
//Article 单篇文章
// Article 单篇文章
type Article struct {
Title string `xml:"Title,omitempty"`
Description string `xml:"Description,omitempty"`
@@ -24,7 +24,7 @@ type Article struct {
URL string `xml:"Url,omitempty"`
}
//NewArticle 初始化文章
// NewArticle 初始化文章
func NewArticle(title, description, picURL, url string) *Article {
article := new(Article)
article.Title = title

View File

@@ -2,13 +2,13 @@ package message
import "errors"
//ErrInvalidReply 无效的回复
// ErrInvalidReply 无效的回复
var ErrInvalidReply = errors.New("无效的回复消息")
//ErrUnsupportReply 不支持的回复类型
// ErrUnsupportReply 不支持的回复类型
var ErrUnsupportReply = errors.New("不支持的回复消息")
//Reply 消息回复
// Reply 消息回复
type Reply struct {
MsgType MsgType
MsgData interface{}

View File

@@ -0,0 +1,147 @@
package message
import (
"fmt"
"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)
const (
subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend"
subscribeTemplateListURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
subscribeTemplateAddURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
subscribeTemplateDelURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
)
// Subscribe 订阅消息
type Subscribe struct {
*context.Context
}
// NewSubscribe 实例化
func NewSubscribe(context *context.Context) *Subscribe {
tpl := new(Subscribe)
tpl.Context = context
return tpl
}
// SubscribeMessage 发送的订阅消息内容
type SubscribeMessage struct {
ToUser string `json:"touser"` // 必须, 接受者OpenID
TemplateID string `json:"template_id"` // 必须, 模版ID
Page string `json:"page,omitempty"` // 可选, 跳转网页时填写
Data map[string]*SubscribeDataItem `json:"data"` // 必须, 模板数据
MiniProgram struct {
AppID string `json:"appid"` // 所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系
PagePath string `json:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar
} `json:"miniprogram"` // 可选,跳转至小程序地址
}
// SubscribeDataItem 模版内某个 .DATA 的值
type SubscribeDataItem struct {
Value string `json:"value"`
}
// Send 发送订阅消息
func (tpl *Subscribe) Send(msg *SubscribeMessage) (err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "SendSubscribeMessage")
}
// PrivateSubscribeItem 私有订阅消息模板
type PrivateSubscribeItem struct {
PriTmplID string `json:"priTmplId"` // 添加至帐号下的模板 id发送订阅通知时所需
Title string `json:"title"` // 模版标题
Content string `json:"content"` // 模版内容
Example string `json:"example"` // 模板内容示例
SubType int `json:"type"` // 模版类型2 为一次性订阅3 为长期订阅
}
type resPrivateSubscribeList struct {
util.CommonError
SubscriptionList []*PrivateSubscribeItem `json:"data"`
}
// List 获取私有订阅消息模板列表
func (tpl *Subscribe) List() (templateList []*PrivateSubscribeItem, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeTemplateListURL, accessToken)
var response []byte
response, err = util.HTTPGet(uri)
if err != nil {
return
}
var res resPrivateSubscribeList
err = util.DecodeWithError(response, &res, "ListSubscribe")
if err != nil {
return
}
templateList = res.SubscriptionList
return
}
type resSubscribeAdd struct {
util.CommonError
TemplateID string `json:"priTmplId"`
}
// Add 添加订阅消息模板
func (tpl *Subscribe) Add(ShortID string, kidList []int, sceneDesc string) (templateID string, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateIDShort string `json:"tid"`
SceneDesc string `json:"sceneDesc"`
KidList []int `json:"kidList"`
}{TemplateIDShort: ShortID, SceneDesc: sceneDesc, KidList: kidList}
uri := fmt.Sprintf("%s?access_token=%s", subscribeTemplateAddURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
var result resSubscribeAdd
err = util.DecodeWithError(response, &result, "AddSubscribe")
if err != nil {
return
}
templateID = result.TemplateID
return
}
// Delete 删除私有模板
func (tpl *Subscribe) Delete(templateID string) (err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateID string `json:"priTmplId"`
}{TemplateID: templateID}
uri := fmt.Sprintf("%s?access_token=%s", subscribeTemplateDelURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "DeleteSubscribe")
}

View File

@@ -11,21 +11,23 @@ import (
const (
templateSendURL = "https://api.weixin.qq.com/cgi-bin/message/template/send"
templateListURL = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template"
templateAddURL = "https://api.weixin.qq.com/cgi-bin/template/api_add_template"
templateDelURL = "https://api.weixin.qq.com/cgi-bin/template/del_private_template"
)
//Template 模板消息
// Template 模板消息
type Template struct {
*context.Context
}
//NewTemplate 实例化
// NewTemplate 实例化
func NewTemplate(context *context.Context) *Template {
tpl := new(Template)
tpl.Context = context
return tpl
}
//TemplateMessage 发送的模板消息内容
// TemplateMessage 发送的模板消息内容
type TemplateMessage struct {
ToUser string `json:"touser"` // 必须, 接受者OpenID
TemplateID string `json:"template_id"` // 必须, 模版ID
@@ -34,12 +36,12 @@ type TemplateMessage struct {
Data map[string]*TemplateDataItem `json:"data"` // 必须, 模板数据
MiniProgram struct {
AppID string `json:"appid"` //所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系
PagePath string `json:"pagepath"` //所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar
} `json:"miniprogram"` //可选,跳转至小程序地址
AppID string `json:"appid"` // 所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系
PagePath string `json:"pagepath"` // 所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar
} `json:"miniprogram"` // 可选,跳转至小程序地址
}
//TemplateDataItem 模版内某个 .DATA 的值
// TemplateDataItem 模版内某个 .DATA 的值
type TemplateDataItem struct {
Value string `json:"value"`
Color string `json:"color,omitempty"`
@@ -51,7 +53,7 @@ type resTemplateSend struct {
MsgID int64 `json:"msgid"`
}
//Send 发送模板消息
// Send 发送模板消息
func (tpl *Template) Send(msg *TemplateMessage) (msgID int64, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
@@ -59,7 +61,8 @@ func (tpl *Template) Send(msg *TemplateMessage) (msgID int64, err error) {
return
}
uri := fmt.Sprintf("%s?access_token=%s", templateSendURL, accessToken)
response, err := util.PostJSON(uri, msg)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
@@ -92,7 +95,7 @@ type resTemplateList struct {
TemplateList []*TemplateItem `json:"template_list"`
}
//List 获取模板列表
// List 获取模板列表
func (tpl *Template) List() (templateList []*TemplateItem, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
@@ -113,3 +116,55 @@ func (tpl *Template) List() (templateList []*TemplateItem, err error) {
templateList = res.TemplateList
return
}
type resTemplateAdd struct {
util.CommonError
TemplateID string `json:"template_id"`
}
// Add 添加模板.
func (tpl *Template) Add(shortID string) (templateID string, err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
var msg = struct {
ShortID string `json:"template_id_short"`
}{ShortID: shortID}
uri := fmt.Sprintf("%s?access_token=%s", templateAddURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
var result resTemplateAdd
err = util.DecodeWithError(response, &result, "AddTemplate")
if err != nil {
return
}
templateID = result.TemplateID
return
}
// Delete 删除私有模板.
func (tpl *Template) Delete(templateID string) (err error) {
var accessToken string
accessToken, err = tpl.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateID string `json:"template_id"`
}{TemplateID: templateID}
uri := fmt.Sprintf("%s?access_token=%s", templateDelURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "DeleteTemplate")
}

View File

@@ -1,12 +1,12 @@
package message
//Text 文本消息
// Text 文本消息
type Text struct {
CommonToken
Content CDATA `xml:"Content"`
}
//NewText 初始化文本消息
// NewText 初始化文本消息
func NewText(content string) *Text {
text := new(Text)
text.Content = CDATA(content)

View File

@@ -1,18 +1,18 @@
package message
//TransferCustomer 转发客服消息
// TransferCustomer 转发客服消息
type TransferCustomer struct {
CommonToken
TransInfo *TransInfo `xml:"TransInfo,omitempty"`
}
//TransInfo 转发到指定客服
// TransInfo 转发到指定客服
type TransInfo struct {
KfAccount string `xml:"KfAccount"`
}
//NewTransferCustomer 实例化
// NewTransferCustomer 实例化
func NewTransferCustomer(kfAccount string) *TransferCustomer {
tc := new(TransferCustomer)
if kfAccount != "" {

View File

@@ -1,6 +1,6 @@
package message
//Video 视频消息
// Video 视频消息
type Video struct {
CommonToken
@@ -11,7 +11,7 @@ type Video struct {
} `xml:"Video"`
}
//NewVideo 回复图片消息
// NewVideo 回复图片消息
func NewVideo(mediaID, title, description string) *Video {
video := new(Video)
video.Video.MediaID = mediaID

View File

@@ -1,6 +1,6 @@
package message
//Voice 语音消息
// Voice 语音消息
type Voice struct {
CommonToken
@@ -9,7 +9,7 @@ type Voice struct {
} `xml:"Voice"`
}
//NewVoice 回复语音消息
// NewVoice 回复语音消息
func NewVoice(mediaID string) *Voice {
voice := new(Voice)
voice.Voice.MediaID = mediaID

View File

@@ -15,36 +15,36 @@ const (
webAppRedirectOauthURL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
accessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
refreshAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s"
userInfoURL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN"
userInfoURL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s"
checkAccessTokenURL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s"
)
//Oauth 保存用户授权信息
// Oauth 保存用户授权信息
type Oauth struct {
*context.Context
}
//NewOauth 实例化授权信息
// NewOauth 实例化授权信息
func NewOauth(context *context.Context) *Oauth {
auth := new(Oauth)
auth.Context = context
return auth
}
//GetRedirectURL 获取跳转的url地址
// GetRedirectURL 获取跳转的url地址
func (oauth *Oauth) GetRedirectURL(redirectURI, scope, state string) (string, error) {
//url encode
// url encode
urlStr := url.QueryEscape(redirectURI)
return fmt.Sprintf(redirectOauthURL, oauth.AppID, urlStr, scope, state), nil
}
//GetWebAppRedirectURL 获取网页应用跳转的url地址
// GetWebAppRedirectURL 获取网页应用跳转的url地址
func (oauth *Oauth) GetWebAppRedirectURL(redirectURI, scope, state string) (string, error) {
urlStr := url.QueryEscape(redirectURI)
return fmt.Sprintf(webAppRedirectOauthURL, oauth.AppID, urlStr, scope, state), nil
}
//Redirect 跳转到网页授权
// Redirect 跳转到网页授权
func (oauth *Oauth) Redirect(writer http.ResponseWriter, req *http.Request, redirectURI, scope, state string) error {
location, err := oauth.GetRedirectURL(redirectURI, scope, state)
if err != nil {
@@ -88,7 +88,7 @@ func (oauth *Oauth) GetUserAccessToken(code string) (result ResAccessToken, err
return
}
//RefreshAccessToken 刷新access_token
// RefreshAccessToken 刷新access_token
func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessToken, err error) {
urlStr := fmt.Sprintf(refreshAccessTokenURL, oauth.AppID, refreshToken)
var response []byte
@@ -107,7 +107,7 @@ func (oauth *Oauth) RefreshAccessToken(refreshToken string) (result ResAccessTok
return
}
//CheckAccessToken 检验access_token是否有效
// CheckAccessToken 检验access_token是否有效
func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err error) {
urlStr := fmt.Sprintf(checkAccessTokenURL, accessToken, openID)
var response []byte
@@ -128,7 +128,7 @@ func (oauth *Oauth) CheckAccessToken(accessToken, openID string) (b bool, err er
return
}
//UserInfo 用户授权获取到用户信息
// UserInfo 用户授权获取到用户信息
type UserInfo struct {
util.CommonError
@@ -143,9 +143,12 @@ type UserInfo struct {
Unionid string `json:"unionid"`
}
//GetUserInfo 如果scope为 snsapi_userinfo 则可以通过此方法获取到用户基本信息
func (oauth *Oauth) GetUserInfo(accessToken, openID string) (result UserInfo, err error) {
urlStr := fmt.Sprintf(userInfoURL, accessToken, openID)
// GetUserInfo 如果scope为 snsapi_userinfo 则可以通过此方法获取到用户基本信息
func (oauth *Oauth) GetUserInfo(accessToken, openID, lang string) (result UserInfo, err error) {
if lang == "" {
lang = "zh_CN"
}
urlStr := fmt.Sprintf(userInfoURL, accessToken, openID, lang)
var response []byte
response, err = util.HTTPGet(urlStr)
if err != nil {

287
officialaccount/ocr/ocr.go Normal file
View File

@@ -0,0 +1,287 @@
package ocr
import (
"fmt"
"net/url"
"github.com/silenceper/wechat/v2/officialaccount/context"
"github.com/silenceper/wechat/v2/util"
)
const (
ocrIDCardURL = "https://api.weixin.qq.com/cv/ocr/idcard"
ocrBankCardURL = "https://api.weixin.qq.com/cv/ocr/bankcard"
ocrDrivingURL = "https://api.weixin.qq.com/cv/ocr/driving"
ocrDrivingLicenseURL = "https://api.weixin.qq.com/cv/ocr/drivinglicense"
ocrBizLicenseURL = "https://api.weixin.qq.com/cv/ocr/bizlicense"
ocrCommonURL = "https://api.weixin.qq.com/cv/ocr/comm"
ocrPlateNumberURL = "https://api.weixin.qq.com/cv/ocr/platenum"
)
// OCR struct
type OCR struct {
*context.Context
}
// coordinate 坐标
type coordinate struct {
X int64 `json:"x,omitempty"`
Y int64 `json:"y,omitempty"`
}
// position 位置
type position struct {
LeftTop coordinate `json:"left_top"`
RightTop coordinate `json:"right_top"`
RightBottom coordinate `json:"right_bottom"`
LeftBottom coordinate `json:"left_bottom"`
}
// imageSize 图片尺寸
type imageSize struct {
Width int64 `json:"w,omitempty"`
Height int64 `json:"h,omitempty"`
}
// ResDriving 行驶证返回结果
type ResDriving struct {
util.CommonError
PlateNumber string `json:"plate_num,omitempty"`
VehicleType string `json:"vehicle_type,omitempty"`
Owner string `json:"owner,omitempty"`
Address string `json:"addr,omitempty"`
UseCharacter string `json:"use_character,omitempty"`
Model string `json:"model,omitempty"`
Vin string `json:"vin,omitempty"`
EngineNumber string `json:"engine_num,omitempty"`
RegisterDate string `json:"register_date,omitempty"`
IssueDate string `json:"issue_date,omitempty"`
PlateNumberB string `json:"plate_num_b,omitempty"`
Record string `json:"record,omitempty"`
PassengersNumber string `json:"passengers_num,omitempty"`
TotalQuality string `json:"total_quality,omitempty"`
PrepareQuality string `json:"prepare_quality,omitempty"`
OverallSize string `json:"overall_size,omitempty"`
CardPositionFront map[string]position `json:"card_position_front,omitempty"`
CardPositionBack map[string]position `json:"card_position_back,omitempty"`
ImageSize imageSize `json:"img_size,omitempty"`
}
// ResIDCard 身份证返回结果
type ResIDCard struct {
util.CommonError
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`
Address string `json:"addr,omitempty"`
Gender string `json:"gender,omitempty"`
Nationality string `json:"nationality,omitempty"`
ValidDate string `json:"valid_date,omitempty"`
}
// ResBankCard 银行卡返回结果
type ResBankCard struct {
util.CommonError
Number string `json:"number,omitempty"`
}
// ResDrivingLicense 驾驶证返回结果
type ResDrivingLicense struct {
util.CommonError
IDNumber string `json:"id_num,omitempty"`
Name string `json:"name,omitempty"`
Sex string `json:"sex,omitempty"`
Nationality string `json:"nationality,omitempty"`
Address string `json:"address,omitempty"`
Birthday string `json:"birth_date,omitempty"`
IssueDate string `json:"issue_date,omitempty"`
CarClass string `json:"car_class,omitempty"`
ValidFrom string `json:"valid_from,omitempty"`
ValidTo string `json:"valid_to,omitempty"`
OfficialSeal string `json:"official_seal,omitempty"`
}
// ResBizLicense 营业执照返回结果
type ResBizLicense struct {
util.CommonError
RegisterNumber string `json:"reg_num,omitempty"`
Serial string `json:"serial,omitempty"`
LegalRepresentative string `json:"legal_representative,omitempty"`
EnterpriseName string `json:"enterprise_name,omitempty"`
TypeOfOrganization string `json:"type_of_organization,omitempty"`
Address string `json:"address,omitempty"`
TypeOfEnterprise string `json:"type_of_enterprise,omitempty"`
BusinessScope string `json:"business_scope,omitempty"`
RegisteredCapital string `json:"registered_capital,omitempty"`
PaidInCapital string `json:"paid_in_capital,omitempty"`
ValidPeriod string `json:"valid_period,omitempty"`
RegisterDate string `json:"registered_date,omitempty"`
CertPosition map[string]position `json:"cert_position,omitempty"`
ImageSize imageSize `json:"img_size,omitempty"`
}
// ResCommon 公共印刷品返回结果
type ResCommon struct {
util.CommonError
Items []commonItem `json:"items,omitempty"`
ImageSize imageSize `json:"img_size,omitempty"`
}
// commonItem 公共元素
type commonItem struct {
Position position `json:"pos"`
Text string `json:"text"`
}
// ResPlateNumber 车牌号返回结果
type ResPlateNumber struct {
util.CommonError
Number string `json:"number"`
}
// NewOCR 实例
func NewOCR(c *context.Context) *OCR {
ocr := new(OCR)
ocr.Context = c
return ocr
}
// IDCard 身份证OCR识别接口
func (ocr *OCR) IDCard(path string) (ResIDCard ResIDCard, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrIDCardURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResIDCard, "OCRIDCard")
return
}
// BankCard 银行卡OCR识别接口
func (ocr *OCR) BankCard(path string) (ResBankCard ResBankCard, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBankCardURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResBankCard, "OCRBankCard")
return
}
// Driving 行驶证OCR识别接口
func (ocr *OCR) Driving(path string) (ResDriving ResDriving, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResDriving, "OCRDriving")
return
}
// DrivingLicense 驾驶证OCR识别接口
func (ocr *OCR) DrivingLicense(path string) (ResDrivingLicense ResDrivingLicense, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingLicenseURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResDrivingLicense, "OCRDrivingLicense")
return
}
// BizLicense 营业执照OCR识别接口
func (ocr *OCR) BizLicense(path string) (ResBizLicense ResBizLicense, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBizLicenseURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResBizLicense, "OCRBizLicense")
return
}
// Common 通用印刷体OCR识别接口
func (ocr *OCR) Common(path string) (ResCommon ResCommon, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrCommonURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResCommon, "OCRCommon")
return
}
// PlateNumber 车牌OCR识别接口
func (ocr *OCR) PlateNumber(path string) (ResPlateNumber ResPlateNumber, err error) {
accessToken, err := ocr.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrPlateNumberURL, url.QueryEscape(path), accessToken)
response, err := util.HTTPPost(uri, "")
if err != nil {
return
}
err = util.DecodeWithError(response, &ResPlateNumber, "OCRPlateNumber")
return
}

View File

@@ -3,6 +3,8 @@ package officialaccount
import (
"net/http"
"github.com/silenceper/wechat/v2/officialaccount/ocr"
"github.com/silenceper/wechat/v2/officialaccount/datacube"
"github.com/silenceper/wechat/v2/credential"
@@ -20,12 +22,12 @@ import (
"github.com/silenceper/wechat/v2/officialaccount/user"
)
//OfficialAccount 微信公众号相关API
// OfficialAccount 微信公众号相关API
type OfficialAccount struct {
ctx *context.Context
}
//NewOfficialAccount 实例化公众号API
// NewOfficialAccount 实例化公众号API
func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
defaultAkHandle := credential.NewDefaultAccessToken(cfg.AppID, cfg.AppSecret, credential.CacheKeyOfficialAccountPrefix, cfg.Cache)
ctx := &context.Context{
@@ -35,7 +37,7 @@ func NewOfficialAccount(cfg *config.Config) *OfficialAccount {
return &OfficialAccount{ctx: ctx}
}
//SetAccessTokenHandle 自定义access_token获取方式
// SetAccessTokenHandle 自定义access_token获取方式
func (officialAccount *OfficialAccount) SetAccessTokenHandle(accessTokenHandle credential.AccessTokenHandle) {
officialAccount.ctx.AccessTokenHandle = accessTokenHandle
}
@@ -63,7 +65,7 @@ func (officialAccount *OfficialAccount) GetServer(req *http.Request, writer http
return srv
}
//GetAccessToken 获取access_token
// GetAccessToken 获取access_token
func (officialAccount *OfficialAccount) GetAccessToken() (string, error) {
return officialAccount.ctx.GetAccessToken()
}
@@ -103,13 +105,23 @@ func (officialAccount *OfficialAccount) GetDevice() *device.Device {
return device.NewDevice(officialAccount.ctx)
}
//GetBroadcast 群发消息
//TODO 待完善
// GetBroadcast 群发消息
// TODO 待完善
func (officialAccount *OfficialAccount) GetBroadcast() *broadcast.Broadcast {
return broadcast.NewBroadcast(officialAccount.ctx)
}
//GetDataCube 数据统计
// GetDataCube 数据统计
func (officialAccount *OfficialAccount) GetDataCube() *datacube.DataCube {
return datacube.NewCube(officialAccount.ctx)
}
// GetOCR OCR接口
func (officialAccount *OfficialAccount) GetOCR() *ocr.OCR {
return ocr.NewOCR(officialAccount.ctx)
}
// GetSubscribe 公众号订阅消息
func (officialAccount *OfficialAccount) GetSubscribe() *message.Subscribe {
return message.NewSubscribe(officialAccount.ctx)
}

View File

@@ -17,7 +17,7 @@ import (
"github.com/silenceper/wechat/v2/util"
)
//Server struct
// Server struct
type Server struct {
*context.Context
Writer http.ResponseWriter
@@ -27,10 +27,10 @@ type Server struct {
openID string
messageHandler func(message.MixMessage) *message.Reply
messageHandler func(*message.MixMessage) *message.Reply
RequestRawXMLMsg []byte
RequestMsg message.MixMessage
RequestMsg *message.MixMessage
ResponseRawXMLMsg []byte
ResponseMsg interface{}
@@ -40,7 +40,7 @@ type Server struct {
timestamp int64
}
//NewServer init
// NewServer init
func NewServer(context *context.Context) *Server {
srv := new(Server)
srv.Context = context
@@ -52,7 +52,7 @@ func (srv *Server) SkipValidate(skip bool) {
srv.skipValidate = skip
}
//Serve 处理微信的请求消息
// Serve 处理微信的请求消息
func (srv *Server) Serve() error {
if !srv.Validate() {
log.Error("Validate Signature Failed.")
@@ -70,13 +70,13 @@ func (srv *Server) Serve() error {
return err
}
//debug print request msg
// debug print request msg
log.Debugf("request msg =%s", string(srv.RequestRawXMLMsg))
return srv.buildResponse(response)
}
//Validate 校验请求是否合法
// Validate 校验请求是否合法
func (srv *Server) Validate() bool {
if srv.skipValidate {
return true
@@ -88,16 +88,16 @@ func (srv *Server) Validate() bool {
return signature == util.Signature(srv.Token, timestamp, nonce)
}
//HandleRequest 处理微信的请求
// HandleRequest 处理微信的请求
func (srv *Server) handleRequest() (reply *message.Reply, err error) {
//set isSafeMode
// set isSafeMode
srv.isSafeMode = false
encryptType := srv.Query("encrypt_type")
if encryptType == "aes" {
srv.isSafeMode = true
}
//set openID
// set openID
srv.openID = srv.Query("openid")
var msg interface{}
@@ -105,7 +105,7 @@ func (srv *Server) handleRequest() (reply *message.Reply, err error) {
if err != nil {
return
}
mixMessage, success := msg.(message.MixMessage)
mixMessage, success := msg.(*message.MixMessage)
if !success {
err = errors.New("消息类型转换失败")
}
@@ -114,12 +114,12 @@ func (srv *Server) handleRequest() (reply *message.Reply, err error) {
return
}
//GetOpenID return openID
// GetOpenID return openID
func (srv *Server) GetOpenID() string {
return srv.openID
}
//getMessage 解析微信返回的消息
// getMessage 解析微信返回的消息
func (srv *Server) getMessage() (interface{}, error) {
var rawXMLMsgBytes []byte
var err error
@@ -129,7 +129,7 @@ func (srv *Server) getMessage() (interface{}, error) {
return nil, fmt.Errorf("从body中解析xml失败,err=%v", err)
}
//验证消息签名
// 验证消息签名
timestamp := srv.Query("timestamp")
srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32)
if err != nil {
@@ -143,7 +143,7 @@ func (srv *Server) getMessage() (interface{}, error) {
return nil, fmt.Errorf("消息不合法,验证签名失败")
}
//解密
// 解密
srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey)
if err != nil {
return nil, fmt.Errorf("消息解密失败, err=%v", err)
@@ -160,14 +160,14 @@ func (srv *Server) getMessage() (interface{}, error) {
return srv.parseRequestMessage(rawXMLMsgBytes)
}
func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg message.MixMessage, err error) {
msg = message.MixMessage{}
err = xml.Unmarshal(rawXMLMsgBytes, &msg)
func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MixMessage, err error) {
msg = &message.MixMessage{}
err = xml.Unmarshal(rawXMLMsgBytes, msg)
return
}
//SetMessageHandler 设置用户自定义的回调方法
func (srv *Server) SetMessageHandler(handler func(message.MixMessage) *message.Reply) {
// SetMessageHandler 设置用户自定义的回调方法
func (srv *Server) SetMessageHandler(handler func(*message.MixMessage) *message.Reply) {
srv.messageHandler = handler
}
@@ -178,7 +178,7 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
}
}()
if reply == nil {
//do nothing
// do nothing
return nil
}
msgType := reply.MsgType
@@ -197,7 +197,7 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
msgData := reply.MsgData
value := reflect.ValueOf(msgData)
//msgData must be a ptr
// msgData must be a ptr
kind := value.Kind().String()
if kind != "ptr" {
return message.ErrUnsupportReply
@@ -221,18 +221,18 @@ func (srv *Server) buildResponse(reply *message.Reply) (err error) {
return
}
//Send 将自定义的消息发送
// Send 将自定义的消息发送
func (srv *Server) Send() (err error) {
replyMsg := srv.ResponseMsg
log.Debugf("response msg =%+v", replyMsg)
if srv.isSafeMode {
//安全模式下对消息进行加密
// 安全模式下对消息进行加密
var encryptedMsg []byte
encryptedMsg, err = util.EncryptMsg(srv.random, srv.ResponseRawXMLMsg, srv.AppID, srv.EncodingAESKey)
if err != nil {
return
}
//TODO 如果获取不到timestamp nonce 则自己生成
// TODO 如果获取不到timestamp nonce 则自己生成
timestamp := srv.timestamp
timestampStr := strconv.FormatInt(timestamp, 10)
msgSignature := util.Signature(srv.Token, timestampStr, srv.nonce, string(encryptedMsg))

View File

@@ -15,10 +15,10 @@ func writeContextType(w http.ResponseWriter, value []string) {
}
}
//Render render from bytes
// Render render from bytes
func (srv *Server) Render(bytes []byte) {
//debug
//fmt.Println("response msg = ", string(bytes))
// debug
// fmt.Println("response msg = ", string(bytes))
srv.Writer.WriteHeader(200)
_, err := srv.Writer.Write(bytes)
if err != nil {
@@ -26,13 +26,13 @@ func (srv *Server) Render(bytes []byte) {
}
}
//String render from string
// String render from string
func (srv *Server) String(str string) {
writeContextType(srv.Writer, plainContentType)
srv.Render([]byte(str))
}
//XML render to xml
// XML render to xml
func (srv *Server) XML(obj interface{}) {
writeContextType(srv.Writer, xmlContentType)
bytes, err := xml.Marshal(obj)

View File

@@ -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 为老账号的openIDopenIDs限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
}

View File

@@ -18,7 +18,7 @@ const (
tagUserTidListURL = "https://api.weixin.qq.com/cgi-bin/tags/getidlist?access_token=%s"
)
//TagInfo 标签信息
// TagInfo 标签信息
type TagInfo struct {
ID int32 `json:"id"`
Name string `json:"name"`
@@ -34,7 +34,7 @@ type TagOpenIDList struct {
NextOpenID string `json:"next_openid"`
}
//CreateTag 创建标签
// CreateTag 创建标签
func (user *User) CreateTag(tagName string) (tagInfo *TagInfo, err error) {
var accessToken string
accessToken, err = user.GetAccessToken()
@@ -68,7 +68,7 @@ func (user *User) CreateTag(tagName string) (tagInfo *TagInfo, err error) {
return result.Tag, nil
}
//DeleteTag 删除标签
// DeleteTag 删除标签
func (user *User) DeleteTag(tagID int32) (err error) {
accessToken, err := user.GetAccessToken()
if err != nil {
@@ -88,7 +88,7 @@ func (user *User) DeleteTag(tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "DeleteTag")
}
//UpdateTag 编辑标签
// UpdateTag 编辑标签
func (user *User) UpdateTag(tagID int32, tagName string) (err error) {
accessToken, err := user.GetAccessToken()
if err != nil {
@@ -110,7 +110,7 @@ func (user *User) UpdateTag(tagID int32, tagName string) (err error) {
return util.DecodeWithCommonError(resp, "UpdateTag")
}
//GetTag 获取公众号已创建的标签
// GetTag 获取公众号已创建的标签
func (user *User) GetTag() (tags []*TagInfo, err error) {
accessToken, err := user.GetAccessToken()
if err != nil {
@@ -132,7 +132,7 @@ func (user *User) GetTag() (tags []*TagInfo, err error) {
return result.Tags, nil
}
//OpenIDListByTag 获取标签下粉丝列表
// OpenIDListByTag 获取标签下粉丝列表
func (user *User) OpenIDListByTag(tagID int32, nextOpenID ...string) (userList *TagOpenIDList, err error) {
accessToken, err := user.GetAccessToken()
if err != nil {
@@ -160,7 +160,7 @@ func (user *User) OpenIDListByTag(tagID int32, nextOpenID ...string) (userList *
return
}
//BatchTag 批量为用户打标签
// BatchTag 批量为用户打标签
func (user *User) BatchTag(openIDList []string, tagID int32) (err error) {
accessToken, err := user.GetAccessToken()
if err != nil {
@@ -184,7 +184,7 @@ func (user *User) BatchTag(openIDList []string, tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "BatchTag")
}
//BatchUntag 批量为用户取消标签
// BatchUntag 批量为用户取消标签
func (user *User) BatchUntag(openIDList []string, tagID int32) (err error) {
if len(openIDList) == 0 {
return
@@ -208,7 +208,7 @@ func (user *User) BatchUntag(openIDList []string, tagID int32) (err error) {
return util.DecodeWithCommonError(resp, "BatchUntag")
}
//UserTidList 获取用户身上的标签列表
// UserTidList 获取用户身上的标签列表
func (user *User) UserTidList(openID string) (tagIDList []int32, err error) {
accessToken, err := user.GetAccessToken()
if err != nil {

View File

@@ -15,19 +15,19 @@ const (
userListURL = "https://api.weixin.qq.com/cgi-bin/user/get"
)
//User 用户管理
// User 用户管理
type User struct {
*context.Context
}
//NewUser 实例化
// NewUser 实例化
func NewUser(context *context.Context) *User {
user := new(User)
user.Context = context
return user
}
//Info 用户基本信息
// Info 用户基本信息
type Info struct {
util.CommonError
@@ -62,7 +62,7 @@ type OpenidList struct {
NextOpenID string `json:"next_openid"`
}
//GetUserInfo 获取用户基本信息
// GetUserInfo 获取用户基本信息
func (user *User) GetUserInfo(openID string) (userInfo *Info, err error) {
var accessToken string
accessToken, err = user.GetAccessToken()
@@ -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)

View File

@@ -22,7 +22,7 @@ openPlatform := wc.GetOpenPlatform(cfg)
// 传入request和responseWriter
server := openPlatform.GetServer(req, rw)
//设置接收消息的处理方法
server.SetMessageHandler(func(msg message.MixMessage) *message.Reply {
server.SetMessageHandler(func(msg *message.MixMessage) *message.Reply {
if msg.InfoType == message.InfoTypeVerifyTicket {
componentVerifyTicket, err := openPlatform.SetComponentAccessToken(msg.ComponentVerifyTicket)
if err != nil {

View File

@@ -2,33 +2,33 @@ package account
import "github.com/silenceper/wechat/v2/openplatform/context"
//Account 开放平台张哈管理
//TODO 实现方法
// Account 开放平台张哈管理
// TODO 实现方法
type Account struct {
*context.Context
}
//NewAccount new
// NewAccount new
func NewAccount(ctx *context.Context) *Account {
return &Account{ctx}
}
//Create 创建开放平台帐号并绑定公众号/小程序
// Create 创建开放平台帐号并绑定公众号/小程序
func (account *Account) Create(appID string) (string, error) {
return "", nil
}
//Bind 将公众号/小程序绑定到开放平台帐号下
// Bind 将公众号/小程序绑定到开放平台帐号下
func (account *Account) Bind(appID string) error {
return nil
}
//Unbind 将公众号/小程序从开放平台帐号下解绑
// Unbind 将公众号/小程序从开放平台帐号下解绑
func (account *Account) Unbind(appID string, openAppID string) error {
return nil
}
//Get 获取公众号/小程序所绑定的开放平台帐号
// Get 获取公众号/小程序所绑定的开放平台帐号
func (account *Account) Get(appID string) (string, error) {
return "", nil
}

View File

@@ -4,11 +4,11 @@ import (
"github.com/silenceper/wechat/v2/cache"
)
//Config config for 微信开放平台
// Config .config for 微信开放平台
type Config struct {
AppID string `json:"app_id"` //appid
AppSecret string `json:"app_secret"` //appsecret
Token string `json:"token"` //token
EncodingAESKey string `json:"encoding_aes_key"` //EncodingAESKey
AppID string `json:"app_id"` // appid
AppSecret string `json:"app_secret"` // appsecret
Token string `json:"token"` // token
EncodingAESKey string `json:"encoding_aes_key"` // EncodingAESKey
Cache cache.Cache
}

View File

@@ -1,4 +1,4 @@
//Package context 开放平台相关context
// Package context 开放平台相关context
package context
import (
@@ -18,10 +18,10 @@ const (
getComponentInfoURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=%s"
componentLoginURL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=%d&biz_appid=%s"
bindComponentURL = "https://mp.weixin.qq.com/safe/bindcomponent?action=bindcomponent&auth_type=%d&no_scan=1&component_appid=%s&pre_auth_code=%s&redirect_uri=%s&biz_appid=%s#wechat_redirect"
//TODO 获取授权方选项信息
//getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
//TODO 获取已授权的账号信息
//getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
// TODO 获取授权方选项信息
// getComponentConfigURL = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=%s"
// TODO 获取已授权的账号信息
// getuthorizerListURL = "POST https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=%s"
)
// ComponentAccessToken 第三方平台

View File

@@ -11,23 +11,23 @@ const (
getAccountBasicInfoURL = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo"
)
//Basic 基础信息设置
// Basic 基础信息设置
type Basic struct {
*openContext.Context
appID string
}
//NewBasic new
// NewBasic new
func NewBasic(opContext *openContext.Context, appID string) *Basic {
return &Basic{Context: opContext, appID: appID}
}
//AccountBasicInfo 基础信息
// AccountBasicInfo 基础信息
type AccountBasicInfo struct {
util.CommonError
}
//GetAccountBasicInfo 获取小程序基础信息
// GetAccountBasicInfo 获取小程序基础信息
//reference:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Mini_Program_Information_Settings.html
func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) {
ak, err := basic.GetAuthrAccessToken(basic.AppID)
@@ -46,7 +46,7 @@ func (basic *Basic) GetAccountBasicInfo() (*AccountBasicInfo, error) {
return result, nil
}
//modify_domain设置服务器域名
//TODO
//func (encryptor *Basic) modifyDomain() {
//}
// modify_domain设置服务器域名
// TODO
// func (encryptor *Basic) modifyDomain() {
// }

View File

@@ -11,28 +11,28 @@ const (
fastregisterweappURL = "https://api.weixin.qq.com/cgi-bin/component/fastregisterweapp"
)
//Component 快速创建小程序
// Component 快速创建小程序
type Component struct {
*openContext.Context
}
//NewComponent new
// NewComponent new
func NewComponent(opContext *openContext.Context) *Component {
return &Component{opContext}
}
//RegisterMiniProgramParam 快速注册小程序参数
// RegisterMiniProgramParam 快速注册小程序参数
type RegisterMiniProgramParam struct {
Name string `json:"name"` //企业名
Code string `json:"code"` //企业代码
CodeType string `json:"code_type"` //企业代码类型 1统一社会信用代码18 位) 2组织机构代码9 位 xxxxxxxx-x 3营业执照注册号(15 位)
LegalPersonaWechat string `json:"legal_persona_wechat"` //法人微信号
LegalPersonaName string `json:"legal_persona_name"` //法人姓名(绑定银行卡)
ComponentPhone string `json:"component_phone"` //第三方联系电话(方便法人与第三方联系)
Name string `json:"name"` // 企业名
Code string `json:"code"` // 企业代码
CodeType string `json:"code_type"` // 企业代码类型 1统一社会信用代码18 位) 2组织机构代码9 位 xxxxxxxx-x 3营业执照注册号(15 位)
LegalPersonaWechat string `json:"legal_persona_wechat"` // 法人微信号
LegalPersonaName string `json:"legal_persona_name"` // 法人姓名(绑定银行卡)
ComponentPhone string `json:"component_phone"` // 第三方联系电话(方便法人与第三方联系)
}
//RegisterMiniProgram 快速创建小程
//reference: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Fast_Registration_Interface_document.html
// RegisterMiniProgram 快速创建小程
// reference: https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Mini_Programs/Fast_Registration_Interface_document.html
func (component *Component) RegisterMiniProgram(param *RegisterMiniProgramParam) error {
componentAK, err := component.GetComponentAccessToken()
if err != nil {
@@ -46,15 +46,15 @@ func (component *Component) RegisterMiniProgram(param *RegisterMiniProgramParam)
return util.DecodeWithCommonError(data, "component/fastregisterweapp?action=create")
}
//GetRegistrationStatusParam 查询任务创建状态
// GetRegistrationStatusParam 查询任务创建状态
type GetRegistrationStatusParam struct {
Name string `json:"name"` //企业名
LegalPersonaWechat string `json:"legal_persona_wechat"` //法人微信号
LegalPersonaName string `json:"legal_persona_name"` //法人姓名(绑定银行卡)
Name string `json:"name"` // 企业名
LegalPersonaWechat string `json:"legal_persona_wechat"` // 法人微信号
LegalPersonaName string `json:"legal_persona_name"` // 法人姓名(绑定银行卡)
}
//GetRegistrationStatus 查询创建任务状态.
// GetRegistrationStatus 查询创建任务状态.
func (component *Component) GetRegistrationStatus(param *GetRegistrationStatusParam) error {
componentAK, err := component.GetComponentAccessToken()
if err != nil {

View File

@@ -6,13 +6,13 @@ import (
"github.com/silenceper/wechat/v2/openplatform/miniprogram/component"
)
//MiniProgram 代小程序实现业务
// MiniProgram 代小程序实现业务
type MiniProgram struct {
AppID string
openContext *openContext.Context
}
//NewMiniProgram 实例化
// NewMiniProgram 实例化
func NewMiniProgram(opCtx *openContext.Context, appID string) *MiniProgram {
return &MiniProgram{
openContext: opCtx,
@@ -20,13 +20,13 @@ func NewMiniProgram(opCtx *openContext.Context, appID string) *MiniProgram {
}
}
//GetComponent get component
//快速注册小程序相关
// GetComponent get component
// 快速注册小程序相关
func (miniProgram *MiniProgram) GetComponent() *component.Component {
return component.NewComponent(miniProgram.openContext)
}
//GetBasic 基础信息设置
// GetBasic 基础信息设置
func (miniProgram *MiniProgram) GetBasic() *basic.Basic {
return basic.NewBasic(miniProgram.openContext, miniProgram.AppID)
}

View File

@@ -15,7 +15,7 @@ type Js struct {
credential.JsTicketHandle
}
//NewJs init
// NewJs init
func NewJs(context *context.Context, appID string) *Js {
js := new(Js)
js.Context = context
@@ -24,13 +24,13 @@ func NewJs(context *context.Context, appID string) *Js {
return js
}
//SetJsTicketHandle 自定义js ticket取值方式
// SetJsTicketHandle 自定义js ticket取值方式
func (js *Js) SetJsTicketHandle(ticketHandle credential.JsTicketHandle) {
js.JsTicketHandle = ticketHandle
}
//GetConfig 第三方平台 - 获取jssdk需要的配置参数
//uri 为当前网页地址
// GetConfig 第三方平台 - 获取jssdk需要的配置参数
// uri 为当前网页地址
func (js *Js) GetConfig(uri, appid string) (config *officialJs.Config, err error) {
config = new(officialJs.Config)
var accessToken string

View File

@@ -28,14 +28,14 @@ func NewOauth(context *context.Context) *Oauth {
return auth
}
//GetRedirectURL 第三方平台 - 获取跳转的url地址
// GetRedirectURL 第三方平台 - 获取跳转的url地址
func (oauth *Oauth) GetRedirectURL(redirectURI, scope, state, appID string) (string, error) {
//url encode
// url encode
urlStr := url.QueryEscape(redirectURI)
return fmt.Sprintf(platformRedirectOauthURL, appID, urlStr, scope, state, oauth.AppID), nil
}
//Redirect 第三方平台 - 跳转到网页授权
// Redirect 第三方平台 - 跳转到网页授权
func (oauth *Oauth) Redirect(writer http.ResponseWriter, req *http.Request, redirectURI, scope, state, appID string) error {
location, err := oauth.GetRedirectURL(redirectURI, scope, state, appID)
if err != nil {

View File

@@ -9,15 +9,15 @@ import (
"github.com/silenceper/wechat/v2/openplatform/officialaccount/oauth"
)
//OfficialAccount 代公众号实现业务
// OfficialAccount 代公众号实现业务
type OfficialAccount struct {
//授权的公众号的appID
// 授权的公众号的appID
appID string
*officialaccount.OfficialAccount
}
//NewOfficialAccount 实例化
//appID :为授权方公众号 APPID非开放平台第三方平台 APPID
// NewOfficialAccount 实例化
// appID :为授权方公众号 APPID非开放平台第三方平台 APPID
func NewOfficialAccount(opCtx *opContext.Context, appID string) *OfficialAccount {
officialAccount := officialaccount.NewOfficialAccount(&offConfig.Config{
AppID: opCtx.AppID,
@@ -25,7 +25,7 @@ func NewOfficialAccount(opCtx *opContext.Context, appID string) *OfficialAccount
Token: opCtx.Token,
Cache: opCtx.Cache,
})
//设置获取access_token的函数
// 设置获取access_token的函数
officialAccount.SetAccessTokenHandle(NewDefaultAuthrAccessToken(opCtx, appID))
return &OfficialAccount{appID: appID, OfficialAccount: officialAccount}
}
@@ -40,13 +40,13 @@ func (officialAccount *OfficialAccount) PlatformJs() *js.Js {
return js.NewJs(officialAccount.GetContext(), officialAccount.appID)
}
//DefaultAuthrAccessToken 默认获取授权ak的方法
// DefaultAuthrAccessToken 默认获取授权ak的方法
type DefaultAuthrAccessToken struct {
opCtx *opContext.Context
appID string
}
//NewDefaultAuthrAccessToken New
// NewDefaultAuthrAccessToken New
func NewDefaultAuthrAccessToken(opCtx *opContext.Context, appID string) credential.AccessTokenHandle {
return &DefaultAuthrAccessToken{
opCtx: opCtx,
@@ -54,7 +54,7 @@ func NewDefaultAuthrAccessToken(opCtx *opContext.Context, appID string) credenti
}
}
//GetAccessToken 获取ak
// GetAccessToken 获取ak
func (ak *DefaultAuthrAccessToken) GetAccessToken() (string, error) {
return ak.opCtx.GetAuthrAccessToken(ak.appID)
}

View File

@@ -11,12 +11,12 @@ import (
"github.com/silenceper/wechat/v2/openplatform/officialaccount"
)
//OpenPlatform 微信开放平台相关api
// OpenPlatform 微信开放平台相关api
type OpenPlatform struct {
*context.Context
}
//NewOpenPlatform new openplatform
// NewOpenPlatform new openplatform
func NewOpenPlatform(cfg *config.Config) *OpenPlatform {
if cfg.Cache == nil {
panic("cache 未设置")
@@ -27,24 +27,24 @@ func NewOpenPlatform(cfg *config.Config) *OpenPlatform {
return &OpenPlatform{ctx}
}
//GetServer get server
// GetServer get server
func (openPlatform *OpenPlatform) GetServer(req *http.Request, writer http.ResponseWriter) *server.Server {
off := officialaccount.NewOfficialAccount(openPlatform.Context, "")
return off.GetServer(req, writer)
}
//GetOfficialAccount 公众号代处理
// GetOfficialAccount 公众号代处理
func (openPlatform *OpenPlatform) GetOfficialAccount(appID string) *officialaccount.OfficialAccount {
return officialaccount.NewOfficialAccount(openPlatform.Context, appID)
}
//GetMiniProgram 小程序代理
// GetMiniProgram 小程序代理
func (openPlatform *OpenPlatform) GetMiniProgram(appID string) *miniprogram.MiniProgram {
return miniprogram.NewMiniProgram(openPlatform.Context, appID)
}
//GetAccountManager 账号管理
//TODO
// GetAccountManager 账号管理
// TODO
func (openPlatform *OpenPlatform) GetAccountManager() *account.Account {
return account.NewAccount(openPlatform.Context)
}

View File

@@ -1,6 +1,6 @@
package config
//Config config for pay
// Config .config for pay
type Config struct {
AppID string `json:"app_id"`
MchID string `json:"mch_id"`

View File

@@ -4,12 +4,12 @@ import (
"github.com/silenceper/wechat/v2/pay/config"
)
//Notify 回调
// Notify 回调
type Notify struct {
*config.Config
}
//NewNotify new
// NewNotify new
func NewNotify(cfg *config.Config) *Notify {
return &Notify{cfg}
}

Some files were not shown because too many files have changed in this diff Show More