├── .gitignore
├── LICENSE
├── README.md
├── apis
├── apis.go
├── handler
│ ├── handler.go
│ ├── webHandler.go
│ └── wxHandler.go
└── jwt
│ └── jwt.go
├── bash_profile
├── README.md
└── bash_profile.go
├── db
├── db.go
├── dblayer.go
├── webCollection.go
└── wxCollection.go
├── git-gin-wechat-test
├── app.js
├── app.json
├── app.wxss
├── pages
│ ├── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ └── logs
│ │ ├── logs.js
│ │ ├── logs.json
│ │ ├── logs.wxml
│ │ └── logs.wxss
├── project.config.json
├── sitemap.json
└── utils
│ └── util.js
├── main
├── main.go
└── models
└── request
├── webRequest.go
└── wxRequest.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 ALiuGuanyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gin-For-Mini-Program
2 | 微信、支付宝、百度、字节跳动、QQ小程序登录请求,Gin框架完全解决方案(数据库采用mongoDB)
3 |
4 |
5 | - [目录]()
6 | - [main.go](#maingo)
7 | - [apis](#apis)
8 | - [wxHandler](#wxhandler)
9 | - [db](#db)
10 | - [registerDB](#registerdb)
11 | - [models](#models)
12 | - [register](#register)
13 | - [bash_profile](#bashprofile)
14 | - [git-gin-wechat-test](#git-gin-wechat-test)
15 |
16 |
17 | ## main.go
18 | 程序入口
19 |
20 | ---
21 |
22 | ## apis
23 | 所有API的入口
24 |
25 | ### wxHandler
26 | 处理来自微信小程序的请求
27 |
28 | ---
29 |
30 | ## db
31 | 数据库相关文件
32 |
33 | ### registerDB
34 | 处理注册相关文件
35 |
36 | ---
37 |
38 | ## models
39 | 定义结构体
40 |
41 | ### register
42 | 注册相关结构体
43 |
44 | ---
45 |
46 | ## bash_profile
47 | 基本配置
48 |
49 | ---
50 |
51 | ## git-gin-wechat-test
52 | 微信小程序接口测试项目代码
53 |
54 | ---
55 |
--------------------------------------------------------------------------------
/apis/apis.go:
--------------------------------------------------------------------------------
1 | package apis
2 |
3 | import (
4 |
5 | "./handler"
6 | "./jwt"
7 | "github.com/gin-contrib/cors"
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | func RunAPI(address string) error {
12 |
13 | h, err := handler.NewHandler()
14 |
15 | if err != nil {
16 | return err
17 | }
18 |
19 | return RunAPIWithHandler(address, h)
20 | }
21 |
22 | func RunAPIWithHandler(address string, h handler.IHandler) error {
23 |
24 | r := gin.Default()
25 |
26 | // 跨域
27 | r.Use(cors.Default())
28 |
29 | // 微信小程序api
30 | wxGroup := r.Group("/wx")
31 | {
32 | wxGroup.POST("/register", h.WxRegister)
33 | }
34 |
35 | // web 端API
36 | webGroup := r.Group("/web")
37 | {
38 | webGroup.POST("/register", h.WebRegister)
39 | webGroup.POST("/login", h.WebLogin)
40 | }
41 |
42 | testGroup := r.Group("/test", jwt.JWTAuth())
43 | {
44 | testGroup.POST("", handler.TestJwt)
45 | }
46 | // 如果需读取静态文件
47 | r.Static("/imgs", "../assets/imgs")
48 | r.Static("/videos", "../assets/videos")
49 |
50 | return r.Run(address)
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/apis/handler/handler.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 |
5 | "../../bash_profile"
6 | "../../db"
7 | "../../models/request"
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | type IHandler interface {
12 | WxLogin(id string) (request.WxUser, error)
13 | WxRegister(c *gin.Context)
14 | WebRegister(c *gin.Context)
15 | WebLogin(c *gin.Context)
16 | }
17 |
18 | type Handler struct {
19 | db db.ODBLayer
20 | }
21 |
22 | func NewHandler() (IHandler, error) {
23 | return NewHandlerWithParams()
24 | }
25 |
26 | func NewHandlerWithParams() (IHandler, error) {
27 | client, err := db.NewConnection(bash_profile.DBConnect)
28 | if err != nil {
29 | return nil, err
30 | }
31 | return &Handler{
32 | db: client,
33 | }, nil
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/apis/handler/webHandler.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "../../models/request"
5 | myjwt "../jwt"
6 | "github.com/dgrijalva/jwt-go"
7 | "github.com/gin-gonic/gin"
8 | "log"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | func (h *Handler) WebRegister(c *gin.Context) {
14 | // 保证数据库接口已经被初始化
15 | if h.db == nil {
16 | c.JSON(http.StatusInternalServerError, gin.H{
17 | "code": 500,
18 | "error": nil,
19 | "msg": "数据库连接失败",
20 | })
21 | c.Abort()
22 | return
23 | }
24 |
25 |
26 | var webRegisterData request.WebRegisterData
27 |
28 | err := c.ShouldBindJSON(&webRegisterData)
29 | if err != nil {
30 | c.JSON(http.StatusBadRequest, gin.H{
31 | "code": 400,
32 | "error": "",
33 | "msg": "请求数据错误",
34 | })
35 | c.Abort()
36 | return
37 | }
38 | log.Print(webRegisterData)
39 | userData, err := h.db.WebRegister(webRegisterData)
40 |
41 | if err != nil {
42 | c.JSON(http.StatusInternalServerError, gin.H{
43 | "code": 500,
44 | "error": err.Error(),
45 | "msg": "注册失败",
46 | "data": nil,
47 | })
48 | c.Abort()
49 | return
50 | }
51 |
52 | // 返回数据给前台完成register
53 | c.JSON(http.StatusCreated, gin.H{
54 | "code": 201,
55 | "error": nil,
56 | "msg": "注册成功",
57 | "Data": userData,
58 | })
59 |
60 | return
61 | }
62 |
63 | func (h Handler) WebLogin(c *gin.Context) {
64 | // 保证数据库接口已经被初始化
65 | if h.db == nil {
66 | c.JSON(http.StatusInternalServerError, gin.H{
67 | "code": 500,
68 | "error": nil,
69 | "msg": "数据库连接失败",
70 | })
71 | c.Abort()
72 | return
73 | }
74 |
75 |
76 | var webLoginData request.WebLoginData
77 | err := c.ShouldBindJSON(&webLoginData)
78 | if err != nil {
79 | c.JSON(http.StatusBadRequest, gin.H{
80 | "code": 400,
81 | "error": err.Error(),
82 | "msg": "请求数据错误",
83 | })
84 | c.Abort()
85 | return
86 | }
87 |
88 |
89 | log.Print(webLoginData)
90 | userData, err := h.db.WebLogin(webLoginData)
91 |
92 | if err != nil {
93 | c.JSON(http.StatusInternalServerError, gin.H{
94 | "code": 500,
95 | "error": err.Error(),
96 | "msg": "登录失败",
97 | "data": nil,
98 | })
99 | c.Abort()
100 | return
101 | }
102 |
103 | claims := myjwt.JWTClaims{
104 | ID: userData.ID,
105 | Email: userData.Email,
106 | StandardClaims: jwt.StandardClaims{
107 | NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间
108 | ExpiresAt: int64(time.Now().Unix() + 3600), // 过期时间 一小时
109 | Issuer: "admin", // 签名的发行者
110 | },
111 | }
112 |
113 | token, jwtErr := myjwt.CreateToken(claims)
114 | if jwtErr != nil {
115 | c.JSON(http.StatusInternalServerError, gin.H{
116 | "code": 500,
117 | "error": jwtErr,
118 | "msg": "无法生成token",
119 | "token": nil,
120 | })
121 | }
122 |
123 | // 返回数据给前台完成register
124 | c.JSON(http.StatusCreated, gin.H{
125 | "code": 200,
126 | "error": nil,
127 | "msg": "登录成功",
128 | "token": token,
129 | })
130 |
131 | return
132 | }
133 |
134 | func TestJwt(c *gin.Context) {
135 | claims := c.MustGet("claims").(*myjwt.JWTClaims)
136 | if claims != nil {
137 | c.JSON(http.StatusOK, gin.H{
138 | "code": 200,
139 | "msg": "测试成功",
140 | "data": claims,
141 | })
142 | }
143 | }
--------------------------------------------------------------------------------
/apis/handler/wxHandler.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "../../bash_profile"
5 | "../../models/request"
6 | "encoding/json"
7 | "github.com/gin-gonic/gin"
8 | "io/ioutil"
9 | "log"
10 | "net/http"
11 | "time"
12 | )
13 |
14 | func (h *Handler) WxLogin(id string) (request.WxUser, error) {
15 | user, err := h.db.WxLogin(id)
16 | return user, err
17 | }
18 |
19 | func (h *Handler) WxRegister(c *gin.Context) {
20 | // 保证数据库接口已经被初始化
21 | if h.db == nil {
22 | c.JSON(http.StatusInternalServerError, gin.H{
23 | "code": 500,
24 | "error": nil,
25 | "msg": "数据库连接失败",
26 | })
27 | c.Abort()
28 | return
29 | }
30 |
31 | var wxRequestData request.WxRequestData
32 |
33 | err := c.ShouldBindJSON(&wxRequestData)
34 | if err != nil {
35 | c.JSON(http.StatusBadRequest, gin.H{
36 | "code": 400,
37 | "error": err.Error(),
38 | "msg": "请求数据错误",
39 | })
40 | c.Abort()
41 | return
42 | }
43 |
44 | // 拼接微信API地址
45 | url := bash_profile.WxSite + "appid=" + bash_profile.WxAppId + "&secret=" + bash_profile.WxSecret + "&js_code=" + wxRequestData.Code + bash_profile.WxHttpTail
46 |
47 | // 转发请求到微信接口
48 | resp, err := http.Get(url)
49 | if err != nil {
50 | c.JSON(http.StatusServiceUnavailable, gin.H{
51 | "code": 503,
52 | "error": err.Error(),
53 | "msg": "第三方服务错误",
54 | "data": nil,
55 | })
56 | c.Abort()
57 | return
58 | }
59 |
60 | defer resp.Body.Close()
61 |
62 | // 读取数据
63 | body, err := ioutil.ReadAll(resp.Body)
64 | if err != nil {
65 | c.JSON(http.StatusInternalServerError, gin.H{
66 | "code": 500,
67 | "error": err.Error(),
68 | "msg": "第三方数据读取失败",
69 | "data": nil,
70 | })
71 | c.Abort()
72 | return
73 | }
74 |
75 | respData := new(request.Code2Session)
76 |
77 | // 解码数据并赋值给返回的 data
78 | json.Unmarshal(body, &respData)
79 |
80 | // 检测openid是否为空
81 | if len(respData.Openid) == 0 {
82 | c.JSON(http.StatusServiceUnavailable, gin.H{
83 | "code": respData.Errcode,
84 | "error": respData.Errmsg,
85 | "msg": "第三方服务错误",
86 | "data": nil,
87 | })
88 | c.Abort()
89 | return
90 | }
91 |
92 | /**
93 | 如果不进行数据库操作请注释掉下方数据库相关代码
94 | */
95 | // mongodb
96 | /**
97 | 避免同一账号重复写入数据库的两种方法:
98 | 1. 使用mongo-driver 去数据库中查询openID是否存在,
99 | 如果存在则不进行写入操作
100 | 2. 通过shell 命令手动设置openID为索引字段,
101 | 此时如果插入重复的openID 则会返回error
102 |
103 | 推荐使用方法二,可以避免查询数据库,减少请求时间
104 | 这里给出方法一的代码
105 | */
106 |
107 | // 进行查询数据库操作
108 | userData, err := h.db.WxLogin(respData.Openid)
109 | log.Print(userData, err)
110 | if err == nil {
111 | c.JSON(http.StatusOK, gin.H{
112 | "code": 200,
113 | "error": nil,
114 | "msg": "登录成功",
115 | "Data": userData,
116 | })
117 |
118 | c.Abort()
119 | return
120 | }
121 |
122 |
123 | var user request.WxUser
124 | user.OpenID = respData.Openid
125 | // user.UnionID = respData.Unionid 如果使用union ID
126 | user.AvatarUrl = wxRequestData.AvatarUrl
127 | user.Nickname = wxRequestData.NickName
128 | user.City = wxRequestData.City
129 | user.Province = wxRequestData.Province
130 | user.Country = wxRequestData.Country
131 | user.Gender = wxRequestData.Gender
132 | user.CreateDate = time.Now()
133 |
134 |
135 | userData, err = h.db.WxRegister(user)
136 |
137 | if err != nil {
138 | c.JSON(http.StatusInternalServerError, gin.H{
139 | "code": 500,
140 | "error": err.Error(),
141 | "msg": "注册失败",
142 | "data": nil,
143 | })
144 | c.Abort()
145 | return
146 | }
147 |
148 | // 返回数据给前台完成register
149 | c.JSON(http.StatusCreated, gin.H{
150 | "code": 201,
151 | "error": nil,
152 | "msg": "注册成功",
153 | "Data": userData,
154 | })
155 |
156 | return
157 | }
--------------------------------------------------------------------------------
/apis/jwt/jwt.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "errors"
5 | "github.com/dgrijalva/jwt-go"
6 | "github.com/gin-gonic/gin"
7 | "log"
8 | "net/http"
9 | "strings"
10 | "time"
11 | )
12 |
13 |
14 | var key = []byte("secret")
15 |
16 | var (
17 | TokenExpired error = errors.New("token is expired")
18 | TokenNotValidYet error = errors.New("token not active yet")
19 | TokenMalformed error = errors.New("that's not even a token")
20 | TokenInvalid error = errors.New("couldn't handle this token")
21 | )
22 |
23 | type JWTClaims struct {
24 | ID interface{} `bson:"_id"`
25 | Email string `bson:"email"`
26 | //Phone string `bson:"phone"`
27 | jwt.StandardClaims
28 | }
29 |
30 | // JWT中间件
31 | func JWTAuth() gin.HandlerFunc {
32 | return func(c *gin.Context) {
33 |
34 | clientToken := c.Request.Header.Get("Authorization")
35 | log.Print(clientToken)
36 | if clientToken == "" {
37 | c.JSON(http.StatusUnauthorized, gin.H{
38 | "msg": "jwt must be provided",
39 | "code": 401,
40 | })
41 | c.Abort()
42 | return
43 | }
44 | clientToken = strings.Replace(string(clientToken), "Bearer ", "", 1)
45 | claims, err := ParseToken(clientToken)
46 | if err != nil {
47 | if err == TokenExpired {
48 | c.JSON(http.StatusUnauthorized, gin.H{
49 | "msg": "token expired",
50 | "code": 401,
51 | })
52 | c.Abort()
53 | return
54 | }
55 | c.JSON(http.StatusUnauthorized, gin.H{
56 | "code": 401,
57 | "msg": err.Error(),
58 | })
59 | c.Abort()
60 | return
61 | }
62 |
63 | // 继续交由下一个路由处理,并将解析出的信息传递下去
64 | c.Set("claims", claims)
65 | }
66 | }
67 |
68 | // 创建token
69 | func CreateToken(claims JWTClaims) (string, error) {
70 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
71 | tokenString, err := token.SignedString(key)
72 | return tokenString, err
73 | }
74 |
75 | // 解析token
76 | func ParseToken(tokenString string) (*JWTClaims, error) {
77 | token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
78 | return key, nil
79 | })
80 | if err != nil {
81 | if ve, ok := err.(*jwt.ValidationError); ok {
82 | if ve.Errors&jwt.ValidationErrorMalformed != 0 {
83 | return nil, TokenMalformed
84 | } else if ve.Errors&jwt.ValidationErrorExpired != 0 {
85 | // Token is expired
86 | return nil, TokenExpired
87 | } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
88 | return nil, TokenNotValidYet
89 | } else {
90 | return nil, TokenInvalid
91 | }
92 | }
93 | }
94 | if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
95 | return claims, nil
96 | }
97 | return nil, errors.New("token can not be handled")
98 | }
99 |
100 | // 更新token
101 | func RefreshToken(tokenString string) (string, error) {
102 | jwt.TimeFunc = func() time.Time {
103 | return time.Unix(0, 0)
104 | }
105 | token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
106 | return key, nil
107 | })
108 | if err != nil {
109 | return "", err
110 | }
111 | if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
112 | jwt.TimeFunc = time.Now
113 | claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
114 | return CreateToken(*claims)
115 | }
116 | return "", TokenInvalid
117 | }
118 |
--------------------------------------------------------------------------------
/bash_profile/README.md:
--------------------------------------------------------------------------------
1 | # 基础配置
2 | 在此文件中配置数据库及各种小程序的基础信息
3 |
--------------------------------------------------------------------------------
/bash_profile/bash_profile.go:
--------------------------------------------------------------------------------
1 | package bash_profile
2 |
3 | // 基础配置
4 | const (
5 | DBConnect = "mongodb://localhost:27017" // 更换成你自己的uri地址
6 | DBName = "mini_program" // 更换成你自己的数据库名字
7 |
8 |
9 | WxUserCollection = "wx_users" // 更换成你自己的表单名字
10 | WxSite = "https://api.weixin.qq.com/sns/jscode2session?"
11 | WxAppId = "wx6f9cc63ed9ded89b" // 使用自己的AppId 进行替换
12 | WxSecret = "421169661fc3ef99110fe20c75a64ad1" // 使用自己的Secret 进行替换
13 | WxHttpTail = "&grant_type=authorization_code"
14 |
15 | WebUserCollection = "web_users"
16 | )
--------------------------------------------------------------------------------
/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "context"
5 | "go.mongodb.org/mongo-driver/mongo"
6 | "go.mongodb.org/mongo-driver/mongo/options"
7 | "time"
8 | )
9 |
10 |
11 |
12 | // 使用mongoDB官方库进行数据库操作
13 | type MongoDBClient struct {
14 | *mongo.Client
15 | }
16 |
17 | // 建立与mongodb的连接
18 | func NewConnection(uri string) (*MongoDBClient, error) {
19 |
20 | clientOptions := options.Client().ApplyURI(uri)
21 | client, err := mongo.Connect(context.TODO(), clientOptions)
22 |
23 | return &MongoDBClient{
24 | client,
25 | }, err
26 | }
27 |
28 | // 选择数据库及表单
29 | func Use(client *mongo.Client, dbname, collname string) ( *mongo.Collection ) {
30 | return client.Database(dbname).Collection(collname)
31 | }
32 |
33 | // 限制每次操作数据库的操作时间为10s
34 | func GetContext() (ctx context.Context) {
35 | ctx, _ = context.WithTimeout(context.Background(), 10*time.Second)
36 | return
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/db/dblayer.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "../models/request"
5 | )
6 |
7 | type ODBLayer interface {
8 | WxRegister(request.WxUser) (request.WxUser, error)
9 | WxLogin(string) (request.WxUser, error)
10 |
11 | WebRegister(user request.WebRegisterData) (request.WebUser, error)
12 | WebLogin(request.WebLoginData) (request.WebLoginResponseData, error)
13 | }
--------------------------------------------------------------------------------
/db/webCollection.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "../bash_profile"
5 | "../models/request"
6 | "context"
7 | "go.mongodb.org/mongo-driver/bson"
8 | "log"
9 | )
10 |
11 | func (odb *MongoDBClient) WebRegister(user request.WebRegisterData) (request.WebUser, error) {
12 | collection := Use(odb.Client, bash_profile.DBName, bash_profile.WebUserCollection)
13 | var result request.WebUser
14 | result.Email = user.Email
15 | result.Username = user.Username
16 | result.Password = user.Password
17 | _id , err := collection.InsertOne(GetContext(), result)
18 | result.ID = _id.InsertedID
19 | return result, err
20 | }
21 |
22 | func (odb *MongoDBClient) WebLogin(data request.WebLoginData) (request.WebLoginResponseData, error) {
23 | var user request.WebLoginResponseData
24 | var users []request.WebLoginResponseData
25 | collection := Use(odb.Client, bash_profile.DBName, bash_profile.WebUserCollection)
26 | rst, _ := collection.Find(GetContext(), bson.D{{}})
27 | for rst.Next(context.TODO()) {
28 | var elem request.WebLoginResponseData
29 | err := rst.Decode(&elem)
30 | if err != nil {
31 | log.Fatal(err)
32 | }
33 | users = append(users, elem)
34 | }
35 | log.Print(users)
36 | result := collection.FindOne(GetContext(), bson.D{{"email", data.Email}}).Decode(&user)
37 |
38 | if result != nil {
39 | return user, result
40 | }
41 |
42 | return user, nil
43 | }
44 |
--------------------------------------------------------------------------------
/db/wxCollection.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "../bash_profile"
5 | "../models/request"
6 | "go.mongodb.org/mongo-driver/bson"
7 | )
8 |
9 | // 实现数据库操作
10 | func (odb *MongoDBClient) WxRegister(user request.WxUser) (request.WxUser, error) {
11 | collection := Use(odb.Client, bash_profile.DBName, bash_profile.WxUserCollection)
12 | _id , err := collection.InsertOne(GetContext(), user)
13 | user.ID = _id.InsertedID
14 | return user, err
15 | }
16 |
17 | func (odb *MongoDBClient) WxLogin(id string) (request.WxUser, error) {
18 | var user request.WxUser
19 | collection := Use(odb.Client, bash_profile.DBName, bash_profile.WxUserCollection)
20 | result := collection.FindOne(GetContext(), bson.D{{"wxOpenId", id}}).Decode(&user)
21 | if result != nil {
22 | return user, result
23 | }
24 |
25 | return user, nil
26 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | onLaunch: function () {
4 | // 展示本地存储能力
5 | var logs = wx.getStorageSync('logs') || []
6 | logs.unshift(Date.now())
7 | wx.setStorageSync('logs', logs)
8 |
9 | // 登录
10 | wx.login({
11 | success: res => {
12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId
13 | }
14 | })
15 | // 获取用户信息
16 | wx.getSetting({
17 | success: res => {
18 | if (res.authSetting['scope.userInfo']) {
19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
20 | wx.getUserInfo({
21 | success: res => {
22 | // 可以将 res 发送给后台解码出 unionId
23 | this.globalData.userInfo = res.userInfo
24 |
25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
26 | // 所以此处加入 callback 以防止这种情况
27 | if (this.userInfoReadyCallback) {
28 | this.userInfoReadyCallback(res)
29 | }
30 | }
31 | })
32 | }
33 | }
34 | })
35 | },
36 | globalData: {
37 | userInfo: null
38 | }
39 | })
--------------------------------------------------------------------------------
/git-gin-wechat-test/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/logs/logs"
5 | ],
6 | "window": {
7 | "backgroundTextStyle": "light",
8 | "navigationBarBackgroundColor": "#fff",
9 | "navigationBarTitleText": "WeChat",
10 | "navigationBarTextStyle": "black"
11 | },
12 | "sitemapLocation": "sitemap.json"
13 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | .container {
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 200rpx 0;
9 | box-sizing: border-box;
10 | }
11 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/index/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | const app = getApp()
4 |
5 | Page({
6 | data: {
7 | motto: 'Hello World',
8 | userInfo: {},
9 | hasUserInfo: false,
10 | canIUse: wx.canIUse('button.open-type.getUserInfo')
11 | },
12 | //事件处理函数
13 | bindViewTap: function() {
14 | wx.navigateTo({
15 | url: '../logs/logs'
16 | })
17 | },
18 | onLoad: function () {
19 | if (app.globalData.userInfo) {
20 | this.setData({
21 | userInfo: app.globalData.userInfo,
22 | hasUserInfo: true
23 | })
24 | } else if (this.data.canIUse){
25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
26 | // 所以此处加入 callback 以防止这种情况
27 | app.userInfoReadyCallback = res => {
28 | this.setData({
29 | userInfo: res.userInfo,
30 | hasUserInfo: true
31 | })
32 | }
33 | } else {
34 | // 在没有 open-type=getUserInfo 版本的兼容处理
35 | wx.getUserInfo({
36 | success: res => {
37 | app.globalData.userInfo = res.userInfo
38 | this.setData({
39 | userInfo: res.userInfo,
40 | hasUserInfo: true
41 | })
42 | }
43 | })
44 | }
45 | },
46 | getUserInfo: function(e) {
47 | console.log(e)
48 | app.globalData.userInfo = e.detail.userInfo
49 | this.setData({
50 | userInfo: e.detail.userInfo,
51 | hasUserInfo: true
52 | })
53 | },
54 | doWxLogin(e) {
55 | wx.login({
56 | success(res) {
57 | if (res.code) {
58 | wx.request({
59 | method: 'POST',
60 | url: 'http://localhost:6340/wx/register', // Api地址
61 | data: {
62 | code: res.code,
63 | ...e.detail.userInfo
64 | }
65 | })
66 | }
67 | }
68 | })
69 |
70 | },
71 | doWebLogin(e) {
72 | wx.request({
73 | method: 'POST',
74 | url: 'http://localhost:6340/web/login', // Api地址
75 | data: {
76 | password: 'lgy981224',
77 | email: '15704634868@163.com'
78 | }
79 | })
80 | },
81 | doWebRegister(e) {
82 | wx.request({
83 | method: 'POST',
84 | url: 'http://localhost:6340/web/register', // Api地址
85 | data: {
86 | username: '给我点阳光就灿烂',
87 | password: 'lgy981224',
88 | email: '15704634868@163.com'
89 | }
90 | })
91 | }
92 | })
93 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{userInfo.nickName}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .userinfo {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | .userinfo-avatar {
9 | width: 128rpx;
10 | height: 128rpx;
11 | margin: 20rpx;
12 | border-radius: 50%;
13 | }
14 |
15 | .userinfo-nickname {
16 | color: #aaa;
17 | }
18 |
19 | .usermotto {
20 | margin-top: 200px;
21 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/logs/logs.js:
--------------------------------------------------------------------------------
1 | //logs.js
2 | const util = require('../../utils/util.js')
3 |
4 | Page({
5 | data: {
6 | logs: []
7 | },
8 | onLoad: function () {
9 | this.setData({
10 | logs: (wx.getStorageSync('logs') || []).map(log => {
11 | return util.formatTime(new Date(log))
12 | })
13 | })
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/logs/logs.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "查看启动日志",
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/logs/logs.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{index + 1}}. {{log}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/pages/logs/logs.wxss:
--------------------------------------------------------------------------------
1 | .log-list {
2 | display: flex;
3 | flex-direction: column;
4 | padding: 40rpx;
5 | }
6 | .log-item {
7 | margin: 10rpx;
8 | }
9 |
--------------------------------------------------------------------------------
/git-gin-wechat-test/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Project configuration file",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": false,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "autoAudits": false
13 | },
14 | "compileType": "miniprogram",
15 | "libVersion": "2.6.5",
16 | "appid": "wx6f9cc63ed9ded89b",
17 | "projectname": "git-gin-test",
18 | "debugOptions": {
19 | "hidedInDevtools": []
20 | },
21 | "isGameTourist": false,
22 | "simulatorType": "wechat",
23 | "simulatorPluginLibVersion": {},
24 | "condition": {
25 | "search": {
26 | "current": -1,
27 | "list": []
28 | },
29 | "conversation": {
30 | "current": -1,
31 | "list": []
32 | },
33 | "game": {
34 | "currentL": -1,
35 | "list": []
36 | },
37 | "miniprogram": {
38 | "current": -1,
39 | "list": []
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/git-gin-wechat-test/utils/util.js:
--------------------------------------------------------------------------------
1 | const formatTime = date => {
2 | const year = date.getFullYear()
3 | const month = date.getMonth() + 1
4 | const day = date.getDate()
5 | const hour = date.getHours()
6 | const minute = date.getMinutes()
7 | const second = date.getSeconds()
8 |
9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
10 | }
11 |
12 | const formatNumber = n => {
13 | n = n.toString()
14 | return n[1] ? n : '0' + n
15 | }
16 |
17 | module.exports = {
18 | formatTime: formatTime
19 | }
20 |
--------------------------------------------------------------------------------
/main:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/al8n/gin-for-miniprogram/8e06e88f8cf7294875d31ab17aec17d5ec80e2af/main
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "./apis"
5 | "log"
6 | )
7 |
8 | func main(){
9 | log.Println("Main log...")
10 | log.Fatal(apis.RunAPI("127.0.0.1:6340"))
11 | }
12 |
--------------------------------------------------------------------------------
/models/request/webRequest.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type WebUser struct {
4 | ID interface{} `bson:"_id,omitempty"` // mongodb创建时自动生成的ID
5 | Username string `bson:"username" binding:"required"`
6 | Password string `bson:"password" binding:"required"`
7 | Email string `bson:"email" binding:"required"`
8 | }
9 |
10 | type WebRegisterData struct {
11 | Username string `json:"username"`
12 | Password string `json:"password"`
13 | Email string `json:"email"`
14 | }
15 |
16 | type WebLoginData struct {
17 | Email string `json:"email" binding:"required"`
18 | Password string `json:"password" binding:"required"`
19 | }
20 |
21 | type WebTestData struct {
22 | Email string `json:"email" binding:"required"`
23 | Password string `json:"password" binding:"required"`
24 | }
25 |
26 | type WebLoginResponseData struct {
27 | ID interface{} `bson:"_id"`
28 | Email string `bson:"email"`
29 | //Phone string `bson:"phone"`
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/models/request/wxRequest.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type WxUser struct {
8 | ID interface{} `bson:"_id,omitempty"` // mongodb创建时自动生成的ID
9 | OpenID string `bson:"wxOpenId" binding:"required"`
10 | // UnionID string `bson:"wxUnionId"` 如果程序使用unionid 登录
11 | Nickname string `bson:"wxNickname"`
12 | AvatarUrl string `bson:"wxAvatarUrl"`
13 | Gender int `bson:"gender"`
14 | City string `bson:"city"`
15 | Province string `bson:"province"`
16 | Country string `bson:"country"`
17 | CreateDate time.Time `bson:"createDate"`
18 | /**
19 | put custom code here
20 | eg.
21 | Email string `json:"email" binding:"required"`
22 | Password string `json:"password" binding:"required"`
23 | UpdateDate time.Time `bson:"updated"`
24 | */
25 | }
26 |
27 |
28 | type WxRequestData struct {
29 | Code string `json:"code"`
30 | NickName string `json:"nickName"`
31 | Gender int `json:"gender"`
32 | City string `json:"city"`
33 | Province string `json:"province"`
34 | Country string `json:"country"`
35 | AvatarUrl string `json:"avatarUrl"`
36 | /**
37 | put custom code here
38 | eg.
39 | Email string `json:"email" binding:"required"`
40 | Password string `json:"password" binding:"required"`
41 | */
42 | }
43 |
44 | type Code2Session struct {
45 | Errcode int32 `json:"errcode"` // 错误码
46 | Errmsg string `json:"errmsg"` // 错误信息
47 | Openid string `json:"openid"` // 用户唯一标识
48 | // Unionid string `json:"unionid"` // 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见微信 UnionID
49 | SessionKey string `json:"session_key"` // 会话密钥
50 | }
--------------------------------------------------------------------------------