JuliangAccountReportChargeService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. namespace App\Service\Callback;
  3. use App\Service\Callback\Tiktok\TiktokEventReportService;
  4. use App\Service\Util\Support\Trace\TraceContext;
  5. use Illuminate\Support\Facades\DB;
  6. class JuliangAccountReportChargeService
  7. {
  8. private $uid;
  9. private $orderId;
  10. /**
  11. * @var TraceContext
  12. */
  13. private $traceContext;
  14. private $order;
  15. private $result;
  16. private $trackRecord;
  17. private $ranseInfo;
  18. private $callbackConfig;
  19. private $promotion;
  20. private $rateConfig;
  21. public function __construct($uid, $orderId, $traceInfo)
  22. {
  23. $this->uid = $uid;
  24. $this->orderId = $orderId;
  25. $this->traceContext = TraceContext::newFromParent($traceInfo);
  26. $this->result = [
  27. 'need_report' => true,
  28. 'config_rate' => 0,
  29. 'current_rate' => 0,
  30. ];
  31. }
  32. public function report() {
  33. $this->order = DB::table('orders')->where('id', $this->orderId)->first();
  34. $this->fillRanseInfo();
  35. $this->fillTrackInfo();
  36. $this->judgeOrderAlreadyDeal();
  37. $this->judgeIsFirstCharge();
  38. $this->judgeChargeMoney();
  39. $this->judgePromotionProtect();
  40. $this->judgeCallbackRate();
  41. dump($this->result);
  42. $this->reportJuliang();
  43. $this->saveRecord();
  44. }
  45. private function saveRecord() {
  46. if($this->result['save_record'] ?? true) {
  47. $now = date('Y-m-d H:i:s');
  48. if($this->result['info_type'] == 'promotion_protect') {
  49. if($this->result['report_success']) {
  50. $protectedRecord = DB::table('juliang_account_promotion_protect_record')
  51. ->where(['adv_promotion_id' => $this->trackRecord->adv_promotion_id,
  52. 'optimizer_uid' => $this->promotion->uid, 'is_enabeld' => 1])
  53. ->first();
  54. if($protectedRecord) {
  55. DB::table('juliang_account_promotion_protect_record')
  56. ->where(['id' => $protectedRecord->id])
  57. ->where('protected_num', '<', $this->callbackConfig->protect_num)
  58. ->increment('protected_num', 1, ['updated_at' => $now]);
  59. } else {
  60. DB::table('juliang_account_promotion_protect_record')
  61. ->insert([
  62. 'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
  63. 'optimizer_uid' => $this->promotion->uid,
  64. 'advertiser_id' => $this->callbackConfig->adv_account_id,
  65. 'protected_num' => 1,
  66. 'is_enabled' => 1,
  67. 'created_at' => $now,
  68. 'updated_at' => $now,
  69. ]);
  70. }
  71. }
  72. }
  73. $chargeRecord = DB::table('callback_report_charge_record')->where([
  74. 'order_no' => $this->order->trade_no
  75. ])->first();
  76. if(!$chargeRecord) {
  77. DB::table('callback_report_charge_record')
  78. ->insert([
  79. 'uid' => $this->uid,
  80. 'order_id' => $this->orderId,
  81. 'order_no' => $this->order->trade_no,
  82. 'optimizer_uid' => $this->promotion->uid,
  83. 'filter_type' => $this->result['info_type'] ?? 'ok',
  84. 'filter_reason' => $this->result['info_str'] ?? '允许回传',
  85. 'report_success' => intval($this->result['report_success'] ?? false),
  86. 'report_result' => \json_encode($this->result['report_result'] ?? []),
  87. 'created_at' => $now,
  88. 'updated_at' => $now,
  89. 'advertiser_id' => $this->trackRecord->advertiser_id,
  90. 'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
  91. 'config_rate' => $this->result['config_rate'],
  92. 'current_rate' => $this->result['current_rate'],
  93. ]);
  94. } else {
  95. DB::table('callback_report_charge_record')
  96. ->where(['id' => $chargeRecord->id])
  97. ->update([
  98. 'uid' => $this->uid,
  99. 'order_id' => $this->orderId,
  100. 'order_no' => $this->order->trade_no,
  101. 'optimizer_uid' => $this->promotion->uid,
  102. 'filter_type' => $this->result['info_type'] ?? 'ok',
  103. 'filter_reason' => $this->result['info_str'] ?? '允许回传',
  104. 'report_success' => intval($this->result['report_success'] ?? false),
  105. 'report_result' => \json_encode($this->result['report_result'] ?? []),
  106. 'updated_at' => $now,
  107. 'advertiser_id' => $this->trackRecord->advertiser_id,
  108. 'adv_promotion_id' => $this->trackRecord->adv_promotion_id,
  109. 'config_rate' => $this->result['config_rate'],
  110. 'current_rate' => $this->result['current_rate'],
  111. ]);
  112. }
  113. DB::table('juliang_account_rate_config_log')
  114. ->where(['id' => $this->rateConfig->id])
  115. ->update([
  116. 'report_count' => $this->rateConfig->report_count + intval($this->result['report_success']),
  117. 'unreport_count' => $this->rateConfig->report_count + intval(!$this->result['report_success']),
  118. 'total_count' => $this->rateConfig->total_count + 1,
  119. 'updated_at' => $now,
  120. ]);
  121. }
  122. }
  123. private function judgeCallbackRate(){
  124. if(!$this->result['need_report']) {
  125. return;
  126. }
  127. $this->rateConfig = DB::table('juliang_account_rate_config_log')
  128. ->where(['company_uid' => $this->promotion->uid,
  129. 'account_id' => $this->trackRecord->advertiser_id, 'is_enabled' => 1])
  130. ->first();
  131. if(!$this->rateConfig) {
  132. $this->result['need_report'] = false;
  133. $this->result['info_type'] = 'no_rate_config';
  134. $this->result['info_str'] = '没有可用的回传比率设置';
  135. return;
  136. }
  137. $configRate = $this->rateConfig->config_per;
  138. if(0 == $configRate) {
  139. $this->result['need_report'] = false;
  140. $this->result['info_type'] = 'rate_eq_0';
  141. $this->result['info_str'] = '设定比例为0不执行回传';
  142. return;
  143. }
  144. $currentTotalCount = $this->rateConfig->total_count;
  145. $currentReportCount = $this->rateConfig->report_count;
  146. if(0 == $currentTotalCount) {
  147. $currentRate = 0;
  148. } else {
  149. $currentRate = min(1, round($currentReportCount / $currentTotalCount, 4)) * 100;
  150. }
  151. if($currentRate <= $configRate) {
  152. $this->result['need_report'] = true;
  153. $this->result['config_rate'] = $configRate;
  154. $this->result['current_rate'] = $currentRate;
  155. } else {
  156. $this->result['need_report'] = false;
  157. $this->result['info_type'] = 'rate_filter';
  158. $this->result['info_type'] = '比率过滤';
  159. $this->result['config_rate'] = $configRate;
  160. $this->result['current_rate'] = $currentRate;
  161. }
  162. }
  163. private function judgePromotionProtect() {
  164. if(!$this->result['need_report']) {
  165. return;
  166. }
  167. if(0 == $this->callbackConfig->protect_num) {
  168. $this->result['need_report'] = true;
  169. } else {
  170. $advPromotionId = $this->trackRecord->adv_promotion_id;
  171. $protectedRecord = DB::table('juliang_account_promotion_protect_record')
  172. ->where(['adv_promotion_id' => $advPromotionId, 'optimizer_uid' => $this->promotion->uid, 'is_enabled' => 1])
  173. ->first();
  174. if(!$protectedRecord || $protectedRecord->protected_num < $this->callbackConfig->protect_num) {
  175. $this->result['need_report'] = true;
  176. $this->result['save_record'] = true;
  177. $this->result['save_record_type'] = 'protect_promotion';
  178. $this->result['info_type'] = 'promotion_protect';
  179. $this->result['info_str'] = '计划被保护';
  180. }
  181. }
  182. }
  183. private function judgeOrderAlreadyDeal() {
  184. if(!$this->result['need_report']) {
  185. return;
  186. }
  187. if(DB::table('callback_report_charge_record')
  188. ->where(['order_id' => $this->order->id])
  189. ->exists()) {
  190. $this->result['need_report'] = false;
  191. $this->result['save_record'] = false;
  192. $this->result['info_type'] = 'order_exists';
  193. $this->result['info_str'] = '订单已经处理';
  194. }
  195. }
  196. private function judgeChargeMoney() {
  197. if(!$this->result['need_report']) {
  198. return;
  199. }
  200. $moneyRange = $this->callbackConfig->moneyRange;
  201. if($this->order->price >= $moneyRange->min && $this->order->price <= $moneyRange->max) {
  202. $this->result['need_report'] = true;
  203. } else {
  204. $this->result['need_report'] = false;
  205. $this->result['info_type'] = 'money_filter';
  206. $this->result['info_str'] = sprintf('充值金额[%s元]不在回传区间范围内[%s - %s]', $this->order->price,
  207. $moneyRange->minStr, $moneyRange->maxStr);
  208. }
  209. }
  210. private function judgeIsFirstCharge() {
  211. if(!$this->result['need_report']) {
  212. return;
  213. }
  214. $isFirstCharge = DB::table('orders')
  215. ->where([
  216. 'uid' => $this->uid,
  217. 'status' => 'paid',
  218. 'promotion_id' => $this->trackRecord->ranse_id,
  219. ])->where('created_at', '>=', $this->trackRecord->ranse_start_at)
  220. ->where('created_at', '<=', $this->trackRecord->ranse_end_at)
  221. ->where('id', '<', $this->orderId)
  222. ->count() == 0;
  223. if($isFirstCharge) {
  224. $this->result['need_report'] = true;
  225. } else {
  226. $this->result['need_report'] = false;
  227. $this->result['info_type'] = 'neq_first_charge';
  228. $this->result['info_str'] = '非首充';
  229. }
  230. }
  231. private function fillRanseInfo() {
  232. $ranseId = $this->order->promotion_id;
  233. $this->promotion = DB::table('promotions')->where('id', $ranseId)->first();
  234. $this->callbackConfig = DB::table('juliang_account_callback_config')
  235. ->where(['id' => $this->promotion->callback_config_id])
  236. ->first();
  237. if(!$this->callbackConfig) {
  238. $this->result['need_report'] = false;
  239. $this->result['info_type'] = 'no_callback_config';
  240. $this->result['info_str'] = '没有回传配置';
  241. return;
  242. }
  243. if(0 == $this->callbackConfig->state) {
  244. $this->result['need_report'] = false;
  245. $this->result['info_type'] = 'callback_close';
  246. $this->result['info_str'] = '回传配置关闭';
  247. return;
  248. }
  249. $min = max(0, $this->callbackConfig->min_money);
  250. if (0 >= $this->callbackConfig->max_money) {
  251. $max = PHP_INT_MAX;
  252. $maxStr = '不限';
  253. } else {
  254. $max = $this->callbackConfig->max_money;
  255. $maxStr = $max . '元';
  256. }
  257. $minStr = $min . '元';
  258. $this->callbackConfig->moneyRange = (object)compact('min', 'max', 'minStr', 'maxStr');
  259. }
  260. private function reportJuliang() {
  261. if(!$this->result['need_report']) {
  262. return;
  263. }
  264. $service = new TiktokEventReportService;
  265. $reportResult = $service->reportCharge((object)[
  266. 'callback' => $this->trackRecord->callback
  267. ]);
  268. $reportContent = \json_decode($reportResult->content, true);
  269. if($reportResult['result'] && 0 == $reportContent['code']) {
  270. $this->result['report_success'] = true;
  271. } else {
  272. $this->result['report_success'] = false;
  273. }
  274. $this->result['report_result'] = $reportResult;
  275. $this->result['need_report'] = false;
  276. }
  277. private function fillTrackInfo() {
  278. if(!$this->result['need_report']) {
  279. return;
  280. }
  281. $this->trackRecord = DB::table('callback_report_ranse_record')
  282. ->where(['uid' => $this->uid, 'ranse_id' => $this->order->promotion_id])
  283. ->where('ranse_start_at', '<=', $this->order->created_at)
  284. ->where('ranse_end_at', '>=', $this->order->created_at)
  285. ->orderBy('id', 'desc')
  286. ->first();
  287. if(!$this->trackRecord) {
  288. $this->result['need_report'] = false;
  289. $this->result['info_type'] = 'no_track_info';
  290. $this->result['info_str'] = '无匹配用户';
  291. }
  292. }
  293. }