MemberSignService.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Niucloud-admin 企业快速开发的saas管理平台
  4. // +----------------------------------------------------------------------
  5. // | 官方网址:https://www.niucloud.com
  6. // +----------------------------------------------------------------------
  7. // | niucloud团队 版权所有 开源版本可自由商用
  8. // +----------------------------------------------------------------------
  9. // | Author: Niucloud Team
  10. // +----------------------------------------------------------------------
  11. namespace app\service\api\member;
  12. use app\job\member\MemberGiftGrantJob;
  13. use app\model\member\MemberSign;
  14. use app\service\core\member\CoreMemberService;
  15. use app\service\core\sys\CoreConfigService;
  16. use core\base\BaseApiService;
  17. use core\exception\CommonException;
  18. use think\db\exception\DbException;
  19. use think\facade\Db;
  20. use DateInterval;
  21. use DateTime;
  22. use DatePeriod;
  23. /**
  24. * 会员签到服务层
  25. * Class BaseService
  26. * @package app\service
  27. */
  28. class MemberSignService extends BaseApiService
  29. {
  30. public function __construct()
  31. {
  32. parent::__construct();
  33. $this->model = new MemberSign();
  34. }
  35. /**
  36. * 会员签到记录
  37. * @param array $where
  38. * @return array
  39. */
  40. public function getPage(array $where = [])
  41. {
  42. $where['member_id'] = $this->member_id;
  43. $where['site_id'] = $this->site_id;
  44. $field = 'sign_id, site_id, member_id, days, day_award, continue_award, continue_tag, create_time, is_sign';
  45. $search_model = $this->model->where($where)->field($field)->append(['is_sign_name'])->order('create_time desc');
  46. return $this->pageQuery($search_model);
  47. }
  48. /**
  49. * 会员签到详情
  50. * @param int $sign_id
  51. * @return array
  52. */
  53. public function getInfo(int $sign_id)
  54. {
  55. $field = 'sign_id, site_id, member_id, days, day_award, continue_award, continue_tag, create_time, is_sign';
  56. return $this->model->where([['sign_id', '=', $sign_id], ['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->field($field)->append(['is_sign_name'])->findOrEmpty()->toArray();
  57. }
  58. /**
  59. * 签到
  60. * @return array
  61. */
  62. public function sign()
  63. {
  64. $sign_config = $this->getSign();
  65. if (!$sign_config['is_use']) throw new CommonException('SIGN_NOT_USE');
  66. if (empty($sign_config['sign_period']) || empty($sign_config['day_award'])) throw new CommonException('SIGN_NOT_SET');
  67. $sign_period = $sign_config['sign_period'];//签到周期
  68. $today = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time')->findOrEmpty()->toArray();
  69. if (!empty($today)) throw new CommonException('SIGNED_TODAY');
  70. Db::startTrans();
  71. try {
  72. $yesterday = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', 'yesterday')->findOrEmpty()->toArray();
  73. if ($yesterday) {
  74. $days = $yesterday['days'];
  75. $days++;
  76. if ($days > $sign_period) { //连签天数大于签到周期,连签天数重置为1
  77. $days = 1;
  78. $data['start_time'] = time();
  79. }
  80. if (!empty($sign_config['continue_award'])) {
  81. $continue_signs = array_column($sign_config['continue_award'], 'continue_sign');
  82. //获取连签奖励最大天数
  83. $max_continue_sign = max($continue_signs);
  84. if ($max_continue_sign < $sign_period && $days > $max_continue_sign) { //连签奖励最大天数 小于 签到周期 并且 连签天数 大于 连签奖励最大天数 连签天数重置为1
  85. $days = 1;
  86. }
  87. }
  88. } else { //断签,连签天数重置为1
  89. $days = 1;
  90. $data['start_time'] = time();
  91. }
  92. $awards = []; //奖励数组
  93. $continue_text = ''; //连签提示
  94. //添加签到记录
  95. $data['site_id'] = $this->site_id;
  96. $data['member_id'] = $this->member_id;
  97. $data['days'] = $days;
  98. $data['day_award'] = $sign_config['day_award'];
  99. $data['is_sign'] = 1;
  100. $data['create_time'] = time();
  101. $res = $this->model->create($data);
  102. if ($res) {
  103. //日签奖励发放
  104. MemberGiftGrantJob::dispatch([
  105. 'site_id' => $this->site_id,
  106. 'member_id' => $this->member_id,
  107. 'gift' => $sign_config['day_award'],
  108. 'param' => [
  109. 'from_type' => 'day_sign_award',
  110. 'memo' => '日签奖励'
  111. ]
  112. ]);
  113. $awards['day_award'] = $sign_config['day_award'];
  114. //签到成功后判断连签天数是否满足连签奖励发放条件
  115. if (!empty($sign_config['continue_award'])) {
  116. foreach ($sign_config['continue_award'] as $key => $value) {
  117. $continue_sign = intval($value['continue_sign']);//连续签到天数要求
  118. //如果连签天数满足配置条件,发放连签奖励
  119. if ($res->days == $continue_sign) {
  120. $gifts = $value;
  121. unset($gifts['continue_sign'], $gifts['continue_tag'], $gifts['receive_limit'], $gifts['receive_num']);
  122. $continue_data['continue_award'] = $value;
  123. $continue_data['continue_tag'] = $value['continue_tag'];//连签奖励标识
  124. if ($value['receive_limit'] == 2) {//receive_limit (1.不限制 2.每人限领 receive_num 次)
  125. //周期开始时间
  126. $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
  127. //周期结束时间
  128. $period_end_time = strtotime("+$sign_period day", $period_start_time);
  129. //查询领取次数
  130. $receive_count = $this->model
  131. ->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['continue_tag', '=', $value['continue_tag']]])
  132. ->whereBetweenTime('create_time', $period_start_time, $period_end_time)->count('sign_id');
  133. if ($receive_count < $value['receive_num']) {
  134. //连签奖励发放
  135. MemberGiftGrantJob::dispatch([
  136. 'site_id' => $this->site_id,
  137. 'member_id' => $this->member_id,
  138. 'gift' => $gifts,
  139. 'param' => [
  140. 'from_type' => 'continue_sign_award',
  141. 'memo' => '连签奖励'
  142. ]
  143. ]);
  144. $awards['continue_award'] = $gifts;
  145. $continue_text = get_lang('CONTINUE_SIGN').$res->days.get_lang('DAYS');
  146. //更新连签发放记录
  147. $this->model->where([['sign_id', '=', $res->sign_id]])->update($continue_data);
  148. }
  149. } else { //不限制
  150. //连签奖励发放
  151. MemberGiftGrantJob::dispatch([
  152. 'site_id' => $this->site_id,
  153. 'member_id' => $this->member_id,
  154. 'gift' => $gifts,
  155. 'param' => [
  156. 'from_type' => 'continue_sign_award',
  157. 'memo' => '连签奖励'
  158. ]
  159. ]);
  160. $awards['continue_award'] = $gifts;
  161. $continue_text = get_lang('CONTINUE_SIGN').$res->days.get_lang('DAYS');
  162. //更新连签发放记录
  163. $this->model->where([['sign_id', '=', $res->sign_id]])->update($continue_data);
  164. }
  165. }
  166. }
  167. }
  168. }
  169. Db::commit();
  170. $awards_total = $this->getTotalAward($awards);
  171. $result['title'] = get_lang('SIGN_SUCCESS');
  172. $result['info'] = $continue_text.get_lang('GET_AWARD');
  173. $result['awards'] = $awards_total;
  174. if ($awards_total) {
  175. return $result;
  176. } else {
  177. return [
  178. 'title' => '',
  179. 'info' => '',
  180. 'awards' => [],
  181. ];
  182. }
  183. } catch (DbException $e) {
  184. Db::rollback();
  185. throw new CommonException($e->getMessage());
  186. }
  187. }
  188. /**
  189. * 获取月签到数据
  190. * @param int $year
  191. * @param int $month
  192. * @return array
  193. */
  194. public function getSignInfo(int $year, int $month)
  195. {
  196. $data = [];
  197. $info = $this->getSign();
  198. if ($info['is_use'] == 1) {//判断签到是否开启
  199. $model_result = $this->model->field('create_time')->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereMonth('create_time', $year . '-' . sprintf("%02d", $month))->select();
  200. $days = [];
  201. foreach ($model_result as $key => $value) {
  202. $day = date('d', strtotime($value['create_time']));
  203. array_push($days, $day);
  204. }
  205. $data['days'] = $days;
  206. if (!empty($info['sign_period']) && !empty($info['continue_award'])) {//判断签到周期和连签奖励是否设置
  207. $sign_period = $info['sign_period'];//签到周期
  208. $continue_signs = array_column($info['continue_award'], 'continue_sign');
  209. //获取连签奖励最大天数
  210. $max_continue_sign = max($continue_signs);
  211. //周期开始时间
  212. $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
  213. if (!empty($period_start_time)) {
  214. //周期结束时间
  215. $period_end_time = strtotime("+$sign_period day", $period_start_time);
  216. //获取两个时间戳之间的天数组
  217. $days_array = $this->getDaysArray($period_start_time, $period_end_time);
  218. foreach ($days_array as $key => $value) {
  219. $day = $key + 1;
  220. foreach ($info['continue_award'] as $k => $v) {
  221. if ($v['receive_limit'] == 1) {//不限制次数奖励添加
  222. $period_num = intdiv($sign_period, $max_continue_sign);//周期内可循环轮次
  223. for ($i = 0; $i < $period_num; $i++) {
  224. if ($max_continue_sign * $i + $v['continue_sign'] == $day) {
  225. $data['period'][$key]['award'] = true;
  226. }
  227. }
  228. } else {//限制次数奖励添加
  229. for ($i = 0; $i < $v['receive_num']; $i++) {
  230. if ($max_continue_sign * $i + $v['continue_sign'] == $day) {
  231. $data['period'][$key]['award'] = true;
  232. }
  233. }
  234. }
  235. }
  236. $data['period'][$key]['day'] = $value;
  237. }
  238. } else {
  239. $data['period'] = [];
  240. }
  241. } else {
  242. $data['period'] = [];
  243. }
  244. }
  245. return $data;
  246. }
  247. /**
  248. * 获取日签到奖励
  249. * @param int $year
  250. * @param int $month
  251. * @param int $day
  252. * @return array
  253. */
  254. public function getDayAward(int $year, int $month, int $day)
  255. {
  256. $max_continue_sign = 1;//连签奖励最大天数
  257. $continue_sign_day = 0;//连签奖励天数
  258. $time = $year.'-'.sprintf("%02d", $month).'-'.sprintf("%02d", $day);
  259. $info = $this->getSign();
  260. if (!$info['is_use']) throw new CommonException('SIGN_NOT_USE');
  261. if (empty($info['sign_period']) || empty($info['day_award'])) throw new CommonException('SIGN_NOT_SET');
  262. $sign_period = $info['sign_period'];//签到周期
  263. if (!empty($info['continue_award'])) {
  264. $continue_signs = array_column($info['continue_award'], 'continue_sign');
  265. //获取连签奖励最大天数
  266. $max_continue_sign = max($continue_signs);
  267. }
  268. //周期开始时间
  269. $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
  270. //周期结束时间
  271. $period_end_time = strtotime("+$sign_period day", $period_start_time);
  272. //获取两个时间戳之间的天数组
  273. $days_array = $this->getDaysArray($period_start_time, $period_end_time);
  274. $award = [];//当日奖励
  275. //判断查询日期是否在签到周期内
  276. if (in_array($time, $days_array)) {
  277. $counter = 0;//计数器
  278. foreach ($days_array as $key => $value) {
  279. $counter++;
  280. if ($value == $time) {
  281. $continue_sign_day = $counter;
  282. $award['day_award'] = $info['day_award'];
  283. if (!empty($info['continue_award'])) {
  284. $days = $key + 1;
  285. foreach ($info['continue_award'] as $k => $v) {
  286. $gift = $v;
  287. unset($gift['continue_sign'], $gift['continue_tag'], $gift['receive_limit'], $gift['receive_num']);
  288. if ($v['receive_limit'] == 1) {//不限制次数奖励添加
  289. $period_num = intdiv($sign_period, $max_continue_sign);//周期内可循环轮次
  290. for ($i = 0; $i < $period_num; $i++) {
  291. if ($max_continue_sign * $i + $v['continue_sign'] == $days) {
  292. $award['continue_award'] = $gift;
  293. }
  294. }
  295. } else {//限制次数奖励添加
  296. for ($i = 0; $i < $v['receive_num']; $i++) {
  297. if ($max_continue_sign * $i + $v['continue_sign'] == $days) {
  298. $award['continue_award'] = $gift;
  299. }
  300. }
  301. }
  302. }
  303. }
  304. }
  305. if (!empty($info['continue_award'])) {
  306. if ($counter % $max_continue_sign == 0) {
  307. $counter = 0;
  308. }
  309. } else {
  310. if ($counter % $sign_period == 0) {
  311. $counter = 0;
  312. }
  313. }
  314. }
  315. } else {
  316. $day_result = $this->model->field('create_time')->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', $time)->findOrEmpty()->toArray();
  317. if (!empty($day_result)) {
  318. $award['day_award'] = $day_result['day_award'];
  319. $continue_award = $day_result['continue_award'];
  320. if (!empty($continue_award)) {
  321. unset($continue_award['continue_sign'], $continue_award['continue_tag'], $continue_award['receive_limit'], $continue_award['receive_num']);
  322. $award['continue_award'] = $continue_award;
  323. }
  324. }
  325. }
  326. $awards_total = $this->getTotalAward($award);
  327. $continue_text = $continue_sign_day > 0 ? get_lang('CONTINUE_SIGN').$continue_sign_day.get_lang('DAYS') : '';
  328. $result['title'] = get_lang('SIGN_AWARD');
  329. $result['info'] = $continue_text.get_lang('WILL_GET_AWARD');
  330. $result['awards'] = $awards_total;
  331. if ($awards_total) {
  332. return $result;
  333. } else {
  334. return [
  335. 'title' => '',
  336. 'info' => '',
  337. 'awards' => [],
  338. ];
  339. }
  340. }
  341. /**
  342. * 获取合并奖励数据
  343. * @param $awards
  344. * @return array|null
  345. */
  346. private function getTotalAward($awards)
  347. {
  348. $total_point = 0;
  349. $total_balance = 0;
  350. $coupon_id = [];
  351. $coupon_list = [];
  352. $is_use_point_day = false;
  353. $is_use_point_continue = false;
  354. $is_use_balance_day = false;
  355. $is_use_balance_continue = false;
  356. $is_use_coupon_day = false;
  357. $is_use_coupon_continue = false;
  358. if (!empty($awards['day_award']['point'])) {
  359. if ($awards['day_award']['point']['is_use'] == 1) {
  360. $is_use_point_day = true;
  361. $total_point += intval($awards['day_award']['point']['num']);
  362. }
  363. }
  364. if (!empty($awards['day_award']['balance'])) {
  365. if ($awards['day_award']['balance']['is_use'] == 1) {
  366. $is_use_balance_day = true;
  367. $total_balance += floatval($awards['day_award']['balance']['money']);
  368. }
  369. }
  370. if (!empty($awards['day_award']['shop_coupon'])) {
  371. if ($awards['day_award']['shop_coupon']['is_use'] == 1) {
  372. $is_use_coupon_day = true;
  373. $coupon_id = array_merge($coupon_id, $awards['day_award']['shop_coupon']['coupon_id']);
  374. $coupon_list = $this->getArrayMerge($coupon_list, $awards['day_award']['shop_coupon']['coupon_list']);
  375. }
  376. }
  377. if (!empty($awards['continue_award'])) {
  378. if (!empty($awards['continue_award']['point'])) {
  379. if ($awards['continue_award']['point']['is_use'] == 1) {
  380. $is_use_point_continue = true;
  381. $total_point += intval($awards['continue_award']['point']['num']);
  382. }
  383. }
  384. if (!empty($awards['continue_award']['balance'])) {
  385. if ($awards['continue_award']['balance']['is_use'] == 1) {
  386. $is_use_balance_continue = true;
  387. $total_balance += floatval($awards['continue_award']['balance']['money']);
  388. }
  389. }
  390. if (!empty($awards['continue_award']['shop_coupon'])) {
  391. if ($awards['continue_award']['shop_coupon']['is_use'] == 1) {
  392. $is_use_coupon_continue = true;
  393. $coupon_id = array_merge($coupon_id, $awards['continue_award']['shop_coupon']['coupon_id']);
  394. $coupon_list = $this->getArrayMerge($coupon_list, $awards['continue_award']['shop_coupon']['coupon_list']);
  395. }
  396. }
  397. }
  398. $coupon_id = array_unique($coupon_id);
  399. $is_use_point = ($is_use_point_day || $is_use_point_continue) ? 1 : 0;
  400. $is_use_balance = ($is_use_balance_day || $is_use_balance_continue) ? 1 : 0;
  401. $is_use_coupon = ($is_use_coupon_day || $is_use_coupon_continue) ? 1 : 0;
  402. //相同奖励合并
  403. $awards_total = [
  404. 'point' => [
  405. 'is_use' => $is_use_point,
  406. 'num' => $total_point,
  407. ],
  408. 'balance' => [
  409. 'is_use' => $is_use_balance,
  410. 'money' => $total_balance,
  411. ],
  412. 'shop_coupon' => [
  413. 'is_use' => $is_use_coupon,
  414. 'coupon_id' => $coupon_id,
  415. 'coupon_list' => $coupon_list,
  416. ]
  417. ];
  418. return (new CoreMemberService())->getGiftContent($this->site_id, $awards_total, 'member_sign');
  419. }
  420. /**
  421. * 获取用户签到设置
  422. * @return array
  423. */
  424. public function getSignConfig()
  425. {
  426. $info = $this->getSign();
  427. $today = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time')->findOrEmpty()->toArray();
  428. $yesterday = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', 'yesterday')->findOrEmpty()->toArray();
  429. if (!empty($info['day_award'])) {
  430. $day_award = (new CoreMemberService())->getGiftContent($this->site_id, $info['day_award'],'member_sign');
  431. $info['day_award'] = $day_award;
  432. }
  433. if (!empty($info['continue_award'])) {
  434. foreach ($info['continue_award'] as $key => $value) {
  435. $gift = $value;
  436. unset($gift['continue_sign'], $gift['continue_tag'], $gift['receive_limit'], $gift['receive_num']);
  437. $gift_content = (new CoreMemberService())->getGiftContent($this->site_id, $gift, 'member_sign_continue');
  438. $gift_count = 0;
  439. $content_text = '';
  440. $content_icon = '';
  441. foreach ($gift_content as $vv) {
  442. if ($vv['is_use'] == 1) {
  443. foreach ($vv['content'] as $v) {
  444. $content_text = $content_text . ($gift_count == 0 ? '' : '+') . $v['text'];
  445. $content_icon = $v['icon'];
  446. $gift_count++;
  447. }
  448. }
  449. }
  450. if ($gift_count > 1) {
  451. $continue_award['gift'] = ['total' => ['text' => $content_text, 'icon' => '/static/resource/images/member/sign/pack01.png']];
  452. } else if($gift_count == 1) {
  453. $continue_award['gift'] = ['total' => ['text' => $content_text, 'icon' => $content_icon]];
  454. } else {
  455. $continue_award['gift'] = [];
  456. }
  457. $continue_award['continue_sign'] = $value['continue_sign'];
  458. $info['continue_award'][$key] = $continue_award;
  459. }
  460. }
  461. $info['is_sign'] = empty($today) ? false : true;//是否签到
  462. if (empty($today)) {
  463. $info['days'] = empty($yesterday) ? 0 : $yesterday['days'];//连签天数
  464. } else {
  465. $info['days'] = $today['days'];//连签天数
  466. }
  467. return $info;
  468. }
  469. /**
  470. * 获取站点签到设置
  471. */
  472. public function getSign()
  473. {
  474. $info = ( new CoreConfigService() )->getConfig($this->request->defaultSiteId(), 'SIGN_CONFIG');
  475. if (empty($info)) {
  476. $info = [];
  477. $info[ 'value' ] = [
  478. 'is_use' => 0,
  479. 'sign_period' => '',
  480. 'day_award' => '',
  481. 'continue_award' => [],
  482. 'rule_explain' => ''
  483. ];
  484. }
  485. return $info[ 'value' ];
  486. }
  487. /**
  488. * 获取两个时间戳之间的天数组
  489. * @param $start_timestamp
  490. * @param $end_timestamp
  491. * @return array
  492. */
  493. private function getDaysArray($start_timestamp, $end_timestamp) {
  494. $start = new DateTime("@$start_timestamp"); // 使用时间戳创建DateTime对象
  495. $end = new DateTime("@$end_timestamp"); // 同上
  496. $interval = new DateInterval('P1D'); // 每天的周期
  497. $period = new DatePeriod($start, $interval, $end); // 创建周期范围
  498. $days_array = [];
  499. foreach ($period as $day) {
  500. $days_array[] = $day->format('Y-m-d'); // 格式化日期并添加到数组
  501. }
  502. return $days_array;
  503. }
  504. /**
  505. * 合并数据,如果键值相等其值相加
  506. * @param $desc
  507. * @param $json_wares
  508. * @return array|false
  509. */
  510. private static function getArrayMerge($desc, $json_wares)
  511. {
  512. if (is_array($desc) && is_array($json_wares)) {
  513. $arrayMerge = array();
  514. foreach ($json_wares as $key=>$value) {
  515. if (array_key_exists($key, $desc)) {
  516. $arrayMerge[$key] = $value + $desc[$key];
  517. unset($desc[$key]);
  518. } else {
  519. $arrayMerge[$key] = $value;
  520. }
  521. }
  522. return $arrayMerge+$desc;
  523. } else {
  524. return false;
  525. }
  526. }
  527. }