添加用户信息更新接口,掩码敏感信息;修正 ContactQQ 字段命名
This commit is contained in:
22
README.md
22
README.md
@@ -2,26 +2,20 @@
|
|||||||
|
|
||||||
核心流程:
|
核心流程:
|
||||||
|
|
||||||
- [x] 注册与登录
|
- [ ] 提取记录
|
||||||
- [x] 对接接口
|
- [ ] 使用记录
|
||||||
- [ ] 人机风险分级验证
|
|
||||||
- [ ] jwt 签发
|
|
||||||
- [x] 鉴权
|
|
||||||
- [x] 实名认证
|
|
||||||
- [x] 对接接口
|
|
||||||
- [x] 充值或购买
|
|
||||||
- [x] 对接接口
|
|
||||||
- [ ] 提取记录
|
|
||||||
- [x] 提取 IP
|
|
||||||
- [ ] 长效提取
|
|
||||||
- [ ] 使用记录
|
|
||||||
- [x] 连接
|
|
||||||
|
|
||||||
中间件:
|
中间件:
|
||||||
|
|
||||||
- [ ] Limiter
|
- [ ] Limiter
|
||||||
- [ ] Compress
|
- [ ] Compress
|
||||||
|
|
||||||
|
考虑统计接口调用频率并通过接口展示
|
||||||
|
|
||||||
|
考虑登录时曾经输入过验证码的用户,登录成功后允许一段时间内免输验证码
|
||||||
|
|
||||||
|
修改 postgres 默认事务级别到 RR
|
||||||
|
|
||||||
撤销令牌需要改成用户权限,只有本人可以撤销
|
撤销令牌需要改成用户权限,只有本人可以撤销
|
||||||
|
|
||||||
扩展 device 权限验证方式,提供一种方法区分内部和外部服务
|
扩展 device 权限验证方式,提供一种方法区分内部和外部服务
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
return field
|
return field
|
||||||
}),
|
}),
|
||||||
|
gen.FieldRename("contact_qq", "ContactQQ"),
|
||||||
}
|
}
|
||||||
|
|
||||||
customs := make(map[string]any)
|
customs := make(map[string]any)
|
||||||
|
|||||||
@@ -313,7 +313,28 @@ func Introspect(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 掩码敏感信息
|
||||||
|
if profile.Phone != "" {
|
||||||
|
profile.Phone = maskPhone(profile.Phone)
|
||||||
|
}
|
||||||
|
if profile.IDNo != "" {
|
||||||
|
profile.IDNo = maskIdNo(profile.IDNo)
|
||||||
|
}
|
||||||
return c.JSON(IntrospectResp{*profile})
|
return c.JSON(IntrospectResp{*profile})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maskPhone(phone string) string {
|
||||||
|
if len(phone) < 11 {
|
||||||
|
return phone
|
||||||
|
}
|
||||||
|
return phone[:3] + "****" + phone[7:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskIdNo(idNo string) string {
|
||||||
|
if len(idNo) < 18 {
|
||||||
|
return idNo
|
||||||
|
}
|
||||||
|
return idNo[:3] + "*********" + idNo[14:]
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|||||||
@@ -2,15 +2,145 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"platform/web/auth"
|
"platform/web/auth"
|
||||||
|
"platform/web/common"
|
||||||
|
m "platform/web/models"
|
||||||
q "platform/web/queries"
|
q "platform/web/queries"
|
||||||
s "platform/web/services"
|
s "platform/web/services"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// region recharge
|
// region /update
|
||||||
|
|
||||||
|
type UpdateUserReq struct {
|
||||||
|
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
||||||
|
Email string `json:"email" validate:"omitempty,email"`
|
||||||
|
ContactQQ string `json:"contact_qq" validate:"omitempty,qq"`
|
||||||
|
ContactWechat string `json:"contact_wechat" validate:"omitempty,wechat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUser(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
authCtx, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(UpdateUserReq)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
_, err = q.User.
|
||||||
|
Where(q.User.ID.Eq(authCtx.Payload.Id)).
|
||||||
|
Updates(m.User{
|
||||||
|
Username: req.Username,
|
||||||
|
Email: req.Email,
|
||||||
|
ContactQQ: req.ContactQQ,
|
||||||
|
ContactWechat: req.ContactWechat,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region /update/account
|
||||||
|
|
||||||
|
type UpdateAccountReq struct {
|
||||||
|
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
||||||
|
Password string `json:"password" validate:"omitempty,min=6,max=20"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateAccount(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
authCtx, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(UpdateAccountReq)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
_, err = q.User.
|
||||||
|
Where(q.User.ID.Eq(authCtx.Payload.Id)).
|
||||||
|
Updates(m.User{
|
||||||
|
Username: req.Username,
|
||||||
|
Password: req.Password,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region /update/password
|
||||||
|
|
||||||
|
type UpdatePasswordReq struct {
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePassword(c *fiber.Ctx) error {
|
||||||
|
// 检查权限
|
||||||
|
authCtx, err := auth.Protect(c, []s.PayloadType{s.PayloadUser}, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求参数
|
||||||
|
req := new(UpdatePasswordReq)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证手机令牌
|
||||||
|
if req.Phone == "" || req.Code == "" {
|
||||||
|
return common.NewErr("user", "手机号码和验证码不能为空")
|
||||||
|
}
|
||||||
|
err = s.Verifier.VerifySms(c.Context(), req.Phone, req.Code)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
newHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = q.User.
|
||||||
|
Where(q.User.ID.Eq(authCtx.Payload.Id)).
|
||||||
|
UpdateColumn(q.User.Password, newHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region /recharge
|
||||||
|
|
||||||
type RechargePrepareReq struct {
|
type RechargePrepareReq struct {
|
||||||
Amount float64 `json:"amount" validate:"required,min=0.01"`
|
Amount float64 `json:"amount" validate:"required,min=0.01"`
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type User struct {
|
|||||||
IDType int32 `gorm:"column:id_type;not null;comment:认证类型:0-未认证,1-个人认证,2-企业认证" json:"id_type"` // 认证类型:0-未认证,1-个人认证,2-企业认证
|
IDType int32 `gorm:"column:id_type;not null;comment:认证类型:0-未认证,1-个人认证,2-企业认证" json:"id_type"` // 认证类型:0-未认证,1-个人认证,2-企业认证
|
||||||
IDNo string `gorm:"column:id_no;comment:身份证号或营业执照号" json:"id_no"` // 身份证号或营业执照号
|
IDNo string `gorm:"column:id_no;comment:身份证号或营业执照号" json:"id_no"` // 身份证号或营业执照号
|
||||||
IDToken string `gorm:"column:id_token;comment:身份验证标识" json:"id_token"` // 身份验证标识
|
IDToken string `gorm:"column:id_token;comment:身份验证标识" json:"id_token"` // 身份验证标识
|
||||||
ContactQq string `gorm:"column:contact_qq;comment:QQ联系方式" json:"contact_qq"` // QQ联系方式
|
ContactQQ string `gorm:"column:contact_qq;comment:QQ联系方式" json:"contact_qq"` // QQ联系方式
|
||||||
ContactWechat string `gorm:"column:contact_wechat;comment:微信联系方式" json:"contact_wechat"` // 微信联系方式
|
ContactWechat string `gorm:"column:contact_wechat;comment:微信联系方式" json:"contact_wechat"` // 微信联系方式
|
||||||
LastLogin common.LocalDateTime `gorm:"column:last_login;comment:最后登录时间" json:"last_login"` // 最后登录时间
|
LastLogin common.LocalDateTime `gorm:"column:last_login;comment:最后登录时间" json:"last_login"` // 最后登录时间
|
||||||
LastLoginHost string `gorm:"column:last_login_host;comment:最后登录地址" json:"last_login_host"` // 最后登录地址
|
LastLoginHost string `gorm:"column:last_login_host;comment:最后登录地址" json:"last_login_host"` // 最后登录地址
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func newUser(db *gorm.DB, opts ...gen.DOOption) user {
|
|||||||
_user.IDType = field.NewInt32(tableName, "id_type")
|
_user.IDType = field.NewInt32(tableName, "id_type")
|
||||||
_user.IDNo = field.NewString(tableName, "id_no")
|
_user.IDNo = field.NewString(tableName, "id_no")
|
||||||
_user.IDToken = field.NewString(tableName, "id_token")
|
_user.IDToken = field.NewString(tableName, "id_token")
|
||||||
_user.ContactQq = field.NewString(tableName, "contact_qq")
|
_user.ContactQQ = field.NewString(tableName, "contact_qq")
|
||||||
_user.ContactWechat = field.NewString(tableName, "contact_wechat")
|
_user.ContactWechat = field.NewString(tableName, "contact_wechat")
|
||||||
_user.LastLogin = field.NewField(tableName, "last_login")
|
_user.LastLogin = field.NewField(tableName, "last_login")
|
||||||
_user.LastLoginHost = field.NewString(tableName, "last_login_host")
|
_user.LastLoginHost = field.NewString(tableName, "last_login_host")
|
||||||
@@ -71,7 +71,7 @@ type user struct {
|
|||||||
IDType field.Int32 // 认证类型:0-未认证,1-个人认证,2-企业认证
|
IDType field.Int32 // 认证类型:0-未认证,1-个人认证,2-企业认证
|
||||||
IDNo field.String // 身份证号或营业执照号
|
IDNo field.String // 身份证号或营业执照号
|
||||||
IDToken field.String // 身份验证标识
|
IDToken field.String // 身份验证标识
|
||||||
ContactQq field.String // QQ联系方式
|
ContactQQ field.String // QQ联系方式
|
||||||
ContactWechat field.String // 微信联系方式
|
ContactWechat field.String // 微信联系方式
|
||||||
LastLogin field.Field // 最后登录时间
|
LastLogin field.Field // 最后登录时间
|
||||||
LastLoginHost field.String // 最后登录地址
|
LastLoginHost field.String // 最后登录地址
|
||||||
@@ -108,7 +108,7 @@ func (u *user) updateTableName(table string) *user {
|
|||||||
u.IDType = field.NewInt32(table, "id_type")
|
u.IDType = field.NewInt32(table, "id_type")
|
||||||
u.IDNo = field.NewString(table, "id_no")
|
u.IDNo = field.NewString(table, "id_no")
|
||||||
u.IDToken = field.NewString(table, "id_token")
|
u.IDToken = field.NewString(table, "id_token")
|
||||||
u.ContactQq = field.NewString(table, "contact_qq")
|
u.ContactQQ = field.NewString(table, "contact_qq")
|
||||||
u.ContactWechat = field.NewString(table, "contact_wechat")
|
u.ContactWechat = field.NewString(table, "contact_wechat")
|
||||||
u.LastLogin = field.NewField(table, "last_login")
|
u.LastLogin = field.NewField(table, "last_login")
|
||||||
u.LastLoginHost = field.NewString(table, "last_login_host")
|
u.LastLoginHost = field.NewString(table, "last_login_host")
|
||||||
@@ -146,7 +146,7 @@ func (u *user) fillFieldMap() {
|
|||||||
u.fieldMap["id_type"] = u.IDType
|
u.fieldMap["id_type"] = u.IDType
|
||||||
u.fieldMap["id_no"] = u.IDNo
|
u.fieldMap["id_no"] = u.IDNo
|
||||||
u.fieldMap["id_token"] = u.IDToken
|
u.fieldMap["id_token"] = u.IDToken
|
||||||
u.fieldMap["contact_qq"] = u.ContactQq
|
u.fieldMap["contact_qq"] = u.ContactQQ
|
||||||
u.fieldMap["contact_wechat"] = u.ContactWechat
|
u.fieldMap["contact_wechat"] = u.ContactWechat
|
||||||
u.fieldMap["last_login"] = u.LastLogin
|
u.fieldMap["last_login"] = u.LastLogin
|
||||||
u.fieldMap["last_login_host"] = u.LastLoginHost
|
u.fieldMap["last_login_host"] = u.LastLoginHost
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ func ApplyRouters(app *fiber.App) {
|
|||||||
|
|
||||||
// 用户
|
// 用户
|
||||||
user := api.Group("/user")
|
user := api.Group("/user")
|
||||||
|
user.Post("/update", handlers.UpdateUser)
|
||||||
|
user.Post("/update/account", handlers.UpdateAccount)
|
||||||
|
user.Post("/update/password", handlers.UpdatePassword)
|
||||||
user.Post("/identify", handlers.Identify)
|
user.Post("/identify", handlers.Identify)
|
||||||
user.Post("/identify/callback", handlers.IdentifyCallback)
|
user.Post("/identify/callback", handlers.IdentifyCallback)
|
||||||
user.Post("/recharge/prepare/alipay", handlers.RechargePrepareAlipay)
|
user.Post("/recharge/prepare/alipay", handlers.RechargePrepareAlipay)
|
||||||
|
|||||||
Reference in New Issue
Block a user