-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
carolmao
committed
Dec 31, 2021
1 parent
3988d27
commit 80610a8
Showing
50 changed files
with
2,549 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/.idea | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "client"] | ||
path = client | ||
url = [email protected]:WeixinCloud/wxcloudrun-wxcomponent-frontend.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
FROM node:12.22.1 as nodeBuilder | ||
|
||
# 指定构建过程中的工作目录 | ||
WORKDIR /wxcloudrun-wxcomponent | ||
|
||
# 将当前目录(dockerfile所在目录)下所有文件都拷贝到工作目录下 | ||
COPY . /wxcloudrun-wxcomponent/ | ||
|
||
RUN cd /wxcloudrun-wxcomponent/client && npm install --registry=https://registry.npm.taobao.org && npm run build | ||
|
||
FROM golang:1.17.1-alpine3.14 as builder | ||
|
||
# 指定构建过程中的工作目录 | ||
WORKDIR /wxcloudrun-wxcomponent | ||
|
||
# 将当前目录(dockerfile所在目录)下所有文件都拷贝到工作目录下 | ||
COPY . /wxcloudrun-wxcomponent/ | ||
|
||
# 执行代码编译命令。操作系统参数为linux,编译后的二进制产物命名为main,并存放在当前目录下。 | ||
RUN GOOS=linux go build -o main . | ||
|
||
# 选用运行时所用基础镜像(GO语言选择原则:尽量体积小、包含基础linux内容的基础镜像) | ||
FROM alpine:3.13 | ||
|
||
# 指定运行时的工作目录 | ||
WORKDIR /wxcloudrun-wxcomponent | ||
|
||
# 将构建产物/wxcloudrun-wxcomponent/main拷贝到运行时的工作目录中 | ||
COPY --from=builder /wxcloudrun-wxcomponent/main /wxcloudrun-wxcomponent/ | ||
COPY --from=builder /wxcloudrun-wxcomponent/comm/config/server.conf /wxcloudrun-wxcomponent/comm/config/ | ||
COPY --from=nodeBuilder /wxcloudrun-wxcomponent/client/dist /wxcloudrun-wxcomponent/client/dist | ||
|
||
# 设置release模式 | ||
ENV GIN_MODE release | ||
|
||
# 执行启动命令 | ||
CMD ["/wxcloudrun-wxcomponent/main"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,87 @@ | ||
# wxcloudrun-wxcomponent | ||
微信云托管 微信第三方平台模版 | ||
微信云托管 微信第三方平台管理工具模版 | ||
|
||
## 功能介绍 | ||
此项目提供第三方平台的后端服务以及第三方平台管理工具。该镜像可一键部署到微信云托管,分钟级别即可完成第三方平台开发环境搭建以及第三方平台管理工具部署。 | ||
|
||
#### 第三方平台推送消息 | ||
微信第三方平台需要填写两个URL用于接受官方推送的消息,详情参考官方文档:[创建与配置第三方平台准备工作](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/operation/thirdparty/prepare.html)。 | ||
- 授权事件URL: 本项目提供了接受官方推送并存入数据库的服务,对推送ticket、授权、解除授权的事件都做了相应处理。 | ||
- 消息与事件URL: 本项目提供了接受官方推送并存入数据库的服务,开发者可以读数据库查看推送消息,也可以在此基础上进行二次开发。 | ||
#### 第三方平台管理工具 | ||
- 授权帐号管理:可查看授权给第三方平台的公众号/小程序帐号信息。 | ||
- 第三方token获取:可一键获取component_verify_ticket、component_access_token、authorizer_access_token以及微信令牌,便于开发者进行调试。 | ||
- 第三方消息查看:可获取推送至授权事件URL和消息与事件URL的消息,便于开发者进行调试。 | ||
- 第三方授权页面生成:可一键生成PC版和H5版的授权页面,商家可扫码或者直接访问授权页面完成授权。 | ||
|
||
## 目录结构 | ||
``` | ||
. | ||
├── Dockerfile | ||
├── README.md | ||
├── api // 后端api | ||
│ ├── admin // 管理工具,需管理员登录 | ||
│ ├── authpage // 授权页面,无鉴权 | ||
│ └── wxcallback // 接收微信消息 | ||
├── client // 前端 | ||
│ ├── dist // 打包结果 | ||
│ ├── index.html | ||
│ ├── node_modules | ||
│ ├── package.json | ||
│ ├── src // 源代码 | ||
│ ├── tsconfig.json | ||
│ ├── vite.config.ts | ||
│ └── yarn.lock | ||
├── comm // 后端公共模块 | ||
│ ├── config // 配置 | ||
│ ├── encrypt // 加密 | ||
│ ├── errno // 错误码 | ||
│ ├── httputils // http | ||
│ ├── inits // 初始化 | ||
│ ├── log // 日志 | ||
│ ├── utils // 其他工具 | ||
│ └── wx // 微信相关 | ||
├── container.config.json // 微信云托管配置 | ||
├── db // 数据库相关 | ||
│ ├── dao | ||
│ ├── init.go | ||
│ └── model | ||
├── go.mod | ||
├── go.sum | ||
├── main.go | ||
├── middleware // 中间件 | ||
│ ├── jwt.go // jwt | ||
│ ├── log.go // 日志 | ||
│ └── wxsource.go // 判断是否为微信来源 | ||
└── routers // 路由 | ||
└── routers.go | ||
``` | ||
|
||
## 其他说明 | ||
#### 本地调试 | ||
服务启动前会从环境变量中读取数据库配置,自行写入环境变量后运行一下代码,即可在本地启动服务。 | ||
``` | ||
go run main | ||
``` | ||
|
||
#### 判断微信来源 | ||
服务部署在微信云托管时,微信推送消息走内网,无需加解密,判断header中是否有x-wx-source即可。 | ||
|
||
#### 数据表 | ||
``` | ||
+-----------------------+ | ||
| Tables_in_wxcomponent | | ||
+-----------------------+ | ||
| authorizers | | ||
| comm | | ||
| user | | ||
| wxcallback_biz | | ||
| wxcallback_component | | ||
+-----------------------+ | ||
``` | ||
- authorizers: 授权账号信息 | ||
- comm: 存储ticket、第三方信息等 | ||
- user: 用户表 | ||
- wxcallback_biz: 推送给消息与事件URL的消息 | ||
- wxcallback_component: 推送给授权事件URL的消息 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package admin | ||
|
||
// 系统鉴权,登录,人员管理 | ||
|
||
import ( | ||
"net/http" | ||
"strconv" | ||
|
||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/errno" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/log" | ||
"github.com/astaxie/beego/validation" | ||
"github.com/gin-gonic/gin" | ||
|
||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/utils" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/db/dao" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/db/model" | ||
) | ||
|
||
func checkAuth(req model.UserRecord) (int32, error) { | ||
record, err := dao.GetUserRecord(req.Username, req.Password) | ||
if err != nil { | ||
log.Error(err) | ||
return 0, err | ||
} | ||
if len(record) > 0 { | ||
return record[0].ID, nil | ||
} | ||
return 0, err | ||
} | ||
|
||
func authHandler(c *gin.Context) { | ||
var req model.UserRecord | ||
if err := c.ShouldBindJSON(&req); err != nil { | ||
log.Error(err.Error()) | ||
c.JSON(http.StatusOK, errno.ErrInvalidParam.WithData(err.Error())) | ||
return | ||
} | ||
|
||
valid := validation.Validation{} | ||
ok, _ := valid.Valid(&req) | ||
|
||
if !ok { | ||
for _, err := range valid.Errors { | ||
log.Debug(err.Key + " " + err.Message) | ||
} | ||
log.Error(valid.Errors) | ||
c.JSON(http.StatusOK, errno.ErrAuthErr.WithData(valid.Errors)) | ||
return | ||
} | ||
|
||
ID, err := checkAuth(req) | ||
if err != nil { | ||
log.Error(err.Error()) | ||
c.JSON(http.StatusOK, errno.ErrAuthErr.WithData(err.Error())) | ||
return | ||
} | ||
|
||
token, _err := utils.GenerateToken(strconv.Itoa(int(ID)), req.Username) | ||
if _err != nil { | ||
log.Error(_err.Error()) | ||
c.JSON(http.StatusOK, errno.ErrAuthErr.WithData(_err.Error())) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, errno.OK.WithData(gin.H{"jwt": token})) | ||
|
||
} | ||
|
||
func refreshAuthHandler(c *gin.Context) { | ||
jwt, _ := c.Get("jwt") | ||
id, userName := jwt.(*utils.Claims).ID, jwt.(*utils.Claims).UserName | ||
log.Debugf("id: %s name: %s", id, userName) | ||
token, err := utils.GenerateToken(id, userName) | ||
if err != nil { | ||
log.Error(err.Error()) | ||
c.JSON(http.StatusOK, errno.ErrAuthErr.WithData(err.Error())) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, errno.OK.WithData(gin.H{"jwt": token})) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package admin | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"strconv" | ||
"sync" | ||
"time" | ||
|
||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/errno" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/httputils" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/log" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/wx" | ||
wxbase "github.com/WeixinCloud/wxcloudrun-wxcomponent/comm/wx/base" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/db/dao" | ||
"github.com/WeixinCloud/wxcloudrun-wxcomponent/db/model" | ||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
type getAuthorizerListReq struct { | ||
ComponentAppid string `json:"component_appid"` | ||
Offset int `json:"offset"` | ||
Count int `json:"count"` | ||
} | ||
|
||
type authorizerInfo struct { | ||
AuthorizerAppid string `json:"authorizer_appid"` | ||
RefreshToken string `json:"refresh_token"` | ||
AuthTime int64 `json:"auth_time"` | ||
} | ||
type getAuthorizerListResp struct { | ||
TotalCount int `json:"total_count"` | ||
List []authorizerInfo `json:list` | ||
} | ||
|
||
func pullAuthorizerListHandler(c *gin.Context) { | ||
go func() { | ||
count := 100 | ||
offset := 0 | ||
total := 0 | ||
now := time.Now() | ||
for { | ||
var resp getAuthorizerListResp | ||
if err := getAuthorizerList(offset, count, &resp); err != nil { | ||
log.Error(err) | ||
return | ||
} | ||
if total == 0 { | ||
total = resp.TotalCount | ||
} | ||
// 插入数据库 | ||
length := len(resp.List) | ||
records := make([]model.Authorizer, length) | ||
var wg sync.WaitGroup | ||
wg.Add(length) | ||
for i, info := range resp.List { | ||
go constructAuthorizerRecord(info, &records[i], &wg) | ||
} | ||
wg.Wait() | ||
dao.BatchCreateOrUpdateAuthorizerRecord(&records) | ||
|
||
if length < count { | ||
break | ||
} | ||
offset += count | ||
} | ||
|
||
// 删除记录 | ||
if err := dao.ClearAuthorizerRecordsBefore(now); err != nil { | ||
log.Error(err) | ||
return | ||
} | ||
}() | ||
c.JSON(http.StatusOK, errno.OK) | ||
} | ||
|
||
func constructAuthorizerRecord(info authorizerInfo, record *model.Authorizer, wg *sync.WaitGroup) error { | ||
defer wg.Done() | ||
record.Appid = info.AuthorizerAppid | ||
record.AuthTime = time.Unix(info.AuthTime, 0) | ||
record.RefreshToken = info.RefreshToken | ||
var appinfo wx.AuthorizerInfoResp | ||
|
||
if err := wx.GetAuthorizerInfo(record.Appid, &appinfo); err != nil { | ||
log.Errorf("GetAuthorizerInfo fail %v", err) | ||
return err | ||
} | ||
record.AppType = appinfo.AuthorizerInfo.AppType | ||
record.ServiceType = appinfo.AuthorizerInfo.ServiceType.Id | ||
record.NickName = appinfo.AuthorizerInfo.NickName | ||
record.UserName = appinfo.AuthorizerInfo.UserName | ||
record.HeadImg = appinfo.AuthorizerInfo.HeadImg | ||
record.QrcodeUrl = appinfo.AuthorizerInfo.QrcodeUrl | ||
record.PrincipalName = appinfo.AuthorizerInfo.PrincipalName | ||
record.FuncInfo = appinfo.AuthorizationInfo.StrFuncInfo | ||
record.VerifyInfo = appinfo.AuthorizerInfo.VerifyInfo.Id | ||
return nil | ||
} | ||
|
||
func getAuthorizerList(offset, count int, resp *getAuthorizerListResp) error { | ||
req := getAuthorizerListReq{ | ||
ComponentAppid: wxbase.GetAppid(), | ||
Offset: offset, | ||
Count: count, | ||
} | ||
_, respbody, err := httputils.PostWxJson("/cgi-bin/component/api_get_authorizer_list", req, true) | ||
if err != nil { | ||
return err | ||
} | ||
if err := json.Unmarshal(respbody, &resp); err != nil { | ||
log.Errorf("Unmarshal err, %v", err) | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func getAuthorizerListHandler(c *gin.Context) { | ||
offset, err := strconv.Atoi(c.DefaultQuery("offset", "0")) | ||
if err != nil { | ||
c.JSON(http.StatusOK, errno.ErrInvalidParam.WithData(err.Error())) | ||
return | ||
} | ||
limit, err := strconv.Atoi(c.DefaultQuery("limit", "10")) | ||
if err != nil { | ||
c.JSON(http.StatusOK, errno.ErrInvalidParam.WithData(err.Error())) | ||
return | ||
} | ||
appid := c.DefaultQuery("appid", "") | ||
records, total, err := dao.GetAuthorizerRecords(appid, offset, limit) | ||
if err != nil { | ||
c.JSON(http.StatusOK, errno.ErrSystemError.WithData(err.Error())) | ||
return | ||
} | ||
c.JSON(http.StatusOK, errno.OK.WithData(gin.H{"total": total, "records": records})) | ||
} |
Oops, something went wrong.