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.
 
 
 
 
 
 

282 lines
9.1 KiB

package user
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"yunyan/comm"
"yunyan/lego/sys/log"
"yunyan/pb"
"yunyan/utils/license"
)
// @Summary 绑定耳机设备
// @Description 绑定耳机设备
// @Tags User
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param user body pb.UserBindDeviceReq true "发送绑定请求"
// @Success 200 {object} comm.HttpResult{data=pb.UserBindDeviceResp} "成功返回"
// @Router /api/home/user_binddevice [post]
func (this *apiComp) BindDevice(session comm.IUserSession, req *pb.UserBindDeviceReq) (resp *pb.UserBindDeviceResp, errdata *pb.ErrorData) {
var (
user *pb.DBUser
product *pb.DBProduct
divices []*pb.DBUserDivice
model *pb.DBAuthCode
codestate int32
divice *pb.DBUserDivice
pid uint16
Isgiveaway bool
err error
)
if user, err = this.module.model.findforid(session.GetUserId()); err != nil { //未找到授权码
errdata = &pb.ErrorData{
Code: pb.ErrorCode_UserSessionNobeing,
Message: err.Error(),
}
return
}
if product, err = this.module.cache.GetProduct(req.Pid); err != nil { //未找到授权码
errdata = &pb.ErrorData{
Code: pb.ErrorCode_AuthorizeNoCanUse,
Message: err.Error(),
}
return
}
if divices, err = this.module.model.getDevices(session.GetUserId()); err != nil { //未找到授权码
errdata = &pb.ErrorData{
Code: pb.ErrorCode_AuthorizeNoCanUse,
Message: err.Error(),
}
return
}
for _, v := range divices {
if v.Devicename == req.Devicename && v.Devicemac == req.Devicemac && v.License == req.Code {
// errdata = &pb.ErrorData{
// Code: pb.ErrorCode_DeviceBinded,
// Message: "设备已绑定",
// }
divice = v
resp = &pb.UserBindDeviceResp{
Device: divice,
}
return
}
}
if product.Devicetype == pb.DeviceType_Classic_bluetooth_Headset {
if model, err = this.module.model.getFactoryDeviceformac(product.Id, req.Devicemac); err != nil { //设备已被注册
errdata = &pb.ErrorData{
Code: pb.ErrorCode_AuthorizeNoCanUse,
Message: err.Error(),
}
return
}
} else {
pid, err = license.ProductID(req.Code)
if err != nil { //老得一批License 由于数据和新版对因不上 会产生错误
// errdata = &pb.ErrorData{
// Code: pb.ErrorCode_AuthorizeIllegal,
// Message: err.Error(),
// }
err = nil
pid = uint16(45058)
// return
}
if model, err = this.module.model.getFactoryDevice(uint32(pid), req.Code); err != nil { //设备已被注册
errdata = &pb.ErrorData{
Code: pb.ErrorCode_AuthorizeNoCanUse,
Message: err.Error(),
}
return
}
}
if model.Disabled != 0 { //设备码已被后台禁用 直接拒绝绑定
errdata = &pb.ErrorData{
Code: pb.ErrorCode_AuthorizeNoCanUse,
Message: "设备码已被禁用",
}
return
}
codestate = model.Status
if model.Status != 0 && model.Uid != session.GetUserId() { //授权码已被使用 自动解绑
if divices, err = this.module.model.getDevices(model.Uid); err == nil {
for _, v := range divices {
if v.License == model.Code {
this.module.model.delDevice(v.Id) // 删除设备
// 接管即原用户被动解绑:按原设备自身的产品/渠道冲正 -1,
// 否则同一台设备每换一次账号就多计一次绑定。
// 不带 Uid:原用户并未操作,不应被计入当日活跃用户。
if this.module.analyze != nil {
this.module.analyze.Report(&comm.StatEvent{
Type: comm.StatEventDeviceBind,
ProductId: uint32(v.Productid),
ChannelId: v.Channelid,
Count: -1,
})
}
}
}
}
// errdata = &pb.ErrorData{
// Code: pb.ErrorCode_AuthorizeNoCanUse,
// Message: "授权码已被使用",
// }
// return
}
// 移动渠道产品生成 CMEI: TUI(8位) + batch(2位) + serial(5位) = 15位
var cmei string
if product.Channel == "mobile" {
var tui string
if product.Channelextra != "" {
extra := struct {
Tui string `json:"tui"`
}{}
if jerr := json.Unmarshal([]byte(product.Channelextra), &extra); jerr == nil {
tui = extra.Tui
}
}
macStr := strings.ReplaceAll(req.Devicemac, ":", "")
if len(tui) == 8 && len(macStr) == 12 {
batch, _ := strconv.ParseUint(macStr[4:6], 16, 8)
serial, _ := strconv.ParseUint(macStr[8:12], 16, 16)
cmei = fmt.Sprintf("%s%02d%05d", tui, batch%100, serial%100000)
}
}
divice = &pb.DBUserDivice{
Uid: session.GetUserId(),
Productid: product.Id,
// 渠道商归属来自授权码(生产时定下),绑定即固化到用户设备上,
// 后续该用户的充值/消耗埋点都按它归到对应渠道商名下(统计与月结算的依据)。
Channelid: model.Channelid,
Devicetype: product.Devicetype,
Devicename: req.Devicename,
Devicemac: req.Devicemac,
License: req.Code,
Cmei: cmei,
Version: req.Version,
}
model.Status = 1 //已绑定
model.Uid = session.GetUserId()
model.Devicemac = req.Devicemac
// 首次激活(此前 status==0)记录激活时间:供按统计日聚合设备激活/绑定数。
// 历史设备漏记 usedtime 是失误,从此补上;仅在 0→1 首激时写,换绑/接管不覆盖原始激活日。
if codestate == 0 {
model.Usedtime = time.Now().Unix()
}
if err = this.module.model.saveFactoryDevice(model.Productid, model); err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_DBError,
Message: err.Error(),
}
return
}
if err = this.module.model.addDevice(divice); err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_DBError,
Message: err.Error(),
}
return
}
// 记录最后绑定的产品ID与渠道商:后续充值/消耗埋点按它们归属统计与结算
user.Lastbindproductid = product.Id
user.Lastbindchannelid = model.Channelid
userSaved := false
// 设备激活 + 绑定埋点。
// 激活是「设备一生一次」的事件:只在 codestate==0 首激时记,换绑/接管不再计——
// 与上面 model.Usedtime 只在 0→1 时写保持同一口径,否则激活数被换绑次数放大。
// 绑定是净增流量:这里 +1,解绑/被接管处 -1(见 api_unbinddevice.go 与上面自动解绑分支)。
if this.module.analyze != nil {
if codestate == 0 {
this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventDeviceActive, Uid: session.GetUserId(), ProductId: uint32(product.Id), ChannelId: model.Channelid})
}
this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventDeviceBind, Uid: session.GetUserId(), ProductId: uint32(product.Id), ChannelId: model.Channelid})
}
this.module.Debug("BindDevice",
log.Field{Key: "Isgiveaway", Value: user.Isgiveaway},
log.Field{Key: "Iscanbindagain", Value: product.Iscanbindagain},
log.Field{Key: "codestate", Value: codestate})
if !user.Isgiveaway || codestate == 0 {
Isgiveaway = true
user.Isgiveaway = true
var addVipDay, addTrade, addMeet int64
if product.Bindrewardvpitime > 0 {
addVipDay = 90
if user.Vipexptime == 0 || user.Vipexptime < time.Now().Unix() { //重置
user.Vipexptime = time.Now().Unix() + 90*24*60*60
} else { //累加
user.Vipexptime = user.Vipexptime + 90*24*60*60
}
}
// translate/meeting 配置为分钟,积分按秒存储
if product.Bindrewardtranslate > 0 {
addTrade = int64(product.Bindrewardtranslate) * 60
user.Tradeintegral += addTrade
user.Tradetotalintegral += addTrade
}
if product.Bindrewardmeeting > 0 {
addMeet = int64(product.Bindrewardmeeting) * 60
user.Meetintegral += addMeet
user.Meettotalintegral += addMeet
}
err = this.module.model.save(user)
if err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_DBError,
Message: err.Error(),
}
return
}
userSaved = true
// 记录绑定设备奖励流水
if addVipDay > 0 || addTrade > 0 || addMeet > 0 {
_ = this.module.model.addIntegralLog(&pb.DBUserUseLog{
Uid: session.GetUserId(),
Logtype: pb.UserLogType_ActivityReward,
Ts: time.Now().Unix(),
Addvipday: addVipDay,
Addtradesecond: addTrade,
Addmeetsecond: addMeet,
Extra: req.Code, // 绑定时使用的设备/授权码
})
// 绑定赠送资源埋点
if this.module.analyze != nil {
this.module.analyze.Report(&comm.StatEvent{
Type: comm.StatEventResourceGrant,
ProductId: product.Id,
ChannelId: model.Channelid,
Uid: session.GetUserId(),
GrantVipDay: addVipDay,
GrantTradeSecond: addTrade,
GrantMeetSecond: addMeet,
})
}
}
}
// 首绑送资源分支未触发时,仍需持久化 lastbindproductid / lastbindchannelid
if !userSaved {
if err = this.module.model.save(user); err != nil {
errdata = &pb.ErrorData{
Code: pb.ErrorCode_DBError,
Message: err.Error(),
}
return
}
}
resp = &pb.UserBindDeviceResp{
Device: divice,
Isgiveaway: Isgiveaway,
Viplv: user.Viplv,
Vipexptime: user.Vipexptime,
Tradeintegral: user.Tradeintegral,
Tradetotalintegral: user.Tradetotalintegral,
Meetintegral: user.Meetintegral,
Meettotalintegral: user.Meettotalintegral,
}
return
}