├── .gitignore
├── resource
├── tree.png
├── cloud.jpg
├── qrcode.png
├── cha-cha-ender.mp3
├── 3d_ocean_1590675653.mp4
└── README.md
├── .editorconfig
├── config
├── model_test.yaml
├── constant.go
├── model_test.go
└── model.go
├── controllers
├── payment
│ ├── redpack
│ │ ├── work.go
│ │ └── redpack.go
│ ├── apply4Sub
│ │ └── apply4Sub.go
│ ├── merchantService
│ │ └── merchantService.go
│ ├── bill.go
│ ├── tax
│ │ └── tax.go
│ ├── paymentScore
│ │ └── paymentScore.go
│ ├── merchant
│ │ └── merchant.go
│ ├── security.go
│ ├── transfer
│ │ ├── batch.go
│ │ └── transfer.go
│ ├── profitSharing.go
│ ├── refund
│ │ └── refund.go
│ └── partner
│ │ └── payment.go
├── wecom
│ ├── jssdk.go
│ ├── base.go
│ ├── miniprogram.go
│ ├── oa
│ │ ├── dial.go
│ │ ├── pstncc.go
│ │ ├── approval.go
│ │ ├── journal.go
│ │ ├── calendar.go
│ │ ├── meeting.go
│ │ ├── webdrive.go
│ │ ├── schedule.go
│ │ ├── living.go
│ │ ├── meetingroom.go
│ │ └── checkin.go
│ ├── user
│ │ ├── validate
│ │ │ └── user-create.go
│ │ ├── message.go
│ │ ├── linked-corp.go
│ │ ├── user-callback.go
│ │ ├── tag.go
│ │ └── department.go
│ ├── account-service
│ │ ├── service-state.go
│ │ ├── servicer.go
│ │ ├── message.go
│ │ ├── account-service.go
│ │ └── customer.go
│ ├── corpgroup.go
│ ├── external-contact
│ │ ├── statistic.go
│ │ ├── group-chat.go
│ │ └── transfer.go
│ ├── msg-audit.go
│ ├── message
│ │ └── message-callback.go
│ ├── invoice.go
│ ├── media.go
│ └── oauth-controller.go
├── official-account
│ ├── reply.go
│ ├── card.go
│ ├── short-url.go
│ ├── qrcode.go
│ ├── base.go
│ ├── oauth.go
│ ├── jssdk.go
│ ├── uniform-message.go
│ ├── user-tag.go
│ ├── user.go
│ ├── server.go
│ ├── comment.go
│ └── menu.go
├── miniprogram
│ ├── internet.go
│ ├── short-link.go
│ ├── virtual-pay.go
│ ├── url-link.go
│ ├── phone-number.go
│ ├── soter.go
│ ├── risk-control.go
│ ├── url-scheme.go
│ ├── service-market.go
│ ├── updatable-message.go
│ ├── server.go
│ ├── search.go
│ ├── wxacode.go
│ ├── uniform-message.go
│ ├── plugin-manager.go
│ ├── security.go
│ ├── near-by-poi.go
│ ├── auth.go
│ ├── img.go
│ ├── industry.go
│ └── subscribe-message.go
└── open-platform
│ ├── server.go
│ └── open-platform.go
├── docs
├── md
│ ├── official-account.getCallbackIp.md
│ └── official-account.clearQuota.md
├── swagger.yaml
├── swagger.json
└── docs.go
├── templates
├── openplatform-auth.html
└── h5-pay.html
├── services
├── get-env.go
├── open-platform.go
├── offiaccount-service.go
├── miniprogram-service.go
├── payment-service.go
└── wecom-service.go
├── routes
├── router.go
├── open-platform.go
└── payment.go
├── main.go
├── README.md
├── go.mod
└── config-example.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | config.yml
4 | certs/
5 |
6 | *.log
--------------------------------------------------------------------------------
/resource/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArtisanCloud/PowerWechatTutorial/HEAD/resource/tree.png
--------------------------------------------------------------------------------
/resource/cloud.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArtisanCloud/PowerWechatTutorial/HEAD/resource/cloud.jpg
--------------------------------------------------------------------------------
/resource/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArtisanCloud/PowerWechatTutorial/HEAD/resource/qrcode.png
--------------------------------------------------------------------------------
/resource/cha-cha-ender.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArtisanCloud/PowerWechatTutorial/HEAD/resource/cha-cha-ender.mp3
--------------------------------------------------------------------------------
/resource/3d_ocean_1590675653.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ArtisanCloud/PowerWechatTutorial/HEAD/resource/3d_ocean_1590675653.mp4
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_size = 2
5 | indent_style = space
6 |
7 | [*.go]
8 | indent_style = tab
9 | indent_size = 2
10 |
--------------------------------------------------------------------------------
/resource/README.md:
--------------------------------------------------------------------------------
1 |
2 | cha-cha-ender.mp3 [来源地址](https://freepd.com/comedy.php)
3 |
4 | 3d_ocean_1590675653.mp4 [来源地址](https://www.videvo.net/video/flying-over-bright-blue-open-ocean/514903/)
--------------------------------------------------------------------------------
/config/model_test.yaml:
--------------------------------------------------------------------------------
1 | appid: hello-app
2 | key: test-key
3 | certpath: certs/apiclient_cert.pem
4 | keypath: certs/apiclient_key.pem
5 | serialno: 55D06F99FF64CF1759F1E5B77A0BEC8B67A78C2E
6 | mchapiv3key: fjaiofadfafafoafjia
7 | notifyurl: https://powerwechat.artisan-cloud.com
8 |
--------------------------------------------------------------------------------
/config/constant.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | const API_RETURN_CODE_INIT = 200
4 | const API_RETURN_CODE_WARNING = 300
5 | const API_RETURN_CODE_ERROR = 400
6 | const API_RETURN_CODE_NOT_FOUND = 404
7 |
8 | //---------------------------------------------
9 |
10 | const API_RESULT_CODE_INIT = 0
11 |
12 |
13 | const API_ERR_CODE_REQUEST_PARAM_ERROR = 401032
--------------------------------------------------------------------------------
/controllers/payment/redpack/work.go:
--------------------------------------------------------------------------------
1 | package redpack
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | func APIWorkSendWXRedpack(c *gin.Context) {
8 |
9 | //payConf, err := services.PaymentApp.RedPack.SendWorkWX(response.PrepayID, true)
10 | //if err != nil {
11 | // panic(err)
12 | //}
13 | //
14 | //c.JSON(200, payConf)
15 | }
16 |
--------------------------------------------------------------------------------
/controllers/wecom/jssdk.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func APITicketGet(c *gin.Context) {
10 |
11 | res, err := services.WeComApp.JSSDK.GetTicket(c.Request.Context())
12 |
13 | if err != nil {
14 | panic(err)
15 | }
16 |
17 | c.JSON(http.StatusOK, res)
18 | }
19 |
--------------------------------------------------------------------------------
/controllers/wecom/base.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func GetCallbackIP(ctx *gin.Context) {
10 | data, err := services.WeComApp.Base.GetCallbackIP(ctx.Request.Context())
11 | if err != nil {
12 | ctx.String(http.StatusBadRequest, err.Error())
13 | return
14 | }
15 | ctx.JSON(http.StatusOK, data)
16 | }
17 |
--------------------------------------------------------------------------------
/controllers/official-account/reply.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // AutoReply 获取当前设置的回复规则
10 | func AutoReplyCurrent(ctx *gin.Context) {
11 | data, err := services.OfficialAccountApp.AutoReply.Current(ctx.Request.Context())
12 | if err != nil {
13 | ctx.String(http.StatusBadRequest, err.Error())
14 | }
15 | ctx.JSON(http.StatusOK, data)
16 | }
17 |
--------------------------------------------------------------------------------
/docs/md/official-account.getCallbackIp.md:
--------------------------------------------------------------------------------
1 |
2 | SDK产品接口的代码展示:
3 | ```
4 | 返回类型定义如下:
5 | type ResponseGetCallBackIP struct {
6 | response.ResponseOfficialAccount
7 |
8 | IPList []string `json:"ip_list"`
9 | }
10 |
11 | 具体使用接口方式:
12 | func GetCallbackIP(ctx *gin.Context) {
13 | data, err := services.OfficialAccountApp.Base.GetCallbackIP(ctx.Request.Context())
14 | if err != nil {
15 | ctx.String(http.StatusBadRequest, err.Error())
16 | return
17 | }
18 | ctx.JSON(http.StatusOK, data)
19 | }
20 | ```
--------------------------------------------------------------------------------
/controllers/official-account/card.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func APIUpdate(ctx *gin.Context) {
10 | data, err := services.OfficialAccountApp.Card.Update(ctx.Request.Context(),
11 | "ph_gmt7cUVrlRk8swPwx7aDyF-pg",
12 | "member_card",
13 | nil,
14 | )
15 | if err != nil {
16 | ctx.String(http.StatusBadRequest, err.Error())
17 | return
18 | }
19 | ctx.JSON(http.StatusOK, data)
20 | }
21 |
--------------------------------------------------------------------------------
/controllers/wecom/miniprogram.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func Code2Session(c *gin.Context) {
10 | code := c.DefaultQuery("code", "")
11 |
12 | miniProgramApp, err := services.WeComApp.MiniProgram()
13 | if err != nil {
14 | panic(err)
15 | }
16 | res, err := miniProgramApp.Auth.Session(c.Request.Context(), code)
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | c.JSON(http.StatusOK, res)
22 | }
23 |
--------------------------------------------------------------------------------
/controllers/miniprogram/internet.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func APIInternetGetUserEncryptKey(c *gin.Context) {
10 |
11 | openID, exist := c.GetQuery("openID")
12 | if !exist {
13 | panic("parameter open id expected")
14 | }
15 |
16 | rs, err := services.MiniProgramApp.Internet.GetUserEncryptKey(c.Request.Context(), openID, "", "hmac_sha256")
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, rs)
23 | }
24 |
--------------------------------------------------------------------------------
/config/model_test.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 | "github.com/go-playground/assert/v2"
6 | "github.com/jinzhu/configor"
7 | "log"
8 | "os"
9 | "testing"
10 | )
11 |
12 | func TestGet(t *testing.T) {
13 | //os.Setenv("app_id", "wx_xxxxx")
14 | _ = os.Setenv("mch_id", "mch_xxx")
15 | var c Payment
16 | err := configor.Load(&c, "./model_test.yaml")
17 | if err != nil {
18 | log.Println(c)
19 | panic(err)
20 | }
21 | fmt.Printf("config: %#v\n", c)
22 | assert.Equal(t, c.AppID, "hello-app")
23 | assert.Equal(t, c.MchID, "mch_xxx")
24 | }
--------------------------------------------------------------------------------
/docs/md/official-account.clearQuota.md:
--------------------------------------------------------------------------------
1 |
2 | SDK产品接口的代码展示:
3 | ```
4 | 返回类型定义如下:
5 | type ResponseOfficialAccount struct {
6 | ResponseBase
7 |
8 | ErrCode int `json:"errcode,omitempty"`
9 | ErrMsg string `json:"errmsg,omitempty"`
10 |
11 | ResultCode string `json:"resultcode,omitempty"`
12 | ResultMsg string `json:"resultmsg,omitempty"`
13 | }
14 |
15 | 具体使用接口方式:
16 | func ClearQuota(ctx *gin.Context) {
17 | data, err := services.OfficialAccountApp.Base.ClearQuota(ctx.Request.Context())
18 | if err != nil {
19 | ctx.String(http.StatusBadRequest, err.Error())
20 | return
21 | }
22 | ctx.JSON(http.StatusOK, data)
23 | }
24 | ```
--------------------------------------------------------------------------------
/controllers/payment/apply4Sub/apply4Sub.go:
--------------------------------------------------------------------------------
1 | package apply4Sub
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/apply4Sub/request"
5 | "github.com/gin-gonic/gin"
6 | "log"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APIApplyFor(c *gin.Context) {
12 | //traceNo := c.Query("traceNo")
13 | //log.Printf("traceNo: %s", traceNo)
14 |
15 | para := &request.RequestApplyForBusiness{}
16 |
17 | rs, err := services.PaymentApp.Apply4Sub.ApplyForBusiness(c.Request.Context(), para)
18 | if err != nil {
19 | log.Println("出错了: ", err)
20 | c.String(400, err.Error())
21 | return
22 | }
23 | c.JSON(http.StatusOK, rs)
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/controllers/miniprogram/short-link.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // 获取小程序 Short Link
10 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/short-link/shortlink.generate.html
11 | func APIShortLinkGenerate(c *gin.Context) {
12 |
13 | pageUrl := c.DefaultQuery("pageUrl", "/pages/index/index?query1=q1")
14 | pageTitle := "Homework title"
15 | isPermanent := false
16 |
17 | rs, err := services.MiniProgramApp.ShortLink.Generate(c.Request.Context(), pageUrl, pageTitle, isPermanent)
18 |
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | c.JSON(http.StatusOK, rs)
24 | }
25 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/dial.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/dial/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 获取公费电话拨打记录
11 | // https://work.weixin.qq.com/api/doc/90000/90135/93662
12 | func APIDialGetDialRecord(c *gin.Context) {
13 |
14 | options := &request.RequestDialGetDialRecord{
15 | MeetingID: 1536508800,
16 | Title: 1536940800,
17 | MeetingStart: 0,
18 | MeetingDuration: 100,
19 | }
20 | res, err := services.WeComApp.OADial.GetDialRecord(c.Request.Context(), options)
21 |
22 | if err != nil {
23 | panic(err)
24 | }
25 |
26 | c.JSON(http.StatusOK, res)
27 | }
28 |
--------------------------------------------------------------------------------
/controllers/payment/merchantService/merchantService.go:
--------------------------------------------------------------------------------
1 | package merchantService
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/merchantService/request"
5 | "github.com/gin-gonic/gin"
6 | "log"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APIComplaints(c *gin.Context) {
12 |
13 | para := &request.RequestComplaints{
14 | Limit: 1,
15 | Offset: 10,
16 | BeginDate: "2024-01-01",
17 | EndDate: "2024-04-01",
18 | ComplaintedMchId: "1616273230",
19 | }
20 |
21 | rs, err := services.PaymentApp.MerchantService.Complaints(c.Request.Context(), para)
22 | if err != nil {
23 | log.Println("出错了: ", err)
24 | c.String(400, err.Error())
25 | return
26 | }
27 | c.JSON(http.StatusOK, rs)
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/controllers/miniprogram/virtual-pay.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/virtualPayment/request"
5 | "github.com/gin-gonic/gin"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func APIStartUploadGoods(ctx *gin.Context) {
10 |
11 | params := &request.UploadProductsRequest{
12 | Env: 0,
13 | UploadItem: []*request.GoodItem{
14 | {
15 | Id: "18",
16 | Name: "1000K币",
17 | Price: 1000,
18 | Remake: "1000K币",
19 | ItemUrl: "https://qiniu.rongjuwh.cn/applet_kb_20230801101836.png",
20 | },
21 | },
22 | }
23 |
24 | rs, err := services.MiniProgramApp.VirtualPayment.StartUploadGoods(ctx.Request.Context(), params)
25 |
26 | if err != nil {
27 | panic(err)
28 | }
29 |
30 | ctx.JSON(200, rs)
31 | }
32 |
--------------------------------------------------------------------------------
/templates/openplatform-auth.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | auth
6 |
7 |
8 | 授权微信小程序
9 | 授权微信公众号
10 |
11 |
12 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/pstncc.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func APIPSTNCCCall(c *gin.Context) {
10 | options := []string{
11 | c.DefaultQuery("userID", "matrix-x"),
12 | }
13 |
14 | res, err := services.WeComApp.OAPSTNCC.Call(c.Request.Context(), options)
15 |
16 | if err != nil {
17 | panic(err)
18 | }
19 |
20 | c.JSON(http.StatusOK, res)
21 | }
22 |
23 | func APIPSTNCCGetStates(c *gin.Context) {
24 |
25 | calleeUserID := c.DefaultQuery("calleeUserID", "matrix-x")
26 | callID := c.DefaultQuery("userID", "matrix-x")
27 |
28 | res, err := services.WeComApp.OAPSTNCC.GetStates(c.Request.Context(), calleeUserID, callID)
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | c.JSON(http.StatusOK, res)
35 | }
36 |
--------------------------------------------------------------------------------
/controllers/miniprogram/url-link.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/urlLink/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 获取小程序 URL Link,适用于短信、邮件、网页、微信内等拉起小程序的业务场景
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html
12 | func APIURLLinkGenerate(c *gin.Context) {
13 |
14 | path := c.DefaultQuery("path", "pages/index/index")
15 |
16 | rs, err := services.MiniProgramApp.URLLink.Generate(c.Request.Context(),
17 | &request.URLLinkGenerate{
18 | EnvVersion: "release",
19 | ExpireInterval: 1606737600,
20 | Path: path,
21 | Query: "a=1",
22 | })
23 |
24 | if err != nil {
25 | panic(err)
26 | }
27 |
28 | c.JSON(http.StatusOK, rs)
29 | }
30 |
--------------------------------------------------------------------------------
/controllers/official-account/short-url.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // ShortGenKey 短key托管
10 | func ShortGenKey(ctx *gin.Context) {
11 | longData := ctx.DefaultQuery("longData", "longData test.....")
12 | data, err := services.OfficialAccountApp.URL.ShortGenKey(ctx.Request.Context(), longData, 30*24*3600)
13 | if err != nil {
14 | ctx.String(http.StatusBadRequest, err.Error())
15 | }
16 | ctx.JSON(http.StatusOK, data)
17 | }
18 |
19 | // FetchShortGen 短key还原
20 | func FetchShortGen(ctx *gin.Context) {
21 | shortKey := ctx.Query("shortKey")
22 | data, err := services.OfficialAccountApp.URL.FetchShorten(ctx.Request.Context(), shortKey)
23 | if err != nil {
24 | ctx.String(http.StatusBadRequest, err.Error())
25 | }
26 | ctx.JSON(http.StatusOK, data)
27 | }
28 |
--------------------------------------------------------------------------------
/controllers/miniprogram/phone-number.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | func GetUserPhoneNumber(c *gin.Context) {
10 |
11 | code, exist := c.GetQuery("code")
12 | if !exist {
13 | panic("parameter code expected")
14 | }
15 |
16 | rs, err := services.MiniProgramApp.PhoneNumber.GetUserPhoneNumber(c.Request.Context(), code)
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, rs)
23 | }
24 |
25 | func GetUserPhoneNumberByAES(c *gin.Context) {
26 | encryptData := c.Query("encryptData")
27 | sessionKey := c.Query("sessionKey")
28 | iv := c.Query("iv")
29 |
30 | data, err := services.MiniProgramApp.Encryptor.DecryptData(encryptData, sessionKey, iv)
31 | if err != nil {
32 | panic(err)
33 | }
34 |
35 | c.String(200, string(data))
36 | }
37 |
--------------------------------------------------------------------------------
/controllers/wecom/user/validate/user-create.go:
--------------------------------------------------------------------------------
1 | package validate
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/object"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/user/request"
6 | "github.com/gin-gonic/gin"
7 | "power-wechat-tutorial/config"
8 | http "power-wechat-tutorial/controllers"
9 | )
10 |
11 | func ValidateUserCreate(context *gin.Context) {
12 | var form request.RequestUserDetail
13 |
14 | if err := context.ShouldBind(&form); err != nil {
15 | if err := context.ShouldBindJSON(&form); err != nil {
16 |
17 | apiResponse := http.NewAPIResponse(context)
18 | apiResponse.SetCode(
19 | config.API_ERR_CODE_REQUEST_PARAM_ERROR,
20 | config.API_RETURN_CODE_ERROR,
21 | "", "").SetData(object.HashMap{
22 | "chat-bot": err.Error(),
23 | }).ThrowJSONResponse(context)
24 | }
25 | }
26 |
27 | context.Set("params", &form)
28 | context.Next()
29 | }
30 |
--------------------------------------------------------------------------------
/controllers/payment/bill.go:
--------------------------------------------------------------------------------
1 | package payment
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "os"
8 | "path"
9 | "power-wechat-tutorial/services"
10 | )
11 |
12 | // 下载账单API为通用接口,交易/资金账单都可以通过该接口获取到对应的账单
13 | // https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_8.shtml
14 | func APIBillDownloadURL(c *gin.Context) {
15 |
16 | requestDownload := &power.RequestDownload{
17 | HashType: "SHA1",
18 | HashValue: "442c2363a7e014b7f7cf3e2c558375bcf385951d",
19 | DownloadURL: "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg",
20 | }
21 | homePath, _ := os.UserHomeDir()
22 | filePath := path.Join(homePath, "Desktop/download-url")
23 |
24 | rs, err := services.PaymentApp.Bill.DownloadBill(c.Request.Context(), requestDownload, filePath)
25 | if err != nil {
26 | panic(err)
27 | }
28 | c.JSON(http.StatusOK, rs)
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/controllers/official-account/qrcode.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // 创建临时二维码
10 | func GetTempQrCode(ctx *gin.Context) {
11 | data, err := services.OfficialAccountApp.QRCode.Temporary(ctx.Request.Context(), "val1", 30*24*3600)
12 | if err != nil {
13 | ctx.String(http.StatusBadRequest, err.Error())
14 | return
15 | }
16 | ctx.JSON(http.StatusOK, data)
17 | }
18 |
19 | // GetForeverQrCode 创建永久二维码
20 | func GetForeverQrCode(ctx *gin.Context) {
21 | data, err := services.OfficialAccountApp.QRCode.Forever(ctx.Request.Context(), "val1")
22 | if err != nil {
23 | ctx.String(http.StatusBadRequest, err.Error())
24 | return
25 | }
26 | ctx.JSON(http.StatusOK, data)
27 | }
28 |
29 | // 获取二维码网址
30 | func GetQrCodeUrl(ctx *gin.Context) {
31 | url := services.OfficialAccountApp.QRCode.URL("from")
32 | ctx.String(http.StatusOK, url)
33 | }
34 |
--------------------------------------------------------------------------------
/services/get-env.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "errors"
5 | "os"
6 | "strconv"
7 | )
8 |
9 | const LOCALE_EN = "en_US"
10 | const LOCALE_CN = "zh_CN"
11 | const LOCALE_TW = "zh_TW"
12 |
13 | var ErrEnvVarEmpty = errors.New("getEnv: environment variable empty")
14 |
15 | func getEnvStr(key string) (string, error) {
16 | v := os.Getenv(key)
17 | if v == "" {
18 | return v, ErrEnvVarEmpty
19 | }
20 | return v, nil
21 | }
22 |
23 | func getEnvInt(key string) (int, error) {
24 | s, err := getEnvStr(key)
25 | if err != nil {
26 | return 0, err
27 | }
28 | v, err := strconv.Atoi(s)
29 | if err != nil {
30 | return 0, err
31 | }
32 | return v, nil
33 | }
34 |
35 | func getEnvBool(key string) (bool, error) {
36 | s, err := getEnvStr(key)
37 | if err != nil {
38 | return false, err
39 | }
40 | v, err := strconv.ParseBool(s)
41 | if err != nil {
42 | return false, err
43 | }
44 | return v, nil
45 | }
46 |
--------------------------------------------------------------------------------
/controllers/miniprogram/soter.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/soter/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // SOTER 生物认证秘钥签名验证
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/soter/soter.verifySignature.html
12 | func APISoterVerifySignature(c *gin.Context) {
13 |
14 | jsonString := c.Query("jsonString")
15 | jsonSignature := c.Query("jsonSignature")
16 |
17 | openID, exist := c.GetQuery("openID")
18 | if !exist {
19 | panic("parameter open id expected")
20 | }
21 |
22 | rs, err := services.MiniProgramApp.Soter.VerifySignature(
23 | c.Request.Context(),
24 | &request.RequestSoter{
25 | OpenID: openID,
26 | JsonString: jsonString,
27 | JsonSignature: jsonSignature,
28 | })
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | c.JSON(http.StatusOK, rs)
35 | }
36 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/approval.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/approval/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | func APIApprovalOaGetTemplateDetail(c *gin.Context) {
11 |
12 | }
13 |
14 | func APIApprovalOaGetApprovalInfo(c *gin.Context) {
15 |
16 | }
17 |
18 | func APIApprovalOaGetApprovalDetail(c *gin.Context) {
19 |
20 | }
21 |
22 | func APIApprovalOaGetApprovalData(c *gin.Context) {
23 |
24 | }
25 |
26 | func APIApprovalVacationGetCorpConf(c *gin.Context) {
27 |
28 | }
29 |
30 | func APIApprovalVacationGetUserVacationQuota(c *gin.Context) {
31 |
32 | }
33 |
34 | func APIApprovalVacationSetOneUserQuota(c *gin.Context) {
35 |
36 | }
37 |
38 | func APIApprovalUpdateTemplate(c *gin.Context) {
39 | options := &request.RequestUpdateTemplate{}
40 | res, err := services.WeComApp.OAApproval.UpdateTemplate(c.Request.Context(), options)
41 |
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | c.JSON(http.StatusOK, res)
47 | }
48 |
--------------------------------------------------------------------------------
/controllers/payment/tax/tax.go:
--------------------------------------------------------------------------------
1 | package tax
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/tax/request"
5 | "github.com/gin-gonic/gin"
6 | "log"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APIApplyForCardTemplate(c *gin.Context) {
12 | //traceNo := c.Query("traceNo")
13 | //log.Printf("traceNo: %s", traceNo)
14 |
15 | para := &request.RequestApplyForCardTemplate{
16 | CardAppid: "wxa5fa5b1adab7aa1f",
17 | CardTemplateInformation: &request.CardTemplateInformation{
18 | PayeeName: "123",
19 | LogoUrl: "123",
20 | CustomCell: &request.CustomCell{
21 | Words: "123",
22 | Description: "321",
23 | JumpUrl: "3213",
24 | MiniProgramUserName: "1231",
25 | MiniProgramPath: "123",
26 | },
27 | },
28 | }
29 |
30 | rs, err := services.PaymentApp.Tax.ApplyForCardTemplate(c.Request.Context(), para)
31 | if err != nil {
32 | log.Println("出错了: ", err)
33 | c.String(400, err.Error())
34 | return
35 | }
36 | c.JSON(http.StatusOK, rs)
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/controllers/payment/paymentScore/paymentScore.go:
--------------------------------------------------------------------------------
1 | package paymentScore
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/payScore/request"
5 | "github.com/gin-gonic/gin"
6 | "log"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APIServiceOrder(c *gin.Context) {
12 | //traceNo := c.Query("traceNo")
13 | //log.Printf("traceNo: %s", traceNo)
14 |
15 | para := &request.RequestServiceOrder{
16 | OutOrderNo: "",
17 | Appid: "",
18 | ServiceId: "",
19 | ServiceIntroduction: "",
20 | PostPayments: nil,
21 | PostDiscounts: nil,
22 | //TimeRange: nil,
23 | //Location: nil,
24 | //RiskFund: nil,
25 | Attach: "",
26 | NotifyUrl: "",
27 | Openid: "",
28 | //NeedUserConfirm: nil,
29 | }
30 |
31 | rs, err := services.PaymentApp.PayScore.ServiceOrder(c.Request.Context(), para)
32 | if err != nil {
33 | log.Println("出错了: ", err)
34 | c.String(400, err.Error())
35 | return
36 | }
37 | c.JSON(http.StatusOK, rs)
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/controllers/payment/merchant/merchant.go:
--------------------------------------------------------------------------------
1 | package merchant
2 |
3 | import (
4 | "crypto/sha256"
5 | "encoding/hex"
6 | "fmt"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/merchant/request"
8 | "github.com/gin-gonic/gin"
9 | "log"
10 | "net/http"
11 | "os"
12 | "path"
13 | "power-wechat-tutorial/services"
14 | )
15 |
16 | func APIUploadImg(c *gin.Context) {
17 |
18 | // 读取图片文件
19 | homePath, _ := os.UserHomeDir()
20 | imagePath := path.Join(homePath, "Desktop/641.png")
21 | imageData, err := os.ReadFile(imagePath)
22 | if err != nil {
23 | fmt.Println("无法读取图片文件:", err)
24 | os.Exit(1)
25 | }
26 |
27 | hash := sha256.Sum256(imageData)
28 | hashString := hex.EncodeToString(hash[:])
29 |
30 | para := &request.RequestMediaUpload{
31 | File: imagePath,
32 | Meta: &request.Meta{
33 | Filename: "641.png",
34 | Sha256: hashString,
35 | },
36 | }
37 |
38 | rs, err := services.PaymentApp.Merchant.UploadImg(c.Request.Context(), para)
39 | if err != nil {
40 | log.Println("出错了: ", err)
41 | c.String(400, err.Error())
42 | return
43 | }
44 | c.JSON(http.StatusOK, rs)
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/services/open-platform.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/openPlatform"
6 | "power-wechat-tutorial/config"
7 | )
8 |
9 | var OpenPlatformApp *openPlatform.OpenPlatform
10 |
11 | func NewOpenPlatformAppService(conf *config.Configuration) (*openPlatform.OpenPlatform, error) {
12 |
13 | var cache kernel.CacheInterface
14 | if conf.MiniProgram.RedisAddr != "" {
15 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
16 | Addrs: []string{conf.MiniProgram.RedisAddr},
17 | })
18 | }
19 |
20 | app, err := openPlatform.NewOpenPlatform(&openPlatform.UserConfig{
21 |
22 | AppID: conf.OpenPlatform.AppID,
23 | Secret: conf.OpenPlatform.AppSecret,
24 |
25 | Token: conf.OpenPlatform.MessageToken,
26 | AESKey: conf.OpenPlatform.MessageAesKey,
27 |
28 | //Log: openPlatform.Log{
29 | // Level: "debug",
30 | // File: "./wechat.log",
31 | //},
32 | Cache: cache,
33 | HttpDebug: false,
34 | Debug: false,
35 | Http: openPlatform.Http{
36 | Timeout: 30,
37 | },
38 | //"sandbox": true,
39 | })
40 |
41 | return app, err
42 | }
43 |
--------------------------------------------------------------------------------
/controllers/miniprogram/risk-control.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/riskControl/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 根据提交的用户信息数据获取用户的安全等级 risk_rank,无需用户授权。
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/safety-control-capability/riskControl.getUserRiskRank.html
12 | func APIRiskControlGetUserRiskRank(c *gin.Context) {
13 |
14 | appID, exist := c.GetQuery("appID")
15 | if !exist {
16 | panic("parameter app id expected")
17 | }
18 |
19 | openID, exist := c.GetQuery("openID")
20 | if !exist {
21 | panic("parameter open id expected")
22 | }
23 |
24 | options := &request.RequestRiskControl{
25 | AppID: appID,
26 | OpenID: openID,
27 | Scene: 1,
28 | MobileNo: "12345678",
29 | BankCardNo: "******",
30 | CertNo: "*******",
31 | ClientIP: "******",
32 | EmailAddress: "***@qq.com",
33 | ExtendedInfo: "",
34 | }
35 |
36 | rs, err := services.MiniProgramApp.RiskControl.GetUserRiskRank(c.Request.Context(), options)
37 |
38 | if err != nil {
39 | panic(err)
40 | }
41 |
42 | c.JSON(http.StatusOK, rs)
43 | }
44 |
--------------------------------------------------------------------------------
/controllers/official-account/base.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // ClearQuota godoc
10 | // @Summary 公众号里清空api的调用quota:https://developers.weixin.qq.com/doc/offiaccount/openApi/clear_quota.html
11 | // @Description.markdown official-account.clearQuota
12 | // @Tags OfficialAccount.base.ClearQuota
13 | // @Router /clearQuota [get]
14 | func ClearQuota(ctx *gin.Context) {
15 | data, err := services.OfficialAccountApp.Base.ClearQuota(ctx.Request.Context())
16 | if err != nil {
17 | ctx.String(http.StatusBadRequest, err.Error())
18 | return
19 | }
20 | ctx.JSON(http.StatusOK, data)
21 | }
22 |
23 | // GetCallbackIP godoc
24 | // @Summary 获取公众号回调的IP地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
25 | // @Description.markdown official-account.getCallbackIp
26 | // @Tags OfficialAccount.base.GetCallbackIP
27 | // @Router /getCallbackIp [get]
28 | func GetCallbackIP(ctx *gin.Context) {
29 | data, err := services.OfficialAccountApp.Base.GetCallbackIP(ctx.Request.Context())
30 | if err != nil {
31 | ctx.String(http.StatusBadRequest, err.Error())
32 | return
33 | }
34 | ctx.JSON(http.StatusOK, data)
35 | }
36 |
--------------------------------------------------------------------------------
/services/offiaccount-service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
6 | "os"
7 | "power-wechat-tutorial/config"
8 | )
9 |
10 | var OfficialAccountApp *officialAccount.OfficialAccount
11 |
12 | func NewOfficialAccountAppService(conf *config.Configuration) (*officialAccount.OfficialAccount, error) {
13 |
14 | var cache kernel.CacheInterface
15 | if conf.MiniProgram.RedisAddr != "" {
16 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
17 | Addrs: []string{conf.MiniProgram.RedisAddr},
18 | })
19 | }
20 |
21 | app, err := officialAccount.NewOfficialAccount(&officialAccount.UserConfig{
22 |
23 | AppID: conf.OffiAccount.AppID, // 小程序、公众号或者企业微信的appid
24 | Secret: conf.OffiAccount.AppSecret, // 商户号 appID
25 |
26 | Token: conf.OffiAccount.MessageToken,
27 | AESKey: conf.OffiAccount.MessageAesKey,
28 | ResponseType: os.Getenv("response_type"),
29 | Log: officialAccount.Log{
30 | //Driver: &testLogDriver.SimpleLogger{},
31 | Level: "debug",
32 | //File: "./wechat.log",
33 | Stdout: false,
34 | },
35 | Cache: cache,
36 | HttpDebug: true,
37 | Debug: false,
38 | //"sandbox": true,
39 | })
40 |
41 | return app, err
42 | }
43 |
--------------------------------------------------------------------------------
/controllers/wecom/user/message.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // SendTextMsg 企业微信内部应用主动发送消息
11 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90236
12 | func APISendTextMsg(c *gin.Context) {
13 | toUser := c.DefaultQuery("toUser", "walle")
14 | res, err := services.WeComApp.Message.Send(c.Request.Context(), &power.HashMap{
15 | "touser": toUser,
16 | "msgtype": "text",
17 | "agentid": 1000004,
18 | "text": &power.StringMap{
19 | "content": "你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看邮件中心视频实况,聪明避开排队。",
20 | },
21 | "safe": 0,
22 | "enable_id_trans": 0,
23 | "enable_duplicate_check": 0,
24 | "duplicate_check_interval": 1800,
25 | })
26 |
27 | if err != nil {
28 | panic(err)
29 | }
30 |
31 | c.JSON(http.StatusOK, res)
32 | }
33 |
34 | // Recall 撤回应用消息
35 | // https://open.work.weixin.qq.com/api/doc/90000/90135/94867
36 | func APIRecallMsg(c *gin.Context) {
37 | msgID := c.Query("msgID")
38 | res, err := services.WeComApp.Message.Recall(c.Request.Context(), msgID)
39 |
40 | if err != nil {
41 | panic(err)
42 | }
43 |
44 | c.JSON(http.StatusOK, res)
45 | }
46 |
--------------------------------------------------------------------------------
/controllers/miniprogram/url-scheme.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/urlScheme/request"
5 | "github.com/gin-gonic/gin"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // 获取小程序 scheme 码,适用于短信、邮件、外部网页、微信内等拉起小程序的业务场景
10 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html
11 | func APIURLSchemeGenerate(c *gin.Context) {
12 |
13 | path, exist := c.GetQuery("path")
14 | if !exist {
15 | panic("parameter path expected")
16 | }
17 |
18 | rs, err := services.MiniProgramApp.URLScheme.Generate(c.Request.Context(),
19 | &request.URLSchemeGenerate{
20 | JumpWxa: &request.JumpWxa{
21 | Path: path,
22 | Query: "b=2&c=2",
23 | },
24 | IsExpire: true,
25 | ExpireType: 1,
26 | ExpireTime: 1606737600,
27 | ExpireInterval: 30,
28 | })
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | //content, _ := ioutil.ReadAll(rs.Body)
35 | ////fmt.Dump("content-type:",rs.Header.Get("Content-Type"))
36 | //c.Header("Content-Type", rs.Header.Get("Content-Type"))
37 | //c.Header("Content-Disposition", rs.Header.Get("attachment;filename=\""+rs.Header.Get("filename")+"\""))
38 | //c.Data(http.StatusOK, rs.Header.Get("Content-Type"), content)
39 | c.JSON(200, rs)
40 | }
41 |
--------------------------------------------------------------------------------
/controllers/wecom/account-service/service-state.go:
--------------------------------------------------------------------------------
1 | package account_service
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | "strconv"
8 | )
9 |
10 | // 获取会话状态
11 | // https://work.weixin.qq.com/api/doc/90000/90135/94669
12 | func APIAccountServiceStateGet(c *gin.Context) {
13 |
14 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
15 | externalUserID := c.DefaultQuery("externalUserID", "wmxxxxxxxxxxxxxxxxxx")
16 |
17 | res, err := services.WeComApp.AccountServiceState.Get(c.Request.Context(), openKFID, externalUserID)
18 |
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | c.JSON(http.StatusOK, res)
24 | }
25 |
26 | // 变更会话状态
27 | // https://work.weixin.qq.com/api/doc/90000/90135/94669
28 | func APIAccountServiceStateTrans(c *gin.Context) {
29 |
30 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
31 | externalUserID := c.DefaultQuery("externalUserID", "kfxxxxxxxxxxxxxx")
32 | serviceState := c.DefaultQuery("serviceState", "kfxxxxxxxxxxxxxx")
33 | servicerUserID := c.DefaultQuery("servicerUserID", "kfxxxxxxxxxxxxxx")
34 |
35 | state, _ := strconv.Atoi(serviceState)
36 |
37 | res, err := services.WeComApp.AccountServiceState.Trans(c.Request.Context(), openKFID, externalUserID, state, servicerUserID)
38 |
39 | if err != nil {
40 | panic(err)
41 | }
42 |
43 | c.JSON(http.StatusOK, res)
44 | }
45 |
--------------------------------------------------------------------------------
/controllers/official-account/oauth.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/gin-gonic/gin"
6 | "log"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func GetAuthCode(ctx *gin.Context) {
12 |
13 | //result, err := services.OfficialAccountApp.JSSDK.ConfigSignature(ctx, "text", "", 0)
14 | //if err != nil {
15 | // panic(err.Error())
16 | //}
17 | //ctx.JSON(http.StatusOK, gin.H{"result": result})
18 | //return
19 | code := ctx.Query("code")
20 | state := ctx.Query("state")
21 |
22 | ctx.JSON(http.StatusOK, gin.H{"code": code, "state": state})
23 | }
24 |
25 | func UserFromCode(ctx *gin.Context) {
26 | code := ctx.Query("code")
27 | services.OfficialAccountApp.OAuth.SetScopes([]string{"snsapi_base"})
28 | user, err := services.OfficialAccountApp.OAuth.UserFromCode(code)
29 | if err != nil {
30 | ctx.String(http.StatusBadRequest, err.Error())
31 | return
32 | }
33 | ctx.JSON(http.StatusOK, user)
34 | }
35 |
36 | func UserFromToken(ctx *gin.Context) {
37 | token := ctx.Query("token")
38 | openID := ctx.Query("openID")
39 | user, err := services.OfficialAccountApp.OAuth.UserFromToken(token, openID)
40 | rsToken := user.GetTokenResponse()
41 | fmt.Dump(rsToken, (*rsToken)["openid"])
42 | log.Println(err)
43 | if err != nil {
44 | ctx.String(http.StatusBadRequest, err.Error())
45 | return
46 | }
47 | ctx.JSON(http.StatusOK, user)
48 | }
49 |
--------------------------------------------------------------------------------
/controllers/payment/redpack/redpack.go:
--------------------------------------------------------------------------------
1 | package redpack
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/redpack/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APISendNormal(c *gin.Context) {
12 |
13 | mchId := services.PaymentApp.GetConfig().GetString("mch_id", "")
14 | appId := services.PaymentApp.GetConfig().GetString("app_id", "")
15 |
16 | options := &request.RequestSendRedPack{
17 | MchBillNO: "0010010404201411170000046545",
18 | MchID: mchId,
19 | WXAppID: appId,
20 | SendName: "ArtisanCloud",
21 | ReOpenID: "oAuaP0TRUMwP169nQfg7XCEAw3HQ",
22 | TotalAmount: 100,
23 | TotalNum: 1,
24 | Wishing: "恭喜发财",
25 | ClientIP: "127.0.0.1",
26 | ActName: "新年红包",
27 | Remark: "新年红包",
28 | SceneID: "PRODUCT_2",
29 | RiskInfo: "posttime%3d123123412%26clientversion%3d234134%26mobile%3d122344545%26deviceid%3dIOS",
30 | }
31 |
32 | payConf, err := services.PaymentApp.RedPack.SendNormal(c.Request.Context(), options)
33 | if err != nil {
34 | fmt.Dump(err.Error())
35 | panic(err)
36 | }
37 |
38 | c.XML(http.StatusOK, payConf)
39 | }
40 |
41 | func APIQueryRedPack(c *gin.Context) {
42 | rs, err := services.PaymentApp.RedPack.Info(c.Request.Context(), "0010010404201411170000046545")
43 | if err != nil {
44 | panic(nil)
45 | }
46 |
47 | c.JSON(http.StatusOK, rs)
48 | }
49 |
--------------------------------------------------------------------------------
/controllers/wecom/corpgroup.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | "strconv"
8 | )
9 |
10 | // 获取应用共享信息
11 | // https://open.work.weixin.qq.com/api/doc/90000/90135/93403
12 | func APICorpGroupCorpListAppShareInfo(c *gin.Context) {
13 |
14 | agentId := c.DefaultQuery("agentID", "")
15 | agentID, _ := strconv.Atoi(agentId)
16 |
17 | res, err := services.WeComApp.CorpGroup.GetAppShareInfo(c.Request.Context(), agentID)
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, res)
23 | }
24 |
25 | // 获取下级企业的access_token
26 | // https://open.work.weixin.qq.com/api/doc/90000/90135/93359
27 | func APICorpGroupCorpGetToken(c *gin.Context) {
28 |
29 | corpID := c.DefaultQuery("corpID", "")
30 | agentID := c.DefaultQuery("agentID", "")
31 | res, err := services.WeComApp.CorpGroup.GetToken(c.Request.Context(), corpID, agentID)
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, res)
38 | }
39 |
40 | // 获取下级企业的小程序session
41 | // https://open.work.weixin.qq.com/api/doc/90000/90135/93355
42 | func APICorpGroupMiniProgramTransferSession(c *gin.Context) {
43 |
44 | userID := c.DefaultQuery("userID", "matrix-x")
45 | sessionKey := ""
46 | res, err := services.WeComApp.CorpGroup.GetMiniProgramTransferSession(c.Request.Context(), userID, sessionKey)
47 |
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | c.JSON(http.StatusOK, res)
53 | }
54 |
--------------------------------------------------------------------------------
/controllers/miniprogram/service-market.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/serviceMarket/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 调用服务平台提供的服务
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/service-market/serviceMarket.invokeService.html
13 | func APIServiceMarketInvokeService(c *gin.Context) {
14 |
15 | serviceID, exist := c.GetQuery("serviceID")
16 | if !exist {
17 | panic("parameter service id expected")
18 | }
19 |
20 | apiName, exist := c.GetQuery("apiName")
21 | if !exist {
22 | panic("parameter api name expected")
23 | }
24 |
25 | clientMsgID, exist := c.GetQuery("clientMsgID")
26 | if !exist {
27 | panic("parameter client msg id expected")
28 | }
29 |
30 | serviceData := &power.HashMap{
31 | "img_url": "http://mmbiz.qpic.cn/mmbiz_jpg/7UFjuNbYxibu66xSqsQqKcuoGBZM77HIyibdiczeWibdMeA2XMt5oibWVQMgDibriazJSOibLqZxcO6DVVcZMxDKgeAtbQ/0",
32 | "data_type": 3,
33 | "ocr_type": 1,
34 | }
35 |
36 | rs, err := services.MiniProgramApp.ServiceMarket.InvokeService(c.Request.Context(),
37 | &request.RequestServiceMarket{
38 | Service: serviceID,
39 | Api: apiName,
40 | ClientMsgID: clientMsgID,
41 | Data: serviceData,
42 | })
43 |
44 | if err != nil {
45 | panic(err)
46 | }
47 |
48 | c.JSON(http.StatusOK, rs)
49 | }
50 |
--------------------------------------------------------------------------------
/controllers/wecom/account-service/servicer.go:
--------------------------------------------------------------------------------
1 | package account_service
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // 添加接待人员
10 | // https://work.weixin.qq.com/api/doc/90000/90135/94646
11 | func APIAccountServiceServicerAdd(c *gin.Context) {
12 |
13 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
14 | userIDList := []string{c.DefaultQuery("userIDList", "matrix-x")}
15 |
16 | res, err := services.WeComApp.AccountServiceServicer.Add(c.Request.Context(), openKFID, userIDList)
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, res)
23 | }
24 |
25 | // 删除接待人员
26 | // https://work.weixin.qq.com/api/doc/90000/90135/94647
27 | func APIAccountServiceServicerDel(c *gin.Context) {
28 |
29 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
30 | userIDList := []string{c.DefaultQuery("userIDList", "matrix-x")}
31 |
32 | res, err := services.WeComApp.AccountServiceServicer.Del(c.Request.Context(), openKFID, userIDList)
33 |
34 | if err != nil {
35 | panic(err)
36 | }
37 |
38 | c.JSON(http.StatusOK, res)
39 | }
40 |
41 | // 获取接待人员列表
42 | // https://work.weixin.qq.com/api/doc/90000/90135/94645
43 | func APIAccountServiceServicerList(c *gin.Context) {
44 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
45 |
46 | res, err := services.WeComApp.AccountServiceServicer.List(c.Request.Context(), openKFID)
47 |
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | c.JSON(http.StatusOK, res)
53 | }
54 |
--------------------------------------------------------------------------------
/controllers/wecom/external-contact/statistic.go:
--------------------------------------------------------------------------------
1 | package external_contact
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/externalContact/statistics/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 获取「联系客户统计」数据
12 | // https://work.weixin.qq.com/api/doc/90000/90135/92132
13 | func APIExternalContactGetUserBehaviorData(c *gin.Context) {
14 |
15 | options := &request.RequestGetUserBehaviorData{
16 | UserID: []string{c.DefaultQuery("userID", "matrix-x")},
17 | PartyID: []int{1001},
18 | StartTime: 1536508800,
19 | EndTime: 1536595200,
20 | }
21 |
22 | res, err := services.WeComContactApp.ExternalContactStatistics.GetUserBehaviorData(c.Request.Context(), options)
23 |
24 | if err != nil {
25 | panic(err)
26 | }
27 |
28 | c.JSON(http.StatusOK, res)
29 | }
30 |
31 | // 获取「群聊数据统计」数据
32 | // https://work.weixin.qq.com/api/doc/90000/90135/92133
33 | func APIExternalContactGroupChatStatistic(c *gin.Context) {
34 | options := &request.RequestStatistic{
35 | DayBeginTime: 1600272000,
36 | DayEndTime: 1600444800,
37 | OwnerFilter: &power.HashMap{
38 | "userid_list": []string{c.DefaultQuery("userID", "matrix-x")},
39 | },
40 | OrderBy: 2,
41 | OrderAsc: 0,
42 | Offset: 0,
43 | Limit: 1000,
44 | }
45 | res, err := services.WeComContactApp.ExternalContactStatistics.Statistic(c.Request.Context(), options)
46 |
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | c.JSON(http.StatusOK, res)
52 | }
53 |
--------------------------------------------------------------------------------
/routes/router.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-contrib/cors"
6 | "github.com/gin-gonic/gin"
7 | "log"
8 | "net/http"
9 | )
10 |
11 | var Router *gin.Engine
12 |
13 | func InitializeRoutes(r *gin.Engine) {
14 |
15 | r.Use(cors.New(cors.Config{
16 | AllowOrigins: []string{"*"},
17 | AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
18 | AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Requested-With"},
19 | AllowCredentials: true,
20 | AllowOriginFunc: func(origin string) bool {
21 | log.Println("origin: ", origin)
22 | return true
23 | },
24 | }))
25 |
26 | // Payment App Router
27 | InitPaymentAPIRoutes(r)
28 |
29 | // MiniProgram App Router
30 | InitMiniProgramAPIRoutes(r)
31 |
32 | // WeCom App Router
33 | InitWecomAPIRoutes(r)
34 |
35 | // Official App Router
36 | InitOfficialAPIRoutes(r)
37 |
38 | // OpenPlatform App Router
39 | InitOpenPlatformAPIRoutes(r)
40 |
41 | r.GET("/", func(c *gin.Context) {
42 | //c.String(200, "hello")
43 | c.Writer.WriteHeader(http.StatusOK)
44 | c.Writer.Write([]byte("Hello, PowerWechat"))
45 |
46 | })
47 |
48 | r.GET("/json/user", func(context *gin.Context) {
49 | obj := &power.HashMap{
50 | "say": "I am",
51 | "something": "ArtisanCloud",
52 | }
53 | context.JSON(http.StatusOK, obj)
54 | })
55 |
56 | r.LoadHTMLGlob("templates/openplatform-auth.html")
57 | r.GET("/openplatform-auth", func(c *gin.Context) {
58 | c.HTML(http.StatusOK, "openplatform-auth.html", nil)
59 | })
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/controllers/payment/security.go:
--------------------------------------------------------------------------------
1 | package payment
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/support"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/security/response"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | "power-wechat-tutorial/services"
10 | )
11 |
12 | // Get RSA Public Key.
13 | // https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay_yhk.php?chapter=25_7&index=4
14 | func APIGetRSAPublicKey(c *gin.Context) {
15 |
16 | rs, err := services.PaymentApp.Security.GetRSAPublicKey(c.Request.Context())
17 | if err != nil {
18 | panic(err)
19 | }
20 | fmt.Dump(rs.PubKey)
21 |
22 | c.JSON(http.StatusOK, rs)
23 |
24 | }
25 |
26 | // 获取平台证书
27 | // https://pay.weixin.qq.com/wiki/doc/apiv3/apis/wechatpay5_1.shtml
28 | func APIGetCertificates(c *gin.Context) {
29 |
30 | rs, err := services.PaymentApp.Security.GetCertificates(c.Request.Context())
31 | if err != nil {
32 | panic(err)
33 | }
34 | c.JSON(http.StatusOK, rs)
35 |
36 | }
37 |
38 | func APIDecryptCertificate(c *gin.Context) {
39 |
40 | form := &response.ResponseGetCertificates{}
41 |
42 | if err := c.ShouldBindJSON(form); err != nil {
43 | panic(err)
44 | }
45 |
46 | config := services.PaymentApp.GetConfig()
47 | v3AESKey := config.GetString("mch_api_v3_key", "")
48 |
49 | plainTXT, err := support.DecryptAES256GCM(
50 | v3AESKey,
51 | form.Data[0].EncryptCertificate.AssociatedData,
52 | form.Data[0].EncryptCertificate.Nonce,
53 | form.Data[0].EncryptCertificate.Ciphertext,
54 | )
55 | if err != nil {
56 | panic(err)
57 | }
58 | fmt.Dump(plainTXT)
59 | c.JSON(http.StatusOK, plainTXT)
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/controllers/miniprogram/updatable-message.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/updatableMessage/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 创建被分享动态消息或私密消息的 activity_id
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.createActivityId.html
12 | func APIUpdatableMessageCreateActivityID(c *gin.Context) {
13 |
14 | openID, exist := c.GetQuery("openID")
15 | if !exist {
16 | panic("parameter open id expected")
17 | }
18 |
19 | rs, err := services.MiniProgramApp.UpdatableMessage.CreateActivityID(c.Request.Context(), "", openID)
20 |
21 | if err != nil {
22 | panic(err)
23 | }
24 |
25 | c.JSON(http.StatusOK, rs)
26 | }
27 |
28 | // 修改被分享的动态消息。
29 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/updatable-message/updatableMessage.setUpdatableMsg.html
30 | func APIUpdatableMessageUpdatableMessage(c *gin.Context) {
31 |
32 | activityID, exist := c.GetQuery("activityID")
33 | if !exist {
34 | panic("parameter open id expected")
35 | }
36 |
37 | rs, err := services.MiniProgramApp.UpdatableMessage.SetUpdatableMsg(c.Request.Context(), &request.RequestSetUpdatableMsg{
38 | ActivityID: activityID,
39 | TargetState: 0,
40 | TemplateInfo: &request.TemplateInfo{
41 | ParameterList: []*request.ParameterListItem{
42 | {
43 | Name: "member_count",
44 | Value: "2",
45 | },
46 | {
47 | Name: "room_limit",
48 | Value: "5",
49 | },
50 | },
51 | },
52 | })
53 |
54 | if err != nil {
55 | panic(err)
56 | }
57 |
58 | c.JSON(http.StatusOK, rs)
59 | }
60 |
--------------------------------------------------------------------------------
/services/miniprogram-service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/logger/drivers"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/response"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram"
8 | "power-wechat-tutorial/config"
9 | )
10 |
11 | var MiniProgramApp *miniProgram.MiniProgram
12 |
13 | const TIMEZONE = "asia/shanghai"
14 | const DATETIME_FORMAT = "20060102"
15 |
16 | func NewMiniMiniProgramService(conf *config.Configuration) (*miniProgram.MiniProgram, error) {
17 | var cache kernel.CacheInterface
18 | if conf.MiniProgram.RedisAddr != "" {
19 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
20 | Addrs: []string{conf.MiniProgram.RedisAddr},
21 | //Addrs: []string{
22 | // "47.108.182.200:7000",
23 | // "47.108.182.200:7001",
24 | // "47.108.182.200:7002",
25 | //},
26 | //Username: "michaelhu",
27 | //Password: "111111",
28 | })
29 | }
30 | app, err := miniProgram.NewMiniProgram(&miniProgram.UserConfig{
31 | AppID: conf.MiniProgram.AppID, // 小程序、公众号或者企业微信的appid
32 | Secret: conf.MiniProgram.Secret, // 商户号 appID
33 | ResponseType: response.TYPE_MAP,
34 | Token: conf.MiniProgram.MessageToken,
35 | AESKey: conf.MiniProgram.MessageAesKey,
36 |
37 | AppKey: conf.MiniProgram.VirtualPayAppKey,
38 | OfferID: conf.MiniProgram.VirtualPayOfferID,
39 | Http: miniProgram.Http{},
40 | Log: miniProgram.Log{
41 | Driver: &drivers.SimpleLogger{},
42 | Level: "debug",
43 | File: "./wechat.log",
44 | },
45 | //"sandbox": true,
46 | Cache: cache,
47 | HttpDebug: true,
48 | Debug: false,
49 | })
50 |
51 | return app, err
52 | }
53 |
--------------------------------------------------------------------------------
/controllers/payment/transfer/batch.go:
--------------------------------------------------------------------------------
1 | package transfer
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/transfer/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | func APIBatchTransfer(c *gin.Context) {
11 |
12 | appId := services.PaymentApp.GetConfig().GetString("app_id", "")
13 |
14 | transfer := &request.RequestTransferBatch{
15 | AppID: appId,
16 | OutBatchNO: "0010010404201411170000046522",
17 | BatchName: "batch-1",
18 | BatchRemark: "batch-1-remark",
19 | TotalAmount: 30,
20 | TotalNum: 1,
21 | TransferDetailList: []*request.TransferDetail{
22 | &request.TransferDetail{
23 | OutDetailNO: "00100104042014111700000465221",
24 | TransferAmount: 30,
25 | TransferRemark: "remark",
26 | OpenID: "o4QEk5Kc_y8QTrENCpKoxYhS4jkg",
27 | //UserName: object.NewNullString("username", true),
28 | },
29 | },
30 | }
31 |
32 | payResult, err := services.PaymentApp.TransferBatch.Batch(c.Request.Context(), transfer)
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, payResult)
38 | }
39 |
40 | func APIQueryBatchOrder(c *gin.Context) {
41 |
42 | rs, err := services.PaymentApp.TransferBatch.QueryBatch(
43 | c.Request.Context(),
44 | "{batchID}}",
45 | true,
46 | 0,
47 | 10,
48 | "")
49 | if err != nil {
50 | panic(nil)
51 | }
52 |
53 | c.JSON(http.StatusOK, rs)
54 | }
55 |
56 | func APIQueryBatchOrderDetail(c *gin.Context) {
57 |
58 | rs, err := services.PaymentApp.TransferBatch.QueryBatchDetail(
59 | c.Request.Context(),
60 | "{batchID}}",
61 | "{batchDetailID}}")
62 |
63 | if err != nil {
64 | panic(nil)
65 | }
66 |
67 | c.JSON(http.StatusOK, rs)
68 | }
69 |
--------------------------------------------------------------------------------
/controllers/official-account/jssdk.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerLibs/v3/object"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/response"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | "power-wechat-tutorial/services"
10 | )
11 |
12 | func APITicketGet(c *gin.Context) {
13 |
14 | services.OfficialAccountApp.AccessToken.SetCacheKey("456")
15 | services.OfficialAccountApp.AccessToken.SetCustomToken = func(token *response.ResponseGetToken) interface{} {
16 | fmt.Dump("SetCustomToken", token)
17 | return nil
18 | //return "72_iraMZORXFIW6IM7bLyP3e3MMcEDkkrRXywvPAy3D7KI5lpSbMWh5ZgQUUSfl7tXg2Uq-aU_C3Vkj551IUTBPD58WFbgTdEt-csoGPQ8Hkf88DpUVs0MKtrDVhGwXNQiAFACSV"
19 | }
20 | services.OfficialAccountApp.AccessToken.GetCustomToken = func(key string, refresh bool) object.HashMap {
21 | fmt.Dump("GetCustomToken", key, refresh)
22 | return object.HashMap{
23 | "access_token": "72_ggzUdSgH99StJ2EhmuaIbHHUP9_3rDvdnQVQ9eoX5gwmNfuLpJgBUb5uPgdoh4aoVv9jYz3EKglRT73ppWqgRwzirNQM-bHaToDQ83ux1sFdCr5GK7jxYQfAESoCOEaAHAKWM",
24 | "expires_in": float64(7200),
25 | }
26 | }
27 |
28 | res, err := services.OfficialAccountApp.JSSDK.GetTicket(c.Request.Context(), false, "jsapi")
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | c.JSON(http.StatusOK, res)
35 | }
36 |
37 | func JSSDKBuildConfig(ctx *gin.Context) {
38 | url := "https://www.artisan-cloud.com/"
39 | jsapiList := []string{"chooseImage"}
40 | debug := false
41 | beta := false
42 | openTagList := []string{"wx-open-launch-app", "wx-open-launch-weapp"}
43 | data, err := services.OfficialAccountApp.JSSDK.BuildConfig(ctx.Request.Context(), jsapiList, debug, beta, openTagList, url)
44 | if err != nil {
45 | ctx.String(http.StatusBadRequest, err.Error())
46 | return
47 | }
48 | ctx.JSON(http.StatusOK, data)
49 | }
50 |
--------------------------------------------------------------------------------
/controllers/official-account/uniform-message.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/uniformMessage/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 下发小程序和公众号统一的服务消息
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
13 | func UniformMessageSend(c *gin.Context) {
14 |
15 | openID, exist := c.GetQuery("openID")
16 | if !exist {
17 | panic("parameter open id expected")
18 | }
19 | templateID := c.DefaultQuery("templateID", "SPNl7d_nnSnssgyj7ZJ6v9B-N04ZBspts_PYvdF3D-8")
20 |
21 | rs, err := services.MiniProgramApp.UniformMessage.Send(c.Request.Context(), &request.RequestUniformMessageSend{
22 | ToUser: openID,
23 | MpTemplateMsg: &request.MPTemplateMsg{
24 | AppID: services.OfficialAccountApp.GetConfig().GetString("app_id", ""),
25 | TemplateID: templateID,
26 | Url: "https://powerwechat.artisan-cloud.com/",
27 | //MiniProgram: &request.MPTemplateMsgMiniProgram{
28 | // AppID: "",
29 | // PagePath: "",
30 | //},
31 | Data: &power.HashMap{
32 | "first": &power.HashMap{
33 | "value": "恭喜你购买成功!",
34 | "color": "#173177",
35 | },
36 | "DateTime": &power.HashMap{
37 | "value": "3-5 16:22",
38 | "color": "#173177",
39 | },
40 | "PayAmount": &power.HashMap{
41 | "value": "39.8元",
42 | "color": "#173177",
43 | },
44 | "Location": &power.HashMap{
45 | "value": "上海市长宁区",
46 | "color": "#173177",
47 | },
48 | "remark": &power.HashMap{
49 | "value": "欢迎再次购买!",
50 | "color": "#173177",
51 | },
52 | },
53 | },
54 | })
55 |
56 | if err != nil {
57 | panic(err)
58 | }
59 |
60 | c.JSON(http.StatusOK, rs)
61 | }
62 |
--------------------------------------------------------------------------------
/controllers/wecom/external-contact/group-chat.go:
--------------------------------------------------------------------------------
1 | package external_contact
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/externalContact/groupChat/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | "strconv"
10 | )
11 |
12 | // 获取客户群列表
13 | // https://work.weixin.qq.com/api/doc/90000/90135/92120
14 | func APIExternalContactGroupChatList(c *gin.Context) {
15 |
16 | options := &request.RequestGroupChatList{
17 | StatusFilter: 0,
18 | OwnerFilter: &power.HashMap{
19 | "userid_list": []string{"abel"},
20 | },
21 | Cursor: c.DefaultQuery("cursor", "r9FqSqsI8fgNbHLHE5QoCP50UIg2cFQbfma3l2QsmwI"),
22 | Limit: 10,
23 | }
24 | res, err := services.WeComContactApp.ExternalContactGroupChat.List(c.Request.Context(), options)
25 |
26 | if err != nil {
27 | panic(err)
28 | }
29 |
30 | c.JSON(http.StatusOK, res)
31 | }
32 |
33 | // 获取客户群详情
34 | // https://work.weixin.qq.com/api/doc/90000/90135/92122
35 | func APIExternalContactGroupChatGet(c *gin.Context) {
36 |
37 | chatID := c.DefaultQuery("chatID", "wrOgQhDgAAMYQiS5ol9G7gK9JVAAAA")
38 | needName := c.DefaultQuery("needName", "true")
39 | bNeedName, _ := strconv.Atoi(needName)
40 |
41 | res, err := services.WeComContactApp.ExternalContactGroupChat.Get(c.Request.Context(), chatID, bNeedName)
42 |
43 | if err != nil {
44 | panic(err)
45 | }
46 |
47 | c.JSON(http.StatusOK, res)
48 | }
49 |
50 | // 客户群opengid转换
51 | // https://work.weixin.qq.com/api/doc/90000/90135/94822
52 | func APIExternalContactOpenGIDToChatID(c *gin.Context) {
53 | openID := c.DefaultQuery("openID", "oAAAAAAA")
54 |
55 | res, err := services.WeComContactApp.ExternalContactGroupChat.OpenGIDToChatID(c.Request.Context(), openID)
56 |
57 | if err != nil {
58 | panic(err)
59 | }
60 |
61 | c.JSON(http.StatusOK, res)
62 | }
63 |
--------------------------------------------------------------------------------
/controllers/miniprogram/server.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerLibs/v3/http/helper"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
8 | models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
9 | "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
10 | "github.com/gin-gonic/gin"
11 | "power-wechat-tutorial/services"
12 | )
13 |
14 | // 回调配置
15 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
16 | func CallbackVerify(c *gin.Context) {
17 | rs, err := services.MiniProgramApp.Server.VerifyURL(c.Request)
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | // 选择1
23 | //text, _ := ioutil.ReadAll(rs.Body)
24 | //c.String(http.StatusOK, string(text))
25 |
26 | // 选择2
27 | err = helper.HttpResponseSend(rs, c.Writer)
28 |
29 | }
30 |
31 | // 回调配置
32 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
33 | func CallbackNotify(c *gin.Context) {
34 | //requestXML, _ := io.ReadAll(c.Request.Body)
35 | //c.Request.Body = io.NopCloser(bytes.NewBuffer(requestXML))
36 | //println(string(requestXML))
37 |
38 | rs, err := services.MiniProgramApp.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
39 | fmt.Dump("event", event)
40 | //return "handle callback"
41 |
42 | switch event.GetMsgType() {
43 | case models2.CALLBACK_MSG_TYPE_TEXT:
44 | msg := models.MessageText{}
45 | err := event.ReadMessage(&msg)
46 | if err != nil {
47 | println(err.Error())
48 | return "error"
49 | }
50 | fmt.Dump(msg)
51 | }
52 |
53 | return kernel.SUCCESS_EMPTY_RESPONSE
54 |
55 | })
56 | if err != nil {
57 | panic(err)
58 | }
59 |
60 | err = helper.HttpResponseSend(rs, c.Writer)
61 |
62 | if err != nil {
63 | panic(err)
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/routes/open-platform.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "power-wechat-tutorial/controllers/open-platform"
6 | )
7 |
8 | func InitOpenPlatformAPIRoutes(r *gin.Engine) {
9 |
10 | apiRouter := r.Group("/wx/api/openplatform")
11 | {
12 |
13 | // auth callback
14 | apiRouter.GET("/callback", open_platform.HandleAuthorize)
15 | apiRouter.POST("/callback", open_platform.APIOpenPlatformCallback)
16 | apiRouter.POST("/callback/:appID", open_platform.APIOpenPlatformCallbackWithApp)
17 |
18 | // auth
19 | apiRouter.GET("/createPreAuthCode", open_platform.APIOpenPlatformPreAuthCode)
20 | apiRouter.GET("/getFastRegistrationURL", open_platform.GetFastRegistrationURL)
21 | apiRouter.GET("/auth", open_platform.HandleAuthorize)
22 |
23 | // authorizer info
24 | apiRouter.GET("/getAuthorizer", open_platform.GetAuthorizer)
25 | apiRouter.GET("/getAuthorizerList", open_platform.GetAuthorizers)
26 | apiRouter.GET("/getAuthorizerOption", open_platform.GetAuthorizerOption)
27 | apiRouter.GET("/setAuthorizerOption", open_platform.SetAuthorizerOption)
28 |
29 | // delegate
30 | apiRouter.GET("/getAuthorizerOfficialAccount", open_platform.GetAuthorizerOfficialAccount)
31 | apiRouter.GET("/getAuthorizerOfficialAccountUser", open_platform.GetAuthorizerOfficialAccountUser)
32 | apiRouter.GET("/getAuthorizerMiniProgram", open_platform.GetAuthorizerMiniProgram)
33 | apiRouter.GET("/authorizerAccountCreate", open_platform.AuthorizerAccountCreate)
34 | apiRouter.GET("/authorizerAccountBind", open_platform.AuthorizerAccountBind)
35 | apiRouter.GET("/authorizerAccountUnbind", open_platform.AuthorizerAccountUnbind)
36 | apiRouter.GET("/authorizerAccountGet", open_platform.AuthorizerAccountGet)
37 | apiRouter.GET("/officialAccountDemo", open_platform.OfficialAccountDemo)
38 | apiRouter.GET("/miniProgramDemo", open_platform.MiniProgramDemo)
39 |
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/controllers/wecom/msg-audit.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 获取会话内容存档开启成员列表
11 | // https://open.work.weixin.qq.com/api/doc/90000/90135/91614
12 | func APIMsgAuditGetPermitUserList(c *gin.Context) {
13 |
14 | msgType := "1"
15 | res, err := services.WeComApp.MsgAudit.GetPermitUsersList(c.Request.Context(), msgType)
16 |
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | c.JSON(http.StatusOK, res)
22 | }
23 |
24 | // 获取会话同意情况
25 | // https://open.work.weixin.qq.com/api/doc/90000/90135/91782
26 | func APIMsgAuditCheckSingleAgree(c *gin.Context) {
27 | info := []*power.StringMap{
28 | {"userid": "XuJinSheng", "exteranalopenid": "wmeDKaCQAAGd9oGiQWxVsAKwV2HxNAAA"},
29 | {"userid": "XuJinSheng", "exteranalopenid": "wmeDKaCQAAIQ_p7ACn_jpLVBJSGocAAA"},
30 | {"userid": "XuJinSheng", "exteranalopenid": "wmeDKaCQAAPE_p7ABnxkpLBBJSGocAAA"},
31 | }
32 | res, err := services.WeComApp.MsgAudit.CheckSingleAgree(c.Request.Context(), info)
33 |
34 | if err != nil {
35 | panic(err)
36 | }
37 |
38 | c.JSON(http.StatusOK, res)
39 | }
40 |
41 | // 获取会话同意情况
42 | // https://open.work.weixin.qq.com/api/doc/90000/90135/91782
43 | func APIMsgAuditCheckRoomAgree(c *gin.Context) {
44 |
45 | roomID := c.DefaultQuery("roomID", "wrjc7bDwAASxc8tZvBErFE02BtPWyAAA")
46 |
47 | res, err := services.WeComApp.MsgAudit.CheckRoomAgree(c.Request.Context(), roomID)
48 |
49 | if err != nil {
50 | panic(err)
51 | }
52 |
53 | c.JSON(http.StatusOK, res)
54 | }
55 |
56 | func APIMsgAuditGroupChatGet(c *gin.Context) {
57 |
58 | roomID := c.DefaultQuery("roomID", "wrjc7bDwAASxc8tZvBErFE02BtPWyAAA")
59 | res, err := services.WeComApp.MsgAudit.GroupChatGet(c.Request.Context(), roomID)
60 |
61 | if err != nil {
62 | panic(err)
63 | }
64 |
65 | c.JSON(http.StatusOK, res)
66 | }
67 |
--------------------------------------------------------------------------------
/services/payment-service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/response"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
7 | "power-wechat-tutorial/config"
8 | )
9 |
10 | const TRANSACTION_SUCCESS = "TRANSACTION.SUCCESS"
11 | const TRANSACTION_FAILED = "TRANSACTION.FAILED"
12 |
13 | var PaymentApp *payment.Payment
14 |
15 | func NewWXPaymentApp(conf *config.Configuration) (*payment.Payment, error) {
16 |
17 | var cache kernel.CacheInterface
18 | if conf.MiniProgram.RedisAddr != "" {
19 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
20 | Addrs: []string{conf.MiniProgram.RedisAddr},
21 | })
22 | }
23 |
24 | Payment, err := payment.NewPayment(&payment.UserConfig{
25 | AppID: conf.Payment.AppID, // 小程序、公众号或者企业微信的appid
26 | MchID: conf.Payment.MchID, // 商户号 appID
27 | MchApiV3Key: conf.Payment.MchApiV3Key, //
28 | Key: conf.Payment.Key,
29 | CertPath: conf.Payment.CertPath,
30 | KeyPath: conf.Payment.KeyPath,
31 | SerialNo: conf.Payment.SerialNo,
32 | CertificateKeyPath: conf.Payment.CertificateKeyPath,
33 | WechatPaySerial: conf.Payment.WechatPaySerial,
34 | RSAPublicKeyPath: conf.Payment.RSAPublicKeyPath,
35 | NotifyURL: conf.Payment.NotifyURL,
36 | SubMchID: conf.Payment.SubMchID,
37 | SubAppID: conf.Payment.SubAppID,
38 | ResponseType: response.TYPE_MAP,
39 | Cache: cache,
40 | Log: payment.Log{
41 | Level: "debug",
42 | File: "./wechat.log",
43 | },
44 | Http: payment.Http{
45 | Timeout: 30.0,
46 | //BaseURI: "http://127.0.0.1:8888",
47 | BaseURI: "https://api.mch.weixin.qq.com",
48 | },
49 |
50 | HttpDebug: false,
51 | Debug: false,
52 | //Debug: true,
53 | })
54 |
55 | return Payment, err
56 | }
57 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/journal.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 批量获取汇报记录单号
11 | // https://work.weixin.qq.com/api/doc/90000/90135/93393
12 | func APIJournalGetRecordList(c *gin.Context) {
13 |
14 | starttime := 1606230000
15 | endtime := 1606361304
16 | cursor := 0
17 | limit := 10
18 | filters := []*power.StringMap{
19 | &power.StringMap{
20 | "key": "creator",
21 | "value": "kele",
22 | },
23 | &power.StringMap{
24 | "key": "department",
25 | "value": "1",
26 | },
27 | &power.StringMap{
28 | "key": "template_id",
29 | "value": "3TmALk1ogfgKiQE3e3jRwnTUhMTh8vca1N8zUVNUx",
30 | },
31 | }
32 |
33 | res, err := services.WeComApp.OAJournal.GetRecordList(c.Request.Context(), starttime, endtime, cursor, limit, filters)
34 |
35 | if err != nil {
36 | panic(err)
37 | }
38 |
39 | c.JSON(http.StatusOK, res)
40 | }
41 |
42 | // 获取汇报记录详情
43 | // https://work.weixin.qq.com/api/doc/90000/90135/93394
44 | func APIJournalGetRecordDetail(c *gin.Context) {
45 |
46 | journalUUID := c.DefaultQuery("journalUUID", "41eJejN57EJNzr8HrZfmKyCN7xwKw1qRxCZUxCVuo9fsWVMSKac6nk4q8rARTDaVNdx")
47 | res, err := services.WeComApp.OAJournal.GetRecordDetail(c.Request.Context(), journalUUID)
48 |
49 | if err != nil {
50 | panic(err)
51 | }
52 |
53 | c.JSON(http.StatusOK, res)
54 | }
55 |
56 | // 获取汇报记录详情
57 | // https://work.weixin.qq.com/api/doc/90000/90135/93395
58 | func APIJournalGetStatList(c *gin.Context) {
59 |
60 | templateID := c.DefaultQuery("templateID", "41eJejN57EJNzr8HrZfmKyCN7xwKw1qRxCZUxCVuo9fsWVMSKac6nk4q8rARTDaVNdx")
61 | startTime := 1604160000
62 | endTime := 1606363092
63 | res, err := services.WeComApp.OAJournal.GetStatList(c.Request.Context(), templateID, startTime, endTime)
64 |
65 | if err != nil {
66 | panic(err)
67 | }
68 |
69 | c.JSON(http.StatusOK, res)
70 | }
71 |
--------------------------------------------------------------------------------
/controllers/miniprogram/search.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 本接口提供基于小程序的站内搜商品图片搜索能力
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/search/search.imageSearch.html
12 | func APISearchImageSearch(c *gin.Context) {
13 |
14 | options := []*power.HashMap{
15 | &power.HashMap{
16 | "title": "image title1",
17 | "img_url": "",
18 | "price": "123",
19 | "path": "path",
20 | },
21 | &power.HashMap{
22 | "title": "image title2",
23 | "img_url": "",
24 | "price": "123",
25 | "path": "path",
26 | },
27 | }
28 |
29 | rs, err := services.MiniProgramApp.Search.ImageSearch(c.Request.Context(), options)
30 |
31 | if err != nil {
32 | panic(err)
33 | }
34 |
35 | c.JSON(http.StatusOK, rs)
36 | }
37 |
38 | // 小程序内部搜索API提供针对页面的查询能力
39 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/search/search.siteSearch.html
40 | func APISearchSiteSearch(c *gin.Context) {
41 |
42 | rs, err := services.MiniProgramApp.Search.SiteSearch(c.Request.Context(), "test", "pages/index/index")
43 |
44 | if err != nil {
45 | panic(err)
46 | }
47 |
48 | c.JSON(http.StatusOK, rs)
49 |
50 | }
51 |
52 | // 小程序开发者可以通过本接口提交小程序页面url及参数信息(不要推送webview页面)
53 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/search/search.submitPages.html
54 | func APISearchSubmitPages(c *gin.Context) {
55 | options := []*power.HashMap{
56 | &power.HashMap{
57 | "path": "pages/index/index",
58 | "query": "userName=wechat_user",
59 | },
60 | &power.HashMap{
61 | "path": "pages/video/index",
62 | "query": "vid=123",
63 | },
64 | }
65 |
66 | rs, err := services.MiniProgramApp.Search.SubmitPages(c.Request.Context(), options)
67 |
68 | if err != nil {
69 | panic(err)
70 | }
71 |
72 | c.JSON(http.StatusOK, rs)
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/controllers/miniprogram/wxacode.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "io"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 获取小程序二维码,适用于需要的码数量较少的业务场景
11 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.createQRCode.html
12 | func APIWXACodeCreateQRCode(c *gin.Context) {
13 |
14 | path, exist := c.GetQuery("path")
15 | if !exist {
16 | panic("parameter path expected")
17 | }
18 |
19 | rs, err := services.MiniProgramApp.WXACode.CreateQRCode(c.Request.Context(), path, 430)
20 |
21 | if err != nil {
22 | panic(err)
23 | }
24 |
25 | io.Copy(c.Writer, rs.Body)
26 | }
27 |
28 | // 获取小程序码,适用于需要的码数量较少的业务场景
29 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.get.html
30 | func APIWXACodeGet(c *gin.Context) {
31 |
32 | path, exist := c.GetQuery("path")
33 | if !exist {
34 | panic("parameter path expected")
35 | }
36 |
37 | rs, err := services.MiniProgramApp.WXACode.Get(
38 | c.Request.Context(),
39 | path,
40 | 430,
41 | false,
42 | &power.HashMap{
43 | "r": 0,
44 | "g": 0,
45 | "b": 0,
46 | },
47 | false,
48 | "develop",
49 | )
50 |
51 | if err != nil {
52 | panic(err)
53 | }
54 |
55 | io.Copy(c.Writer, rs.Body)
56 | }
57 |
58 | // 获取小程序码,适用于需要的码数量极多的业务场景
59 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html
60 | func APIWXACodeGetUnlimited(c *gin.Context) {
61 |
62 | page, exist := c.GetQuery("page")
63 | if !exist {
64 | panic("parameter page expected")
65 | }
66 |
67 | rs, err := services.MiniProgramApp.WXACode.GetUnlimited(
68 | c.Request.Context(),
69 | "a=1&b=123",
70 | page,
71 | false,
72 | "develop",
73 | 430,
74 | false,
75 | &power.HashMap{
76 | "r": 0,
77 | "g": 0,
78 | "b": 0,
79 | },
80 | false,
81 | )
82 |
83 | if err != nil {
84 | panic(err)
85 | }
86 |
87 | io.Copy(c.Writer, rs.Body)
88 | }
89 |
--------------------------------------------------------------------------------
/controllers/payment/profitSharing.go:
--------------------------------------------------------------------------------
1 | package payment
2 |
3 | import (
4 | "encoding/base64"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/profitSharing/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | func APIOrders(c *gin.Context) {
12 |
13 | transactionID := c.DefaultQuery("transactionID", "")
14 | outOrderNo := c.DefaultQuery("OutRefundNo", "")
15 |
16 | config := services.MiniProgramApp.GetConfig()
17 |
18 | cipherdata, err := services.PaymentApp.Base.BaseClient.RsaOAEP.RSAEncryptor.Encrypt([]byte("hu89ohu89ohu89o"))
19 | bufferEncryptedName := base64.StdEncoding.EncodeToString(cipherdata)
20 | if err != nil {
21 | panic(err)
22 | }
23 |
24 | para := &request.RequestShare{
25 | AppID: config.GetString("app_id", ""),
26 | TransactionID: transactionID,
27 | OutOrderNO: outOrderNo,
28 | Receivers: []*request.Receiver{
29 | &request.Receiver{
30 | Type: "MERCHANT_ID",
31 | Account: "86693852",
32 | Name: string(bufferEncryptedName),
33 | Amount: 300,
34 | Description: "分给商户A",
35 | },
36 | },
37 | //UnfreezeUnSplit: true,
38 | UnfreezeUnSplit: false,
39 | }
40 |
41 | rs, err := services.PaymentApp.ProfitSharing.Share(c.Request.Context(), para)
42 | if err != nil {
43 | panic(err)
44 | }
45 | c.JSON(http.StatusOK, rs)
46 |
47 | }
48 |
49 | func APIAddReceiver(c *gin.Context) {
50 |
51 | cipherdata, err := services.PaymentApp.Base.BaseClient.RsaOAEP.RSAEncryptor.Encrypt([]byte("hu89ohu89ohu89o"))
52 | bufferEncryptedName := base64.StdEncoding.EncodeToString(cipherdata)
53 |
54 | receiverType := "MERCHANT_ID"
55 | account := "86693852"
56 | name := bufferEncryptedName
57 | relationType := "STAFF"
58 | customRelation := "分给商户A"
59 |
60 | rs, err := services.PaymentApp.ProfitSharing.AddReceiver(c.Request.Context(),
61 | receiverType, account, name,
62 | relationType, customRelation)
63 | if err != nil {
64 | panic(err)
65 | }
66 | c.JSON(http.StatusOK, rs)
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/controllers/payment/transfer/transfer.go:
--------------------------------------------------------------------------------
1 | package transfer
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/transfer/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | func APIToTransfer(c *gin.Context) {
11 |
12 | mchId := services.PaymentApp.GetConfig().GetString("mch_id", "")
13 | appId := services.PaymentApp.GetConfig().GetString("app_id", "")
14 |
15 | options := &request.RequestTransferToBalance{
16 | MchAppID: appId,
17 | MchID: mchId,
18 | DeviceInfo: "",
19 | NonceStr: "",
20 | PartnerTradeNo: "0010010404201411170000046545",
21 | OpenID: "o4QEk5Kc_y8QTrENCpKoxYhS4jkg",
22 | CheckName: "NO_CHECK",
23 | ReUserName: "",
24 | Amount: 30,
25 | Desc: "活动奖励",
26 | SpBillCreateIP: "",
27 | Scene: "",
28 | BrandID: 0,
29 | FinderTemplateID: "",
30 | }
31 |
32 | payConf, err := services.PaymentApp.Transfer.ToBalance(c.Request.Context(), options)
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.XML(http.StatusOK, payConf)
38 | }
39 |
40 | func APIQueryBalanceOrder(c *gin.Context) {
41 |
42 | rs, err := services.PaymentApp.Transfer.QueryBalanceOrder(c.Request.Context(), "0010010404201411170000046545")
43 | if err != nil {
44 | panic(nil)
45 | }
46 |
47 | c.JSON(http.StatusOK, rs)
48 | }
49 |
50 | func APIToBankCard(c *gin.Context) {
51 |
52 | mchId := services.PaymentApp.GetConfig().GetString("mch_id", "")
53 |
54 | options := &request.RequestToBankCard{
55 | MchID: mchId,
56 | BankCode: "0010010404201411170000046545",
57 | Amount: 30,
58 | Desc: "活动奖励",
59 | EncBankNO: "123213",
60 | EncTrueName: "321312",
61 | NonceStr: "1231232131",
62 | PartnerTradeNO: "213313213212",
63 | }
64 |
65 | payConf, err := services.PaymentApp.Transfer.ToBankCard(c.Request.Context(), options)
66 | if err != nil {
67 | panic(err)
68 | }
69 |
70 | c.XML(http.StatusOK, payConf)
71 | }
72 |
--------------------------------------------------------------------------------
/controllers/wecom/user/linked-corp.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // APILinkedCorpAgentGetPermList 获取应用的可见范围
10 | // https://open.work.weixin.qq.com/api/doc/90000/90135/93172
11 | func APILinkedCorpAgentGetPermList(c *gin.Context) {
12 |
13 | res, err := services.WeComApp.UserLinkedCorp.GetPermList(c.Request.Context())
14 | if err != nil {
15 | panic(err)
16 | }
17 | c.JSON(http.StatusOK, res)
18 | }
19 |
20 | // APILinkedCorpUserGet 获取互联企业成员详细信息
21 | // https://work.weixin.qq.com/api/doc/90000/90135/93171
22 | func APILinkedCorpUserGet(c *gin.Context) {
23 | userID := c.DefaultQuery("userID", "walle")
24 | res, err := services.WeComApp.UserLinkedCorp.GetUser(c.Request.Context(), userID)
25 | if err != nil {
26 | panic(err)
27 | }
28 | c.JSON(http.StatusOK, res)
29 | }
30 |
31 | // APILinkedCorpUserSimpleList 获取互联企业部门成员
32 | // https://work.weixin.qq.com/api/doc/90000/90135/93168
33 | func APILinkedCorpUserSimpleList(c *gin.Context) {
34 | departmentID := c.DefaultQuery("departmentID", "xxx")
35 | res, err := services.WeComApp.UserLinkedCorp.GetUserSimpleList(c.Request.Context(), departmentID, true)
36 | if err != nil {
37 | panic(err)
38 | }
39 | c.JSON(http.StatusOK, res)
40 |
41 | }
42 |
43 | // APILinkedCorpUserList 获取互联企业部门成员详情
44 | // https://work.weixin.qq.com/api/doc/90000/90135/93169
45 | func APILinkedCorpUserList(c *gin.Context) {
46 | departmentID := c.DefaultQuery("departmentID", "xxx")
47 | res, err := services.WeComApp.UserLinkedCorp.GetUserList(c.Request.Context(), departmentID, true)
48 | if err != nil {
49 | panic(err)
50 | }
51 | c.JSON(http.StatusOK, res)
52 | }
53 |
54 | // APILinkedCorpDepartmentList 获取互联企业部门列表
55 | // https://work.weixin.qq.com/api/doc/90000/90135/93170
56 | func APILinkedCorpDepartmentList(c *gin.Context) {
57 | departmentID := c.DefaultQuery("departmentID", "xxx")
58 | res, err := services.WeComApp.UserLinkedCorp.GetDepartmentList(c.Request.Context(), departmentID)
59 | if err != nil {
60 | panic(err)
61 | }
62 | c.JSON(http.StatusOK, res)
63 | }
64 |
--------------------------------------------------------------------------------
/controllers/wecom/user/user-callback.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerLibs/v3/http/helper"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
8 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/server/handlers/models"
9 | "github.com/gin-gonic/gin"
10 | "io/ioutil"
11 | "net/http"
12 | "power-wechat-tutorial/services"
13 | )
14 |
15 | // 回调配置
16 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
17 | func CallbackVerify(c *gin.Context) {
18 | rs, err := services.WeComContactApp.Server.Serve(c.Request)
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | text, _ := ioutil.ReadAll(rs.Body)
24 | c.String(http.StatusOK, string(text))
25 |
26 | }
27 |
28 | // 回调配置
29 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
30 | func CallbackNotify(c *gin.Context) {
31 |
32 | rs, err := services.WeComContactApp.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
33 | fmt.Dump("event", event)
34 |
35 | // 这里需要获取到事件类型,然后把对应的结构体传递进去进一步解析
36 | // 所有包含的结构体请参考: https://github.com/ArtisanCloud/PowerWeChat/tree/master/src/work/server/handlers/models
37 | if event.GetEvent() == models.CALLBACK_EVENT_CHANGE_CONTACT && event.GetChangeType() == models.CALLBACK_EVENT_CHANGE_TYPE_CREATE_PARTY {
38 | //msg := models.EventPartyCreate{}
39 | msg := models.EventKFMsgOrEvent{}
40 | err := event.ReadMessage(&msg)
41 | if err != nil {
42 | println(err.Error())
43 | return "error"
44 | }
45 | fmt.Dump(msg)
46 | }
47 |
48 | // 假设员工给应用发送消息,这里可以直接回复消息文本,
49 | // return "I'm recv..."
50 |
51 | // 这里回复success告诉微信我收到了,后续需要回复用户信息可以主动调发消息接口
52 | return kernel.SUCCESS_EMPTY_RESPONSE
53 | })
54 | if err != nil {
55 | panic(err)
56 | }
57 |
58 | // 选择1: 直接把gin context writer传入,会自动回复。
59 | err = helper.HttpResponseSend(rs, c.Writer)
60 | if err != nil {
61 | panic(err)
62 | }
63 |
64 | // 选择2: 或者是把内容读取出来,回复
65 | //text, _ := ioutil.ReadAll(rs.Body)
66 | //c.String(http.StatusOK, string(text))
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "log"
6 | "power-wechat-tutorial/config"
7 | "power-wechat-tutorial/routes"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | var Host = ""
12 | var Port = "8888"
13 |
14 | // @title PowerWechat API Docs
15 | // @version 1.0.1
16 | // @description 这是一个开源的使用教程,包含PowerWechat的大部分接口调试代码.
17 | // @termsOfService http://artisan-cloud.com/terms/
18 |
19 | // @contact.name ArtisanCloud Support
20 | // @contact.url https://powerwechat.artisan-cloud.com/zh/start/qa.html
21 | // @contact.email matrix-x@artisan-cloud.como
22 |
23 | // @license.name MIT 2.0
24 | // @license.url https://github.com/ArtisanCloud/PowerWeChat?tab=MIT-1-ov-file#readme
25 |
26 | // @host localhost:8080
27 | // @BasePath /api/v1
28 |
29 | // @securityDefinitions.basic BasicAuth
30 |
31 | // @externalDocs.description OpenAPI
32 | // @externalDocs.url https://swagger.io/resources/open-api/
33 | func main() {
34 | conf := config.Get()
35 |
36 | var err error
37 | services.PaymentApp, err = services.NewWXPaymentApp(conf)
38 | if err != nil || services.PaymentApp == nil {
39 | panic(err)
40 | }
41 |
42 | services.MiniProgramApp, err = services.NewMiniMiniProgramService(conf)
43 | if err != nil || services.MiniProgramApp == nil {
44 | panic(err)
45 | }
46 |
47 | services.OfficialAccountApp, err = services.NewOfficialAccountAppService(conf)
48 | if err != nil || services.OfficialAccountApp == nil {
49 | panic(err)
50 | }
51 |
52 | services.WeComApp, err = services.NewWeComService(conf)
53 | if err != nil || services.WeComApp == nil {
54 | panic(err)
55 | }
56 |
57 | services.WeComContactApp, err = services.NewWeComContactService(conf)
58 | if err != nil || services.WeComContactApp == nil {
59 | panic(err)
60 | }
61 |
62 | services.OpenPlatformApp, err = services.NewOpenPlatformAppService(conf)
63 | if err != nil || services.OpenPlatformApp == nil {
64 | panic(err)
65 | }
66 |
67 | r := gin.Default()
68 |
69 | // Initialize the routes
70 | routes.InitializeRoutes(r)
71 |
72 | log.Fatalln(r.Run(Host + ":" + Port))
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/controllers/wecom/account-service/message.go:
--------------------------------------------------------------------------------
1 | package account_service
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/accountService/message/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 读取消息
11 | // https://work.weixin.qq.com/api/doc/90000/90135/94670
12 | func APIAccountServiceSyncMsg(c *gin.Context) {
13 |
14 | cursor := c.DefaultQuery("cursor", "4gw7MepFLfgF2VC5npN")
15 | token := c.DefaultQuery("token", "ENCApHxnGDNAVNY4AaSJKj4Tb5mwsEMzxhFmHVGcra996NR")
16 | limit := 1000
17 |
18 | res, err := services.WeComApp.AccountServiceMessage.SyncMsg(c.Request.Context(), cursor, token, limit, 0, "")
19 |
20 | if err != nil {
21 | panic(err)
22 | }
23 |
24 | c.JSON(http.StatusOK, res)
25 | }
26 |
27 | // 发送消息
28 | // https://work.weixin.qq.com/api/doc/90000/90135/90236
29 | func APIAccountServiceSendMsg(c *gin.Context) {
30 |
31 | options := &request.RequestAccountServiceSendMsg{
32 | ToUser: c.DefaultQuery("toUser", "EXTERNAL_USERID"),
33 | OpenKfid: c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx"),
34 | MsgID: c.DefaultQuery("msgID", "MSGID"),
35 | MsgType: "text",
36 | Text: &request.RequestAccountServiceMsgText{
37 | Content: "你购买的物品已发货,可点击链接查看物流状态http://work.weixin.qq.com/xxxxxx",
38 | },
39 | }
40 |
41 | res, err := services.WeComApp.AccountServiceMessage.SendMsg(c.Request.Context(), options)
42 |
43 | if err != nil {
44 | panic(err)
45 | }
46 |
47 | c.JSON(http.StatusOK, res)
48 | }
49 |
50 | // 发送消息
51 | // https://work.weixin.qq.com/api/doc/90000/90135/95122
52 | func APIAccountServiceSendMsgOnEvent(c *gin.Context) {
53 |
54 | options := &request.RequestAccountServiceSendMsgOnEvent{
55 | Code: c.DefaultQuery("code", "CODE"),
56 | MsgID: c.DefaultQuery("msgID", "MSG_ID"),
57 | MsgType: "text", // 对应的消息体字段,目前支持文本与菜单消息,详见下文
58 | Text: request.RequestAccountServiceMsgText{
59 | Content: "欢迎咨询",
60 | },
61 | }
62 |
63 | res, err := services.WeComApp.AccountServiceMessage.SendMsgOnEvent(c.Request.Context(), options)
64 |
65 | if err != nil {
66 | panic(err)
67 | }
68 |
69 | c.JSON(http.StatusOK, res)
70 | }
71 |
--------------------------------------------------------------------------------
/controllers/wecom/message/message-callback.go:
--------------------------------------------------------------------------------
1 | package message
2 |
3 | import (
4 | "encoding/xml"
5 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
6 | "github.com/ArtisanCloud/PowerLibs/v3/http/helper"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
8 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
9 | models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
10 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/server/handlers/models"
11 | "github.com/gin-gonic/gin"
12 | "power-wechat-tutorial/services"
13 | )
14 |
15 | func TestBuffer(c *gin.Context) {
16 |
17 | textXML := "163440105270196990678405619241000008"
18 | var md interface{}
19 | md2 := models.MessageText{}
20 | err := xml.Unmarshal([]byte(textXML), &md2)
21 | md = md2
22 | fmt.Dump(md, err)
23 | }
24 |
25 | // 回调配置
26 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
27 | func CallbackVerify(c *gin.Context) {
28 | rs, err := services.WeComApp.Server.Serve(c.Request)
29 | if err != nil {
30 | panic(err)
31 | }
32 |
33 | // 选择1
34 | //text, _ := ioutil.ReadAll(rs.Body)
35 | //c.String(http.StatusOK, string(text))
36 |
37 | // 选择2
38 | err = helper.HttpResponseSend(rs, c.Writer)
39 |
40 | }
41 |
42 | // 回调配置
43 | // https://work.weixin.qq.com/api/doc/90000/90135/90930
44 | func CallbackNotify(c *gin.Context) {
45 |
46 | rs, err := services.WeComApp.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
47 | fmt.Dump("event", event)
48 | //return "handle callback"
49 |
50 | switch event.GetMsgType() {
51 | case models2.CALLBACK_MSG_TYPE_TEXT:
52 | msg := models.MessageText{}
53 | err := event.ReadMessage(&msg)
54 | if err != nil {
55 | println(err.Error())
56 | return "error"
57 | }
58 | fmt.Dump(msg)
59 | }
60 |
61 | return kernel.SUCCESS_EMPTY_RESPONSE
62 |
63 | })
64 | if err != nil {
65 | panic(err)
66 | }
67 |
68 | err = helper.HttpResponseSend(rs, c.Writer)
69 |
70 | if err != nil {
71 | panic(err)
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PowerWeChat Tutorial 使用实例
2 |
3 | 我们单独写了一个项目[PowerWechatTutorial](https://github.com/ArtisanCloud/PowerWechatTutorial),基本上覆盖了所有的API使用,希望能够帮助大家更快的上手Golang WeChat开发。
4 |
5 | ### 下载项目
6 | ```bash
7 | git clone https://github.com/ArtisanCloud/PowerWechatTutorial.git
8 | ```
9 |
10 | ### Insomnia配置(可选)
11 | 可以下载insomnia的配置文件,便于在Tutorial里使用insomnia调试。
12 | [insomnia.json](./insomnia.json)
13 |
14 | ### 项目配置
15 | 在项目根目录下,新建一个`config.yaml`, 把下面字段内容复制进去, 然后执行`go run main.go`。
16 | 如果程序正常启动,访问 [http://localhost:8888](http://localhost:8888) 会返回一个`Hello, PowerWechat`
17 |
18 | ```yaml
19 | # 微信支付配置文档: https://powerwechat.artisan-cloud.com/zh/payment/index.html#userconfig%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E%EF%BC%9A
20 | payment:
21 | appid: xxxxx # 公众号appid、小程序appid、企业微信corpid等
22 | mchid: 100000 # ArtisanCloud商户号
23 | certpath: certs/apiclient_cert.pem # 证书路径
24 | keypath: certs/apiclient_key.pem # 证书私钥路径
25 | serialno: 55D06F99FF64CF1759FDE5B77A0BEC8B67A78C2E
26 | key: xxxxx
27 | mchapiv3key: xxxxx
28 | notifyurl: https://www.artisancloud.cn
29 | redisaddr: localhost:6379
30 |
31 | # 小程序配置文档: https://powerwechat.artisan-cloud.com/zh/mini-program/index.html
32 | miniprogram:
33 | appid: xxxxx
34 | secret: xxxxx
35 | redisaddr: localhost:6379
36 | messagetoken: xxxxx
37 | messageaeskey: xxxxx
38 |
39 | # 企业微信配置文档: https://powerwechat.artisan-cloud.com/zh/wecom/index.html
40 | wecom:
41 | corpid: ww454dfb9d6f6d432a
42 |
43 | # ----- powerwechat-docs-demo start ------
44 | agent_id: 1000000
45 | secret: xxxxx
46 | messagetoken: xxxxx
47 | messageaeskey: xxxxx
48 | messagecallback: https://www.artisan-cloud.com/message/callback
49 | oauthcallback: https://www.artisan-cloud.com/oauth/callback
50 | # ----- powerwechat-docs-demo end ------
51 |
52 | # 联系人配置
53 | contactsecret: xxxxx
54 | contacttoken: xxxxx
55 | contactaeskey: xxxxx
56 | contactcallback: https://www.artisan-cloud.com/api/wx/customer
57 |
58 | redisaddr: localhost:6379
59 |
60 | # 公众号配置文档: https://powerwechat.artisan-cloud.com/zh/official-account/index.html
61 | offiaccount:
62 | appid: xxxxx
63 | appsecret: xxxxx
64 | redisaddr: localhost:6379
65 | messagetoken: xxxxx
66 | messageaeskey: xxxxx
67 | ```
68 |
69 | 在`main.go`里面,你可以选择性的注释掉不需要的模块,避免没有配置的时候报错影响运行。
70 |
--------------------------------------------------------------------------------
/docs/swagger.yaml:
--------------------------------------------------------------------------------
1 | basePath: /api/v1
2 | externalDocs:
3 | description: OpenAPI
4 | url: https://swagger.io/resources/open-api/
5 | host: localhost:8080
6 | info:
7 | contact:
8 | email: matrix-x@artisan-cloud.como
9 | name: ArtisanCloud Support
10 | url: https://powerwechat.artisan-cloud.com/zh/start/qa.html
11 | description: 这是一个开源的使用教程,包含PowerWechat的大部分接口调试代码.
12 | license:
13 | name: MIT 2.0
14 | url: https://github.com/ArtisanCloud/PowerWeChat?tab=MIT-1-ov-file#readme
15 | termsOfService: http://artisan-cloud.com/terms/
16 | title: PowerWechat API Docs
17 | version: 1.0.1
18 | paths:
19 | /clearQuota:
20 | get:
21 | description: "\nSDK产品接口的代码展示:\n```\n返回类型定义如下:\ntype ResponseOfficialAccount
22 | struct {\n\tResponseBase\n\n\tErrCode int `json:\"errcode,omitempty\"`\n\tErrMsg
23 | \ string `json:\"errmsg,omitempty\"`\n\n\tResultCode string `json:\"resultcode,omitempty\"`\n\tResultMsg
24 | \ string `json:\"resultmsg,omitempty\"`\n}\n\n具体使用接口方式:\nfunc ClearQuota(ctx
25 | *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.ClearQuota(ctx.Request.Context())\n\tif
26 | err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK,
27 | data)\n}\n```"
28 | responses: {}
29 | summary: 公众号里清空api的调用quota:https://developers.weixin.qq.com/doc/offiaccount/openApi/clear_quota.html
30 | tags:
31 | - OfficialAccount.base.ClearQuota
32 | /getCallbackIp:
33 | get:
34 | description: "\nSDK产品接口的代码展示:\n```\n返回类型定义如下:\ntype ResponseGetCallBackIP struct
35 | {\n\tresponse.ResponseOfficialAccount\n\n\tIPList []string `json:\"ip_list\"`\n}\n\n具体使用接口方式:\nfunc
36 | GetCallbackIP(ctx *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.GetCallbackIP(ctx.Request.Context())\n\tif
37 | err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK,
38 | data)\n}\n```"
39 | responses: {}
40 | summary: 获取公众号回调的IP地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
41 | tags:
42 | - OfficialAccount.base.GetCallbackIP
43 | securityDefinitions:
44 | BasicAuth:
45 | type: basic
46 | swagger: "2.0"
47 |
--------------------------------------------------------------------------------
/controllers/miniprogram/uniform-message.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/uniformMessage/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 下发小程序和公众号统一的服务消息
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
13 | func APIUniformMessageSend(c *gin.Context) {
14 |
15 | openID, exist := c.GetQuery("openID")
16 | if !exist {
17 | panic("parameter open id expected")
18 | }
19 |
20 | rs, err := services.MiniProgramApp.UniformMessage.Send(c.Request.Context(),
21 | &request.RequestUniformMessageSend{
22 | ToUser: openID,
23 | WeAppTemplateMsg: &request.WeAppTemplateMsg{
24 | TemplateID: "TEMPLATE_ID",
25 | Page: "page/page/index",
26 | FormID: "FORMID",
27 | Data: &power.HashMap{
28 | "keyword1": &power.HashMap{
29 | "value": "339208499",
30 | },
31 | "keyword2": &power.HashMap{
32 | "value": "2015年01月05日 12:30",
33 | },
34 | "keyword3": &power.HashMap{
35 | "value": "腾讯微信总部",
36 | },
37 | "keyword4": &power.HashMap{
38 | "value": "广州市海珠区新港中路397号",
39 | },
40 | },
41 | EmphasisKeyword: "keyword1.DATA",
42 | },
43 | MpTemplateMsg: &request.MPTemplateMsg{
44 | AppID: services.MiniProgramApp.GetConfig().GetString("app_id", ""),
45 | TemplateID: "MTvUCMmZfl-Dv66C5fVWdf4zPJkYSaRbnrtk6DXIfTQ",
46 | Url: "https://weixin.qq.com/download",
47 | MiniProgram: &request.MPTemplateMsgMiniProgram{
48 | AppID: "",
49 | PagePath: "",
50 | },
51 | Data: &power.HashMap{
52 | "first": &power.HashMap{
53 | "value": "恭喜你购买成功!",
54 | "color": "#173177",
55 | },
56 | "keyword1": &power.HashMap{
57 | "value": "巧克力",
58 | "color": "#173177",
59 | },
60 | "keyword2": &power.HashMap{
61 | "value": "39.8元",
62 | "color": "#173177",
63 | },
64 | "keyword3": &power.HashMap{
65 | "value": "2014年9月22日",
66 | "color": "#173177",
67 | },
68 | "remark": &power.HashMap{
69 | "value": "欢迎再次购买!",
70 | "color": "#173177",
71 | },
72 | },
73 | },
74 | })
75 |
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | c.JSON(http.StatusOK, rs)
81 | }
82 |
--------------------------------------------------------------------------------
/controllers/wecom/invoice.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/invoice/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 查询电子发票
11 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90284
12 | func APIInvoiceReimburseGetInvoiceInfo(c *gin.Context) {
13 | cardID := c.DefaultQuery("cardID", "CARDID")
14 | encryptCode := c.DefaultQuery("encryptCode", "ENCRYPTCODE")
15 |
16 | res, err := services.WeComApp.Invoice.GetInvoiceInfo(c.Request.Context(), cardID, encryptCode)
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, res)
23 | }
24 |
25 | // 更新发票状态
26 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90285
27 | func APIInvoiceReimburseUpdateInvoiceStatus(c *gin.Context) {
28 | cardID := c.DefaultQuery("cardID", "CARDID")
29 | encryptCode := c.DefaultQuery("encryptCode", "ENCRYPTCODE")
30 | reimburseStatus := c.DefaultQuery("status", "INVOICE_REIMBURSE_INIT")
31 |
32 | res, err := services.WeComApp.Invoice.UpdateInvoiceStatus(c.Request.Context(), cardID, encryptCode, reimburseStatus)
33 |
34 | if err != nil {
35 | panic(err)
36 | }
37 |
38 | c.JSON(http.StatusOK, res)
39 | }
40 |
41 | // 批量更新发票状态
42 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90286
43 | func APIInvoiceReimburseUpdateStatusBatch(c *gin.Context) {
44 | openID := c.DefaultQuery("openID", "OPENID")
45 | reimburseStatus := c.DefaultQuery("status", "INVOICE_REIMBURSE_INIT")
46 | invoiceList := []*request.RequestCardInvoice{
47 | {CardID: "cardid_1", EncryptCode: "encrypt_code_1"},
48 | {CardID: "cardid_2", EncryptCode: "encrypt_code_2"},
49 | }
50 |
51 | res, err := services.WeComApp.Invoice.UpdateStatusBatch(c.Request.Context(), openID, reimburseStatus, invoiceList)
52 |
53 | if err != nil {
54 | panic(err)
55 | }
56 |
57 | c.JSON(http.StatusOK, res)
58 | }
59 |
60 | // 批量查询电子发票
61 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90287
62 | func APIInvoiceReimburseGetInvoiceInfoBatch(c *gin.Context) {
63 |
64 | invoiceList := []*request.RequestCardInvoice{
65 | {CardID: "CARDID1", EncryptCode: "ENCRYPTCODE1"},
66 | {CardID: "CARDID2", EncryptCode: "ENCRYPTCODE2"},
67 | }
68 | res, err := services.WeComApp.Invoice.GetInvoiceInfoBatch(c.Request.Context(), invoiceList)
69 |
70 | if err != nil {
71 | panic(err)
72 | }
73 |
74 | c.JSON(http.StatusOK, res)
75 | }
76 |
--------------------------------------------------------------------------------
/services/wecom-service.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/logger/drivers"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work"
7 | "power-wechat-tutorial/config"
8 | )
9 |
10 | var WeComApp *work.Work
11 | var WeComContactApp *work.Work
12 |
13 | func NewWeComService(conf *config.Configuration) (*work.Work, error) {
14 | var cache kernel.CacheInterface
15 | if conf.WeCom.RedisAddr != "" {
16 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
17 | Addrs: []string{conf.MiniProgram.RedisAddr},
18 | })
19 | }
20 |
21 | app, err := work.NewWork(&work.UserConfig{
22 | CorpID: conf.WeCom.CorpID, // 企业微信的corp id,所有企业微信共用一个。
23 | AgentID: conf.WeCom.AgentID, // 内部应用的app id
24 | Secret: conf.WeCom.Secret, // 内部应用的app secret
25 | Token: conf.WeCom.MessageToken, // 内部应用的app token
26 | AESKey: conf.WeCom.MessageAesKey, // 内部应用的app aeskey
27 | CallbackURL: conf.WeCom.MessageCallback, // 内部应用的场景回调设置
28 | OAuth: work.OAuth{
29 | Callback: conf.WeCom.OAuthCallback, // 内部应用的app oauth url
30 | Scopes: []string{"snsapi_base"},
31 | },
32 | Log: work.Log{
33 | Driver: &drivers.DummyLogger{},
34 | Level: "debug",
35 | //Level: "off",
36 | File: "./wechat.log",
37 | },
38 | Cache: cache,
39 | HttpDebug: true,
40 | Debug: false,
41 | })
42 |
43 | return app, err
44 | }
45 |
46 | func NewWeComContactService(conf *config.Configuration) (*work.Work, error) {
47 | var cache kernel.CacheInterface
48 | if conf.MiniProgram.RedisAddr != "" {
49 | cache = kernel.NewRedisClient(&kernel.UniversalOptions{
50 | Addrs: []string{conf.MiniProgram.RedisAddr},
51 | })
52 | }
53 |
54 | app, err := work.NewWork(&work.UserConfig{
55 | CorpID: conf.WeCom.CorpID, // 企业微信的app id,所有企业微信共用一个。
56 | AgentID: conf.WeCom.AgentID, // 内部应用的app id
57 | Secret: conf.WeCom.Secret, // 内部应用的app secret
58 | Token: conf.WeCom.ContactToken, // 内部应用的app token
59 | AESKey: conf.WeCom.ContactAESKey, // 内部应用的app aeskey
60 | CallbackURL: conf.WeCom.ContactCallback, // 内部应用的场景回调设置
61 | OAuth: work.OAuth{
62 | Callback: conf.WeCom.OAuthCallback, // 内部应用的app oauth url
63 | Scopes: nil,
64 | },
65 | HttpDebug: true,
66 | Debug: false,
67 | Cache: cache,
68 | })
69 | return app, err
70 | }
71 |
--------------------------------------------------------------------------------
/controllers/open-platform/server.go:
--------------------------------------------------------------------------------
1 | package open_platform
2 |
3 | import (
4 | "bytes"
5 | "encoding/xml"
6 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
8 | openplatform "github.com/ArtisanCloud/PowerWeChat/v3/src/openPlatform/server/callbacks"
9 | "github.com/gin-gonic/gin"
10 | "io"
11 | "io/ioutil"
12 | "power-wechat-tutorial/services"
13 | )
14 |
15 | // 如果要上生产或者一直使用,需要保证开起推送ticket,在这里接受ticket,PowerWechat会一直帮你缓存最新的ticket。
16 | func APIOpenPlatformCallback(context *gin.Context) {
17 |
18 | requestXML, _ := io.ReadAll(context.Request.Body)
19 | context.Request.Body = io.NopCloser(bytes.NewBuffer(requestXML))
20 | println(string(requestXML))
21 |
22 | var err error
23 |
24 | rs, err := services.OpenPlatformApp.Server.Notify(context.Request, func(event *openplatform.Callback, decrypted []byte, infoType string) (result interface{}) {
25 |
26 | result = kernel.SUCCESS_EMPTY_RESPONSE
27 |
28 | switch infoType {
29 | case openplatform.EVENT_COMPONENT_VERIFY_TICKET:
30 | msg := &openplatform.EventVerifyTicket{}
31 | err = xml.Unmarshal(decrypted, msg)
32 | //fmt.Dump(event)
33 | if err != nil {
34 | return err
35 | }
36 | // set ticket in redis
37 | err = services.OpenPlatformApp.VerifyTicket.SetTicket(msg.ComponentVerifyTicket)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | fmt.Dump(msg)
43 | case openplatform.EVENT_AUTHORIZED:
44 |
45 | }
46 | return result
47 | })
48 |
49 | if err != nil {
50 | panic(err)
51 | }
52 |
53 | err = rs.Write(context.Writer)
54 | if err != nil {
55 | panic(err)
56 | }
57 |
58 | }
59 |
60 | func APIOpenPlatformCallbackWithApp(context *gin.Context) {
61 |
62 | requestXML, _ := ioutil.ReadAll(context.Request.Body)
63 | context.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestXML))
64 | println(string(requestXML))
65 |
66 | var err error
67 |
68 | rs, err := services.OpenPlatformApp.Server.Notify(context.Request, func(event *openplatform.Callback, decrypted []byte, infoType string) (result interface{}) {
69 |
70 | result = kernel.SUCCESS_EMPTY_RESPONSE
71 | //fmt.Dump(event)
72 | msg := &openplatform.EventAuthorization{}
73 | err = xml.Unmarshal(decrypted, msg)
74 | if err != nil {
75 | return err
76 | }
77 | fmt.Dump(msg)
78 |
79 | return result
80 | })
81 |
82 | if err != nil {
83 | panic(err)
84 | }
85 |
86 | err = rs.Write(context.Writer)
87 | if err != nil {
88 | panic(err)
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/controllers/wecom/account-service/account-service.go:
--------------------------------------------------------------------------------
1 | package account_service
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/accountService/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 添加客服帐号
11 | // https://work.weixin.qq.com/api/doc/90000/90135/94648
12 | func APIAccountServiceAccountAdd(c *gin.Context) {
13 |
14 | mediaID := c.DefaultQuery("mediaID", "294DpAog3YA5b9rTK4PjjfRfYLO0L5qpDHAJIzhhQ2jAEWjb9i661Q4lk8oFnPtmj")
15 |
16 | res, err := services.WeComApp.AccountService.Add(c.Request.Context(), "新建的客服帐号", mediaID)
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, res)
23 | }
24 |
25 | // 删除客服帐号
26 | // https://work.weixin.qq.com/api/doc/90000/90135/94663
27 | func APIAccountServiceAccountDel(c *gin.Context) {
28 |
29 | openKFID := c.DefaultQuery("openKFID", "wkAJ2GCAAAZSfhHCt7IFSvLKtMPxyJTw")
30 |
31 | res, err := services.WeComApp.AccountService.Del(c.Request.Context(), openKFID)
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, res)
38 | }
39 |
40 | // 修改客服帐号
41 | // https://work.weixin.qq.com/api/doc/90000/90135/94664
42 | func APIAccountServiceAccountUpdate(c *gin.Context) {
43 |
44 | options := &request.RequestAccountUpdate{
45 | OpenKFID: c.DefaultQuery("openKFID", "wkAJ2GCAAAZSfhHCt7IFSvLKtMPxyJTw"),
46 | Name: "修改客服名",
47 | MediaID: c.DefaultQuery("mediaID", "294DpAog3YA5b9rTK4PjjfRfYLO0L5qpDHAJIzhhQ2jAEWjb9i661Q4lk8oFnPtmj"),
48 | }
49 |
50 | res, err := services.WeComApp.AccountService.Update(c.Request.Context(), options)
51 |
52 | if err != nil {
53 | panic(err)
54 | }
55 |
56 | c.JSON(http.StatusOK, res)
57 | }
58 |
59 | // 获取客服帐号列表
60 | // https://work.weixin.qq.com/api/doc/90000/90135/94661
61 | func APIAccountServiceAccountList(c *gin.Context) {
62 |
63 | res, err := services.WeComApp.AccountService.List(c.Request.Context())
64 |
65 | if err != nil {
66 | panic(err)
67 | }
68 |
69 | c.JSON(http.StatusOK, res)
70 | }
71 |
72 | // 获取客服帐号链接
73 | // https://work.weixin.qq.com/api/doc/90000/90135/94665
74 | func APIAccountServiceAddContactWay(c *gin.Context) {
75 |
76 | openKFID := c.DefaultQuery("openKFID", "wkAJ2GCAAAZSfhHCt7IFSvLKtMPxyJTw")
77 | scene := c.DefaultQuery("scene", "1234")
78 |
79 | res, err := services.WeComApp.AccountService.AddContactWay(c.Request.Context(), openKFID, scene)
80 |
81 | if err != nil {
82 | panic(err)
83 | }
84 |
85 | c.JSON(http.StatusOK, res)
86 | }
87 |
--------------------------------------------------------------------------------
/controllers/wecom/account-service/customer.go:
--------------------------------------------------------------------------------
1 | package account_service
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/accountService/customer/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 读取消息
11 | // https://work.weixin.qq.com/api/doc/90000/90135/94670
12 | func APIAccountServiceCustomerBatchGet(c *gin.Context) {
13 | externalUserIDList := []string{c.DefaultQuery("externalUserIDList", "matrix-x")}
14 |
15 | res, err := services.WeComApp.AccountServiceCustomer.BatchGet(c.Request.Context(), externalUserIDList)
16 |
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | c.JSON(http.StatusOK, res)
22 | }
23 |
24 | // 获取配置的专员与客户群
25 | // https://work.weixin.qq.com/api/doc/90000/90135/94674
26 | func APIAccountServiceCustomerGetUpgradeServiceConfig(c *gin.Context) {
27 | res, err := services.WeComApp.AccountServiceCustomer.GetUpgradeServiceConfig(c.Request.Context())
28 |
29 | if err != nil {
30 | panic(err)
31 | }
32 |
33 | c.JSON(http.StatusOK, res)
34 | }
35 |
36 | // 为客户升级为专员或客户群服务
37 | // https://work.weixin.qq.com/api/doc/90000/90135/94674
38 | func APIAccountServiceCustomerUpgradeService(c *gin.Context) {
39 | options := &request.RequestUpgradeService{
40 | OpenKFID: c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx"),
41 | ExternalUserID: c.DefaultQuery("externalUserID", "kfxxxxxxxxxxxxxx"),
42 | Type: 2, // 表示是升级到专员服务还是客户群服务。1:专员服务。2:客户群服务
43 | Member: &request.RequestUpgradeServiceMember{
44 | UserID: c.DefaultQuery("member", "matrix-x"),
45 | Wording: "你好,我是你的专属服务专员zhangsan",
46 | }, // 推荐的服务专员,type等于1时有效
47 | GroupChat: &request.RequestUpgradeServiceGroupChat{
48 | ChatID: "wraaaaaaaaaaaaaaaa",
49 | Wording: "欢迎加入你的专属服务群",
50 | }, // 推荐的客户群,type等于2时有效
51 | }
52 | res, err := services.WeComApp.AccountServiceCustomer.UpgradeService(c.Request.Context(), options)
53 |
54 | if err != nil {
55 | panic(err)
56 | }
57 |
58 | c.JSON(http.StatusOK, res)
59 | }
60 |
61 | // 为客户取消推荐
62 | // https://work.weixin.qq.com/api/doc/90000/90135/94674
63 | func APIAccountServiceCustomerCancelUpgradeService(c *gin.Context) {
64 | openKFID := c.DefaultQuery("openKFID", "kfxxxxxxxxxxxxxx")
65 | externalUserID := c.DefaultQuery("externalUserID", "kfxxxxxxxxxxxxxx")
66 | res, err := services.WeComApp.AccountServiceCustomer.CancelUpgradeService(c.Request.Context(), openKFID, externalUserID)
67 |
68 | if err != nil {
69 | panic(err)
70 | }
71 |
72 | c.JSON(http.StatusOK, res)
73 | }
74 |
--------------------------------------------------------------------------------
/controllers/wecom/user/tag.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | const defaultTagId = 100
10 |
11 | // APITagCreate 创建标签
12 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90210
13 | func APITagCreate(c *gin.Context) {
14 | res, err := services.WeComApp.UserTag.Create(c.Request.Context(), "TestTag", defaultTagId)
15 | if err != nil {
16 | panic(err)
17 | }
18 | c.JSON(http.StatusOK, res)
19 | }
20 |
21 | // APITagUpdate 更新标签名字
22 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90211
23 | func APITagUpdate(c *gin.Context) {
24 | res, err := services.WeComApp.UserTag.Update(c.Request.Context(), "TestTag1", defaultTagId)
25 | if err != nil {
26 | panic(err)
27 | }
28 | c.JSON(http.StatusOK, res)
29 | }
30 |
31 | // APITagDelete 删除标签
32 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90212
33 | func APITagDelete(c *gin.Context) {
34 | res, err := services.WeComApp.UserTag.Delete(c.Request.Context(), defaultTagId)
35 | if err != nil {
36 | panic(err)
37 | }
38 |
39 | c.JSON(http.StatusOK, res)
40 | }
41 |
42 | // TagList 获取标签列表
43 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90216
44 | func APITagList(c *gin.Context) {
45 | res, err := services.WeComApp.UserTag.List(c.Request.Context())
46 | if err != nil {
47 | panic(err)
48 | }
49 | c.JSON(http.StatusOK, res)
50 | }
51 |
52 | // TagUserGet 获取标签成员
53 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90213
54 | func APITagUserGet(c *gin.Context) {
55 | res, err := services.WeComApp.UserTag.Get(c.Request.Context(), defaultTagId)
56 | if err != nil {
57 | panic(err)
58 | }
59 | c.JSON(http.StatusOK, res)
60 | }
61 |
62 | // TagUserAdd 增加标签成员
63 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90214
64 | func APITagUserAdd(c *gin.Context) {
65 | //tagId := c.DefaultQuery("tagId", string(rune(defaultTagId)))
66 | userId := c.DefaultQuery("tagId", "walle")
67 | res, err := services.WeComApp.UserTag.TagUsers(c.Request.Context(), defaultTagId, []string{userId})
68 | if err != nil {
69 | panic(err)
70 | }
71 | c.JSON(http.StatusOK, res)
72 | }
73 |
74 | // TagUserDel 删除标签成员
75 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90215
76 | func APITagUserDel(c *gin.Context) {
77 | //tagId := c.DefaultQuery("tagId", string(rune(defaultTagId)))
78 | userId := c.DefaultQuery("tagId", "walle")
79 | res, err := services.WeComApp.UserTag.TagUsers(c.Request.Context(), defaultTagId, []string{userId})
80 | if err != nil {
81 | panic(err)
82 | }
83 | c.JSON(http.StatusOK, res)
84 | }
85 |
--------------------------------------------------------------------------------
/templates/h5-pay.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Wechat Payment
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
28 |
29 |
30 | 开始支付
31 |
74 |
75 |
--------------------------------------------------------------------------------
/controllers/miniprogram/plugin-manager.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | )
8 |
9 | // 向插件开发者发起使用插件的申请
10 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.applyPlugin.html
11 | func APIPluginManagerApplyPlugin(c *gin.Context) {
12 |
13 | pluginAppID, exist := c.GetQuery("pluginAppID")
14 | if !exist {
15 | panic("parameter plugin app id expected")
16 | }
17 |
18 | rs, err := services.MiniProgramApp.Plugin.ApplyPlugin(c.Request.Context(), pluginAppID, "test reason")
19 |
20 | if err != nil {
21 | panic(err)
22 | }
23 |
24 | c.JSON(http.StatusOK, rs)
25 | }
26 |
27 | // 获取当前所有插件使用方(供插件开发者调用)
28 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.getPluginDevApplyList.html
29 | func APIPluginManagerGetPluginDevApplyList(c *gin.Context) {
30 |
31 | rs, err := services.MiniProgramApp.Plugin.GetPluginDevApplyList(c.Request.Context(), "dev_apply_list", 1, 1)
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, rs)
38 | }
39 |
40 | // 查询已添加的插件
41 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.getPluginList.html
42 | func APIPluginManagerGetPluginList(c *gin.Context) {
43 |
44 | rs, err := services.MiniProgramApp.Plugin.GetPluginList(c.Request.Context())
45 |
46 | if err != nil {
47 | panic(err)
48 | }
49 |
50 | c.JSON(http.StatusOK, rs)
51 | }
52 |
53 | // 修改插件使用申请的状态(供插件开发者调用)
54 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.setDevPluginApplyStatus.html
55 | func APIPluginManagerSetDevPluginApplyStatus(c *gin.Context) {
56 |
57 | pluginAppID, exist := c.GetQuery("pluginAppID")
58 | if !exist {
59 | panic("parameter plugin app id expected")
60 | }
61 |
62 | rs, err := services.MiniProgramApp.Plugin.SetDevPluginApplyStatus(c.Request.Context(), "dev_agree", pluginAppID, "test reason")
63 |
64 | if err != nil {
65 | panic(err)
66 | }
67 |
68 | c.JSON(http.StatusOK, rs)
69 | }
70 |
71 | // 删除已添加的插件
72 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.unbindPlugin.html
73 | func APIPluginManagerUnbindPlugin(c *gin.Context) {
74 |
75 | pluginAppID, exist := c.GetQuery("pluginAppID")
76 | if !exist {
77 | panic("parameter plugin app id expected")
78 | }
79 |
80 | rs, err := services.MiniProgramApp.Plugin.UnbindPlugin(c.Request.Context(), pluginAppID)
81 |
82 | if err != nil {
83 | panic(err)
84 | }
85 |
86 | c.JSON(http.StatusOK, rs)
87 | }
88 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/calendar.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | "strconv"
9 | )
10 |
11 | // 创建日历
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93647
13 | func APICalendarAdd(c *gin.Context) {
14 | agentId := c.DefaultQuery("agentID", "1000014")
15 | agentID, _ := strconv.Atoi(agentId)
16 |
17 | calendar := &power.HashMap{
18 | "organizer": "userid1",
19 | "readonly": 1,
20 | "set_as_default": 1,
21 | "summary": "test_summary",
22 | "color": "#FF3030",
23 | "description": "test_describe",
24 | "shares": []power.HashMap{
25 | power.HashMap{
26 | "userid": "userid2",
27 | },
28 | power.HashMap{
29 | "userid": "userid3",
30 | "readonly": 1,
31 | },
32 | },
33 | }
34 |
35 | res, err := services.WeComApp.OACalendar.Add(c.Request.Context(), calendar, agentID)
36 |
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | c.JSON(http.StatusOK, res)
42 | }
43 |
44 | // 更新日历
45 | // https://work.weixin.qq.com/api/doc/90000/90135/93647
46 | func APICalendarUpdate(c *gin.Context) {
47 |
48 | calendar := &power.HashMap{
49 | "cal_id": "wcjgewCwAAqeJcPI1d8Pwbjt7nttzAAA",
50 | "readonly": 1,
51 | "summary": "test_summary",
52 | "color": "#FF3030",
53 | "description": "test_describe_1",
54 | "shares": []power.HashMap{
55 | power.HashMap{
56 | "userid": "userid1",
57 | },
58 | power.HashMap{
59 | "userid": "userid2",
60 | "readonly": 1,
61 | },
62 | },
63 | }
64 |
65 | res, err := services.WeComApp.OACalendar.Update(c.Request.Context(), calendar)
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, res)
72 | }
73 |
74 | // 获取日历详情
75 | // https://work.weixin.qq.com/api/doc/90000/90135/93647
76 | func APICalendarGet(c *gin.Context) {
77 | calIDList := []string{
78 | c.DefaultQuery("calID", "wcjgewCwAAqeJcPI1d8Pwbjt7nttzAAA"),
79 | }
80 | res, err := services.WeComApp.OACalendar.Get(c.Request.Context(), calIDList)
81 |
82 | if err != nil {
83 | panic(err)
84 | }
85 |
86 | c.JSON(http.StatusOK, res)
87 | }
88 |
89 | // 删除日历
90 | // https://work.weixin.qq.com/api/doc/90000/90135/93647
91 | func APICalendarDel(c *gin.Context) {
92 | calID := c.DefaultQuery("calID", "wcjgewCwAAqeJcPI1d8Pwbjt7nttzAAA")
93 | res, err := services.WeComApp.OACalendar.Del(c.Request.Context(), calID)
94 |
95 | if err != nil {
96 | panic(err)
97 | }
98 |
99 | c.JSON(http.StatusOK, res)
100 | }
101 |
--------------------------------------------------------------------------------
/controllers/wecom/user/department.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/department/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | "strconv"
9 | )
10 |
11 | const defaultDepartmentId = 3000001
12 |
13 | // APIDepartmentCreate 创建部门
14 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90205
15 | func APIDepartmentCreate(c *gin.Context) {
16 | name := c.DefaultQuery("name", "IT支持部")
17 | idStr := c.DefaultQuery("id", string(rune(defaultDepartmentId)))
18 | id, _ := strconv.Atoi(idStr)
19 |
20 | res, err := services.WeComContactApp.Department.Create(c.Request.Context(), &request.RequestDepartmentInsert{
21 | Name: name,
22 | NameEn: "",
23 | ParentID: 0,
24 | Order: 0,
25 | ID: id,
26 | })
27 |
28 | if err != nil {
29 | panic(err)
30 | }
31 |
32 | c.JSON(http.StatusOK, res)
33 | }
34 |
35 | // APIDepartmentUpdate 更新部门
36 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90206
37 | func APIDepartmentUpdate(c *gin.Context) {
38 | name := c.DefaultQuery("name", "IT支持部1")
39 | idStr := c.DefaultQuery("id", string(rune(defaultDepartmentId)))
40 | id, _ := strconv.Atoi(idStr)
41 | res, err := services.WeComContactApp.Department.Update(c.Request.Context(), &request.RequestDepartmentUpdate{
42 | Name: name,
43 | ParentID: id,
44 | })
45 |
46 | if err != nil {
47 | panic(err)
48 | }
49 |
50 | c.JSON(http.StatusOK, res)
51 | }
52 |
53 | // APIDepartmentDelete 删除部门
54 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90207
55 | func APIDepartmentDelete(c *gin.Context) {
56 | idStr := c.DefaultQuery("id", string(rune(defaultDepartmentId)))
57 | id, _ := strconv.Atoi(idStr)
58 | res, err := services.WeComContactApp.Department.Delete(c.Request.Context(), id)
59 |
60 | if err != nil {
61 | panic(err)
62 | }
63 |
64 | c.JSON(http.StatusOK, res)
65 | }
66 |
67 | // APIDepartmentList 获取部门列表
68 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90208
69 | func APIDepartmentList(c *gin.Context) {
70 | idStr := c.DefaultQuery("id", "0")
71 | id, _ := strconv.Atoi(idStr)
72 | // 0 表示获取公司所有部门
73 | res, err := services.WeComContactApp.Department.List(c.Request.Context(), id)
74 |
75 | if err != nil {
76 | panic(err)
77 | }
78 |
79 | c.JSON(http.StatusOK, res)
80 | }
81 |
82 | // APIDepartmentSimpleList 获取子部门ID列表
83 | // https://open.work.weixin.qq.com/api/doc/90000/90135/90208
84 | func APIDepartmentSimpleList(c *gin.Context) {
85 | idStr := c.DefaultQuery("id", "0")
86 | id, _ := strconv.Atoi(idStr)
87 | // 0 表示获取企业所有部门
88 | res, err := services.WeComContactApp.Department.SimpleList(c.Request.Context(), id)
89 |
90 | if err != nil {
91 | panic(err)
92 | }
93 |
94 | c.JSON(http.StatusOK, res)
95 | }
96 |
--------------------------------------------------------------------------------
/docs/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "description": "这是一个开源的使用教程,包含PowerWechat的大部分接口调试代码.",
5 | "title": "PowerWechat API Docs",
6 | "termsOfService": "http://artisan-cloud.com/terms/",
7 | "contact": {
8 | "name": "ArtisanCloud Support",
9 | "url": "https://powerwechat.artisan-cloud.com/zh/start/qa.html",
10 | "email": "matrix-x@artisan-cloud.como"
11 | },
12 | "license": {
13 | "name": "MIT 2.0",
14 | "url": "https://github.com/ArtisanCloud/PowerWeChat?tab=MIT-1-ov-file#readme"
15 | },
16 | "version": "1.0.1"
17 | },
18 | "host": "localhost:8080",
19 | "basePath": "/api/v1",
20 | "paths": {
21 | "/clearQuota": {
22 | "get": {
23 | "description": "\nSDK产品接口的代码展示:\n```\n返回类型定义如下:\ntype ResponseOfficialAccount struct {\n\tResponseBase\n\n\tErrCode int `json:\"errcode,omitempty\"`\n\tErrMsg string `json:\"errmsg,omitempty\"`\n\n\tResultCode string `json:\"resultcode,omitempty\"`\n\tResultMsg string `json:\"resultmsg,omitempty\"`\n}\n\n具体使用接口方式:\nfunc ClearQuota(ctx *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.ClearQuota(ctx.Request.Context())\n\tif err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK, data)\n}\n```",
24 | "tags": [
25 | "OfficialAccount.base.ClearQuota"
26 | ],
27 | "summary": "公众号里清空api的调用quota:https://developers.weixin.qq.com/doc/offiaccount/openApi/clear_quota.html",
28 | "responses": {}
29 | }
30 | },
31 | "/getCallbackIp": {
32 | "get": {
33 | "description": "\nSDK产品接口的代码展示:\n```\n返回类型定义如下:\ntype ResponseGetCallBackIP struct {\n\tresponse.ResponseOfficialAccount\n\n\tIPList []string `json:\"ip_list\"`\n}\n\n具体使用接口方式:\nfunc GetCallbackIP(ctx *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.GetCallbackIP(ctx.Request.Context())\n\tif err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK, data)\n}\n```",
34 | "tags": [
35 | "OfficialAccount.base.GetCallbackIP"
36 | ],
37 | "summary": "获取公众号回调的IP地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html",
38 | "responses": {}
39 | }
40 | }
41 | },
42 | "securityDefinitions": {
43 | "BasicAuth": {
44 | "type": "basic"
45 | }
46 | },
47 | "externalDocs": {
48 | "description": "OpenAPI",
49 | "url": "https://swagger.io/resources/open-api/"
50 | }
51 | }
--------------------------------------------------------------------------------
/controllers/wecom/oa/meeting.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/meeting/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 创建预约会议
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93627
13 | func APIMeetingCreate(c *gin.Context) {
14 |
15 | options := &request.RequestMeetingCreate{
16 | CreatorUserID: "zhangsan",
17 | Title: "新建会议",
18 | MeetingStart: 1600000000,
19 | MeetingDuration: 3600,
20 | Description: "新建会议描述",
21 | Type: 1,
22 | RemindTime: 60,
23 | AgentID: 1000014,
24 | Attendees: &power.HashMap{
25 | "userid": []string{
26 | "lisi",
27 | "wangwu",
28 | },
29 | "external_userid": []string{
30 | "woabc",
31 | "woced",
32 | },
33 | "device_sn": []string{
34 | "devsn1",
35 | "devsn2",
36 | },
37 | },
38 | }
39 | res, err := services.WeComApp.OAMeeting.Create(c.Request.Context(), options)
40 |
41 | if err != nil {
42 | panic(err)
43 | }
44 |
45 | c.JSON(http.StatusOK, res)
46 | }
47 |
48 | func APIMeetingUpdate(c *gin.Context) {
49 |
50 | options := &request.RequestMeetingUpdate{
51 | MeetingID: "XXXXXXXXX",
52 | Title: "新需求",
53 | MeetingStart: 1600000000,
54 | MeetingDuration: 10000,
55 | Description: "test",
56 | Type: 1,
57 | RemindTime: 60,
58 | Attendees: &power.HashMap{
59 | "userid": []string{
60 | "lisi",
61 | "wangwu",
62 | },
63 |
64 | "external_userid": []string{
65 | "woabc",
66 | "woced",
67 | },
68 | "device_sn": []string{
69 | "devsn1",
70 | "devsn2",
71 | },
72 | },
73 | }
74 | res, err := services.WeComApp.OAMeeting.Update(c.Request.Context(), options)
75 |
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | c.JSON(http.StatusOK, res)
81 | }
82 |
83 | func APIMeetingCancel(c *gin.Context) {
84 | meetingID := c.DefaultQuery("meetingID", "xxxxxx")
85 | res, err := services.WeComApp.OAMeeting.Cancel(c.Request.Context(), meetingID)
86 |
87 | if err != nil {
88 | panic(err)
89 | }
90 |
91 | c.JSON(http.StatusOK, res)
92 | }
93 |
94 | func APIMeetingGetUserMeetingID(c *gin.Context) {
95 |
96 | userID := c.DefaultQuery("userID", "")
97 | cursor := "cursor"
98 | beginTime := int64(1586136317)
99 | endTime := int64(1586236317)
100 | limit := 100
101 |
102 | res, err := services.WeComApp.OAMeeting.GetUserMeetingID(c.Request.Context(), userID, cursor, beginTime, endTime, limit)
103 |
104 | if err != nil {
105 | panic(err)
106 | }
107 |
108 | c.JSON(http.StatusOK, res)
109 | }
110 |
--------------------------------------------------------------------------------
/controllers/wecom/media.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "io/ioutil"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 上传临时素材
12 | // https://work.weixin.qq.com/api/doc/90000/90135/90253
13 | func APIMediaUploadByURL(c *gin.Context) {
14 | mediaPath := "./resource/qrcode.png"
15 | rs, err := services.WeComApp.Media.UploadTempFile(c.Request.Context(), mediaPath, nil)
16 |
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | c.JSON(http.StatusOK, rs)
22 | }
23 |
24 | // 上传临时素材
25 | // https://work.weixin.qq.com/api/doc/90000/90135/90253
26 | func APIMediaUploadByData(c *gin.Context) {
27 | var err error
28 | mediaPath := "./resource/cloud.jpg"
29 | value, err := ioutil.ReadFile(mediaPath)
30 |
31 | rs, err := services.WeComApp.Media.UploadTempFile(c.Request.Context(), "", &power.HashMap{
32 | "name": "temp.jpg", // 请确保文件名有准确的文件类型
33 | "value": value,
34 | })
35 |
36 | if err != nil {
37 | panic(err)
38 | }
39 |
40 | c.JSON(http.StatusOK, rs)
41 | }
42 |
43 | // 上传图片
44 | // https://work.weixin.qq.com/api/doc/90000/90135/90256
45 | func APIMediaUploadImgByPath(c *gin.Context) {
46 | var err error
47 | mediaPath := "./resource/cloud.jpg"
48 |
49 | rs, err := services.WeComApp.Media.UploadImage(c.Request.Context(), mediaPath, nil)
50 |
51 | if err != nil {
52 | panic(err)
53 | }
54 |
55 | c.JSON(http.StatusOK, rs)
56 | }
57 |
58 | // 上传图片
59 | // https://work.weixin.qq.com/api/doc/90000/90135/90256
60 | func APIMediaUploadImgByData(c *gin.Context) {
61 | var err error
62 | mediaPath := "./resource/cloud.jpg"
63 | value, err := ioutil.ReadFile(mediaPath)
64 |
65 | rs, err := services.WeComApp.Media.UploadImage(c.Request.Context(), "", &power.HashMap{
66 | "name": "image.jpg", // 请确保文件名有准确的文件类型
67 | "value": value,
68 | })
69 |
70 | if err != nil {
71 | panic(err)
72 | }
73 |
74 | c.JSON(http.StatusOK, rs)
75 | }
76 |
77 | // 获取临时素材
78 | // https://work.weixin.qq.com/api/doc/90000/90135/90254
79 | func APIMediaGet(c *gin.Context) {
80 | var err error
81 |
82 | mediaID := c.DefaultQuery("mediaID", "MEDIAID")
83 |
84 | rs, err := services.WeComApp.Media.Get(c.Request.Context(), mediaID)
85 |
86 | if err != nil {
87 | panic(err)
88 | }
89 |
90 | c.JSON(http.StatusOK, rs)
91 | }
92 |
93 | // 获取高清语音素材
94 | // https://work.weixin.qq.com/api/doc/90000/90135/90255
95 | func APIMediaGetJSSDK(c *gin.Context) {
96 | var err error
97 |
98 | mediaID := c.DefaultQuery("mediaID", "MEDIAID")
99 |
100 | rs, err := services.WeComApp.Media.GetJSSDK(c.Request.Context(), mediaID)
101 |
102 | if err != nil {
103 | panic(err)
104 | }
105 |
106 | c.JSON(http.StatusOK, rs)
107 | }
108 |
--------------------------------------------------------------------------------
/controllers/miniprogram/security.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "io/ioutil"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 同步校验图片违法违规内容
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.imgSecCheck.html
13 | func APISecurityImgSecCheckByPath(c *gin.Context) {
14 |
15 | mediaPath := "./resource/cloud.jpg"
16 | rs, err := services.MiniProgramApp.Security.ImgSecCheck(c.Request.Context(), mediaPath, nil)
17 |
18 | if err != nil {
19 | panic(err)
20 | }
21 |
22 | c.JSON(http.StatusOK, rs)
23 |
24 | }
25 |
26 | // 同步校验图片违法违规内容
27 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.imgSecCheck.html
28 | func APISecurityImgSecCheckByData(c *gin.Context) {
29 |
30 | var err error
31 | //mediaPath := "./resource/cloud.jpg"
32 | mediaPath := "/Users/walle/Library/Containers/com.tencent.WeWorkMac/Data/Documents/Profiles/7AC209353D1541276F36EBD6B929D4C4/Caches/Files/2022-08/a2fd7932928b4c8d9e88f44745d53391/2bc9b19229a761ab184dba7584adc1d1.jpg"
33 | value, err := ioutil.ReadFile(mediaPath)
34 |
35 | rs, err := services.MiniProgramApp.Security.ImgSecCheck(c.Request.Context(), "", &power.HashMap{
36 | "name": "media", // 请确保文件名有准确的文件类型
37 | "value": value,
38 | })
39 |
40 | if err != nil {
41 | panic(err)
42 | }
43 |
44 | c.JSON(http.StatusOK, rs)
45 |
46 | }
47 |
48 | // 异步校验图片/音频是否含有违法违规内容
49 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.mediaCheckAsync.html
50 | func APISecurityMediaCheckAsync(c *gin.Context) {
51 | mediaURL := c.DefaultQuery("mediaURL", "https://www.w3school.com.cn/i/horse.mp3")
52 | openID, exist := c.GetQuery("openid")
53 |
54 | if !exist {
55 | panic("parameter open id expected")
56 | }
57 |
58 | rs, err := services.MiniProgramApp.Security.MediaCheckAsync(
59 | c.Request.Context(),
60 | mediaURL,
61 | 1,
62 | 2,
63 | openID,
64 | 1,
65 | )
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, rs)
72 |
73 | }
74 |
75 | // 检查一段文本是否含有违法违规内容
76 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.msgSecCheck.html
77 | func APISecurityMsgSecCheck(c *gin.Context) {
78 | content := c.DefaultQuery("content", "Hello, World")
79 | openID, exist := c.GetQuery("openid")
80 | if !exist {
81 | panic("parameter open id expected")
82 | }
83 |
84 | rs, err := services.MiniProgramApp.Security.MsgSecCheck(
85 | c.Request.Context(),
86 | openID,
87 | 1,
88 | 2,
89 | content,
90 | "test name",
91 | "test title",
92 | "test sign",
93 | )
94 |
95 | if err != nil {
96 | panic(err)
97 | }
98 |
99 | c.JSON(http.StatusOK, rs)
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/controllers/miniprogram/near-by-poi.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | "strconv"
9 | )
10 |
11 | // 添加地点
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/nearby-poi/nearbyPoi.add.html
13 | func APINearbyPoiAdd(c *gin.Context) {
14 |
15 | rs, err := services.MiniProgramApp.NearbyPoi.Add(
16 | c.Request.Context(),
17 | &power.HashMap{
18 | "is_comm_nearby": "1", //值固定
19 | "kf_info": "{\"open_kf\":true,\"kf_headimg\":\"http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg\",\"kf_name\":\"Harden\"}",
20 | "pic_list": "{\"list\":[\"http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg\",\"http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITRneE5FS9uYruXGMmrtmhsBySwddEWUGOibG8Ze2NT5E3Dyt79I0htNg/0?wx_fmt=jpeg\"]}",
21 | "service_infos": "{\"service_infos\":[{\"id\":2,\"type\":1,\"name\":\"快递\",\"appid\":\"wx1373169e494e0c39\",\"path\":\"index\"},{\"id\":0,\"type\":2,\"name\":\"自定义\",\"appid\":\"wx1373169e494e0c39\",\"path\":\"index\"}]}",
22 | "store_name": "羊村小马烧烤",
23 | "contract_phone": "111111111",
24 | "hour": "00:00-11:11",
25 | "company_name": "深圳市腾讯计算机系统有限公司",
26 | "credential": "156718193518281",
27 | "address": "新疆维吾尔自治区克拉玛依市克拉玛依区碧水路15-1-8号(碧水云天广场)",
28 | "qualification_list": "3LaLzqiTrQcD20DlX_o-OV1-nlYMu7sdVAL7SV2PrxVyjZFZZmB3O6LPGaYXlZWq",
29 | "poi_id": "",
30 | })
31 |
32 | if err != nil {
33 | panic(err)
34 | }
35 |
36 | c.JSON(http.StatusOK, rs)
37 | }
38 |
39 | func APINearbyPoiDelete(c *gin.Context) {
40 |
41 | poiID, exist := c.GetQuery("poiID")
42 | if !exist {
43 | panic("parameter poi id expected")
44 | }
45 |
46 | rs, err := services.MiniProgramApp.NearbyPoi.Delete(c.Request.Context(), poiID)
47 |
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | c.JSON(http.StatusOK, rs)
53 | }
54 |
55 | func APINearbyPoiGetList(c *gin.Context) {
56 |
57 | strPage := c.DefaultQuery("page", "1")
58 |
59 | page, err := strconv.Atoi(strPage)
60 |
61 | strPageRows := c.DefaultQuery("pageRows", "100")
62 |
63 | pageRows, err := strconv.Atoi(strPageRows)
64 |
65 | rs, err := services.MiniProgramApp.NearbyPoi.GetList(c.Request.Context(), page, pageRows)
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, rs)
72 | }
73 |
74 | func APINearbySetShowStatus(c *gin.Context) {
75 |
76 | poiID, exist := c.GetQuery("poiID")
77 | if !exist {
78 | panic("parameter poi id expected")
79 | }
80 |
81 | rs, err := services.MiniProgramApp.NearbyPoi.SetShowStatus(c.Request.Context(), poiID, 1)
82 |
83 | if err != nil {
84 | panic(err)
85 | }
86 |
87 | c.JSON(http.StatusOK, rs)
88 | }
89 |
--------------------------------------------------------------------------------
/config/model.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/jinzhu/configor"
5 | "log"
6 | )
7 |
8 | type Configuration struct {
9 | Payment Payment
10 | MiniProgram MiniProgram
11 | WeCom WeCom
12 | OffiAccount OffiAccount
13 | OpenPlatform OpenPlatform
14 | }
15 |
16 | type Payment struct {
17 | AppID string `required:"true" env:"app_id"`
18 | MchID string `required:"true" env:"mch_id"`
19 | CertPath string `required:"true" env:"wx_cert_path"`
20 | KeyPath string `required:"true" env:"wx_key_path"`
21 | SerialNo string `required:"true" env:"serial_no"`
22 | CertificateKeyPath string `required:"false" env:"certificate_key_path"`
23 | WechatPaySerial string `required:"false" env:"wechat_pay_serial"`
24 | RSAPublicKeyPath string `required:"false" env:"wx_rsa_public_key_path"`
25 | MchApiV3Key string `env:"mch_api_v3_key"`
26 | Key string `env:"key"`
27 | ResponseType string `default:"map"`
28 | NotifyURL string `env:"notify_url"`
29 | SubMchID string `env:"sub_mch_id"`
30 | SubAppID string `env:"sub_app_id"`
31 | HttpDebug bool `default:"false"`
32 | RedisAddr string `env:"redis_addr"`
33 | }
34 |
35 | type MiniProgram struct {
36 | AppID string `required:"true" env:"miniprogram_app_id"`
37 | Secret string `required:"true" env:"miniprogram_secret"`
38 | RedisAddr string `env:"redis_addr"`
39 | MessageToken string `env:"message_token"`
40 | MessageAesKey string `env:"message_aes_key"`
41 |
42 | VirtualPayAppKey string `env:"virtual_pay_app_key"`
43 | VirtualPayOfferID string `env:"virtual_pay_offer_id"`
44 | }
45 |
46 | type WeCom struct {
47 | CorpID string `env:"corp_id"`
48 | AgentID int `env:"wecom_agent_id"`
49 | Secret string `env:"wecom_secret"`
50 | MessageToken string `env:"app_message_token"`
51 | MessageAesKey string `env:"app_message_aes_key"`
52 | MessageCallback string `env:"app_message_callback_url"`
53 | OAuthCallback string `env:"app_oauth_callback_url"`
54 | ContactSecret string `env:"contact_secret"`
55 | ContactToken string `env:"contact_token"`
56 | ContactAESKey string `env:"contact_aes_key"`
57 | ContactCallback string `env:"contact_callback_url"`
58 | RedisAddr string `env:"redis_addr"`
59 | }
60 |
61 | type OffiAccount struct {
62 | AppID string `required:"true" env:"appid"`
63 | AppSecret string `required:"true" env:"appsecret"`
64 | RedisAddr string `env:"redis_addr"`
65 | MessageToken string `env:"message_token"`
66 | MessageAesKey string `env:"message_aes_key"`
67 | }
68 |
69 | type OpenPlatform struct {
70 | AppID string
71 | AppSecret string
72 | MessageToken string
73 | MessageAesKey string
74 | RedisAddr string
75 | }
76 |
77 | func configFiles() []string {
78 | return []string{"config.yml"}
79 | }
80 |
81 | // Get returns the configuration extracted from env variables or config file.
82 | func Get() *Configuration {
83 | conf := new(Configuration)
84 | err := configor.New(&configor.Config{}).Load(conf, configFiles()...)
85 | if err != nil {
86 | log.Printf("%#v", conf)
87 | panic(err)
88 | }
89 | return conf
90 | }
91 |
--------------------------------------------------------------------------------
/controllers/official-account/user-tag.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | "strconv"
8 | )
9 |
10 | // UserTagList 获取用户标签列表
11 | func GetUserTagList(ctx *gin.Context) {
12 | data, err := services.OfficialAccountApp.UserTag.List(ctx.Request.Context())
13 | if err != nil {
14 | ctx.String(http.StatusBadRequest, err.Error())
15 | return
16 | }
17 | ctx.JSON(http.StatusOK, data)
18 | }
19 |
20 | // UserTagCreate 创建用户标签
21 | func UserTagCreate(ctx *gin.Context) {
22 | tagName, _ := ctx.GetPostForm("tagName")
23 | data, err := services.OfficialAccountApp.UserTag.Create(ctx.Request.Context(), tagName)
24 | if err != nil {
25 | ctx.String(http.StatusBadRequest, err.Error())
26 | return
27 | }
28 | ctx.JSON(http.StatusOK, data)
29 | }
30 |
31 | // UserTagUpdate 更新用户标签
32 | func UserTagUpdate(ctx *gin.Context) {
33 | tagID, _ := ctx.GetPostForm("tagID")
34 | tagName, _ := ctx.GetPostForm("tagName")
35 | data, err := services.OfficialAccountApp.UserTag.Update(ctx.Request.Context(), tagID, tagName)
36 | if err != nil {
37 | ctx.String(http.StatusBadRequest, err.Error())
38 | return
39 | }
40 | ctx.JSON(http.StatusOK, data)
41 | }
42 |
43 | // UserTagDelete 删除用户标签
44 | func UserTagDelete(ctx *gin.Context) {
45 | tagID, _ := ctx.GetPostForm("tagID")
46 | data, err := services.OfficialAccountApp.UserTag.Delete(ctx.Request.Context(), tagID)
47 | if err != nil {
48 | ctx.String(http.StatusBadRequest, err.Error())
49 | return
50 | }
51 | ctx.JSON(http.StatusOK, data)
52 | }
53 |
54 | // GetUserTagsByOpenID 获取用户身上的标签列表
55 | func GetUserTagsByOpenID(ctx *gin.Context) {
56 | openID := ctx.Query("openID")
57 | data, err := services.OfficialAccountApp.UserTag.UserTags(ctx.Request.Context(), openID)
58 | if err != nil {
59 | ctx.String(http.StatusBadRequest, err.Error())
60 | return
61 | }
62 | ctx.JSON(http.StatusOK, data)
63 | }
64 |
65 | // GetUsersOfTag 获取标签下粉丝列表
66 | func GetUsersOfTag(ctx *gin.Context) {
67 | tagID := ctx.Query("tagID")
68 | nextOpenID := ctx.Query("nextOpenID")
69 | data, err := services.OfficialAccountApp.UserTag.UsersOfTag(ctx.Request.Context(), tagID, nextOpenID)
70 | if err != nil {
71 | ctx.String(http.StatusBadRequest, err.Error())
72 | return
73 | }
74 | ctx.JSON(http.StatusOK, data)
75 | }
76 |
77 | // UserTagBatchTagUsers 批量为用户打标签
78 | func UserTagBatchTagUsers(ctx *gin.Context) {
79 | openID := ctx.Query("openID")
80 | tagID, _ := strconv.Atoi(ctx.Query("tagID"))
81 | data, err := services.OfficialAccountApp.UserTag.TagUsers(ctx.Request.Context(), []string{openID}, tagID)
82 | if err != nil {
83 | ctx.String(http.StatusBadRequest, err.Error())
84 | return
85 | }
86 | ctx.JSON(http.StatusOK, data)
87 | }
88 |
89 | // UserTagBatchUnTagUsers 批量为用户取消标签
90 | func UserTagBatchUnTagUsers(ctx *gin.Context) {
91 | openID := ctx.Query("openID")
92 | tagID := ctx.Query("tagID")
93 | data, err := services.OfficialAccountApp.UserTag.UntagUsers(ctx.Request.Context(), []string{openID}, tagID)
94 | if err != nil {
95 | ctx.String(http.StatusBadRequest, err.Error())
96 | return
97 | }
98 | ctx.JSON(http.StatusOK, data)
99 | }
100 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/webdrive.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/webdrive/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 新建空间
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93655
13 | func APIWebDriveSpaceCreate(c *gin.Context) {
14 | options := &request.RequestWebDriveSpaceCreate{
15 | UserID: c.DefaultQuery("userID", "USERID"),
16 | SpaceName: c.DefaultQuery("spaceName", "SPACE_NAME"),
17 | AuthInfo: []*power.HashMap{
18 | &power.HashMap{
19 | "type": 1,
20 | "userid": "USERID",
21 | "auth": 2,
22 | },
23 | &power.HashMap{
24 | "type": 2,
25 | "departmentid": "DEPARTMENTID",
26 | "auth": 1,
27 | },
28 | },
29 | }
30 |
31 | res, err := services.WeComApp.OAWebDrive.SpaceCreate(c.Request.Context(), options)
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, res)
38 | }
39 |
40 | // 添加成员/部门
41 | // https://work.weixin.qq.com/api/doc/90000/90135/93656
42 | func APIWebDriveSpaceAcAdd(c *gin.Context) {
43 |
44 | options := &request.RequestWebDriveSpaceACLAdd{
45 | UserID: c.DefaultQuery("userID", "USERID"),
46 | SpaceID: c.DefaultQuery("spaceName", "SPACE_NAME"),
47 | AuthInfo: []*power.HashMap{
48 | &power.HashMap{
49 | "type": 1,
50 | "userid": "USERID",
51 | "auth": 2,
52 | },
53 | &power.HashMap{
54 | "type": 2,
55 | "departmentid": "DEPARTMENTID",
56 | "auth": 1,
57 | },
58 | },
59 | }
60 | res, err := services.WeComApp.OAWebDrive.SpaceACLAdd(c.Request.Context(), options)
61 |
62 | if err != nil {
63 | panic(err)
64 | }
65 |
66 | c.JSON(http.StatusOK, res)
67 | }
68 |
69 | // 获取文件列表
70 | // https://work.weixin.qq.com/api/doc/90000/90135/93657
71 | func APIWebDriveFileList(c *gin.Context) {
72 |
73 | options := &request.RequestWebDriveFileList{
74 | UserID: c.DefaultQuery("userID", "USERID"),
75 | SpaceID: c.DefaultQuery("spaceID", "SPACEID"),
76 | FatherID: c.DefaultQuery("fatherID", "FATHERID"),
77 | SortType: 1,
78 | Start: 1,
79 | Limit: 20,
80 | }
81 |
82 | res, err := services.WeComApp.OAWebDrive.FileList(c.Request.Context(), options)
83 |
84 | if err != nil {
85 | panic(err)
86 | }
87 |
88 | c.JSON(http.StatusOK, res)
89 | }
90 |
91 | // 新增指定人
92 | // https://work.weixin.qq.com/api/doc/90000/90135/93658
93 | func APIWebDriveFileAclAdd(c *gin.Context) {
94 |
95 | options := &request.RequestWebDriveFileACLAdd{
96 | UserID: c.DefaultQuery("userID", "USERID"),
97 | FileID: c.DefaultQuery("fileID", "FILEID"),
98 | AuthInfo: []*power.HashMap{
99 | &power.HashMap{
100 | "type": 1,
101 | "userid": "USERID",
102 | "auth": 2,
103 | },
104 | &power.HashMap{
105 | "type": 2,
106 | "departmentid": "DEPARTMENTID",
107 | "auth": 1,
108 | },
109 | },
110 | }
111 |
112 | res, err := services.WeComApp.OAWebDrive.FileACLAdd(c.Request.Context(), options)
113 |
114 | if err != nil {
115 | panic(err)
116 | }
117 |
118 | c.JSON(http.StatusOK, res)
119 | }
120 |
--------------------------------------------------------------------------------
/controllers/official-account/user.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/user/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // GetUserInfo 获取单个用户信息
11 | func GetUserInfo(ctx *gin.Context) {
12 | openID := ctx.Query("openID")
13 | lang := ctx.Query("lang")
14 | data, err := services.OfficialAccountApp.User.Get(ctx.Request.Context(), openID, lang)
15 | if err != nil {
16 | ctx.String(http.StatusBadRequest, err.Error())
17 | return
18 | }
19 | ctx.JSON(http.StatusOK, data)
20 | }
21 |
22 | // 获取多个用户信息
23 | func GetBatchUserInfo(ctx *gin.Context) {
24 | openID := ctx.Query("openID")
25 | data, err := services.OfficialAccountApp.User.BatchGet(ctx.Request.Context(), &request.RequestBatchGetUserInfo{
26 | UserList: []*request.UserList{
27 | {
28 | Openid: openID,
29 | },
30 | },
31 | })
32 | if err != nil {
33 | ctx.String(http.StatusBadRequest, err.Error())
34 | return
35 | }
36 | ctx.JSON(http.StatusOK, data)
37 | }
38 |
39 | // GetUserList 获取用户列表
40 | func GetUserList(ctx *gin.Context) {
41 | nextOpenId := ctx.Query("nextOpenId")
42 | data, err := services.OfficialAccountApp.User.List(ctx.Request.Context(), nextOpenId)
43 | if err != nil {
44 | ctx.String(http.StatusBadRequest, err.Error())
45 | return
46 | }
47 | ctx.JSON(http.StatusOK, data)
48 | }
49 |
50 | // UserRemark 修改用户备注
51 | func UserRemark(ctx *gin.Context) {
52 | openID := ctx.Query("openID")
53 | remark := ctx.Query("remark")
54 | data, err := services.OfficialAccountApp.User.Remark(ctx.Request.Context(), openID, remark)
55 | if err != nil {
56 | ctx.String(http.StatusBadRequest, err.Error())
57 | return
58 | }
59 | ctx.JSON(http.StatusOK, data)
60 | }
61 |
62 | // UserBlock 拉黑用户
63 | func UserBlock(ctx *gin.Context) {
64 | openID := ctx.Query("openID")
65 | data, err := services.OfficialAccountApp.User.Block(ctx.Request.Context(), []string{openID})
66 | if err != nil {
67 | ctx.String(http.StatusBadRequest, err.Error())
68 | return
69 | }
70 | ctx.JSON(http.StatusOK, data)
71 | }
72 |
73 | // UserUnBlock 取消拉黑用户
74 | func UserUnBlock(ctx *gin.Context) {
75 | openID := ctx.Query("openID")
76 | data, err := services.OfficialAccountApp.User.Unblock(ctx.Request.Context(), []string{openID})
77 | if err != nil {
78 | ctx.String(http.StatusBadRequest, err.Error())
79 | return
80 | }
81 | ctx.JSON(http.StatusOK, data)
82 | }
83 |
84 | // GetUserBlacklist 获取用户列表
85 | func GetUserBlacklist(ctx *gin.Context) {
86 | beginOpenid := ctx.Query("beginOpenid")
87 | data, err := services.OfficialAccountApp.User.Blacklist(ctx.Request.Context(), beginOpenid)
88 | if err != nil {
89 | ctx.String(http.StatusBadRequest, err.Error())
90 | return
91 | }
92 | ctx.JSON(http.StatusOK, data)
93 | }
94 |
95 | // UserChangeOpenID 账号迁移 openid 转换
96 | func UserChangeOpenID(ctx *gin.Context) {
97 | oldAppId := ctx.Query("oldAppId")
98 | data, err := services.OfficialAccountApp.User.ChangeOpenID(ctx.Request.Context(), oldAppId, []string{})
99 | if err != nil {
100 | ctx.String(http.StatusBadRequest, err.Error())
101 | return
102 | }
103 | ctx.JSON(http.StatusOK, data)
104 | }
105 |
--------------------------------------------------------------------------------
/controllers/miniprogram/auth.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "crypto/sha256"
5 | "fmt"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/base/request"
7 | "github.com/gin-gonic/gin"
8 | "log"
9 | "net/http"
10 | "power-wechat-tutorial/services"
11 | )
12 |
13 | func APISNSSession(c *gin.Context) {
14 |
15 | code, exist := c.GetQuery("code")
16 | if !exist {
17 | panic("parameter code expected")
18 | }
19 | rs, err := services.MiniProgramApp.Auth.Session(c.Request.Context(), code)
20 | services.MiniProgramApp.AccessToken.Refresh(c.Request.Context())
21 | print(rs)
22 |
23 | //{"session_key":"7o91EfeHO4PEnoeVzJxvqw==","openid":"o4QEk5Kc_y8QTrENCpKoxYhS4jkg","unionid":"orLIIs_Tfr0DLoG2iMwSq7RuaYRg"}
24 |
25 | encrypt := "DRdLarcsm8c2jsdSOubaLWh/FuNgC38OszQly2n5OTchOMdTMJb6FqwfSKD3PV3B5UKfljlwpkdQxFAd3q8orqfBK/X9+lth5zXvSZ+OxICncxqbycpJFf+o695N2aCE66oC+GPebSpYld0aiUYtbPZQdwr1uqg1toCIDkZCKuuXkFFl5QeKgwg6VJVZk5w5IM2mDzAnUr8pIQllvlA/4A=="
26 | sessionKey := "7o91EfeHO4PEnoeVzJxvqw=="
27 | //openId:="o4QEk5Kc_y8QTrENCpKoxYhS4jkg"
28 | //unionId:="orLIIs_Tfr0DLoG2iMwSq7RuaYRg"
29 | iv := "JhojkHhkdgqHPkqD9uYZYQ=="
30 | rs_e, err_e := services.MiniProgramApp.Encryptor.DecryptData(encrypt, sessionKey, iv)
31 | //services.MiniProgramApp.Base.CheckEncryptedData(c.Request.Context())
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | if err_e != nil {
38 | panic(err_e)
39 | }
40 |
41 | c.JSON(http.StatusOK, rs_e)
42 |
43 | }
44 |
45 | func APIResetUserSessionKey(c *gin.Context) {
46 |
47 | sessionKey := "7o91EfeHO4PEnoeVzJxvqw=="
48 | openId := "o4QEk5Kc_y8QTrENCpKoxYhS4jkg"
49 |
50 | rs, err := services.MiniProgramApp.Auth.ResetUserSessionKey(c.Request.Context(), openId, sessionKey)
51 |
52 | if err != nil {
53 | panic(err)
54 | }
55 |
56 | c.JSON(http.StatusOK, rs)
57 |
58 | }
59 |
60 | func APICheckSession(c *gin.Context) {
61 |
62 | sessionKey := "7o91EfeHO4PEnoeVzJxvqw=="
63 | openId := "o4QEk5Kc_y8QTrENCpKoxYhS4jkg"
64 |
65 | rs, err := services.MiniProgramApp.Auth.CheckSession(c.Request.Context(), openId, sessionKey)
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, rs)
72 |
73 | }
74 |
75 | func APICheckEncryptedData(c *gin.Context) {
76 | encryptedData := c.DefaultQuery("encryptedData", "sTWzm26PrbsXlSA8AoW+GpiyNLJP0H5p2UT4dXKwLSvXv8aU4wIiJcZUcM/IzNXnoFtERY3BDRbZh6bwd0ZGENVhucqDPXmchTqseryIZnJiKsiNMHCpAkCA2Yl00q4UpOZYtGMuTX5BTuo1yB3bOOuIfDu6neHV3D158CofGB9m7TxFQ8A/JcauWzhvmEAPygfFaqCgDTEmluLu7S8wMA==")
77 | hashByte := sha256.Sum256([]byte(encryptedData))
78 | hash := hashByte[:]
79 | rs, err := services.MiniProgramApp.Base.CheckEncryptedData(c.Request.Context(), fmt.Sprintf("%x", hash))
80 |
81 | if err != nil {
82 | panic(err)
83 | }
84 |
85 | c.JSON(http.StatusOK, rs)
86 |
87 | }
88 |
89 | func APIGetPaidUnionID(c *gin.Context) {
90 | openid := c.DefaultQuery("openid", "")
91 | log.Printf("openid: %s\n", openid)
92 | rs, err := services.MiniProgramApp.Base.GetPaidUnionID(c.Request.Context(), &request.RequestGetPaidUnionID{
93 | OpenID: openid,
94 | // TransactionID: "",
95 | // MchID: "",
96 | // OutTradeNo: "",
97 | })
98 |
99 | if err != nil {
100 | panic(err)
101 | }
102 |
103 | c.JSON(http.StatusOK, rs)
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/controllers/official-account/server.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerLibs/v3/http/helper"
6 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
7 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract"
8 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages"
9 | models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
10 | "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models"
11 | "github.com/gin-gonic/gin"
12 | "power-wechat-tutorial/services"
13 | )
14 |
15 | // 回调配置
16 | // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
17 | func CallbackVerify(c *gin.Context) {
18 | rs, err := services.OfficialAccountApp.Server.VerifyURL(c.Request)
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | // 选择1
24 | //text, _ := ioutil.ReadAll(rs.Body)
25 | //c.String(http.StatusOK, string(text))
26 |
27 | // 选择2
28 | err = helper.HttpResponseSend(rs, c.Writer)
29 |
30 | }
31 |
32 | // 回调配置
33 | // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
34 | func CallbackNotify(c *gin.Context) {
35 |
36 | //requestXML, _ := io.ReadAll(c.Request.Body)
37 | //c.Request.Body = io.NopCloser(bytes.NewBuffer(requestXML))
38 | //contentType := c.Request.Header.Get("Content-Type")
39 | //
40 | //println(contentType)
41 | //println(string(requestXML))
42 |
43 | rs, err := services.OfficialAccountApp.Server.Notify(c.Request, func(event contract.EventInterface) interface{} {
44 | fmt.Dump("event", event)
45 | //return "handle callback"
46 |
47 | switch event.GetMsgType() {
48 |
49 | case models2.CALLBACK_MSG_TYPE_EVENT:
50 | switch event.GetEvent() {
51 | case models.CALLBACK_EVENT_SUBSCRIBE:
52 | msg := models.EventSubscribe{}
53 | err := event.ReadMessage(&msg)
54 | if err != nil {
55 | println(err.Error())
56 | return "error"
57 | }
58 | fmt.Dump(msg)
59 | return kernel.SUCCESS_EMPTY_RESPONSE
60 |
61 | case models.CALLBACK_EVENT_UNSUBSCRIBE:
62 | msg := models.EventUnSubscribe{}
63 | err := event.ReadMessage(&msg)
64 | if err != nil {
65 | println(err.Error())
66 | return "error"
67 | }
68 | fmt.Dump(msg)
69 | return kernel.SUCCESS_EMPTY_RESPONSE
70 |
71 | }
72 |
73 | case models2.CALLBACK_MSG_TYPE_TEXT:
74 | msg := models.MessageText{}
75 | err := event.ReadMessage(&msg)
76 | if err != nil {
77 | println(err.Error())
78 | return "error"
79 | }
80 | fmt.Dump(msg)
81 | case models.CALLBACK_EVENT_SCAN:
82 | msg := models.EventScanCodePush{}
83 | err := event.ReadMessage(&msg)
84 | if err != nil {
85 | println(err.Error())
86 | return "error"
87 | }
88 | }
89 |
90 | //return replyData
91 | return messages.NewText("ok")
92 |
93 | //media_id := "JRzcFCs0neDADadmOep2YOszEXI0ZFesCRP75VgIX7UgLzy7Uqk2YeYcwyHtOmAe"
94 | //return messages.NewImage(media_id, &power.HashMap{})
95 | //return kernel.SUCCESS_EMPTY_RESPONSE
96 |
97 | })
98 | if err != nil {
99 | panic(err)
100 | }
101 |
102 | err = helper.HttpResponseSend(rs, c.Writer)
103 |
104 | if err != nil {
105 | panic(err)
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module power-wechat-tutorial
2 |
3 | go 1.23
4 |
5 | replace github.com/ArtisanCloud/PowerWeChat/v3 => ../PowerWeChat
6 |
7 | replace github.com/ArtisanCloud/PowerLibs/v3 => ../PowerLibs
8 |
9 | //replace github.com/ArtisanCloud/PowerSocialite/v3 => ../PowerSocialite
10 |
11 | require (
12 | github.com/ArtisanCloud/PowerLibs/v3 v3.2.6
13 | github.com/ArtisanCloud/PowerWeChat/v3 v3.2.45
14 | github.com/gin-contrib/cors v1.4.0
15 | github.com/gin-gonic/gin v1.9.1
16 | github.com/go-playground/assert/v2 v2.2.0
17 | github.com/golang-module/carbon v1.6.0
18 | github.com/jinzhu/configor v1.2.1
19 | github.com/swaggo/swag v1.16.3
20 | golang.org/x/text v0.18.0
21 | )
22 |
23 | require (
24 | github.com/ArtisanCloud/PowerSocialite/v3 v3.0.7 // indirect
25 | github.com/BurntSushi/toml v0.3.1 // indirect
26 | github.com/KyleBanks/depth v1.2.1 // indirect
27 | github.com/PuerkitoBio/purell v1.1.1 // indirect
28 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
29 | github.com/bytedance/sonic v1.9.1 // indirect
30 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
31 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
32 | github.com/clbanning/mxj/v2 v2.7.0 // indirect
33 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
34 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect
35 | github.com/gin-contrib/sse v0.1.0 // indirect
36 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
37 | github.com/go-openapi/jsonreference v0.19.6 // indirect
38 | github.com/go-openapi/spec v0.20.4 // indirect
39 | github.com/go-openapi/swag v0.19.15 // indirect
40 | github.com/go-playground/locales v0.14.1 // indirect
41 | github.com/go-playground/universal-translator v0.18.1 // indirect
42 | github.com/go-playground/validator/v10 v10.14.1 // indirect
43 | github.com/gobuffalo/envy v1.7.0 // indirect
44 | github.com/gobuffalo/packd v0.3.0 // indirect
45 | github.com/gobuffalo/packr v1.30.1 // indirect
46 | github.com/goccy/go-json v0.10.2 // indirect
47 | github.com/joho/godotenv v1.3.0 // indirect
48 | github.com/josharian/intern v1.0.0 // indirect
49 | github.com/json-iterator/go v1.1.12 // indirect
50 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect
51 | github.com/leodido/go-urn v1.2.4 // indirect
52 | github.com/mailru/easyjson v0.7.6 // indirect
53 | github.com/mattn/go-isatty v0.0.19 // indirect
54 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
55 | github.com/modern-go/reflect2 v1.0.2 // indirect
56 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
57 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect
58 | github.com/pkg/errors v0.9.1 // indirect
59 | github.com/redis/go-redis/v9 v9.1.0 // indirect
60 | github.com/rogpeppe/go-internal v1.8.0 // indirect
61 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
62 | github.com/ugorji/go/codec v1.2.11 // indirect
63 | go.uber.org/multierr v1.11.0 // indirect
64 | go.uber.org/zap v1.25.0 // indirect
65 | golang.org/x/arch v0.3.0 // indirect
66 | golang.org/x/crypto v0.27.0 // indirect
67 | golang.org/x/net v0.25.0 // indirect
68 | golang.org/x/sys v0.25.0 // indirect
69 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
70 | google.golang.org/protobuf v1.30.0 // indirect
71 | gopkg.in/yaml.v2 v2.4.0 // indirect
72 | gopkg.in/yaml.v3 v3.0.1 // indirect
73 | )
74 |
--------------------------------------------------------------------------------
/controllers/miniprogram/img.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "io/ioutil"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 本接口提供基于小程序的图片智能裁剪能力
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.aiCrop.html
13 | func APIImgAICropByURL(c *gin.Context) {
14 |
15 | // https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg
16 | url, exist := c.GetQuery("url")
17 | if !exist {
18 | panic("parameter url expected")
19 | }
20 |
21 | rs, err := services.MiniProgramApp.Image.AICrop(c.Request.Context(), url, nil)
22 |
23 | if err != nil {
24 | panic(err)
25 | }
26 |
27 | c.JSON(http.StatusOK, rs)
28 | }
29 |
30 | // 本接口提供基于小程序的图片智能裁剪能力
31 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.aiCrop.html
32 | func APIImgAICropByData(c *gin.Context) {
33 | var err error
34 | mediaPath := "./resource/tree.png"
35 | value, err := ioutil.ReadFile(mediaPath)
36 |
37 | rs, err := services.MiniProgramApp.Image.AICrop(c.Request.Context(), "", &power.HashMap{
38 | "name": "tree.png", // 请确保文件名有准确的文件类型
39 | "value": value,
40 | })
41 |
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | c.JSON(http.StatusOK, rs)
47 | }
48 |
49 | // 本接口提供基于小程序的条码/二维码识别的API
50 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.scanQRCode.html
51 | func APIImgScanQRCodeByURL(c *gin.Context) {
52 | url, exist := c.GetQuery("url")
53 | if !exist {
54 | panic("parameter url expected")
55 | }
56 |
57 | rs, err := services.MiniProgramApp.Image.ScanQRCode(c.Request.Context(), url, nil)
58 |
59 | if err != nil {
60 | panic(err)
61 | }
62 |
63 | c.JSON(http.StatusOK, rs)
64 | }
65 |
66 | // 本接口提供基于小程序的条码/二维码识别的API
67 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.scanQRCode.html
68 | func APIImgScanQRCodeByData(c *gin.Context) {
69 | var err error
70 | mediaPath := "./resource/qrcode.png"
71 | value, err := ioutil.ReadFile(mediaPath)
72 |
73 | rs, err := services.MiniProgramApp.Image.ScanQRCode(c.Request.Context(), "", &power.HashMap{
74 | "name": "qrcode.png", // 请确保文件名有准确的文件类型
75 | "value": value,
76 | })
77 |
78 | if err != nil {
79 | panic(err)
80 | }
81 |
82 | c.JSON(http.StatusOK, rs)
83 | }
84 |
85 | // 本接口提供基于小程序的图片高清化能力
86 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.superresolution.html
87 | func APIImgSuperResolutionByURL(c *gin.Context) {
88 | url, exist := c.GetQuery("url")
89 | if !exist {
90 | panic("parameter url expected")
91 | }
92 |
93 | rs, err := services.MiniProgramApp.Image.SuperResolution(c.Request.Context(), url, nil)
94 |
95 | if err != nil {
96 | panic(err)
97 | }
98 |
99 | c.JSON(http.StatusOK, rs)
100 | }
101 |
102 | // 本接口提供基于小程序的图片高清化能力
103 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/img/img.superresolution.html
104 | func APIImgSuperResolutionByData(c *gin.Context) {
105 | var err error
106 | mediaPath := "./resource/tree.png"
107 | value, err := ioutil.ReadFile(mediaPath)
108 |
109 | rs, err := services.MiniProgramApp.Image.SuperResolution(c.Request.Context(), "", &power.HashMap{
110 | "name": "tree.png", // 请确保文件名有准确的文件类型
111 | "value": value,
112 | })
113 |
114 | if err != nil {
115 | panic(err)
116 | }
117 |
118 | c.JSON(http.StatusOK, rs)
119 | }
120 |
--------------------------------------------------------------------------------
/controllers/payment/refund/refund.go:
--------------------------------------------------------------------------------
1 | package refund
2 |
3 | import (
4 | "context"
5 | request2 "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/partner/request"
6 | "github.com/gin-gonic/gin"
7 | "log"
8 | "net/http"
9 | "power-wechat-tutorial/services"
10 | "time"
11 | )
12 |
13 | func APIMakeOrder(c *gin.Context) {
14 | options := &request2.RequestJSAPIPrepay{
15 | Amount: &request2.JSAPIAmount{
16 | Total: 1,
17 | Currency: "CNY",
18 | },
19 | Attach: "自定义数据说明",
20 | Description: "Image形象店-深圳腾大-QQ公仔",
21 | OutTradeNo: "5519778939773395659222498002", // 这里是商户订单号,不能重复提交给微信
22 | Payer: &request2.JSAPIPayer{
23 | SpOpenId: "o4QEk5Mf1Do7utS7-SF5Go30s8i4", // 用户的openid, 记得也是动态的。
24 | },
25 | }
26 |
27 | // 如果需要覆盖掉全局的notify_url
28 | options.SetNotifyUrl("https://pay.xxx.com/wx/notify")
29 |
30 | // 下单
31 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
32 | defer cancel()
33 | response, err := services.PaymentApp.Partner.JSAPITransaction(ctx, options)
34 |
35 | if err != nil {
36 | log.Printf("error: %s", err)
37 | c.JSON(400, response)
38 | return
39 | }
40 |
41 | payConf, err := services.PaymentApp.JSSDK.BridgeConfig(response.PrepayID, true)
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | c.JSON(200, payConf)
47 | }
48 |
49 | // APIMakeOrderNative 生成Native支付二维码让用户扫
50 | func APIMakeOrderNative(c *gin.Context) {
51 | options := &request2.RequestNativePrepay{
52 | Amount: &request2.NativeAmount{
53 | Total: 1,
54 | Currency: "CNY",
55 | },
56 | Attach: "自定义数据说明",
57 | Description: "Image形象店-深圳腾大-QQ公仔",
58 | OutTradeNo: "55197789397733956592225981111", // 这里是商户订单号,不能重复提交给微信
59 | }
60 |
61 | response, err := services.PaymentApp.Partner.TransactionNative(c.Request.Context(), options)
62 |
63 | if err != nil {
64 | log.Printf("error: %s", err)
65 | c.JSON(400, response)
66 | return
67 | }
68 |
69 | c.JSON(200, response)
70 | }
71 |
72 | // APIMakeOrder App下单
73 | func APIMakeOrderApp(c *gin.Context) {
74 | options := &request2.RequestAppPrepay{
75 | Amount: &request2.AppAmount{
76 | Total: 1,
77 | Currency: "CNY",
78 | },
79 | Attach: "自定义数据说明",
80 | Description: "Image形象店-深圳腾大-QQ公仔",
81 | OutTradeNo: "5519778939773395659222498001", // 这里是商户订单号,不能重复提交给微信
82 | }
83 |
84 | // 如果需要覆盖掉全局的notify_url
85 | //options.SetNotifyUrl("https://pay.xxx.com/wx/notify")
86 |
87 | // 下单
88 | response, err := services.PaymentApp.Partner.TransactionApp(c.Request.Context(), options)
89 |
90 | if err != nil {
91 | log.Printf("error: %s", err)
92 | c.JSON(400, response)
93 | return
94 | }
95 |
96 | payConf, err := services.PaymentApp.JSSDK.BridgeConfig(response.PrepayID, true)
97 | if err != nil {
98 | panic(err)
99 | }
100 |
101 | c.JSON(200, payConf)
102 | }
103 |
104 | func APIQueryRefundOrder(c *gin.Context) {
105 |
106 | traceNo := c.Query("traceNo")
107 |
108 | rs, err := services.PaymentApp.Refund.Query(c.Request.Context(), traceNo)
109 | if err != nil {
110 | panic(err)
111 | }
112 | c.JSON(http.StatusOK, rs)
113 |
114 | }
115 |
116 | func APICloseOrder(c *gin.Context) {
117 | traceNo := c.Query("traceNo")
118 | log.Printf("traceNo: %s", traceNo)
119 |
120 | rs, err := services.PaymentApp.Partner.Close(c.Request.Context(), traceNo)
121 | if err != nil {
122 | log.Println("出错了: ", err)
123 | c.String(400, err.Error())
124 | return
125 | }
126 | c.JSON(http.StatusOK, rs)
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/controllers/payment/partner/payment.go:
--------------------------------------------------------------------------------
1 | package partner
2 |
3 | import (
4 | "context"
5 | request2 "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/partner/request"
6 | "github.com/gin-gonic/gin"
7 | "log"
8 | "net/http"
9 | "power-wechat-tutorial/services"
10 | "time"
11 | )
12 |
13 | func APIMakeOrder(c *gin.Context) {
14 | options := &request2.RequestJSAPIPrepay{
15 | Amount: &request2.JSAPIAmount{
16 | Total: 1,
17 | Currency: "CNY",
18 | },
19 | Attach: "自定义数据说明",
20 | Description: "Image形象店-深圳腾大-QQ公仔",
21 | OutTradeNo: "5519778939773395659222498002", // 这里是商户订单号,不能重复提交给微信
22 | Payer: &request2.JSAPIPayer{
23 | SpOpenId: "o4QEk5Mf1Do7utS7-SF5Go30s8i4", // 用户的openid, 记得也是动态的。
24 | },
25 | }
26 |
27 | // 如果需要覆盖掉全局的notify_url
28 | //options.SetNotifyUrl("https://pay.xxx.com/wx/notify")
29 |
30 | // 下单
31 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
32 | defer cancel()
33 | response, err := services.PaymentApp.Partner.JSAPITransaction(ctx, options)
34 |
35 | if err != nil {
36 | log.Printf("error: %s", err)
37 | c.JSON(400, response)
38 | return
39 | }
40 |
41 | payConf, err := services.PaymentApp.JSSDK.BridgeConfig(response.PrepayID, true)
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | c.JSON(200, payConf)
47 | }
48 |
49 | // APIMakeOrderNative 生成Native支付二维码让用户扫
50 | func APIMakeOrderNative(c *gin.Context) {
51 | options := &request2.RequestNativePrepay{
52 | Amount: &request2.NativeAmount{
53 | Total: 1,
54 | Currency: "CNY",
55 | },
56 | Attach: "自定义数据说明",
57 | Description: "Image形象店-深圳腾大-QQ公仔",
58 | OutTradeNo: "55197789397733956592225981111", // 这里是商户订单号,不能重复提交给微信
59 | }
60 |
61 | response, err := services.PaymentApp.Partner.TransactionNative(c.Request.Context(), options)
62 |
63 | if err != nil {
64 | log.Printf("error: %s", err)
65 | c.JSON(400, response)
66 | return
67 | }
68 |
69 | c.JSON(200, response)
70 | }
71 |
72 | // APIMakeOrder App下单
73 | func APIMakeOrderApp(c *gin.Context) {
74 | options := &request2.RequestAppPrepay{
75 | Amount: &request2.AppAmount{
76 | Total: 1,
77 | Currency: "CNY",
78 | },
79 | Attach: "自定义数据说明",
80 | Description: "Image形象店-深圳腾大-QQ公仔",
81 | OutTradeNo: "5519778939773395659222498001", // 这里是商户订单号,不能重复提交给微信
82 | }
83 |
84 | // 如果需要覆盖掉全局的notify_url
85 | //options.SetNotifyUrl("https://pay.xxx.com/wx/notify")
86 |
87 | // 下单
88 | response, err := services.PaymentApp.Partner.TransactionApp(c.Request.Context(), options)
89 |
90 | if err != nil {
91 | log.Printf("error: %s", err)
92 | c.JSON(400, response)
93 | return
94 | }
95 |
96 | payConf, err := services.PaymentApp.JSSDK.BridgeConfig(response.PrepayID, true)
97 | if err != nil {
98 | panic(err)
99 | }
100 |
101 | c.JSON(200, payConf)
102 | }
103 |
104 | func APIQueryOrder(c *gin.Context) {
105 |
106 | traceNo := c.Query("traceNo")
107 |
108 | rs, err := services.PaymentApp.Partner.QueryByOutTradeNumber(c.Request.Context(), traceNo)
109 | if err != nil {
110 | panic(err)
111 | }
112 | c.JSON(http.StatusOK, rs)
113 |
114 | }
115 |
116 | func APICloseOrder(c *gin.Context) {
117 | traceNo := c.Query("traceNo")
118 | log.Printf("traceNo: %s", traceNo)
119 |
120 | rs, err := services.PaymentApp.Partner.Close(c.Request.Context(), traceNo)
121 | if err != nil {
122 | log.Println("出错了: ", err)
123 | c.String(400, err.Error())
124 | return
125 | }
126 | c.JSON(http.StatusOK, rs)
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/schedule.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | "strconv"
9 | )
10 |
11 | // 创建日程
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93648
13 | func APIScheduleAdd(c *gin.Context) {
14 |
15 | agentId := c.DefaultQuery("agentID", "1000014")
16 | agentID, _ := strconv.Atoi(agentId)
17 |
18 | schedule := &power.HashMap{
19 | "organizer": "userid1",
20 | "start_time": 1571274600,
21 | "end_time": 1571320210,
22 | "attendees": []power.HashMap{
23 | power.HashMap{
24 | "userid": "userid2",
25 | },
26 | },
27 | "summary": "需求评审会议",
28 | "description": "2.0版本需求初步评审",
29 | "reminders": power.HashMap{
30 | "is_remind": 1,
31 | "remind_before_event_secs": 3600,
32 | "is_repeat": 1,
33 | "repeat_type": 7,
34 | "repeat_until": 1606976813,
35 | "is_custom_repeat": 1,
36 | "repeat_interval": 1,
37 | "repeat_day_of_week": []int{3, 7},
38 | "repeat_day_of_month": []int{10, 21},
39 | "timezone": 8,
40 | },
41 | "location": "广州国际媒体港10楼1005会议室",
42 | "cal_id": "wcjgewCwAAqeJcPI1d8Pwbjt7nttzAAA",
43 | }
44 |
45 | res, err := services.WeComApp.OASchedule.Add(c.Request.Context(), schedule, agentID)
46 |
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | c.JSON(http.StatusOK, res)
52 | }
53 |
54 | // 更新日程
55 | // https://work.weixin.qq.com/api/doc/90000/90135/93648
56 | func APIScheduleUpdate(c *gin.Context) {
57 |
58 | schedule := &power.HashMap{
59 | "organizer": "userid1",
60 | "schedule_id": "17c7d2bd9f20d652840f72f59e796AAA",
61 | "start_time": 1571274600,
62 | "end_time": 1571320210,
63 | "attendees": []power.HashMap{
64 | {
65 | "userid": "userid2",
66 | },
67 | },
68 | "summary": "test_summary",
69 | "description": "test_description",
70 | "reminders": power.HashMap{
71 | "is_remind": 1,
72 | "remind_before_event_secs": 3600,
73 | "is_repeat": 1,
74 | "repeat_type": 7,
75 | "repeat_until": 1606976813,
76 | "is_custom_repeat": 1,
77 | "repeat_interval": 1,
78 | "repeat_day_of_week": []int{3, 7},
79 | "repeat_day_of_month": []int{10, 21},
80 | "timezone": 8,
81 | },
82 | "location": "test_place",
83 | }
84 | res, err := services.WeComApp.OASchedule.Update(c.Request.Context(), schedule)
85 |
86 | if err != nil {
87 | panic(err)
88 | }
89 |
90 | c.JSON(http.StatusOK, res)
91 | }
92 |
93 | // 获取日程详情
94 | // https://work.weixin.qq.com/api/doc/90000/90135/93648
95 | func APIScheduleGet(c *gin.Context) {
96 | scheduleIDList := []string{
97 | c.DefaultQuery("scheduleID", "17c7d2bd9f20d652840f72f59e796AAA"),
98 | }
99 | res, err := services.WeComApp.OASchedule.Get(c.Request.Context(), scheduleIDList)
100 |
101 | if err != nil {
102 | panic(err)
103 | }
104 |
105 | c.JSON(http.StatusOK, res)
106 | }
107 |
108 | // 删除日程
109 | // https://work.weixin.qq.com/api/doc/90000/90135/93648
110 | func APIScheduleDel(c *gin.Context) {
111 | scheduleID := c.DefaultQuery("scheduleID", "17c7d2bd9f20d652840f72f59e796AAA")
112 | res, err := services.WeComApp.OASchedule.Del(c.Request.Context(), scheduleID)
113 |
114 | if err != nil {
115 | panic(err)
116 | }
117 |
118 | c.JSON(http.StatusOK, res)
119 | }
120 |
--------------------------------------------------------------------------------
/controllers/official-account/comment.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | "power-wechat-tutorial/services"
7 | "strconv"
8 | )
9 |
10 | // CommentOpen 打开已群发文章评论
11 | func CommentOpen(ctx *gin.Context) {
12 | msgID := ctx.Query("msgID")
13 | data, err := services.OfficialAccountApp.Comment.Open(ctx.Request.Context(), msgID, 0)
14 | if err != nil {
15 | ctx.String(http.StatusBadRequest, err.Error())
16 | return
17 | }
18 | ctx.JSON(http.StatusOK, data)
19 | }
20 |
21 | // CommentClose 关闭已群发文章评论
22 | func CommentClose(ctx *gin.Context) {
23 | msgID := ctx.Query("msgID")
24 | data, err := services.OfficialAccountApp.Comment.Close(ctx.Request.Context(), msgID, 0)
25 | if err != nil {
26 | ctx.String(http.StatusBadRequest, err.Error())
27 | return
28 | }
29 | ctx.JSON(http.StatusOK, data)
30 | }
31 |
32 | // CommentList 查看指定文章的评论数据
33 | func CommentList(ctx *gin.Context) {
34 | msgID := ctx.Query("msgID")
35 | data, err := services.OfficialAccountApp.Comment.List(ctx.Request.Context(), msgID, 0, 0, 100, 0)
36 | if err != nil {
37 | ctx.String(http.StatusBadRequest, err.Error())
38 | return
39 | }
40 | ctx.JSON(http.StatusOK, data)
41 | }
42 |
43 | // CommentMarkElect 将评论标记精选
44 | func CommentMarkElect(ctx *gin.Context) {
45 | msgID := ctx.Query("msgID")
46 | commentIDStr := ctx.Query("commentID")
47 | commentID, _ := strconv.Atoi(commentIDStr)
48 | data, err := services.OfficialAccountApp.Comment.MarkElect(ctx.Request.Context(), msgID, 0, commentID)
49 | if err != nil {
50 | ctx.String(http.StatusBadRequest, err.Error())
51 | return
52 | }
53 | ctx.JSON(http.StatusOK, data)
54 | }
55 |
56 | // CommentUnMarkElect 将评论取消精选
57 | func CommentUnMarkElect(ctx *gin.Context) {
58 | msgID := ctx.Query("msgID")
59 | commentIDStr := ctx.Query("commentID")
60 | commentID, _ := strconv.Atoi(commentIDStr)
61 | data, err := services.OfficialAccountApp.Comment.UnmarkElect(ctx.Request.Context(), msgID, 0, commentID)
62 | if err != nil {
63 | ctx.String(http.StatusBadRequest, err.Error())
64 | return
65 | }
66 | ctx.JSON(http.StatusOK, data)
67 | }
68 |
69 | // CommentDelete 删除评论
70 | func CommentDelete(ctx *gin.Context) {
71 | msgID := ctx.Query("msgID")
72 | commentIDStr := ctx.Query("commentID")
73 | commentID, _ := strconv.Atoi(commentIDStr)
74 | data, err := services.OfficialAccountApp.Comment.Delete(ctx.Request.Context(), msgID, 0, commentID)
75 | if err != nil {
76 | ctx.String(http.StatusBadRequest, err.Error())
77 | return
78 | }
79 | ctx.JSON(http.StatusOK, data)
80 | }
81 |
82 | // CommentReply 回复评论
83 | func CommentReply(ctx *gin.Context) {
84 | msgID := ctx.Query("msgID")
85 | commentIDStr := ctx.Query("commentID")
86 | content := ctx.Query("content")
87 | commentID, _ := strconv.Atoi(commentIDStr)
88 | data, err := services.OfficialAccountApp.Comment.Reply(ctx.Request.Context(), msgID, 0, commentID, content)
89 | if err != nil {
90 | ctx.String(http.StatusBadRequest, err.Error())
91 | return
92 | }
93 | ctx.JSON(http.StatusOK, data)
94 | }
95 |
96 | // CommentDeleteReply 删除回复评论
97 | func CommentDeleteReply(ctx *gin.Context) {
98 | msgID := ctx.Query("msgID")
99 | commentIDStr := ctx.Query("commentID")
100 | commentID, _ := strconv.Atoi(commentIDStr)
101 | data, err := services.OfficialAccountApp.Comment.DeleteReply(ctx.Request.Context(), msgID, 0, commentID)
102 | if err != nil {
103 | ctx.String(http.StatusBadRequest, err.Error())
104 | return
105 | }
106 | ctx.JSON(http.StatusOK, data)
107 | }
108 |
--------------------------------------------------------------------------------
/controllers/official-account/menu.go:
--------------------------------------------------------------------------------
1 | package official_account
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/menu/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | func MenuList(ctx *gin.Context) {
11 | data, err := services.OfficialAccountApp.Menu.Get(ctx.Request.Context())
12 | if err != nil {
13 | ctx.String(http.StatusBadRequest, err.Error())
14 | return
15 | }
16 | ctx.JSON(http.StatusOK, data)
17 | }
18 |
19 | func MenuGet(ctx *gin.Context) {
20 | data, err := services.OfficialAccountApp.Menu.Get(ctx.Request.Context())
21 | if err != nil {
22 | ctx.String(http.StatusBadRequest, err.Error())
23 | return
24 | }
25 | ctx.JSON(http.StatusOK, data)
26 | }
27 |
28 | func MenuCurrent(ctx *gin.Context) {
29 | data, err := services.OfficialAccountApp.Menu.CurrentSelfMenu(ctx.Request.Context())
30 | if err != nil {
31 | ctx.String(http.StatusBadRequest, err.Error())
32 | return
33 | }
34 | ctx.JSON(http.StatusOK, data)
35 | }
36 |
37 | func MenuCreate(ctx *gin.Context) {
38 | data, err := services.OfficialAccountApp.Menu.Create(ctx.Request.Context(), []*request.Button{
39 | {
40 | Type: "view",
41 | Name: "最新文章",
42 | URL: "https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5NDY5ODc5NA==&action=getalbum&album_id=2690448178684411909#wechat_redirect",
43 | },
44 | //{
45 | // Name: "知识分类",
46 | // SubButtons: []request.SubButton{
47 | // {
48 | // Type: "click",
49 | // Name: "AI市场",
50 | // URL: "V1001_AI_MARKET",
51 | // },
52 | // {
53 | // Type: "click",
54 | // Name: "AI技术",
55 | // Key: "V1001_AI_TECH",
56 | // },
57 | // {
58 | // Type: "click",
59 | // Name: "AI运营",
60 | // Key: "V1001_AI_OPERATION",
61 | // },
62 | // {
63 | // Type: "view",
64 | // Name: "AI方案",
65 | // Key: "V1001_AI_SOLUTION",
66 | // },
67 | // },
68 | //}
69 | })
70 | if err != nil {
71 | ctx.String(http.StatusBadRequest, err.Error())
72 | return
73 | }
74 | ctx.JSON(http.StatusOK, data)
75 | }
76 |
77 | func MenuCreateConditional(ctx *gin.Context) {
78 | data, err := services.OfficialAccountApp.Menu.CreateConditional(ctx.Request.Context(), []*request.Button{
79 | {
80 | Type: "click",
81 | Name: "今日歌曲",
82 | Key: "V1001_TODAY_MUSIC",
83 | },
84 | }, &request.RequestMatchRule{
85 | Sex: "1",
86 | Country: "中国",
87 | Province: "广东",
88 | City: "广州",
89 | ClientPlatformType: "2",
90 | })
91 | if err != nil {
92 | ctx.String(http.StatusBadRequest, err.Error())
93 | return
94 | }
95 | ctx.JSON(http.StatusOK, data)
96 | }
97 |
98 | func MenuDelete(ctx *gin.Context) {
99 | data, err := services.OfficialAccountApp.Menu.Delete(ctx.Request.Context())
100 | if err != nil {
101 | ctx.String(http.StatusBadRequest, err.Error())
102 | return
103 | }
104 | ctx.JSON(http.StatusOK, data)
105 | }
106 |
107 | func MenuDeleteConditional(ctx *gin.Context) {
108 | data, err := services.OfficialAccountApp.Menu.DeleteConditional(ctx.Request.Context(), 1)
109 | if err != nil {
110 | ctx.String(http.StatusBadRequest, err.Error())
111 | return
112 | }
113 | ctx.JSON(http.StatusOK, data)
114 | }
115 |
116 | func MenuMatch(ctx *gin.Context) {
117 | userID := ctx.Query("userID")
118 | data, err := services.OfficialAccountApp.Menu.TryMatch(ctx.Request.Context(), userID)
119 | if err != nil {
120 | ctx.String(http.StatusBadRequest, err.Error())
121 | return
122 | }
123 | ctx.JSON(http.StatusOK, data)
124 | }
125 |
--------------------------------------------------------------------------------
/routes/payment.go:
--------------------------------------------------------------------------------
1 | package routes
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "power-wechat-tutorial/controllers/payment"
6 | "power-wechat-tutorial/controllers/payment/merchant"
7 | "power-wechat-tutorial/controllers/payment/merchantService"
8 | "power-wechat-tutorial/controllers/payment/partner"
9 | "power-wechat-tutorial/controllers/payment/paymentScore"
10 | "power-wechat-tutorial/controllers/payment/redpack"
11 | "power-wechat-tutorial/controllers/payment/refund"
12 | "power-wechat-tutorial/controllers/payment/tax"
13 | "power-wechat-tutorial/controllers/payment/transfer"
14 | )
15 |
16 | func InitPaymentAPIRoutes(r *gin.Engine) {
17 |
18 | r.Static("/wx/payment", "./templates")
19 | r.POST("/wx/notify", payment.CallbackWXNotify)
20 | r.POST("/v3/pay/transactions/out-trade-no/5519778939773395659222199111/close", payment.APIMockCloseOrderResponse)
21 |
22 | apiRouterPayment := r.Group("/payment")
23 | {
24 | // Handle the pay route
25 | apiRouterPayment.GET("/order/make", payment.APIMakeOrder)
26 | apiRouterPayment.GET("/order/make/native", payment.APIMakeOrderNative)
27 | apiRouterPayment.GET("/order/make/app", payment.APIMakeOrderApp)
28 | apiRouterPayment.GET("/order/query", payment.APIQueryOrder)
29 | apiRouterPayment.GET("/order/close", payment.APICloseOrder)
30 | apiRouterPayment.GET("/order/refund", payment.APIRefundOrder)
31 | apiRouterPayment.GET("/order/revertOrderByOutTradeNumber", payment.APIRevertOrderByOutTradeNumber)
32 |
33 | apiRouterPayment.GET("/order/refund/query", refund.APIQueryRefundOrder)
34 |
35 | // Handle the partner pay route
36 | apiRouterPayment.GET("/partner/make", partner.APIMakeOrder)
37 | apiRouterPayment.GET("/partner/make/native", partner.APIMakeOrderNative)
38 | apiRouterPayment.GET("/partner/make/app", partner.APIMakeOrderApp)
39 | apiRouterPayment.GET("/partner/query", partner.APIQueryOrder)
40 | apiRouterPayment.GET("/partner/close", partner.APICloseOrder)
41 |
42 | // Handle the bill route
43 | apiRouterPayment.GET("/bill/downloadURL", payment.APIBillDownloadURL)
44 |
45 | apiRouterPayment.GET("/merchant/uploadImg", merchant.APIUploadImg)
46 | apiRouterPayment.GET("/merchantService/complaints", merchantService.APIComplaints)
47 |
48 | // Handle payment route
49 | apiRouterPayment.GET("redpack/sendNormal", redpack.APISendNormal)
50 | apiRouterPayment.GET("redpack/info", redpack.APIQueryRedPack)
51 | apiRouterPayment.GET("work/sendworkwxredpack", redpack.APIWorkSendWXRedpack)
52 | apiRouterPayment.GET("transfer/toBalance", transfer.APIToTransfer)
53 | apiRouterPayment.GET("transfer/toBankCard", transfer.APIToBankCard)
54 | apiRouterPayment.GET("transfer/queryBalanceOrder", transfer.APIQueryBalanceOrder)
55 | apiRouterPayment.GET("transfer/batch/batchTransfer", transfer.APIBatchTransfer)
56 | apiRouterPayment.GET("transfer/batch/queryBatchOrder", transfer.APIQueryBatchOrder)
57 | apiRouterPayment.GET("transfer/batch/queryBatchOrderDetail", transfer.APIQueryBatchOrderDetail)
58 |
59 | // Handle security route
60 | apiRouterPayment.GET("security/getRSAPublicKey", payment.APIGetRSAPublicKey)
61 | apiRouterPayment.POST("security/decryptCertificate", payment.APIDecryptCertificate)
62 | apiRouterPayment.GET("security/getCertificates", payment.APIGetCertificates)
63 |
64 | apiRouterPayment.GET("tax/applyForCardTemplate", tax.APIApplyForCardTemplate)
65 |
66 | apiRouterPayment.GET("paymentScore/serviceOrder", paymentScore.APIServiceOrder)
67 |
68 | // Handle profitSharing route
69 | apiRouterPayment.GET("profitSharing/orders", payment.APIOrders)
70 | apiRouterPayment.GET("profitSharing/addReceiver", payment.APIAddReceiver)
71 |
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/controllers/wecom/external-contact/transfer.go:
--------------------------------------------------------------------------------
1 | package external_contact
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/externalContact/transfer/request"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 | )
9 |
10 | // 分配在职成员的客户
11 | // https://work.weixin.qq.com/api/doc/90000/90135/92125
12 | func APIExternalContactTransferCustomer(c *gin.Context) {
13 | options := &request.RequestTransferCustomer{
14 | HandoverUserID: c.DefaultQuery("handoverUserID", "walle"),
15 | TakeoverUserID: c.DefaultQuery("takeoverUserID", "matrix-x"),
16 | ExternalUserID: []string{c.DefaultQuery("externalUserID", "woAJ2GCAAAXtWyujaWJHDDGi0mACAAAA")},
17 | TransferSuccessMsg: "您好,您的服务已升级,后续将由我的同事李四@腾讯接替我的工作,继续为您服务。",
18 | }
19 |
20 | res, err := services.WeComContactApp.ExternalContactTransfer.TransferCustomer(c.Request.Context(), options)
21 |
22 | if err != nil {
23 | panic(err)
24 | }
25 |
26 | c.JSON(http.StatusOK, res)
27 | }
28 |
29 | // 查询客户接替状态
30 | // https://work.weixin.qq.com/api/doc/90000/90135/94088
31 | func APIExternalContactTransferResult(c *gin.Context) {
32 |
33 | options := &request.RequestTransferResult{
34 | HandoverUserID: "walle",
35 | TakeoverUserID: "matrix-x",
36 | Cursor: c.DefaultQuery("cursor", "CURSOR"),
37 | }
38 | res, err := services.WeComContactApp.ExternalContactTransfer.TransferResult(c.Request.Context(), options)
39 |
40 | if err != nil {
41 | panic(err)
42 | }
43 |
44 | c.JSON(http.StatusOK, res)
45 | }
46 |
47 | // 获取待分配的离职成员列表
48 | // https://work.weixin.qq.com/api/doc/90000/90135/92125
49 | func APIExternalContactGetUnassignedList(c *gin.Context) {
50 |
51 | pageID := 1
52 | pageSize := 1000
53 | cursor := ""
54 |
55 | res, err := services.WeComContactApp.ExternalContactTransfer.GetUnassignedList(c.Request.Context(), pageID, cursor, pageSize)
56 |
57 | if err != nil {
58 | panic(err)
59 | }
60 |
61 | c.JSON(http.StatusOK, res)
62 | }
63 |
64 | // 分配离职成员的客户
65 | // https://work.weixin.qq.com/api/doc/90000/90135/94081
66 | func APIExternalContactResignedTransferCustomer(c *gin.Context) {
67 |
68 | options := &request.RequestResignedTransferCustomer{
69 | HandoverUserID: "walle",
70 | TakeoverUserID: "matrix-x",
71 | ExternalUserID: []string{"woAJ2GCAAAXtWyujaWJHDDGi0mACBBBB"},
72 | }
73 |
74 | res, err := services.WeComContactApp.ExternalContactTransfer.ResignedTransferCustomer(c.Request.Context(), options)
75 |
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | c.JSON(http.StatusOK, res)
81 | }
82 |
83 | // 查询客户接替状态
84 | // https://work.weixin.qq.com/api/doc/90000/90135/94082
85 | func APIExternalContactResignedTransferResult(c *gin.Context) {
86 | options := &request.RequestResignedTransferResult{
87 | HandoverUserID: "walle",
88 | TakeoverUserID: "matrix-x",
89 | Cursor: c.DefaultQuery("cursor", "CURSOR"),
90 | }
91 |
92 | res, err := services.WeComContactApp.ExternalContactTransfer.ResignedTransferResult(c.Request.Context(), options)
93 |
94 | if err != nil {
95 | panic(err)
96 | }
97 |
98 | c.JSON(http.StatusOK, res)
99 | }
100 |
101 | // 分配离职成员的客户群
102 | // https://work.weixin.qq.com/api/doc/90000/90135/92127
103 | func APIExternalContactGroupChatTransfer(c *gin.Context) {
104 |
105 | options := &request.RequestGroupChatTransfer{
106 | ChatIDList: []string{"wrOgQhDgAAcwMTB7YmDkbeBsgT_AAAA", "wrOgQhDgAAMYQiS5ol9G7gK9JVQUAAAA"},
107 | NewOwner: "walle",
108 | }
109 |
110 | res, err := services.WeComContactApp.ExternalContactTransfer.GroupChatTransfer(c.Request.Context(), options)
111 |
112 | if err != nil {
113 | panic(err)
114 | }
115 |
116 | c.JSON(http.StatusOK, res)
117 | }
118 |
--------------------------------------------------------------------------------
/docs/docs.go:
--------------------------------------------------------------------------------
1 | // Package docs Code generated by swaggo/swag. DO NOT EDIT
2 | package docs
3 |
4 | import "github.com/swaggo/swag"
5 |
6 | const docTemplate = `{
7 | "schemes": {{ marshal .Schemes }},
8 | "swagger": "2.0",
9 | "info": {
10 | "description": "{{escape .Description}}",
11 | "title": "{{.Title}}",
12 | "termsOfService": "http://artisan-cloud.com/terms/",
13 | "contact": {
14 | "name": "ArtisanCloud Support",
15 | "url": "https://powerwechat.artisan-cloud.com/zh/start/qa.html",
16 | "email": "matrix-x@artisan-cloud.como"
17 | },
18 | "license": {
19 | "name": "MIT 2.0",
20 | "url": "https://github.com/ArtisanCloud/PowerWeChat?tab=MIT-1-ov-file#readme"
21 | },
22 | "version": "{{.Version}}"
23 | },
24 | "host": "{{.Host}}",
25 | "basePath": "{{.BasePath}}",
26 | "paths": {
27 | "/clearQuota": {
28 | "get": {
29 | "description": "\nSDK产品接口的代码展示:\n` + "`" + `` + "`" + `` + "`" + `\n返回类型定义如下:\ntype ResponseOfficialAccount struct {\n\tResponseBase\n\n\tErrCode int ` + "`" + `json:\"errcode,omitempty\"` + "`" + `\n\tErrMsg string ` + "`" + `json:\"errmsg,omitempty\"` + "`" + `\n\n\tResultCode string ` + "`" + `json:\"resultcode,omitempty\"` + "`" + `\n\tResultMsg string ` + "`" + `json:\"resultmsg,omitempty\"` + "`" + `\n}\n\n具体使用接口方式:\nfunc ClearQuota(ctx *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.ClearQuota(ctx.Request.Context())\n\tif err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK, data)\n}\n` + "`" + `` + "`" + `` + "`" + `",
30 | "tags": [
31 | "OfficialAccount.base.ClearQuota"
32 | ],
33 | "summary": "公众号里清空api的调用quota:https://developers.weixin.qq.com/doc/offiaccount/openApi/clear_quota.html",
34 | "responses": {}
35 | }
36 | },
37 | "/getCallbackIp": {
38 | "get": {
39 | "description": "\nSDK产品接口的代码展示:\n` + "`" + `` + "`" + `` + "`" + `\n返回类型定义如下:\ntype ResponseGetCallBackIP struct {\n\tresponse.ResponseOfficialAccount\n\n\tIPList []string ` + "`" + `json:\"ip_list\"` + "`" + `\n}\n\n具体使用接口方式:\nfunc GetCallbackIP(ctx *gin.Context) {\n\tdata, err := services.OfficialAccountApp.Base.GetCallbackIP(ctx.Request.Context())\n\tif err != nil {\n\t\tctx.String(http.StatusBadRequest, err.Error())\n\t\treturn\n\t}\n\tctx.JSON(http.StatusOK, data)\n}\n` + "`" + `` + "`" + `` + "`" + `",
40 | "tags": [
41 | "OfficialAccount.base.GetCallbackIP"
42 | ],
43 | "summary": "获取公众号回调的IP地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html",
44 | "responses": {}
45 | }
46 | }
47 | },
48 | "securityDefinitions": {
49 | "BasicAuth": {
50 | "type": "basic"
51 | }
52 | },
53 | "externalDocs": {
54 | "description": "OpenAPI",
55 | "url": "https://swagger.io/resources/open-api/"
56 | }
57 | }`
58 |
59 | // SwaggerInfo holds exported Swagger Info so clients can modify it
60 | var SwaggerInfo = &swag.Spec{
61 | Version: "1.0.1",
62 | Host: "localhost:8080",
63 | BasePath: "/api/v1",
64 | Schemes: []string{},
65 | Title: "PowerWechat API Docs",
66 | Description: "这是一个开源的使用教程,包含PowerWechat的大部分接口调试代码.",
67 | InfoInstanceName: "swagger",
68 | SwaggerTemplate: docTemplate,
69 | LeftDelim: "{{",
70 | RightDelim: "}}",
71 | }
72 |
73 | func init() {
74 | swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
75 | }
76 |
--------------------------------------------------------------------------------
/controllers/wecom/oauth-controller.go:
--------------------------------------------------------------------------------
1 | package wecom
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "log"
6 | "net/http"
7 | "power-wechat-tutorial/services"
8 |
9 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | func WebAuthorizeUser(ctx *gin.Context) {
14 |
15 | // $callbackUrl 为授权回调地址
16 | callbackUrl := services.WeComApp.GetConfig().GetString("oauth.callbacks", "artisan-cloud.com") + "/callback/authorized/user" // 需设置可信域名
17 | services.WeComApp.OAuth.Provider.WithRedirectURL(callbackUrl)
18 |
19 | // 返回一个 redirect 实例
20 | redirectURL, _ := services.WeComApp.OAuth.Provider.GetAuthURL()
21 |
22 | log.Println("redirectURL: ", redirectURL)
23 |
24 | // 请在企业微信客户端打开链接
25 | //http.Redirect(ctx.Writer, ctx.Request, redirectURL, http.StatusFound)
26 | ctx.Redirect(http.StatusFound, redirectURL)
27 | }
28 |
29 | func WebAuthorizedUser(ctx *gin.Context) {
30 |
31 | code := ctx.Query("code")
32 | user, err := services.WeComApp.OAuth.Provider.Detailed().UserFromCode(code)
33 | //user, err := services.WeComApp.OAuth.Provider.ContactFromCode(code)
34 | if err != nil {
35 | ctx.String(http.StatusBadRequest, err.Error())
36 | }
37 |
38 | rawData, err := user.GetRaw()
39 | fmt.Dump(rawData)
40 | fmt.Dump((*rawData)["id"])
41 | fmt.Dump(user.Attributes["id"])
42 |
43 | if err != nil {
44 | ctx.String(http.StatusBadRequest, err.Error())
45 | }
46 |
47 | rs := &power.HashMap{
48 | "deviceID": user.GetDeviceID(),
49 | "userID": user.GetID(),
50 | "openID": user.GetOpenID(),
51 | }
52 |
53 | //// 正常返回json
54 | ctx.JSON(http.StatusOK, rs)
55 | }
56 |
57 | func WebAuthorizedUserV2(ctx *gin.Context) {
58 |
59 | code := ctx.Query("code")
60 | user, err := services.WeComApp.OAuth.Provider.GetUserInfo(code)
61 | if err != nil {
62 | ctx.String(http.StatusBadRequest, err.Error())
63 | return
64 | }
65 | if user.ErrCode != 0 {
66 | ctx.JSON(http.StatusBadRequest, user)
67 | return
68 | }
69 |
70 | accessToken, err := services.WeComApp.AccessToken.GetToken(ctx.Request.Context(), false)
71 | if err != nil {
72 | ctx.String(http.StatusBadRequest, err.Error())
73 | return
74 | }
75 | if accessToken.ErrCode != 0 {
76 | ctx.JSON(http.StatusBadRequest, accessToken)
77 | return
78 | }
79 |
80 | userDetail, err := services.WeComApp.OAuth.Provider.WithApiAccessToken(accessToken.AccessToken).GetUserDetail(user.UserTicket)
81 | if err != nil {
82 | ctx.String(http.StatusBadRequest, err.Error())
83 | return
84 | }
85 |
86 | if userDetail.ErrCode != 0 {
87 | ctx.JSON(http.StatusBadRequest, userDetail)
88 | return
89 | }
90 |
91 | //// 正常返回json
92 | ctx.JSON(http.StatusOK, userDetail)
93 | }
94 |
95 | func WebAuthorizeContact(ctx *gin.Context) {
96 |
97 | // $callbackUrl 为授权回调地址
98 | // 需设置可信域名
99 | callbackUrl := services.WeComApp.GetConfig().GetString("oauth.callbacks", "") + "/callback/authorized/contact"
100 |
101 | // 返回一个 redirect 实例
102 | services.WeComApp.OAuth.Provider.WithRedirectURL(callbackUrl)
103 | redirectURL, _ := services.WeComApp.OAuth.Provider.GetQrConnectURL()
104 |
105 | log.Println("redirectURL: ", redirectURL)
106 |
107 | // 直接跳转到企业微信授权
108 | http.Redirect(ctx.Writer, ctx.Request, redirectURL, http.StatusFound)
109 |
110 | }
111 |
112 | func WebAuthorizedContact(ctx *gin.Context) {
113 |
114 | code := ctx.Query("code")
115 | user, err := services.WeComApp.OAuth.Provider.Detailed().ContactFromCode(code)
116 | if err != nil {
117 | panic(err)
118 | }
119 |
120 | rs := &power.HashMap{
121 | "openID": user.GetOpenID(),
122 | "userID": user.GetID(),
123 | "name": user.GetName(),
124 | "avatar": user.GetAvatar(),
125 | }
126 |
127 | //// 正常返回json
128 | ctx.JSON(http.StatusOK, rs)
129 |
130 | }
131 |
--------------------------------------------------------------------------------
/controllers/open-platform/open-platform.go:
--------------------------------------------------------------------------------
1 | package open_platform
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerLibs/v3/object"
6 | "github.com/gin-gonic/gin"
7 | "power-wechat-tutorial/services"
8 | "strconv"
9 | )
10 |
11 | func GetPreAuthorizationUrl(ctx *gin.Context) {
12 | appID := ctx.DefaultQuery("app_id", "wx3c7e1c9f9f1f9f9f")
13 | refreshToken := ctx.DefaultQuery("refresh_token", "wx3c7e1c9f9f1f9f9f")
14 | //services.OpenPlatformApp.GetMobilePreAuthorizationURL()
15 | account, _ := services.OpenPlatformApp.OfficialAccount(appID, refreshToken, nil)
16 | a := account.Account
17 | a.Create(ctx.Request.Context())
18 | }
19 |
20 | // 获取PreAuthorizationCode,component token会自动缓存
21 | func APIOpenPlatformPreAuthCode(ctx *gin.Context) {
22 | // 如果之前在回调的时候,已经将获取的ComponentVerifyTicket通过SetTicket缓存后,则可以注销代码,跳过这个环节
23 | ticket := ctx.Query("ticket")
24 | err := services.OpenPlatformApp.VerifyTicket.SetTicket(ticket)
25 | if err != nil {
26 | panic(err)
27 | }
28 |
29 | rs, err := services.OpenPlatformApp.Base.CreatePreAuthorizationCode(ctx.Request.Context())
30 | if err != nil {
31 | panic(err)
32 | }
33 | fmt.Dump(rs)
34 | ctx.JSON(200, rs)
35 | }
36 |
37 | func GetFastRegistrationURL(ctx *gin.Context) {
38 | // 如果之前在回调的时候,已经将获取的ComponentVerifyTicket通过SetTicket缓存后,则可以注销代码,跳过这个环节
39 | //ticket, _ := services.OpenPlatformApp.VerifyTicket.GetTicket()
40 | //if ticket == "" {
41 | // ticket = ctx.Query("ticket")
42 | // err := services.OpenPlatformApp.VerifyTicket.SetTicket(ticket)
43 | // if err != nil {
44 | // panic(err)
45 | // }
46 | //}
47 |
48 | url, err := services.OpenPlatformApp.GetFastRegistrationURL(ctx.Request.Context(), "https://michael.debug.artisancloud.cn/wx/api/openplatform/callback", &object.StringMap{
49 | "auth_type": "1",
50 | })
51 |
52 | if err != nil {
53 | panic(err)
54 | }
55 |
56 | ctx.JSON(200, url)
57 | }
58 |
59 | // HandleAuthorize Code换调用凭据信息
60 | func HandleAuthorize(ctx *gin.Context) {
61 | authCode := ctx.DefaultQuery("auth_code", "")
62 | res, err := services.OpenPlatformApp.Base.HandleAuthorize(ctx.Request.Context(), authCode)
63 | if err != nil {
64 | panic(err)
65 | }
66 | ctx.JSON(200, res)
67 | }
68 |
69 | // GetAuthorizer 获取授权方的帐号基本信息
70 | func GetAuthorizer(ctx *gin.Context) {
71 | appID := ctx.DefaultQuery("app_id", "wx3c7e1c9f9f1f9f9f")
72 | res, err := services.OpenPlatformApp.Base.GetAuthorizer(ctx.Request.Context(), appID)
73 | if err != nil {
74 | panic(err)
75 | }
76 | ctx.JSON(200, res)
77 | }
78 |
79 | // GetAuthorizerOption 获取授权方的选项设置信息
80 | func GetAuthorizerOption(ctx *gin.Context) {
81 | appID := ctx.DefaultQuery("app_id", "wx3c7e1c9f9f1f9f9f")
82 | name := ctx.DefaultQuery("name", "location_report")
83 | res, err := services.OpenPlatformApp.Base.GetAuthorizerOption(ctx.Request.Context(), appID, name)
84 | if err != nil {
85 | panic(err)
86 | }
87 | ctx.JSON(200, res)
88 | }
89 |
90 | // SetAuthorizerOption 设置授权方的选项信息
91 | func SetAuthorizerOption(ctx *gin.Context) {
92 | appID := ctx.DefaultQuery("app_id", "wx3c7e1c9f9f1f9f9f")
93 | name := ctx.DefaultQuery("name", "location_report")
94 | value := ctx.DefaultQuery("value", "1")
95 | res, err := services.OpenPlatformApp.Base.SetAuthorizerOption(ctx.Request.Context(), appID, name, value)
96 | if err != nil {
97 | panic(err)
98 | }
99 | ctx.JSON(200, res)
100 | }
101 |
102 | // GetAuthorizers 获取授权方的帐号列表
103 | func GetAuthorizers(ctx *gin.Context) {
104 | offset := ctx.DefaultQuery("offset", "0")
105 | count := ctx.DefaultQuery("count", "10")
106 | offsetInt, _ := strconv.Atoi(offset)
107 | countInt, _ := strconv.Atoi(count)
108 | res, err := services.OpenPlatformApp.Base.GetAuthorizers(ctx.Request.Context(), offsetInt, countInt)
109 | if err != nil {
110 | panic(err)
111 | }
112 | ctx.JSON(200, res)
113 | }
114 |
--------------------------------------------------------------------------------
/config-example.yml:
--------------------------------------------------------------------------------
1 | # 微信支付配置文档: https://powerwechat.artisan-cloud.com/zh/payment/index.html#userconfig%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E%EF%BC%9A
2 | payment:
3 | appid: xxxxx # 公众号appid、小程序appid、企业微信corpid等
4 | mchid: 100000 # ArtisanCloud商户号
5 | certpath: certs/apiclient_cert.pem # 证书路径
6 | keypath: certs/apiclient_key.pem # 证书私钥路径
7 | serialno: 5XXX06F99FF64CF175XXXXXXX
8 | key: xxxxx
9 | # certificatekeypath: /xxx/ArtisanCloud/dev/wx_cert_artisan_cloud/certificates_key.pem
10 | wechatpayserial: 7DED6E61FEA3C847XXXXXXXXXXXXXXX
11 | # rsapublickeypath: /xxx/ArtisanCloud/dev/wx_cert_artisan_cloud/wx_rsa_public_key.pem
12 | mchapiv3key: xxxxx
13 | notifyurl: https://www.artisancloud.cn
14 | submchid: xxxxx
15 | subappid: xxxxx
16 | redisaddr: localhost:6379
17 |
18 | # 小程序配置文档: https://powerwechat.artisan-cloud.com/zh/mini-program/index.html
19 | miniprogram:
20 | appid: xxxxx
21 | secret: xxxxx
22 | redisaddr: localhost:6379
23 | messagetoken: xxxx
24 | messageaeskey: xxxx
25 |
26 | # 企业微信配置文档: https://powerwechat.artisan-cloud.com/zh/wecom/index.html
27 | wecom:
28 | corpid: ww454dfb9d6f6d432a
29 |
30 | # ----- powerwechat-docs-demo start ------
31 | agent_id: 1000000
32 | secret:
33 | messagetoken: xxxxx
34 | messageaeskey: KIBOjioaf3fabKJ32d9zjioafF
35 | messagecallback: https://www.artisan-cloud.com/message/callback
36 | oauthcallback: https://www.artisan-cloud.com/oauth/callback
37 | # ----- powerwechat-docs-demo end ------
38 |
39 | # 联系人配置
40 | contactsecret:
41 | contacttoken: xxxxx
42 | contactaeskey: KIBOjioaf3fabKJ32d9zjioafF
43 | contactcallback: https://www.artisan-cloud.com/api/wx/customer
44 |
45 | redisaddr: localhost:6379
46 |
47 | # 公众号配置文档: https://powerwechat.artisan-cloud.com/zh/official-account/index.html
48 | offiaccount:
49 | appid: xxxxx
50 | appsecret: KIBOjioaf3fabKJ32d9zjioafF
51 | redisaddr: localhost:6379
52 | messagetoken: xxxxx
53 | messageaeskey:
54 |
55 | # 开放平台配置
56 | openplatform:
57 | appid: xxxxxx # 开放平台第三方平台 APPID
58 | appsecret: xxxxxx # 开放平台第三方平台 Secret'
59 | messagetoken: xxxxxx # 开放平台第三方平台 Token'
60 | messageaeskey: xxxxxx # 开放平台第三方平台 AES Key
61 | redisaddr: localhost:6379
62 |
63 |
64 | robotchat:
65 | artbot:
66 | channel: stableDiffusion
67 | stablediffusion:
68 | token: MTA5OTI0MjgzOTI2NDcyNzA2MAkeykeykeykeykeykeykeykeykeykeykeykeykey
69 | baseurl: http://127.0.0.1:7861
70 | prefixuri:
71 | version:
72 | httpdebug:
73 | proxyurl:
74 |
75 | queue:
76 | driver: redis
77 | # 异步操作回调通知
78 | notifyurl: http://127.0.0.1:8888/api/v1/webhook/art-bot/queue/notify
79 | redis:
80 | addr: 127.0.0.1:6379
81 | clientname: ArtBot
82 | username:
83 | password:
84 | db: 1
85 | maxretries: 3
86 | log:
87 | driver: zap
88 | env: develop
89 | infolog: ./logs/artBot/info.log
90 | errorlog: ./logs/artBot/error.log
91 | httpdebug: true
92 |
93 | chatbot:
94 | channel: OPENAI
95 | chatgpt:
96 | openapikey: sk-r4KaVKEmWiogWiVSkeykeykeykeykeykeykeykeykeykeykeykeykeykey
97 | model: ada:ft-55hudong-2023-06-15-05-51-27
98 | organization: 55hudong
99 | httpdebug: true
100 | baseurl: https://chat.kxllen.com/v1
101 | apitype:
102 | apiversion: v1
103 |
104 | queue:
105 | driver: redis
106 | # 异步操作回调通知
107 | notifyurl: http://127.0.0.1:8888/api/v1/webhook/chat-bot/queue/notify
108 | redis:
109 | addr: 127.0.0.1:6379
110 | clientname: ChatBot
111 | username:
112 | password:
113 | db: 2
114 | maxretries: 3
115 | log:
116 | driver: zap
117 | env: develop
118 | infolog: ./logs/chatBot/info.log
119 | errorlog: ./logs/chatBot/error.log
120 | httpdebug: true
121 |
122 |
--------------------------------------------------------------------------------
/controllers/miniprogram/industry.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerLibs/v3/fmt"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram/industry/miniDrama/request"
6 | "github.com/gin-gonic/gin"
7 | "power-wechat-tutorial/services"
8 | "time"
9 | )
10 |
11 | func APIVideoMediaUploadByURL(ctx *gin.Context) {
12 |
13 | var params []*request.VideoMediaUploadByURLRequest
14 |
15 | params = append(params, &request.VideoMediaUploadByURLRequest{
16 | MediaUrl: "xxxx.mp4",
17 | CoverUrl: "xxxx",
18 | MediaName: "xxxx",
19 | })
20 | params = append(params, &request.VideoMediaUploadByURLRequest{
21 | MediaUrl: "xxxx",
22 | CoverUrl: "xxxx",
23 | MediaName: "xxx",
24 | })
25 |
26 | for _, param := range params {
27 |
28 | result, err := services.MiniProgramApp.MiniDramaVOD.VideoMediaUploadByURL(ctx, param)
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | fmt.Dump(result)
35 | }
36 |
37 | }
38 |
39 | func APISearchMediaByTaskId(ctx *gin.Context) {
40 |
41 | taskIDs := []int64{111, 222, 333, 444, 555}
42 |
43 | for _, task := range taskIDs {
44 |
45 | result, err := services.MiniProgramApp.MiniDramaVOD.SearchMediaByTaskId(ctx, task)
46 |
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | fmt.Dump(result)
52 | }
53 |
54 | }
55 |
56 | func APIGetMediaList(ctx *gin.Context) {
57 |
58 | result, err := services.MiniProgramApp.MiniDramaVOD.GetMediaList(ctx, &request.MediaListRequest{})
59 |
60 | if err != nil {
61 | panic(err)
62 | }
63 |
64 | fmt.Dump(result)
65 |
66 | }
67 |
68 | func APIGetMediaInfo(ctx *gin.Context) {
69 | MediaID := int64(111)
70 |
71 | result, err := services.MiniProgramApp.MiniDramaVOD.GetMediaInfo(ctx, MediaID)
72 |
73 | if err != nil {
74 | panic(err)
75 | }
76 |
77 | fmt.Dump(result)
78 |
79 | }
80 |
81 | // 需要提审后才能调用该接口
82 | func APIGetMediaLink(ctx *gin.Context) {
83 |
84 | in := &request.GetMediaLinkRequest{
85 | MediaId: 1111,
86 | T: time.Now().Unix() + 60*60*2, // 最大2小时过期
87 | Expr: 10, // 试看时常
88 | }
89 |
90 | result, err := services.MiniProgramApp.MiniDramaVOD.GetMediaLink(ctx, in)
91 |
92 | if err != nil {
93 | panic(err)
94 | }
95 |
96 | fmt.Dump(result)
97 | }
98 |
99 | func APIDeleteMedia(ctx *gin.Context) {
100 |
101 | MediaIds := []int64{111, 222, 333}
102 |
103 | for _, MediaID := range MediaIds {
104 |
105 | result, err := services.MiniProgramApp.MiniDramaVOD.DeleteMedia(ctx, MediaID)
106 |
107 | if err != nil {
108 | panic(err)
109 | }
110 |
111 | fmt.Dump(result)
112 | }
113 |
114 | }
115 |
116 | func UploadMediaMp(ctx *gin.Context) {
117 | result, err := services.MiniProgramApp.CustomerServiceMessage.UploadTempMedia(ctx, "image", "xxx.png", nil)
118 |
119 | if err != nil {
120 |
121 | panic(err)
122 | }
123 |
124 | fmt.Dump(result)
125 | }
126 |
127 | func APISubmitAudit(ctx *gin.Context) {
128 |
129 | result, err := services.MiniProgramApp.MiniDramaVOD.SubmitAudit(ctx, &request.SubmitAuditRequest{
130 | Name: "xxxx",
131 | MediaCount: 1,
132 | MediaIdList: []int64{111, 222, 333, 444, 555},
133 | Producer: "xxxxx", // 作者
134 | CoverMaterialId: "xxxxxxxx", // 封面临时素材id (三天的那个 这里要调用《小程序》的上传临时素材接口)
135 | RegistrationNumber: "xxxxxx", // 备案号
136 | })
137 |
138 | if err != nil {
139 | panic(err)
140 | }
141 |
142 | fmt.Dump(result)
143 |
144 | }
145 |
146 | func APIListDramas(ctx *gin.Context) {
147 | result, err := services.MiniProgramApp.MiniDramaVOD.GetDramaList(ctx, &request.ListRequest{})
148 |
149 | if err != nil {
150 | panic(err)
151 | }
152 |
153 | fmt.Dump(result)
154 |
155 | }
156 |
157 | func APIUploadByFile(ctx *gin.Context) {
158 | result, err := services.MiniProgramApp.MiniDramaVOD.VideoMediaUploadByFile(ctx, nil)
159 |
160 | if err != nil {
161 | panic(err)
162 | }
163 |
164 | fmt.Dump(result)
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/living.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/living/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 创建预约直播
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93637
13 | func APILivingCreate(c *gin.Context) {
14 |
15 | options := &request.RequestLivingCreate{
16 | AnchorUserID: "zhangsan",
17 | Theme: "theme",
18 | LivingStart: 1600000000,
19 | LivingDuration: 3600,
20 | Description: "test description",
21 | Type: 4,
22 | AgentID: 1000014,
23 | RemindTime: 60,
24 | ActivityCoverMediaID: "MEDIA_ID",
25 | ActivityShareMediaID: "MEDIA_ID",
26 | ActivityDetail: &power.HashMap{
27 | "description": "活动描述,非活动类型的直播不用传",
28 | "image_list": []string{
29 | "MEDIA_ID_1",
30 | "MEDIA_ID_2",
31 | },
32 | },
33 | }
34 | res, err := services.WeComApp.OALiving.Create(c.Request.Context(), options)
35 |
36 | if err != nil {
37 | panic(err)
38 | }
39 |
40 | c.JSON(http.StatusOK, res)
41 | }
42 |
43 | func APILivingModify(c *gin.Context) {
44 |
45 | options := &request.RequestLivingModify{
46 | LivingID: c.DefaultQuery("livingID", "XXXXXXXXX"),
47 | Theme: "theme",
48 | LivingStart: 1600100000,
49 | LivingDuration: 3600,
50 | Description: "test description",
51 | Type: 1,
52 | RemindTime: 60,
53 | }
54 | res, err := services.WeComApp.OALiving.Modify(c.Request.Context(), options)
55 |
56 | if err != nil {
57 | panic(err)
58 | }
59 |
60 | c.JSON(http.StatusOK, res)
61 | }
62 |
63 | func APILivingCancel(c *gin.Context) {
64 | livingID := c.DefaultQuery("livingID", "XXXXXXXXX")
65 | res, err := services.WeComApp.OALiving.Cancel(c.Request.Context(), livingID)
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, res)
72 | }
73 |
74 | func APILivingDeleteReplayData(c *gin.Context) {
75 | livingID := c.DefaultQuery("livingID", "XXXXXXXXX")
76 | res, err := services.WeComApp.OALiving.DeleteReplayData(c.Request.Context(), livingID)
77 |
78 | if err != nil {
79 | panic(err)
80 | }
81 |
82 | c.JSON(http.StatusOK, res)
83 | }
84 |
85 | func APILivingGetLivingCode(c *gin.Context) {
86 | livingID := c.DefaultQuery("livingID", "XXXXXXXXX")
87 | openID := c.DefaultQuery("openID", "XXXXXXXXX")
88 | res, err := services.WeComApp.OALiving.GetLivingCode(c.Request.Context(), livingID, openID)
89 |
90 | if err != nil {
91 | panic(err)
92 | }
93 |
94 | c.JSON(http.StatusOK, res)
95 | }
96 |
97 | func APILivingGetUserAllLivingID(c *gin.Context) {
98 | userID := c.DefaultQuery("userID", "XXXXXXXXX")
99 | cursor := "CURSOR"
100 | limit := 20
101 | res, err := services.WeComApp.OALiving.GetUserAllLivingID(c.Request.Context(), userID, cursor, limit)
102 |
103 | if err != nil {
104 | panic(err)
105 | }
106 |
107 | c.JSON(http.StatusOK, res)
108 | }
109 |
110 | func APILivingGetLivingInfo(c *gin.Context) {
111 | livingID := c.DefaultQuery("livingID", "XXXXXXXXX")
112 | res, err := services.WeComApp.OALiving.GetLivingInfo(c.Request.Context(), livingID)
113 |
114 | if err != nil {
115 | panic(err)
116 | }
117 |
118 | c.JSON(http.StatusOK, res)
119 | }
120 |
121 | func APILivingGetWatchStat(c *gin.Context) {
122 | livingID := c.DefaultQuery("livingID", "XXXXXXXXX")
123 | nextKey := c.DefaultQuery("nextKey", "XXXXXXXXX")
124 |
125 | res, err := services.WeComApp.OALiving.GetWatchStat(c.Request.Context(), livingID, nextKey)
126 |
127 | if err != nil {
128 | panic(err)
129 | }
130 |
131 | c.JSON(http.StatusOK, res)
132 | }
133 |
134 | func APILivingGetLivingShareInfo(c *gin.Context) {
135 | wwShareCode := c.DefaultQuery("wwShareCode", "XXXXXXXXX")
136 |
137 | res, err := services.WeComApp.OALiving.GetLivingShareInfo(c.Request.Context(), wwShareCode)
138 |
139 | if err != nil {
140 | panic(err)
141 | }
142 |
143 | c.JSON(http.StatusOK, res)
144 | }
145 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/meetingroom.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/meetingroom/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 添加会议室
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93619
13 | func APIMeetingRoomAdd(c *gin.Context) {
14 |
15 | options := &request.RequestMeetingRoomAdd{
16 | Name: "18F-会议室",
17 | Capacity: 10,
18 | City: "深圳",
19 | Building: "腾讯大厦",
20 | Floor: "18F",
21 | Equipment: []int{1, 2, 3},
22 | Coordinate: &power.StringMap{
23 | "latitude": "22.540503",
24 | "longitude": "113.934528",
25 | },
26 | }
27 |
28 | res, err := services.WeComApp.OAMeetingRoom.Add(c.Request.Context(), options)
29 |
30 | if err != nil {
31 | panic(err)
32 | }
33 |
34 | c.JSON(http.StatusOK, res)
35 | }
36 |
37 | // 查询会议室
38 | // https://work.weixin.qq.com/api/doc/90000/90135/93619
39 | func APIMeetingRoomList(c *gin.Context) {
40 |
41 | options := &request.RequestMeetingRoomList{
42 | City: "深圳",
43 | Building: "腾讯大厦",
44 | Floor: "18F",
45 | Equipment: []int{1, 2, 3},
46 | }
47 |
48 | res, err := services.WeComApp.OAMeetingRoom.List(c.Request.Context(), options)
49 |
50 | if err != nil {
51 | panic(err)
52 | }
53 |
54 | c.JSON(http.StatusOK, res)
55 | }
56 |
57 | // 编辑会议室
58 | // https://work.weixin.qq.com/api/doc/90000/90135/93619
59 | func APIMeetingRoomEdit(c *gin.Context) {
60 |
61 | options := &request.RequestMeetingRoomEdit{
62 | MeetingRoomID: 2,
63 | Name: "18F-会议室",
64 | Capacity: 10,
65 | City: "深圳",
66 | Building: "腾讯大厦",
67 | Floor: "18F",
68 | Equipment: []int{1, 2, 3},
69 | Coordinate: &power.StringMap{
70 | "latitude": "22.540503",
71 | "longitude": "113.934528",
72 | },
73 | }
74 |
75 | res, err := services.WeComApp.OAMeetingRoom.Edit(c.Request.Context(), options)
76 |
77 | if err != nil {
78 | panic(err)
79 | }
80 |
81 | c.JSON(http.StatusOK, res)
82 | }
83 |
84 | // 删除会议室
85 | // https://work.weixin.qq.com/api/doc/90000/90135/93619
86 | func APIMeetingRoomDel(c *gin.Context) {
87 | options := &request.RequestMeetingRoomDel{
88 | MeetingRoomID: 1,
89 | }
90 | res, err := services.WeComApp.OAMeetingRoom.Del(c.Request.Context(), options)
91 |
92 | if err != nil {
93 | panic(err)
94 | }
95 |
96 | c.JSON(http.StatusOK, res)
97 | }
98 |
99 | // 查询会议室的预定信息
100 | // https://work.weixin.qq.com/api/doc/90000/90135/93620#%E6%9F%A5%E8%AF%A2%E4%BC%9A%E8%AE%AE%E5%AE%A4%E7%9A%84%E9%A2%84%E5%AE%9A%E4%BF%A1%E6%81%AF
101 | func APIMeetingRoomGetBookingInfo(c *gin.Context) {
102 |
103 | options := &request.RequestMeetingRoomGetBookingInfo{
104 | MeetingroomID: 1,
105 | StartTime: 1593532800,
106 | EndTime: 1593619200,
107 | City: "深圳",
108 | Building: "腾讯大厦",
109 | Floor: "18F",
110 | }
111 | res, err := services.WeComApp.OAMeetingRoom.GetBookingInfo(c.Request.Context(), options)
112 |
113 | if err != nil {
114 | panic(err)
115 | }
116 |
117 | c.JSON(http.StatusOK, res)
118 | }
119 |
120 | // 预定会议室
121 | // https://work.weixin.qq.com/api/doc/90000/90135/93620
122 | func APIMeetingRoomBook(c *gin.Context) {
123 |
124 | options := &request.RequestMeetingRoomBook{
125 | MeetingRoomID: 1,
126 | Subject: "周会",
127 | StartTime: 1593532800,
128 | EndTime: 1593619200,
129 | Booker: "zhangsan",
130 | Attendees: []string{"lisi", "wangwu"},
131 | }
132 |
133 | res, err := services.WeComApp.OAMeetingRoom.Book(c.Request.Context(), options)
134 |
135 | if err != nil {
136 | panic(err)
137 | }
138 |
139 | c.JSON(http.StatusOK, res)
140 | }
141 |
142 | func APIMeetingRoomCancelBook(c *gin.Context) {
143 |
144 | options := &request.RequestMeetingRoomCancelBook{
145 | MeetingID: c.DefaultQuery("meetingID", "mt42b34949gsaseb6e027c123cbafAAA"),
146 | KeepSchedule: 1,
147 | }
148 |
149 | res, err := services.WeComApp.OAMeetingRoom.CancelBook(c.Request.Context(), options)
150 |
151 | if err != nil {
152 | panic(err)
153 | }
154 |
155 | c.JSON(http.StatusOK, res)
156 | }
157 |
--------------------------------------------------------------------------------
/controllers/wecom/oa/checkin.go:
--------------------------------------------------------------------------------
1 | package oa
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/work/oa/request"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 获取企业所有打卡规则
12 | // https://work.weixin.qq.com/api/doc/90000/90135/93384
13 | func APICheckinGetCorpCheckinOption(c *gin.Context) {
14 |
15 | res, err := services.WeComApp.OA.GetCorpCheckInOption(c.Request.Context())
16 |
17 | if err != nil {
18 | panic(err)
19 | }
20 |
21 | c.JSON(http.StatusOK, res)
22 | }
23 |
24 | // 获取员工打卡规则
25 | // https://work.weixin.qq.com/api/doc/90000/90135/90263
26 | func APICheckinGetCheckinOption(c *gin.Context) {
27 |
28 | datetime := 1511971200
29 | userIDList := []string{c.DefaultQuery("userID", "matrix-x")}
30 |
31 | res, err := services.WeComApp.OA.GetCheckInOption(c.Request.Context(), datetime, userIDList)
32 |
33 | if err != nil {
34 | panic(err)
35 | }
36 |
37 | c.JSON(http.StatusOK, res)
38 | }
39 |
40 | // 获取打卡记录数据
41 | // https://work.weixin.qq.com/api/doc/90000/90135/90262
42 | func APICheckinGetCheckinData(c *gin.Context) {
43 |
44 | userIDList := []string{c.DefaultQuery("userID", "matrix-x")}
45 |
46 | res, err := services.WeComApp.OA.GetCheckinData(c.Request.Context(), 3, 1492617600, 1492790400, userIDList)
47 |
48 | if err != nil {
49 | panic(err)
50 | }
51 |
52 | c.JSON(http.StatusOK, res)
53 | }
54 |
55 | // 获取打卡日报数据
56 | // https://work.weixin.qq.com/api/doc/90000/90135/93374
57 | func APICheckinGetCheckinDayData(c *gin.Context) {
58 | userIDList := []string{c.DefaultQuery("userID", "matrix-x")}
59 |
60 | res, err := services.WeComApp.OA.GetCheckinDayData(c.Request.Context(), 1599062400, 1599062400, userIDList)
61 |
62 | if err != nil {
63 | panic(err)
64 | }
65 |
66 | c.JSON(http.StatusOK, res)
67 | }
68 |
69 | // 获取打卡月报数据
70 | // https://work.weixin.qq.com/api/doc/90000/90135/93387
71 | func APICheckinGetCheckinMonthData(c *gin.Context) {
72 | userIDList := []string{c.DefaultQuery("userID", "matrix-x")}
73 |
74 | res, err := services.WeComApp.OA.GetCheckInMonthData(c.Request.Context(), 1599062400, 1599408000, userIDList)
75 |
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | c.JSON(http.StatusOK, res)
81 | }
82 |
83 | // 获取打卡人员排班信息
84 | // https://work.weixin.qq.com/api/doc/90000/90135/93380
85 | func APICheckinGetCheckinSchedulist(c *gin.Context) {
86 | userIDList := []string{c.DefaultQuery("userID", "matrix-x")}
87 |
88 | res, err := services.WeComApp.OA.GetCheckInScheduleList(c.Request.Context(), 1492617600, 1492790400, userIDList)
89 |
90 | if err != nil {
91 | panic(err)
92 | }
93 |
94 | c.JSON(http.StatusOK, res)
95 | }
96 |
97 | // 为打卡人员排班
98 | // https://work.weixin.qq.com/api/doc/90000/90135/93385
99 | func APICheckinSetCheckinSchedulist(c *gin.Context) {
100 |
101 | options := &request.RequestCheckInSetScheduleList{
102 | GroupID: 226,
103 | Items: []*power.HashMap{
104 | &power.HashMap{
105 | "userid": "james",
106 | "day": 5,
107 | "schedule_id": 234,
108 | },
109 | },
110 | YearMonth: 202012,
111 | }
112 |
113 | res, err := services.WeComApp.OA.SetCheckInScheduleList(c.Request.Context(), options)
114 |
115 | if err != nil {
116 | panic(err)
117 | }
118 |
119 | c.JSON(http.StatusOK, res)
120 |
121 | }
122 |
123 | // 录入打卡人员人脸信息
124 | // https://work.weixin.qq.com/api/doc/90000/90135/93378
125 | func APICheckinAddCheckinUserFace(c *gin.Context) {
126 | userID := c.DefaultQuery("userID", "matrix-x")
127 | userFace := c.DefaultQuery("userFace", "PLACE_HOLDER")
128 |
129 | res, err := services.WeComApp.OA.AddCheckInUserFace(c.Request.Context(), userID, userFace)
130 |
131 | if err != nil {
132 | panic(err)
133 | }
134 |
135 | c.JSON(http.StatusOK, res)
136 | }
137 |
138 | // 创建打卡规则
139 | // https://developer.work.weixin.qq.com/document/path/98041#创建打卡规则
140 | func APICheckinAddCheckinOption(c *gin.Context) {
141 | //userID := c.DefaultQuery("userID", "matrix-x")
142 | //userFace := c.DefaultQuery("userFace", "PLACE_HOLDER")
143 |
144 | res, err := services.WeComApp.OA.AddCheckinOption(c.Request.Context(), nil)
145 |
146 | if err != nil {
147 | panic(err)
148 | }
149 |
150 | c.JSON(http.StatusOK, res)
151 | }
152 |
--------------------------------------------------------------------------------
/controllers/miniprogram/subscribe-message.go:
--------------------------------------------------------------------------------
1 | package miniprogram
2 |
3 | import (
4 | "github.com/ArtisanCloud/PowerWeChat/v3/src/basicService/subscribeMessage/request"
5 | "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/power"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | "power-wechat-tutorial/services"
9 | )
10 |
11 | // 组合模板并添加至帐号下的个人模板库
12 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
13 | func APISubscribeMessageAddTemplate(c *gin.Context) {
14 | tID, exist := c.GetQuery("tid")
15 | if !exist {
16 | panic("parameter tid expected")
17 | }
18 |
19 | rs, err := services.MiniProgramApp.SubscribeMessage.AddTemplate(c.Request.Context(), tID, []int{1, 2}, "测试数据")
20 |
21 | if err != nil {
22 | panic(err)
23 | }
24 |
25 | c.JSON(http.StatusOK, rs)
26 | }
27 |
28 | // 删除帐号下的个人模板
29 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.deleteTemplate.html
30 | func APISubscribeMessageDeleteTemplate(c *gin.Context) {
31 | priTmplID, exist := c.GetQuery("priTmplID")
32 | if !exist {
33 | panic("parameter tid expected")
34 | }
35 |
36 | rs, err := services.MiniProgramApp.SubscribeMessage.DeleteTemplate(c.Request.Context(), priTmplID)
37 |
38 | if err != nil {
39 | panic(err)
40 | }
41 |
42 | c.JSON(http.StatusOK, rs)
43 | }
44 |
45 | // 获取小程序账号的类目
46 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getCategory.html
47 | func APISubscribeMessageGetCategory(c *gin.Context) {
48 | rs, err := services.MiniProgramApp.SubscribeMessage.GetCategory(c.Request.Context())
49 |
50 | if err != nil {
51 | panic(err)
52 | }
53 |
54 | c.JSON(http.StatusOK, rs)
55 | }
56 |
57 | // 获取模板标题下的关键词列表
58 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getPubTemplateKeyWordsById.html
59 | func APISubscribeMessageGetPubTemplateKeyWordsByID(c *gin.Context) {
60 | tID, exist := c.GetQuery("tid")
61 | if !exist {
62 | panic("parameter tid expected")
63 | }
64 |
65 | rs, err := services.MiniProgramApp.SubscribeMessage.GetPubTemplateKeyWordsByID(c.Request.Context(), tID)
66 |
67 | if err != nil {
68 | panic(err)
69 | }
70 |
71 | c.JSON(http.StatusOK, rs)
72 | }
73 |
74 | // 获取帐号所属类目下的公共模板标题
75 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getPubTemplateTitleList.html
76 | func APISubscribeMessageGetPubTemplateTitleList(c *gin.Context) {
77 | rs, err := services.MiniProgramApp.SubscribeMessage.GetPubTemplateTitleList(c.Request.Context(), "676", 0, 10)
78 |
79 | if err != nil {
80 | panic(err)
81 | }
82 |
83 | c.JSON(http.StatusOK, rs)
84 | }
85 |
86 | // 获取当前帐号下的个人模板列表
87 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
88 | func APISubscribeMessageGetTemplateList(c *gin.Context) {
89 | rs, err := services.MiniProgramApp.SubscribeMessage.GetTemplateList(c.Request.Context())
90 |
91 | if err != nil {
92 | panic(err)
93 | }
94 |
95 | c.JSON(http.StatusOK, rs)
96 | }
97 |
98 | // 发送订阅消息
99 | // https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
100 | func APISubscribeMessageSend(c *gin.Context) {
101 |
102 | toUser, exist := c.GetQuery("openid")
103 | if !exist {
104 | panic("parameter open id expected")
105 | }
106 |
107 | templateID := c.DefaultQuery("templateID", "Y1471771tIQyEogSHjqCgD1P7iy52N_JYH-q0Sw7EvQ")
108 |
109 | page := "page/index/index"
110 | miniprogramState := "developer"
111 | lang := "zh_CN"
112 |
113 | data := &power.HashMap{
114 | "thing1": power.StringMap{
115 | "value": "新年活动",
116 | },
117 | "time2": power.StringMap{
118 | "value": "2023-01-01",
119 | },
120 | }
121 |
122 | rs, err := services.MiniProgramApp.SubscribeMessage.Send(c.Request.Context(),
123 | &request.RequestSubscribeMessageSend{
124 | ToUser: toUser,
125 | TemplateID: templateID,
126 | Page: page,
127 | MiniProgramState: miniprogramState,
128 | Lang: lang,
129 | Data: data,
130 | })
131 |
132 | if err != nil {
133 | panic(err)
134 | }
135 |
136 | c.JSON(http.StatusOK, rs)
137 | }
138 |
--------------------------------------------------------------------------------