feat(wechat): 重构小程序登录接口,实现自动生成用户信息和头像
- 移除微信用户信息解密相关代码,改为系统自动生成用户名和头像 - 添加用户信息存储功能,使用openID作为用户ID - 集成govatar和namegenerator库生成用户头像和随机用户名 - 添加token生成功能,返回给客户端用于后续认证 - 更新swagger文档,反映接口变更
This commit is contained in:
parent
e4d4258918
commit
a4e532c6b6
89
docs/docs.go
89
docs/docs.go
@ -1165,7 +1165,7 @@ const docTemplate = `{
|
||||
},
|
||||
"/api/wechat/miniprogram/login": {
|
||||
"post": {
|
||||
"description": "通过AppID和code获取用户的openid和session_key",
|
||||
"description": "通过AppID和code获取用户的openid,系统自动生成用户名和头像",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@ -2378,49 +2378,6 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.DecryptedUserInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatarUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
},
|
||||
"country": {
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"type": "integer"
|
||||
},
|
||||
"nickName": {
|
||||
"type": "string"
|
||||
},
|
||||
"openId": {
|
||||
"type": "string"
|
||||
},
|
||||
"province": {
|
||||
"type": "string"
|
||||
},
|
||||
"unionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"watermark": {
|
||||
"$ref": "#/definitions/wechat.Watermark"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.Watermark": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appid": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.generateQRCodeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -2469,39 +2426,15 @@ const docTemplate = `{
|
||||
"description": "小程序AppID",
|
||||
"type": "string"
|
||||
},
|
||||
"encrypted_data": {
|
||||
"description": "加密数据(可选)",
|
||||
"type": "string"
|
||||
},
|
||||
"iv": {
|
||||
"description": "初始向量(可选)",
|
||||
"type": "string"
|
||||
},
|
||||
"js_code": {
|
||||
"description": "登录时获取的code",
|
||||
"type": "string"
|
||||
},
|
||||
"raw_data": {
|
||||
"description": "原始数据(可选,用于签名验证)",
|
||||
"type": "string"
|
||||
},
|
||||
"signature": {
|
||||
"description": "签名(可选,用于验证数据完整性)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.miniprogramLoginResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"decrypted_data": {
|
||||
"description": "解密后的用户信息(可选)",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/wechat.DecryptedUserInfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -2509,16 +2442,28 @@ const docTemplate = `{
|
||||
"description": "用户唯一标识",
|
||||
"type": "string"
|
||||
},
|
||||
"session_key": {
|
||||
"description": "会话密钥",
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"token": {
|
||||
"description": "登录token",
|
||||
"type": "string"
|
||||
},
|
||||
"unionid": {
|
||||
"description": "用户在开放平台的唯一标识符",
|
||||
"type": "string"
|
||||
},
|
||||
"user_avatar": {
|
||||
"description": "用户头像",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"description": "用户ID",
|
||||
"type": "string"
|
||||
},
|
||||
"user_name": {
|
||||
"description": "用户昵称",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,7 +1157,7 @@
|
||||
},
|
||||
"/api/wechat/miniprogram/login": {
|
||||
"post": {
|
||||
"description": "通过AppID和code获取用户的openid和session_key",
|
||||
"description": "通过AppID和code获取用户的openid,系统自动生成用户名和头像",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
@ -2370,49 +2370,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.DecryptedUserInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatarUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
},
|
||||
"country": {
|
||||
"type": "string"
|
||||
},
|
||||
"gender": {
|
||||
"type": "integer"
|
||||
},
|
||||
"nickName": {
|
||||
"type": "string"
|
||||
},
|
||||
"openId": {
|
||||
"type": "string"
|
||||
},
|
||||
"province": {
|
||||
"type": "string"
|
||||
},
|
||||
"unionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"watermark": {
|
||||
"$ref": "#/definitions/wechat.Watermark"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.Watermark": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appid": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.generateQRCodeRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -2461,39 +2418,15 @@
|
||||
"description": "小程序AppID",
|
||||
"type": "string"
|
||||
},
|
||||
"encrypted_data": {
|
||||
"description": "加密数据(可选)",
|
||||
"type": "string"
|
||||
},
|
||||
"iv": {
|
||||
"description": "初始向量(可选)",
|
||||
"type": "string"
|
||||
},
|
||||
"js_code": {
|
||||
"description": "登录时获取的code",
|
||||
"type": "string"
|
||||
},
|
||||
"raw_data": {
|
||||
"description": "原始数据(可选,用于签名验证)",
|
||||
"type": "string"
|
||||
},
|
||||
"signature": {
|
||||
"description": "签名(可选,用于验证数据完整性)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wechat.miniprogramLoginResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"decrypted_data": {
|
||||
"description": "解密后的用户信息(可选)",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/wechat.DecryptedUserInfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -2501,16 +2434,28 @@
|
||||
"description": "用户唯一标识",
|
||||
"type": "string"
|
||||
},
|
||||
"session_key": {
|
||||
"description": "会话密钥",
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"token": {
|
||||
"description": "登录token",
|
||||
"type": "string"
|
||||
},
|
||||
"unionid": {
|
||||
"description": "用户在开放平台的唯一标识符",
|
||||
"type": "string"
|
||||
},
|
||||
"user_avatar": {
|
||||
"description": "用户头像",
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"description": "用户ID",
|
||||
"type": "string"
|
||||
},
|
||||
"user_name": {
|
||||
"description": "用户昵称",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -695,34 +695,6 @@ definitions:
|
||||
description: 真实图片地址
|
||||
type: string
|
||||
type: object
|
||||
wechat.DecryptedUserInfo:
|
||||
properties:
|
||||
avatarUrl:
|
||||
type: string
|
||||
city:
|
||||
type: string
|
||||
country:
|
||||
type: string
|
||||
gender:
|
||||
type: integer
|
||||
nickName:
|
||||
type: string
|
||||
openId:
|
||||
type: string
|
||||
province:
|
||||
type: string
|
||||
unionId:
|
||||
type: string
|
||||
watermark:
|
||||
$ref: '#/definitions/wechat.Watermark'
|
||||
type: object
|
||||
wechat.Watermark:
|
||||
properties:
|
||||
appid:
|
||||
type: string
|
||||
timestamp:
|
||||
type: integer
|
||||
type: object
|
||||
wechat.generateQRCodeRequest:
|
||||
properties:
|
||||
app_id:
|
||||
@ -754,44 +726,37 @@ definitions:
|
||||
app_id:
|
||||
description: 小程序AppID
|
||||
type: string
|
||||
encrypted_data:
|
||||
description: 加密数据(可选)
|
||||
type: string
|
||||
iv:
|
||||
description: 初始向量(可选)
|
||||
type: string
|
||||
js_code:
|
||||
description: 登录时获取的code
|
||||
type: string
|
||||
raw_data:
|
||||
description: 原始数据(可选,用于签名验证)
|
||||
type: string
|
||||
signature:
|
||||
description: 签名(可选,用于验证数据完整性)
|
||||
type: string
|
||||
required:
|
||||
- app_id
|
||||
- js_code
|
||||
type: object
|
||||
wechat.miniprogramLoginResponse:
|
||||
properties:
|
||||
decrypted_data:
|
||||
allOf:
|
||||
- $ref: '#/definitions/wechat.DecryptedUserInfo'
|
||||
description: 解密后的用户信息(可选)
|
||||
message:
|
||||
type: string
|
||||
openid:
|
||||
description: 用户唯一标识
|
||||
type: string
|
||||
session_key:
|
||||
description: 会话密钥
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
token:
|
||||
description: 登录token
|
||||
type: string
|
||||
unionid:
|
||||
description: 用户在开放平台的唯一标识符
|
||||
type: string
|
||||
user_avatar:
|
||||
description: 用户头像
|
||||
type: string
|
||||
user_id:
|
||||
description: 用户ID
|
||||
type: string
|
||||
user_name:
|
||||
description: 用户昵称
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
@ -1542,7 +1507,7 @@ paths:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 通过AppID和code获取用户的openid和session_key
|
||||
description: 通过AppID和code获取用户的openid,系统自动生成用户名和头像
|
||||
parameters:
|
||||
- description: 请求参数
|
||||
in: body
|
||||
|
||||
2
go.mod
2
go.mod
@ -63,6 +63,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
@ -79,6 +80,7 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/o1egl/govatar v0.4.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
|
||||
9
go.sum
9
go.sum
@ -119,6 +119,7 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
|
||||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -241,6 +242,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40=
|
||||
github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
@ -318,6 +321,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/o1egl/govatar v0.4.1 h1:RRzAxm52WpZMSEoWgAXrTcXWKhIUPpgpI54KP+UI0Ew=
|
||||
github.com/o1egl/govatar v0.4.1/go.mod h1:cSBJjpgYiKmQ8E+C4zNBcsbuDwy9UH4HS8BwE4m6JmQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
@ -344,10 +349,12 @@ github.com/rs/cors v1.8.1 h1:OrP+y5H+5Md29ACTA9imbALaKHwOSUZkcizaG0LT5ow=
|
||||
github.com/rs/cors v1.8.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644 h1:BBwREPixt0iE77C9z7DOenoeh5OGFrzyL1cWOp5oQTs=
|
||||
github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644/go.mod h1:gmu40DuK3SLdKUzGOUofS3UDZwyeOUy6ZjPPuaALatw=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ=
|
||||
github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
@ -395,6 +402,7 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -800,6 +808,7 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
||||
@ -94,7 +94,7 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc {
|
||||
|
||||
// 分页查询指定小程序的最新消息
|
||||
resultData, err := query.
|
||||
Order(h.readDB.AppMessageLog.SendTime.Desc()).
|
||||
Order(h.readDB.AppMessageLog.SendTime.Asc()).
|
||||
Offset((req.Page - 1) * req.PageSize).
|
||||
Limit(req.PageSize).
|
||||
Find()
|
||||
|
||||
@ -87,7 +87,7 @@ func (h *handler) AppMessagePageList() core.HandlerFunc {
|
||||
countQueryDB := query.Session(&gorm.Session{})
|
||||
|
||||
resultData, err := listQueryDB.
|
||||
Order(h.readDB.AppMessageLog.SendTime.Desc()).
|
||||
Order(h.readDB.AppMessageLog.SendTime.Asc()).
|
||||
Limit(req.PageSize).
|
||||
Offset((req.Page - 1) * req.PageSize).
|
||||
Find()
|
||||
|
||||
@ -86,7 +86,7 @@ func (h *handler) AppMessagePageList() core.HandlerFunc {
|
||||
countQueryDB := query.Session(&gorm.Session{})
|
||||
|
||||
resultData, err := listQueryDB.
|
||||
Order(h.readDB.AppMessageLog.SendTime.Desc()).
|
||||
Order(h.readDB.AppMessageLog.SendTime.Asc()).
|
||||
Limit(req.PageSize).
|
||||
Offset((req.Page - 1) * req.PageSize).
|
||||
Find()
|
||||
|
||||
@ -1,33 +1,39 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/goombaio/namegenerator"
|
||||
"github.com/o1egl/govatar"
|
||||
|
||||
"mini-chat/internal/code"
|
||||
"mini-chat/internal/pkg/core"
|
||||
"mini-chat/internal/pkg/httpclient"
|
||||
"mini-chat/internal/pkg/validation"
|
||||
"mini-chat/internal/pkg/wechat"
|
||||
"mini-chat/internal/repository/mysql/model"
|
||||
)
|
||||
|
||||
type miniprogramLoginRequest struct {
|
||||
AppID string `json:"app_id" binding:"required"` // 小程序AppID
|
||||
JSCode string `json:"js_code" binding:"required"` // 登录时获取的code
|
||||
EncryptedData string `json:"encrypted_data,omitempty"` // 加密数据(可选)
|
||||
IV string `json:"iv,omitempty"` // 初始向量(可选)
|
||||
RawData string `json:"raw_data,omitempty"` // 原始数据(可选,用于签名验证)
|
||||
Signature string `json:"signature,omitempty"` // 签名(可选,用于验证数据完整性)
|
||||
AppID string `json:"app_id" binding:"required"` // 小程序AppID
|
||||
JSCode string `json:"js_code" binding:"required"` // 登录时获取的code
|
||||
}
|
||||
|
||||
type miniprogramLoginResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
OpenID string `json:"openid,omitempty"` // 用户唯一标识
|
||||
SessionKey string `json:"session_key,omitempty"` // 会话密钥
|
||||
UnionID string `json:"unionid,omitempty"` // 用户在开放平台的唯一标识符
|
||||
DecryptedData *wechat.DecryptedUserInfo `json:"decrypted_data,omitempty"` // 解密后的用户信息(可选)
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Token string `json:"token"` // 登录token
|
||||
UserID string `json:"user_id"` // 用户ID
|
||||
UserName string `json:"user_name"` // 用户昵称
|
||||
Avatar string `json:"user_avatar"` // 用户头像
|
||||
OpenID string `json:"openid,omitempty"` // 用户唯一标识
|
||||
UnionID string `json:"unionid,omitempty"` // 用户在开放平台的唯一标识符
|
||||
}
|
||||
|
||||
// Code2SessionResponse 微信code2Session接口响应
|
||||
@ -41,7 +47,7 @@ type Code2SessionResponse struct {
|
||||
|
||||
// MiniprogramLogin 小程序登录
|
||||
// @Summary 小程序登录
|
||||
// @Description 通过AppID和code获取用户的openid和session_key
|
||||
// @Description 通过AppID和code获取用户的openid,系统自动生成用户名和头像
|
||||
// @Tags 微信
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
@ -90,46 +96,150 @@ func (h *handler) MiniprogramLogin() core.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// 查询或创建用户
|
||||
user, err := h.getOrCreateUser(ctx, req.AppID, openID, unionID)
|
||||
if err != nil {
|
||||
h.logger.Error(fmt.Sprintf("获取或创建用户失败: %s", err.Error()))
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
"用户创建失败",
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成登录token
|
||||
token, err := h.generateToken(user.UserID, sessionKey)
|
||||
if err != nil {
|
||||
h.logger.Error(fmt.Sprintf("生成token失败: %s", err.Error()))
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusInternalServerError,
|
||||
code.ServerError,
|
||||
"token生成失败",
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
res.Success = true
|
||||
res.Message = "登录成功"
|
||||
res.Token = token
|
||||
res.UserID = user.UserID
|
||||
res.UserName = user.UserName
|
||||
res.Avatar = user.UserAvatar
|
||||
res.OpenID = openID
|
||||
res.SessionKey = sessionKey
|
||||
res.UnionID = unionID
|
||||
|
||||
// 如果提供了加密数据,则进行解密
|
||||
if req.EncryptedData != "" && req.IV != "" {
|
||||
// 如果提供了签名验证数据,先验证签名
|
||||
if req.RawData != "" && req.Signature != "" {
|
||||
if !wechat.VerifySignature(req.RawData, req.Signature, sessionKey) {
|
||||
h.logger.Warn("数据签名验证失败")
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ParamBindError,
|
||||
"数据签名验证失败",
|
||||
))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 解密用户数据
|
||||
decryptedUserInfo, err := wechat.DecryptUserInfo(sessionKey, req.EncryptedData, req.IV)
|
||||
if err != nil {
|
||||
h.logger.Error(fmt.Sprintf("解密用户数据失败: %s", err.Error()))
|
||||
ctx.AbortWithError(core.Error(
|
||||
http.StatusBadRequest,
|
||||
code.ServerError,
|
||||
"解密用户数据失败",
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
res.DecryptedData = decryptedUserInfo
|
||||
}
|
||||
|
||||
ctx.Payload(res)
|
||||
}
|
||||
}
|
||||
|
||||
// getOrCreateUser 获取或创建用户
|
||||
func (h *handler) getOrCreateUser(ctx core.Context, appID, openID, unionID string) (*model.AppUser, error) {
|
||||
// 先查询用户是否存在(使用openID作为用户ID)
|
||||
user, err := h.readDB.AppUser.WithContext(ctx.RequestContext()).
|
||||
Where(h.readDB.AppUser.AppID.Eq(appID)).
|
||||
Where(h.readDB.AppUser.UserID.Eq(openID)).
|
||||
First()
|
||||
|
||||
if err == nil {
|
||||
// 用户已存在,直接返回
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// 用户不存在,创建新用户
|
||||
// 生成随机用户名
|
||||
seed := time.Now().UTC().UnixNano()
|
||||
nameGen := namegenerator.NewNameGenerator(seed)
|
||||
username := nameGen.Generate()
|
||||
|
||||
// 生成头像URL
|
||||
avatarURL, err := h.generateAvatar(openID)
|
||||
if err != nil {
|
||||
h.logger.Warn(fmt.Sprintf("生成头像失败: %s,使用默认头像", err.Error()))
|
||||
avatarURL = "/static/avatars/default.svg"
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
newUser := &model.AppUser{
|
||||
AppID: appID,
|
||||
UserID: openID, // 使用openID作为用户ID
|
||||
UserName: username,
|
||||
UserMobile: "", // 暂时为空
|
||||
UserAvatar: avatarURL,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = h.writeDB.AppUser.WithContext(ctx.RequestContext()).Create(newUser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建用户失败: %v", err)
|
||||
}
|
||||
|
||||
return newUser, nil
|
||||
}
|
||||
|
||||
// generateAvatar 生成头像
|
||||
func (h *handler) generateAvatar(seed string) (string, error) {
|
||||
// 根据seed生成随机性别
|
||||
gender := govatar.MALE
|
||||
if len(seed)%2 == 0 {
|
||||
gender = govatar.FEMALE
|
||||
}
|
||||
|
||||
// 生成头像文件名(基于seed的hash)
|
||||
filename := fmt.Sprintf("%x", seed)
|
||||
if len(filename) > 16 {
|
||||
filename = filename[:16]
|
||||
}
|
||||
|
||||
// 生成头像文件路径
|
||||
avatarFilename := fmt.Sprintf("%s_%d.png", filename, gender)
|
||||
avatarPath := fmt.Sprintf("static/avatars/%s", avatarFilename)
|
||||
avatarURL := fmt.Sprintf("/static/avatars/%s", avatarFilename)
|
||||
|
||||
// 检查文件是否已存在
|
||||
if _, err := os.Stat(avatarPath); err == nil {
|
||||
// 文件已存在,直接返回URL
|
||||
return avatarURL, nil
|
||||
}
|
||||
|
||||
// 生成头像图片
|
||||
img, err := govatar.GenerateForUsername(gender, seed)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("生成头像失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
file, err := os.Create(avatarPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建头像文件失败: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 保存PNG图片
|
||||
err = png.Encode(file, img)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("保存头像文件失败: %v", err)
|
||||
}
|
||||
|
||||
return avatarURL, nil
|
||||
}
|
||||
|
||||
// generateToken 生成登录token
|
||||
func (h *handler) generateToken(userID, sessionKey string) (string, error) {
|
||||
// 生成32字节的随机token
|
||||
bytes := make([]byte, 32)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := hex.EncodeToString(bytes)
|
||||
|
||||
// TODO: 这里应该将token保存到缓存或数据库中,并设置过期时间
|
||||
// 可以结合sessionKey一起存储,用于后续的用户身份验证
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// callCode2Session 调用微信code2Session接口
|
||||
func (h *handler) callCode2Session(ctx core.Context, appID, appSecret, jsCode string) (string, string, string, error) {
|
||||
// 构建请求URL
|
||||
|
||||
@ -233,6 +233,7 @@ func New(logger logger.CustomLogger, options ...Option) (Mux, error) {
|
||||
|
||||
mux.engine.Use(cors.New())
|
||||
mux.engine.StaticFS("resources", gin.Dir(configs.GetResourcesFilePath(), true))
|
||||
mux.engine.StaticFS("static", gin.Dir("static", true))
|
||||
|
||||
// withoutTracePaths 这些请求,默认不记录日志
|
||||
withoutTracePaths := map[string]bool{
|
||||
|
||||
5
static/avatars/default.svg
Normal file
5
static/avatars/default.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="50" cy="50" r="50" fill="#e0e0e0"/>
|
||||
<circle cx="50" cy="35" r="15" fill="#9e9e9e"/>
|
||||
<path d="M20 80 Q20 65 35 65 L65 65 Q80 65 80 80 L80 100 L20 100 Z" fill="#9e9e9e"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 281 B |
Loading…
x
Reference in New Issue
Block a user