ProfitService.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <?php
  2. namespace app\api\service;
  3. use app\admin\model\Admin;
  4. use app\admin\model\profit\Bill;
  5. use app\api\model\massager\Massager;
  6. use app\api\model\massager\Wallet;
  7. use app\api\model\Store;
  8. use app\api\model\User;
  9. use redis\RedLock;
  10. use think\Db;
  11. use think\Exception;
  12. class ProfitService extends BaseService
  13. {
  14. private $adminModel;
  15. private $storeModel;
  16. private $massagerModel;
  17. private $pBillModel;
  18. private $mBillModel;
  19. private $mWalletModel;
  20. private $userModel;
  21. private $uBillModel;
  22. private $uWalletModel;
  23. public function __construct()
  24. {
  25. $this->adminModel = new Admin();
  26. $this->storeModel = new Store();
  27. $this->massagerModel = new Massager();
  28. $this->pBillModel = new Bill();
  29. $this->mBillModel = new \app\api\model\massager\Bill();
  30. $this->mWalletModel = new Wallet();
  31. $this->userModel = new User();
  32. $this->uBillModel = new \app\api\model\user\Bill();
  33. $this->uWalletModel = new \app\api\model\user\Wallet();
  34. }
  35. public function profit($order, $user, $unusual = false)
  36. {
  37. $platform = $this->adminModel->where("id", "=", 1)->find();
  38. if (!$platform)
  39. return $this->fail("Admin不存在!");
  40. $massager = $this->massagerModel->getMassager($order->massager_id);
  41. if ($order["massager_id"] > 0) {
  42. if (!$massager)
  43. return $this->fail("助教信息不存在!");
  44. }
  45. $agency = $this->adminModel->findAgency($order->city_code);
  46. $redLock = new RedLock();
  47. $agencyLock = false;
  48. $sLock = false;
  49. $mLock = false;
  50. if ($agency) {
  51. $agencyLock = $redLock->lock(Admin::AgencyKey($agency->id));
  52. if (!is_array($agencyLock))
  53. return $this->fail("请稍后再试!");
  54. }
  55. // 平台抽成比例
  56. $platform_rate = config("site.platform_profit_rate") ?? 6;
  57. // 代理商抽成比例
  58. $agency_rate = 0;
  59. // 渠道商分佣
  60. $parent_direct_invite_rate = 0; // 1级
  61. $store = $this->storeModel->findById($order->store_id ?? -1);
  62. if ($order["store_id"] > 0) { // 球房分润标准
  63. if (!$store)
  64. return $this->fail("球房信息不存在!");
  65. $sLock = $redLock->lock(Store::SWKey($order->store_id));
  66. if (!is_array($sLock))
  67. return $this->fail("请稍后再试!");
  68. $self_rate = 100 - $store["platform_profit_rate"] - $store["agency_profit_rate"];
  69. if ($agency) {
  70. $agency_rate = $store["agency_profit_rate"];
  71. } else {
  72. // 如果代理商不存在 则剩余部分 全部归于平台;
  73. $platform_rate = $store["agency_profit_rate"] + $store["platform_profit_rate"];
  74. }
  75. } else { // 线上分润标准
  76. $mLock = $redLock->lock(\app\api\model\massager\Wallet::MWKey($order->massager_id));
  77. if (!is_array($mLock))
  78. return $this->fail("请稍后再试!");
  79. // 本人比例
  80. $self_rate = (new MassagerService())->getProfitRate($massager["id"], $massager["city_code"], null, null, date("Y-m-d", $order["createtime"]));
  81. if ($agency) {
  82. $agency_rate = 100 - ($platform_rate + $self_rate);
  83. } else {
  84. // 如果代理商不存在 则剩余部分 全部归于平台;
  85. $platform_rate = 100 - $self_rate;
  86. }
  87. }
  88. $parent = null;
  89. $parent_lock = null;
  90. $parent_wallet = null;
  91. if (1 == config("site.invite_grant_switch") && !$unusual && $massager["parent_id"] > 0) { // 渠道奖励发放开启
  92. $parent = $this->massagerModel->get($massager["parent_id"]);
  93. if ($parent) {
  94. $parent_lock = $redLock->lock(Wallet::MWKey($parent->id));
  95. $parent_wallet = $this->mWalletModel->getWallet($parent->id);
  96. $parent_direct_invite_rate = config("site.massager_direct_invite_rate");
  97. }
  98. }
  99. $invite_store = null;
  100. $store_direct_invite_rate = 0;
  101. if (!$unusual && $user["invite_store_id"] > 0) {// 渠道奖励发放开启
  102. $invite_store = $this->storeModel->get($user["invite_store_id"]);
  103. $store_direct_invite_rate = config("site.store_direct_invite_rate");
  104. }
  105. if (fixed2Float(($platform_rate + $agency_rate + $self_rate)) != 100) {
  106. return $this->fail("比例计算错误!");
  107. }
  108. $pLock = $redLock->lock(Admin::PlatformKey());
  109. if (!is_array($pLock))
  110. return $this->fail("请稍后再试!");
  111. $mWallet = $this->mWalletModel->getWallet($order->massager_id);
  112. $locks = [$pLock, $agencyLock, $sLock, $mLock, $parent_lock];
  113. $trip_amount = $order->trip_amount;
  114. // 实际支付金额 - 出行费 = 订单分润金额
  115. $total_profit_amount = $order->total_real_amount - $trip_amount;
  116. if ($unusual && $order["is_refund_trip"] == 1) {
  117. $trip_amount = 0;
  118. }
  119. if ($unusual) { // 管理员终止
  120. $total_profit_amount = $order->total_real_amount - $order->refund_amount - $trip_amount;
  121. }
  122. if ($total_profit_amount <= 0)
  123. return $this->fail("分润金额小于0");
  124. $p_profit_amount = fixed2Float($total_profit_amount * ($platform_rate / 100));
  125. $a_profit_amount = $agency_rate > 0 ? fixed2Float($total_profit_amount * ($agency_rate / 100)) : 0;
  126. $self_profit_amount = fixed2Float($total_profit_amount * ($self_rate / 100));
  127. // 助教直推助教奖励(%)
  128. $parent_profit_amount = $parent_direct_invite_rate > 0 ? fixed2Float($total_profit_amount * ($parent_direct_invite_rate / 100)) : 0;
  129. $total_parent_amount = $parent_profit_amount;
  130. // 球房直推用户奖励(%)
  131. $invite_store_profit_amount = $store_direct_invite_rate > 0 ? fixed2Float($total_profit_amount * ($store_direct_invite_rate / 100)) : 0;
  132. Db::startTrans();
  133. try {
  134. $profit_bills = [
  135. [
  136. "identity_type" => \E_IDENTITY_TYPE::Platform,
  137. "target_id" => 1,
  138. "target_name" => "平台",
  139. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  140. "order_no" => $order->no,
  141. "total_amount" => $total_profit_amount,
  142. "rate" => $platform_rate,
  143. "change" => $p_profit_amount,
  144. "before" => $platform->profit_amount,
  145. "after" => fixed2Float($platform->profit_amount + $p_profit_amount),
  146. "createtime" => time(),
  147. "city_code" => $order->city_code
  148. ]
  149. ];
  150. if ($order->store_id > 0) { // 门店
  151. $after = fixed2Float($store->profit_amount + $self_profit_amount);
  152. $profit_bills[] = [
  153. "identity_type" => \E_IDENTITY_TYPE::Store,
  154. "target_id" => $store->id,
  155. "target_name" => $store->name,
  156. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  157. "order_no" => $order->no,
  158. "total_amount" => $total_profit_amount,
  159. "rate" => $self_rate,
  160. "change" => $self_profit_amount,
  161. "before" => $store->profit_amount,
  162. "after" => $after,
  163. "createtime" => time(),
  164. "city_code" => $order->city_code
  165. ];
  166. $this->storeModel->where("id", $store->id)->setInc("profit_amount", $self_profit_amount);
  167. } else { // 助教
  168. $after = fixed2Float($mWallet->profit_amount + $self_profit_amount);
  169. $profit_bills[] = [
  170. "identity_type" => \E_IDENTITY_TYPE::Massager,
  171. "target_id" => $massager->id,
  172. "target_name" => $massager->name,
  173. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  174. "order_no" => $order->no,
  175. "total_amount" => $total_profit_amount,
  176. "rate" => $self_rate,
  177. "change" => $self_profit_amount - $total_parent_amount - $invite_store_profit_amount,
  178. "before" => $mWallet->profit_amount,
  179. "after" => $after,
  180. "createtime" => time(),
  181. "city_code" => $order->city_code
  182. ];
  183. $m_bills = [
  184. [
  185. "massager_id" => $massager->id,
  186. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  187. "change_type" => \E_M_BILL_CHANGE_TYPE::Profit,
  188. "change" => $self_profit_amount,
  189. "before" => $mWallet->profit_amount,
  190. "after" => $after,
  191. "reason" => "服务费用",
  192. "relation_no" => $order->no,
  193. "createtime" => time()
  194. ]
  195. ];
  196. if ($trip_amount > 0) {
  197. $m_bills[] = [
  198. "massager_id" => $massager->id,
  199. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  200. "change_type" => \E_M_BILL_CHANGE_TYPE::Travel,
  201. "change" => $trip_amount,
  202. "before" => $after,
  203. "after" => $after + $trip_amount,
  204. "reason" => "出行费用",
  205. "relation_no" => $order->no,
  206. "createtime" => time()
  207. ];
  208. }
  209. if ($total_parent_amount > 0) {
  210. $m_bills[] = [
  211. "massager_id" => $massager->id,
  212. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  213. "change_type" => \E_M_BILL_CHANGE_TYPE::ChannelExpend,
  214. "change" => -$total_parent_amount,
  215. "before" => ($after + $trip_amount),
  216. "after" => ($after + $trip_amount) - $total_parent_amount,
  217. "reason" => "技师渠道抽佣",
  218. "relation_no" => $order->no,
  219. "createtime" => time()
  220. ];
  221. }
  222. if ($invite_store_profit_amount > 0) {
  223. $m_bills[] = [
  224. "massager_id" => $massager->id,
  225. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  226. "change_type" => \E_M_BILL_CHANGE_TYPE::ChannelExpend,
  227. "change" => -$invite_store_profit_amount,
  228. "before" => ($after + $trip_amount),
  229. "after" => ($after + $trip_amount) - $total_parent_amount - $invite_store_profit_amount,
  230. "reason" => "门店渠道抽佣",
  231. "relation_no" => $order->no,
  232. "createtime" => time()
  233. ];
  234. }
  235. $this->mWalletModel->where("id", $mWallet->id)->setInc(
  236. "profit_amount",
  237. ($self_profit_amount + $trip_amount - $total_parent_amount - $invite_store_profit_amount)
  238. );
  239. $this->mBillModel->insertAll($m_bills);
  240. }
  241. if ($agency && $a_profit_amount > 0) {
  242. $profit_bills[] = [
  243. "identity_type" => \E_IDENTITY_TYPE::Agency,
  244. "target_id" => $agency->id,
  245. "target_name" => $agency->nickname,
  246. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  247. "order_no" => $order->no,
  248. "total_amount" => $total_profit_amount,
  249. "rate" => $agency_rate,
  250. "change" => $a_profit_amount,
  251. "before" => $agency->profit_amount,
  252. "after" => fixed2Float($agency->profit_amount + $a_profit_amount),
  253. "createtime" => time(),
  254. "city_code" => $order->city_code
  255. ];
  256. $this->adminModel->where("id", $agency->id)->setInc("profit_amount", $a_profit_amount);
  257. }
  258. $this->adminModel->where("id", 1)->setInc("profit_amount", $p_profit_amount);
  259. if ($parent && $total_parent_amount > 0) { // 技师邀请技师存在!
  260. $profit_bills[] = [
  261. "identity_type" => \E_IDENTITY_TYPE::Massager,
  262. "target_id" => $parent->id,
  263. "target_name" => $parent->name,
  264. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::ChannelIncome,
  265. "order_no" => $order->no,
  266. "total_amount" => $total_profit_amount,
  267. "rate" => $parent_direct_invite_rate,
  268. "change" => $parent_profit_amount,
  269. "before" => fixed2Float($parent_wallet->profit_amount),
  270. "after" => fixed2Float($parent_wallet->profit_amount + $parent_profit_amount),
  271. "createtime" => time(),
  272. "city_code" => $order->city_code
  273. ];
  274. $this->mBillModel->save([
  275. "massager_id" => $parent->id,
  276. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  277. "change_type" => \E_M_BILL_CHANGE_TYPE::ChannelIncome,
  278. "change" => $total_parent_amount,
  279. "before" => $parent_wallet["profit_amount"],
  280. "after" => $parent_wallet["profit_amount"] + $total_parent_amount,
  281. "reason" => "渠道抽佣",
  282. "relation_no" => $order->no,
  283. "createtime" => time()
  284. ]);
  285. if ($parent_wallet) {
  286. $this->mWalletModel->where("id", $parent_wallet->id)->setInc("profit_amount", $parent_profit_amount);
  287. }
  288. }
  289. if ($invite_store_profit_amount > 0) { // 门店邀请技师存在 - 渠道抽佣
  290. if ($invite_store) {
  291. $profit_bills[] = [
  292. "identity_type" => \E_IDENTITY_TYPE::Store,
  293. "target_id" => $invite_store->id,
  294. "target_name" => $invite_store->name,
  295. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::ChannelIncome,
  296. "order_no" => $order->no,
  297. "total_amount" => $total_profit_amount,
  298. "rate" => $store_direct_invite_rate,
  299. "change" => $invite_store_profit_amount,
  300. "before" => fixed2Float($invite_store->profit_amount),
  301. "after" => fixed2Float($invite_store->profit_amount + $invite_store_profit_amount),
  302. "createtime" => time(),
  303. "city_code" => $order->city_code
  304. ];
  305. $this->storeModel->where("id", $invite_store->id)->setInc("profit_amount", $invite_store_profit_amount);
  306. }
  307. }
  308. $this->pBillModel->insertAll($profit_bills);
  309. Db::commit();
  310. return $this->ok(true);
  311. } catch (Exception $e) {
  312. Db::rollback();
  313. return $this->fail($e->getMessage());
  314. } finally {
  315. foreach ($locks as $lock) {
  316. if (is_array($lock))
  317. $redLock->unlock($lock);
  318. }
  319. }
  320. }
  321. public function profitV2($order, $user, $unusual = false)
  322. {
  323. $platform = $this->adminModel->where("id", "=", 1)->find();
  324. if (!$platform)
  325. return $this->fail("Admin不存在!");
  326. $massager = $this->massagerModel->getMassager($order->massager_id);
  327. if ($order["massager_id"] > 0) {
  328. if (!$massager)
  329. return $this->fail("助教信息不存在!");
  330. }
  331. $agency = $this->adminModel->findAgency($order->city_code);
  332. $redLock = new RedLock();
  333. $agencyLock = false;
  334. $sLock = false;
  335. $mLock = false;
  336. if ($agency) {
  337. $agencyLock = $redLock->lock(Admin::AgencyKey($agency->id));
  338. if (!is_array($agencyLock))
  339. return $this->fail("请稍后再试!");
  340. }
  341. // 平台抽成比例
  342. $platform_rate = 10;
  343. // 代理商抽成比例
  344. $agency_rate = 0;
  345. $store = $this->storeModel->findById($order->store_id ?? -1);
  346. $self_rate = 0;
  347. if ($order["store_id"] > 0) { // 球房分润标准
  348. } else { // 线上分润标准
  349. $mLock = $redLock->lock(\app\api\model\massager\Wallet::MWKey($order->massager_id));
  350. if (!is_array($mLock))
  351. return $this->fail("请稍后再试");
  352. // 本人比例
  353. $self_rate = 80;
  354. if ($agency) {
  355. $agency_rate = 10;
  356. } else {
  357. // 如果代理商不存在 则剩余部分 全部归于平台;
  358. $platform_rate = 20;
  359. }
  360. }
  361. if (fixed2Float(($platform_rate + $agency_rate + $self_rate)) != 100) {
  362. return $this->fail("比例计算错误!");
  363. }
  364. $pLock = $redLock->lock(Admin::PlatformKey());
  365. if (!is_array($pLock))
  366. return $this->fail("请稍后再试!");
  367. $mWallet = $this->mWalletModel->getWallet($order->massager_id);
  368. $locks = [$pLock, $agencyLock, $sLock, $mLock];
  369. $trip_amount = $order->trip_amount;
  370. // 实际支付金额 - 出行费 = 订单分润金额
  371. $total_profit_amount = $order->total_real_amount - $trip_amount;
  372. if ($unusual && $order["is_refund_trip"] == 1) {
  373. $trip_amount = 0;
  374. }
  375. if ($unusual) { // 管理员终止
  376. $total_profit_amount = $order->total_real_amount - $order->refund_amount - $trip_amount;
  377. }
  378. if ($total_profit_amount <= 0)
  379. return $this->fail("分润金额小于0");
  380. $p_profit_amount = fixed2Float($total_profit_amount * ($platform_rate / 100));
  381. $a_profit_amount = $agency_rate > 0 ? fixed2Float($total_profit_amount * ($agency_rate / 100)) : 0;
  382. $self_profit_amount = fixed2Float($total_profit_amount * ($self_rate / 100));
  383. Db::startTrans();
  384. try {
  385. $profit_bills = [
  386. [
  387. "identity_type" => \E_IDENTITY_TYPE::Platform,
  388. "target_id" => 1,
  389. "target_name" => "平台",
  390. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  391. "order_no" => $order->no,
  392. "total_amount" => $total_profit_amount,
  393. "rate" => $platform_rate,
  394. "change" => $p_profit_amount,
  395. "before" => $platform->profit_amount,
  396. "after" => fixed2Float($platform->profit_amount + $p_profit_amount),
  397. "createtime" => time(),
  398. "city_code" => $order->city_code
  399. ]
  400. ];
  401. if ($order->store_id > 0) { // 门店
  402. $after = fixed2Float($store->profit_amount + $self_profit_amount);
  403. $profit_bills[] = [
  404. "identity_type" => \E_IDENTITY_TYPE::Store,
  405. "target_id" => $store->id,
  406. "target_name" => $store->name,
  407. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  408. "order_no" => $order->no,
  409. "total_amount" => $total_profit_amount,
  410. "rate" => $self_rate,
  411. "change" => $self_profit_amount,
  412. "before" => $store->profit_amount,
  413. "after" => $after,
  414. "createtime" => time(),
  415. "city_code" => $order->city_code
  416. ];
  417. $this->storeModel->where("id", $store->id)->setInc("profit_amount", $self_profit_amount);
  418. } else { // 助教
  419. $after = fixed2Float($mWallet->profit_amount + $self_profit_amount);
  420. $profit_bills[] = [
  421. "identity_type" => \E_IDENTITY_TYPE::Massager,
  422. "target_id" => $massager->id,
  423. "target_name" => $massager->name,
  424. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  425. "order_no" => $order->no,
  426. "total_amount" => $total_profit_amount,
  427. "rate" => $self_rate,
  428. "change" => $self_profit_amount,
  429. "before" => $mWallet->profit_amount,
  430. "after" => $after,
  431. "createtime" => time(),
  432. "city_code" => $order->city_code
  433. ];
  434. $m_bills = [
  435. [
  436. "massager_id" => $massager->id,
  437. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  438. "change_type" => \E_M_BILL_CHANGE_TYPE::Profit,
  439. "change" => $self_profit_amount,
  440. "before" => $mWallet->profit_amount,
  441. "after" => $after,
  442. "reason" => "服务费用",
  443. "relation_no" => $order->no,
  444. "createtime" => time()
  445. ]
  446. ];
  447. if ($trip_amount > 0) {
  448. $m_bills[] = [
  449. "massager_id" => $massager->id,
  450. "currency_type" => \E_USER_BILL_CURRENCY_TYPE::Money,
  451. "change_type" => \E_M_BILL_CHANGE_TYPE::Travel,
  452. "change" => $trip_amount,
  453. "before" => $after,
  454. "after" => $after + $trip_amount,
  455. "reason" => "出行费用",
  456. "relation_no" => $order->no,
  457. "createtime" => time()
  458. ];
  459. }
  460. $this->mWalletModel->where("id", $mWallet->id)->setInc(
  461. "profit_amount",
  462. ($self_profit_amount + $trip_amount)
  463. );
  464. $this->mBillModel->insertAll($m_bills);
  465. }
  466. if ($agency && $a_profit_amount > 0) {
  467. $profit_bills[] = [
  468. "identity_type" => \E_IDENTITY_TYPE::Agency,
  469. "target_id" => $agency->id,
  470. "target_name" => $agency->nickname,
  471. "change_type" => \E_PROFIT_BILL_CHANGE_TYPE::Profit,
  472. "order_no" => $order->no,
  473. "total_amount" => $total_profit_amount,
  474. "rate" => $agency_rate,
  475. "change" => $a_profit_amount,
  476. "before" => $agency->profit_amount,
  477. "after" => fixed2Float($agency->profit_amount + $a_profit_amount),
  478. "createtime" => time(),
  479. "city_code" => $order->city_code
  480. ];
  481. $this->adminModel->where("id", $agency->id)->setInc("profit_amount", $a_profit_amount);
  482. }
  483. $this->adminModel->where("id", 1)->setInc("profit_amount", $p_profit_amount);
  484. $this->pBillModel->insertAll($profit_bills);
  485. Db::commit();
  486. return $this->ok(true);
  487. } catch (Exception $e) {
  488. Db::rollback();
  489. return $this->fail($e->getMessage());
  490. } finally {
  491. foreach ($locks as $lock) {
  492. if (is_array($lock))
  493. $redLock->unlock($lock);
  494. }
  495. }
  496. }
  497. }