const logger = require('../utils/log') interface CDKInfo { itenList : string, // 物品列表 batch : number, // 当前批次CDK描述 serverList: string // 服务器ID列表 cnt : number } interface CDKData { batch : string, // json字符串 code : string, // json字符串 } let CDKCache:Map = new Map() const code:string[] = ["0","c","e","u","m", "k","d","7","x","f", "9","j","w","6","8", "t","1","h","4","p", "y","3","2","b","5","n"] const codeMap:Map = new Map([ ['0',0],["c",1],["e", 2],["u", 3],["m",4], ["k", 5],["d",6],["7", 7],["x", 8],["f", 9], ["9", 10],["j",11],["w",12],["6",13],["8",14], ["t",15],["1",16],["h",17],["4",18],["p",19], ["y",20],["3",21],["2",22],["b",23],["5",24],["n",25] ]) const unit = 20 const bit = unit const bit2 = unit * unit const maxCDKRand = 25 const redis_batch_incr = "cdk_batch" const redis_cdk_batch = "cdk_" // cdk批次信息 const redis_cdk = "hcdk_" // 批次对应cdk hset function transNumber(num:number):[number,number,number]{ let first = 0 let second = 0 if (num >= bit2){ first = Math.floor(num / bit2) } if (num >= bit){ let t = num - (first * bit2) second = Math.floor(t / bit) } let third = num - (first * bit2) - (second * bit) return [first,second,third] } function calcNumber(first:number,second:number,third:number) { if (first === undefined || second === undefined || third === undefined) { return -1 } if (first > unit || second > unit || third > unit) { return -1 } return first * bit2 + second * bit + third } function generateCDK(batch:number , idx:number) { let ret = "" for (let i = 0; i <= 1;i++) { let r = (Math.floor(maxCDKRand * Math.random()) % 20) + 2 // 保证数字在2 - 22之间错在首字母0 ret = ret + code[r] } let [idx_f,idx_s,idx_t] = transNumber(idx) ret = ret + code[idx_t] + code[idx_s] + code[idx_f] // 321 let [batch_f,batch_s,batch_t] = transNumber(batch) // 是否需要检测是否超标 ret = ret + code[batch_s] + code[batch_t] + code[batch_f] // 231 for (let i = 0; i <= 1;i++) { let r = Math.floor(maxCDKRand * Math.random()) ret = ret + code[r] } return ret } function decodeCDK(code:string):[boolean,number,number] { if (code.length != 10) { return [false,0,0] } let idx = calcNumber(codeMap.get(code[4]),codeMap.get(code[3]),codeMap.get(code[2])) if (idx < 0 ) { return [false,0,0] } let batch = calcNumber(codeMap.get(code[7]),codeMap.get(code[5]),codeMap.get(code[6])) if (batch < 0) { return [false,0,0] } return [true,batch,idx] } class CDK { async genCDK (ctx,cnt:number,useCnt:number,serverList:string,itemList:string) { const redisClient = ctx.redis.client; let batch = await redisClient.incr(redis_batch_incr) let batch_info_key = redis_cdk_batch + batch let data : CDKInfo = { itenList:itemList, serverList:serverList, batch: batch, cnt:useCnt } // 将批次数据存入redis await redisClient.set(batch_info_key,JSON.stringify(data)) const pipeline = redisClient.pipeline(); const cdk_hset_key = redis_cdk + batch // 生成cdk代码 for (let i = 1; i <= cnt;i++) { let cdk = generateCDK(batch,i) pipeline.hset(cdk_hset_key,cdk,1) } // 执行管道代码 return await pipeline.exec() } async getCDK(ctx,batch:number){ const redisClient = ctx.redis.client; let curBatch = await redisClient.get(redis_batch_incr) if (Number(curBatch) < batch) { return "" } let batch_info_key = redis_cdk_batch + batch let batch_info = await redisClient.get(batch_info_key) const cdk_hset_key = redis_cdk + batch let ret = await redisClient.hgetall(cdk_hset_key) /*let cdkList = ret.reduce((acc:any,value,index) => { if (index % 2 == 1) { acc.push(acc,value) } return acc })*/ let cdkdata : CDKData = { batch:batch_info, code: ret } return JSON.stringify(cdkdata) } async getCDKItemList(ctx,code:string) { logger.info(`收到cdk is :${code}`) const redisClient = ctx.redis.client let [ok,batch,index] = decodeCDK(code) if (!ok) { return "" } let batch_info_key = redis_cdk_batch + batch let batch_info_ret = await redisClient.get(batch_info_key) if (batch_info_ret == undefined || batch_info_ret == null ) { return "" } return batch_info_ret } async useCDK(ctx,code:string) { logger.info(` checkCDK 收到cdk is :${code}`) let [ok,batch,index] = decodeCDK(code) if (!ok) { return "invalid code" } const redisClient = ctx.redis.client; let cdk_hset_key = redis_cdk + batch // 必须保持原子性,只能用eval let evalStr = ` local cdk = redis.call('hget',KEYS[1],KEYS[2]) if not cdk then return -1 end redis.call('hdel',KEYS[1],KEYS[2]) return 0 ` let ret = await redisClient.eval(evalStr,2,cdk_hset_key,code) if (ret == -1) { return 'cdk not found' } return "success" } async checkCDK(ctx,code:string,serverId : number) { logger.info(` checkCDK 收到cdk is :${code}`) let [ok,batch,index] = decodeCDK(code) if (!ok) { return "invalid code" } let time = CDKCache.get(code) if (time != undefined && time > Date.now()) { return "invalid code" } const redisClient = ctx.redis.client; // 先判断是否满足batch let batch_info_key = redis_cdk_batch + batch let batch_info_ret = await redisClient.get(batch_info_key) if (batch_info_ret == undefined || batch_info_ret == null ) { return "batch not found" } let batch_info = JSON.parse(batch_info_ret) as CDKInfo let jsonret = JSON.parse(batch_info.serverList) let serverList = jsonret as number[] let isServer = false for(let i = 0;i < serverList.length;i++) { if (serverList[i] == serverId) { isServer = true break } } if(!isServer) { return "serverId error" } let cdk_hset_key = redis_cdk + batch // 必须保持原子性,只能用eval let evalStr = ` local cdk = redis.call('hget',KEYS[1],KEYS[2]) if not cdk then return -1 end return 0 ` let ret = await redisClient.eval(evalStr,2,cdk_hset_key,code) if (ret == -1) { return 'cdk not found' } CDKCache.set(code,Date.now() + 2000) return "success" } } module.exports = new CDK()