diff --git a/.gitignore b/.gitignore index 6237796..83e4288 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ .idea/ .git/ .DS_Store/ +.tmp/ \ No newline at end of file diff --git a/api/controller/Login.go b/api/controller/Login.go index 109b532..8c08678 100644 --- a/api/controller/Login.go +++ b/api/controller/Login.go @@ -1,12 +1,18 @@ package controller import ( + "fmt" "github.com/gin-gonic/gin" + "hepa-calc-api/api/dao" + "hepa-calc-api/api/model" "hepa-calc-api/api/requests" "hepa-calc-api/api/responses" + "hepa-calc-api/api/service" + "hepa-calc-api/extend/weChat" "hepa-calc-api/global" "hepa-calc-api/utils" "strconv" + "time" ) type Login struct{} @@ -29,6 +35,18 @@ func (r *Login) LoginPhone(c *gin.Context) { // LoginWx 微信授权登录 func (r *Login) LoginWx(c *gin.Context) { + userService := service.UserService{} + // 处理用户头像 + avatar, err := userService.HandleUserAvatar("https://thirdwx.qlogo.cn/mmopen/vi_32/dzhWq43iaB5GyJJUbajibSQtIt6d4Y9QPk5ImB1C14fPk6AOtcgaPztUzjKwb9kmPtqNpyCal5cEyw0YyTr1QEbdtHiaOViay4RgSpdm2XaduXA/132") + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + fmt.Println(avatar) + responses.Ok(c) + return + loginRequest := requests.LoginRequest{} req := loginRequest.LoginWx if err := c.ShouldBind(&req); err != nil { @@ -42,5 +60,88 @@ func (r *Login) LoginWx(c *gin.Context) { return } - responses.Ok(c) + // 获取微信网页授权access_token + webAccessToken, err := weChat.GetWebAccessToken(req.Code) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + fmt.Println(webAccessToken) + + // 网页授权拉取用户信息 + userInfo, err := weChat.GetUserInfo(webAccessToken.AccessToken, webAccessToken.OpenId) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + fmt.Println(userInfo) + + // 检测用户信息 + userDao := dao.UserDao{} + maps := make(map[string]interface{}) + maps["open_id"] = webAccessToken.OpenId + user, _ := userDao.GetUser(maps) + // 新用户处理方式 + if user == nil { + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + userService := service.UserService{} + // 处理用户头像 + avatar, err := userService.HandleUserAvatar(userInfo.HeadImgUrl) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + // 新增用户 + user := &model.User{ + UserName: userInfo.Nickname, + Mobile: "", + RegisterSource: req.Source, + OpenId: userInfo.OpenId, + UnionId: userInfo.UnionId, + Age: nil, + Sex: userInfo.Sex, + Avatar: avatar, + IsMember: 0, + MemberExpireDate: nil, + LoginAt: model.LocalTime(time.Now()), + LoginIp: req.LoginIp, + } + user, err = userDao.AddUser(tx, user) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + // 获取app用户信息 + var result *string + if result == nil { + // 新增app用户信息 + } + + tx.Commit() + } + + // 下发token + token := &utils.Token{ + UserId: fmt.Sprintf("%d", user.UserId), + } + + // 生成jwt + jwt, err := token.NewJWT() + if err != nil { + responses.FailWithMessage("登陆失败", c) + return + } + + responses.OkWithData(jwt, c) } diff --git a/api/dto/User.go b/api/dto/User.go index 3a58f4f..696a52a 100644 --- a/api/dto/User.go +++ b/api/dto/User.go @@ -4,6 +4,7 @@ import ( "fmt" "hepa-calc-api/api/model" "hepa-calc-api/utils" + "time" ) // UserDto 用户表 @@ -19,7 +20,7 @@ type UserDto struct { Sex uint `json:"sex"` // 性别(0:未知 1:男 2:女) Avatar string `json:"avatar"` // 头像 IsMember int `json:"is_member"` // 是否会员(0:否 1:是) - MemberExpireDate model.LocalTime `json:"member_expire_date"` // 会员到期时间(非会员时为null) + MemberExpireDate *time.Time `json:"member_expire_date"` // 会员到期时间(非会员时为null) LoginAt model.LocalTime `json:"login_at"` // 登陆时间 LoginIp string `json:"login_ip"` // 登陆ip CreatedAt model.LocalTime `json:"created_at"` // 创建时间 @@ -34,8 +35,8 @@ func GetUserDto(m *model.User) *UserDto { Mobile: m.Mobile, UserStatus: m.UserStatus, RegisterSource: m.RegisterSource, - Age: m.Age, - Sex: m.Sex, + Age: *m.Age, + Sex: uint(m.Sex), Avatar: utils.AddOssDomain(m.Avatar), IsMember: m.IsMember, MemberExpireDate: m.MemberExpireDate, diff --git a/api/model/User.go b/api/model/User.go index c6b0ba0..65bb301 100644 --- a/api/model/User.go +++ b/api/model/User.go @@ -8,20 +8,20 @@ import ( // User 用户表 type User struct { - UserId int64 `gorm:"column:user_id;type:bigint(19);primary_key;comment:用户id" json:"user_id"` - UserName string `gorm:"column:user_name;type:varchar(200);comment:用户名称" json:"user_name"` - Mobile string `gorm:"column:mobile;type:varchar(20);comment:手机号;NOT NULL" json:"mobile"` - UserStatus int `gorm:"column:user_status;type:tinyint(1);default:1;comment:状态(1:正常 2:禁用)" json:"user_status"` - RegisterSource int `gorm:"column:register_source;type:tinyint(1);default:1;comment:注册来源(1:app注册 2:公众号注册)" json:"register_source"` - OpenId string `gorm:"column:open_id;type:varchar(100);comment:用户微信标识" json:"open_id"` - UnionId string `gorm:"column:union_id;type:varchar(100);comment:微信开放平台标识" json:"union_id"` - Age uint `gorm:"column:age;type:int(10) unsigned;comment:年龄" json:"age"` - Sex uint `gorm:"column:sex;type:tinyint(1) unsigned;default:0;comment:性别(0:未知 1:男 2:女)" json:"sex"` - Avatar string `gorm:"column:avatar;type:varchar(255);comment:头像" json:"avatar"` - IsMember int `gorm:"column:is_member;type:tinyint(1);default:0;comment:是否会员(0:否 1:是)" json:"is_member"` - MemberExpireDate LocalTime `gorm:"column:member_expire_date;type:datetime;comment:会员到期时间(非会员时为null)" json:"member_expire_date"` - LoginAt LocalTime `gorm:"column:login_at;type:datetime;comment:登陆时间" json:"login_at"` - LoginIp string `gorm:"column:login_ip;type:varchar(255);comment:登陆ip" json:"login_ip"` + UserId int64 `gorm:"column:user_id;type:bigint(19);primary_key;comment:用户id" json:"user_id"` + UserName string `gorm:"column:user_name;type:varchar(200);comment:用户名称" json:"user_name"` + Mobile string `gorm:"column:mobile;type:varchar(20);comment:手机号;NOT NULL" json:"mobile"` + UserStatus int `gorm:"column:user_status;type:tinyint(1);default:1;comment:状态(1:正常 2:禁用)" json:"user_status"` + RegisterSource int `gorm:"column:register_source;type:tinyint(1);default:1;comment:注册来源(1:app注册 2:公众号注册)" json:"register_source"` + OpenId string `gorm:"column:open_id;type:varchar(100);comment:用户微信标识" json:"open_id"` + UnionId string `gorm:"column:union_id;type:varchar(100);comment:微信开放平台标识" json:"union_id"` + Age *uint `gorm:"column:age;type:int(10) unsigned;comment:年龄" json:"age"` + Sex int `gorm:"column:sex;type:tinyint(1) unsigned;default:0;comment:性别(0:未知 1:男 2:女)" json:"sex"` + Avatar string `gorm:"column:avatar;type:varchar(255);comment:头像" json:"avatar"` + IsMember int `gorm:"column:is_member;type:tinyint(1);default:0;comment:是否会员(0:否 1:是)" json:"is_member"` + MemberExpireDate *time.Time `gorm:"column:member_expire_date;type:datetime;comment:会员到期时间(非会员时为null)" json:"member_expire_date"` + LoginAt LocalTime `gorm:"column:login_at;type:datetime;comment:登陆时间" json:"login_at"` + LoginIp string `gorm:"column:login_ip;type:varchar(255);comment:登陆ip" json:"login_ip"` Model } diff --git a/api/requests/Login.go b/api/requests/Login.go index 545326e..aaa51b1 100644 --- a/api/requests/Login.go +++ b/api/requests/Login.go @@ -6,5 +6,7 @@ type LoginRequest struct { // LoginWx 微信授权登录 type LoginWx struct { - Code string `json:"code" form:"code" label:"授权码"` + Code string `json:"code" form:"code" label:"授权码"` + Source int `json:"source" form:"source" label:"来源(1:app 2:公众号)"` + LoginIp string `json:"login_ip" form:"login_ip" label:"登录ip"` } diff --git a/api/router/router.go b/api/router/router.go index 48c3e06..af8c6c4 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -78,11 +78,8 @@ func publicRouter(r *gin.Engine, api controller.Api) { // 手机号登录 loginGroup.POST("/phone", api.Login.LoginPhone) - wxGroup := loginGroup.Group("/wx") - { - // 微信授权登录 - wxGroup.POST("", api.Login.LoginWx) - } + // 微信授权登录 + loginGroup.POST("/wx", api.Login.LoginWx) } // 验证码 diff --git a/api/service/User.go b/api/service/User.go index 9c6073a..60fa32f 100644 --- a/api/service/User.go +++ b/api/service/User.go @@ -1,4 +1,41 @@ package service +import ( + "fmt" + "hepa-calc-api/extend/aliyun" + "hepa-calc-api/utils" + "math/rand" + "time" +) + type UserService struct { } + +// HandleUserAvatar 处理用户头像 +func (r *UserService) HandleUserAvatar(wxAvatar string) (avatar string, err error) { + if wxAvatar == "" { + return "", nil + } + + // 下载文件到内存 + ram, err := aliyun.GetObjectToRAM(wxAvatar) + if err != nil { + return "", err + } + + // 设置文件名字 + now := time.Now() + dateTimeString := now.Format("20060102150405") // 当前时间字符串 + rand.New(rand.NewSource(time.Now().UnixNano())) // 设置随机数 + ossPath := "/test/医生账户" + dateTimeString + fmt.Sprintf("%d", rand.Intn(9000)+1000) + ".png" + + // 上传oss + _, err = aliyun.PutObjectByte(ossPath, []byte(ram)) + if err != nil { + return "", err + } + + ossPath = utils.AddOssDomain("/" + ossPath) + + return ossPath, nil +} diff --git a/config.yaml b/config.yaml index 2a6d958..f06a4c8 100644 --- a/config.yaml +++ b/config.yaml @@ -47,8 +47,8 @@ dysms: # [微信] wechat: - app-id: wxa2ab38f49998a990 - app-secret: 2671d2f4285180ddec5a5a2b16ed50f2 + app-id: wxc8ac5051745bc795 + app-secret: 678b63a8a7541e528abc3040c3cea809 pay-notify-url: callback/wxpay/inquiry/success refund-notify-url: callback/wxpay/inquiry/refund refund-notify-domain: https://dev.hospital.applets.igandanyiyuan.com/ diff --git a/config/wechat.go b/config/wechat.go index 2f67c6e..70a4afd 100644 --- a/config/wechat.go +++ b/config/wechat.go @@ -1,10 +1,10 @@ package config type Wechat struct { - AppId string `mapstructure:"patient-app-id" json:"patient-app-id" yaml:"patient-app-id"` - AppSecret string `mapstructure:"patient-app-secret" json:"patient-app-secret" yaml:"patient-app-secret"` - PayNotifyUrl string `mapstructure:"patient-inquiry-pay-notify-url" json:"patient-inquiry-pay-notify-url" yaml:"patient-inquiry-pay-notify-url"` - RefundNotifyUrl string `mapstructure:"patient-inquiry-refund-notify-url" json:"patient-inquiry-refund-notify-url" yaml:"patient-inquiry-refund-notify-url"` + AppId string `mapstructure:"app-id" json:"app-id" yaml:"patient-app-id"` + AppSecret string `mapstructure:"app-secret" json:"app-secret" yaml:"app-secret"` + PayNotifyUrl string `mapstructure:"pay-notify-url" json:"pay-notify-url" yaml:"pay-notify-url"` + RefundNotifyUrl string `mapstructure:"refund-notify-url" json:"refund-notify-url" yaml:"refund-notify-url"` Pay1636644248 Pay1636644248 `mapstructure:"pay-1636644248" json:"pay-1636644248" yaml:"pay-1636644248"` } diff --git a/extend/weChat/userInfo.go b/extend/weChat/userInfo.go new file mode 100644 index 0000000..b61804a --- /dev/null +++ b/extend/weChat/userInfo.go @@ -0,0 +1,76 @@ +package weChat + +import ( + "encoding/json" + "errors" + "io" + "net/http" +) + +// GetUserInfoResponse 网页授权拉取用户信息返回值 +type GetUserInfoResponse struct { + OpenId string `json:"openid" form:"openid" label:"openid"` + Nickname string `json:"nickname" form:"nickname" label:"用户昵称"` + Sex int `json:"sex" form:"sex" label:"性别"` // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 + Province string `json:"province" form:"province" label:"省份"` + City string `json:"city" form:"city" label:"城市"` + Country string `json:"country" form:"country" label:"国家"` + HeadImgUrl string `json:"headimgurl" form:"headimgurl" label:"头像"` + UnionId string `json:"unionid" form:"unionid" label:"unionid"` + Errcode *int `json:"errcode" form:"errcode" label:"errcode"` + Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"` +} + +// GetUserInfo 网页授权拉取用户信息 +func GetUserInfo(accessToken, openId string) (r *GetUserInfoResponse, err error) { + if accessToken == "" { + return nil, errors.New("授权失败") + } + + if openId == "" { + return nil, errors.New("授权失败") + } + + // 拼接请求数据 + requestUrl := "https://api.weixin.qq.com/sns/userinfo?" + + "access_token=" + accessToken + + "&openid=" + openId + + "&lang=zh_CN" + + // 发送GET请求 + resp, err := http.Get(requestUrl) + if err != nil { + return nil, err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != 200 { + return nil, errors.New("请求失败") + } + + // 读取响应体 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response GetUserInfoResponse + err = json.Unmarshal([]byte(respBody), &response) + if err != nil { + // json解析失败 + return nil, err + } + + if response.Errcode != nil { + if response.Errmsg != "" { + return nil, errors.New(response.Errmsg) + } else { + return nil, errors.New("请求失败") + } + } + + return &response, err +} diff --git a/extend/weChat/webAccessToken.go b/extend/weChat/webAccessToken.go new file mode 100644 index 0000000..f41ee42 --- /dev/null +++ b/extend/weChat/webAccessToken.go @@ -0,0 +1,68 @@ +package weChat + +import ( + "encoding/json" + "errors" + "hepa-calc-api/config" + "io" + "net/http" +) + +// GetWebAccessTokenResponse 获取网页授权access_token返回值 +type GetWebAccessTokenResponse struct { + AccessToken string `json:"access_token" form:"access_token" label:"网页授权接口调用凭证"` + ExpiresIn int `json:"expires_in" form:"expires_in" label:"access_token接口调用凭证超时时间"` + RefreshToken string `json:"refresh_token" form:"refresh_token" label:"用户刷新access_token"` + OpenId string `json:"openid" form:"openid" label:"openid"` + Scope string `json:"scope" form:"scope" label:"scope"` + UnionId string `json:"unionid" form:"unionid" label:"unionid"` + Errcode *int `json:"errcode" form:"errcode" label:"errcode"` + Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"` +} + +// GetWebAccessToken 获取网页授权access_token +func GetWebAccessToken(code string) (r *GetWebAccessTokenResponse, err error) { + // 拼接请求数据 + requestUrl := "https://api.weixin.qq.com/sns/oauth2/access_token?" + + "appid=" + config.C.Wechat.AppId + + "&secret=" + config.C.Wechat.AppSecret + + "&code=" + code + + "&grant_type=authorization_code" + + // 发送GET请求 + resp, err := http.Get(requestUrl) + if err != nil { + return nil, err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != 200 { + return nil, errors.New("请求失败") + } + + // 读取响应体 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response GetWebAccessTokenResponse + err = json.Unmarshal([]byte(respBody), &response) + if err != nil { + // json解析失败 + return nil, err + } + + if response.Errcode != nil { + if response.Errmsg != "" { + return nil, errors.New(response.Errmsg) + } else { + return nil, errors.New("请求失败") + } + } + + return &response, err +}