You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
20 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\Service;
  3. use App\Commons\Log;
  4. use App\Constants\LogLabel;
  5. use App\Constants\SsdbKeysPrefix;
  6. use App\Model\FinancialRecord;
  7. use App\Model\Market;
  8. use App\Model\MmInfo;
  9. use App\Model\MpInfo;
  10. use App\Model\Order;
  11. use App\Model\OrderMain;
  12. use App\Model\ServiceReward;
  13. use App\Model\Store;
  14. use App\Model\StoreAccount;
  15. use App\Model\SystemConfig;
  16. use App\Model\UserBalance;
  17. use App\Model\UserRelationBind;
  18. use App\Model\Users;
  19. use App\TaskWorker\SSDBTask;
  20. use Hyperf\DbConnection\Db;
  21. use Hyperf\Di\Annotation\Inject;
  22. use Hyperf\Utils\ApplicationContext;
  23. class SeparateAccountsService implements SeparateAccountsServiceInterface
  24. {
  25. /**
  26. * @Inject
  27. * @var Log
  28. */
  29. protected $log;
  30. /**
  31. * @Inject
  32. * @var UserServiceInterface
  33. */
  34. protected $userService;
  35. /**
  36. * @Inject
  37. * @var FinancialRecordServiceInterface
  38. */
  39. protected $financialRecordService;
  40. /**
  41. * @Inject
  42. * @var MiniprogramServiceInterface
  43. */
  44. protected $miniprogramService;
  45. /**
  46. * @inheritDoc
  47. */
  48. public function orderOnlinePaid($global_order_id)
  49. {
  50. // 线上订单支付完成
  51. // 订单
  52. $orderMain = OrderMain::query()
  53. ->where(['global_order_id' => $global_order_id])
  54. ->first();
  55. if (empty($orderMain)) {
  56. return false;
  57. }
  58. // =======用户支付流水 / Start=======
  59. $this->financialRecordService->userByOLOrderPaid($orderMain->user_id, $global_order_id, $orderMain->money);
  60. // =======用户支付流水 / End=======
  61. }
  62. /**
  63. * @inheritDoc
  64. */
  65. public function orderOnlineCompleted($global_order_id)
  66. {
  67. // 线上订单完成(用户点击确认收货完成/管理后台点击完成/配送员点击完成/自动收货等),进行相关分账
  68. // 订单
  69. $orderMain = OrderMain::query()
  70. ->where(['global_order_id' => $global_order_id])
  71. ->whereIn('state', [OrderMain::ORDER_STATE_COMPLETE,OrderMain::ORDER_STATE_EVALUATED,OrderMain::ORDER_STATE_UNREFUND])
  72. ->first();
  73. if (empty($orderMain)) {
  74. return false;
  75. }
  76. $currentTime = time();
  77. Db::beginTransaction();
  78. try {
  79. $ssdb = ApplicationContext::getContainer()->get(SSDBTask::class);
  80. // =======商户订单收入流水 / Start=======
  81. // 查询子订单
  82. $orders = Order::query()->select(['id', 'money', 'user_id', 'store_id', 'pay_time'])
  83. ->where(['order_main_id' => $orderMain->id])
  84. ->get()->toArray();
  85. foreach ($orders as $key => &$order) {
  86. // 商户
  87. $store = Store::find($order['store_id']);
  88. // 旧商户流水基础数据 TODO 直接移除或后续考虑移除
  89. $storeAccountBase = [
  90. 'user_id' => $order['user_id'],
  91. 'order_id' => $order['id'],
  92. 'store_id' => $order['store_id'],
  93. 'type' => 1,
  94. 'time' => date('Y-m-d H:i:s', $currentTime),
  95. 'add_time' => $currentTime,
  96. ];
  97. // 旧商户流水 TODO 直接移除或后续考虑移除
  98. $storeAccount = [
  99. 'money' => $order['money'],
  100. 'note' => '线上订单',
  101. 'category' => 1,
  102. ];
  103. StoreAccount::query()->insert(array_merge($storeAccountBase, $storeAccount));
  104. // 新商户流水
  105. $this->financialRecordService->storeByOLOrderComp($store->user_id, $global_order_id ,$order['money']);
  106. }
  107. // =======商户订单收入流水 / End=======
  108. // =======社区服务点分账 / Start=======
  109. // 前提:用户线上下单并且订单完成
  110. // 奖励规则A:用户是平台新用户,奖励社区服务点平台新用户奖励x元+平台新用户首单奖励y元+订单商品金额z%的分成
  111. // 奖励规则B:用户是非新用户,奖励社区服务点订单实际支付金额z%的分成
  112. // =======社区服务点分账 / Start=======
  113. // 当前用户的社区服务点绑定关系
  114. $communityBind = UserRelationBind::query()
  115. ->where(['bind_type' => UserRelationBind::BIND_TYPE_COMMUNITY, 'user_id' => $orderMain->user_id])
  116. ->first();
  117. if ($communityBind) {
  118. // 奖励/分账金额
  119. $award = ServiceReward::query()->where(['type' => ServiceReward::TYPE_COMMUNITY])->first();
  120. if (empty($award)) {
  121. Db::rollBack();
  122. return false;
  123. }
  124. $award = $award->set_reward;
  125. // 平台新用户
  126. if ($this->userService->isPlatformNewUser($orderMain->user_id, $orderMain->id)) {
  127. $this->financialRecordService->communityAwardByPlatNewUser($communityBind->source_id, $global_order_id, $award['new_user_reward']);
  128. $this->financialRecordService->communityAwardByPlatNewUserFirstOLOrder($communityBind->source_id, $global_order_id, $award['first_reward']);
  129. }
  130. // 账单分成
  131. if ($orderMain->type == OrderMain::ORDER_TYPE_ONLINE) {
  132. $money = bcmul($orderMain->money, bcdiv($award['flow_reward'], 100, 6), 2);
  133. $this->financialRecordService->communitySeparateAccountsByOrderComp($communityBind->source_id, $global_order_id, $money);
  134. }
  135. }
  136. // =======社区服务点分账 / End=======
  137. // =======服务商、市场经理奖励分账 / Start=======
  138. // 前提A:新用户下单并且订单完成(线上、线下都行)
  139. // 奖励规则A:用户是平台新用户,奖励市场经理 1 元,服务商 0.5 元,如果是线上订单,服务商有6%的订单分成
  140. // 前提B:新商户旗下产生 10 个新用户
  141. // 奖励规则B:奖励市场经理 25 元,服务商 10 元(仅仅奖励一次)
  142. // 前提C:用户线上下单并且订单完成
  143. // 奖励规则C:奖励服务商账单 6% 分成
  144. // 判断是新商户:入驻绑定的时候把关系存在SSDB
  145. // =======服务商、市场经理奖励分账 / Start=======
  146. $MmMpAwardConfig = [
  147. 'mm_new_user' => 1,
  148. 'mm_new_store' => 25,
  149. 'mp_new_user' => 0.5,
  150. 'mp_new_store' => 10,
  151. 'separate_rate' => 6,
  152. 'limit_new_user_number' => 10,
  153. ];
  154. foreach ($orders as $key => &$order) {
  155. // 当前订单(子)对应商户是否有市场经理绑定关系
  156. $store = Store::query()->find($order['store_id']); // 商户
  157. // $mmInfo = MmInfo::query()->where(['user_id' => $store->mm_user_id])->first(); // 市场经理
  158. // if (empty($mmInfo)) continue;
  159. // $market = Market::query()->find($mmInfo->market_id); // 市场
  160. $market = Market::query()->find($orderMain->market_id); // 市场
  161. if (empty($market)) continue;
  162. $mpInfo = MpInfo::query()->find($market->mp_id); // 服务商
  163. if (empty($mpInfo)) continue;
  164. // $ssdbName = 'mm_'.$mmInfo->id.'_award_'.$store->id;
  165. $ssdbName = 'mm_'.$mpInfo->id.'_award_'.$store->id;
  166. // TODO 暂时在这里初始化
  167. if (!$ssdb->exec('hexists', $ssdbName, 'is_awarded')) {
  168. $ssdb->exec('hset', $ssdbName, 'is_awarded', 0);
  169. }
  170. if (!$ssdb->exec('hexists', $ssdbName, 'new_user_number')) {
  171. $ssdb->exec('hset', $ssdbName, 'new_user_number', 0);
  172. }
  173. // 平台新用户
  174. if ($this->userService->isPlatformNewUser($orderMain->user_id, $orderMain->id)) {
  175. $ssdb->exec('hincr', $ssdbName, 'new_user_number', 1);
  176. // $this->financialRecordService->mmAwardByPlatNewUser($mmInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mm_new_user'], '发展新用户'); // 市场经理新用户奖励
  177. // $this->financialRecordService->mpAwardByPlatNewUser($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_user'], '市场经理「'.$mmInfo->name.'」发展新用户'); // 服务商新用户奖励
  178. $this->financialRecordService->mpAwardByPlatNewUser($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_user'], '发展新用户'); // 服务商新用户奖励
  179. }
  180. $record = $ssdb->exec('hgetall', $ssdbName);
  181. // 判断是否已经奖励过新拓展商户的奖励,没有的话判断新用户个数是否达到要求,进行奖励
  182. if (
  183. !empty($record)
  184. &&$record['is_awarded']==0
  185. &&$record['new_user_number']>=$MmMpAwardConfig['limit_new_user_number']
  186. ) { // 存在记录且未发放奖励,同时新用户数已经超过10
  187. $ssdb->exec('hset', $ssdbName, 'is_awarded', 1);
  188. // $this->financialRecordService->mmAwardByNewStore($mmInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mm_new_store'], '发展新商户【'.$store->name.'】'); // 市场经理新商户奖励
  189. // $this->financialRecordService->mpAwardByNewStore($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_store'], '市场经理「'.$mmInfo->name.'」发展新商户【'.$store->name.'】'); // 服务商新商户奖励
  190. $this->financialRecordService->mpAwardByNewStore($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_store'], '发展新商户【'.$store->name.'】'); // 服务商新商户奖励
  191. }
  192. // 线上订单服务商分账
  193. if ($orderMain->type == OrderMain::ORDER_TYPE_ONLINE) {
  194. $rate = bcdiv($order['money'], $orderMain->total_money, 6);
  195. $preMoney = bcmul($orderMain->money, $rate, 6);
  196. $money = bcmul($preMoney, bcdiv($MmMpAwardConfig['separate_rate'], 100, 6), 2);
  197. $this->financialRecordService->mpSeparateAccountByOLOrderComp($mpInfo->admin_user_id, $global_order_id, $money);
  198. }
  199. }
  200. // =======服务商、市场经理奖励分账 / End=======
  201. Db::commit();
  202. return true;
  203. } catch (\Exception $e) {
  204. $this->log->event(LogLabel::SEPARATE_ACCOUNTS_LOG, ['exception' => $e->getMessage(), 'order_main' => json_encode($orderMain)]);
  205. Db::rollBack();
  206. return false;
  207. }
  208. }
  209. /**
  210. * @inheritDoc
  211. */
  212. public function orderOfflinePaid($global_order_id)
  213. {
  214. // 线下订单支付完成
  215. // 订单
  216. $orderMain = OrderMain::query()
  217. ->where(['global_order_id' => $global_order_id])
  218. ->first();
  219. if (empty($orderMain)) {
  220. return false;
  221. }
  222. // 查询子订单,当面付目前实际上只有一个子订单
  223. $order = Order::query()->select(['id', 'money', 'user_id', 'store_id', 'pay_time'])
  224. ->where(['order_main_id' => $orderMain->id])
  225. ->first();
  226. if (empty($order)) {
  227. return false;
  228. }
  229. $currentTime = time();
  230. Db::beginTransaction();
  231. try {
  232. // =======用户支付流水 / Start=======
  233. $this->financialRecordService->userByOFLOrderPaid($orderMain->user_id, $global_order_id, $orderMain->money);
  234. // =======用户支付流水 / End=======
  235. // =======线下订单支付完成商户分账 / Start=======
  236. // 前提:用户线上下单并且支付完成
  237. // 奖励规则A:用户是平台新用户,奖励商户2元
  238. // 奖励规则B:用户是非新用户,但是是商户当日首单,奖励商户0.05元
  239. // =======线下订单支付完成商户分账 / Start=======
  240. // 旧商户订单流水基础数据 TODO 直接移除或后续考虑移除
  241. $storeAccountBase = [
  242. 'user_id' => $order->user_id,
  243. 'order_id' => $order->id,
  244. 'store_id' => $order->store_id,
  245. 'type' => 1,
  246. 'time' => date('Y-m-d H:i:s', $currentTime),
  247. 'add_time' => $currentTime,
  248. ];
  249. // 旧商户订单流水 TODO 直接移除或后续考虑移除
  250. $storeAccount = [
  251. 'money' => $order->money,
  252. 'note' => '当面付订单收入',
  253. 'category' => 2,
  254. ];
  255. StoreAccount::query()->insert(array_merge($storeAccountBase, $storeAccount));
  256. // 商户
  257. $store = Store::find($order->store_id);
  258. // 新商户订单流水
  259. $this->financialRecordService->storeByOFLOrderComp($store->user_id, $global_order_id, $order->money);
  260. $needAward = false;
  261. $awardAmount = 0;
  262. // 新用户商户奖励
  263. if ($this->userService->isPlatformNewUser($orderMain->user_id, $orderMain->id)) {
  264. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_new_user'])->value('value');
  265. // 旧商户流水 TODO 直接移除或后续考虑移除
  266. $storeAccount = [
  267. 'money' => $awardAmount,
  268. 'note' => '新用户下单成功,平台奖励',
  269. 'category' => 3,
  270. ];
  271. // 新商户流水
  272. $this->financialRecordService->storeAwardByPlatNewUserOFLOrder($store->user_id, $global_order_id, $awardAmount);
  273. $needAward = true;
  274. }
  275. else {
  276. // 商户当日首单奖励
  277. if (
  278. $this->userService->isStoreFirstOrderToday(
  279. $order->user_id,
  280. $order->store_id,
  281. $order->id,
  282. FinancialRecord::OFL_FIRST_AWARD_LIMIT_AMOUNT
  283. )
  284. && $order->money >= FinancialRecord::OFL_FIRST_AWARD_LIMIT_AMOUNT
  285. ) {
  286. $awardAmount = SystemConfig::query()->where(['type' => 1, 'menu_name' => 'award_each_order'])->value('value');
  287. // 旧商户流水 TODO 直接移除或后续考虑移除
  288. $storeAccount = [
  289. 'money' => $awardAmount,
  290. 'note' => '用户下单成功,平台奖励',
  291. 'category' => 4,
  292. ];
  293. // 新商户流水
  294. $this->financialRecordService->storeAwardByTodayFirstOFLOrder($store->user_id, $global_order_id, $awardAmount);
  295. $needAward = true;
  296. }
  297. }
  298. if ($needAward && $awardAmount) {
  299. // 旧商户流水 TODO 直接移除或后续考虑移除
  300. StoreAccount::query()->insert(array_merge($storeAccountBase, $storeAccount));
  301. // 发模板消息
  302. $openid = Users::query()->where(['id' => $store['user_id']])->value('openid');
  303. $res = $this->miniprogramService->sendTemMsgForAward($storeAccount['money'], $storeAccount['note'], $openid, $storeAccountBase['time']);
  304. }
  305. // =======线下订单支付完成商户分账 / End=======
  306. // =======服务商、市场经理奖励分账 / Start=======
  307. // 前提A:新用户下单并且订单完成(线上、线下都行)
  308. // 奖励规则A:用户是平台新用户,奖励市场经理 1 元,服务商 0.5 元,如果是线上订单,服务商有6%的订单分成
  309. // 前提B:新商户旗下产生 10 个新用户
  310. // 奖励规则B:奖励市场经理 25 元,服务商 10 元(仅仅奖励一次)
  311. // 前提C:用户线上下单并且订单完成
  312. // 奖励规则C:奖励服务商账单 6% 分成
  313. // 判断是新商户:入驻绑定的时候把关系存在SSDB
  314. // =======服务商、市场经理奖励分账 / Start=======
  315. $MmMpAwardConfig = [
  316. 'mm_new_user' => 1,
  317. 'mm_new_store' => 25,
  318. 'mp_new_user' => 0.5,
  319. 'mp_new_store' => 10,
  320. 'separate_rate' => 6,
  321. 'limit_new_user_number' => 10,
  322. ];
  323. // 当前订单(子)对应商户是否有市场经理绑定关系
  324. $store = Store::query()->find($order['store_id']); // 商户
  325. // $mmInfo = MmInfo::query()->where(['user_id' => $store->mm_user_id])->first(); // 市场经理
  326. $market = Market::query()->find($orderMain->market_id); // 市场
  327. $mpInfo = MpInfo::query()->find($market->mp_id); // 服务商
  328. if (!empty($mpInfo)) {
  329. $ssdb = ApplicationContext::getContainer()->get(SSDBTask::class);
  330. // $ssdbName = 'mm_'.$mmInfo->id.'_award_'.$store->id;
  331. $ssdbName = 'mm_'.$mpInfo->id.'_award_'.$store->id;
  332. // TODO 暂时在这里初始化
  333. if (!$ssdb->exec('hexists', $ssdbName, 'is_awarded')) {
  334. $ssdb->exec('hset', $ssdbName, 'is_awarded', 0);
  335. }
  336. if (!$ssdb->exec('hexists', $ssdbName, 'new_user_number')) {
  337. $ssdb->exec('hset', $ssdbName, 'new_user_number', 0);
  338. }
  339. // 平台新用户
  340. if ($this->userService->isPlatformNewUser($orderMain->user_id, $orderMain->id)) {
  341. $ssdb->exec('hincr', $ssdbName, 'new_user_number', 1);
  342. // $this->financialRecordService->mmAwardByPlatNewUser($mmInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mm_new_user'], '发展新用户'); // 市场经理新用户奖励
  343. // $this->financialRecordService->mpAwardByPlatNewUser($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_user'], '市场经理「'.$mmInfo->name.'」发展新用户'); // 服务商新用户奖励
  344. $this->financialRecordService->mpAwardByPlatNewUser($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_user'], '发展新用户'); // 服务商新用户奖励
  345. }
  346. $record = $ssdb->exec('hgetall', $ssdbName);
  347. // 判断是否已经奖励过新拓展商户的奖励,没有的话判断新用户个数是否达到要求,进行奖励
  348. if (
  349. !empty($record)
  350. &&$record['is_awarded']==0
  351. &&$record['new_user_number']>=$MmMpAwardConfig['limit_new_user_number']
  352. ) { // 存在记录且未发放奖励,同时新用户数已经超过10
  353. $ssdb->exec('hset', $ssdbName, 'is_awarded', 1);
  354. // $this->financialRecordService->mmAwardByNewStore($mmInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mm_new_store'], '发展新商户【'.$store->name.'】'); // 市场经理新商户奖励
  355. // $this->financialRecordService->mpAwardByNewStore($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_store'], '市场经理「'.$mmInfo->name.'」发展新新商户【'.$store->name.'】'); // 服务商新商户奖励
  356. $this->financialRecordService->mpAwardByNewStore($mpInfo->admin_user_id, $global_order_id, $MmMpAwardConfig['mp_new_store'], '发展新商户【'.$store->name.'】'); // 服务商新商户奖励
  357. }
  358. }
  359. // =======服务商、市场经理奖励分账 / End=======
  360. Db::commit();
  361. return true;
  362. } catch (\Exception $e) {
  363. $this->log->event(LogLabel::SEPARATE_ACCOUNTS_LOG, ['exception' => $e->getMessage(), 'order_main' => json_encode($orderMain)]);
  364. Db::rollBack();
  365. return false;
  366. }
  367. }
  368. }