TaoniuController.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. /**
  3. * 对接桃牛接口
  4. * @author: lyh
  5. * @date: 2019/4/3
  6. */
  7. namespace App\Http\Controllers\Api;
  8. use App\Http\Controllers\Controller;
  9. use App\Modes\AdverAlarm;
  10. use App\Modes\AdverDeductLog;
  11. use App\Modes\Advertising;
  12. use App\Modes\AdverVisit;
  13. use App\Modes\Area;
  14. use App\Modes\Config;
  15. use App\Modes\Industry;
  16. use Carbon\Carbon;
  17. use Illuminate\Http\Request;
  18. class TaoniuController extends Controller
  19. {
  20. /**
  21. * 桃牛获取广告接口
  22. * @author lyh
  23. * @date 2019/4/3
  24. * @description
  25. * 要求:筛选条件包括:关键字、地区、行业、数量
  26. * @todo 待定
  27. */
  28. public function adver(Request $request)
  29. {
  30. $validator = \Validator::make($param = $request->all(), [
  31. 'keyword' => 'min:1', // 查询-关键字
  32. 'industry' => 'exists:industry,content', // 查询-行业
  33. 'province' => 'exists:area,name', // 查询-省
  34. 'city' => 'exists:area,name', // 查询-市
  35. 'district' => 'exists:area,name', // 查询-区
  36. 'num' => 'required|integer', //查询-提取广告数量
  37. 'shop_master_id' => 'required|integer',// 防作弊-主商家id
  38. //'shop_slave_id' => 'required|integer',//防作弊-子商家id、
  39. //'user_industry' => 'required|exists:industry,content', // 防作弊-当前登录用户的所在的行业
  40. //'uid' => 'required|integer', //防作弊-用户id
  41. 'ip' => 'required|ipv4', // 防作弊-ip
  42. 'is_free' => 'required|integer|between:0,1', //防作弊 0-扣费 1-免费
  43. 'type' => 'required|integer|between:1,2' // 防作弊-1-点击、2-浏览、3-展现
  44. ]);
  45. if ($validator->fails()) {
  46. return showJsonErr($validator->errors()->first());
  47. }
  48. $adver = Advertising::select(['id', 'scene', 'title', 'image', 'scene', 'image_group', 'content', 'redirect'])
  49. ->where('residue_num', '>=', 1)
  50. ->where('status', 3)
  51. ->limit($param['num'])
  52. ->orderBy(\DB::raw('rand()')); // @TODO 随机待优化
  53. // 关键字查询
  54. if (!empty($param['keyword'])) {
  55. $adver->where('title', 'like', "%{$param['keyword']}%");
  56. }
  57. // 行业查询
  58. if (!empty($param['industry'])) {
  59. $adver->where('industry', Industry::whereContent($param['industry'])->select('id')->first()->id);
  60. }
  61. // 省级查询
  62. if (!empty($param['province'])) {
  63. $adver->where('province', Area::whereName($param['province'])->select(['id'])->first()->id);
  64. }
  65. // 地级市查询
  66. if (!empty($param['city'])) {
  67. $adver->where('city', Area::whereName($param['city'])->select(['id'])->first()->id);
  68. }
  69. // 县区查询
  70. if (!empty($param['district'])) {
  71. $adver->where('district', Area::whereName($param['district'])->select(['id'])->first()->id);
  72. }
  73. $res = $adver->get();
  74. if (empty($res)) {
  75. return showJsonErr(1012);
  76. }
  77. $param['adver'] = collect($res)->pluck('id');
  78. $param['ip'] = $request->ip();
  79. if (!empty($param['shop_slave_id'])) {
  80. }
  81. $param['shop_id'] = $param['shop_master_id'] . '-' . $param['shop_slave_id'];
  82. // 保存广告获取日志
  83. AdverVisit::insertGetId($param);
  84. $remark = [];
  85. if ($res->isNotEmpty() && $param['is_free'] == 0) {
  86. foreach ($res as $ad) {
  87. $param['adver_id'] = $ad->id;
  88. // 扣费
  89. $remark = $this->deduction($param);
  90. }
  91. }
  92. return showJsonSucc(1001, ['data' => $res, 'remark' => $remark]);
  93. }
  94. /**
  95. * 广告扣费
  96. * @author lyh
  97. * @date 2019/4/3
  98. * @description
  99. * 要求:支持批量
  100. * 预计要做的防作弊规则是这样:
  101. * 1. 根据同用户点击同一个商家,某个时间段M内,最多只能N次生效
  102. * 2. 根据同IP点击同一个商家,某个时间段M内,最多只能N次生效
  103. * 3. 根据同一个用户,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效
  104. * 4. 根据同一个IP,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效
  105. *
  106. */
  107. public function adverDeduction(Request $request)
  108. {
  109. $validator = \Validator::make($param = $request->all(), [
  110. 'uid' => 'required|integer',
  111. 'shopId' => 'required|integer',
  112. 'ip' => 'required',
  113. 'industry' => 'required|exists:industry,content',
  114. 'adver_id' => 'required|exists:advertising,id',
  115. 'is_free' => 'required|integer|between:0,1', // 0-扣费 1-免费
  116. 'type' => 'required|integer|between:1,3' // 1-点击、2-浏览、3-展现
  117. ]);
  118. if ($validator->fails()) {
  119. return showJsonErr($validator->errors()->first());
  120. }
  121. $param['industry'] = Industry::whereContent($param['industry'])->first()->id;
  122. // 请求记录
  123. AdverDeductLog::insertGetId([
  124. 'uid' => $param['uid'],
  125. 'shop_id' => $param['shopId'],
  126. 'ip' => $param['ip'],
  127. 'industry' => $param['industry'],
  128. 'adver_id' => $param['adver_id'],
  129. 'is_free' => $param['is_free'],
  130. 'type' => $param['type']
  131. ]);
  132. // 对广告统计
  133. switch ($param['type']) {
  134. case 1:
  135. Advertising::whereId($param['adver_id'])->increment('click_num');
  136. break;
  137. case 2:
  138. Advertising::whereId($param['adver_id'])->increment('browse_num');
  139. break;
  140. case 3:
  141. Advertising::whereId($param['adver_id'])->increment('show_num');
  142. break;
  143. default;
  144. }
  145. $remark = [];
  146. if ($param['is_free'] == 0) {
  147. /**********根据同用户点击同一个商家,某个时间段M内,最多只能N次生效**********/
  148. $where = [['adver_id', '=', $param['adver_id']], ['uid', ' = ', $param['uid']], ['shop_id', ' = ', $param['shopId']]];
  149. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'user', $keyEnd = 'shoper', $paramStart = $param['uid'], $paramEnd = $param['shopId'], $where);
  150. if (is_string($re)) {
  151. $remark[] = $re;
  152. }
  153. /**********根据同IP点击同一个商家,某个时间段M内,最多只能N次生效**********/
  154. $where = [['adver_id', ' = ', $param['adver_id']], ['ip', ' = ', $param['ip']], ['shop_id', ' = ', $param['shop_id']]];
  155. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'ip', $keyEnd = 'shoper', $paramStart = $param['ip'], $paramEnd = $param['shopId'], $where);
  156. if (is_string($re)) {
  157. $remark[] = $re;
  158. }
  159. /**********根据同一个用户,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效**********/
  160. $where = [['adver_id', ' = ', $param['adver_id']], ['uid', ' = ', $param['uid']], ['industry', ' = ', $param['industry']]];
  161. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'user', $keyEnd = 'industry', $paramStart = $param['uid'], $paramEnd = $param['industry'], $where);
  162. if (is_string($re)) {
  163. $remark[] = $re;
  164. }
  165. /**********根据同一个IP,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效**********/
  166. $where = [['adver_id', ' = ', $param['adver_id']], ['ip', ' = ', $param['ip']], ['industry', ' = ', $param['industry']]];
  167. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'ip', $keyEnd = 'industry', $paramStart = $param['ip'], $paramEnd = $param['industry'], $where);
  168. if (is_string($re)) {
  169. $remark[] = $re;
  170. }
  171. if (!empty($remark)) {
  172. return showJsonErr('扣费失败', $remark);
  173. }
  174. $adver = Advertising::find($param['adver_id']);
  175. if ($adver->residue_num <= 0) {
  176. return showJsonErr("广告[ID:{$param['adver_id']},剩余次数为{$adver->residue_num}");
  177. }
  178. $res = Advertising::whereId($param['adver_id'])->decrement('residue_num');
  179. if (empty($res)) {
  180. return showJsonErr("广告[id:{$param['adver_id']}]扣费失败");
  181. }
  182. // 广告过期
  183. $adver = Advertising::whereId($param['adver_id'])->select(['residue_num'])->first();
  184. if ($adver->residue_num <= 0) {
  185. Advertising::whereId($param['adver_id'])->update(['status' => 9]);
  186. // todo 告诉广告发布者广告到期?
  187. }
  188. }
  189. return showJsonSucc("广告[id:{$param['adver_id']}]请求成功", $remark);
  190. }
  191. /**
  192. * 分享计费
  193. * @author lyh
  194. * @date 2019/4/3
  195. * @param \Request $request
  196. * @description
  197. * todo 待定
  198. */
  199. public function shareBilling(\Request $request)
  200. {
  201. }
  202. /**
  203. * @author lyh
  204. * @date 2019/4/4
  205. * @param $duration
  206. * @param $time
  207. * @return array
  208. * @description
  209. */
  210. private function sendMsg($adverId, $duration, $time, $where = [], $remark = '%s')
  211. {
  212. $afterTime = Carbon::now()->subMinute($duration);
  213. $requestNum = AdverDeductLog::whereBetween('created_at', [$afterTime, Carbon::now()])
  214. ->where($where)
  215. ->count('id');
  216. // 如果请求次数过多,则发送消息给城市运营商
  217. // if ($requestNum > $time) {
  218. $remark = sprintf($remark, $requestNum);
  219. $adver = Advertising::find($adverId);
  220. // todo
  221. AdverAlarm::insert([
  222. 'uid' => $adver->uid, // 广告发布人
  223. 'province' => $adver->province,
  224. 'city' => $adver->city,
  225. 'district' => $adver->district,
  226. 'remark' => $remark
  227. ]);
  228. // }
  229. }
  230. private function checkCache($adverId, $keyStart, $keyEnd, $paramStart, $paramEnd, $where)
  231. {
  232. $prefix = sprintf('%sAND%s_', strtoupper($keyStart), strtoupper($keyEnd));
  233. $duration = Config::getValue("{$prefix}DURATION");
  234. $time = Config::getValue("{$prefix}TIME");
  235. $cachekey = sprintf(' %sAnd%s_%s_%s', $keyStart, $keyEnd, $paramStart, $paramEnd);
  236. $attri = [
  237. 'user' => '用户',
  238. 'shoper' => '商家',
  239. 'ip' => 'IP',
  240. 'industry' => '行业'
  241. ];
  242. $msgStart = $attri[$keyStart];
  243. $msgEnd = $attri[$keyEnd];
  244. $commonComment = sprintf('%s[%s]点击%s[%s],在%s分钟内', $msgStart, $paramStart, $msgEnd, $paramEnd, $duration);
  245. if (\Cache::has($cachekey)) {
  246. $usdtNum = \Cache::get($cachekey);
  247. if ($usdtNum >= $time) {
  248. // todo 发送消息给谁
  249. $this->sendMsg($adverId, $duration, $time, $where, $remark = $commonComment . "请求了[%s]次");
  250. // return showJsonErr('触发失败'); // todo 修改提示内容
  251. $remark = sprintf("[对接提醒:该消息内容正式上线会去除]: %s生效了%s次", $commonComment, $usdtNum);
  252. return $remark;
  253. } else {
  254. \Cache::increment($cachekey);
  255. }
  256. } else {
  257. \Cache::add($cachekey, 1, $duration);
  258. }
  259. return true;
  260. }
  261. /**
  262. * 获取广告扣费
  263. * @author lyh
  264. * @date 2019/4/19
  265. * @param $param
  266. * @return array
  267. * @description
  268. */
  269. private function deduction($param)
  270. {
  271. $param['industry'] = Industry::whereContent($param['user_industry'])->first()->id;
  272. // 请求记录
  273. AdverDeductLog::insertGetId([
  274. 'uid' => $param['uid'],
  275. 'shop_id' => $param['shop_id'],
  276. 'ip' => $param['ip'],
  277. 'industry' => $param['industry'],
  278. 'adver_id' => $param['adver_id'],
  279. 'is_free' => $param['is_free'],
  280. 'type' => $param['type'],
  281. 'shop_slave_id' => $param['shop_slave_id'],
  282. 'shop_master_id' => $param['shop_master_id']
  283. ]);
  284. // 对广告统计
  285. switch ($param['type']) {
  286. case 1:
  287. Advertising::whereId($param['adver_id'])->increment('click_num');
  288. break;
  289. case 2:
  290. Advertising::whereId($param['adver_id'])->increment('browse_num');
  291. break;
  292. case 3:
  293. Advertising::whereId($param['adver_id'])->increment('show_num');
  294. break;
  295. default;
  296. }
  297. $remark = [];
  298. if ($param['is_free'] == 0) {
  299. /**********根据同用户点击同一个商家,某个时间段M内,最多只能N次生效**********/
  300. $where = [['adver_id', '=', $param['adver_id']], ['uid', ' = ', $param['uid']], ['shop_id', ' = ', $param['shop_id']]];
  301. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'user', $keyEnd = 'shoper', $paramStart = $param['uid'], $paramEnd = $param['shop_id'], $where);
  302. if (is_string($re)) {
  303. $remark[] = $re;
  304. }
  305. /**********根据同IP点击同一个商家,某个时间段M内,最多只能N次生效**********/
  306. $where = [['adver_id', ' = ', $param['adver_id']], ['ip', ' = ', $param['ip']], ['shop_id', ' = ', $param['shop_id']]];
  307. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'ip', $keyEnd = 'shoper', $paramStart = $param['ip'], $paramEnd = $param['shop_id'], $where);
  308. if (is_string($re)) {
  309. $remark[] = $re;
  310. }
  311. /**********根据同一个用户,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效**********/
  312. $where = [['adver_id', ' = ', $param['adver_id']], ['uid', ' = ', $param['uid']], ['industry', ' = ', $param['industry']]];
  313. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'user', $keyEnd = 'industry', $paramStart = $param['uid'], $paramEnd = $param['industry'], $where);
  314. if (is_string($re)) {
  315. $remark[] = $re;
  316. }
  317. /**********根据同一个IP,点击同一个行业的某个商家,某个时间段M内,最多只能N次生效**********/
  318. $where = [['adver_id', ' = ', $param['adver_id']], ['ip', ' = ', $param['ip']], ['industry', ' = ', $param['industry']]];
  319. $re = $this->checkCache($adver_id = $param['adver_id'], $keyStart = 'ip', $keyEnd = 'industry', $paramStart = $param['ip'], $paramEnd = $param['industry'], $where);
  320. if (is_string($re)) {
  321. $remark[] = $re;
  322. }
  323. $adver = Advertising::find($param['adver_id']);
  324. if ($adver->residue_num <= 0) {
  325. $remark[] = "广告[ID:{$param['adver_id']},剩余次数为{$adver->residue_num}";
  326. }
  327. $res = Advertising::whereId($param['adver_id'])->decrement('residue_num');
  328. if (empty($res)) {
  329. $remark[] = "广告[id:{$param['adver_id']}]扣费失败";
  330. }
  331. // 广告过期
  332. $adver = Advertising::whereId($param['adver_id'])->select(['residue_num'])->first();
  333. if ($adver->residue_num <= 0) {
  334. Advertising::whereId($param['adver_id'])->update(['status' => 9]);
  335. // todo 告诉广告发布者广告到期?
  336. }
  337. }
  338. return $remark;
  339. }
  340. }