MemberService.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Laravel框架 [ Laravel ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 Laravel研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: wesmiler <12345678@qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services;
  12. use App\Models\CityModel;
  13. use App\Models\FansModel;
  14. use App\Models\MemberModel;
  15. use App\Models\SiyuanModel;
  16. use App\Models\TradeModel;
  17. use SimpleSoftwareIO\QrCode\Facades\QrCode;
  18. /**
  19. * 会员管理-服务类
  20. * @author wesmiler
  21. * @since 2020/11/11
  22. * Class MemberService
  23. * @package App\Services
  24. */
  25. class MemberService extends BaseService
  26. {
  27. protected static $instance = null;
  28. /**
  29. * 构造函数
  30. * @author wesmiler
  31. * @since 2020/11/11
  32. * MemberService constructor.
  33. */
  34. public function __construct()
  35. {
  36. $this->model = new MemberModel();
  37. $this->cityModel = new CityModel();
  38. }
  39. /**
  40. * 静态入口
  41. * @return MemberService|null
  42. */
  43. public static function make(){
  44. if(!self::$instance){
  45. self::$instance = new MemberService();
  46. }
  47. return self::$instance;
  48. }
  49. /**
  50. * 添加会编辑会员
  51. * @return array
  52. * @since 2020/11/11
  53. * @author wesmiler
  54. */
  55. public function edit()
  56. {
  57. // 请求参数
  58. $data = request()->all();
  59. // 头像处理
  60. $avatar = trim($data['avatar']);
  61. if (strpos($avatar, "temp")) {
  62. $data['avatar'] = save_image($avatar, 'member');
  63. } else {
  64. $data['avatar'] = str_replace(IMG_URL, "", $data['avatar']);
  65. }
  66. // 出生日期
  67. if ($data['birthday']) {
  68. $data['birthday'] = strtotime($data['birthday']);
  69. }
  70. // 城市处理
  71. $city = isset($data['city']) ? $data['city'] : [3];
  72. if (!empty($data['city'])) {
  73. // 省份
  74. $data['province_id'] = $city[0];
  75. // 城市
  76. $data['city_id'] = $city[1];
  77. // 县区
  78. $data['district_id'] = $city[2];
  79. }
  80. unset($data['city']);
  81. return parent::edit($data); // TODO: Change the autogenerated stub
  82. }
  83. /**
  84. * 获取用户信息
  85. * @param $openid OPENID
  86. * @param array $field m用户表
  87. * @return array
  88. */
  89. public function getUserInfo($where, $field = [], $type=0)
  90. {
  91. $field = $field ? $field : ['m.id', 'm.openid', 'm.invite_id','m.birthday','m.age','m.gender','m.intro','m.province_id','m.city_id','m.district_id', 'ms.type as mtype', 'yg.status as yigong_status','yg.on_siyuan as yg_on_siyuan','yg.siyuan_id','ms.siyuan','ms.on_siyuan as sr_on_siyuan', 'ms.master_type', 'ms.realname as master_name', 'ms.status as master_status', 'm.mobile', 'm.nickname', 'm.avatar', 'm.gender', 'm.member_level', 'm.is_vip','m.vip_expire', 'm.realname', 'm.balance', 'm.salary', 'm.merits_num', 'm.coupon', 'm.score', 'm.login_time','m.intro', 'm.status'];
  92. $info = $this->model::from('member as m')
  93. ->leftJoin('member_level as ml', 'ml.id', '=', 'm.member_level')
  94. ->leftJoin('master as ms', 'ms.user_id', '=', 'm.id')
  95. ->leftJoin('yigong as yg', 'yg.user_id', '=', 'm.id')
  96. ->select($field)
  97. ->where($where)
  98. ->where('m.status', '>', 0)
  99. ->first();
  100. $info = $info ? $info->toArray() : [];
  101. if (isset($info['avatar'])) {
  102. $info['avatar'] = $info['avatar'] ? get_image_url($info['avatar']) : '';
  103. }
  104. if($info){
  105. // 僧人参数
  106. if(array_key_exists('mtype', $info)){
  107. $info['mtype'] = $info['mtype']? intval($info['mtype']) : 0;
  108. }
  109. if(array_key_exists('master_type', $info)){
  110. $info['master_type'] = $info['master_type']? intval($info['master_type']) : 0;
  111. }
  112. if(array_key_exists('master_status', $info)){
  113. $info['master_status'] = $info['master_status']? intval($info['master_status']) : 0;
  114. }
  115. if(array_key_exists('master_name', $info)){
  116. $info['master_name'] = $info['master_name']? trim($info['master_name']) : '';
  117. }
  118. if(array_key_exists('yigong_siyuan', $info)){
  119. $info['yigong_siyuan'] = $info['yigong_siyuan']? trim($info['yigong_siyuan']) : '';
  120. }
  121. if(array_key_exists('yg_on_siyuan', $info)){
  122. $info['yg_on_siyuan'] = $info['yg_on_siyuan']? intval($info['yg_on_siyuan']) : 0;
  123. }
  124. if(array_key_exists('yigong_status', $info)){
  125. $info['yigong_status'] = $info['yigong_status']? intval($info['yigong_status']) : 0;
  126. }
  127. if(array_key_exists('siyuan', $info)){
  128. $info['siyuan'] = $info['siyuan']? trim($info['siyuan']) : '';
  129. }
  130. if(array_key_exists('sr_on_siyuan', $info)){
  131. $info['sr_on_siyuan'] = $info['sr_on_siyuan']? intval($info['sr_on_siyuan']) : 0;
  132. }
  133. $info['yg_siyuan'] = '';
  134. if(array_key_exists('siyuan_id', $info) && $info['siyuan_id']){
  135. $info['yg_siyuan'] = $info['siyuan_id']? SiyuanModel::where('id', $info['siyuan_id'])->value('title') : '';
  136. }
  137. if($type == 1) {
  138. // 城市
  139. $cityData = [0, 0, 0];
  140. $cityNames = [];
  141. if (isset($info['province_id'])) {
  142. $info['province_name'] = $info['province_id'] ? CityService::make()->getName($info['province_id']) : '';
  143. $cityData[0] = $info['province_id'] ? $info['province_id'] : 0;
  144. $cityNames[] = $info['province_name'] ? $info['province_name'] : '';
  145. }
  146. if (isset($info['city_id'])) {
  147. $info['city_name'] = $info['city_id'] ? CityService::make()->getName($info['city_id']) : '';
  148. $openid = isset($info['openid']) ? $info['openid'] : '';
  149. $cityData[1] = $info['city_id'] ? $info['city_id'] : 0;
  150. $cityNames[] = $info['city_name'] ? $info['city_name'] : '';
  151. if (empty($info['city_name']) && $openid) {
  152. $info['city_name'] = FansModel::where(['openid' => $openid])->value('city');
  153. }
  154. }
  155. if (isset($info['district_id'])) {
  156. $info['district_name'] = $info['district_id'] ? CityService::make()->getName($info['district_id']) : '';
  157. $cityData[2] = $info['district_id'] ? $info['district_id'] : 0;
  158. $cityNames[] = $info['district_name'] ? $info['district_name'] : '';
  159. }
  160. if ($cityData) {
  161. $info['cityData'] = $cityData;
  162. $info['cityText'] = implode(' ', array_filter($cityNames));
  163. }
  164. if (is_empty($info['age'])) {
  165. if (!is_empty($info['birthday'])) {
  166. $year = date('Y', $info['birthday']);
  167. $info['age'] = date('Y') - $year > 0 ? date('Y') - $year : 0;
  168. } else {
  169. $info['age'] = 0;
  170. }
  171. }
  172. if (is_empty($info['birthday'])) {
  173. $info['birthday'] = 0;
  174. } else {
  175. $info['birthday'] = date('Y-m-d', $info['birthday']);
  176. }
  177. if (is_empty($info['intro'])) {
  178. $info['intro'] = '';
  179. }
  180. if (is_empty($info['realname'])) {
  181. $info['realname'] = '';
  182. }
  183. // 二维码
  184. $url = env('WEB_URL') . '/pages/entry/auth?sid=' . $info['id'];
  185. $qrcode = WechatService::makeNormalQrcode($url);
  186. $info['qrcode'] = $qrcode ? get_image_url($qrcode) : '';
  187. // 会员
  188. if($info['is_vip'] && $info['vip_expire'] >= time()){
  189. $info['vip_expire_text'] = date('Y-m-d H:i:s', $info['vip_expire']);
  190. }else{
  191. $info['is_vip'] = 0;
  192. $info['vip_expire_text'] = '';
  193. }
  194. }
  195. }
  196. return $info;
  197. }
  198. /**
  199. * 保存资料
  200. * @param $userId
  201. * @return array
  202. */
  203. public function saveInfo($userId){
  204. $params = request()->all();
  205. $memberInfo = $this->model::where(['id'=> $userId])->first();
  206. if(!$memberInfo){
  207. return message('用户账号不可操作', false);
  208. }
  209. if($params['avatar']){
  210. $memberInfo->avatar = trim($params['avatar']);
  211. }
  212. if($params['birthday']){
  213. $memberInfo->birthday = strtotime($params['birthday']);
  214. }
  215. if($params['age']){
  216. $memberInfo->age = strtotime($params['age']);
  217. }
  218. if(!is_empty($params['intro'])){
  219. $memberInfo->intro = trim($params['intro']);
  220. }
  221. if(!is_empty($params['mobile'])){
  222. $memberInfo->mobile = trim($params['mobile']);
  223. }
  224. if(!empty($params['cityCodes'])){
  225. $cityData = $params['cityCodes'];
  226. if(!is_empty($cityData[0]) && $cityData[0]){
  227. $memberInfo->province_id = $cityData[0];
  228. }
  229. if(!is_empty($cityData[1]) && $cityData[1]){
  230. $memberInfo->city_id = $cityData[1];
  231. }
  232. if(!is_empty($cityData[2]) && $cityData[2]){
  233. $memberInfo->district_id = $cityData[2];
  234. }
  235. }
  236. $memberInfo->nickname = trim($params['nickname']);
  237. $memberInfo->gender = intval($params['gender']);
  238. if($memberInfo->save()){
  239. return message('保存成功', true);
  240. }else{
  241. return message('保存失败', false);
  242. }
  243. }
  244. /**
  245. * 账号注销
  246. * @param $userId
  247. * @return array
  248. */
  249. public function logout($userId){
  250. $memberInfo = $this->model::where(['id'=> $userId,'mark'=> 1,'status'=> 1])->first();
  251. if(!$memberInfo){
  252. return message('用户账号不可操作', false);
  253. }
  254. $memberInfo->mark = 0;
  255. $memberInfo->status = 2;
  256. $memberInfo->black_remark = request()->get('remark','');
  257. if($memberInfo->save()){
  258. return message('操作成功', true);
  259. }else{
  260. return message('操作失败', false);
  261. }
  262. }
  263. /**
  264. * 邀请奖励
  265. * @param $userId 注册用户
  266. * @param $inviteId 邀请用户
  267. * @param string $nickname 注册用户昵称
  268. * @return bool
  269. */
  270. public function inviteAward($userId, $inviteId, $nickname=''){
  271. // 验证当前用户是否已经奖励过
  272. $cacheKey = "caches:invite:u{$userId}_s{$inviteId}";
  273. $check = TradeModel::where(['user_id'=> $userId,'type'=> 6,'source_uid'=> $inviteId,'status'=>1,'mark'=> 1])->value('id');
  274. if($check){
  275. RedisService::set($cacheKey.':error_catched', ['error'=>'当前奖励已发放','date'=> date('Y-m-d H:i:s')], 7200);
  276. return false;
  277. }
  278. // 验证当前邀请用户是否账户有效
  279. $inviteInfo = $this->model::where(['id'=> $inviteId,'mark'=> 1,'status'=> 1])
  280. ->select(['id','openid','nickname','score','coupon','merits_num'])
  281. ->first();
  282. if(!$inviteInfo){
  283. RedisService::set($cacheKey.':error_invite', ['error'=>'当前邀请用户状态异常','date'=> date('Y-m-d H:i:s')], 7200);
  284. return false;
  285. }
  286. // 是否到达人数限制
  287. $curTime = strtotime(date('Y-m-d'));
  288. $inviteConfig = ConfigService::make()->getConfigByGroup(14);
  289. $inviteLimit = isset($inviteConfig['invite_limit'])? $inviteConfig['invite_limit']['value'] : 0;
  290. $checkCount = TradeModel::where(['user_id'=> $userId,'type'=> 6,'status'=>1,'mark'=> 1])
  291. ->where('create_time','>=',$curTime)
  292. ->count('id');
  293. if($inviteLimit>0 && $checkCount>=$inviteLimit){
  294. RedisService::set($cacheKey.':error_limit', ['error'=>'今日奖励已到达限制人数:'.$inviteLimit,'date'=> date('Y-m-d H:i:s')], 7200);
  295. return false;
  296. }
  297. // 奖励花灯券
  298. \DB::beginTransaction();
  299. $coupon = isset($inviteConfig['invite_give_coupon'])? intval($inviteConfig['invite_give_coupon']['value']) : 0;
  300. if($coupon>0){
  301. // 账户
  302. if(!$this->model::where(['id'=> $inviteId])->increment('coupon', $coupon)){
  303. \DB::rollBack();
  304. RedisService::set($cacheKey.':error_coupon', ['error'=>'奖励花灯券账户更新失败:'.$coupon,'date'=> date('Y-m-d H:i:s')], 7200);
  305. return false;
  306. }
  307. $data = [
  308. 'user_id'=> $inviteId,
  309. 'source_uid'=> $userId,
  310. 'type'=> 6,
  311. 'coin_type'=> 1,
  312. 'pay_type'=> 4,
  313. 'money'=> $coupon,
  314. 'change_type'=> 1,
  315. 'balance'=> $inviteInfo->coupon,
  316. 'create_time'=> time(),
  317. 'remark'=> "邀请用户[{$nickname}],奖励{$coupon}花灯券",
  318. 'status'=> 1,
  319. ];
  320. if(!TradeModel::insertGetId($data)){
  321. \DB::rollBack();
  322. RedisService::set($cacheKey.':error_acccount_coupon', ['error'=>'奖励花灯券账户明细处理失败','data'=> $data,'date'=> date('Y-m-d H:i:s')], 7200);
  323. return false;
  324. }
  325. }
  326. // 奖励功德值
  327. $giveGd = isset($inviteConfig['invite_give_gd'])? intval($inviteConfig['invite_give_gd']['value']) : 0;
  328. if($giveGd>0){
  329. // 账户
  330. if(!$this->model::where(['id'=> $inviteId])->increment('merits_num', $giveGd)){
  331. \DB::rollBack();
  332. RedisService::set($cacheKey.':error_gd', ['error'=>'奖励功德值账户更新失败:'.$giveGd,'date'=> date('Y-m-d H:i:s')], 7200);
  333. return false;
  334. }
  335. $data = [
  336. 'user_id'=> $inviteId,
  337. 'source_uid'=> $userId,
  338. 'type'=> 6,
  339. 'coin_type'=> 4,
  340. 'pay_type'=> 4,
  341. 'money'=> $giveGd,
  342. 'change_type'=> 1,
  343. 'balance'=> $inviteInfo->merits_num,
  344. 'create_time'=> time(),
  345. 'remark'=> "邀请用户[{$nickname}],奖励{$giveGd}功德值",
  346. 'status'=> 1,
  347. ];
  348. if(!TradeModel::insertGetId($data)){
  349. \DB::rollBack();
  350. RedisService::set($cacheKey.':error_acccount_gd', ['error'=>'奖励花灯券账户明细处理失败','data'=> $data,'date'=> date('Y-m-d H:i:s')], 7200);
  351. return false;
  352. }
  353. }
  354. // 奖励积分
  355. \DB::commit();
  356. return true;
  357. }
  358. /**
  359. * 加入会员
  360. * @param $userId
  361. */
  362. public function buyVip($userId){
  363. $memberInfo = $this->model::where(['id'=> $userId,'mark'=> 1,'status'=> 1])
  364. ->select(['id','nickname','openid','coupon','mobile','is_vip','vip_expire'])
  365. ->first();
  366. if(!$memberInfo){
  367. return message('用户账户不可操作,请联系客服',false);
  368. }
  369. $params = request()->all();
  370. $receiveVipMessage = isset($params['receive_vip_message'])? $params['receive_vip_message'] : 0;
  371. if($receiveVipMessage>0 && empty($memberInfo->mobile)){
  372. return message('接收消息通知,请先到个人资料设置手机号码再尝试',false);
  373. }
  374. // 是否已经加入了会员
  375. $isVip = false;
  376. if($memberInfo->is_vip && $memberInfo->vip_expire>=time()){
  377. $isVip = true;
  378. }
  379. // 验证账户
  380. $config = ConfigService::make()->getConfigByGroup(15);
  381. $vipPrice = isset($config['vip_price'])? intval($config['vip_price']['value']) : 0;
  382. $vipGiveCoupon = isset($config['vip_give_coupon'])? intval($config['vip_give_coupon']['value']) : 0;
  383. if($vipPrice<=0){
  384. return message('当前会员价格参数设置错误,请联系客服',false);
  385. }
  386. if($vipPrice<=$vipGiveCoupon){
  387. return message('当前会员赠送参数设置错误,请联系客服',false);
  388. }
  389. if($memberInfo->coupon < $vipPrice){
  390. return message('您的账户花灯券不足,请先充值后重试',false,[],'10003');
  391. }
  392. // 操作账户
  393. \DB::beginTransaction();
  394. if(!$this->model::where(['id'=> $userId,'mark'=> 1])->decrement('coupon',($vipPrice - $vipGiveCoupon))){
  395. \DB::rollBack();
  396. return message('扣除用户账户失败,请刷新后重试',false);
  397. }
  398. // 更新会员有效期
  399. if($isVip){
  400. $vipExpire = $memberInfo->vip_expire+365*24*3600;
  401. }else{
  402. $vipExpire = time()+365*24*3600;
  403. }
  404. if(!$this->model::where(['id'=> $userId,'mark'=> 1])->update(['is_vip'=> 1,'receive_vip_message'=> $receiveVipMessage,'vip_expire'=> $vipExpire])){
  405. \DB::rollBack();
  406. return message('更新会员有效期失败,请刷新后重试',false);
  407. }
  408. // 账户明细
  409. $data = [
  410. 'user_id'=> $userId,
  411. 'type'=> 1,
  412. 'coin_type'=> 1,
  413. 'pay_type'=> 1,
  414. 'money'=> $vipPrice,
  415. 'change_type'=> 2,
  416. 'balance'=> $memberInfo->coupon,
  417. 'create_time'=> time(),
  418. 'remark'=> ($isVip? '续费':'加入')."会员扣除{$vipPrice}花灯券",
  419. 'status'=> 1,
  420. ];
  421. if(!TradeModel::insertGetId($data)){
  422. \DB::rollBack();
  423. return message('处理账户明细失败,请刷新后重试',false);
  424. }
  425. // 赠送
  426. if($vipGiveCoupon>0){
  427. $data = [
  428. 'user_id'=> $userId,
  429. 'type'=> 3,
  430. 'coin_type'=> 1,
  431. 'pay_type'=> 4,
  432. 'money'=> $vipGiveCoupon,
  433. 'change_type'=> 1,
  434. 'balance'=> ($memberInfo->coupon-$vipPrice),
  435. 'create_time'=> time()+1,
  436. 'remark'=> ($isVip? '续费':'加入')."会员赠送{$vipGiveCoupon}花灯券",
  437. 'status'=> 1,
  438. ];
  439. if(!TradeModel::insertGetId($data)){
  440. \DB::rollBack();
  441. return message('赠送券处理失败,请刷新后重试',false);
  442. }
  443. }
  444. \DB::commit();
  445. return message(($isVip? '续费':'加入').'会员成功',true);
  446. }
  447. public function sign($userId){
  448. $memberInfo = $this->model::where(['id'=> $userId, 'mark'=>1 ,'status'=> 1])
  449. ->select(['id','openid','nickname','score','sign_time'])
  450. ->first();
  451. if(!$memberInfo){
  452. return message('账户不可操作,请联系客服',false);
  453. }
  454. if($memberInfo->sign_time > strtotime(date('Y-m-d'))){
  455. return message('您今天已签到过,请明天再来',false);
  456. }
  457. // 参数
  458. $score = ConfigService::make()->getConfigByCode('sign_give_score');
  459. $score = $score? $score : 0;
  460. // 处理
  461. \DB::beginTransaction();
  462. if($score>0){
  463. if(!$this->model::where(['id'=> $userId,'mark'=> 1])->increment('score', $score)){
  464. \DB::rollBack();
  465. return message('更新账户积分失败,请刷新后重试',false);
  466. }
  467. $data = [
  468. 'user_id'=> $userId,
  469. 'type'=> 3,
  470. 'coin_type'=> 3,
  471. 'pay_type'=> 4,
  472. 'money'=> $score,
  473. 'change_type'=> 1,
  474. 'balance'=> $memberInfo->score,
  475. 'create_time'=> time(),
  476. 'remark'=> "每日签到赠送{$score}积分",
  477. 'status'=> 1,
  478. ];
  479. if(!TradeModel::insertGetId($data)){
  480. \DB::rollBack();
  481. return message('积分奖励处理失败,请刷新后重试',false);
  482. }
  483. }
  484. if(!$this->model::where(['id'=> $userId,'mark'=> 1])->update(['sign_time'=> time()])){
  485. \DB::rollBack();
  486. return message('签到处理失败,请刷新后重试',false);
  487. }
  488. \DB::commit();
  489. return message('签到成功',true);
  490. }
  491. }