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.
179 lines
5.6 KiB
179 lines
5.6 KiB
package api
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/lego/sys/log"
|
|
"yunyan/pb"
|
|
"yunyan/utils/license"
|
|
"yunyan/utils/mac"
|
|
)
|
|
|
|
// CreateAuthCodes 创建授权码/设备码(一次生产 = 一个批次)。
|
|
//
|
|
// 序号采用「产品级全局游标」:每次生产原子地从 product.serialcursor 上分配一段连续区间
|
|
// [start, end],不再随批次重置,也不再依赖厂家维度的 probatch 计数器。
|
|
// 每次生产落一条 production_batch 台账(记录序号区间/MAC区间/数量),
|
|
// 再把该批次的全部授权码写入 license_<productid十六进制> 分表。
|
|
func (this *apiComp) CreateAuthCodes(session comm.IUserSession, req *pb.ApiCreateAuthCodesReq) (resp *pb.ApiCreateAuthCodesResp, errdata *pb.ErrorData) {
|
|
var (
|
|
err error
|
|
product *pb.DBProduct
|
|
now = time.Now().Unix()
|
|
pid = req.Productid
|
|
)
|
|
|
|
// 校验品牌商存在
|
|
if _, err = this.module.model.factory(req.Brandid); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()}
|
|
return
|
|
}
|
|
// 渠道商必选:归属随批次写进每条授权码,用户绑定该设备后统计与结算都按它归属。
|
|
channel, errdata := this.resolveProductionChannel(req.Channelid, req.Brandid)
|
|
if errdata != nil {
|
|
return
|
|
}
|
|
if product, err = this.module.model.product(pid); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()}
|
|
return
|
|
}
|
|
|
|
// 每副产品需要生产的设备码份数(0或1视为单份)
|
|
codeCount := product.Devicecodecount
|
|
if codeCount == 0 {
|
|
codeCount = 1
|
|
}
|
|
isClassicBT := product.Devicetype == pb.DeviceType_Classic_bluetooth_Headset
|
|
|
|
// 计算本次生产的授权码总数
|
|
var (
|
|
total int64
|
|
macaddresses []string
|
|
)
|
|
switch req.Createtype {
|
|
case 1: // 生产模式:数量 * 每副份数
|
|
total = int64(req.Number) * int64(codeCount)
|
|
case 2: // 导入模式:按逗号拆分的 MAC 份数
|
|
macaddresses = strings.Split(req.Macaddresses, ",")
|
|
total = int64(len(macaddresses))
|
|
default:
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_ReqParameterError, Message: "createtype 仅支持 1(生产) 或 2(导入)"}
|
|
return
|
|
}
|
|
if total <= 0 {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_ReqParameterError, Message: "生成数量必须大于0"}
|
|
return
|
|
}
|
|
|
|
// 原子分配产品级序号区间 [start, end]
|
|
end, err := this.module.model.allocSerialRange(pid, total)
|
|
if err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()}
|
|
this.module.Error("allocSerialRange Fail!", log.Field{Key: "err", Value: err.Error()})
|
|
return
|
|
}
|
|
start := end - total + 1
|
|
|
|
models := make([]*pb.DBAuthCode, 0, total)
|
|
if req.Createtype == 1 {
|
|
for i := int64(0); i < total; i++ {
|
|
serial := start + i
|
|
var code string
|
|
if code, err = license.GenerateV2(uint16(pid), uint32(serial)); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_SystemError, Message: err.Error()}
|
|
return
|
|
}
|
|
device := &pb.DBAuthCode{
|
|
Code: code,
|
|
Codetype: pb.Codetype_AUTHCODE,
|
|
Brandid: req.Brandid,
|
|
Channelid: channel.Id,
|
|
Productid: pid,
|
|
Number: serial,
|
|
Createtime: now,
|
|
}
|
|
if isClassicBT { // 经典蓝牙耳机:生成设备码 + MAC
|
|
device.Codetype = pb.Codetype_DEVICECODE
|
|
device.Devicemac = mac.Generate(uint16(pid), uint32(serial))
|
|
}
|
|
models = append(models, device)
|
|
}
|
|
} else { // 导入模式
|
|
for i := int64(0); i < total; i++ {
|
|
serial := start + i
|
|
var code string
|
|
if code, err = license.GenerateV2(uint16(pid), uint32(serial)); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_SystemError, Message: err.Error()}
|
|
return
|
|
}
|
|
models = append(models, &pb.DBAuthCode{
|
|
Code: code,
|
|
Codetype: pb.Codetype_DEVICECODE,
|
|
Brandid: req.Brandid,
|
|
Channelid: channel.Id,
|
|
Productid: pid,
|
|
Number: serial,
|
|
Createtime: now,
|
|
Devicemac: strings.TrimSpace(macaddresses[i]),
|
|
})
|
|
}
|
|
}
|
|
|
|
// 经典蓝牙时批次记录 MAC 区间,否则留空
|
|
var startmac, endmac string
|
|
if isClassicBT {
|
|
startmac = mac.Generate(uint16(pid), uint32(start))
|
|
endmac = mac.Generate(uint16(pid), uint32(end))
|
|
}
|
|
|
|
// 先落批次台账。批次号 = 6 位可读短码(batchno 唯一索引兜底防碰撞),授权码用它关联批次。
|
|
// 优先用预览确认流程回传的 req.Batchno(保证二次确认展示的号 = 实际生产的号),未传则现生成。
|
|
batchno := strings.TrimSpace(req.Batchno)
|
|
if batchno == "" {
|
|
batchno, _ = comm.GeneratePublicCode()
|
|
}
|
|
batch := &pb.DBProductionBatch{
|
|
Ts: now,
|
|
Brandid: req.Brandid,
|
|
Channelid: channel.Id,
|
|
Productid: pid,
|
|
Serialstart: start,
|
|
Serialend: end,
|
|
Startmac: startmac,
|
|
Endmac: endmac,
|
|
Devicenum: uint32(total),
|
|
Batchno: batchno,
|
|
Remark: req.Remark,
|
|
}
|
|
if err = this.module.model.addproductionbatch(batch); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()}
|
|
this.module.Error("addproductionbatch Fail!", log.Field{Key: "err", Value: err.Error()})
|
|
return
|
|
}
|
|
|
|
// 回填批次号后批量写入分表
|
|
for _, row := range models {
|
|
row.Batchno = batchno
|
|
}
|
|
if err = this.module.model.addfactoryDevics(pid, models); err != nil {
|
|
errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()}
|
|
this.module.Error("addauthcodes Fail!", log.Field{Key: "err", Value: err.Error()})
|
|
return
|
|
}
|
|
|
|
resp = &pb.ApiCreateAuthCodesResp{
|
|
Brandid: req.Brandid,
|
|
Channelid: channel.Id,
|
|
Productid: pid,
|
|
Batchno: batchno,
|
|
Startmac: startmac,
|
|
Endmac: endmac,
|
|
Serialstart: start,
|
|
Serialend: end,
|
|
Number: uint32(total),
|
|
Devices: models,
|
|
}
|
|
return
|
|
}
|
|
|