diff --git a/api/consts/consts.go b/api/consts/consts.go index 28c9fceb..df86307b 100644 --- a/api/consts/consts.go +++ b/api/consts/consts.go @@ -23,14 +23,15 @@ const ( // apollo_config const ( - MaxPswdWrongTimesKey = "max_pswd_wrong_times" - MaxVeriCodeValidDurationKey = "max_veri_code_valid_duration" - IosKey = "ios" - AndroidKey = "android" - AccountInitKey = "account_init" - TagNumKey = "tag_num" - PlatformNumKey = "platform_num" - SupportWxIdNumKey = "support_wx_id_num" + MaxPswdWrongTimesKey = "max_pswd_wrong_times" + MaxVeriCodeValidDurationKey = "max_veri_code_valid_duration" + IosKey = "ios" + AndroidKey = "android" + AccountInitKey = "account_init" + TagNumKey = "tag_num" + PlatformNumKey = "platform_num" + SupportWxIdNumKey = "support_wx_id_num" + MaxDailyVeriCodeSendTimesKey = "max_daily_veri_code_send_times" ) // del_flag diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index 6561133f..93b8a398 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -126,6 +126,10 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeContactCustomerServiceSessionSrvFail: "联系客服对话表服务错误", ErrCodeContactCustomerServiceSessionNotExist: "联系客服对话表不存在", + + ErrCodeVeriCodeSendTimesSrvFail: "验证码频次表服务错误", + ErrCodeVeriCodeSendTimesNotExist: "验证码频次表不存在", + ErrCodeVeriCodeSendTimesReachedDailyUpperbound: "验证码发送次数已达每日上限", } const ( @@ -300,6 +304,12 @@ const ( ErrCodeContactCustomerServiceSessionSrvFail ErrCode = -24001 // 联系客服对话表服务错误 ErrCodeContactCustomerServiceSessionNotExist ErrCode = -24002 // 联系客服对话表不存在 + // VeriCodeSendTimes: 25xxx + ErrCodeVeriCodeSendTimesSrvOk ErrCode = ErrCodeOk + ErrCodeVeriCodeSendTimesSrvFail ErrCode = -25001 // 验证码频次表服务错误 + ErrCodeVeriCodeSendTimesNotExist ErrCode = -25002 // 验证码频次表不存在 + ErrCodeVeriCodeSendTimesReachedDailyUpperbound ErrCode = -25003 // 验证码发送次数已达每日上限 + // Media: 60xxx ErrCodeMediaSrvOk ErrCode = ErrCodeOk ErrCodeMediaSrvFail ErrCode = -60001 // 媒体服务错误 diff --git a/app/mix/controller/veriCode_op.go b/app/mix/controller/veriCode_op.go index e8823b3b..c1005ea3 100644 --- a/app/mix/controller/veriCode_op.go +++ b/app/mix/controller/veriCode_op.go @@ -15,7 +15,7 @@ func OpSendVeriCode(ctx *gin.Context) { ec := service.DefaultService.OpSendVeriCode(ctx, req) if ec != errcode.ErrCodeLoginSrvOk { logger.Error("OpSendVeriCode fail, req: %v, ec: %v", util.ToJson(req), ec) - ReplyErrorMsg(ctx, "server error") + ReplyErrCodeMsg(ctx, ec) return } diff --git a/app/mix/controller/vericode_api.go b/app/mix/controller/vericode_api.go index a647f62b..c75a5a74 100644 --- a/app/mix/controller/vericode_api.go +++ b/app/mix/controller/vericode_api.go @@ -15,7 +15,7 @@ func ApiSendVeriCode(ctx *gin.Context) { ec := service.DefaultService.ApiSendVeriCode(ctx, req) if ec != errcode.ErrCodeLoginSrvOk { logger.Error("ApiSendVeriCode fail, req: %v, ec: %v", util.ToJson(req), ec) - ReplyErrorMsg(ctx, "server error") + ReplyErrCodeMsg(ctx, ec) return } diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index 126ae617..0a96f864 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -86,8 +86,9 @@ const ( DBAccount = "account" COLAccount = "account" - DBVeriCode = "vericode" - COLVeriCode = "vericode" + DBVeriCode = "vericode" + COLVeriCode = "vericode" + COLVeriCodeSendTimes = "vericode_send_times" DBMoment = "moment" COLMoment = "moment" @@ -214,6 +215,11 @@ func (m *Mongo) getColVeriCode() *qmgo.Collection { return m.clientMix.Database(DBVeriCode).Collection(COLVeriCode) } +// VeriCode频次表 +func (m *Mongo) getColVeriCodeSendTimes() *qmgo.Collection { + return m.clientMix.Database(DBVeriCode).Collection(COLVeriCodeSendTimes) +} + // 动态表 func (m *Mongo) getColMoment() *qmgo.Collection { return m.clientMix.Database(DBMoment).Collection(COLMoment) @@ -1020,6 +1026,32 @@ func (m *Mongo) GetVeriCodeListByPhoneHash(ctx *gin.Context, req *vericodeproto. return vericode, err } +// 验证码频次 +func (m *Mongo) GetAndUpdateVeriCodeSendTimes(ctx *gin.Context, deviceId string) (veriCodeSendTimes *dbstruct.VeriCodeSendTimes, err error) { + col := m.getColVeriCodeSendTimes() + + change := qmgo.Change{ + Update: qmgo.M{"$inc": qmgo.M{"send_times": 1}}, + Upsert: true, + ReturnNew: false, + } + + veriCodeSendTimesInstance := dbstruct.VeriCodeSendTimes{} + if err = col.Find(ctx, qmgo.M{"_id": deviceId}).Apply(change, &veriCodeSendTimesInstance); err != nil { + logger.Error("change error : %v", err) + return + } + + return &veriCodeSendTimesInstance, err +} + +// 清空验证码频次表 +func (m *Mongo) ClearVeriCodeSendTimes(ctx *gin.Context) error { + col := m.getColVeriCodeSendTimes() + _, err := col.RemoveAll(ctx, qmgo.M{}) + return err +} + // 动态相关 func (m *Mongo) CreateMoment(ctx *gin.Context, moment *dbstruct.Moment) error { col := m.getColMoment() diff --git a/app/mix/service/apiservice.go b/app/mix/service/apiservice.go index e71ee1b8..03bd4209 100644 --- a/app/mix/service/apiservice.go +++ b/app/mix/service/apiservice.go @@ -37,7 +37,13 @@ import ( // 发送验证码 func (s *Service) ApiSendVeriCode(ctx *gin.Context, req *vericodeproto.ApiSendReq) (ec errcode.ErrCode) { - ec = errcode.ErrCodeResourceSrvOk + ec = errcode.ErrCodeLoginSrvOk + + ec = s.ApiSendVeriCodeBusinessValidate(ctx, req) + if ec != errcode.ErrCodeLoginSrvOk { + return + } + err := _DefaultVeriCode.OpSendVeriCode(ctx, &vericodeproto.OpSendReq{ MobilePhone: req.MobilePhone, RegionCode: req.RegionCode, diff --git a/app/mix/service/apiservice_business_validation.go b/app/mix/service/apiservice_business_validation.go index 6e315d11..75c397a8 100644 --- a/app/mix/service/apiservice_business_validation.go +++ b/app/mix/service/apiservice_business_validation.go @@ -8,6 +8,7 @@ import ( loginproto "service/api/proto/login/proto" streamerproto "service/api/proto/streamer/proto" streamerauthapprovalproto "service/api/proto/streamerauthapproval/proto" + vericodeproto "service/api/proto/vericode/proto" businessvalidator "service/app/mix/service/business_validator" "service/bizcommon/util" "service/dbstruct" @@ -18,6 +19,24 @@ import ( "github.com/gin-gonic/gin" ) +// 发送验证码 +func (s *Service) ApiSendVeriCodeBusinessValidate(ctx *gin.Context, req *vericodeproto.ApiSendReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeLoginSrvOk + + resultList := businessvalidator.NewLoginBusinessValidator(ctx, req). + QueryVeriCodeSendTimes(_DefaultVeriCodeSendTimes.OpGetAndUpdate, req.Did). + EnsureVeriCodeSendTimesNotReachedDailyUpperbound(). + Validate(). + Collect() + ec, _ = resultList[0].(errcode.ErrCode) + if ec != errcode.ErrCodeLoginSrvOk { + logger.Error("ApiSendVeriCodeBusinessValidate business validation failed!") + return + } + + return +} + // 密码登录 func (s *Service) ApiLoginByPswdBusinessValidate(ctx *gin.Context, req *loginproto.ApiLoginByPswdReq) (login *dbstruct.Login, account *dbstruct.Account, ec errcode.ErrCode) { ec = errcode.ErrCodeLoginSrvOk diff --git a/app/mix/service/business_validator/login.go b/app/mix/service/business_validator/login.go index e549689a..2ba774ef 100644 --- a/app/mix/service/business_validator/login.go +++ b/app/mix/service/business_validator/login.go @@ -25,9 +25,10 @@ type LoginBusinessValidator struct { passwordAccessor loginproto.PasswordAccessible //访问密码的接口 // 结果 - login *dbstruct.Login - account *dbstruct.Account - vericode *dbstruct.VeriCode + login *dbstruct.Login + account *dbstruct.Account + vericode *dbstruct.VeriCode + vericodeSendTimes *dbstruct.VeriCodeSendTimes } func NewLoginBusinessValidator(ctx *gin.Context, req any) *LoginBusinessValidator { @@ -242,6 +243,40 @@ func (l *LoginBusinessValidator) EnsureIsOpRole() *LoginBusinessValidator { return l } +func (l *LoginBusinessValidator) QueryVeriCodeSendTimes(fun func(*gin.Context, string) (*dbstruct.VeriCodeSendTimes, error), devId string) *LoginBusinessValidator { + l.oplist = append(l.oplist, func() { + + vericodeSendTimes, err := fun(l.ctx, devId) + if err != nil { + l.ec = errcode.ErrCodeVeriCodeSendTimesSrvFail + return + } + + l.vericodeSendTimes = vericodeSendTimes + }) + return l +} + +func (l *LoginBusinessValidator) EnsureVeriCodeSendTimesNotReachedDailyUpperbound() *LoginBusinessValidator { + l.oplist = append(l.oplist, func() { + + // 读取每日发送上限 + maxDailyVeriCodeSendTimes, err := apollo.GetIntValue(consts.MaxDailyVeriCodeSendTimesKey, apollo.ApolloOpts().SetNamespace("application")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + l.ec = errcode.ErrCodeApolloReadFail + return + } + + if l.vericodeSendTimes.SendTimes > int64(maxDailyVeriCodeSendTimes) { + logger.Error("the vericode send times of this device has reached its daily upperbound") + l.ec = errcode.ErrCodeVeriCodeSendTimesReachedDailyUpperbound + return + } + }) + return l +} + // 执行校验 func (s *LoginBusinessValidator) Validate() *LoginBusinessValidator { s.BusinessValidateStream.Validate() diff --git a/app/mix/service/cronservice.go b/app/mix/service/cronservice.go index f8dd4764..964e16cc 100644 --- a/app/mix/service/cronservice.go +++ b/app/mix/service/cronservice.go @@ -30,6 +30,7 @@ func (s *CronService) Run() { s.ReloadRecommList() s.ImageAuditBatch() s.TextAuditBatch() + s.ClearVeriCodeSendTimes() } func (s *CronService) ReloadRecommList() { @@ -79,3 +80,18 @@ func (s *CronService) TextAuditBatch() { }) scheduler.StartAsync() } + +func (s *CronService) ClearVeriCodeSendTimes() { + loc, _ := time.LoadLocation("Asia/Shanghai") + scheduler := gocron.NewScheduler(loc) + + scheduler.Every(1).Day().At("00:00").Do(func() { + logger.Info("Clearing vericode_send_times collection...") + ctx := &gin.Context{} + if err := _DefaultVeriCodeSendTimes.OpClear(ctx); err != nil { + logger.Error("Clear vericode_send_times collection fail: %v", err) + } + logger.Info("vericode_send_times collection has been cleared") + }) + scheduler.StartAsync() +} diff --git a/app/mix/service/logic/vericode_send_times.go b/app/mix/service/logic/vericode_send_times.go new file mode 100644 index 00000000..ef73e991 --- /dev/null +++ b/app/mix/service/logic/vericode_send_times.go @@ -0,0 +1,40 @@ +package logic + +import ( + "service/app/mix/dao" + "service/dbstruct" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +type VeriCodeSendTimes struct { + store *dao.Store +} + +func NewVeriCodeSendTimes(store *dao.Store) (a *VeriCodeSendTimes) { + a = &VeriCodeSendTimes{ + store: store, + } + return +} + +func (p *VeriCodeSendTimes) OpGetAndUpdate(ctx *gin.Context, devId string) (*dbstruct.VeriCodeSendTimes, error) { + + vericodeSendTimes, err := p.store.GetAndUpdateVeriCodeSendTimes(ctx, devId) + if err != nil { + logger.Error("GetAndUpdateVeriCodeSendTimes fail, err: %v", err) + return nil, err + } + + return vericodeSendTimes, nil +} + +func (p *VeriCodeSendTimes) OpClear(ctx *gin.Context) error { + err := p.store.ClearVeriCodeSendTimes(ctx) + if err != nil { + logger.Error("ClearVeriCodeSendTimes fail, err: %v", err) + return err + } + return nil +} diff --git a/app/mix/service/opservice_business_validation.go b/app/mix/service/opservice_business_validation.go index a19d14ab..052fe1f4 100644 --- a/app/mix/service/opservice_business_validation.go +++ b/app/mix/service/opservice_business_validation.go @@ -16,6 +16,7 @@ import ( streamerlinkproto "service/api/proto/streamerlink/proto" textaudittaskproto "service/api/proto/textaudittask/proto" userwxaddcheckproto "service/api/proto/userwxaddcheck/proto" + vericodeproto "service/api/proto/vericode/proto" businessvalidator "service/app/mix/service/business_validator" "service/dbstruct" "service/library/logger" @@ -23,6 +24,24 @@ import ( "github.com/gin-gonic/gin" ) +// 发送验证码 +func (s *Service) OpSendVeriCodeBusinessValidate(ctx *gin.Context, req *vericodeproto.OpSendReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeLoginSrvOk + + resultList := businessvalidator.NewLoginBusinessValidator(ctx, req). + QueryVeriCodeSendTimes(_DefaultVeriCodeSendTimes.OpGetAndUpdate, req.Did). + EnsureVeriCodeSendTimesNotReachedDailyUpperbound(). + Validate(). + Collect() + ec, _ = resultList[0].(errcode.ErrCode) + if ec != errcode.ErrCodeLoginSrvOk { + logger.Error("OpSendVeriCodeBusinessValidate business validation failed!") + return + } + + return +} + // 密码登录 func (s *Service) OpLoginByPswdBusinessValidate(ctx *gin.Context, req *loginproto.OpLoginByPswdReq) (login *dbstruct.Login, account *dbstruct.Account, ec errcode.ErrCode) { ec = errcode.ErrCodeLoginSrvOk diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 3abe1f91..196706fa 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -65,6 +65,7 @@ var ( var ( _DefaultToken *logic.Token _DefaultVeriCode *logic.VeriCode + _DefaultVeriCodeSendTimes *logic.VeriCodeSendTimes _DefaultAccount *logic.Account _DefaultProduct *logic.Product _DefaultCatalog *logic.Catalog @@ -129,6 +130,7 @@ func (s *Service) Init(c any) (err error) { _DefaultToken = logic.NewToken(store, cfg.Crypto) _DefaultVeriCode = logic.NewVeriCode(store, cfg.Dysmsapi) + _DefaultVeriCodeSendTimes = logic.NewVeriCodeSendTimes(store) _DefaultAccount = logic.NewAccount(store) _DefaultProduct = logic.NewProduct(store) _DefaultCatalog = logic.NewCatalog(store) @@ -439,6 +441,12 @@ func (s *Service) GetBannerList(ctx *gin.Context) (list []*dbstruct.Banner, ec e // 发送验证码 func (s *Service) OpSendVeriCode(ctx *gin.Context, req *vericodeproto.OpSendReq) (ec errcode.ErrCode) { ec = errcode.ErrCodeResourceSrvOk + + ec = s.OpSendVeriCodeBusinessValidate(ctx, req) + if ec != errcode.ErrCodeLoginSrvOk { + return + } + err := _DefaultVeriCode.OpSendVeriCode(ctx, req) if err != nil { logger.Error("OpSendVeriCode fail, err: %v", err) diff --git a/dbstruct/vericode_send_times.go b/dbstruct/vericode_send_times.go new file mode 100644 index 00000000..b7165e88 --- /dev/null +++ b/dbstruct/vericode_send_times.go @@ -0,0 +1,6 @@ +package dbstruct + +type VeriCodeSendTimes struct { + Id string `json:"id" bson:"_id"` //id,用户的b_did + SendTimes int64 `json:"send_times" bson:"send_times"` //发送次数 +}