AgentSupplierCommissionService.php 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  1. <?php
  2. namespace addons\Agent\backend\services;
  3. use addons\Agent\common\enums\AgentEnum;
  4. use addons\Agent\common\enums\ComputingEnum;
  5. use addons\Agent\common\logic\reward\AgentReward;
  6. use addons\Agent\common\models\Agent;
  7. use addons\Agent\common\models\AgentCommissionDetails;
  8. use addons\Agent\common\models\AgentCommissionItemSetting;
  9. use addons\Agent\common\models\AgentDelayLog;
  10. use addons\Agent\common\models\AgentGoodsRepurchase;
  11. use addons\Agent\common\models\AgentLevel;
  12. use addons\Agent\common\models\AgentSupplierCommission;
  13. use addons\Agent\common\models\AgentSupplierCommissionSyncLog;
  14. use addons\Agent\common\models\AgentSupplierOrder;
  15. use addons\Course\common\models\AddonsCourseOrders;
  16. use addons\PointUpperLimit\common\service\SettingService;
  17. use addons\Store\common\models\StoreCashierOrder;
  18. use addons\Store\common\models\StoreOrder;
  19. use addons\Store\common\models\StoreOrderDetail;
  20. use common\components\export\CsvTool;
  21. use common\enums\AddonsEnum;
  22. use common\enums\DownloadEnum;
  23. use common\enums\OrderStatusEnum;
  24. use common\enums\RabbitMqEnum;
  25. use common\enums\RefundStatusEnum;
  26. use common\enums\RefundTypeEnum;
  27. use common\enums\StatusEnum;
  28. use common\helpers\FormatHelper;
  29. use common\models\common\CapitalLog;
  30. use common\models\common\Download;
  31. use common\models\mall\MallAddons;
  32. use common\models\order\Order;
  33. use common\models\order\OrderDetail;
  34. use common\models\order\OrderRefund;
  35. use common\models\user\DigitalLog;
  36. use common\models\user\User;
  37. use common\models\user\UserPartitionRelationship;
  38. use Exception;
  39. use RuntimeException;
  40. use services\common\UserWalletService;
  41. use Yii;
  42. class AgentSupplierCommissionService extends BaseService
  43. {
  44. /**
  45. * 供应商收入列表
  46. *
  47. * @param array $params
  48. * @param bool $getAll
  49. *
  50. * @return void
  51. */
  52. public function getList(array $params, bool $getAll = false)
  53. {
  54. $order_no = $params['order_no'] ?? '';
  55. $buy_keyword = $params['buy_keyword'] ?? '';
  56. $seller_keyword = $params['seller_keyword'] ?? '';
  57. $where = [['=', 'mall_id', $this->mall_id], ['status' => StatusEnum::ENABLED]];
  58. if ($order_no) {
  59. $where[] = ['like', 'order_no', $order_no];
  60. }
  61. if (is_numeric($buy_keyword)) {
  62. $userIds = User::find()
  63. ->where(['mall_id' => $this->mall_id])
  64. ->andWhere(['like', 'mobile', $buy_keyword])
  65. ->select('id')
  66. ->column();
  67. $where[] = ['or', ['=', 'buy_user_id', $buy_keyword], ['in', 'buy_user_id', $userIds]];
  68. }
  69. if (is_numeric($seller_keyword)) {
  70. $userIds = User::find()
  71. ->where(['mall_id' => $this->mall_id])
  72. ->andWhere(['like', 'mobile', $seller_keyword])
  73. ->select('id')
  74. ->column();
  75. $where[] = ['or', ['=', 'seller_user_id', $seller_keyword], ['in', 'seller_user_id', $userIds]];
  76. }
  77. $getListParams = [
  78. 'with' => [
  79. 'order' => function ($query) {
  80. $query->select('order_no, order_status');
  81. },
  82. 'buyUser' => function ($query) {
  83. $query->select('id, mobile, nickname, avatar_url');
  84. },
  85. 'sellerUser' => function ($query) {
  86. $query->select('id, mobile, nickname, avatar_url');
  87. },
  88. ],
  89. 'where' => $where,
  90. 'order' => 'pay_time desc, order_no desc, id desc'
  91. ];
  92. $levelMap = AgentLevel::getLevelByColumn($this->mall_id);
  93. if ($getAll) {
  94. $list = AgentSupplierCommission::lists($getListParams);
  95. $this->formatList($list, $levelMap);
  96. } else {
  97. $list = AgentSupplierCommission::listPage($getListParams);
  98. $this->formatList($list['list'], $levelMap);
  99. }
  100. return $list;
  101. }
  102. /**
  103. * 字段修改
  104. *
  105. * @param array $list
  106. * @param array $levelMap
  107. *
  108. * @return void
  109. */
  110. protected function formatList(array &$list, array $levelMap)
  111. {
  112. array_walk($list, function (&$item) use ($levelMap) {
  113. $item['order']['order_status_text'] = OrderStatusEnum::getMap()[$item['order']['order_status']] ?? '-';
  114. $item['pay_time_text'] = date('Y-m-d H:i:s', $item['pay_time']);
  115. $item['buy_level_text'] = $levelMap[$item['buy_level']] ?? '-';
  116. $item['seller_level_text'] = $levelMap[$item['seller_level']] ?? '-';
  117. $item['capital_type_text'] = ComputingEnum::getTypeMap()[$item['capital_type']] ?? '-';
  118. });
  119. }
  120. public function export(array $data): bool
  121. {
  122. $download_id = $data['download_id'] ?? 0;
  123. $download = Download::findOne(['id' => $download_id]);
  124. if (!$download) {
  125. return false;
  126. }
  127. $this->mall_id = $download->mall_id;
  128. try {
  129. $params = json_decode($download->params, true);
  130. $filename = $download->name;
  131. $type = $download->type;
  132. $list = $this->getList($params, true);
  133. $exportList = [];
  134. foreach ($list as $item) {
  135. $exportList[] = [
  136. 'buy_user' => $item['buyUser']['mobile'] ?? '-',
  137. 'buy_level_text' => $item['buy_level_text'],
  138. 'seller_user' => $item['sellerUser']['mobile'] ?? '-',
  139. 'seller_level_text' => $item['seller_level_text'],
  140. 'profit' => $item['profit'],
  141. 'order_no' => $item['order_no'],
  142. 'payment_amount' => $item['payment_amount'],
  143. 'order_status_text' => $item['order']['order_status_text'],
  144. 'pay_time_text' => $item['pay_time_text'],
  145. 'capital_type_text' => $item['capital_type_text'],
  146. ];
  147. }
  148. unset($list);
  149. if (!empty($exportList)) {
  150. $csv = new CsvTool();
  151. $csv->filename = $filename;
  152. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  153. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  154. $csv->header = ['买家', '身份', '卖家', '身份', '利润', '订单号', '货款金额', '订单状态', '收入时间', '奖项'];
  155. $csv->data = $exportList;
  156. $csv->export();
  157. $csv->updateDown($download, $params['mall_id']);
  158. }
  159. } catch (Exception $e) {
  160. $download->status = 3;
  161. $download->save();
  162. $this->setError($e->getMessage());
  163. return false;
  164. }
  165. return true;
  166. }
  167. /**
  168. * 返回是否允许投递任务
  169. *
  170. * @return bool
  171. */
  172. public function allowDeliveryTask()
  173. {
  174. $status = AgentSupplierCommissionSyncLog::find()
  175. ->where(['mall_id' => $this->mall_id])
  176. ->select('status')
  177. ->scalar();
  178. // 未处理 或者 处理成功不允许投递
  179. return !(is_numeric($status) && in_array($status, [StatusEnum::DISABLED, StatusEnum::ENABLED]));
  180. }
  181. /**
  182. * 同步旧数据
  183. *
  184. * @return void
  185. */
  186. public function syncOldData($data)
  187. {
  188. if (empty($data['mall_id'])) {
  189. Yii::$app->custom->logs('同步供应商收入数据:data为空', $data);
  190. return;
  191. }
  192. $this->mall_id = $data['mall_id'];
  193. $pendingData = AgentSupplierCommissionSyncLog::find()
  194. ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::DISABLED])
  195. ->one();
  196. if (!$pendingData) {
  197. Yii::$app->custom->logs('同步供应商收入数据:不存在待处理的数据', $data);
  198. return;
  199. }
  200. try {
  201. $this->insertCommission('commission', ['mall_id' => $this->mall_id, 'from_type' => 'Agent'], $this->mall_id);
  202. $this->insertCommission('commission_frozen', ['mall_id' => $this->mall_id, 'from_type' => 'Agent', 'status' => StatusEnum::DISABLED], $this->mall_id);
  203. $pendingData->status = StatusEnum::ENABLED;
  204. } catch (\Exception $e) {
  205. $pendingData->status = StatusEnum::DELETE;
  206. Yii::$app->custom->logs(FormatHelper::exception($e, '同步供应商收入数据:处理失败'), $data);
  207. }
  208. $pendingData->save();
  209. }
  210. /**
  211. * 查询并写入
  212. *
  213. * @param string $wallet_type
  214. * @param array $where
  215. * @param int $mallId
  216. *
  217. * @return void
  218. */
  219. protected function insertCommission(string $wallet_type, array $where, int $mallId)
  220. {
  221. CapitalLog::$capitalType = $wallet_type;
  222. foreach (CapitalLog::find()->where($where)->batch(500) as $batch) {
  223. $batchInsertData = [];
  224. foreach ($batch as $commissionItem) {
  225. [
  226. $payment_amount,
  227. $goods_nums,
  228. $buy_user_id,
  229. $buy_user_level,
  230. $seller_level,
  231. $order_no,
  232. $pay_time
  233. ] = self::formatData($commissionItem);
  234. $batchInsertData[] = [
  235. 'mall_id' => $mallId,
  236. 'buy_user_id' => $buy_user_id,
  237. 'buy_level' => $buy_user_level,
  238. 'seller_user_id' => $commissionItem['user_id'],
  239. 'seller_level' => $seller_level,
  240. 'order_no' => $order_no,
  241. 'supplier_order_no' => Order::generateOrderNo('G'),
  242. 'goods_nums' => $goods_nums,
  243. 'stock_nums' => $goods_nums,
  244. 'payment_amount' => $payment_amount,
  245. 'profit' => $commissionItem['change_num'],
  246. 'pay_time' => !is_numeric($pay_time) ? 0 : $pay_time,
  247. 'capital_type' => $commissionItem['capital_type'],
  248. 'source_table' => $commissionItem['source_table'],
  249. 'source_table_id' => $commissionItem['source_table_id'],
  250. ];
  251. }
  252. AgentSupplierCommission::insertData($batchInsertData);
  253. }
  254. }
  255. /**
  256. * 格式化数据
  257. * @Author:hua
  258. * @Date:2024-02-23 15:07
  259. * @Copyright:copyright(c)2024 广东七件事集团
  260. * @param $item
  261. * @return array
  262. */
  263. public static function formatData($item)
  264. {
  265. $payment_amount = 0;
  266. $goods_nums = 0;
  267. $buy_user_id = 0;
  268. $buy_user_level = 0;
  269. $seller_level = 0;
  270. $order_no = '';
  271. $pay_time = '';
  272. switch ($item['source_table']) {
  273. case 'order_detail':
  274. $order_detail = OrderDetail::findOne(['id' => $item['source_table_id']]);
  275. if ($order_detail) {
  276. $order = Order::findOne(['id' => $order_detail['order_id']]);
  277. if ($order) {
  278. $order_no = $order['order_no'];
  279. $pay_time = $order['pay_time'];
  280. $buy_user_id = $order['user_id'];
  281. }
  282. $goods_nums = $order_detail['num'];
  283. $payment_amount = $order_detail['goods_price'];
  284. }
  285. break;
  286. case 'store_cashier_order':
  287. $store_cashier_order = StoreCashierOrder::findOne(['id' => $item['source_table_id']]);
  288. if ($store_cashier_order) {
  289. $order_no = $store_cashier_order['order_no'];
  290. $pay_time = $store_cashier_order['pay_time'];
  291. $buy_user_id = $store_cashier_order['user_id'];
  292. $payment_amount = $store_cashier_order['pay_money'];
  293. }
  294. $goods_nums = 1;
  295. break;
  296. case 'store_order_detail':
  297. $store_order_detail = StoreOrderDetail::findOne(['id' => $item['source_table_id']]);
  298. if ($store_order_detail) {
  299. $store_order = StoreOrder::findOne(['id' => $store_order_detail['order_id']]);
  300. if ($store_order) {
  301. $order_no = $store_order['order_no'];
  302. $pay_time = $store_order['pay_time'];
  303. $buy_user_id = $store_order['user_id'];
  304. }
  305. $payment_amount = $store_order_detail['goods_price'];
  306. }
  307. $goods_nums = $store_order_detail['num'];
  308. break;
  309. case '{{%addons_agent_team_achievement}}':
  310. break;
  311. case '{{%addons_course_orders}}':
  312. $course_order = AddonsCourseOrders::findOne(['id' => $item['source_table_id']]);
  313. if ($course_order) {
  314. $order_no = $course_order['order_no'];
  315. $pay_time = $course_order['pay_time'];
  316. $buy_user_id = $course_order['user_id'];
  317. $goods_nums = 1;
  318. $payment_amount = $course_order['pay_money'];
  319. }
  320. break;
  321. case '{{%order}}':
  322. $order = Order::findOne(['id' => $item['source_table_id']]);
  323. if ($order) {
  324. $num = OrderDetail::find()->where(['order_id' => $order['id']])->sum('num');
  325. $goods_price = OrderDetail::find()->where(['order_id' => $order['id']])->sum('goods_price');
  326. if (!empty($num)) $goods_nums = $num;
  327. if (!empty($goods_price)) $payment_amount = $goods_price;
  328. $order_no = $order['order_no'];
  329. $pay_time = $order['pay_time'];
  330. $buy_user_id = $order['user_id'];
  331. }
  332. break;
  333. }
  334. // 从供应商订单找之前的等级
  335. if (!empty($order_no)) {
  336. $levelArr = AgentSupplierOrder::find()
  337. ->where(['order_no' => $order_no])
  338. ->asArray()
  339. ->all();
  340. $buyUserLevelArr = array_column($levelArr, 'buy_level', 'buy_user_id');
  341. $sellerUserLevelArr = array_column($levelArr, 'seller_level', 'seller_user_id');
  342. }
  343. $agent_arr = Agent::find()->where(['mall_id' => $item['mall_id'], 'user_id' => [$buy_user_id, $item['user_id']], 'status' => StatusEnum::ENABLED])
  344. ->indexBy('user_id')->all();
  345. if (isset($buyUserLevelArr[$buy_user_id])) {
  346. $buy_user_level = $buyUserLevelArr[$buy_user_id];
  347. }else if (isset($agent_arr[$buy_user_id])) {
  348. $buy_user_level = $agent_arr[$buy_user_id]['level'];
  349. }
  350. if (isset($sellerUserLevelArr[$item['user_id']])) {
  351. $seller_level = $sellerUserLevelArr[$item['user_id']];
  352. }else if (isset($agent_arr[$item['user_id']])) {
  353. $seller_level = $agent_arr[$item['user_id']]['level'];
  354. }
  355. return [$payment_amount, $goods_nums, $buy_user_id, $buy_user_level, $seller_level, $order_no, $pay_time];
  356. }
  357. /**
  358. * 重新分配佣金(数字币只验证是否已结算,不处理其他情况)
  359. *
  360. * @param array $post
  361. *
  362. * @return bool
  363. */
  364. public function redistributeCommissions(array $post): bool
  365. {
  366. $orderDetailsIds = $post['order_details_ids'];
  367. $mallId = $this->mall_id;
  368. try {
  369. $this->redistributeCommissionsValidate($orderDetailsIds, $mallId);
  370. // 验证通过则丢队列处理
  371. Yii::$app->services->rabbitMq->push(
  372. RabbitMqEnum::EXCHANGE_ORDER,
  373. RabbitMqEnum::ORDER_QUEUE,
  374. ['mall_id' => $this->mall_id, 'order_details_ids' => $orderDetailsIds],
  375. self::class,
  376. 'asyncRedistributeCommissions'
  377. );
  378. return true;
  379. }catch (Exception $e) {
  380. $this->error = $e->getMessage();
  381. return false;
  382. }
  383. }
  384. /**
  385. * 执行重新分配前数据验证
  386. *
  387. * @param array $orderDetailsIds
  388. * @param int $mallId
  389. *
  390. * @return array
  391. */
  392. protected function redistributeCommissionsValidate(array &$orderDetailsIds, int $mallId): array
  393. {
  394. $orderDetailList = OrderDetail::find()
  395. ->where([
  396. 'status' => StatusEnum::ENABLED,
  397. 'id' => $orderDetailsIds,
  398. 'mall_id' => $mallId
  399. ])
  400. ->andWhere(['not in', 'order_status', [OrderStatusEnum::REPEAL,OrderStatusEnum::ACCOMPLISH]])//已完成的订单无法重新分配
  401. ->select('id, order_id, is_feedback')
  402. ->asArray()
  403. ->all();
  404. if (empty($orderDetailList)) {
  405. throw new RuntimeException('订单明细不存在或已退款、已完成');
  406. }
  407. $orderIds = array_column($orderDetailList, 'order_id');
  408. // 验证 details_id正确 并且是 同一个订单
  409. if (count(array_unique($orderIds)) > 1) {
  410. throw new RuntimeException('订单参数错误,存在多张订单');
  411. }
  412. $orderDetailsIds = array_column($orderDetailList, 'id');
  413. // 排除售后完成并且不是换货的order details id
  414. $refundOrderDetailIds = OrderRefund::find()
  415. ->where([
  416. 'order_detail_id' => $orderDetailsIds,
  417. 'status' => StatusEnum::ENABLED,
  418. 'refund_status' => RefundStatusEnum::FINISH,
  419. 'type' => [RefundTypeEnum::MONEY, RefundTypeEnum::MONEY_AND_PRODUCT]
  420. ])
  421. ->select('order_detail_id')
  422. ->column();
  423. $orderDetailsIds = array_diff($orderDetailsIds, $refundOrderDetailIds);
  424. if (empty($orderDetailsIds)) {
  425. throw new RuntimeException('订单已退款');
  426. }
  427. $order = Order::find()
  428. ->where([
  429. 'id' => reset($orderIds),
  430. 'mall_id' => $this->mall_id,
  431. 'status' => StatusEnum::ENABLED,
  432. 'pay_status' => OrderStatusEnum::PAY,
  433. ])
  434. ->andWhere(['not in', 'order_status', [OrderStatusEnum::REPEAL,OrderStatusEnum::ACCOMPLISH]])
  435. ->one();
  436. if (empty($order)) {
  437. throw new RuntimeException('订单不存在或已退款、已完成');
  438. }
  439. $orderNo = $order['order_no'];
  440. // 验证是否可以重新分配
  441. try {
  442. $frozenCommissionList = $this->checkCommissionOrScore($orderDetailsIds, 'commission');
  443. $frozenScoreList = $this->checkCommissionOrScore($orderDetailsIds, 'score');
  444. $this->checkDigital($orderDetailsIds);
  445. $orderNo && $this->checkDelayLog($orderNo);
  446. }catch (Exception $e) {
  447. throw new RuntimeException($e->getMessage());
  448. }
  449. // 将冻结列表组装成$insert_wallet
  450. return [
  451. 'insert_wallet' => $this->frozenDataToInsertWallet($frozenCommissionList, $frozenScoreList),
  452. 'order' => $order
  453. ];
  454. }
  455. /**
  456. * 重新分配,异步执行
  457. *
  458. * @param array $data
  459. *
  460. * @return false|void
  461. */
  462. public function asyncRedistributeCommissions(array $data)
  463. {
  464. if (empty($data['mall_id']) || empty($data['order_details_ids'])) {
  465. return;
  466. }
  467. $this->mall_id = $data['mall_id'];
  468. $orderDetailsIds = $data['order_details_ids'];
  469. $validateRes = $this->redistributeCommissionsValidate($orderDetailsIds, $this->mall_id);
  470. $order = $validateRes['order'];
  471. $insertWallet = $validateRes['insert_wallet'];
  472. $t = Yii::$app->db->beginTransaction();
  473. try {
  474. // 将供应商收入的旧数据置为失效
  475. $this->disableAgentSupplierCommission($orderDetailsIds);
  476. // 将供应商订单的旧数据置为失效
  477. $this->disableAgentSupplierOrder($orderDetailsIds);
  478. // $this->disableFrozenDigital($orderDetailsIds); // 不处理数字币
  479. // 将佣金和积分的旧数据置为失效
  480. $this->disableFrozenCommissionOrScore($orderDetailsIds, 'commission');
  481. $this->disableFrozenCommissionOrScore($orderDetailsIds, 'score');
  482. // 将延时任务的任务明细置为失效
  483. $this->disableAgentDelayLog($orderDetailsIds);
  484. // 减少agent total_commission total_score字段
  485. foreach ($insertWallet as $walletItem) {
  486. // 不处理数字币
  487. if($walletItem['wallet_type'] === 'digital'){
  488. continue;
  489. }
  490. if($walletItem['from_type'] === AddonsEnum::ADDONS_NAME_REGULAR_BONUS. '_'. AgentEnum::ADDONS_NAME) {
  491. continue;
  492. }
  493. $agent = Agent::findOne(['user_id' => $walletItem['user_id'],'status'=>[StatusEnum::DISABLED,StatusEnum::ENABLED], 'mall_id' => $this->mall_id]);
  494. if(empty($agent)) {
  495. continue;
  496. }
  497. $field = 'total_' . ($this->getCommissionNameMap()[$walletItem['wallet_type']] ?? $walletItem['wallet_type']);
  498. $agent->$field -= $walletItem['money'];
  499. $res = $agent->save();
  500. if (empty($res)) {
  501. throw new RuntimeException(Agent::getStaticError());
  502. }
  503. }
  504. $t->commit();
  505. // 重新分配
  506. $this->reward($order, $orderDetailsIds);
  507. }catch (Exception $e) {
  508. $t->rollBack();
  509. Yii::$app->custom->logs(FormatHelper::exception($e, '重新分配佣金失败'), compact('data'));
  510. return false;
  511. }
  512. }
  513. /**
  514. * 检查佣金或积分是否已结算(返回待结算的数据)
  515. *
  516. * @param array $orderDetailsIds
  517. * @param string $wallet_type
  518. *
  519. * @return array
  520. */
  521. protected function checkCommissionOrScore(array $orderDetailsIds, string $wallet_type): array
  522. {
  523. // 实际已结算的表
  524. CapitalLog::$capitalType = $wallet_type;
  525. $where = [
  526. 'source_table' => $this->getOrderDetailsTable(),
  527. 'source_table_id' => $orderDetailsIds,
  528. 'from_type' => AgentEnum::ADDONS_NAME
  529. ];
  530. $sendCount = CapitalLog::find()
  531. ->where($where)
  532. ->count();
  533. if ($sendCount) {
  534. throw new RuntimeException('当前订单已存在已结算数据,不允许重新分配');
  535. }
  536. // 冻结表
  537. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  538. $where['status'] = [CapitalLog::STATUS_SEND, CapitalLog::STATUS_WAIT_SEND];
  539. $frozenList = [];
  540. $commissionFrozenList = CapitalLog::find()
  541. ->where($where)
  542. ->asArray()
  543. ->all();
  544. foreach ($commissionFrozenList as $item) {
  545. if ($item['status'] == CapitalLog::STATUS_SEND) {
  546. throw new RuntimeException('当前订单已存在已结算数据,不允许重新分配');
  547. }
  548. if ($item['status'] == CapitalLog::STATUS_WAIT_SEND) {
  549. $frozenList[] = $item;
  550. }
  551. }
  552. return $frozenList;
  553. }
  554. /**
  555. * 检查数字币是否已结算
  556. *
  557. * @param array $orderDetailsIds
  558. */
  559. protected function checkDigital(array $orderDetailsIds)
  560. {
  561. $digitalCount = DigitalLog::find()
  562. ->where([
  563. 'source_table' => $this->getOrderDetailsTable(),
  564. 'source_table_id' => $orderDetailsIds,
  565. 'from_type' => AgentEnum::ADDONS_NAME,
  566. 'send_status' => DigitalLog::SEND_STATUS_NORMAL
  567. ])
  568. ->count();
  569. if ($digitalCount) {
  570. throw new RuntimeException('当前订单已存在已结算数据,不允许重新分配');
  571. }
  572. }
  573. /**
  574. * 查看延迟结算表是否已结算,或者已经到了结算的时间
  575. *
  576. * @param string $orderNo
  577. *
  578. * @return void
  579. */
  580. protected function checkDelayLog(string $orderNo)
  581. {
  582. $sendCount = AgentDelayLog::find()
  583. ->where([
  584. 'wallet_type' => ['commission', 'score', 'digital'],
  585. 'order_no' => $orderNo,
  586. 'mall_id' => $this->mall_id,
  587. 'status' => CapitalLog::STATUS_SEND
  588. ])
  589. ->count();
  590. if ($sendCount) {
  591. throw new RuntimeException('当前订单已存在已结算数据,不允许重新分配');
  592. }
  593. $delayLog = AgentDelayLog::find()
  594. ->where([
  595. 'wallet_type' => ['commission', 'score', 'digital'],
  596. 'order_no' => $orderNo,
  597. 'mall_id' => $this->mall_id
  598. ])
  599. ->groupBy('wallet_type')
  600. ->select('min(day_num) day_num, min(created_at) created_at')
  601. ->all();
  602. if (empty($delayLog)) {
  603. return;
  604. }
  605. foreach ($delayLog as $item) {
  606. if ($item['day_num'] * 24 * 3600 + $item['created_at'] <= time() - 60) {
  607. throw new RuntimeException('当前订单正在结算或已结算,不允许重新分配');
  608. }
  609. }
  610. }
  611. /**
  612. * 将冻结数据置为失效(积分,佣金)
  613. *
  614. * @return void
  615. */
  616. protected function disableFrozenCommissionOrScore(array $orderDetailsIds, string $wallet_type)
  617. {
  618. $params = [
  619. 'where' => [
  620. [
  621. 'source_table' => $this->getOrderDetailsTable(),
  622. 'source_table_id' => $orderDetailsIds,
  623. 'from_type' => AgentEnum::ADDONS_NAME,
  624. 'status' => CapitalLog::STATUS_WAIT_SEND
  625. ]
  626. ]
  627. ];
  628. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  629. $list = CapitalLog::lists($params);
  630. if ($list) {
  631. foreach ($list as $item) {
  632. UserWalletService::unfrozen($item['id'], $wallet_type, false);
  633. }
  634. }
  635. }
  636. /**
  637. * 将冻结数据置为失效(数字币)
  638. *
  639. * @return void
  640. */
  641. protected function disableFrozenDigital(array $orderDetailsIds)
  642. {
  643. DigitalLog::updateAll(
  644. [
  645. 'send_status' => DigitalLog::SEND_STATUS_NORMAL,
  646. 'status' => StatusEnum::DELETE,
  647. 'updated_at' => time()
  648. ],
  649. [
  650. 'source_table' => $this->getOrderDetailsTable(),
  651. 'source_table_id' => $orderDetailsIds,
  652. 'from_type' => AgentEnum::ADDONS_NAME,
  653. 'send_status' => DigitalLog::SEND_STATUS_FROZEN
  654. ]
  655. );
  656. }
  657. /**
  658. * 将佣金和积分禁用(不处理数字币)
  659. *
  660. * @return void
  661. */
  662. protected function disableAgentDelayLog(array $orderDetailsIds)
  663. {
  664. AgentDelayLog::updateAll(
  665. [
  666. 'status' => StatusEnum::DELETE,
  667. 'updated_at' => time()
  668. ],
  669. [
  670. 'wallet_type' => ['commission', 'score'],
  671. 'mall_id' => $this->mall_id,
  672. 'status' => CapitalLog::STATUS_WAIT_SEND,
  673. 'source_table' => $this->getOrderDetailsTable(),
  674. 'source_table_id' => $orderDetailsIds,
  675. ]
  676. );
  677. }
  678. /**
  679. * 禁用供应商收入的数据
  680. *
  681. * @return void
  682. */
  683. protected function disableAgentSupplierCommission(array $orderDetailsIds)
  684. {
  685. AgentSupplierCommission::updateAll(
  686. [
  687. 'status' => StatusEnum::DELETE,
  688. 'updated_at' => time()
  689. ],
  690. [
  691. 'mall_id' => $this->mall_id,
  692. 'status' => StatusEnum::ENABLED,
  693. 'source_table' => $this->getOrderDetailsTable(),
  694. 'source_table_id' => $orderDetailsIds,
  695. ]
  696. );
  697. }
  698. /**
  699. * @return array
  700. */
  701. protected function getOrderDetailsTable(): array
  702. {
  703. return ['order_detail', OrderDetail::tableName()];
  704. }
  705. public function reward($order, $orderDetailsIds){
  706. $mall_id = $order['mall_id'];
  707. $order_id = $order['id'];
  708. $user_id = $order['user_id'];
  709. $setting = \Yii::$app->services->setting->addons->get($mall_id, AgentEnum::ADDONS_NAME, 'basic');
  710. if (empty($setting['is_enable'])) throw new \yii\db\Exception('没开启经销商');
  711. $params = [];
  712. $params['where'] = [['order_id' => $order_id], ['user_id' => $user_id], ['mall_id' => $mall_id], ['id' => $orderDetailsIds]];
  713. $order_detail_list = OrderDetail::lists($params);
  714. if (empty($order_detail_list)) throw new \yii\db\Exception('订单详情不存在');
  715. $is_self_buy = $setting['is_self_buy'];// 经销商内购
  716. $enable_repurchase_reward = $setting['enable_repurchase_reward'] ?? StatusEnum::DISABLED;// 复购等于报单 - 需要判断当前用户是否有支付过的订单
  717. if ($enable_repurchase_reward) {
  718. $enable_repurchase_reward = Order::find()
  719. ->where(['user_id' => $user_id])
  720. ->andWhere(['mall_id' => $mall_id])
  721. ->andWhere(['<>', 'id', $order_id])
  722. ->andWhere(['pay_status' => StatusEnum::ENABLED])
  723. ->count() > 0 ? StatusEnum::ENABLED : StatusEnum::DISABLED;
  724. }
  725. $parent_id_arr = UserPartitionRelationship::getParentIdArr($user_id);
  726. $parent_id_arr = array_reverse($parent_id_arr);
  727. $where = ['user_id' => $parent_id_arr, 'mall_id' => $mall_id, 'status' => StatusEnum::ENABLED];
  728. $addons_agent_list = Agent::find()->where($where)->indexBy('user_id')->all();//查询经销商列表
  729. $agent_list = [];
  730. foreach ($parent_id_arr as $parent_id) {
  731. if (!empty($addons_agent_list[$parent_id])) $agent_list[] = $addons_agent_list[$parent_id];//按照关系排序
  732. }
  733. $agent = Agent::findOne(['user_id' => $user_id, 'mall_id' => $mall_id, 'status' => StatusEnum::ENABLED]);// 查询购买者身份
  734. $reward_detail = $this->unshiftAgentList($is_self_buy, $enable_repurchase_reward, $agent_list, $order_detail_list, $agent);
  735. $commission_arr = [];
  736. $is_settle_additional_score = StatusEnum::DISABLED;
  737. if (MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_POINT_UPPER_LIMIT)) {
  738. $is_settle_additional_score = (new SettingService())->isEnable($mall_id);
  739. }
  740. $insert_wallet_data = [];
  741. foreach ($order_detail_list as $order_detail) {
  742. // 如果是针对商品的,那么就判断
  743. // 检测商品是否参与了渠道分红
  744. [$agent_list, $self_buy_user_id, $enable_repurchase_reward] = $reward_detail[$order_detail['id']];
  745. $item_setting = AgentCommissionItemSetting::find()->select('item_id,is_participate')->where(['item_id' => $order_detail['goods_id'], 'status' => StatusEnum::ENABLED])->asArray()->one();
  746. if (!empty($item_setting) && empty($item_setting['is_participate'])) continue;
  747. $agent_reward_obj = new AgentReward($mall_id, $user_id, $setting, $agent_list, $self_buy_user_id, $enable_repurchase_reward);
  748. $order_detail['order_no'] = $order['order_no'];
  749. $agent_reward_obj->order_detail = $order_detail;
  750. $commission_arr[$order_detail['id']][AgentEnum::ADDONS_NAME]['commission'] = $agent_reward_obj->addFrozenCommission();
  751. $insert_wallet_data = array_merge($insert_wallet_data, $agent_reward_obj->insert_wallet);
  752. $agent_reward_obj = new AgentReward($mall_id, $user_id, $setting, $agent_list, $self_buy_user_id, $enable_repurchase_reward, 'score');
  753. $agent_reward_obj->order_detail = $order_detail;
  754. $agent_reward_obj->addFrozenCommission();
  755. $insert_wallet_data = array_merge($insert_wallet_data, $agent_reward_obj->insert_wallet);
  756. //数字币赠送 (数字币不处理)
  757. /*$agent_reward_obj = new AgentReward($mall_id, $user_id, $setting, $agent_list, $self_buy_user_id, $enable_repurchase_reward, 'digital');
  758. $agent_reward_obj->order_detail = $order_detail;
  759. $agent_reward_obj->addFrozenCommission();
  760. $insert_wallet_data = array_merge($insert_wallet_data, $agent_reward_obj->insert_wallet);*/
  761. // 额外积分 - 需要判断是否开启
  762. if (!empty($is_settle_additional_score)) {
  763. // 判断当前用户是否已经超出上限, 只有有额度的情况下才需要进行处理
  764. // if ((new UserService())->getUserQuota($mall_id, $user_id) > 0) {
  765. $agent_reward_obj = new AgentReward($mall_id, $user_id, $setting, $agent_list, $self_buy_user_id, $enable_repurchase_reward, 'additional_score');
  766. $agent_reward_obj->order_detail = $order_detail;
  767. $agent_reward_obj->addFrozenCommission();
  768. // }
  769. }
  770. }
  771. /*
  772. * TODO: 渠道分红延时结算/解冻资产
  773. * 结算方式为“订单完成后”并且“延时结算天数”不为0时执行延时结算
  774. */
  775. if (isset($setting['compute_type']) && isset($setting['compute_delay_day']) && !empty($setting['compute_type']) && !empty($setting['compute_delay_day'])) {
  776. $delay_logs_data = []; // TODO: 渠道分红延时结算 - 需要延时解冻数据
  777. foreach ($insert_wallet_data as $item) {
  778. $delay_logs_data[] = [
  779. 'mall_id' => $mall_id,
  780. 'user_id' => $item['user_id'],
  781. 'wallet_type' => $item['wallet_type'],
  782. 'wallet_num' => $item['money'],
  783. 'compute_type' => $setting['compute_type'],
  784. 'day_num' => $setting['compute_delay_day'],
  785. 'wallet_desc' => $item['desc'],
  786. 'order_no' => $item['order_no'],
  787. 'capital_type' => $item['capital_type'],
  788. 'contributor_name' => $item['contributor_name'],
  789. 'contributor_money' => $item['contributor_money'],
  790. 'contributor_id' => $item['contributor_id'],
  791. 'source_table' => $item['source_table'],
  792. 'source_table_id' => $item['source_table_id'],
  793. ];
  794. }
  795. // 写入延时结算日志
  796. AgentDelayLog::batchSave($delay_logs_data);
  797. }
  798. $this->addOrderLog($order,$commission_arr);
  799. return true;
  800. }
  801. /**
  802. * 添加订单
  803. *
  804. * @param $order
  805. * @param $commission_arr
  806. *
  807. * @throws Exception
  808. */
  809. public function addOrderLog($order,$commission_arr)
  810. {
  811. $total_commission = 0;
  812. foreach ($commission_arr as $order_detail_id => $commission) {
  813. $total_commission += $commission['commission'] ?? ($commission['Agent']['commission'] ?? 0);
  814. }
  815. $agent = Agent::findOne(['mall_id' => $order->mall_id,'user_id' => $order->user_id]);
  816. // 将旧的禁用
  817. AgentCommissionDetails::updateAll(
  818. [
  819. 'status' => StatusEnum::DELETE,
  820. 'updated_at' => time()
  821. ],
  822. [
  823. 'mall_id' => $order->mall_id,
  824. 'order_id' => $order->id,
  825. 'user_id' => $order->user_id,
  826. 'agent_id' => $agent->id ?? 0,
  827. ]
  828. );
  829. $data = [
  830. 'mall_id' => $order->mall_id,
  831. 'order_id' => $order->id,
  832. 'user_id' => $order->user_id,
  833. 'agent_id' => $agent->id ?? 0,
  834. 'level' => $agent->level ?? 0,
  835. 'order_price' => $order->pay_money,
  836. 'commission' => $total_commission,
  837. 'order_source' => 'order',
  838. ];
  839. $res = AgentCommissionDetails::setData($data);
  840. if(!$res) throw new Exception(AgentCommissionDetails::getStaticError());
  841. }
  842. /**
  843. * 针对每一款商品的设置
  844. * @param int $is_self_buy
  845. * @param int $enable_repurchase_reward
  846. * @param array $agent_list
  847. * @param array $orders
  848. * @param Agent|null $agent
  849. * @return array
  850. */
  851. protected function unshiftAgentList(int $is_self_buy, int $enable_repurchase_reward, array $agent_list, array $orders, Agent $agent = null): array
  852. {
  853. $data = [];
  854. foreach ($orders as $order_detail) {
  855. // 独立设置了
  856. $data[$order_detail['id']][0] = $agent_list; // agent_list
  857. $data[$order_detail['id']][1] = StatusEnum::DISABLED; // self_buy_user_id
  858. $data[$order_detail['id']][2] = StatusEnum::DISABLED; // enable_repurchase_reward
  859. if (!empty($agent)) {
  860. $order_detail_enable_repurchase_reward = $enable_repurchase_reward;
  861. $is_alone_setting = AgentGoodsRepurchase::isAloneSetting($order_detail['mall_id'], $order_detail['goods_id']);
  862. if ($is_alone_setting) {
  863. // 单独设置 那么就需要判断之前是否有购买过当前商品
  864. $order_detail_enable_repurchase_reward = AgentGoodsRepurchase::isBuyCurrentGoods($order_detail['mall_id'], $order_detail['user_id'],
  865. $order_detail['order_id'], $order_detail['goods_id']) ? StatusEnum::ENABLED : StatusEnum::DISABLED;
  866. }
  867. //独立设置内购
  868. $item_setting = AgentCommissionItemSetting::find()->select('item_id,is_participate,is_self_buy_alone,is_self_buy,enable_goods_deduct')->where(['item_id' => $order_detail['goods_id'], 'status' => StatusEnum::ENABLED])->asArray()->one();
  869. if (!empty($item_setting) && !empty($item_setting['is_participate']) && !empty($item_setting['is_self_buy_alone'])){
  870. $is_self_buy_detail = $item_setting['is_self_buy'];
  871. }else{
  872. $is_self_buy_detail = $is_self_buy;
  873. }
  874. if ($is_self_buy_detail || $order_detail_enable_repurchase_reward) {
  875. array_unshift($data[$order_detail['id']][0], $agent);
  876. $data[$order_detail['id']][1] = $agent->user_id;
  877. $data[$order_detail['id']][2] = StatusEnum::ENABLED;
  878. }
  879. }
  880. }
  881. return $data;
  882. }
  883. /**
  884. * 将冻结的数据组装成$insert_wallet
  885. *
  886. * @return array
  887. */
  888. protected function frozenDataToInsertWallet(array $frozenCommissionList, array $frozenScoreList)
  889. {
  890. foreach ($frozenCommissionList as &$item) {
  891. $item['wallet_type'] = 'commission';
  892. $item['is_frozen'] = true;
  893. $item['money'] = $item['change_num'];
  894. }
  895. foreach ($frozenScoreList as &$item) {
  896. $item['wallet_type'] = 'score';
  897. $item['is_frozen'] = true;
  898. $item['money'] = $item['change_num'];
  899. }
  900. return array_merge($frozenCommissionList, $frozenScoreList);
  901. }
  902. protected function getCommissionNameMap(): array
  903. {
  904. return [
  905. 'additional_score' => 'score'
  906. ];
  907. }
  908. /**
  909. * 将供应商订单数据扣回,因为原来写入时同一个订单是累加的,所以为了兼容纠错只选择单个order_detail的情况下只能扣回,不能将原数据删除
  910. *
  911. * @param $orderDetailsIds
  912. *
  913. * @return void
  914. */
  915. private function disableAgentSupplierOrder($orderDetailsIds)
  916. {
  917. $wallet_type = 'commission';
  918. CapitalLog::$capitalType = "{$wallet_type}_frozen";
  919. $where = [
  920. [
  921. 'source_table' => $this->getOrderDetailsTable(),
  922. 'source_table_id' => $orderDetailsIds,
  923. 'from_type' => AgentEnum::ADDONS_NAME,
  924. 'status' => StatusEnum::DISABLED,
  925. 'change_type' => 'add',
  926. 'mall_id' => $this->mall_id
  927. ]
  928. ];
  929. $commissionList = CapitalLog::lists([
  930. 'where' => $where,
  931. 'select' => 'change_num, user_id, source_table_id, order_no'
  932. ]);
  933. // 得到购买数量
  934. $orderDetails = OrderDetail::find()
  935. ->where([
  936. 'id' => $orderDetailsIds,
  937. 'status' => StatusEnum::ENABLED,
  938. 'mall_id' => $this->mall_id
  939. ])
  940. ->indexBy('id')
  941. ->all();
  942. $setting = \Yii::$app->services->setting->addons->get($this->mall_id, AgentEnum::ADDONS_NAME, 'basic');
  943. $agent_reward_obj = new AgentReward($this->mall_id, 0, $setting, [], 0, 0, '');
  944. foreach ($commissionList as $commissionItem) {
  945. $model = AgentSupplierOrder::find()
  946. ->where([
  947. 'seller_user_id' => $commissionItem['user_id'],
  948. 'order_no' => $commissionItem['order_no'],
  949. 'status' => StatusEnum::ENABLED
  950. ])
  951. ->one();
  952. if (!$model) {
  953. continue;
  954. }
  955. if (empty($orderDetails[$commissionItem['source_table_id']])) {
  956. continue;
  957. }
  958. $agent_reward_obj->order_detail = $orderDetails[$commissionItem['source_table_id']];
  959. ['money' => $money] = $agent_reward_obj->getRules();
  960. $model->payment_amount -= $money; // getRule得到
  961. $model->profit -= $commissionItem['change_num']; // 佣金
  962. $model->goods_nums -= $orderDetails[$commissionItem['source_table_id']]->num;
  963. $model->stock_nums -= $orderDetails[$commissionItem['source_table_id']]->num;
  964. $model->payment_amount = $model->payment_amount < 0 ? 0 : $model->payment_amount;
  965. $model->profit = $model->profit < 0 ? 0 : $model->profit;
  966. $model->goods_nums = $model->goods_nums < 0 ? 0 : $model->goods_nums;
  967. $model->stock_nums = $model->stock_nums < 0 ? 0 : $model->stock_nums;
  968. if (
  969. $model->payment_amount == 0 &&
  970. $model->profit == 0 &&
  971. $model->goods_nums == 0 &&
  972. $model->stock_nums == 0
  973. ) {
  974. $model->status = StatusEnum::DELETE;
  975. }
  976. $model->save();
  977. }
  978. }
  979. }