custom.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. package addata
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "miads/adslib/redis_data"
  6. "miads/adslib/utils"
  7. "strconv"
  8. "strings"
  9. "time"
  10. )
  11. type CustomAdData struct {
  12. Duration int64 `json:"duration"`
  13. ImageURL string `json:"image_url"`
  14. JsOrderID int64 `json:"js_order_id"`
  15. Msg int64 `json:"msg"`
  16. OrderName string `json:"order_name"`
  17. Result int64 `json:"result"`
  18. Target string `json:"target"`
  19. TargetAddition []interface{} `json:"target_addition"`
  20. UserAgent string `json:"user_agent"`
  21. VideoURL string `json:"video_url"`
  22. }
  23. func CombineOrderBy(adData *AdData, dsp *utils.DspParam) {
  24. ads_item, extra_item = yield get_ads_infos(dsp, order_type = 1)
  25. if extra_item == None:
  26. raise
  27. gen.Return(infos)
  28. last_target_addition = infos.get('target_addition', [])
  29. extra_item_addition = extra_item.get('target_addition', [])
  30. if len(extra_item_addition) == 0:
  31. raise
  32. gen.Return(infos)
  33. if len(extra_item_addition) == 1:
  34. last_target_addition[0]['urls'].extend(extra_item_addition[0]['urls'])
  35. raise
  36. gen.Return(infos)
  37. else:
  38. last_target_addition[0]['urls'].extend(extra_item_addition[0]['urls'])
  39. last_target_addition[1]['urls'].extend(extra_item_addition[1]['urls'])
  40. infos['target'] = extra_item['target']
  41. infos['js_order_id'] = extra_item['js_order_id']
  42. last_target_addition[1]['type'] = 'CLICK'
  43. raise
  44. gen.Return(infos)
  45. }
  46. // 获取一个广告
  47. func getOneAds(dsp *utils.DspParam, orderType int, fixFlag int) (*redis_data.AdOrderInfo, error){
  48. // 取出广告
  49. orders, err := redis_data.GetOrderInfos(dsp, fixFlag)
  50. if err != nil {
  51. return nil, err
  52. }
  53. if len(orders) == 0 {
  54. return nil, nil
  55. }
  56. gotOrders := make([]redis_data.AdOrderInfo, 0, 1000)
  57. allKpi := int64(0)
  58. for _, order := range orders {
  59. if order.OrderType == int64(orderType) {
  60. gotOrders = append(gotOrders, order)
  61. allKpi += order.ShowKpi
  62. }
  63. }
  64. orderRange := make([]int, 0, 1000)
  65. curRateIdx := 0
  66. // orderRange中记录的是当前同索引位置的gotOrders里的order的比例上限, 比如如果有两个order [ShowKpi:40, ShowKpi: 60]
  67. // 那么orderRange就是 [rate:400, rate: 1000], 所以要按比例取order, 只需要获得在比例上限里随机取一个数, 然后判断落在那个orderRange里,
  68. // 对应去gotOrders取同索引里的order即可, 因为算法里有对小于1的比例做补偿, 所以rate上限可能超出1000, rate乘1000而不是100主要是为了
  69. // 如果showKpi差异过大, 对大量小于1的订单做补偿, 会影响整体流量分布, 这里放到1000倍, 降低补偿1的影响, 算法复杂度是O(n), 只有三个非嵌套循环
  70. for _, order := range gotOrders {
  71. rate := int(float32(order.ShowKpi) / float32(allKpi) * 1000)
  72. // 防止比例过小, 取int后变为0
  73. if rate < 1 {
  74. rate = 1
  75. }
  76. curRateIdx = curRateIdx + rate
  77. orderRange = append(orderRange, curRateIdx)
  78. }
  79. randNum := rand.Intn(curRateIdx)
  80. for i, rateRange := range orderRange {
  81. if randNum <= rateRange {
  82. return &gotOrders[i], nil
  83. }
  84. }
  85. return nil, nil
  86. }
  87. // 获取投放数量
  88. func getNeedDispatchCount(adData *redis_data.AdOrderInfo) (int,error) {
  89. beginTime := time.Unix(adData.BeginTime, 0)
  90. // 获取当前分钟值
  91. beginMinute := beginTime.Minute() + (beginTime.Hour() * 60)
  92. // 获取起点分钟, 不知道原因, 不加会有bug
  93. beginMinute += 2
  94. // 获取分钟数到24点还能跑多少值
  95. key := "time_all_count_" + strconv.Itoa(beginMinute)
  96. // 0 默认曲线, 1 定制曲线
  97. if adData.LineType == 1 {
  98. key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, beginMinute)
  99. }
  100. // 起始剩余值
  101. beginRemainDispatchCount, err := redis_data.GetRemainDispatchCount(key)
  102. if err != nil {
  103. return 0, err
  104. }
  105. // 计算最后分钟
  106. endTime := time.Unix(adData.EndTime, 0)
  107. endMinute := endTime.Minute() + (endTime.Hour() * 60)
  108. if endMinute > 1439 {
  109. // 不懂这里逻辑
  110. endMinute = endMinute - 1440 + 2
  111. }
  112. key = "time_all_count_" + strconv.Itoa(endMinute)
  113. if adData.LineType == 1 {
  114. // 定制曲线
  115. key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, endMinute)
  116. }
  117. endRemainDispatchCnt, err := redis_data.GetRemainDispatchCount(key)
  118. if err != nil {
  119. return 0, err
  120. }
  121. // 结束的剩余值 - 起始剩余值 = 获取区间能跑的值
  122. needDispatchCount := beginRemainDispatchCount - endRemainDispatchCnt
  123. return needDispatchCount, nil
  124. }
  125. func GetAdsInfos(dsp *utils.DspParam, advertiser string, orderType int, fixFlag int, xiaomiHasFlag int) {
  126. ua = dsp.ua_client
  127. req_source = dsp.req_source
  128. order, err := getOneAds(dsp, order_type, fix_flag)
  129. if err != nil {
  130. return err
  131. }
  132. if order == nil {
  133. return nil
  134. }
  135. r = rand.Intn( 100)
  136. if xiaomiHasFlag == 1 {
  137. if strings.Index(order.Title, "_ios") > 0 {
  138. return nil
  139. } else {
  140. r = rand.Intn(50)
  141. }
  142. }
  143. // 获取剩余时间内的值
  144. needDispatchCnt, err := getNeedDispatchCount(order)
  145. if err != nil {
  146. return err
  147. }
  148. if needDispatchCnt < 1 {
  149. return nil, nil
  150. }
  151. curTime := time.Now()
  152. // #已经投放的key
  153. finishShowCnt, err := redis_data.GetFinishedDispatchCount(order.OrderID, "show", curTime)
  154. if err != nil {
  155. return nil, err
  156. }
  157. finishClickCnt, err := redis_data.GetFinishedDispatchCount(order.OrderID, "click", curTime)
  158. if err != nil {
  159. return nil, err
  160. }
  161. redis_data.SetPlanDispatchCount(order.OrderID, "show", needDispatchCount, time.Now().Unix())
  162. // 计算曲线比例
  163. rate := float32(order.ShowKpi) / float32(needDispatchCnt)
  164. curMinutes := curTime.Hour() * 60 + curTime.Minute()
  165. key := "time_" + strconv.Itoa(curMinutes)
  166. if order.LineType == 1 {
  167. // 定制曲线
  168. key = fmt.Sprintf("time_%d_%d", order.OrderID, curMinutes)
  169. }
  170. lineValue, err := redis_data.GetPerMinuteNeedDispatchCnt(order.OrderID, curTime)
  171. if err != nil {
  172. return nil, err
  173. }
  174. // 当前分钟需要放出去的数量
  175. curMinuteNeedDispatchCnt := int(float32(lineValue) * rate)
  176. if dispatchCnt < 1 {
  177. dispatchCnt = 1
  178. }
  179. redis_data.SetOrderPlanDispatchCount(order.OrderID, "show", dispatchCnt, curTime)
  180. // 获取当前分钟已经完成的下发
  181. curMinutefinishedDispatchCnt, err := redis_data.GetPreMinuteFinishedDispatchCount(order.OrderID, "show", curTime)
  182. if curMinutefinishedDispatchCnt < curMinuteNeedDispatchCnt {
  183. data := CustomAdData{
  184. Duration: 5,
  185. JsOrderID: order.JsOrderID,
  186. OrderName: order.Title,
  187. UserAgent: dsp.UaOrigin,
  188. }
  189. //放量
  190. _, err := redis_data.IncrFinishedDispatchCount(order.OrderID, "show", 1, curTime)
  191. if err != nil {
  192. return err
  193. }
  194. _, err := redis_data.IncrPreMinuteFinishedDispatchCount(order.OrderID, "show", 1, curTime)
  195. if err != nil {
  196. return err
  197. }
  198. showUrl := order.ShowURL
  199. clickUrl := order.ClickURL
  200. targetUrl := order.TargetURL
  201. if strings.Index(order.Title, "_ios") != -1 {
  202. iosImei, iosUa, err := redis_data.GetIosUaImei(dsp.Ip)
  203. if err != nil {
  204. return err
  205. }
  206. if iosUa != "" {
  207. data.UserAgent = iosUa
  208. }
  209. if order.ImeiReplaceFlag == 1 && iosImei != "" {
  210. showUrl = strings.ReplaceAll(showUrl, "__IDFA__", iosImei)
  211. clickUrl = strings.ReplaceAll(clickUrl, "__IDFA__", iosImei)
  212. targetUrl = strings.ReplaceAll(targetUrl, "__IDFA__", iosImei)
  213. }
  214. if strings.Index(order.Title, "__OS__") != -1 {
  215. showUrl = strings.ReplaceAll(showUrl, "__OS__", "1")
  216. clickUrl = strings.ReplaceAll(clickUrl, "__OS__", "1")
  217. targetUrl = strings.ReplaceAll(targetUrl, "__OS__", "1")
  218. }
  219. } else if strings.Index(order.Title, "_android") != -1 {
  220. // 判断是否需要替换imei
  221. if order.ImeiReplaceFlag == 1 {
  222. showUrl = strings.ReplaceAll(showUrl, "__IMEI__", dsp.RealMd5Imei)
  223. clickUrl = strings.ReplaceAll(clickUrl, "__IMEI__", dsp.RealMd5Imei)
  224. targetUrl = strings.ReplaceAll(targetUrl, "__IMEI__", dsp.RealMd5Imei)
  225. }
  226. if strings.Index(order.Title, "__OS__") != -1 {
  227. showUrl = strings.ReplaceAll(showUrl, "__OS__", "0")
  228. clickUrl = strings.ReplaceAll(clickUrl, "__OS__", "0")
  229. targetUrl = strings.ReplaceAll(targetUrl, "__OS__", "0")
  230. }
  231. if ads_item.get('imei_replace_flag', 0) == 1:
  232. show_url = show_url.replace('__IMEI__', dsp.real_md5_imei)
  233. click_url = click_url.replace('__IMEI__', dsp.real_md5_imei)
  234. target = target.replace('__IMEI__', dsp.real_md5_imei)
  235. if order_name.find("__OS__") > 0:
  236. show_url = show_url.replace('__OS__', '0')
  237. click_url = click_url.replace('__OS__', '0')
  238. target = target.replace('__OS__', '0')
  239. }
  240. else:
  241. if r < 40:
  242. ios_imei, ios_ua = get_ios_ua_imei(dsp)
  243. if ios_ua:
  244. data['user_agent'] = ios_ua
  245. if ads_item.get('imei_replace_flag', 0) == 1 and
  246. ios_imei:
  247. show_url = show_url.replace('__IDFA__', ios_imei)
  248. click_url = click_url.replace('__IDFA__', ios_imei)
  249. target = target.replace('__IDFA__', ios_imei)
  250. if order_name.find("__OS__") > 0:
  251. show_url = show_url.replace('__OS__', '1')
  252. click_url = click_url.replace('__OS__', '1')
  253. target = target.replace('__OS__', '1')
  254. else:
  255. if ads_item.get('imei_replace_flag', 0) == 1:
  256. show_url = show_url.replace('__IMEI__', dsp.real_md5_imei)
  257. click_url = click_url.replace('__IMEI__', dsp.real_md5_imei)
  258. target = target.replace('__IMEI__', dsp.real_md5_imei)
  259. if order_name.find("__OS__") > 0:
  260. show_url = show_url.replace('__OS__', '0')
  261. click_url = click_url.replace('__OS__', '0')
  262. target = target.replace('__OS__', '0')
  263. if order_name.find("__IP__") > 0:
  264. show_url = show_url.replace('__IP__', dsp.ip)
  265. click_url = click_url.replace('__IP__', dsp.ip)
  266. target = target.replace('__IP__', dsp.ip)
  267. if order_type == 0:
  268. addi = get_monitor_url('VIEW', 5, order_name, req_source, show_url)
  269. else:
  270. addi = get_monitor_url('VIEW', 5, order_name, 'follow', show_url)
  271. #addi['urls'].append(show_url)
  272. data['target_addition'].append(addi)
  273. r = random.randint(0, 1000)
  274. c_rate = click_kpi * 1.0 / (ads_item['show_kpi']) * 1000
  275. if r < c_rate:
  276. #下发点击
  277. addi = get_monitor_url('CLICK', 1, order_name, req_source, click_url)
  278. #addi['urls'].append(click_url)
  279. data['target_addition'].append(addi)
  280. md5_skip = hashlib.md5(target).hexdigest()
  281. redis_tools.incr_key_value(click_key)
  282. real_target = ads_config.Host + "?action=LOADING&req_source={0}&advertiser={2}&skip={1}&skip_other={3}".format(
  283. req_source, '', order_name, md5_skip)
  284. ### 塞入缓存中
  285. redis_tools.set_skip_info(md5_skip, target)
  286. data['target'] = real_target
  287. raise
  288. gen.Return((None, data))
  289. }
  290. #获取当前已经放出去的次数
  291. raise gen.Return((None, None))
  292. }~