DistributionLogic.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeshop100%开源免费商用商城系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | gitee下载:https://gitee.com/likeshop_gitee
  10. // | github下载:https://github.com/likeshop-github
  11. // | 访问官网:https://www.likeshop.cn
  12. // | 访问社区:https://home.likeshop.cn
  13. // | 访问手册:http://doc.likeshop.cn
  14. // | 微信公众号:likeshop技术社区
  15. // | likeshop团队 版权所有 拥有最终解释权
  16. // +----------------------------------------------------------------------
  17. // | author: likeshopTeam
  18. // +----------------------------------------------------------------------
  19. namespace app\shopapi\logic;
  20. use app\common\enum\DistributionConfigEnum;
  21. use app\common\enum\DistributionEnum;
  22. use app\common\enum\DistributionOrderGoodsEnum;
  23. use app\common\enum\TerminalEnum;
  24. use app\common\enum\UserTerminalEnum;
  25. use app\common\enum\YesNoEnum;
  26. use app\common\logic\BaseLogic;
  27. use app\common\logic\PosterLogic;
  28. use app\common\model\DistributionConfig;
  29. use app\common\model\DistributionLevel;
  30. use app\common\model\DistributionOrderGoods;
  31. use app\common\model\User;
  32. use app\common\model\Distribution;
  33. use app\common\model\DistributionApply;
  34. use app\common\service\FileService;
  35. use app\common\service\RegionService;
  36. use think\facade\Db;
  37. /**
  38. * 分销逻辑层
  39. * Class DistributionLogic
  40. * @package app\shopapi\logic
  41. */
  42. class DistributionLogic extends BaseLogic
  43. {
  44. /**
  45. * @notes 判断用户是否为分销会员
  46. * @param $userId
  47. * @return array
  48. * @author Tab
  49. * @date 2021/8/4 11:15
  50. */
  51. public static function isDistributionMember($userId)
  52. {
  53. // 是否为分销会员
  54. $isDistribution = Distribution::where('user_id', $userId)->value('is_distribution');
  55. // 分销会员开通方式
  56. $open = DistributionConfig::where('key', 'open')->value('value');
  57. $open = $open ?: DistributionConfigEnum::DEFAULT_OPEN;
  58. $show_apply_page = $open == DistributionConfigEnum::OPEN_APPLY ? YesNoEnum::YES : YesNoEnum::NO;
  59. return [
  60. 'is_distributon' => $isDistribution,
  61. 'show_apply_page' => $show_apply_page
  62. ];
  63. }
  64. /**
  65. * @notes 填写邀请码
  66. * @param $params
  67. * @return array
  68. * @author Tab
  69. * @date 2021/7/16 18:44
  70. */
  71. public static function code($params)
  72. {
  73. Db::startTrans();
  74. try{
  75. $firstLeader = User::field(['id', 'first_leader', 'second_leader', 'third_leader', 'ancestor_relation'])
  76. ->where(['code' => $params['code']])
  77. ->findOrEmpty()->toArray();
  78. // 上级
  79. $firstLeaderId = $firstLeader['id'];
  80. // 上上级
  81. $secondLeaderId = $firstLeader['first_leader'];
  82. // 上上上级
  83. $thirdLeaderId = $firstLeader['second_leader'];
  84. // 拼接关系链
  85. $firstLeader['ancestor_relation'] = boolval($firstLeader['ancestor_relation']) ? $firstLeader['ancestor_relation'] : ''; // 清空null值及0
  86. $myAncestorRelation = $firstLeaderId. ',' . $firstLeader['ancestor_relation'];
  87. // 去除两端逗号
  88. $myAncestorRelation = trim($myAncestorRelation, ',');
  89. $user = User::findOrEmpty($params['user_id']);
  90. // 旧关系链
  91. if (!empty($user->ancestor_relation)) {
  92. $old_ancestor_relation = $user->id . ',' .$user->ancestor_relation;
  93. } else {
  94. $old_ancestor_relation = $user->id;
  95. }
  96. $data = [
  97. 'id' => $params['user_id'],
  98. 'first_leader' => $firstLeaderId,
  99. 'second_leader' => $secondLeaderId,
  100. 'third_leader' => $thirdLeaderId,
  101. 'ancestor_relation' => $myAncestorRelation,
  102. ];
  103. // 更新当前用户的分销关系
  104. User::update($data);
  105. //更新当前用户下级的分销关系
  106. $data = [
  107. 'second_leader' => $firstLeaderId,
  108. 'third_leader' => $secondLeaderId,
  109. ];
  110. User::where('first_leader', $params['user_id'])->update($data);
  111. //更新当前用户下下级的分销关系
  112. $data = [
  113. 'third_leader' => $firstLeaderId,
  114. ];
  115. User::where('second_leader', $params['user_id'])->update($data);
  116. //更新当前用户所有后代的关系链
  117. $posterityArr = User::field('id,ancestor_relation')
  118. ->whereFindInSet('ancestor_relation', $params['user_id'])
  119. ->select()
  120. ->toArray();
  121. $updateData = [];
  122. $replaceAncestorRelation = $params['user_id'] . ','. $myAncestorRelation;
  123. foreach($posterityArr as $item) {
  124. $updateData[] = [
  125. 'id' => $item['id'], // 主键
  126. 'ancestor_relation' => trim(str_replace($old_ancestor_relation, $replaceAncestorRelation, $item['ancestor_relation']), ',')
  127. ];
  128. }
  129. // 批量更新
  130. (new User())->saveAll($updateData);
  131. Db::commit();
  132. return true;
  133. }catch(\Exception $e) {
  134. Db::rollback();
  135. self::setError($e->getMessage());
  136. return false;
  137. }
  138. }
  139. /**
  140. * @notes 申请分销
  141. * @param $params
  142. * @author Tab
  143. * @date 2021/7/17 10:37
  144. */
  145. public static function apply($params)
  146. {
  147. DistributionApply::create($params);
  148. }
  149. /**
  150. * @notes 查看申请详情
  151. * @param $userId
  152. * @return array
  153. * @author Tab
  154. * @date 2021/7/17 11:38
  155. */
  156. public static function applyDetail($userId)
  157. {
  158. $applyDetail = DistributionApply::where('user_id', $userId)->order('id', 'desc')->findOrEmpty();
  159. if($applyDetail->isEmpty()) {
  160. return [];
  161. }
  162. $applyDetail = $applyDetail->toArray();
  163. $applyDetail['province'] = RegionService::getAddress($applyDetail['province']);
  164. $applyDetail['city'] = RegionService::getAddress($applyDetail['city']);
  165. $applyDetail['district'] = RegionService::getAddress($applyDetail['district']);
  166. $applyDetail['status_tips'] = DistributionApply::getStatusTips($applyDetail['status']);
  167. return $applyDetail;
  168. }
  169. /**
  170. * @notes 查看当前用户及上级信息
  171. * @param $userId
  172. * @return array
  173. * @author Tab
  174. * @date 2021/7/17 14:57
  175. */
  176. public static function myLeader($userId)
  177. {
  178. $my = User::field('id,nickname,avatar,first_leader,user_earnings,code,admin_update_leader')->findOrEmpty($userId)->toArray();
  179. return [
  180. 'user' => $my,
  181. 'leader' => User::getFirstLeader($my),
  182. ];
  183. }
  184. /**
  185. * @notes 查看分销推广主页
  186. * @param $userId
  187. * @return array
  188. * @author Tab
  189. * @date 2021/7/17 16:27
  190. */
  191. public static function index($userId)
  192. {
  193. // 是否为分销会员
  194. $distribution = Distribution::where('user_id', $userId)->field('is_distribution,level_id')->find();
  195. if (empty($distribution['is_distribution'])) {
  196. $distribution['is_distribution'] = 0;
  197. }
  198. // 分销会员开通方式
  199. $open = DistributionConfig::where('key', 'open')->value('value');
  200. $open = $open ?: DistributionConfigEnum::DEFAULT_OPEN;
  201. $show_apply_page = $open == DistributionConfigEnum::OPEN_APPLY ? YesNoEnum::YES : YesNoEnum::NO;
  202. // 当前用户及上级信息
  203. $myLeader = self::myLeader($userId);
  204. // 粉丝数(一级/二级)
  205. $fans = User::getFans($userId);
  206. // 今天预估收益(未返佣金)
  207. $todayEarnings = DistributionOrderGoods::whereDay('create_time')
  208. ->where(['user_id' => $userId, 'status' => DistributionOrderGoodsEnum::UN_RETURNED])
  209. ->sum('earnings');
  210. // 本月预估收益(未返佣金)
  211. $monthEarnings = DistributionOrderGoods::whereMonth('create_time')
  212. ->where(['user_id' => $userId, 'status' => DistributionOrderGoodsEnum::UN_RETURNED])
  213. ->sum('earnings');
  214. // 累计收益(已返佣金)
  215. $historyEarnings = DistributionOrderGoods::where(['user_id' => $userId, 'status' => DistributionOrderGoodsEnum::RETURNED])
  216. ->sum('earnings');
  217. //申请页顶部宣传图
  218. $dbConfig = DistributionConfig::column('value', 'key');
  219. $top_apply_image = $dbConfig['apply_image'] ?? DistributionConfigEnum::DEFAULT_APPLY_IMAGE;
  220. $protocol_show = $dbConfig['protocol_show'] ?? DistributionConfigEnum::DEFAULT_PROTOCOL_SHOW;
  221. $data = [
  222. 'is_distributon' => $distribution['is_distribution'],
  223. 'show_apply_page' => $show_apply_page,
  224. 'user' => $myLeader['user'],
  225. 'leader' => $myLeader['leader'],
  226. 'level_name' => DistributionLevel::where(['id'=>$distribution['level_id']])->value('name') ?? '',
  227. 'fans' => $fans,
  228. 'able_withdrawal' => $myLeader['user']['user_earnings'],
  229. 'code' => $myLeader['user']['code'],
  230. 'today_earnings' => round($todayEarnings, 2),
  231. 'month_earnings' => round($monthEarnings, 2),
  232. 'history_earnings' => round($historyEarnings, 2),
  233. 'top_apply_image' => $top_apply_image ? FileService::getFileUrl($top_apply_image) : '',
  234. 'protocol_show' => $protocol_show,
  235. ];
  236. return $data;
  237. }
  238. /**
  239. * @notes 查看分销订单列表
  240. * @param $params
  241. * @return array
  242. * @author Tab
  243. * @date 2021/7/17 18:22
  244. */
  245. public static function order($params)
  246. {
  247. $where[] = ['dog.user_id', '=', $params['user_id']];
  248. // 佣金状态
  249. if(isset($params['status']) && $params['status']) {
  250. $where[] = ['dog.status', '=', $params['status']];
  251. }
  252. if(isset($params['start_time']) && !empty($params['start_time'])) {
  253. $where[] = ['dog.create_time', '>=', $params['start_time']];
  254. }
  255. if(isset($params['end_time']) && !empty($params['end_time'])) {
  256. $where[] = ['dog.create_time', '<=', $params['end_time']];
  257. }
  258. $field = 'o.sn,o.create_time';
  259. $field .= ',og.goods_num,og.total_pay_price';
  260. $field .= ',dog.status,dog.status as status_desc,dog.earnings,dog.id,dog.order_goods_id';
  261. $field .= ',g.image as goods_image,g.name as goods_name';
  262. $field .= ',gi.image as item_image,gi.spec_value_str';
  263. $lists = DistributionOrderGoods::field($field)
  264. ->alias('dog')
  265. ->leftJoin('order_goods og', 'dog.order_goods_id=og.id')
  266. ->leftJoin('order o', 'o.id=og.order_id')
  267. ->leftJoin('goods g', 'g.id=og.goods_id')
  268. ->leftJoin('goods_item gi', 'gi.id=og.item_id')
  269. ->where($where)
  270. ->order('o.id', 'desc')
  271. ->page($params['page_no'], $params['page_size'])
  272. ->select()
  273. ->toArray();
  274. foreach($lists as &$item) {
  275. $item['image'] = $item['item_image'] ? FileService::getFileUrl($item['item_image']) : FileService::getFileUrl($item['goods_image']);
  276. }
  277. $count = DistributionOrderGoods::alias('dog')
  278. ->leftJoin('order_goods og', 'dog.order_goods_id=og.id')
  279. ->leftJoin('order o', 'o.id=og.order_id')
  280. ->leftJoin('goods g', 'g.id=og.goods_id')
  281. ->leftJoin('goods_item gi', 'gi.id=og.item_id')
  282. ->where($where)
  283. ->count();
  284. $data = [
  285. 'lists' => $lists,
  286. 'count' => $count,
  287. 'page_no' => $params['page_no'],
  288. 'page_size' => $params['page_size'],
  289. 'more' => is_more($count, $params['page_no'], $params['page_size'])
  290. ];
  291. return $data;
  292. }
  293. /**
  294. * @notes 查看月度账单
  295. * @param $params
  296. * @return array
  297. * @author Tab
  298. * @date 2021/7/17 19:07
  299. */
  300. public static function monthBill($params)
  301. {
  302. $field = [
  303. "any_value(FROM_UNIXTIME(create_time,'%Y年%m月')) as date",
  304. "any_value(FROM_UNIXTIME(create_time,'%Y')) as year",
  305. "any_value(FROM_UNIXTIME(create_time,'%m')) as month",
  306. 'sum(earnings) as total_money',
  307. 'count(order_goods_id) as order_goods_num'
  308. ];
  309. $lists = DistributionOrderGoods::field($field)
  310. ->where('user_id', $params['user_id'])
  311. ->where('status', 'in', [DistributionOrderGoodsEnum::UN_RETURNED, DistributionOrderGoodsEnum::RETURNED])
  312. ->page($params['page_no'], $params['page_size'])
  313. ->group('date')
  314. ->select()
  315. ->toArray();
  316. $count = DistributionOrderGoods::field($field)
  317. ->where('user_id', $params['user_id'])
  318. ->where('status', 'in', [DistributionOrderGoodsEnum::UN_RETURNED, DistributionOrderGoodsEnum::RETURNED])
  319. ->group('date')
  320. ->count();
  321. $data = [
  322. 'lists' => $lists,
  323. 'count' => $count,
  324. 'page_no' => $params['page_no'],
  325. 'page_size' => $params['page_size'],
  326. 'more' => is_more($count, $params['page_no'], $params['page_size'])
  327. ];
  328. return $data;
  329. }
  330. /**
  331. * @notes 查看月度账单明细
  332. * @param $params
  333. * @return array
  334. * @author Tab
  335. * @date 2021/7/19 10:26
  336. */
  337. public static function monthDetail($params)
  338. {
  339. // -t 获取指定月份的天数
  340. list($year, $month, $days) = explode('-', date("{$params['year']}-{$params['month']}-t"));
  341. // 月初时间
  342. $params['start_time'] = strtotime("{$year}-{$month}-01 00:00:00");
  343. // 月末时间
  344. $params['end_time'] = strtotime("{$year}-{$month}-{$days} 23:59:59");
  345. return self::order($params);
  346. }
  347. /**
  348. * @notes 分销海报
  349. * @param $params
  350. * @return array|false
  351. * @author Tab
  352. * @date 2021/8/6 15:07
  353. */
  354. public static function poster($params)
  355. {
  356. try {
  357. // 小程序url必填
  358. if($params['terminal'] == UserTerminalEnum::WECHAT_MMP && (!isset($params['url']) || empty($params['url']))) {
  359. throw new \think\Exception('请提供url');
  360. }
  361. // 公众号url必填
  362. if($params['terminal'] == UserTerminalEnum::WECHAT_OA && (!isset($params['url']) || empty($params['url']))) {
  363. throw new \think\Exception('请提供url');
  364. }
  365. // 用户信息
  366. $user = User::field('id,avatar,nickname,code')->findOrEmpty($params['user_id'])->toArray();
  367. switch($params['terminal']) {
  368. case UserTerminalEnum::WECHAT_MMP:
  369. $urlType = 'path';
  370. $qrCodeField = 'mnp_qr_code';
  371. $content = $params['url'];
  372. break;
  373. case UserTerminalEnum::WECHAT_OA:
  374. case UserTerminalEnum::H5:
  375. $urlType = 'url';
  376. $qrCodeField = 'h5_qr_code';
  377. $url = FileService::getFileUrl($params['url']);
  378. $content = $url . '?invite_code=' . $user['code'];
  379. break;
  380. case UserTerminalEnum::IOS:
  381. case UserTerminalEnum::ANDROID:
  382. $urlType = 'url';
  383. $qrCodeField = 'app_qr_code';
  384. $content = url('index/index/app', [], '', true);
  385. break;
  386. default:
  387. throw new \think\Exception('终端类型错误');
  388. }
  389. // 生成分销海报
  390. $poster = PosterLogic::generate($user, $content, $urlType, $params['terminal']);
  391. // 更新分销基础信息表
  392. Distribution::where('user_id', $params['user_id'])->update([
  393. $qrCodeField => $poster['url']
  394. ]);
  395. return ['url' => FileService::getFileUrl($poster['url'])];
  396. } catch (\Exception $e) {
  397. self::setError($e->getMessage());
  398. return false;
  399. }
  400. }
  401. /**
  402. * 修复旧的关系链
  403. * @return bool
  404. */
  405. public static function fixAncestorRelation()
  406. {
  407. Db::startTrans();
  408. try {
  409. $userList = User::select()->toArray();
  410. if (empty($userList)) {
  411. throw new \Exception('没有用户,无需修复');
  412. }
  413. $updateEmptyData = [];
  414. $updateData = [];
  415. foreach($userList as $user) {
  416. $my_ancestor_relation = self::myAncestorRelation($user);
  417. $updateEmptyData[] = ['id' => $user['id'], 'ancestor_relation' => ''];
  418. $updateData[] = ['id' => $user['id'], 'ancestor_relation' => $my_ancestor_relation];
  419. }
  420. // 先清除所有关系链
  421. (new User())->saveAll($updateEmptyData);
  422. // 重新设置关系链
  423. (new User())->saveAll($updateData);
  424. Db::commit();
  425. return true;
  426. } catch (\Exception $e) {
  427. Db::rollback();
  428. self::$error = $e->getMessage();
  429. return false;
  430. }
  431. }
  432. public static function myAncestorRelation($user)
  433. {
  434. if (empty($user['first_leader'])) {
  435. return '';
  436. }
  437. return trim(self::findAncestorRelation($user['first_leader']), ',');
  438. }
  439. public static function findAncestorRelation($id, $flag = true)
  440. {
  441. static $ancestor_relation = '';
  442. if ($flag) {
  443. $ancestor_relation = '';
  444. }
  445. $ancestor_relation .= $id . ',';
  446. $user = User::findOrEmpty($id);
  447. if (empty($user['first_leader'])) {
  448. return $ancestor_relation;
  449. }
  450. return self::findAncestorRelation($user['first_leader'], false);
  451. }
  452. /**
  453. * @notes 获取海报
  454. * @author cjhao
  455. * @date 2021/11/17 18:35
  456. */
  457. public static function getPoster(){
  458. $poster = DistributionConfig::where(['key'=>'poster'])->value('value');
  459. if(empty($poster)){
  460. $poster = DistributionConfigEnum::DEFAULT_POSTER;
  461. }
  462. return ['poster' => FileService::getFileUrl($poster)];
  463. }
  464. }