├── .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 | } --------------------------------------------------------------------------------