custom.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. package addata
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "miads/adslib"
  6. "miads/adslib/redis_data"
  7. "miads/adslib/utils"
  8. "strconv"
  9. "strings"
  10. "time"
  11. )
  12. type CustomAdData struct {
  13. Duration int64 `json:"duration"`
  14. ImageURL string `json:"image_url"`
  15. JsOrderID int64 `json:"js_order_id"`
  16. Msg int64 `json:"msg"`
  17. OrderName string `json:"order_name"`
  18. Result int64 `json:"result"`
  19. Target string `json:"target"`
  20. TargetAddition []AdAction `json:"target_addition"`
  21. UserAgent string `json:"user_agent"`
  22. VideoURL string `json:"video_url"`
  23. }
  24. func CombineOrderBy(adData *AdData, advertiser string, dsp *utils.DspParam) (*AdData, error) {
  25. customAdData, err := GetCustomAdsInfos(dsp, advertiser, 1, 0, 0)
  26. if err != nil {
  27. return adData, err
  28. }
  29. if customAdData == nil {
  30. return adData, nil
  31. }
  32. if len(customAdData.TargetAddition) == 0 {
  33. return adData, nil
  34. }
  35. maxExchangeLen := len(customAdData.TargetAddition)
  36. // 最多替换数量, 不知道为啥是2
  37. if maxExchangeLen > 2 {
  38. maxExchangeLen = 2
  39. }
  40. hasClickTarget := false
  41. for i := 0; i < maxExchangeLen; i++ {
  42. adData.TargetAddition[i].Urls = customAdData.TargetAddition[i].Urls
  43. if customAdData.TargetAddition[i].Type == "CLICK" {
  44. adData.TargetAddition[i].Type = "CLICK"
  45. hasClickTarget = true
  46. }
  47. }
  48. if hasClickTarget {
  49. adData.Target = customAdData.Target
  50. adData.JsOrderId = customAdData.JsOrderID
  51. }
  52. return adData, nil
  53. }
  54. // 获取一个广告
  55. func getOneAds(dsp *utils.DspParam, orderType int, fixFlag int) (*redis_data.AdOrderInfo, error){
  56. // 取出广告
  57. orders, err := redis_data.GetOrderInfos(dsp, fixFlag)
  58. if err != nil {
  59. return nil, err
  60. }
  61. if len(orders) == 0 {
  62. return nil, nil
  63. }
  64. gotOrders := make([]redis_data.AdOrderInfo, 0, 1000)
  65. allKpi := int64(0)
  66. for _, order := range orders {
  67. if order.OrderType == int64(orderType) {
  68. gotOrders = append(gotOrders, order)
  69. allKpi += order.ShowKpi
  70. }
  71. }
  72. orderRange := make([]int, 0, 1000)
  73. curRateIdx := 0
  74. // orderRange中记录的是当前同索引位置的gotOrders里的order的比例上限, 比如如果有两个order [ShowKpi:40, ShowKpi: 60]
  75. // 那么orderRange就是 [rate:400, rate: 1000], 所以要按比例取order, 只需要获得在比例上限里随机取一个数, 然后判断落在那个orderRange里,
  76. // 对应去gotOrders取同索引里的order即可, 因为算法里有对小于1的比例做补偿, 所以rate上限可能超出1000, rate乘1000而不是100主要是为了
  77. // 如果showKpi差异过大, 对大量小于1的订单做补偿, 会影响整体流量分布, 这里放到1000倍, 降低补偿1的影响, 算法复杂度是O(n), 只有三个非嵌套循环
  78. for _, order := range gotOrders {
  79. rate := int(float32(order.ShowKpi) / float32(allKpi) * 1000)
  80. // 防止比例过小, 取int后变为0
  81. if rate < 1 {
  82. rate = 1
  83. }
  84. curRateIdx = curRateIdx + rate
  85. orderRange = append(orderRange, curRateIdx)
  86. }
  87. randNum := rand.Intn(curRateIdx)
  88. for i, rateRange := range orderRange {
  89. if randNum <= rateRange {
  90. return &gotOrders[i], nil
  91. }
  92. }
  93. return nil, nil
  94. }
  95. // 获取投放数量
  96. func getNeedDispatchCount(adData *redis_data.AdOrderInfo) (int,error) {
  97. beginTime := time.Unix(adData.BeginTime, 0)
  98. // 获取当前分钟值
  99. beginMinute := beginTime.Minute() + (beginTime.Hour() * 60)
  100. // 获取起点分钟, 不知道原因, 不加会有bug
  101. beginMinute += 2
  102. // 获取分钟数到24点还能跑多少值
  103. key := "time_all_count_" + strconv.Itoa(beginMinute)
  104. // 0 默认曲线, 1 定制曲线
  105. if adData.LineType == 1 {
  106. key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, beginMinute)
  107. }
  108. // 起始剩余值
  109. beginRemainDispatchCount, err := redis_data.GetRemainDispatchCount(key)
  110. if err != nil {
  111. return 0, err
  112. }
  113. // 计算最后分钟
  114. endTime := time.Unix(adData.EndTime, 0)
  115. endMinute := endTime.Minute() + (endTime.Hour() * 60)
  116. if endMinute > 1439 {
  117. // 不懂这里逻辑
  118. endMinute = endMinute - 1440 + 2
  119. }
  120. key = "time_all_count_" + strconv.Itoa(endMinute)
  121. if adData.LineType == 1 {
  122. // 定制曲线
  123. key = fmt.Sprintf("time_all_count_%d_%d", adData.OrderID, endMinute)
  124. }
  125. endRemainDispatchCnt, err := redis_data.GetRemainDispatchCount(key)
  126. if err != nil {
  127. return 0, err
  128. }
  129. // 结束的剩余值 - 起始剩余值 = 获取区间能跑的值
  130. needDispatchCount := beginRemainDispatchCount - endRemainDispatchCnt
  131. return needDispatchCount, nil
  132. }
  133. func GetCustomAdsInfos(dsp *utils.DspParam, advertiser string, orderType int, fixFlag int, xiaomiHasFlag int) (*CustomAdData, error){
  134. order, err := getOneAds(dsp, orderType, fixFlag)
  135. if err != nil {
  136. return nil, err
  137. }
  138. if order == nil {
  139. return nil, nil
  140. }
  141. if xiaomiHasFlag == 1 {
  142. if strings.Index(order.Title, "_ios") > 0 {
  143. return nil, nil
  144. }
  145. }
  146. // 获取剩余时间内的值
  147. needDispatchCnt, err := getNeedDispatchCount(order)
  148. if err != nil {
  149. return nil, err
  150. }
  151. if needDispatchCnt < 1 {
  152. return nil, nil
  153. }
  154. curTime := time.Now()
  155. // #已经投放的key
  156. finishShowCnt, err := redis_data.GetFinishedDispatchCount(order.OrderID, "show", curTime)
  157. if err != nil {
  158. return nil, err
  159. }
  160. fmt.Sprintf("finish show cnt: %d\n", finishShowCnt)
  161. redis_data.SetPlanDispatchCount(order.OrderID, "show", needDispatchCnt, curTime)
  162. // 计算曲线比例
  163. rate := float32(order.ShowKpi) / float32(needDispatchCnt)
  164. lineValue := 0
  165. if order.LineType == 1 {
  166. // 订单定制曲线
  167. lineValue, err = redis_data.GetOrderPerMinuteNeedDispatchCnt(order.OrderID, curTime)
  168. } else {
  169. lineValue, err = redis_data.GetPerMinuteNeedDispatchCnt(curTime)
  170. }
  171. if err != nil {
  172. return nil, err
  173. }
  174. // 当前分钟需要放出去的数量
  175. curMinuteNeedDispatchCnt := int(float32(lineValue) * rate)
  176. if curMinuteNeedDispatchCnt < 1 {
  177. curMinuteNeedDispatchCnt = 1
  178. }
  179. redis_data.SetOrderPlanDispatchCount(order.OrderID, "show", curMinuteNeedDispatchCnt, 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 nil, err
  193. }
  194. _, err = redis_data.IncrPreMinuteFinishedDispatchCount(order.OrderID, "show", 1, curTime)
  195. if err != nil {
  196. return nil, 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 nil, 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. } else {
  232. r := rand.Intn(100)
  233. if r < 40 && xiaomiHasFlag == 0 {
  234. iosImei, iosUa, err := redis_data.GetIosUaImei(dsp.Ip)
  235. if err != nil {
  236. return nil, err
  237. }
  238. if iosUa != "" {
  239. data.UserAgent = iosUa
  240. }
  241. if order.ImeiReplaceFlag == 1 && iosImei != "" {
  242. showUrl = strings.ReplaceAll(showUrl, "__IDFA__", iosImei)
  243. clickUrl = strings.ReplaceAll(clickUrl, "__IDFA__", iosImei)
  244. targetUrl = strings.ReplaceAll(targetUrl, "__IDFA__", iosImei)
  245. }
  246. if strings.Index(order.Title, "__OS__") != -1 {
  247. showUrl = strings.ReplaceAll(showUrl, "__OS__", "1")
  248. clickUrl = strings.ReplaceAll(clickUrl, "__OS__", "1")
  249. targetUrl = strings.ReplaceAll(targetUrl, "__OS__", "1")
  250. }
  251. } else {
  252. // 判断是否需要替换imei
  253. if order.ImeiReplaceFlag == 1 {
  254. showUrl = strings.ReplaceAll(showUrl, "__IMEI__", dsp.RealMd5Imei)
  255. clickUrl = strings.ReplaceAll(clickUrl, "__IMEI__", dsp.RealMd5Imei)
  256. targetUrl = strings.ReplaceAll(targetUrl, "__IMEI__", dsp.RealMd5Imei)
  257. }
  258. if strings.Index(order.Title, "__OS__") != -1 {
  259. showUrl = strings.ReplaceAll(showUrl, "__OS__", "0")
  260. clickUrl = strings.ReplaceAll(clickUrl, "__OS__", "0")
  261. targetUrl = strings.ReplaceAll(targetUrl, "__OS__", "0")
  262. }
  263. }
  264. }
  265. if strings.Index(order.Title, "__IP__") != -1 {
  266. showUrl = strings.ReplaceAll(showUrl, "__IP__", dsp.Ip)
  267. clickUrl = strings.ReplaceAll(clickUrl, "__IP__", dsp.Ip)
  268. targetUrl = strings.ReplaceAll(targetUrl, "__IP__", dsp.Ip)
  269. }
  270. var addi *AdAction
  271. if orderType == 0 {
  272. addi = genMonitorAction("VIEW", order.Title, dsp.ReqSource, showUrl)
  273. } else {
  274. addi = genMonitorAction("VIEW", order.Title, "follow", showUrl)
  275. }
  276. data.TargetAddition = append(data.TargetAddition, *addi)
  277. r := rand.Intn( 1000)
  278. clickRate := int(float32(order.ClickKpi) / float32(order.ShowKpi) * 1000)
  279. if r < clickRate {
  280. //下发点击
  281. addi = genMonitorAction("CLICK", order.Title, dsp.ReqSource, clickUrl)
  282. data.TargetAddition = append(data.TargetAddition, *addi)
  283. md5Skip := utils.Md5(targetUrl)
  284. redis_data.IncrFinishedDispatchCount(order.OrderID, "click", 1, curTime)
  285. realTarget := adslib.GetConf().Host + fmt.Sprintf("?action=LOADING&req_source=%s&advertiser=%s&skip=%s&skip_other=%s",
  286. dsp.ReqSource, "", order.Title, md5Skip)
  287. // 塞入缓存中
  288. redis_data.SetSkipInfo(md5Skip, targetUrl)
  289. data.Target = realTarget
  290. return &data, nil
  291. }
  292. }
  293. return nil, nil
  294. }
  295. func genMonitorAction(action string, title string, reqSource string, url string) *AdAction {
  296. duration := 5
  297. if action =="VIDEO_TIMER" {
  298. duration = 7
  299. }
  300. urlArr := make([]string,0, 50)
  301. if url != "" {
  302. urlArr = strings.Split(url, "||")
  303. }
  304. adAction := AdAction{
  305. Type: action,
  306. Duration: duration,
  307. Urls: urlArr,
  308. }
  309. urlHost := adslib.GetConf().HostIos
  310. actionUrl := fmt.Sprintf("%s?action=%s&advertiser=%s&req_source=%s",
  311. urlHost, action, reqSource, title)
  312. adAction.Urls = append(adAction.Urls, actionUrl)
  313. return &adAction
  314. }