You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
5.6 KiB
191 lines
5.6 KiB
package user
|
|
|
|
import (
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/log"
|
|
"yunyan/pb"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// @Summary 扫激活码领取奖励
|
|
// @Description 新设计:激活码 = 厂家公码。每个用户一生仅可领取 1 次(DBUser.Isactivatecode 判重)
|
|
// @Tags User
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param user body pb.UserBindAuthCodeReq true "扫激活码请求"
|
|
// @Success 200 {object} comm.HttpResult{data=pb.UserBindAuthCodeResp} "成功返回"
|
|
// @Router /api/home/user_bindauthcode [post]
|
|
func (this *apiComp) BindAuthCode(session comm.IUserSession, req *pb.UserBindAuthCodeReq) (resp *pb.UserBindAuthCodeResp, errdata *pb.ErrorData) {
|
|
var (
|
|
user *pb.DBUser
|
|
code *pb.DBFactoryPublicCode
|
|
err error
|
|
now = time.Now().Unix()
|
|
codeStr = strings.ToUpper(strings.TrimSpace(req.Code))
|
|
affected int64
|
|
)
|
|
|
|
if codeStr == "" {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_ReqParameterError,
|
|
Message: "激活码不能为空",
|
|
}
|
|
return
|
|
}
|
|
|
|
if user, err = this.module.model.findforid(session.GetUserId()); err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_UserSessionNobeing,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
|
|
// 1) 快速判重:user.Isactivatecode 为 true 表示已经领过激活码奖励
|
|
if user.Isactivatecode {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "您已领取过激活码奖励,不可重复领取",
|
|
}
|
|
return
|
|
}
|
|
|
|
// 2) 校验公码
|
|
if code, err = this.module.model.getFactoryPublicCode(codeStr); err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeIllegal,
|
|
Message: "激活码无效",
|
|
}
|
|
return
|
|
}
|
|
if code.Status != 0 {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "激活码已停用",
|
|
}
|
|
return
|
|
}
|
|
if code.Expiretime > 0 && code.Expiretime < now {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "激活码已过期",
|
|
}
|
|
return
|
|
}
|
|
if code.Maxuses > 0 && code.Useduses >= code.Maxuses {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "激活码已达核销上限",
|
|
}
|
|
return
|
|
}
|
|
|
|
// 3) 原子置位 user.isactivatecode → 防同账号并发扫码兜底,输的请求 affected=0
|
|
if affected, err = this.module.model.tryMarkActivateCode(user.Uid); err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
if affected == 0 {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "您已领取过激活码奖励,不可重复领取",
|
|
}
|
|
return
|
|
}
|
|
user.Isactivatecode = true
|
|
|
|
// 4) 原子扣减公码名额(CAS 防超发);失败需回滚 user.isactivatecode 以便用户换码重试
|
|
if affected, err = this.module.model.incrFactoryPublicCodeUsed(codeStr); err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
if affected == 0 {
|
|
// 公码已达上限:回滚 user.isactivatecode,让用户换码重试
|
|
if rbAff, rbErr := this.module.model.rollbackActivateCode(user.Uid); rbErr != nil || rbAff == 0 {
|
|
this.module.Error("BindAuthCode rollback isactivatecode fail",
|
|
log.Field{Key: "uid", Value: user.Uid},
|
|
log.Field{Key: "rbAff", Value: rbAff})
|
|
}
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_AuthorizeNoCanUse,
|
|
Message: "激活码已达核销上限",
|
|
}
|
|
return
|
|
}
|
|
|
|
// 5) 计算奖励(单位:vip=天,translate/meeting=分钟;存储统一按秒)
|
|
addVipDay := int64(code.Bindrewardvpitime)
|
|
addTradeSec := int64(code.Bindrewardtranslate) * 60
|
|
addMeetSec := int64(code.Bindrewardmeeting) * 60
|
|
if addVipDay > 0 {
|
|
addVipSec := addVipDay * 24 * 60 * 60
|
|
if user.Vipexptime == 0 || user.Vipexptime < now {
|
|
user.Vipexptime = now + addVipSec
|
|
} else {
|
|
user.Vipexptime += addVipSec
|
|
}
|
|
}
|
|
if addTradeSec > 0 {
|
|
user.Tradeintegral += addTradeSec
|
|
user.Tradetotalintegral += addTradeSec
|
|
}
|
|
if addMeetSec > 0 {
|
|
user.Meetintegral += addMeetSec
|
|
user.Meettotalintegral += addMeetSec
|
|
}
|
|
|
|
// 6) 保存用户余额(isactivatecode 已经在步骤 3 提前落库,这里 Save 会覆盖一并写回,是幂等的)
|
|
if err = this.module.model.save(user); err != nil {
|
|
errdata = &pb.ErrorData{
|
|
Code: pb.ErrorCode_DBError,
|
|
Message: err.Error(),
|
|
}
|
|
return
|
|
}
|
|
|
|
if addVipDay > 0 || addTradeSec > 0 || addMeetSec > 0 {
|
|
_ = this.module.model.addIntegralLog(&pb.DBUserUseLog{
|
|
Uid: user.Uid,
|
|
Logtype: pb.UserLogType_ActivityReward,
|
|
Ts: now,
|
|
Addvipday: addVipDay,
|
|
Addtradesecond: addTradeSec,
|
|
Addmeetsecond: addMeetSec,
|
|
Extra: code.Code,
|
|
})
|
|
}
|
|
|
|
// 埋点:二维码(公码)激活成功。只统计到 应用×区域(ProductId 留 0,不落地到产品),与用户口径一致。
|
|
if this.module.analyze != nil {
|
|
this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventQRCodeActive, Uid: user.Uid})
|
|
}
|
|
|
|
this.module.Debug("BindAuthCode",
|
|
log.Field{Key: "uid", Value: user.Uid},
|
|
log.Field{Key: "code", Value: code.Code},
|
|
log.Field{Key: "factoryid", Value: code.Factoryid})
|
|
|
|
resp = &pb.UserBindAuthCodeResp{
|
|
Isgiveaway: addVipDay > 0 || addTradeSec > 0 || addMeetSec > 0,
|
|
Viplv: user.Viplv,
|
|
Vipexptime: user.Vipexptime,
|
|
Tradeintegral: user.Tradeintegral,
|
|
Tradetotalintegral: user.Tradetotalintegral,
|
|
Meetintegral: user.Meetintegral,
|
|
Meettotalintegral: user.Meettotalintegral,
|
|
Addvipday: addVipDay,
|
|
Addtradesecond: addTradeSec,
|
|
Addmeetsecond: addMeetSec,
|
|
Factoryid: code.Factoryid,
|
|
Productid: code.Productid,
|
|
}
|
|
return
|
|
}
|
|
|