LossAmountService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. <?php
  2. namespace addons\CloudStock\common\services;
  3. use addons\CloudStock\common\models\CloudStockAgent;
  4. use addons\CloudStock\common\models\CloudStockGoods;
  5. use addons\CloudStock\common\models\CloudStockLossAmount;
  6. use addons\CloudStock\common\models\CloudStockPaymentLog;
  7. use common\components\export\CsvTool;
  8. use common\enums\DownloadEnum;
  9. use common\enums\StatusEnum;
  10. use common\helpers\DateHelper;
  11. use common\models\common\Download;
  12. use common\models\goods\Goods;
  13. use common\models\order\Order;
  14. use common\models\user\User;
  15. use common\models\user\UserPartitionRelationship;
  16. class LossAmountService extends BaseService
  17. {
  18. public $order_no;
  19. public function typeMap()
  20. {
  21. //1:上级配货,2:下级补货;3:会员买货
  22. return [
  23. 1 => '上级配货',
  24. 2 => '下级补货',
  25. 3 => '会员买货'
  26. ];
  27. }
  28. public function index($params)
  29. {
  30. $this->getWhere($params, $where);
  31. $params['where'] = $where;
  32. $params['order'] = 'id desc';
  33. $page_data = CloudStockLossAmount::listPage($params, false, true);
  34. if (!empty($page_data['list'])) {
  35. $user_ids = array_column($page_data['list'], 'user_id');
  36. $child_ids = array_column($page_data['list'], 'child_id');
  37. $goods_ids = array_column($page_data['list'], 'goods_id');
  38. $goods_list = Goods::lists(['where' => [
  39. ['id' => $goods_ids],
  40. ['mall_id' => $this->mall_id]
  41. ],
  42. 'index' => 'id',
  43. ]);
  44. $user_list = User::lists(['where' => [
  45. ['id' => array_merge($user_ids, $child_ids)],
  46. ['mall_id' => $this->mall_id]
  47. ],
  48. 'index' => 'id',
  49. ]);
  50. foreach ($page_data['list'] as &$item) {
  51. $item['type_text'] = $this->typeMap()[$item['type']] ?? '';
  52. $item['user'] = $item['from_user'] = [];
  53. $item['cover_pic'] = $item['goods_name'] = '';
  54. if (isset($user_list[$item['user_id']])) {
  55. $item['user'] = [
  56. 'id' => $user_list[$item['user_id']]['id'],
  57. 'nickname' => $user_list[$item['user_id']]['nickname'],
  58. 'mobile' => $user_list[$item['user_id']]['mobile'],
  59. 'avatar_url' => $user_list[$item['user_id']]['avatar_url'],
  60. ];
  61. }
  62. if (isset($goods_list[$item['goods_id']])) {
  63. $item['cover_pic'] = $goods_list[$item['goods_id']]['cover_pic'];
  64. $item['goods_name'] = $goods_list[$item['goods_id']]['goods_name'];
  65. }
  66. if (isset($user_list[$item['child_id']])) {
  67. $item['from_user'] = [
  68. 'id' => $user_list[$item['child_id']]['id'],
  69. 'nickname' => $user_list[$item['child_id']]['nickname'],
  70. 'mobile' => $user_list[$item['child_id']]['mobile'],
  71. 'avatar_url' => $user_list[$item['child_id']]['avatar_url'],
  72. ];
  73. }
  74. }
  75. }
  76. return $page_data;
  77. }
  78. public function lossAmountList($params)
  79. {
  80. if (empty($params['month'])) {
  81. $month = date('Y-m');
  82. } else {
  83. $month = $params['month'];
  84. }
  85. $start_time = strtotime($month . '-01 00:00:00');
  86. $end_time = DateHelper::getMonthLastDayTime($start_time);
  87. $params['date'][0] = date('Y-m-d', $start_time);
  88. $params['date'][1] = date('Y-m-d', $end_time);
  89. $this->getWhere($params, $where);
  90. $params['where'] = $where;
  91. $params['order'] = 'id desc';
  92. $page_data = CloudStockLossAmount::listPage($params, false, true);
  93. if (!empty($page_data['list'])) {
  94. $user_ids = array_column($page_data['list'], 'user_id');
  95. $child_ids = array_column($page_data['list'], 'child_id');
  96. $goods_ids = array_column($page_data['list'], 'goods_id');
  97. $goods_list = Goods::lists(['where' => [
  98. ['id' => $goods_ids],
  99. ['mall_id' => $this->mall_id]
  100. ],
  101. 'index' => 'id',
  102. ]);
  103. $user_list = User::lists(['where' => [
  104. ['id' => array_merge($user_ids, $child_ids)],
  105. ['mall_id' => $this->mall_id]
  106. ],
  107. 'index' => 'id',
  108. ]);
  109. foreach ($page_data['list'] as &$item) {
  110. $item['type_text'] = $this->typeMap()[$item['type']] ?? '';
  111. $item['from_user_id'] = $item['from_user_mobile'] = '';
  112. $item['goods_name'] = '';
  113. if (isset($goods_list[$item['goods_id']])) {
  114. $item['goods_name'] = $goods_list[$item['goods_id']]['goods_name'];
  115. }
  116. if (isset($user_list[$item['child_id']])) {
  117. $item['from_user_id'] = $user_list[$item['child_id']]['id'];
  118. $item['from_user_mobile'] = $user_list[$item['child_id']]['mobile'];
  119. }
  120. }
  121. }
  122. return $page_data;
  123. }
  124. protected function getWhere($params, &$where)
  125. {
  126. $where[] = ['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED];
  127. if (!empty($params['keyword'])) {
  128. $user_list = User::lists(['where' => [
  129. ['mall_id' => $this->mall_id],
  130. ['or',
  131. ['like', 'nickname', $params['keyword']],
  132. ['like', 'mobile', $params['keyword']]]],
  133. 'select' => 'id']);
  134. if (!empty($user_list)) {
  135. $user_ids = array_column($user_list, 'id');
  136. $where[] = ['user_id' => $user_ids];
  137. } else {
  138. $where[] = ['user_id' => []];
  139. }
  140. }
  141. if (!empty($params['order_no'])) {
  142. $where[] = ['order_no' => trim($params['order_no'])];
  143. }
  144. if (!empty($params['user_id'])) {
  145. $where[] = ['user_id' => $params['user_id']];
  146. }
  147. if (!empty($params['type'])) {
  148. $where[] = ['type' => trim($params['type'])];
  149. }
  150. if (!empty($params['date'][0])) {
  151. $where[] = ['>=', 'created_at', strtotime($params['date'][0] . ' 00:00:00')];
  152. }
  153. if (!empty($params['date'][1])) {
  154. $where[] = ['<=', 'created_at', strtotime($params['date'][1] . ' 23:59:59')];
  155. }
  156. }
  157. public function getListExportFieldList()
  158. {
  159. return $this->exportFieldsList();
  160. }
  161. public function exportFieldsList()
  162. {
  163. return [
  164. 'user_id' => '用户ID',
  165. 'nickname' => '用户昵称',
  166. 'mobile' => '手机号',
  167. 'type_text' => '业务类型',
  168. 'money' => '流失金额',
  169. 'order_no' => '订单编号',
  170. 'created_at' => '创建时间',
  171. ];
  172. }
  173. public static function exportData($params)
  174. {
  175. (new LossAmountService(['mall_id' => $params['mall_id']]))->export($params);
  176. }
  177. public function add($params)
  178. {
  179. $res = CloudStockPaymentLog::add($params);
  180. if (empty($res)) {
  181. throw new \Exception(CloudStockPaymentLog::$static_error);
  182. }
  183. if (empty($params['status'])) {
  184. $params['is_frozen'] = true;
  185. } else {
  186. $params['is_frozen'] = false;
  187. }
  188. WithdrawService::handle($params);
  189. return $res;
  190. }
  191. public function export($data)
  192. {
  193. ini_set('memory_limit', '512M');
  194. $download_id = $data['download_id'] ?? 0;
  195. $download = Download::findOne(['id' => $download_id]);
  196. if ($download) {
  197. try {
  198. $params = json_decode($download->params, true);
  199. $filename = $download->name;
  200. $type = $download->type;
  201. $where = [];
  202. $this->getWhere($params, $where);
  203. $params['where'] = $where;
  204. $params['order'] = 'id desc';
  205. $list = CloudStockLossAmount::lists($params);
  206. $fields = $params['fields'];
  207. $fields_arr = $this->exportFieldsList();
  208. $have_select_field_arr = [];
  209. foreach ($fields as $value) {
  210. $have_select_field_arr[] = $fields_arr[$value];
  211. }
  212. $csv = new CsvTool();
  213. $csv->filename = $filename;
  214. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  215. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  216. $csv->header = $have_select_field_arr;
  217. $user_id_arr = array_column($list, 'user_id');
  218. $user_arr = User::lists([
  219. 'where' => [
  220. ['id' => $user_id_arr]
  221. ],
  222. 'index' => 'id',
  223. ]);
  224. $csv->data = array_map(function ($item) use ($fields, $user_arr) {
  225. $row = [];
  226. foreach ($fields as $field) {
  227. switch (true) {
  228. case $field == 'created_at':
  229. $row[] = !empty($item[$field]) ? date('Y-m-d H:i:s', $item[$field]) : '';
  230. break;
  231. case $field == 'mobile':
  232. $row[] = $user_arr[$item['user_id']]['mobile'] ?? '';
  233. break;
  234. case $field == 'type_text':
  235. $row[] = $this->typeMap()[$item['type']] ?? '';
  236. break;
  237. case $field == 'nickname':
  238. $row[] = $user_arr[$item['user_id']]['nickname'] ?? '';
  239. break;
  240. default:
  241. $row[] = !empty($item[$field]) ? $item[$field] : '';
  242. }
  243. }
  244. return $row;
  245. }, $list);
  246. $csv->export();
  247. $csv->updateDown($download, $params['mall_id']);
  248. return true;
  249. } catch (\Exception $e) {
  250. $download->status = 3;
  251. $res = $download->save();
  252. return $e->getMessage();
  253. }
  254. }
  255. }
  256. /**
  257. * 计算流失金额
  258. * @param $type
  259. * @param $num
  260. * @param $order
  261. * @param $parent_agent
  262. * @return true|void
  263. */
  264. public function compute($type, $num, $order, $parent_agent = null)
  265. {
  266. switch ($type) {
  267. case 1:
  268. $this->computeByDispense($num, $order);
  269. break;
  270. case 2:
  271. $this->order_no = $order['order_no'];
  272. $agent = CloudStockAgent::findOne(['user_id' => $order['user_id'], 'status' => StatusEnum::ENABLED]);
  273. $cloud_stock_goods = CloudStockGoods::findOne(['goods_id' => $order['goods_id'], 'status' => StatusEnum::ENABLED]);
  274. if (empty($cloud_stock_goods)) {
  275. throw new \Exception('商品不存在');
  276. }
  277. $agent_price_list = json_decode($cloud_stock_goods['agent_price'], true);
  278. $agent_price_list = array_column($agent_price_list, null, 'level');
  279. $parent_agent_price = 0;
  280. $child_agent_price = 0;
  281. if (isset($agent_price_list[$parent_agent['level']])) {
  282. $parent_agent_price = $agent_price_list[$parent_agent['level']]['price'];
  283. }
  284. if (isset($agent_price_list[$agent['level']])) {
  285. $child_agent_price = $agent_price_list[$agent['level']]['price'];
  286. }
  287. $money = ($child_agent_price - $parent_agent_price) * $order['num'];
  288. if ($money <= 0) {
  289. return true;
  290. }
  291. $this->computeByBuyOrFill($type, $num, $money, $order, $parent_agent);
  292. break;
  293. case 3:
  294. $cloud_stock_goods = CloudStockGoods::findOne(['goods_id' => $order['goods_id'], 'status' => StatusEnum::ENABLED]);
  295. if (empty($cloud_stock_goods)) {
  296. throw new \Exception('商品不存在');
  297. }
  298. $agent_price_list = json_decode($cloud_stock_goods['agent_price'], true);
  299. $agent_price_list = array_column($agent_price_list, null, 'level');
  300. $parent_agent_price = 0;
  301. if (isset($agent_price_list[$parent_agent['level']])) {
  302. $parent_agent_price = $agent_price_list[$parent_agent['level']]['price'];
  303. }
  304. $money = ($order['after_distribution_price'] / $order['num'] - $parent_agent_price) * $order['num'];
  305. if ($money <= 0) {
  306. return true;
  307. }
  308. $main_order = Order::findOne(['id' => $order['order_id']]);
  309. $this->order_no = $main_order['order_no'];
  310. $this->computeByBuyOrFill($type, $num, $money, $order, $parent_agent);
  311. break;
  312. }
  313. }
  314. /**
  315. * 配货
  316. * @param $num
  317. * @param $order
  318. * @return void
  319. */
  320. public function computeByDispense($num, $order)
  321. {
  322. $goods_id = $order['goods_id'];
  323. $order_no = $order['order_no'];
  324. $user_id = $order['user_id'];//配货者
  325. $child_id = $order['child_id'];//配货下级
  326. $cloud_stock_goods = CloudStockGoods::findOne(['goods_id' => $goods_id, 'status' => StatusEnum::ENABLED]);
  327. if (empty($cloud_stock_goods)) {
  328. throw new \Exception('商品不存在');
  329. }
  330. $agent_price_list = json_decode($cloud_stock_goods['agent_price'], true);
  331. $agent_price_list = array_column($agent_price_list, null, 'level');
  332. $parent_id_arr = UserPartitionRelationship::getParentIdArr($child_id);
  333. $parent_id_arr = array_reverse($parent_id_arr);
  334. $cloud_stock_agent = CloudStockAgent::getOne([
  335. ['user_id' => $child_id],
  336. ['mall_id' => $this->mall_id],
  337. ['status' => StatusEnum::ENABLED],
  338. ]);
  339. $parent_agent_list = CloudStockAgent::lists([
  340. 'where' => [
  341. ['user_id' => $parent_id_arr],
  342. ['mall_id' => $this->mall_id],
  343. ['status' => StatusEnum::ENABLED],
  344. ],
  345. 'index' => 'user_id'
  346. ], false);
  347. foreach ($parent_id_arr as $uid) {
  348. if ($uid == $user_id) {
  349. break;
  350. }
  351. $agent = $parent_agent_list[$uid];
  352. if (empty($agent)) {
  353. continue;
  354. }
  355. if ($agent['level'] <= $cloud_stock_agent['level']) {
  356. continue;
  357. }
  358. $cloud_stock_agent = $agent;
  359. if ($agent['user_id'] == $user_id) {
  360. break;
  361. }
  362. $money = 0;
  363. if (isset($agent_price_list[$agent['level']])) {
  364. $money = ($order['child_price'] - $agent_price_list[$agent['level']]['price']) * $order['num'];
  365. }
  366. $add = [
  367. 'mall_id' => $this->mall_id,
  368. 'user_id' => $agent['user_id'],
  369. 'order_no' => $order_no,
  370. 'num' => $num,
  371. 'money' => $money,
  372. 'goods_id' => $goods_id,
  373. 'child_id' => $child_id,
  374. 'type' => 1,
  375. ];
  376. CloudStockLossAmount::add($add);
  377. $agent->loss_amount += $money;
  378. $agent->save();
  379. }
  380. }
  381. /**
  382. * 会员买货/补货
  383. * @param $type
  384. * @param $num
  385. * @param $money
  386. * @param $order
  387. * @param $parent_agent
  388. * @return void
  389. */
  390. public function computeByBuyOrFill($type, $num, $money, $order, $parent_agent)
  391. {
  392. $child_id = $order['user_id'];
  393. $add = [
  394. 'mall_id' => $this->mall_id,
  395. 'user_id' => $parent_agent['user_id'],
  396. 'order_no' => $this->order_no,
  397. 'num' => $num,
  398. 'money' => $money,
  399. 'goods_id' => $order['goods_id'],
  400. 'child_id' => $child_id,
  401. 'type' => $type,
  402. ];
  403. CloudStockLossAmount::add($add);
  404. $parent_agent->loss_amount += $money;
  405. $parent_agent->save();
  406. }
  407. }