package addata import ( "fmt" "math/rand" "miads/adslib/redis_data" "miads/adslib/utils" "strconv" "strings" "time" ) type CustomAdData struct { Duration int64 `json:"duration"` ImageURL string `json:"image_url"` JsOrderID string `json:"js_order_id"` Msg int64 `json:"msg"` OrderName string `json:"order_name"` Result int64 `json:"result"` Target string `json:"target"` TargetAddition []interface{} `json:"target_addition"` UserAgent string `json:"user_agent"` VideoURL string `json:"video_url"` } func CombineOrderBy(adData *AdData, dsp *utils.DspParam) { ads_item, extra_item = yield get_ads_infos(dsp, order_type = 1) if extra_item == None: raise gen.Return(infos) last_target_addition = infos.get('target_addition', []) extra_item_addition = extra_item.get('target_addition', []) if len(extra_item_addition) == 0: raise gen.Return(infos) if len(extra_item_addition) == 1: last_target_addition[0]['urls'].extend(extra_item_addition[0]['urls']) raise gen.Return(infos) else: last_target_addition[0]['urls'].extend(extra_item_addition[0]['urls']) last_target_addition[1]['urls'].extend(extra_item_addition[1]['urls']) infos['target'] = extra_item['target'] infos['js_order_id'] = extra_item['js_order_id'] last_target_addition[1]['type'] = 'CLICK' raise gen.Return(infos) } // 获取一个广告 func getOneAds(dsp *utils.DspParam, orderType int, fixFlag int) (*redis_data.AdOrderInfo, error){ // 取出广告 orders, err := redis_data.GetOrderInfos(dsp, fixFlag) if err != nil { return nil, err } if len(orders) == 0 { return nil, nil } gotOrders := make([]redis_data.AdOrderInfo, 0, 1000) allKpi := int64(0) for _, order := range orders { if order.OrderType == int64(orderType) { gotOrders = append(gotOrders, order) allKpi += order.ShowKpi } } orderRange := make([]int, 0, 1000) curRateIdx := 0 // orderRange中记录的是当前同索引位置的gotOrders里的order的比例上限, 比如如果有两个order [ShowKpi:40, ShowKpi: 60] // 那么orderRange就是 [rate:400, rate: 1000], 所以要按比例取order, 只需要获得在比例上限里随机取一个数, 然后判断落在那个orderRange里, // 对应去gotOrders取同索引里的order即可, 因为算法里有对小于1的比例做补偿, 所以rate上限可能超出1000, rate乘1000而不是100主要是为了 // 如果showKpi差异过大, 对大量小于1的订单做补偿, 会影响整体流量分布, 这里放到1000倍, 降低补偿1的影响, 算法复杂度是O(n), 只有三个非嵌套循环 for _, order := range gotOrders { rate := int(float32(order.ShowKpi) / float32(allKpi) * 1000) // 防止比例过小, 取int后变为0 if rate < 1 { rate = 1 } curRateIdx = curRateIdx + rate orderRange = append(orderRange, curRateIdx) } randNum := rand.Intn(curRateIdx) for i, rateRange := range orderRange { if randNum <= rateRange { return &gotOrders[i], nil } } return nil, nil } // 获取投放数量 func getNeedDispatchCount(adData *redis_data.AdOrderInfo) (int,error) { beginTime := time.Unix(adData.BeginTime, 0) // 获取当前分钟值 beginMinute := beginTime.Minute() + (beginTime.Hour() * 60) // 获取起点分钟, 不知道原因, 不加会有bug beginMinute += 2 // 获取分钟数到24点还能跑多少值 key := "time_all_count_" + strconv.Itoa(beginMinute) // 0 默认曲线, 1 定制曲线 if adData.LineType == 1 { key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, beginMinute) } // 起始剩余值 beginRemainDispatchCount, err := redis_data.GetRemainDispatchCount(key) if err != nil { return 0, err } // 计算最后分钟 endTime := time.Unix(adData.EndTime, 0) endMinute := endTime.Minute() + (endTime.Hour() * 60) if endMinute > 1439 { // 不懂这里逻辑 endMinute = endMinute - 1440 + 2 } key = "time_all_count_" + strconv.Itoa(endMinute) if adData.LineType == 1 { // 定制曲线 key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, endMinute) } endRemainDispatchCnt, err := redis_data.GetRemainDispatchCount(key) if err != nil { return 0, err } // 结束的剩余值 - 起始剩余值 = 获取区间能跑的值 needDispatchCount := beginRemainDispatchCount - endRemainDispatchCnt return needDispatchCount, nil } func GetAdsInfos(dsp *utils.DspParam, advertiser string, orderType int, fixFlag int, xiaomiHasFlag int) { ua = dsp.ua_client req_source = dsp.req_source order, err := getOneAds(dsp, order_type, fix_flag) if err != nil { return err } if order == nil { return nil } r = rand.Intn( 100) if xiaomiHasFlag == 1 { if strings.Index(order.Title, "_ios") > 0 { return nil } else { r = rand.Intn(50) } } // 获取剩余时间内的值 needDispatchCnt, err := getNeedDispatchCount(order) if err != nil { return err } if needDispatchCnt < 1 { return nil, nil } curTime := time.Now() // #已经投放的key finishShowCnt, err := redis_data.GetFinishedDispatchCount(order.OrderID, "show", curTime) if err != nil { return nil, err } finishClickCnt, err := redis_data.GetFinishedDispatchCount(order.OrderID, "click", curTime) if err != nil { return nil, err } redis_data.SetPlanDispatchCount(order.OrderID, "show", needDispatchCount, time.Now().Unix()) // 计算曲线比例 rate := float32(order.ShowKpi) / float32(needDispatchCnt) curMinutes := curTime.Hour() * 60 + curTime.Minute() key := "time_" + strconv.Itoa(curMinutes) if order.LineType == 1 { // 定制曲线 key = fmt.Sprintf("time_%d_%d", order.OrderID, curMinutes) } lineValue, err := redis_data.GetPerMinuteNeedDispatchCnt(order.OrderID, curTime) if err != nil { return nil, err } // 当前分钟需要放出去的数量 curMinuteNeedDispatchCnt := int(float32(lineValue) * rate) if dispatchCnt < 1 { dispatchCnt = 1 } redis_data.SetOrderPlanDispatchCount(order.OrderID, "show", dispatchCnt, curTime) // 获取当前分钟已经完成的下发 curMinutefinishedDispatchCnt, err := redis_data.GetPreMinuteFinishedDispatchCount(order.OrderID, "show", curTime) if curMinutefinishedDispatchCnt < curMinuteNeedDispatchCnt { data = { "js_order_id":js_order_id, "target":"", "video_url":"", "duration":5, "target_addition":[], "image_url":"", "result":0, "msg":0, "user_agent":ua, "order_name":order_name } //放量 newFinishCnt, err := redis_data.IncrFinishedDispatchCount(order.OrderID, "show", 1, curTime) if err != nil { return err } newCurMinutefinishedCnt, err := redis_data.IncrPreMinuteFinishedDispatchCount(order.OrderID, "show", 1, curTime) if err != nil { return err } show_url = ads_item['show_url'] click_url = ads_item['click_url'] target = ads_item['target_url'] if strings.Index(order.Title, "_ios") != -1 { iosImei, iosUa, err := redis_data.GetIosUaImei(dsp.Ip) if err != nil { return err } if iosUa { } data['user_agent'] = ios_ua if ads_item.get('imei_replace_flag', 0) == 1 and ios_imei: show_url = show_url.replace('__IDFA__', ios_imei) click_url = click_url.replace('__IDFA__', ios_imei) target = target.replace('__IDFA__', ios_imei) if order_name.find("__OS__") > 0: show_url = show_url.replace('__OS__', '1') click_url = click_url.replace('__OS__', '1') target = target.replace('__OS__', '1') } elif order_name.find("_android") > 0 : ### 判断是否需要替换imei if ads_item.get('imei_replace_flag', 0) == 1: show_url = show_url.replace('__IMEI__', dsp.real_md5_imei) click_url = click_url.replace('__IMEI__', dsp.real_md5_imei) target = target.replace('__IMEI__', dsp.real_md5_imei) if order_name.find("__OS__") > 0: show_url = show_url.replace('__OS__', '0') click_url = click_url.replace('__OS__', '0') target = target.replace('__OS__', '0') else: if r < 40: ios_imei, ios_ua = get_ios_ua_imei(dsp) if ios_ua: data['user_agent'] = ios_ua if ads_item.get('imei_replace_flag', 0) == 1 and ios_imei: show_url = show_url.replace('__IDFA__', ios_imei) click_url = click_url.replace('__IDFA__', ios_imei) target = target.replace('__IDFA__', ios_imei) if order_name.find("__OS__") > 0: show_url = show_url.replace('__OS__', '1') click_url = click_url.replace('__OS__', '1') target = target.replace('__OS__', '1') else: if ads_item.get('imei_replace_flag', 0) == 1: show_url = show_url.replace('__IMEI__', dsp.real_md5_imei) click_url = click_url.replace('__IMEI__', dsp.real_md5_imei) target = target.replace('__IMEI__', dsp.real_md5_imei) if order_name.find("__OS__") > 0: show_url = show_url.replace('__OS__', '0') click_url = click_url.replace('__OS__', '0') target = target.replace('__OS__', '0') if order_name.find("__IP__") > 0: show_url = show_url.replace('__IP__', dsp.ip) click_url = click_url.replace('__IP__', dsp.ip) target = target.replace('__IP__', dsp.ip) if order_type == 0: addi = get_monitor_url('VIEW', 5, order_name, req_source, show_url) else: addi = get_monitor_url('VIEW', 5, order_name, 'follow', show_url) #addi['urls'].append(show_url) data['target_addition'].append(addi) r = random.randint(0, 1000) c_rate = click_kpi * 1.0 / (ads_item['show_kpi']) * 1000 if r < c_rate: #下发点击 addi = get_monitor_url('CLICK', 1, order_name, req_source, click_url) #addi['urls'].append(click_url) data['target_addition'].append(addi) md5_skip = hashlib.md5(target).hexdigest() redis_tools.incr_key_value(click_key) real_target = ads_config.Host + "?action=LOADING&req_source={0}&advertiser={2}&skip={1}&skip_other={3}".format( req_source, '', order_name, md5_skip) ### 塞入缓存中 redis_tools.set_skip_info(md5_skip, target) data['target'] = real_target raise gen.Return((None, data)) } #获取当前已经放出去的次数 raise gen.Return((None, None)) }~