OrderService.php 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. <?php
  2. namespace App\Services\Order;
  3. use App\Consts\BaseConst;
  4. use App\Consts\ErrorConst;
  5. use App\Dao\Order\OrderDao;
  6. use App\Facade\Site;
  7. use App\Models\Order\Order;
  8. use App\Libs\Utils;
  9. use App\Services\OpenApi\OpenService;
  10. use GuzzleHttp\Client;
  11. use Illuminate\Support\Facades\DB;
  12. use Illuminate\Support\Facades\Log;
  13. use Illuminate\Support\Facades\Redis;
  14. use Illuminate\Support\Facades\Response;
  15. use Vinkla\Hashids\Facades\Hashids;
  16. use App\Exceptions\ApiException;
  17. class OrderService
  18. {
  19. protected $orderDao;
  20. protected $openService;
  21. public function __construct(
  22. OrderDao $orderDao,
  23. OpenService $openService
  24. )
  25. {
  26. $this->orderDao = $orderDao;
  27. $this->openService = $openService;
  28. }
  29. public static function getByTradeNo($trade_no)
  30. {
  31. return Order::getByTradeNo($trade_no);
  32. }
  33. public static function getByTikTokOrderId($orderId)
  34. {
  35. return Order::getByTikTokOrderId($orderId);
  36. }
  37. public static function getOrderList($uid, $page_size)
  38. {
  39. return Order::getOrderList($uid, $page_size);
  40. }
  41. public static function getSuccessOrderList($uid, $page_size)
  42. {
  43. return Order::select('id', 'price', 'created_at', 'status', 'trade_no', 'order_type', 'pay_merchant_id')->where('uid', $uid)->where('status', 'PAID')->where('from_type', '!=', 'order_add')->orderBy('id', 'desc')->paginate($page_size);
  44. }
  45. /**
  46. * 获取充值订单
  47. *
  48. * @param $uid
  49. * @return mixed
  50. */
  51. public static function totalChargeList($uid)
  52. {
  53. return Order::join('products', 'orders.product_id', '=', 'products.id')
  54. ->select('products.price', 'products.given')
  55. ->where('orders.order_type', 'RECHARGE')
  56. ->where('orders.status', 'PAID')
  57. ->where('orders.uid', $uid)
  58. ->get();
  59. }
  60. static function create_order($data)
  61. {
  62. $insert_data = array();
  63. $insert_data['uid'] = isset($data['uid']) ? $data['uid'] : '';
  64. $insert_data['price'] = isset($data['price']) ? $data['price'] : '';
  65. $insert_data['status'] = isset($data['status']) ? $data['status'] : '';
  66. $insert_data['pay_num'] = isset($data['pay_num']) ? $data['pay_num'] : '';
  67. $insert_data['product_id'] = isset($data['product_id']) ? $data['product_id'] : '';
  68. $insert_data['distribution_channel_id'] = isset($data['distribution_channel_id']) ? $data['distribution_channel_id'] : '';
  69. $insert_data['trade_no'] = isset($data['trade_no']) ? $data['trade_no'] : '';
  70. $insert_data['pay_merchant_source'] = isset($data['pay_merchant_source']) ? $data['pay_merchant_source'] : '';
  71. $insert_data['pay_merchant_id'] = isset($data['pay_merchant_id']) ? $data['pay_merchant_id'] : '';
  72. $insert_data['transaction_id'] = isset($data['transaction_id']) ? $data['transaction_id'] : '';
  73. $insert_data['from_bid'] = isset($data['from_bid']) ? $data['from_bid'] : '';
  74. $insert_data['order_type'] = isset($data['order_type']) ? $data['order_type'] : '';
  75. $insert_data['pay_end_at'] = isset($data['pay_end_at']) ? $data['pay_end_at'] : null;
  76. $insert_data['create_ip'] = isset($data['create_ip']) ? $data['create_ip'] : '';
  77. $insert_data['send_order_id'] = isset($data['send_order_id']) ? $data['send_order_id'] : '';
  78. $result = Order::create_order($insert_data);
  79. return $result;
  80. }
  81. /**
  82. * 判断用户首充
  83. */
  84. public static function judgeUserFirstRecharge($uid)
  85. {
  86. $user_recharge = Order::where([
  87. ['status', '=', 'PAID'],
  88. ['uid', '=', $uid]
  89. ])->first();
  90. if (!$user_recharge) {
  91. return true;
  92. }
  93. return false;
  94. }
  95. /**
  96. * 是否是付费用户
  97. *
  98. * @param $uid
  99. * @return bool
  100. */
  101. public static function isPaidUser($uid)
  102. {
  103. if (empty($uid)) {
  104. return false;
  105. }
  106. $result = Order::select('id')->where('uid', $uid)->where('status', 'PAID')->first();
  107. if ($result && $result->id) {
  108. return true;
  109. }
  110. return false;
  111. }
  112. /**
  113. * 是否是付费用户
  114. *
  115. * @param $openid
  116. * @return bool
  117. */
  118. public static function isPaidUserByOpenid($openid)
  119. {
  120. if (empty($openid)) {
  121. return false;
  122. }
  123. $result = Order::join('users', 'users.id', '=', 'orders.uid')
  124. ->select('orders.id')
  125. ->where('users.openid', $openid)
  126. ->where('orders.status', 'PAID')
  127. ->first();
  128. if ($result && $result->id) {
  129. return true;
  130. }
  131. return false;
  132. }
  133. public static function getUserChargeTimes($uid)
  134. {
  135. return Order::getUserChargeTimes($uid);
  136. }
  137. public static function getChargeNum($uid)
  138. {
  139. return Order::where('uid', $uid)->where('status', 'PAID')->count();
  140. }
  141. public static function getChargeNumByOpenId($openid)
  142. {
  143. $res = Order::join('users', 'orders.uid', 'users.id')
  144. ->where('users.openid', $openid)
  145. ->where('orders.status', 'PAID')
  146. ->sum('orders.price');
  147. return $res ? $res : 0;
  148. }
  149. // 订单明细
  150. public function getOrderData($data)
  151. {
  152. $send_order_id = getProp($data, 'send_order_id');
  153. $trade_no = getProp($data, 'trade_no');
  154. $transaction_id = getProp($data, 'transaction_id');
  155. $date_range = getProp($data, 'date_range');
  156. $query = Order::where('status', 'PAID')->select('uid', 'price', 'distribution_channel_id', 'trade_no', 'transaction_id',
  157. 'send_order_id', 'created_at', 'order_type');
  158. if ($send_order_id) {
  159. $query->where('send_order_id', $send_order_id);
  160. }
  161. if ($trade_no) {
  162. $query->where('trade_no', $trade_no);
  163. }
  164. if ($transaction_id) {
  165. $query->where('transaction_id', $transaction_id);
  166. }
  167. //时间周期筛选
  168. if ($date_range) {
  169. $date_range = explode(',', $date_range);
  170. if (count($date_range) == 2) {
  171. if ($date_range[0]) {
  172. $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
  173. $query->where('created_at', '>=', $start);
  174. }
  175. if ($date_range[1]) {
  176. $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
  177. $query->where('created_at', '<=', $end);
  178. }
  179. }
  180. }
  181. return $query->get();
  182. }
  183. /**
  184. * @param $data
  185. * @return mixed
  186. * @throws ApiException
  187. */
  188. public function orderList($data)
  189. {
  190. // 是否导出
  191. $export = (int)getProp($data, 'export');
  192. if ($export) {
  193. return $this->exportOrderList($data);
  194. }
  195. return $this->orderListData($data);
  196. }
  197. /**
  198. * @param $param
  199. * @return mixed
  200. * @throws ApiException
  201. */
  202. public function exportOrderList($param)
  203. {
  204. $param['all'] = 1;
  205. $result = $this->orderListData($param);
  206. if ($result->isEmpty()) {
  207. Utils::throwError(ErrorConst::NO_DATA_FOR_EXPORT);
  208. }
  209. // 设置超时时间
  210. set_time_limit(0);
  211. // 组装下载数据
  212. $data = [];
  213. foreach ($result as $order) {
  214. // 支付状态说明
  215. $status = getProp($order, 'status');
  216. $statusDesc = $status === 'PAID' ? '已支付' : '未支付';
  217. // 订单类型说明
  218. $orderType = getProp($order, 'order_type');
  219. switch ($orderType) {
  220. case 'YEAR':
  221. $orderTypeDesc = '包年';
  222. break;
  223. case 'QUARTER':
  224. $orderTypeDesc = '包季';
  225. break;
  226. case 'MONTH':
  227. $orderTypeDesc = '包月';
  228. break;
  229. case 'WEEK':
  230. $orderTypeDesc = '包周';
  231. break;
  232. case 'RECHARGE':
  233. $orderTypeDesc = '普通充值';
  234. break;
  235. default:
  236. $orderTypeDesc = '其他充值';
  237. break;
  238. }
  239. $data[] = [
  240. getProp($order, 'distribution_channel_id'),
  241. getProp($order, 'trade_no'),
  242. getProp($order, 'uid'),
  243. getProp($order, 'register_ip'),
  244. getProp($order, 'register_time'),
  245. trim($orderTypeDesc),
  246. trim(getProp($order, 'book_name')),
  247. trim(getProp($order, 'send_order_name')),
  248. getProp($order, 'send_order_id'),
  249. getProp($order, 'price'),
  250. $statusDesc,
  251. $order->created_at->format('Y-m-d H:i:s'),
  252. $order->updated_at->format('Y-m-d H:i:s'),
  253. ];
  254. }
  255. $headers = [
  256. '站点ID', '订单编号', '用户ID', '注册IP', '注册时间', '充值类型', '充值书籍',
  257. '派单名称', '派单ID', '充值金额(元)', '订单状态', '创建时间', '更新时间'
  258. ];
  259. // 导出csv格式
  260. exportCsv('订单-' . date("YmdHis"), $headers, $data);
  261. // exportExcel($data, $headers, '订单-' . date("YmdHis"));
  262. // exit();
  263. }
  264. /**
  265. * 订单数据
  266. *
  267. * @param $data
  268. * @return mixed
  269. * @throws ApiException
  270. */
  271. private function orderListData($data)
  272. {
  273. // 获取当前站点
  274. $channelId = Site::getCurrentChannelId();
  275. if (empty($channelId)) {
  276. Utils::throwError(ErrorConst::CHANNEL_ID_INVALID);
  277. }
  278. $uid = Site::getUid();
  279. $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();
  280. if ($sub_channel_ids) { // 主账号
  281. // 未筛选推广员则取全站数据
  282. $channel_ids = array_merge([$channelId], $sub_channel_ids);
  283. $promote_user = getProp($data, 'promote_user');
  284. if ($promote_user) { // 推广员筛选
  285. // 如果推广员符合子账号条件则取对应子站点数据
  286. $distribution_channel_id = DB::table('channel_users')->leftJoin('distribution_channels', 'distribution_channels.channel_user_id', 'channel_users.id')
  287. ->where('channel_users.nickname', 'like', '%' . $promote_user . '%')->value('distribution_channels.id');
  288. if (in_array($distribution_channel_id, $channel_ids)) $channel_ids = [$distribution_channel_id];
  289. else $channel_ids = [-1];
  290. }
  291. } else { // 默认获取本站点数据
  292. $channel_ids = [$channelId];
  293. }
  294. // 站点对应
  295. $data['channel_id'] = $channel_ids;
  296. // 返回数据
  297. return $this->orderDao->orderList($data);
  298. }
  299. // 用户数据
  300. public function getUserData($data)
  301. {
  302. $uid = getProp($data, 'uid');
  303. $send_order_id = getProp($data, 'send_order_id');
  304. $from_uid = getProp($data, 'from_uid');
  305. $date_range = getProp($data, 'date_range');
  306. $query = DB::table('users')->select('id', 'distribution_channel_id', 'send_order_id', 'from_uid', 'balance', 'charge_balance',
  307. 'reward_balance', 'nickname', 'vip_limit_date', 'token', 'created_at');
  308. if ($uid) {
  309. $query->where('id', $uid);
  310. }
  311. if ($send_order_id) {
  312. $query->where('send_order_id', $send_order_id);
  313. }
  314. if ($from_uid) {
  315. $query->where('from_uid', $from_uid);
  316. }
  317. //时间周期筛选
  318. if ($date_range) {
  319. $date_range = explode(',', $date_range);
  320. if (count($date_range) == 2) {
  321. if ($date_range[0]) {
  322. $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
  323. $query->where('created_at', '>=', $start);
  324. }
  325. if ($date_range[1]) {
  326. $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
  327. $query->where('created_at', '<=', $end);
  328. }
  329. }
  330. }
  331. return $query->get();
  332. }
  333. // 回传日志
  334. public function getReportData($data, $all = false)
  335. {
  336. $distribution_channel_id = Site::getCurrentChannelId();
  337. $uid = getProp($data, 'uid');
  338. $send_order_id = getProp($data, 'send_order_id');
  339. $advertiserid = getProp($data, 'advertiser_id');
  340. $promotionid = getProp($data, 'promotion_id');
  341. $register_date_range = getProp($data, 'register_date_range');
  342. $pay_date_range = getProp($data, 'pay_date_range');
  343. // 列表
  344. $query = DB::table('orders')->leftJoin('users', 'users.id', 'orders.uid')
  345. ->leftJoin('dy_report_logs', 'dy_report_logs.trade_no', 'orders.trade_no')
  346. ->leftJoin('send_orders', 'send_orders.id', 'orders.send_order_id')
  347. ->where('send_orders.distribution_channel_id', $distribution_channel_id)
  348. ->where('orders.status', 'PAID');
  349. // 汇总
  350. $query2 = DB::table('orders')->leftJoin('users', 'users.id', 'orders.uid')
  351. ->leftJoin('dy_report_logs', 'dy_report_logs.trade_no', 'orders.trade_no')
  352. ->leftJoin('send_orders', 'send_orders.id', 'orders.send_order_id')
  353. ->where('send_orders.distribution_channel_id', $distribution_channel_id)
  354. ->where('orders.status', 'PAID');
  355. if ($uid) {
  356. $query->where('orders.uid', $uid);
  357. $query2->where('orders.uid', $uid);
  358. }
  359. if ($send_order_id) {
  360. $query->where('orders.send_order_id', $send_order_id);
  361. $query2->where('orders.send_order_id', $send_order_id);
  362. }
  363. if ($advertiserid) {
  364. $query->where('dy_report_logs.advertiserid', $advertiserid);
  365. $query2->where('dy_report_logs.advertiserid', $advertiserid);
  366. }
  367. if ($promotionid) {
  368. $query->where('dy_report_logs.promotionid', $promotionid);
  369. $query2->where('dy_report_logs.promotionid', $promotionid);
  370. }
  371. // 注册时间周期筛选
  372. if ($register_date_range) {
  373. $date_range = explode(',', $register_date_range);
  374. if (count($date_range) == 2) {
  375. if ($date_range[0]) {
  376. $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
  377. $query->where('users.created_at', '>=', $start);
  378. $query2->where('users.created_at', '>=', $start);
  379. }
  380. if ($date_range[1]) {
  381. $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
  382. $query->where('users.created_at', '<=', $end);
  383. $query2->where('users.created_at', '<=', $end);
  384. }
  385. }
  386. }
  387. // 支付时间周期筛选
  388. if ($pay_date_range) {
  389. $date_range = explode(',', $pay_date_range);
  390. if (count($date_range) == 2) {
  391. if ($date_range[0]) {
  392. $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
  393. $query->where('orders.pay_end_at', '>=', $start);
  394. $query2->where('orders.pay_end_at', '>=', $start);
  395. }
  396. if ($date_range[1]) {
  397. $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
  398. $query->where('orders.pay_end_at', '<=', $end);
  399. $query2->where('orders.pay_end_at', '<=', $end);
  400. }
  401. }
  402. }
  403. if ($all) return $query->get();
  404. // 获取分页数据
  405. $result = $query->select('dy_report_logs.id', 'dy_report_logs.advertiserid', 'dy_report_logs.promotionid',
  406. 'dy_report_logs.callback_result', 'dy_report_logs.remark', 'users.created_at as register_time',
  407. 'orders.send_order_id', 'orders.trade_no', 'orders.uid', 'users.register_ip', 'orders.pay_end_at as pay_time', 'orders.price',
  408. 'send_orders.report_percent', 'send_orders.report_receive_num', 'send_orders.report_post_num')
  409. ->orderBy('orders.pay_end_at', 'desc')->orderBy('users.created_at', 'desc')->paginate();
  410. // 获取汇总数据
  411. $all_result = $query2->selectRaw("sum(orders.price) as total_pay_amount, count(orders.id) as order_num,
  412. (select count(o.id) from orders as o where o.pay_num = 1 and o.trade_no = orders.trade_no) as first_order_num,
  413. (select sum(o.price) from orders as o where o.pay_num = 1 and o.trade_no = orders.trade_no) as first_total_pay_amount")->groupBy('orders.trade_no')->get();
  414. $header = [
  415. 'total_pay_amount' => 0,
  416. 'order_num' => 0,
  417. 'first_order_num' => 0,
  418. 'first_total_pay_amount' => 0,
  419. ];
  420. foreach ($all_result as $item) {
  421. $header['total_pay_amount'] += getProp($item, 'total_pay_amount') ? getProp($item, 'total_pay_amount') : 0;
  422. $header['order_num'] += getProp($item, 'order_num');
  423. $header['first_order_num'] += getProp($item, 'first_order_num');
  424. $header['first_total_pay_amount'] += getProp($item, 'first_total_pay_amount') ? getProp($item, 'first_total_pay_amount') : 0;
  425. }
  426. $header['total_pay_amount'] = round($header['total_pay_amount'], 2);
  427. $header['first_total_pay_amount'] = round($header['first_total_pay_amount'], 2);
  428. $result->header = $header;
  429. return $result;
  430. }
  431. /**
  432. * 导出回传日志
  433. *
  434. * @param $data
  435. * @throws ApiException
  436. */
  437. public function exportReportData($data)
  438. {
  439. $list = $this->getReportData($data, true);
  440. if (empty($list)) {
  441. Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
  442. }
  443. $result = [];
  444. foreach ($list as $item) {
  445. $callback_result = getProp($item, 'callback_result');
  446. $remark = getProp($item, 'remark');
  447. $remark = $remark == '' ? '比例回传' : $remark;
  448. $result[] = [
  449. 'report_id' => getProp($item, 'id'),
  450. 'advertiserid' => getProp($item, 'advertiserid'),
  451. 'promotionid' => getProp($item, 'promotionid'),
  452. 'callback_result' => $callback_result,
  453. 'remark' => $remark,
  454. 'uid' => getProp($item, 'uid'),
  455. 'register_ip' => getProp($item, 'register_ip'),
  456. 'register_time' => transDate(getProp($item, 'register_time')),
  457. 'pay_time' => transDate(getProp($item, 'pay_time')),
  458. 'price' => getProp($item, 'price'),
  459. 'current_percent' => getProp($item, 'report_receive_num') ? sprintf('%.1f', getProp($item, 'report_post_num') / getProp($item, 'report_receive_num') * 100) : 0,
  460. 'report_percent' => getProp($item, 'report_percent'),
  461. ];
  462. }
  463. $headers = ['ID', '广告账户ID', '广告ID', '回传状态', '备注', '用户ID', '注册IP', '注册时间', '充值时间', '充值金额', '已回传/待回传(%)', '应用回传比例(%)'];
  464. exportFileCsv($headers, $result, '回传日志-' . date('YmdHis'));
  465. }
  466. // 派单列表
  467. public function getSendOrderData($data, $all = false)
  468. {
  469. $distribution_channel_id = Site::getCurrentChannelId();
  470. $send_order_id = getProp($data, 'send_order_id');
  471. $send_order_name = getProp($data, 'send_order_name');
  472. $date_range = getProp($data, 'date_range');
  473. $uid = Site::getUid();
  474. $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();
  475. if ($sub_channel_ids) { // 主账号
  476. // 未筛选推广员则取全站数据
  477. $channel_ids = array_merge([$distribution_channel_id], $sub_channel_ids);
  478. $promote_user = getProp($data, 'promote_user');
  479. if ($promote_user) { // 推广员筛选
  480. // 如果推广员符合子账号条件则取对应子站点数据
  481. $distribution_channel_id = DB::table('channel_users')->leftJoin('distribution_channels', 'distribution_channels.channel_user_id', 'channel_users.id')
  482. ->where('channel_users.nickname', 'like', '%' . $promote_user . '%')->value('distribution_channels.id');
  483. if (in_array($distribution_channel_id, $channel_ids)) $channel_ids = [$distribution_channel_id];
  484. else $channel_ids = [-1];
  485. }
  486. } else { // 默认获取本站点数据
  487. $channel_ids = [$distribution_channel_id];
  488. }
  489. $query = DB::table('send_orders')->leftJoin('books', 'books.id', 'send_orders.book_id')
  490. ->leftJoin('distribution_channels', 'distribution_channels.id', 'send_orders.distribution_channel_id')
  491. ->leftJoin('channel_users', 'channel_users.id', 'distribution_channels.channel_user_id')
  492. ->leftJoin('channel_templates', 'channel_templates.id', 'send_orders.pay_id')
  493. ->whereIn('send_orders.distribution_channel_id', $channel_ids)->select('send_orders.*', 'books.cover', 'books.category_name', 'books.intro',
  494. 'channel_templates.template_name', 'channel_templates.id as template_id', 'channel_users.nickname');
  495. if ($send_order_id) {
  496. $query->where('send_orders.id', $send_order_id);
  497. }
  498. if ($send_order_name) {
  499. $query->where('send_orders.name', 'like', '%' . $send_order_name . '%');
  500. }
  501. // 支付时间周期筛选
  502. if ($date_range) {
  503. $date_range = explode(',', $date_range);
  504. if (count($date_range) == 2) {
  505. if ($date_range[0]) {
  506. $start = date('Y-m-d 00:00:00', strtotime($date_range[0]));
  507. $query->where('send_orders.created_at', '>=', $start);
  508. }
  509. if ($date_range[1]) {
  510. $end = date('Y-m-d 23:59:59', strtotime($date_range[1]));
  511. $query->where('send_orders.created_at', '<=', $end);
  512. }
  513. }
  514. }
  515. if ($all) return $query->orderBy('send_orders.created_at', 'desc')->get();
  516. $result = $query->orderBy('send_orders.created_at', 'desc')->paginate();
  517. $result->channel_id = $distribution_channel_id;
  518. return $result;
  519. }
  520. // 导出派单数据
  521. public function exportSendOrderData($data)
  522. {
  523. $list = $this->getSendOrderData($data, true);
  524. if (empty($list)) {
  525. Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
  526. }
  527. $start = date('Y-m-d 00:00:00');
  528. $result = [];
  529. foreach ($list as $item) {
  530. $send_order_id = getProp($item, 'id');
  531. // 获取今日注册人数
  532. $today_register_num = DB::table('users')->where('send_order_id', $send_order_id)->where('created_at', '>', $start)->count('id');
  533. // 今日订单信息
  534. $today_order = DB::table('orders')->leftJoin('users', 'orders.uid', 'users.id')->where('users.send_order_id', $send_order_id)
  535. ->where('orders.send_order_id', $send_order_id)->where('users.created_at', '>', $start)
  536. ->where('orders.status', 'PAID')->where('orders.created_at', '>', $start)->selectRaw("count(orders.pay_num = 1) as register_pay_num, sum(orders.price) as register_pay_amount")->get();
  537. $today_register_pay_num = getProp($today_order[0], 'register_pay_num');
  538. $today_register_pay_amount = getProp($today_order[0], 'register_pay_amount') ? getProp($today_order[0], 'register_pay_amount') : 0;
  539. // 派单uv
  540. $uv = Redis::scard('send_order_uv_' . $send_order_id);
  541. $uv = $uv ? $uv : 0;
  542. // 累计订单信息
  543. $total_order = DB::table('orders')->where('orders.send_order_id', $send_order_id)->where('orders.status', 'PAID')
  544. ->selectRaw("count(distinct orders.uid) as pay_num, sum(orders.price) as pay_amount")->get();
  545. $total_pay_num = getProp($total_order[0], 'pay_num');
  546. $total_pay_amount = getProp($total_order[0], 'pay_amount') ? getProp($total_order[0], 'pay_amount') : 0;
  547. $result[] = [
  548. 'send_order_id' => $send_order_id,
  549. 'send_order_name' => getProp($item, 'name'),
  550. 'book_name' => getProp($item, 'book_name'),
  551. 'chapter_name' => getProp($item, 'chapter_name'),
  552. 'today_register_num' => $today_register_num,
  553. 'today_register_pay_num' => $today_register_pay_num,
  554. 'today_register_pay_amount' => $today_register_pay_amount,
  555. 'total_register_num' => getProp($item, 'total_register_num') + $today_register_num,
  556. 'total_register_pay_num' => getProp($item, 'total_register_pay_num') + $today_register_pay_num,
  557. 'total_register_pay_amount' => getProp($item, 'total_register_pay_amount') + $today_register_pay_amount,
  558. 'uv' => $uv,
  559. 'total_pay_num' => $total_pay_num,
  560. 'total_pay_amount' => $total_pay_amount,
  561. 'total_cost' => getProp($item, 'cost'),
  562. 'total_profit' => getProp($item, 'total_profit'),
  563. 'total_profit_percent' => (float)getProp($item, 'cost') ? round($total_pay_amount / getProp($item, 'cost') * 100, 2) : 0,
  564. 'created_at' => transDate(getProp($item, 'created_at')),
  565. ];
  566. }
  567. $headers = ['派单id', '派单名称', '书名', '章节名', '今日新增注册', '今日注册充值人数', '今日注册用户充值(元)', '累计注册', '累计注册充值人数', '累计注册用户充值(元)', 'UV', '累计充值人数', '累计充值金额(元)', '累计成本(元)', '累计盈利(元)', '累计回本(%)', '创建时间'];
  568. exportFileCsv($headers, $result, '派单数据-' . date('YmdHis'));
  569. }
  570. /**
  571. * 获取派单链接适用的模板
  572. *
  573. * @param $data
  574. * @return array
  575. */
  576. public function getSendOrderTemplates($data)
  577. {
  578. $distribution_channel_id = Site::getCurrentChannelId();
  579. return DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('is_enable', 1)->whereIn('user_scope', [1, 3])
  580. ->select('id as template_id', 'template_name')->get()->map(function ($value) {
  581. return (array)$value;
  582. })->toArray();
  583. }
  584. /**
  585. * 编辑派单链接
  586. *
  587. * @param $data
  588. * @return mixed
  589. * @throws \App\Exceptions\ApiException
  590. * @throws \GuzzleHttp\Exception\GuzzleException
  591. */
  592. public function editSendOrderUrl($data)
  593. {
  594. $send_order_id = getProp($data, 'send_order_id');
  595. $send_order_name = getProp($data, 'send_order_name');
  596. $report_percent = getProp($data, 'report_percent', 0);
  597. $template_id = getProp($data, 'template_id');
  598. if (!is_numeric($report_percent)) {
  599. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  600. }
  601. // 获取派单信息
  602. $send_order = DB::table('send_orders')->where('id', $send_order_id)->first();
  603. if (!$send_order) {
  604. Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
  605. }
  606. $distribution_channel_id = getProp($send_order, 'distribution_channel_id');
  607. if ((int)$distribution_channel_id !== (int)Site::getCurrentChannelId()) {
  608. Utils::throwError(ErrorConst::CHANNEL_NOT_EXIST);
  609. }
  610. if ($template_id) {
  611. $template = DB::table('channel_templates')->where('id', $template_id)->where('distribution_channel_id', $distribution_channel_id)->first();
  612. if (!$template) {
  613. Utils::throwError('1002:该充值模板不存在!');
  614. }
  615. }
  616. // 判断派单名称是否重复
  617. $id = DB::table('send_orders')->where('name', $send_order_name)->value('id');
  618. if ($id && $id != $send_order_id) {
  619. Utils::throwError(ErrorConst::SEND_ORDER_NAME_INVALID);
  620. }
  621. // 更新派单信息
  622. $update_data = [
  623. 'name' => $send_order_name,
  624. 'report_percent' => $report_percent,
  625. 'updated_at' => date('Y-m-d H:i:s')
  626. ];
  627. if (getProp($send_order, 'report_percent') != $report_percent) {
  628. $update_data['report_receive_num'] = 0;
  629. $update_data['report_post_num'] = 0;
  630. }
  631. if ($template_id) {
  632. $update_data['pay_id'] = $template_id;
  633. }
  634. // 调用抖音三方库生成分享链接(非本地模式调用)
  635. if (env('APP_ENV') != 'local') {
  636. $bid = getProp($send_order, 'book_id');
  637. $cid = getProp($send_order, 'chapter_id');
  638. $distribution_channel_id = getProp($send_order, 'distribution_channel_id');
  639. // 重新生成派单链接
  640. $arr = [
  641. 'bid' => $bid ? Hashids::encode($bid) : '',
  642. 'cid' => $cid ? Hashids::encode($cid) : '',
  643. 'send_order_id' => $send_order_id,
  644. 'distribution_channel_id' => $distribution_channel_id,
  645. 'jump' => 'no',
  646. ];
  647. $json = json_encode($arr, 256);
  648. $path = 'pages/reader/index'; // 固定页面(阅读页)
  649. $instance = $this->openService->getInstance(['sandbox' => env('DOUYIN_APP_SANDBOX')]);
  650. $urlLink = $instance->generateShareLink($path, $json);
  651. if (!isset($urlLink['url_link'])) {
  652. Utils::throwError(ErrorConst::SYS_EXCEPTION);
  653. }
  654. $update_data['send_order_url'] = $urlLink['url_link'];
  655. }
  656. return DB::table('send_orders')->where('id', $send_order_id)->update($update_data);
  657. }
  658. // 派单日数据
  659. public function getSendOrderDayData($data, $all = false)
  660. {
  661. $distribution_channel_id = Site::getCurrentChannelId();
  662. $send_order_id = getProp($data, 'send_order_id');
  663. $date_range = getProp($data, 'date_range');
  664. // 获取主账号下的子账号站点
  665. $uid = Site::getUid();
  666. $sub_channel_ids = DB::table('channel_user_mappings')->where('master_uid', $uid)->select('sub_channel_id')->get()->pluck('sub_channel_id')->toArray();
  667. $send_order_info = DB::table('send_orders')->leftJoin('books', 'books.id', 'send_orders.book_id')
  668. ->leftJoin('book_configs', 'book_configs.bid', 'send_orders.book_id')
  669. ->leftJoin('books as b1', 'b1.id', 'book_configs.origin_bid')
  670. ->where('send_orders.id', $send_order_id)->select('send_orders.id', 'send_orders.name', 'send_orders.distribution_channel_id',
  671. 'books.name as book_name', 'b1.name as origin_book_name')->first();
  672. if (!$send_order_info) {
  673. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  674. }
  675. $send_order_name = getProp($send_order_info, 'name');
  676. $send_order_channel_id = getProp($send_order_info, 'distribution_channel_id');
  677. if (!in_array($send_order_channel_id, $sub_channel_ids) && $send_order_channel_id != $distribution_channel_id) {
  678. Utils::throwError('1002: 没有该站点的查看权限!');
  679. }
  680. $query = DB::table('send_order_day_stats')->where('send_order_id', $send_order_id);
  681. // 默认时间范围是最近一个月
  682. $end = date('Y-m-d', strtotime('-1 day'));
  683. $start = date('Y-m-d', strtotime('-31 day'));
  684. if ($date_range) {
  685. $date_range = explode(',', $date_range);
  686. if (count($date_range) == 2) {
  687. $start = $date_range[0];
  688. $end = $date_range[1];
  689. if (strtotime($end) - strtotime($start) > 86400 * 30) {
  690. Utils::throwError(ErrorConst::DATE_RANGE_OVER_MONTH);
  691. }
  692. }
  693. }
  694. $query->whereBetween('day', [$start, $end]);
  695. if ($all) {
  696. $result = $query->orderBy('day', 'desc')->get();
  697. return [$result, $start, $send_order_info];
  698. }
  699. $result = $query->orderBy('day', 'desc')->paginate(5);
  700. $result->send_order_name = $send_order_name;
  701. $result->start_date = $start;
  702. $result->send_order_id = $send_order_id;
  703. return $result;
  704. }
  705. // 导出派单日数据
  706. public function exportSendOrderDayData($data)
  707. {
  708. [$list, $start, $send_order_info] = $this->getSendOrderDayData($data, true);
  709. if (empty($list)) {
  710. Utils::throwError(ErrorConst::RECORD_NOT_EXIST);
  711. }
  712. $send_order_id = getProp($send_order_info, 'id');
  713. $send_order_name = getProp($send_order_info, 'name');
  714. $end = date('Y-m-d', strtotime($start . ' +120 day'));
  715. // 获取时间段内的数据
  716. $period_data = DB::table('send_order_recharge_day_stats')->where('send_order_id', $send_order_id)->whereBetween('register_date', [$start, $end])
  717. ->orderBy('pay_date')->get()->map(function ($value) {
  718. return (array)$value;
  719. })->toArray();
  720. $day_stats = [];
  721. // 注册用户派单日数据初始化
  722. foreach ($list as $item) {
  723. $day_stats[getProp($item, 'day')] = [];
  724. }
  725. // 将已有数据填充
  726. foreach ($period_data as $item) {
  727. $day = $item['register_date'];
  728. $day_stats[$day]['t' . $item['day_num']] = [
  729. 'register_pay_amount' => $item['pay_amount'],
  730. 'total_register_pay_amount' => $item['day_num'] == 0 ? $item['pay_amount'] : 0, // t0日有金额,其他日在下个循环累加
  731. ];
  732. }
  733. $i_arr = range(0, 29); // 第1~30天
  734. $i_arr = array_merge($i_arr, [44, 59, 89]); // 第45、60、90天
  735. // 将当日无数据的日期占位并计算累计值
  736. foreach ($day_stats as $day => $value) {
  737. $today_start = date('Y-m-d 00:00:00');
  738. $today_end = date('Y-m-d 23:59:59');
  739. // 获取当日注册用户
  740. if (Redis::exists('register_uids_' . $send_order_id . '_' . $day)) { //获取缓存
  741. $day_register_uids = json_decode(Redis::get('register_uids_' . $send_order_id . '_' . $day), true);
  742. } else {
  743. $day_register_uids = DB::table('users')->where('send_order_id', $send_order_id)
  744. ->whereBetween('created_at', [date('Y-m-d 00:00:00', strtotime($day)), date('Y-m-d 23:59:59', strtotime($day))])
  745. ->select('id')->get()->pluck('id')->toArray();
  746. // 写入缓存
  747. Redis::set('register_uids_' . $send_order_id . '_' . $day, json_encode($day_register_uids));
  748. Redis::expire('register_uids_' . $send_order_id . '_' . $day, strtotime($today_end) - time());
  749. }
  750. // 获取注册用户今日数据
  751. $day_stats[$day]['today']['register_pay_amount'] = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
  752. ->whereIn('uid', $day_register_uids)->whereBetween('created_at', [$today_start, $today_end])->sum('price');
  753. $day_stats[$day]['today']['total_register_pay_amount'] = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
  754. ->whereIn('uid', $day_register_uids)->where('created_at', '<=', $today_end)->sum('price');
  755. // 获取注册用户总数据
  756. $total_data = DB::table('orders')->where('send_order_id', $send_order_id)->where('status', 'PAID')
  757. ->whereIn('uid', $day_register_uids)->selectRaw("count(distinct uid) as pay_num, sum(price) as total_pay_amount")->get();
  758. $day_stats[$day]['total']['pay_num'] = getProp($total_data[0], 'pay_num');
  759. $day_stats[$day]['total']['total_pay_amount'] = getProp($total_data[0], 'total_pay_amount') ? getProp($total_data[0], 'total_pay_amount') : 0;
  760. for ($i = 0; $i <= 89; $i++) {
  761. $diff_day = (strtotime(date('Y-m-d')) - strtotime($day)) / 86400;
  762. if (isset($day_stats[$day]['t' . $i])) {
  763. if ($i != 0) $day_stats[$day]['t' . $i]['total_register_pay_amount'] = round($day_stats[$day]['t' . ($i - 1)]['total_register_pay_amount'] + $day_stats[$day]['t' . $i]['register_pay_amount'], 2);
  764. } else {
  765. if ($i == $diff_day) { // 如果是当日则取当日数据
  766. $day_stats[$day]['t' . $i] = [
  767. 'register_pay_amount' => $day_stats[$day]['today']['register_pay_amount'],
  768. 'total_register_pay_amount' => 0,
  769. ];
  770. } else {
  771. $day_stats[$day]['t' . $i] = [
  772. 'register_pay_amount' => 0,
  773. 'total_register_pay_amount' => 0,
  774. ];
  775. }
  776. if ($i != 0) $day_stats[$day]['t' . $i]['total_register_pay_amount'] = round($day_stats[$day]['t' . ($i - 1)]['total_register_pay_amount'] + $day_stats[$day]['t' . $i]['register_pay_amount'], 2);
  777. }
  778. }
  779. }
  780. $result = [];
  781. foreach ($list as $item) {
  782. $send_order_id = getProp($item, 'send_order_id');
  783. $day = getProp($item, 'day');
  784. $cost = getProp($item, 'cost');
  785. // 获取日统计数据
  786. $stats = $day_stats[$day];
  787. $arr = [
  788. 'distribution_channel_id' => getProp($send_order_info, 'distribution_channel_id'),
  789. 'send_order_id' => $send_order_id,
  790. 'send_order_name' => $send_order_name,
  791. 'book_name' => getProp($send_order_info, 'book_name'),
  792. 'origin_book_name' => getProp($send_order_info, 'origin_book_name'),
  793. 'date' => $day,
  794. 'cost' => $cost,
  795. 'register_num' => getProp($item, 'register_num'), // 注册用户
  796. 'register_cost' => getProp($item, 'register_num') ? round($cost / getProp($item, 'register_num'), 2) : 0, // 注册成本
  797. 'register_pay_num' => getProp($item, 'register_pay_num'), // 当日注册充值人数
  798. 'register_pay_percent' => getProp($item, 'register_num') ? round(getProp($item, 'register_pay_num') / getProp($item, 'register_num') * 100, 2) : 0, // 当日注册付费率
  799. 'register_pay_cost' => getProp($item, 'register_pay_num') ? round($cost / getProp($item, 'register_pay_num'), 2) : 0, // 当日转化成本
  800. 'total_register_pay_amount' => getProp($stats, 'total')['total_pay_amount'], // 累计充值
  801. 'total_profit_percent' => (float)$cost ? round(getProp($stats, 'total')['total_pay_amount'] / $cost * 100, 2) : 0, //累计回本率
  802. 'total_profit_cost' => getProp($stats, 'total')['pay_num'] ? round($cost / getProp($stats, 'total')['pay_num'], 2) : 0,
  803. ];
  804. foreach ($i_arr as $i) {
  805. $profit_percent = (float)$cost ? round($stats['t' . $i]['register_pay_amount'] / $cost * 100, 2) : 0;
  806. $total_profit_percent = (float)$cost ? round($stats['t' . $i]['total_register_pay_amount'] / $cost * 100, 2) : 0;
  807. $arr['t' . $i . '_register_pay_amount'] = $stats['t' . $i]['register_pay_amount'];
  808. $arr['t' . $i . '_total_register_pay_amount'] = $stats['t' . $i]['total_register_pay_amount'];
  809. $arr['t' . $i . '_profit_percent'] = $profit_percent;
  810. $arr['t' . $i . '_total_profit_percent'] = $total_profit_percent;
  811. // $arr['t'.$i] = '当日充值金额(元): ' . $stats['t'.$i]['register_pay_amount'] . PHP_EOL .
  812. // '累计充值金额(元): ' . $stats['t'.$i]['total_register_pay_amount'] . PHP_EOL .
  813. // '当日回本率(%): '. $profit_percent . PHP_EOL .
  814. // '累计回本率(%): ' . $total_profit_percent;
  815. }
  816. $result[] = $arr;
  817. }
  818. $headers = ['站点ID', '派单ID', '派单名称', '推广书籍', '原书名', '日期', '成本', '注册用户', '注册成本', '当日注册充值人数', '当日注册付费率(%)', '当日转化成本(元)', '累计充值', '累计回本率(%)', '累计转化成本(元)', 'T+0(当日)当日充值金额(元)', 'T+0(当日)累计充值金额(元)', 'T+0(当日)当日回本率(%)', 'T+0(当日)累计回本率(%)'];
  819. for ($i = 1; $i <= 29; $i++) {
  820. $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)当日充值金额(元)';
  821. $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)累计充值金额(元)';
  822. $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)当日回本率(%)';
  823. $headers[] = 'T+' . $i . '(第' . ($i + 1) . '日)累计回本率(%)';
  824. }
  825. $headers = array_merge($headers, ['T+44(第45日)当日充值金额(元)', 'T+44(第45日)累计充值金额(元)', 'T+44(第45日)当日回本率(%)', 'T+44(第45日)累计回本率(%)', 'T+59(第60日)当日充值金额(元)', 'T+59(第60日)累计充值金额(元)', 'T+59(第60日)当日回本率(%)', 'T+59(第60日)累计回本率(%)', 'T+89(第90日)当日充值金额(元)', 'T+89(第90日)累计充值金额(元)', 'T+89(第90日)当日回本率(%)', 'T+89(第90日)累计回本率(%)']);
  826. exportFileCsv($headers, $result, '派单日数据-' . $send_order_name . '-' . date('YmdHis'));
  827. }
  828. /**
  829. * 设置派单日成本
  830. *
  831. * @param $data
  832. * @return mixed
  833. * @throws \App\Exceptions\ApiException
  834. * @throws \GuzzleHttp\Exception\GuzzleException
  835. */
  836. public function setDayCost($data)
  837. {
  838. $id = getProp($data, 'id');
  839. $cost = getProp($data, 'cost');
  840. if (!is_numeric($cost)) {
  841. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  842. }
  843. // 获取派单日信息
  844. $send_order_day_stats = DB::table('send_order_day_stats')->where('id', $id)->first();
  845. if (!$send_order_day_stats) {
  846. Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
  847. }
  848. // 数据未变更则直接返回
  849. if (getProp($send_order_day_stats, 'cost') == $cost) {
  850. return true;
  851. }
  852. // 获取派单信息
  853. $send_order_id = getProp($send_order_day_stats, 'send_order_id');
  854. $send_order = DB::table('send_orders')->where('id', $send_order_id)->first();
  855. if (!$send_order) {
  856. Utils::throwError(ErrorConst::SEND_ORDER_NOT_EXIST);
  857. }
  858. try {
  859. DB::beginTransaction();
  860. $boolen = DB::table('send_order_day_stats')->where('id', $id)->update([
  861. 'cost' => $cost,
  862. 'updated_at' => date('Y-m-d H:i:s')
  863. ]);
  864. if (!$boolen) {
  865. DB::rollback();
  866. Log::info('派单日数据更新失败: ' . json_encode([
  867. 'id' => $id,
  868. 'cost' => $cost,
  869. 'updated_at' => date('Y-m-d H:i:s')
  870. ]));
  871. Utils::throwError(ErrorConst::DB_INVALID);
  872. }
  873. $total_cost = DB::table('send_order_day_stats')->where('send_order_id', $send_order_id)->sum('cost');
  874. $total_profit = getProp($send_order, 'total_register_pay_amount') - $total_cost;
  875. $boolen1 = DB::table('send_orders')->where('id', $send_order_id)->update([
  876. 'cost' => $total_cost,
  877. 'total_profit' => $total_profit,
  878. 'updated_at' => date('Y-m-d H:i:s')
  879. ]);
  880. if (!$boolen1) {
  881. DB::rollback();
  882. Log::info('派单总数据更新失败: ' . json_encode([
  883. 'send_order_id' => $send_order_id,
  884. 'cost' => $total_cost,
  885. 'total_profit' => $total_profit,
  886. 'updated_at' => date('Y-m-d H:i:s')
  887. ]));
  888. Utils::throwError(ErrorConst::DB_INVALID);
  889. }
  890. } catch (\Exception $e) {
  891. DB::rollback();
  892. Utils::throwError(ErrorConst::STOP_TRANSACTION);
  893. }
  894. DB::commit();
  895. return true;
  896. }
  897. /**
  898. * 充值模板
  899. *
  900. * @param $data
  901. * @return mixed
  902. */
  903. public function getTemplateList($data)
  904. {
  905. $distribution_channel_id = Site::getCurrentChannelId();
  906. $template_id = getProp($data, 'template_id');
  907. $query = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id);
  908. if ($template_id) {
  909. $query->where('id', $template_id);
  910. }
  911. return $query->orderBy('template_type')->orderBy('created_at', 'desc')->paginate();
  912. }
  913. /**
  914. * 设置充值模板启用状态
  915. *
  916. * @param $data
  917. * @return int
  918. * @throws ApiException
  919. */
  920. public function setTemplateStatus($data)
  921. {
  922. $template_id = getProp($data, 'template_id');
  923. $distribution_channel_id = Site::getCurrentChannelId();
  924. $template = DB::table('channel_templates')->where('id', $template_id)->where('distribution_channel_id', $distribution_channel_id)->first();
  925. if (!$template) {
  926. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  927. }
  928. $is_enable = getProp($data, 'is_enable');
  929. if ($is_enable === '' || !in_array($is_enable, [0, 1])) {
  930. Utils::throwError('1002:未知状态');
  931. }
  932. if ((int)$is_enable === 0 && (int)getProp($template, 'template_type') === 1) {
  933. Utils::throwError('1002:默认模版不可被停用');
  934. }
  935. return DB::table('channel_templates')->where('id', $template_id)->update([
  936. 'is_enable' => $is_enable,
  937. 'updated_at' => date('Y-m-d H:i:s')
  938. ]);
  939. }
  940. /**
  941. * 新增模版
  942. *
  943. * @param $data
  944. * @return bool
  945. * @throws ApiException
  946. */
  947. public function addTemplate($data)
  948. {
  949. $distribution_channel_id = Site::getCurrentChannelId();
  950. $user_scope = getProp($data, 'user_scope');
  951. if ((int)$user_scope === 1) {
  952. Utils::throwError('1002:不可选择该适用人群');
  953. }
  954. $template_arr = [
  955. 'distribution_channel_id' => $distribution_channel_id,
  956. 'template_name' => getProp($data, 'template_name'),
  957. 'template_type' => 2,
  958. 'user_scope' => $user_scope,
  959. 'orders' => $user_scope == 3 ? 0 : getProp($data, 'orders', 0),
  960. 'is_enable' => getProp($data, 'is_enable', 0),
  961. 'template_cate' => getProp($data, 'template_cate', 1),
  962. 'created_at' => date('Y-m-d H:i:s'),
  963. 'updated_at' => date('Y-m-d H:i:s'),
  964. ];
  965. $channel_type = DB::table('distribution_channels')->where('id', $distribution_channel_id)->value('channel_type');
  966. if (DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('template_name', getProp($data, 'template_name'))->value('id')) {
  967. Utils::throwError('1002:模板名称重复,请更换');
  968. }
  969. try {
  970. DB::beginTransaction();
  971. $template_id = DB::table('channel_templates')->insertGetId($template_arr);
  972. if (!$template_id) {
  973. DB::rollBack();
  974. Log::info('创建模版失败: ' . json_encode($template_arr, 256));
  975. Utils::throwError('1002:创建模版失败');
  976. }
  977. $products = getProp($data, 'products', []);
  978. if (is_string($products)) {
  979. $products = json_decode($products, true);
  980. }
  981. $product_type = BaseConst::PRODUCT_TYPE;
  982. $type_valid = array_keys($product_type);
  983. $type_valid[] = 'RECHARGE';
  984. $sequence = 1;
  985. $product_arr = [];
  986. $default_count = 0;
  987. foreach ($products as $product) {
  988. $type = getProp($product, 'type');
  989. $price = sprintf('%.2f', getProp($product, 'price'));
  990. if (!$type || (!in_array($type, $type_valid)) || $price <= 0) {
  991. Utils::throwError('1002:档位类型或价格设置有误,请重新设置!');
  992. }
  993. $given = getProp($product, 'given', 0);
  994. $angle_sign_text = getProp($product, 'angle_sign_text');
  995. $price_desc = '';
  996. $is_default = getProp($product, 'is_default', 0); // 是否默认
  997. if ($is_default) $default_count += 1;
  998. switch ($type) {
  999. case 'RECHARGE':
  1000. if ($channel_type == 'PERIOD') Utils::throwError('1002:会员制站点不得设置充值档位,只能设置包周期档位!');
  1001. $name = $price . '元';
  1002. $name_desc = '';
  1003. if ($given) $price_desc = '赠' . $given . '书币';
  1004. break;
  1005. default:
  1006. $name = $product_type[$type];
  1007. $name_desc = $name;
  1008. break;
  1009. }
  1010. $product_arr[] = [
  1011. 'type' => $type,
  1012. 'name' => $name,
  1013. 'name_desc' => $name_desc,
  1014. 'price' => $price,
  1015. 'price_desc' => $price_desc,
  1016. 'given' => $given,
  1017. 'angle_sign_text' => $angle_sign_text,
  1018. 'is_default' => $is_default,
  1019. 'is_enabled' => 1,
  1020. 'sequence' => $sequence,
  1021. 'template_type' => $template_id,
  1022. 'template_id' => $template_id,
  1023. 'created_at' => date('Y-m-d H:i:s'),
  1024. 'updated_at' => date('Y-m-d H:i:s'),
  1025. ];
  1026. $sequence += 1;
  1027. }
  1028. if (count($product_arr) < 3 || count($product_arr) > 6) {
  1029. Utils::throwError('1002:充值档位不得低于3个,也不得高于6个');
  1030. }
  1031. if ((int)$default_count === 0) {
  1032. Utils::throwError('1002:请设置默认档位');
  1033. }
  1034. if ((int)$default_count > 1) {
  1035. Utils::throwError('1002:只可设置一个默认档位');
  1036. }
  1037. $boolen = DB::table('products')->insert($product_arr);
  1038. if (!$boolen) {
  1039. DB::rollBack();
  1040. Log::info('创建充值档位失败: ' . json_encode($product_arr, 256));
  1041. Utils::throwError('1002:创建充值档位失败');
  1042. }
  1043. } catch (\Exception $e) {
  1044. DB::rollback();
  1045. Utils::throwError('1002:' . $e->getMessage());
  1046. }
  1047. DB::commit();
  1048. return true;
  1049. }
  1050. /**
  1051. * 编辑模版
  1052. *
  1053. * @param $data
  1054. * @return bool
  1055. * @throws ApiException
  1056. */
  1057. public function editTemplate($data)
  1058. {
  1059. $distribution_channel_id = Site::getCurrentChannelId();
  1060. $user_scope = getProp($data, 'user_scope');
  1061. if ((int)$user_scope === 1) {
  1062. Utils::throwError('1002:不可选择该适用人群');
  1063. }
  1064. $template_id = getProp($data, 'template_id');
  1065. $template = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('id', $template_id)->first();
  1066. if (!$template) {
  1067. Utils::throwError('1002:该模版不存在');
  1068. }
  1069. $channel_type = DB::table('distribution_channels')->where('id', $distribution_channel_id)->value('channel_type');
  1070. $id = DB::table('channel_templates')->where('distribution_channel_id', $distribution_channel_id)->where('template_name', getProp($data, 'template_name'))->value('id');
  1071. if ($id && $id != getProp($template, 'id')) {
  1072. Utils::throwError('1002:模板名称重复,请更换');
  1073. }
  1074. $template_arr = [
  1075. 'distribution_channel_id' => $distribution_channel_id,
  1076. 'template_name' => getProp($data, 'template_name'),
  1077. 'orders' => $user_scope == 3 ? 0 : getProp($data, 'orders', 0),
  1078. 'is_enable' => getProp($data, 'is_enable', 0),
  1079. 'template_cate' => getProp($data, 'template_cate', 1),
  1080. 'updated_at' => date('Y-m-d H:i:s'),
  1081. ];
  1082. if (getProp($template, 'template_type') != 1) {
  1083. $template_arr['user_scope'] = $user_scope;
  1084. }
  1085. $product_ids = DB::table('products')->where('template_id', $template_id)->select('id')->get()->pluck('id')->toArray();
  1086. try {
  1087. DB::beginTransaction();
  1088. $boolen = DB::table('channel_templates')->where('id', $template_id)->update($template_arr);
  1089. if (!$boolen) {
  1090. DB::rollBack();
  1091. Log::info('更新模版失败: ' . json_encode($template_arr, 256));
  1092. Utils::throwError('1002:更新模版失败');
  1093. }
  1094. $products = getProp($data, 'products', []);
  1095. if (is_string($products)) {
  1096. $products = json_decode($products, true);
  1097. }
  1098. $product_type = BaseConst::PRODUCT_TYPE;
  1099. $type_valid = array_keys($product_type);
  1100. $type_valid[] = 'RECHARGE';
  1101. $sequence = 1;
  1102. $product_arr = [];
  1103. $default_count = 0;
  1104. $remain_product_ids = [];
  1105. foreach ($products as $product) {
  1106. $product_id = getProp($data, 'product_id');
  1107. if ($product_id) $remain_product_ids[] = $product_id;
  1108. $type = getProp($product, 'type');
  1109. $price = sprintf('%.2f', getProp($product, 'price'));
  1110. if (!$type || (!in_array($type, $type_valid)) || $price <= 0) {
  1111. Utils::throwError('1002:档位类型或价格设置有误,请重新设置!');
  1112. }
  1113. $given = getProp($product, 'given', 0);
  1114. $angle_sign_text = getProp($product, 'angle_sign_text');
  1115. $price_desc = '';
  1116. $is_default = getProp($product, 'is_default', 0); // 是否默认
  1117. if ($is_default) $default_count += 1;
  1118. switch ($type) {
  1119. case 'RECHARGE':
  1120. if ($channel_type == 'PERIOD') Utils::throwError('1002:会员制站点不得设置充值档位,只能设置包周期档位!');
  1121. $name = $price . '元';
  1122. $name_desc = '';
  1123. if ($given) $price_desc = '赠' . $given . '书币';
  1124. break;
  1125. default:
  1126. $name = $product_type[$type];
  1127. $name_desc = $name;
  1128. break;
  1129. }
  1130. $tmp = [
  1131. 'type' => $type,
  1132. 'name' => $name,
  1133. 'name_desc' => $name_desc,
  1134. 'price' => $price,
  1135. 'price_desc' => $price_desc,
  1136. 'given' => $given,
  1137. 'angle_sign_text' => $angle_sign_text,
  1138. 'is_default' => $is_default,
  1139. 'is_enabled' => 1,
  1140. 'sequence' => $sequence,
  1141. 'template_type' => $template_id,
  1142. 'template_id' => $template_id,
  1143. 'updated_at' => date('Y-m-d H:i:s'),
  1144. ];
  1145. if ($product_id) { // 更新已有的档位
  1146. $boolen1 = DB::table('product')->where('id', $product_id)->update($tmp);
  1147. if (!$boolen1) {
  1148. DB::rollBack();
  1149. Log::info('更新充值档位失败: ' . json_encode($tmp, 256));
  1150. Utils::throwError('1002:更新充值档位失败');
  1151. }
  1152. } else { // 写入新的档位
  1153. $tmp['created_at'] = date('Y-m-d H:i:s');
  1154. $product_arr[] = $tmp;
  1155. }
  1156. $sequence += 1;
  1157. }
  1158. if (count($products) < 3 || count($products) > 6) {
  1159. Utils::throwError('1002:充值档位不得低于3个,也不得高于6个');
  1160. }
  1161. if ((int)$default_count === 0) {
  1162. Utils::throwError('1002:请设置默认档位');
  1163. }
  1164. if ((int)$default_count > 1) {
  1165. Utils::throwError('1002:只可设置一个默认档位');
  1166. }
  1167. // 执行写入
  1168. $boolen2 = DB::table('products')->insert($product_arr);
  1169. if (!$boolen2) {
  1170. DB::rollBack();
  1171. Log::info('创建充值档位失败: ' . json_encode($product_arr, 256));
  1172. Utils::throwError('1002:创建充值档位失败');
  1173. }
  1174. // 执行删除档位
  1175. $delete_ids = array_diff($product_ids, $remain_product_ids);
  1176. $boolen3 = DB::table('products')->whereIn('id', $delete_ids)->delete();
  1177. if (!$boolen3) {
  1178. DB::rollBack();
  1179. Log::info('删除充值档位失败: ' . json_encode($delete_ids, 256));
  1180. Utils::throwError('1002:删除充值档位失败');
  1181. }
  1182. } catch (\Exception $e) {
  1183. DB::rollback();
  1184. Utils::throwError('1002:' . $e->getMessage());
  1185. }
  1186. DB::commit();
  1187. return true;
  1188. }
  1189. // 推广数据
  1190. public function getPromotionData($data)
  1191. {
  1192. $promotionid = getProp($data, 'promotion_id');
  1193. $start_date = getProp($data, 'start_date');
  1194. $end_date = getProp($data, 'end_date');
  1195. if (!$promotionid) {
  1196. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  1197. }
  1198. $channel_id = DB::table('dy_report_logs')->leftJoin('send_orders', 'send_orders.id', 'dy_report_logs.send_order_id')->where('dy_report_logs.promotionid', $promotionid)->value('send_orders.distribution_channel_id');
  1199. $query = DB::table('dy_report_logs')->leftJoin('orders', 'orders.uid', 'dy_report_logs.uid')->where('orders.status', 'PAID')
  1200. ->where('dy_report_logs.promotionid', $promotionid)->select('dy_report_logs.uid')->groupBY('dy_report_logs.uid');
  1201. $uids = $query->get()->pluck('uid')->toArray();
  1202. $result = [
  1203. 'distribution_channel_id' => $channel_id,
  1204. 'promotion_id' => $promotionid,
  1205. 'recharge_amount' => 0,
  1206. 'first_recharge_amount' => 0,
  1207. 'recharge_user_num' => count($uids),
  1208. 'recharge_order_num' => 0,
  1209. ];
  1210. $where = [];
  1211. if ($start_date) {
  1212. $start = date('Y-m-d 00:00:00', strtotime($start_date));
  1213. $where[] = [
  1214. 'created_at', '>=', $start
  1215. ];
  1216. }
  1217. if ($end_date) {
  1218. $end = date('Y-m-d 23:59:59', strtotime($end_date));
  1219. $where[] = [
  1220. 'created_at', '<=', $end
  1221. ];
  1222. }
  1223. if (!$start_date && !$end_date) {
  1224. $start = date('Y-m-d 00:00:00');
  1225. $end = date('Y-m-d 23:59:59');
  1226. $where = [
  1227. [
  1228. 'created_at', '>=', $start
  1229. ],
  1230. [
  1231. 'created_at', '<=', $end
  1232. ]
  1233. ];
  1234. }
  1235. // 获取充值总额
  1236. $result['recharge_amount'] = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->sum('price');
  1237. $result['recharge_order_num'] = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->count('id');
  1238. $result['first_recharge_amount'] = DB::table('orders')->whereIn('uid', $uids)->where('status', 'PAID')->where($where)->where('pay_num', 1)->sum('price');
  1239. return $result;
  1240. }
  1241. // 补回传
  1242. public function reportPromotionOrder($data)
  1243. {
  1244. $report_id = getProp($data, 'report_id');
  1245. if (!$report_id) {
  1246. Utils::throwError(ErrorConst::DATA_EXCEPTION);
  1247. }
  1248. $dy_report_log = DB::table('dy_report_logs')->where('id', $report_id)->where('callback_result', '失败')->where('remark', '比例过滤')->first();
  1249. if (!$dy_report_log) {
  1250. Utils::throwError('1002:暂无可回传数据');
  1251. }
  1252. $clickid = getProp($dy_report_log, 'clickid');
  1253. $post_data = [
  1254. 'clickid' => $clickid,
  1255. 'event_type' => 'active_pay',
  1256. 'context' => [
  1257. 'ad' => [
  1258. 'callback' => $clickid,
  1259. ]
  1260. ],
  1261. 'timestamp' => (int)(microtime(true) * 1000),
  1262. ];
  1263. try {
  1264. $client = new Client(['verify' => false]);
  1265. $response = $client->post('https://analytics.oceanengine.com/api/v2/conversion', ['json' => $post_data]);
  1266. $response_json = $response->getBody()->getContents();
  1267. $result = json_decode($response_json, true);
  1268. $update_data = [
  1269. 'callback_result' => (isset($result['message']) && $result['message'] == '成功') ? '成功' : '失败',
  1270. 'callback_response' => $response_json,
  1271. 'remark' => isset($result['message']) ? ($result['message'] == '成功' ? '手动回传' : $result['message']) : '回参错误',
  1272. 'updated_at' => date('Y-m-d H:i:s')
  1273. ];
  1274. // 写入抖音广告主投放回调日志表
  1275. $boolen = DB::table('dy_report_logs')->where('id', getProp($dy_report_log, 'id'))->update($update_data);
  1276. if (!$boolen) {
  1277. dLog('reportDy')->info('写入抖音广告主投放回调日志表失败: ', $update_data);
  1278. Utils::throwError(ErrorConst::REPORT_FAILED);
  1279. }
  1280. return $boolen;
  1281. } catch (\Exception $e) {
  1282. dLog('reportDy')->info('上报异常: ', ['error' => $e->getMessage()]);
  1283. }
  1284. }
  1285. }