UserBillRepository.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\common\repositories\user;
  12. use app\common\dao\BaseDao;
  13. use app\common\dao\user\UserBillDao;
  14. use app\common\repositories\BaseRepository;
  15. use app\common\repositories\store\product\ProductRepository;
  16. use crmeb\jobs\SendSmsJob;
  17. use think\facade\Queue;
  18. use think\Model;
  19. /**
  20. * Class UserBillRepository
  21. * @package app\common\repositories\user
  22. * @author xaboy
  23. * @day 2020-05-07
  24. * @mixin UserBillDao
  25. */
  26. class UserBillRepository extends BaseRepository
  27. {
  28. /**
  29. * @var UserBillDao
  30. */
  31. protected $dao;
  32. const TYPE_INFO = [
  33. 'brokerage' => [
  34. 'brokerage/now_money' => '奖金转入余额',
  35. 'brokerage/extract_award' => '分红积分兑换',
  36. 'brokerage/order_one' => '获得一级推广佣金',
  37. 'brokerage/order_two' => '获得二级推广佣金',
  38. 'brokerage/refund_one' => '退还一级佣金',
  39. 'brokerage/refund_two' => '退还二级佣金',
  40. 'brokerage/extract' => '奖金提现',
  41. 'brokerage/extract_return' => '提现退回',
  42. 'brokerage/order_up' => '订单分红利润',
  43. 'brokerage/spread_oil_brokerage' =>'团队节能油订单分红积分',
  44. 'brokerage/spread_pack_brokerage' =>'团队礼包订单分红积分',
  45. 'brokerage/ordinary_brokerage' =>'下单普通商品获得分红积分',
  46. 'brokerage/order_brokerage' =>'下单普通商品获得奖金',
  47. ],
  48. 'integral' => [
  49. 'integral/cancel' => '退回积分',
  50. 'integral/deduction' => '购买商品',
  51. 'integral/points' => '兑换商品',
  52. 'integral/lock' => '下单赠送积分',
  53. 'integral/refund' => '订单退款',
  54. 'integral/refund_lock' => '扣除赠送积分',
  55. 'integral/sign_integral' => '签到赠送积分',
  56. 'integral/spread' => '邀请好友',
  57. 'integral/sys_dec' => '系统减少积分',
  58. 'integral/sys_inc' => '系统增加积分',
  59. 'integral/timeout' => '积分过期',
  60. ],
  61. 'mer_integral' => [
  62. 'mer_integral/deduction' => '积分抵扣',
  63. 'mer_integral/refund' => '订单退款',
  64. ],
  65. 'now_money' => [
  66. //'now_money/extract' => '佣金提现余额',
  67. 'now_money/brokerage' => '奖金转入余额',
  68. 'now_money/pay_product' => '购买商品',
  69. 'now_money/presell' => '支付预售尾款',
  70. 'now_money/recharge' => '余额充值',
  71. 'now_money/sys_dec_money' => '系统减少余额',
  72. 'now_money/sys_inc_money' => '系统增加余额',
  73. 'svip_pay/svip_pay' => '付费会员支付'
  74. ],
  75. 'mer_margin' => [
  76. 'mer_margin/local_margin' => '线下缴纳保证金',
  77. 'mer_margin/pay_margin' => '线上缴纳保证金',
  78. ],
  79. 'mer_lock_money' => [
  80. 'mer_lock_money/order' => '商户佣金冻结',
  81. ],
  82. 'sys_members' => [
  83. 'sys_members/member_upgrade' => '会员升级',
  84. 'sys_members/platform_clearing' => '平台清除',
  85. 'sys_members/member_pay_num' => '下单获得成长值',
  86. 'sys_members/member_sign_num' => '签到获得成长值',
  87. 'sys_members/member_reply_num' => '评价获得成长值',
  88. 'sys_members/member_share_num' => '邀请获得成长值',
  89. 'sys_members/member_community_num' => '社区种草内容获得成长值',
  90. ],
  91. 'award_range' =>[
  92. 'award/energy_saving_oil' =>'下单节能油获得分红额度',
  93. 'award/award_gift_pack' =>'下单礼包获得分红额度',
  94. 'award/ordinary' =>'下单普通商品获得分红额度',
  95. // 'award/unclaimed_range' =>'下单商品获得冻结份额',
  96. 'award/thaw_range' =>'团队下单解冻份额',
  97. 'award/thaw_brokerage' =>'解冻冻结奖金',
  98. 'award_range/sys_dec_range' => '系统减少分红额度',
  99. 'award_range/sys_inc_range' => '系统增加分红额度',
  100. ],
  101. 'award_integral' =>[
  102. 'award/spread_oil_integral' =>'团队节能油订单分红积分',
  103. 'award/spread_pack_integral' =>'团队礼包订单分红积分',
  104. 'award/ordinary_integral' =>'下单普通商品获得分红积分',
  105. 'award/oil_integral' =>'节能油分红积分',
  106. 'award/pack_integral' =>'礼包分红积分',
  107. 'award/confirm_oil_integral' =>'核销节能油获得积分',
  108. 'award/freeze_integral' =>'解冻奖金为分红积分',
  109. 'award_integral/sys_dec_integral' => '系统减少分红积分',
  110. 'award_integral/sys_inc_integral' => '系统增加分红积分',
  111. ],
  112. 'award_unclaimed_range' =>[
  113. 'award/unclaimed_range' =>'下单商品获得冻结份额',
  114. 'award/thaw_range' =>'团队下单解冻份额',
  115. 'award/thaw_brokerage' =>'解冻冻结奖金',
  116. ],
  117. 'freeze_brokerage' =>[
  118. 'freeze_brokerage/freeze_brokerage' =>'冻结奖金',
  119. 'freeze_brokerage/thaw_brokerage' =>'解冻冻结奖金',
  120. ],
  121. ];
  122. const CATEGORY_SVIP_PAY = 'svip_pay';
  123. const CATEGORY_NOW_MONEY = 'now_money';
  124. const CATEGORY_INTEGRAL = 'integral';
  125. const CATEGORY_BROKERAGE= 'brokerage';
  126. const CATEGORY_MER_MARGIN = 'mer_margin';
  127. const CATEGORY_MER_INTEGRAL = 'mer_integral';
  128. const CATEGORY_MER_LOCK_MONEY = 'mer_lock_money';
  129. const CATEGORY_SYS_MEMBERS = 'sys_members';
  130. const CATEGORY_AWARD_RANGE = 'award_range';
  131. const CATEGORY_AWARD_INTEGRAL = 'award_integral';
  132. // 需要去重复的类型
  133. const TO_REPEAT_TYPE = ['member_community_num'];
  134. /**
  135. * UserBillRepository constructor.
  136. * @param UserBillDao $dao
  137. */
  138. public function __construct(UserBillDao $dao)
  139. {
  140. $this->dao = $dao;
  141. }
  142. /**
  143. * 获取用户列表数据
  144. *
  145. * 根据给定的条件查询用户相关列表信息,包括账单ID、项目名称、数量、余额、标记、创建时间和状态等。
  146. * 支持分页查询,便于前端进行数据展示。
  147. *
  148. * @param array $where 查询条件,用于定制查询的详细要求。
  149. * @param int $uid 用户ID,用于限定查询的用户范围。
  150. * @param int $page 当前页码,用于分页查询。
  151. * @param int $limit 每页数据条数,用于控制分页查询的数据量。
  152. * @return array 返回包含总数和列表数据的数组,方便前端进行分页展示。
  153. */
  154. public function userList($where, $uid, $page, $limit)
  155. {
  156. // 将用户ID添加到查询条件中
  157. $where['uid'] = $uid;
  158. // 构建查询语句,根据创建时间降序排列
  159. $query = $this->dao->search($where)->order('create_time DESC');
  160. // 计算满足条件的数据总数
  161. $count = $query->count();
  162. // 确定查询的字段,进行分页查询,并获取满足条件的数据列表
  163. $list = $query->setOption('field', [])->field('bill_id,pm,title,number,balance,mark,create_time,status')->page($page, $limit)->select();
  164. $income = $query->where('pm',1)->sum('number');
  165. $expend = $query->where('pm',0)->sum('number');
  166. // 返回数据总数和数据列表,供前端使用
  167. return compact('count', 'list','income','expend');
  168. }
  169. /**
  170. * 根据条件获取每个月的数据统计
  171. *
  172. * 本函数用于根据传入的条件数组,查询数据库中每个月的统计数据。
  173. * 包括每个月的标题、文章数量、评论数量等,并按照时间降序排列。
  174. *
  175. * @param array $where 查询条件数组
  176. * @return array 返回每个月的统计数据,包括月份和该月的具体数据列表
  177. */
  178. public function month(array $where)
  179. {
  180. // 根据条件查询数据库,统计每个月的记录,并按照时间降序排列
  181. $group = $this->dao->search($where)
  182. ->field('FROM_UNIXTIME(unix_timestamp(create_time),"%Y-%m") as time')
  183. ->order('time DESC')
  184. ->group('time')
  185. ->select();
  186. $ret = [];
  187. // 遍历每个月的统计数据,细化每个月的数据列表
  188. foreach ($group as $k => $item){
  189. // 设置每个月的名称
  190. $ret[$k]['month'] = $item['time'];
  191. // 查询该月的具体数据,包括标题、评论数、文章数和创建时间
  192. $query = $this->dao->getSearch($where)
  193. ->field('title,pm,number,create_time')
  194. ->whereMonth('create_time', $item['time']);
  195. // 获取该月的具体数据列表,并按照创建时间降序排列
  196. $ret[$k]['list'] = $query->order('create_time DESC')->select();
  197. }
  198. // 返回每个月的统计数据
  199. return $ret;
  200. }
  201. /**
  202. * 获取列表数据
  203. * 通过此方法可以从数据库中检索满足特定条件的列表数据。它支持分页查询,以优化性能和用户体验。
  204. *
  205. * @param string|array $where 查询条件,可以是字符串或数组,用于指定检索数据的具体条件。
  206. * @param int $page 当前页码,用于分页查询,指定要返回的数据页。
  207. * @param int $limit 每页数据的数量,用于分页查询,指定每页返回的数据条数。
  208. * @return array 返回包含两个元素的数组,'count'表示满足条件的数据总数量,'list'表示当前页的数据列表。
  209. */
  210. public function getList($where, $page, $limit)
  211. {
  212. // 根据提供的条件进行查询,并按创建时间降序和账单ID降序排序
  213. $query = $this->dao->searchJoin($where)->order('a.create_time DESC,bill_id DESC');
  214. // 计算满足条件的数据总数量
  215. $count = $query->count();
  216. // 获取当前页的数据列表
  217. $list = $query->page($page, $limit)->select();
  218. // 返回包含数据总数量和当前页数据列表的数组
  219. return compact('count', 'list');
  220. }
  221. /**
  222. * 获取列表数据
  223. *
  224. * 本函数用于根据条件查询数据库中的数据列表,并进行分页处理。
  225. * 返回包含数据总数和当前分页数据的数组。
  226. *
  227. * @param string $where 查询条件,用于构造SQL语句中的WHERE子句。
  228. * @param int $page 当前页码,用于确定要返回的分页数据。
  229. * @param int $limit 每页显示的数据条数,用于确定分页数据的数量。
  230. * @return array 返回一个包含 'count' 和 'list' 两个元素的数组,'count' 表示数据总数,'list' 表示当前分页的数据列表。
  231. */
  232. public function getLst($where, $page, $limit)
  233. {
  234. // 根据$where条件搜索数据,并按创建时间降序排序
  235. $query = $this->dao->search($where)->order('create_time DESC');
  236. // 计算满足条件的数据总数
  237. $count = $query->count();
  238. // 获取当前页码的分页数据列表
  239. $list = $query->page($page, $limit)->select();
  240. // 将数据总数和当前分页数据列表打包成数组返回
  241. return compact('count', 'list');
  242. }
  243. /**
  244. * 创建账单
  245. *
  246. * 该方法用于生成用户的消费或收入账单。根据传入的参数,账单信息将被记录到数据库中。
  247. * 如果账单类别为'now_money'(即涉及现金变动),则会将发送短信通知的任务推入队列。
  248. *
  249. * @param int $uid 用户ID,用于标识账单所属的用户。
  250. * @param string $category 账单类别,用于分类账单类型。
  251. * @param string $type 账单的具体类型,进一步细化账单类别。
  252. * @param int $pm 账单的正负标识,用于区分收入还是支出。
  253. * @param array $data 其他账单相关数据,以键值对形式传入,将被合并到账单数据中。
  254. * @return object 返回创建的账单对象。
  255. */
  256. public function bill(int $uid, string $category, string $type, int $pm, array $data)
  257. {
  258. // 合并传入的数据与账单基础信息,准备创建账单
  259. $data['category'] = $category;
  260. $data['type'] = $type;
  261. $data['uid'] = $uid;
  262. $data['pm'] = $pm;
  263. // 通过数据访问对象创建账单记录
  264. $bill = $this->dao->create($data);
  265. // 如果账单类别是'now_money',则推送发送短信通知的任务到队列
  266. if($category == 'now_money'){
  267. Queue::push(SendSmsJob::class,['tempId' => 'USER_BALANCE_CHANGE','id' => $bill->bill_id]);
  268. }
  269. // 返回创建的账单对象
  270. return $bill;
  271. }
  272. /**
  273. * 递增计费项
  274. * 此函数用于增加指定用户的计费项。它是一个简化的版本,通过调用另一个函数来实现。
  275. *
  276. * @param int $uid 用户ID,用于标识哪个用户产生了计费项。
  277. * @param string $category 计费项分类,用于细化计费项的类型。
  278. * @param string $type 计费项的类型,进一步定义计费的具体内容。
  279. * @param array $data 关于计费项的额外数据,以键值对形式提供。
  280. *
  281. * @return mixed 返回调用bill函数的结果,具体类型取决于bill函数的实现。
  282. */
  283. public function incBill(int $uid, string $category, string $type, array $data)
  284. {
  285. // 调用bill函数来处理计费项的增加,其中最后一个参数1表示增加操作。
  286. return $this->bill($uid, $category, $type, 1, $data);
  287. }
  288. /**
  289. * 减少用户账单
  290. * 此函数用于减少用户的某种账单。它是一个辅助函数,通过调用另一个函数来实现具体的账单减少操作。
  291. *
  292. * @param int $uid 用户ID。标识进行账单操作的用户。
  293. * @param string $category 账单分类。指示账单的具体类型。
  294. * @param string $type 账单操作类型。指示对账单进行的操作,例如增加、减少等。
  295. * @param array $data 额外数据。包含与账单操作相关的其他信息。
  296. *
  297. * @return mixed 返回账单操作的结果。具体类型取决于底层实现。
  298. */
  299. public function decBill(int $uid, string $category, string $type, array $data)
  300. {
  301. // 调用bill方法减少用户的账单,其中第五个参数设置为0,表示减少操作。
  302. return $this->bill($uid, $category, $type, 0, $data);
  303. }
  304. /**
  305. * 根据分类获取类型信息
  306. *
  307. * 本函数旨在根据给定的分类,从预定义的TYPE_INFO数组中提取并返回相关类型的详细信息。
  308. * 它通过遍历TYPE_INFO数组中指定分类下的所有类型,将类型和对应的标题打包成数组返回。
  309. * 这样做的目的是为了方便调用者一次性获取到特定分类下的所有类型及其标题,便于进一步处理或展示。
  310. *
  311. * @param string $category 分类名称,用于从TYPE_INFO数组中筛选类型信息
  312. * @return array 包含类型和标题的二维数组,每个元素是一个包含'type'和'title'两个键的关联数组
  313. */
  314. public function type($category)
  315. {
  316. // 初始化用于存储结果的数组
  317. $data = [];
  318. // 遍历TYPE_INFO数组中指定分类下的所有类型和标题
  319. foreach (self::TYPE_INFO[$category] as $type => $title) {
  320. // 将类型和标题打包成关联数组,并添加到结果数组中
  321. $data[] = compact('type', 'title');
  322. }
  323. // 返回包含所有类型信息的数组
  324. return $data;
  325. }
  326. /**
  327. * 积分日志头部统计
  328. * @return array
  329. * @author Qinii
  330. * @day 6/9/21
  331. */
  332. public function getStat($merId = 0)
  333. {
  334. if($merId){
  335. $isusd = app()->make(ProductRepository::class)->getSearch(['mer_id' => $merId])->sum('integral_total');
  336. $refund = $this->dao->search(['category' => 'mer_integral','type' => 'refund','mer_id' => $merId])->sum('number');
  337. $numb = app()->make(ProductRepository::class)->getSearch(['mer_id' => $merId])->sum('integral_price_total');
  338. return [
  339. [
  340. 'className' => 'el-icon-s-cooperation',
  341. 'count' => $isusd,
  342. 'field' => '个',
  343. 'name' => '已使用积分(分)'
  344. ],
  345. [
  346. 'className' => 'el-icon-edit',
  347. 'count' => $refund,
  348. 'field' => '次',
  349. 'name' => '退款订单返回积分(分)'
  350. ],
  351. [
  352. 'className' => 'el-icon-edit',
  353. 'count' => $numb,
  354. 'field' => '次',
  355. 'name' => '积分抵扣金额(元)'
  356. ],
  357. ];
  358. }
  359. // 总积分
  360. $integral = app()->make(UserRepository::class)->search(['status' => 1])->sum('integral');
  361. // 客户签到次数
  362. $sign = app()->make(UserSignRepository::class)->getSearch([])->count('*');
  363. // 签到送出积分
  364. $sign_integral = $this->dao->search(['type' => 'sign_integral'])->sum('number');
  365. // 使用积分
  366. $isusd = $this->dao->search(['category' => 'integral','type' => 'deduction'])->sum('number');
  367. $refund = $this->dao->search(['category' => 'mer_integral','type' => 'refund'])->sum('number');
  368. $order = $isusd - $refund;
  369. // 下单赠送积分
  370. $order_integral1 = $this->dao->search(['category' => 'integral','type' => 'lock'])->sum('number');
  371. $order_integral2 = $this->dao->search(['category' => 'integral','type' => 'refund_lock'])->sum('number');
  372. $order_integral = $order_integral1 - $order_integral2;
  373. $order_integral = $order_integral < 0 ? 0 : $order_integral;
  374. // 冻结积分
  375. $freeze_integral = $this->dao->lockIntegral();
  376. return [
  377. [
  378. 'className' => 'el-icon-s-cooperation',
  379. 'count' => $integral,
  380. 'field' => '个',
  381. 'name' => '总积分'
  382. ],
  383. [
  384. 'className' => 'el-icon-edit',
  385. 'count' => $sign,
  386. 'field' => '次',
  387. 'name' => '客户签到次数'
  388. ],
  389. [
  390. 'className' => 'el-icon-s-goods',
  391. 'count' => $sign_integral ,
  392. 'field' => '个',
  393. 'name' => '签到送出积分'
  394. ],
  395. [
  396. 'className' => 'el-icon-s-order',
  397. 'count' => $order,
  398. 'field' => '个',
  399. 'name' => '使用积分'
  400. ],
  401. [
  402. 'className' => 'el-icon-present',
  403. 'count' => $order_integral,
  404. 'field' => '个',
  405. 'name' => '下单赠送积分'
  406. ],
  407. [
  408. 'className' => 'el-icon-warning',
  409. 'count' => $freeze_integral,
  410. 'field' => '',
  411. 'name' => '冻结积分'
  412. ],
  413. ];
  414. }
  415. /**
  416. * 判断是否需要去除重复添加
  417. * @param int $uid
  418. * @param string $type
  419. * @param int $link_id
  420. * @return bool
  421. */
  422. public function ToRepeat(int $uid, string $type, int $link_id)
  423. {
  424. if (in_array($type, self::TO_REPEAT_TYPE)) {
  425. //判断是否重复
  426. $make = app()->make(UserBillRepository::class);
  427. $count = $make->getWhereCount(['uid' => $uid, 'type' => $type, 'link_id' => $link_id]);
  428. if ($count) {
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. }