Commit a4671c53 a4671c53f6497be3923bd8359c08a222313b19c5 by 杨俊

feat(master): 初始化

0 parents
Showing 507 changed files with 4944 additions and 0 deletions
1 root = true
2
3 [*]
4 charset = utf-8
5 end_of_line = lf
6 indent_size = 4
7 indent_style = space
8 insert_final_newline = true
9 trim_trailing_whitespace = true
10
11 [*.md]
12 trim_trailing_whitespace = false
13
14 [*.{yml,yaml}]
15 indent_size = 2
16
17 [docker-compose.yml]
18 indent_size = 4
1 APP_NAME=Laravel
2 APP_ENV=local
3 APP_KEY=
4 APP_DEBUG=true
5 APP_URL=http://localhost
6
7 LOG_CHANNEL=stack
8 LOG_DEPRECATIONS_CHANNEL=null
9 LOG_LEVEL=debug
10
11 DB_CONNECTION=mysql
12 DB_HOST=127.0.0.1
13 DB_PORT=3306
14 DB_DATABASE=laravel
15 DB_USERNAME=root
16 DB_PASSWORD=
17
18 BROADCAST_DRIVER=log
19 CACHE_DRIVER=file
20 FILESYSTEM_DISK=local
21 QUEUE_CONNECTION=sync
22 SESSION_DRIVER=file
23 SESSION_LIFETIME=120
24
25 REDIS_HOST=127.0.0.1
26 REDIS_PASSWORD=null
27 REDIS_PORT=6379
28
29 MAIL_MAILER=smtp
30 MAIL_HOST=mailpit
31 MAIL_PORT=1025
32 MAIL_USERNAME=null
33 MAIL_PASSWORD=null
34 MAIL_ENCRYPTION=null
35 MAIL_FROM_ADDRESS="hello@example.com"
36 MAIL_FROM_NAME="${APP_NAME}"
37
38 JPUSH_KEY=860a3d4ecfaf3c38eb539c4e
39 JPUSH_SECRET=df208c0eb485ed3ab3b18ff2
40
41 IM_KEY=
42 IM_SECRET=
43
44 WECHAT_OFFICIAL_ACCOUNT_APPID=
45 WECHAT_OFFICIAL_ACCOUNT_SECRET=
46 WECHAT_OFFICIAL_ACCOUNT_TOKEN=
47 WECHAT_OFFICIAL_ACCOUNT_AES_KEY=
48
49 WECHAT_MINI_APP_APPID=
50 WECHAT_MINI_APP_SECRET=
51 WECHAT_MINI_APP_TOKEN=
52 WECHAT_MINI_APP_AES_KEY=
53
54 OCTANE_SERVER=swoole
55 OCTANE_HTTPS=true
1 * text=auto
2
3 *.blade.php diff=html
4 *.css diff=css
5 *.html diff=html
6 *.md diff=markdown
7 *.php diff=php
8
9 /.github export-ignore
10 CHANGELOG.md export-ignore
11 .styleci.yml export-ignore
1 /node_modules
2 /public/build
3 /public/vendor/app
4 /public/hot
5 /public/storage
6 /storage/*.key
7 /vendor
8 .env
9 .env.backup
10 .env.production
11 .phpunit.result.cache
12 Homestead.json
13 Homestead.yaml
14 auth.json
15 npm-debug.log
16 yarn-error.log
17 supervisord.pid
18 /.fleet
19 /.idea
20 /.vscode
21 package-lock.json
1 # ChangeLog
2
3 ### feat
4
5 * 调整telescope的访问权限 ([4664ae7](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/4664ae7))
6 * 歌曲分享,厂牌管理员 ([37fc0c9](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/37fc0c9))
7 * 所有Token接口增加用户状态过滤中间件 ([e8fa624](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/e8fa624))
8 * 【平台端】Banner浏览用户模块重构 ([bea107a](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/bea107a))
9 * 【平台端】明星标签配置首页筛选 ([c0df0e2](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/c0df0e2))
10 * 【平台端】系统推送通知对象调整 ([11ec662](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/11ec662))
11 * 【平台端】用户详情补充“注册来源” ([923ac36](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/923ac36))
12 * 【平台端】认证待审核补充“注册来源” ([87ef34f](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/87ef34f))
13 * 【平台端】用户详情补充“注册来源” ([d64e76d](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/d64e76d))
14 * 【平台端】认证待审核补充“注册来源” ([2d83fb8](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/2d83fb8))
15 * 【应用前台】增加推荐用户列表 ([874d2cf](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/874d2cf))
16 * 【应用前台】审核认证补充字段“来源渠道” ([7c1502a](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/7c1502a))
17 * 【应用前台】聊天列表系统通知,邀请注册消息 ([704e352](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/704e352))
18 * 【应用前台】用户注册 ([85d2a56](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/85d2a56))
19 * 【应用前台】用户注册短信申请 ([a0fc305](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/a0fc305))
20 * 【应用前台】Demo详情 ([96d9e53](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/96d9e53))
21 * 【应用前台】Demo库 ([b679b8b](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/b679b8b))
22 * 【应用前台】首页音乐人筛选(明星标签) ([f482258](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/f482258))
23
24 ### fix
25
26 * 修复Banner异常 ([f3deedf](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/f3deedf))
27 * 修复普通权限用户浏览Banner异常bug ([92c42de](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/92c42de))
28 * 修复登录账号排序问题 ([892393c](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/892393c))
29 * 修复手机号登录显示区号不正确提示 ([ce3d123](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/ce3d123))
30 * 修复PHP发布JWT,Python无法通过 ([0892d1e](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/0892d1e))
31 * 修复邮件附件过大导致邮件无法发送bug ([7d0b5ee](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/7d0b5ee))
32 * 修复极光推送脚本参数名错误 ([278fa45](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/278fa45))
33 * 修复Demo详情分享数不正确 ([201dbbf](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/201dbbf))
34 * 修复线上groupMember接口app漏传pageSizebug,暂时调整默认分页数为30 ([2581fd4](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/2581fd4))
35 * 修复活动分享用户详情取消去重 ([f56c7ae](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/f56c7ae))
36 * 修复活动(试听|分享)用户标签数据错误 ([c018751](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/c018751))
37 * 修复音乐人列表star参数名 ([78bba46](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/78bba46))
38 * 修复厂牌主页下添加管理员日志记录错误bug ([8417c68](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/8417c68))
39 * 修复因为负载均衡无法获取真实IP问题 ([dcbd675](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/dcbd675))
40
41
42 ### docs
43
44 * 2.0.6.sql ([0553910](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/0553910))
45
46
47 ### refactor
48
49 * 重构Http Log记录方式 ([6c5454d](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/6c5454d))
50
51
52 ### other
53
54 * 2.0.7 sql文件 ([b796e8b](http://gitlab.hikoon.com/hi-sing/serviceWorker/commit/b796e8b))
1 <p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
2
3 <p align="center">
4 <a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
5 <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
6 <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
7 <a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
8 </p>
1 <?php
2
3 namespace App\Actions;
4
5 use App\Jobs\WechatTemplateMessageJob;
6 use App\Models\Activity;
7 use App\Models\SystemRole;
8 use App\Models\User;
9 use Carbon\Carbon;
10 use Lorisleiva\Actions\Concerns\AsAction;
11 use Lorisleiva\Actions\Concerns\AsJob;
12
13 class ActivityPublishApply
14 {
15 use AsAction, AsJob;
16
17 protected string $template = 'o9i6qtgr4ghtGvU-piimB9_mAaSOAXDDMzqx9zXYkss';
18
19 public function handle(Activity $activity): void
20 {
21 $activity->loadMissing('user:id,nick_name,real_name');
22
23 $roleIds = SystemRole::query()
24 ->whereRelation('permissions', fn($query) => $query->where('guard', 'Admin')->where('system_permissions.name', 'audition-activity-audit-audit'))
25 ->where('system_roles.status', 1)
26 ->pluck('system_roles.id')->toArray();
27
28
29 User::query()
30 ->join('user_has_roles', 'user_has_roles.user_id', 'users.id')
31 ->whereIn('user_has_roles.role_id', $roleIds)
32 ->where('users.status', 1)->where('users.official_status', 1)
33 ->distinct()->pluck('users.official_id')
34 ->each(fn($item) => WechatTemplateMessageJob::dispatch($item, $this->template, [
35 'first' => '厂牌提交试唱发布申请',
36 'keyword1' => '海星试唱提交了上架申请',
37 'keyword2' => sprintf('%s(%s)', data_get($activity, 'user.nick_name'), data_get($activity, 'user.real_name')),
38 'keyword3' => (string)data_get($activity, 'created_at', Carbon::now()->toDateTimeString()),
39 'keyword4' => sprintf('【%s】活动上架需要您的审核', $activity->getAttribute('song_name')),
40 'remark' => ''
41 ]));
42 }
43 }
1 <?php
2
3 namespace App\Actions;
4
5 use App\Enums\ActivityWorkStatusEnum;
6 use App\Models\ActivityUser;
7 use App\Models\Views\ActivityWork;
8 use App\Notifications\ActivitySingerSubmitWorkRejectNotification;
9 use App\Notifications\ActivitySubmitWorkRejectNotification;
10 use Lorisleiva\Actions\Concerns\AsAction;
11 use Lorisleiva\Actions\Concerns\AsJob;
12
13 class ActivityWorkAuditFail
14 {
15 use AsAction, AsJob;
16
17 public function handle(ActivityWork $work): void
18 {
19 ActivityUser::query()->whereKey($work->getKey())->update(['status' => ActivityWorkStatusEnum::REJECT]);
20 $this->pushUserNotification($work);
21 $this->pushBusinessNotification($work);
22 }
23
24 /**
25 * @param \App\Models\Views\ActivityWork $work
26 * @return void
27 */
28 private function pushUserNotification(ActivityWork $work): void
29 {
30 $work->user?->notify(new ActivitySubmitWorkRejectNotification($work));
31 }
32
33 /**
34 * @param \App\Models\Views\ActivityWork $work
35 * @return void
36 */
37 private function pushBusinessNotification(ActivityWork $work): void
38 {
39 $work->business?->notify(new ActivitySingerSubmitWorkRejectNotification($work));
40 }
41 }
1 <?php
2
3 namespace App\Actions;
4
5 use App\Enums\ActivityStatusEnum;
6 use App\Enums\ActivityWorkStatusEnum;
7 use App\Enums\ActivityWorkTypeEnum;
8 use App\Enums\BrokerPushMatchRecordStatusEnum;
9 use App\Jobs\BrokerPushMatchRecordJob;
10 use App\Models\Activity;
11 use App\Models\ActivityUser;
12 use App\Models\BrokerPushConfig;
13 use App\Models\BrokerPushMatchRecord;
14 use App\Models\BrokerUserConfig;
15 use App\Models\BrokerUserConfigItem;
16 use App\Models\SystemConfig;
17 use App\Models\Views\ActivityWork;
18 use App\Notifications\ActivitySingerSubmitWorkPassNotification;
19 use App\Notifications\ActivitySubmitWorkPassNotification;
20 use App\Support\Model;
21 use DB;
22 use Lorisleiva\Actions\Concerns\AsAction;
23
24 class ActivityWorkAuditSuccess
25 {
26 use AsAction;
27
28 private int $brokerId;
29
30 private BrokerUserConfig|Model $brokerUserConfig;
31
32 private string $brokerUserLevel;
33
34 /**
35 * @param \App\Models\Views\ActivityWork $work
36 * @param string $remark
37 * @return void
38 */
39 public function handle(ActivityWork $work): void
40 {
41 $this->brokerId = (int)$work->getAttribute('business_id');
42
43 DB::transaction(function () use ($work) {
44 Activity::query()->whereKey($work->getAttribute('activity_id'))
45 ->update(['status' => ActivityStatusEnum::MATCH, 'match_at' => now()->toDateTimeString()]);
46 ActivityUser::query()->whereKey($work->getKey())
47 ->update(['status' => ActivityWorkStatusEnum::SUCCESS, 'broker_id' => $this->brokerId, 'broker_level' => '']);
48 ActivityWork::query()->where('activity_id', $work->getAttribute('activity_id'))
49 ->where('type', ActivityWorkTypeEnum::SUBMIT)->where('status', ActivityWorkStatusEnum::WAIT)
50 ->eachById(fn(ActivityWork $activityWork) => ActivityWorkAuditFail::dispatch($activityWork));
51 });
52
53 $this->pushUserNotification($work);
54 $this->pushBusinessNotification($work);
55 }
56
57 /**
58 * @param \App\Models\Views\ActivityWork $work
59 * @param string $remark
60 * @return void
61 */
62 private function pushUserNotification(ActivityWork $work): void
63 {
64 $work->user?->notify(new ActivitySubmitWorkPassNotification($work));
65 }
66
67 /**
68 * @param \App\Models\Views\ActivityWork $work
69 * @return void
70 */
71 private function pushBusinessNotification(ActivityWork $work): void
72 {
73 $work->business?->notify(new ActivitySingerSubmitWorkPassNotification($work));
74
75 if ($config = $this->getUserConfig()) {
76 $level = $this->getUserLevel($config);
77
78 ActivityUser::query()->whereKey($work->getKey())->update(['broker_id' => $this->brokerId, 'broker_level' => $level]);
79 if ($pushConfig = BrokerPushConfig::query()->where('identifier', $level)->where('match_title', '!=', '')->first()) {
80 BrokerPushMatchRecord::created(static fn($record) => BrokerPushMatchRecordJob::dispatch($record));
81 BrokerPushMatchRecord::query()->create([
82 'broker_id' => $this->brokerId,
83 'broker_level' => str_replace('对应分类:', '', $level),
84 'user_id' => $work->getAttribute('user_id'),
85 'user_tag' => $work->user->authTags()->pluck('name')?->join('|') ?? '',
86 'activity_id' => $work->getAttribute('activity_id'),
87 'project_id' => $work->getAttribute('project_id'),
88 'title' => $pushConfig->getAttribute('match_title'),
89 'content' => str_replace(
90 ['{border}', '{singer}', '{start}', '{end}', '{type}', '{brand}', '{song}'],
91 [
92 $work->business->getAttribute('nick_name'),
93 $work->user->getAttribute('nick_name'),
94 $config->getAttribute('begin_at'),
95 $config->getAttribute('end_at'),
96 str_replace('对应分类:', '', $level),
97 $work->project()->value('name'),
98 $work->getAttribute('activity_name')
99 ],
100 $pushConfig->getAttribute('match_intro')
101 ),
102 'send_at' => now()->toDateTimeString(),
103 'status' => BrokerPushMatchRecordStatusEnum::PROCESSING
104 ]);
105 }
106 }
107 }
108
109 /**
110 * @return \App\Support\Model|null
111 */
112 private function getUserConfig(): ?Model
113 {
114 return BrokerUserConfig::query()->where('begin_at', '<=', now()->toDateTimeString())->where('end_at', '>=', now()->toDateTimeString())->first();
115 }
116
117 /**
118 * @param \App\Support\Model $config
119 * @return string
120 */
121 private function getUserLevel(Model $config): string
122 {
123 if (!$level = BrokerUserConfigItem::query()->where('config_id', $config->getKey())->where('user_id', $this->brokerId)->value('identifier')) {
124 $level = SystemConfig::query()->where('identifier', 'g65owmrgKSUhxV2afndUg')->value('content') ?? '';
125 $level = (empty($level) ? '' : '对应分类:' . $level);
126 }
127
128 return $level;
129 }
130 }
1 <?php
2
3 namespace App\Actions;
4
5 use App\Helpers\IMHelper;
6 use App\Models\User;
7 use Lorisleiva\Actions\Concerns\AsAction;
8
9 class UserRegisterAction
10 {
11 use AsAction;
12
13 public string $jobQueue = 'im';
14
15 /**
16 * @throws \JsonException
17 * @throws \Exception
18 */
19 public function handle(User $user): void
20 {
21 $content = '您邀请的用户:' . $user->getAttribute('nick_name') . ',已完成账号注册。感谢您对平台的支持!';
22 IMHelper::sendSingleChatMessage(IMHelper::SYSTEM_USER, $user->getAttribute('inviter_id'), [
23 "MsgBody" => [
24 IMHelper::createCustomMessage([
25 "Desc" => $content,
26 "Data" => [
27 'businessID' => 'user_register',
28 'MsgType' => 'text',
29 'MsgEvent' => 'user_register',
30 'MsgAttribute' => ['Id' => $user->getKey(), 'NickName' => $user->getAttribute('nick_name')],
31 'Content' => $content,
32 ],
33 ])
34 ],
35 "CloudCustomData" => [],
36 "OfflinePushInfo" => IMHelper::createOfflineMessage('系统消息', $content)
37 ]);
38 }
39 }
1 <?php
2
3 namespace App\Channels;
4
5 use App\Jobs\JPushMessageJob;
6 use App\Models\User;
7 use Illuminate\Notifications\Notification;
8 use Illuminate\Support\Arr;
9
10 class JPushNotificationChannel
11 {
12 public function send(User $notifiable, Notification $notification): void
13 {
14 // @phpstan-ignore-next-line
15 $message = $notification->toJPush($notifiable);
16
17 JPushMessageJob::dispatchSync(
18 Arr::get($message, 'user', []),
19 Arr::get($message, 'title', ''),
20 Arr::get($message, 'content', ''),
21 Arr::get($message, 'data', [])
22 );
23 }
24 }
1 <?php
2
3 namespace App\Channels;
4
5 use App\Jobs\SmsMessageJob;
6 use App\Models\User;
7 use Arr;
8 use Illuminate\Notifications\Notification;
9
10 class SmsNotificationChannel
11 {
12 public function send(User $notifiable, Notification $notification): void
13 {
14 // @phpstan-ignore-next-line
15 $message = $notification->toSms($notifiable);
16 $phone = Arr::get($message, 'phone');
17 $data = Arr::get($message, 'data', []);
18
19 SmsMessageJob::dispatchSync($phone, $data);
20 }
21 }
1 <?php
2
3 namespace App\Channels;
4
5 use App\Jobs\WechatTemplateMessageJob;
6 use App\Models\User;
7 use Illuminate\Notifications\Notification;
8 use Illuminate\Support\Arr;
9
10 class WechatNotificationChannel
11 {
12 public function send(User $notifiable, Notification $notification): void
13 {
14 // @phpstan-ignore-next-line
15 $message = $notification->toWechat($notifiable);
16
17 WechatTemplateMessageJob::dispatchSync(
18 openId: Arr::get($message, 'openId', ''),
19 template: Arr::get($message, 'template', ''),
20 data: Arr::get($message, 'data', []),
21 url: Arr::get($message, 'url', ''),
22 page: Arr::get($message, 'page', '')
23 );
24 }
25 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Enums\ActivityStatusEnum;
6 use App\Jobs\ActivityMakeMediaJob;
7 use App\Models\Activity;
8 use Illuminate\Console\Command;
9
10 class ActivityMediaFixCommand extends Command
11 {
12 /**
13 * The name and signature of the console command.
14 *
15 * @var string
16 */
17 protected $signature = 'activity:Media';
18
19 /**
20 * The console command description.
21 *
22 * @var string
23 */
24 protected $description = '修复歌曲音频文件';
25
26 /**
27 * Execute the console command.
28 *
29 * @return int
30 */
31 public function handle()
32 {
33 $id = $this->ask('请输入修复歌曲ID?');
34 $status = $this->ask('请选择修复歌曲状态');
35
36 ActivityMakeMediaJob::dispatchSync(Activity::find($id), ActivityStatusEnum::tryFrom($status), 1);
37
38 return self::SUCCESS;
39 }
40 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Enums\ActivityAuditStatusEnum;
6 use App\Enums\ActivityStatusEnum;
7 use App\Models\Activity;
8 use DB;
9 use Illuminate\Console\Command;
10
11 class ActivityStatusRecordCommand extends Command
12 {
13 /**
14 * The name and signature of the console command.
15 *
16 * @var string
17 */
18 protected $signature = 'activity:status-record';
19
20 /**
21 * The console command description.
22 *
23 * @var string
24 */
25 protected $description = '记录活动状态节点';
26
27 /**
28 * Execute the console command.
29 *
30 * @return int
31 */
32 public function handle(): int
33 {
34 DB::table('activity_status_records')->updateOrInsert(
35 ['time' => today()->toDateString()],
36 [
37 'up' => Activity::query()->where('activitys.audit_status', ActivityAuditStatusEnum::SUCCESS)->where('activitys.status', ActivityStatusEnum::UP)->count(),
38 'down' => Activity::query()->where('activitys.audit_status', ActivityAuditStatusEnum::SUCCESS)->where('activitys.status', ActivityStatusEnum::DOWN)->count(),
39 'match' => Activity::query()->where('activitys.audit_status', ActivityAuditStatusEnum::SUCCESS)->where('activitys.status', ActivityStatusEnum::MATCH)->count(),
40 'send' => Activity::query()->where('activitys.audit_status', ActivityAuditStatusEnum::SUCCESS)->where('activitys.status', ActivityStatusEnum::SEND)->count(),
41 ]
42 );
43
44 return self::SUCCESS;
45 }
46 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Enums\BrokerPushLevelRecordStatusEnum;
6 use App\Jobs\BrokerPushLevelRecordSendJob;
7 use App\Models\BrokerPushLevelRecord;
8 use App\Models\BrokerUserConfig;
9 use App\Models\BrokerUserConfigItem;
10 use DB;
11 use Illuminate\Console\Command;
12
13 class BrokerUserConfigCommand extends Command
14 {
15 /**
16 * The name and signature of the console command.
17 *
18 * @var string
19 */
20 protected $signature = 'broker:user-config {method}';
21
22 /**
23 * The console command description.
24 *
25 * @var string
26 */
27 protected $description = '经纪人运营-用户配置';
28
29 /**
30 * Execute the console command.
31 *
32 * @return int
33 */
34 public function handle(): int
35 {
36 $method = $this->argument('method');
37
38 switch ($method) {
39 case 'singer_num':
40 BrokerUserConfigItem::query()
41 ->leftJoin('users', 'users.id', 'broker_user_config_items.user_id')
42 ->where('config_id', BrokerUserConfig::query()->whereDate('begin_at', now()->toDateString())->value('id'))
43 ->where('broker_user_config_items.status', 1)
44 ->where('broker_user_config_items.is_sync', 0)
45 ->update([
46 'users.business_singer_limit' => DB::raw('CASE WHEN broker_user_config_items.singer_num = "" THEN users.business_singer_limit ELSE broker_user_config_items.singer_num END'),
47 'broker_user_config_items.is_sync' => 1
48 ]);
49 break;
50 case 'level_message':
51 BrokerPushLevelRecord::query()
52 ->where('status', BrokerPushLevelRecordStatusEnum::WAITING)
53 ->whereBetween('publish_at', [now()->subHour()->toDateTimeString(), now()->toDateTimeString()])
54 ->eachById(fn(BrokerPushLevelRecord $record) => BrokerPushLevelRecordSendJob::dispatchSync($record));
55 break;
56 }
57
58 return self::SUCCESS;
59 }
60 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Helpers\IMHelper;
6 use App\Http\Service\UserService;
7 use App\Models\GroupHasMember;
8 use App\Models\SystemConfig;
9 use Illuminate\Console\Command;
10
11
12 class GroupOut extends Command
13 {
14 /**
15 * The name and signature of the console command.
16 *
17 * @var string
18 */
19 protected $signature = 'group:out';
20
21 /**
22 * The console command description.
23 *
24 * @var string
25 */
26 protected $description = '自动退出团队';
27
28 /**
29 * Execute the console command.
30 *
31 * @return int
32 */
33 public function handle(): int
34 {
35 GroupHasMember::with(['group:id,user_id', 'member:id,business_id'])->where('status', 2)
36 ->where('updated_at', '<=', now()->subDays($this->getLimitDay()))
37 ->eachById(function (GroupHasMember $member) {
38 $response = IMHelper::sendSingleChatMessage($member->getAttribute('member_id'), $member->getRelation('group')?->getAttribute('user_id') ?? 0, [
39 "MsgBody" => IMHelper::createOnlineMessage('TIMCustomElem', [
40 "Data" => ['businessID' => 'invite_tip', 'content' => '', 'text' => '自动退出团队', 'tipStatus' => 8, 'title' => '', 'version' => 4],
41 "Desc" => "notification", "Ext" => "url"
42 ]),
43 "OfflinePushInfo" => IMHelper::createOfflineMessage('', '自动退出团队')
44 ]);
45
46 UserService::deleteGroupMember($member->getRelation('member'));
47 info('自动退出团队', $response->json());
48 });
49
50 return self::SUCCESS;
51 }
52
53
54 public function getLimitDay()
55 {
56 return SystemConfig::query()->where('identifier', 'F_bk65qzuTXvyemFAl4DR')->value('content') ?? 7;
57 }
58
59 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use Illuminate\Console\Command;
6 use Illuminate\Support\Facades\DB;
7
8 class HttpLogClearCommand extends Command
9 {
10 /**
11 * The name and signature of the console command.
12 *
13 * @var string
14 */
15 protected $signature = 'http-log:clear';
16
17 /**
18 * The console command description.
19 *
20 * @var string
21 */
22 protected $description = '清理请求系统日志';
23
24 /**
25 * Execute the console command.
26 *
27 * @return int
28 */
29 public function handle(): int
30 {
31 $this->line('clear start ...');
32 do {
33 $deleted = DB::table('system_http_logs')->where('created_at', '<', now()->subMonth()->toDateTimeString())->take(1000)->delete();
34 } while ($deleted !== 0);
35 $this->line('clear end ...');
36
37 return self::SUCCESS;
38 }
39 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Helpers\IMHelper;
6 use App\Models\User;
7 use Illuminate\Console\Command;
8 use Illuminate\Database\Eloquent\Collection;
9 use Illuminate\Support\Arr;
10
11 class ImFixCommand extends Command
12 {
13 /**
14 * The name and signature of the console command.
15 *
16 * @var string
17 */
18 protected $signature = 'im:fix';
19
20 /**
21 * The console command description.
22 *
23 * @var string
24 */
25 protected $description = 'IM 用户同步';
26
27 /**
28 * Execute the console command.
29 *
30 * @return int
31 * @throws \Exception
32 */
33 public function handle(): int
34 {
35
36 User::with(['business:id,nick_name,avatar', 'artTags:id,name'])->select(['id', 'nick_name', 'avatar', 'business_id', 'chat_mode', 'identity'])
37 ->where('audit_status', 1)->chunkById(100, function (Collection $collection) {
38 $result = IMHelper::checkAccount($collection->modelKeys())->json('ResultItem', []);
39 $checkUser = Arr::pluck($result, 'AccountStatus', 'UserID');
40
41 $collection->each(function (User $user) use ($checkUser) {
42 if (isset($checkUser[IMHelper::userKeyToAccount($user->getKey())]) && $checkUser[IMHelper::userKeyToAccount($user->getKey())] === 'NotImported') {
43 $data = IMHelper::importSingleAccount($user->getKey(), [
44 'chat_mode' => $user->getAttribute('chat_mode'),
45 'nickName' => $user->getAttribute('nick_name'),
46 'business' => $user->getAttribute('business'),
47 'artTag' => $user->getAttribute('artTags'),
48 'identity' => $user->getAttribute('identity')
49 ], $user->getAttribute('avatar') ?? '');
50 $this->info($user->getKey() . ':' . json_encode($data->json(), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
51 }
52 });
53 });
54
55
56 return self::SUCCESS;
57 }
58 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Enums\NotificationStatusEnum;
6 use App\Jobs\SystemNotificationSendJob;
7 use App\Models\Notification;
8 use Illuminate\Console\Command;
9
10 class NotifyMonitorCommand extends Command
11 {
12 /**
13 * The name and signature of the console command.
14 *
15 * @var string
16 */
17 protected $signature = 'notify:monitor';
18
19 /**
20 * The console command description.
21 *
22 * @var string
23 */
24 protected $description = '监控系统推送消息';
25
26 /**
27 * Execute the console command.
28 *
29 * @return int
30 */
31 public function handle(): int
32 {
33 Notification::query()
34 ->where('status', NotificationStatusEnum::WAITING)
35 ->where('publish_type', 2)
36 ->where('publish_at', '<=', now()->toDateTimeString())
37 ->eachById(fn(Notification $notification) => SystemNotificationSendJob::dispatchSync($notification));
38
39 return self::SUCCESS;
40 }
41 }
1 <?php
2
3
4 namespace App\Console\Commands;
5
6
7 use App\Http\Service\ImSign;
8 use App\Http\Service\ImSendService;
9 use App\Models\Pivots\ProjectMemberPivot;
10 use App\Models\Pivots\UserProjectPivot;
11 use App\Models\Project;
12 use App\Models\SystemConfig;
13 use Illuminate\Console\Command;
14 use Illuminate\Support\Facades\Auth;
15
16 class ProjectOut extends Command
17 {
18 protected $signature = 'project:out';
19
20 /**
21 * The console command description.
22 *
23 * @var string
24 */
25 protected $description = '自动退出厂牌';
26
27 /**
28 * Execute the console command.
29 *
30 * @return int
31 */
32 public function handle(): int
33 {
34 $members = ProjectMemberPivot::query()->where('status', 3)->where('updated_at', '<=', now()->subDays($this->getLimitDay()))->get();
35 foreach ($members as $member){
36 $MsgBody = [
37 [
38 "MsgType" => "TIMCustomElem",
39 "MsgContent" => [
40 "Data" => "{\"businessID\": \"system_notice\",\"text\": \"厂牌成员提醒\",\"title\": \"厂牌成员提醒\",\"content\": \"到期自动退出厂牌\",\"version\": 4}",
41 "Desc" => "notification",
42 "Ext" => "url",
43 "Sound" => "dingdong.aiff"
44 ]
45 ]
46 ];
47
48 $OfflinePushInfo = [
49 "PushFlag" => 0,
50 "Title" =>'厂牌提醒',
51 "Desc" => "到期自动退出厂牌",
52 "Ext" => "到期自动退出厂牌",
53 "AndroidInfo" => [
54 "Sound" => "android.mp3"
55 ],
56 "ApnsInfo" => [
57 "Sound" => "apns.mp3",
58 "BadgeMode" => 1,
59 "Image" => "www.image.com"
60 ]
61 ];
62 $sendData = ['MsgBody'=>$MsgBody,'OfflinePushInfo'=>$OfflinePushInfo];
63 $getSign = new ImSign();
64 $sendService = new ImSendService();
65 $sign = $getSign->genUserSig('administrator');
66 $sendService->batchSend($sign,[$member->user_id],"system",$sendData,2);
67 $member->delete();
68 }
69 return self::SUCCESS;
70 }
71
72
73 public function getLimitDay()
74 {
75 return SystemConfig::query()->where('identifier', 'i9V0wD_FitcGXR3HxUK5C')->value('content') ?? 7;
76 }
77
78 }
1 <?php
2
3 namespace App\Console\Commands;
4
5 use App\Helpers\ConfigHelper;
6 use App\Models\SystemConfig;
7 use App\Models\User;
8 use Illuminate\Console\Command;
9
10 class SyncBusinessDemoCommand extends Command
11 {
12 /**
13 * The name and signature of the console command.
14 *
15 * @var string
16 */
17 protected $signature = 'sync:businessDemo';
18
19 /**
20 * The console command description.
21 *
22 * @var string
23 */
24 protected $description = '同步经纪人Demo权限';
25
26 /**
27 * Execute the console command.
28 *
29 * @return int
30 */
31 public function handle(): int
32 {
33 SystemConfig::query()->where('identifier', ConfigHelper::DEMO_USER_KEY)->update([
34 'content' => User::query()->whereIn('identity', [2, 3])->pluck('id')->implode(',') ?? ''
35 ]);
36
37 return self::SUCCESS;
38 }
39 }
1 <?php
2
3 namespace App\Console;
4
5 use App\Console\Commands\ActivityStatusRecordCommand;
6 use App\Console\Commands\BrokerUserConfigCommand;
7 use App\Console\Commands\GroupOut;
8 use App\Console\Commands\HttpLogClearCommand;
9 use App\Console\Commands\NotifyMonitorCommand;
10 use App\Console\Commands\ProjectOut;
11 use App\Console\Commands\SyncBusinessDemoCommand;
12 use Illuminate\Console\Scheduling\Schedule;
13 use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
14 use Illuminate\Foundation\Inspiring;
15 use Illuminate\Support\Facades\Artisan;
16
17 /**
18 * @method comment(string $quote)
19 */
20 class Kernel extends ConsoleKernel
21 {
22 /**
23 * Define the application's command schedule.
24 *
25 * @param \Illuminate\Console\Scheduling\Schedule $schedule
26 * @return void
27 */
28 protected function schedule(Schedule $schedule): void
29 {
30 $schedule->command('telescope:prune --hours=336')->daily()->onOneServer();
31 $schedule->command('queue:prune-batches --hours=72')->daily()->onOneServer();
32 $schedule->command(SyncBusinessDemoCommand::class)->everyMinute()->onOneServer();
33 $schedule->command(HttpLogClearCommand::class)->withoutOverlapping(60)->lastDayOfMonth('03:00')->onOneServer();
34 $schedule->command(GroupOut::class)->everyOddHour()->onOneServer();
35 $schedule->command(ProjectOut::class)->everyOddHour()->onOneServer();
36 $schedule->command(ActivityStatusRecordCommand::class)->dailyAt('23:55')->onOneServer();
37 $schedule->command(NotifyMonitorCommand::class)->withoutOverlapping(5)->everyMinute()->onOneServer();
38 $schedule->command(BrokerUserConfigCommand::class, ['singer_num'])->dailyAt('01:00')->onOneServer();
39 $schedule->command(BrokerUserConfigCommand::class, ['level_message'])->withoutOverlapping(5)->everyMinute()->onOneServer();
40 }
41
42 /**
43 * Register the commands for the application.
44 *
45 * @return void
46 */
47 protected function commands(): void
48 {
49 $this->load(__DIR__ . '/Commands');
50
51 Artisan::command('inspire', function () {
52 $this->comment(Inspiring::quote());
53 })->purpose('Display an inspiring quote');
54 }
55 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityAuditStatusEnum: int
6 {
7 case PROCESS = 0;
8 case SUCCESS = 1;
9 case FAIL = 2;
10
11 public function label(): string
12 {
13 return match ($this) {
14 self::PROCESS => '处理中',
15 self::SUCCESS => '通过',
16 self::FAIL => '不通过',
17 };
18 }
19 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityCreateFormEnum: int
6 {
7 case PROJECT = 0;
8 case ADMIN = 1;
9 case USER = 2;
10 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivitySongTypeEnum: int
6 {
7 case SONG = 1;
8 case DEMO = 2;
9
10 /**
11 * @return string
12 */
13 public function label(): string
14 {
15 return match ($this) {
16 self::SONG => '歌曲',
17 self::DEMO => 'Demo',
18 default => '未知'
19 };
20 }
21 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityStatusEnum: int
6 {
7 case PROCESS = 0;
8 case UP = 1;
9 case DOWN = 2;
10 case MATCH = 3;
11 case FAIL = 4;
12 case SEND = 5;
13
14 /**
15 * @return string
16 */
17 public function label(): string
18 {
19 return match ($this) {
20 self::PROCESS => '处理中',
21 self::UP => '上架',
22 self::DOWN => '下架',
23 self::MATCH => '已匹配',
24 self::FAIL => '处理失败',
25 self::SEND => '已发行'
26 };
27 }
28 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityWorkModeEnum: int
6 {
7 case ONLINE = 0;
8 case OFFLINE = 1;
9
10 /**
11 * @return string
12 */
13 public function label(): string
14 {
15 return match ($this) {
16 self::ONLINE => '在线演唱',
17 self::OFFLINE => '自主上传'
18 };
19 }
20 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityWorkSingTypeEnum: string
6 {
7 case FULL = 'Full';
8 case PART = 'Part';
9
10 /**
11 * @return string
12 */
13 public function label(): string
14 {
15 return match ($this) {
16 self::FULL => '唱整首',
17 self::PART => '唱片段'
18 };
19 }
20 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityWorkStatusEnum: int
6 {
7 case WAIT = 0;
8 case SUCCESS = 1;
9 case REJECT = 2;
10
11 /**
12 * @return string
13 */
14 public function label(): string
15 {
16 return match ($this) {
17 self::WAIT => '待采纳',
18 self::SUCCESS => '已采纳',
19 self::REJECT => '不合适',
20 };
21 }
22
23 }
1 <?php
2
3 namespace App\Enums;
4
5 enum ActivityWorkTypeEnum: string
6 {
7 case SUBMIT = 'Submit';
8 case SAVE = 'Save';
9
10 /**
11 * @return string
12 */
13 public function label(): string
14 {
15 return match ($this) {
16 self::SUBMIT => '提交',
17 self::SAVE => '保存'
18 };
19 }
20 }
1 <?php
2
3 namespace App\Enums;
4
5 enum BannerStatusEnum: int
6 {
7 case DISABLE = 0;
8 case ACTIVATE = 1;
9
10 public function label(): string
11 {
12 return match ($this) {
13 self::DISABLE => '下架',
14 self::ACTIVATE => '上架',
15 };
16 }
17 }
1 <?php
2
3 namespace App\Enums;
4
5 enum BrokerPushLevelRecordStatusEnum: int
6 {
7 case FAIL = -1;
8 case WAITING = 0;
9 case PROCESSING = 1;
10 case SUCCESS = 2;
11 case ROLLBACK = 3;
12
13
14 public function label(): string
15 {
16 return match ($this) {
17 self::WAITING => '待发送',
18 self::PROCESSING => '发送中',
19 self::SUCCESS => '已发送',
20 self::FAIL => '发送失败',
21 self::ROLLBACK => '已撤销',
22 };
23 }
24 }
1 <?php
2
3 namespace App\Enums;
4
5 enum BrokerPushMatchRecordStatusEnum: int
6 {
7 case FAIL = -1;
8 case PROCESSING = 0;
9 case SUCCESS = 1;
10 case ROLLBACK = 2;
11
12
13 public function label(): string
14 {
15 return match ($this) {
16 self::PROCESSING => '发送中',
17 self::SUCCESS => '已发送',
18 self::FAIL => '发送失败',
19 self::ROLLBACK => '已撤销',
20 };
21 }
22 }
1 <?php
2
3 namespace App\Enums;
4
5 enum NotificationStatusEnum: int
6 {
7 case CANCEL = -2;
8 case FAIL = -1;
9 case WAITING = 0;
10 case PROCESSING = 1;
11 case SUCCESS = 2;
12 case ROLLBACK = 3;
13
14 public function label(): string
15 {
16 return match ($this) {
17 self::WAITING => '待发送',
18 self::PROCESSING => '发送中',
19 self::SUCCESS => '已发送',
20 self::FAIL => '发送失败',
21 self::CANCEL => '发送取消',
22 self::ROLLBACK => '已撤销',
23 };
24 }
25 }
1 <?php
2
3 namespace App\Enums;
4
5 enum RoleStatusEnum: int
6 {
7 case DISABLE = 0;
8 case ACTIVATE = 1;
9
10 public function label(): string
11 {
12 return match ($this) {
13 self::DISABLE => '禁用',
14 self::ACTIVATE => '启用',
15 };
16 }
17 }
1 <?php
2
3 namespace App\Enums;
4
5 enum UserOfficialStatusEnum: int
6 {
7 case SUBSCRIBE = 1;
8 case UN_SUBSCRIBE = 0;
9
10 public function label(): string
11 {
12 return match ($this) {
13 self::SUBSCRIBE => '已关注',
14 self::UN_SUBSCRIBE => '未关注',
15 };
16 }
17 }
1 <?php
2
3 namespace App\Enums;
4
5 enum UserScopeEnum: int
6 {
7 case UNSET = 0;
8 case SYSTEM = 1;
9 case PROJECT = 2;
10
11 public function label(): string
12 {
13 return match ($this) {
14 self::UNSET => '无',
15 self::SYSTEM => '平台管理员',
16 self::PROJECT => '厂牌管理员'
17 };
18 }
19 }
1 <?php
2
3 namespace App\Enums;
4
5 enum UserSexEnum: int
6 {
7 case UNSET = 0;
8 case MALE = 1;
9 case FEMALE = 2;
10
11 public function label(): string
12 {
13 return match ($this) {
14 self::UNSET => '无',
15 self::MALE => '男',
16 self::FEMALE => '女'
17 };
18 }
19 }
1 <?php
2
3 namespace App\Enums;
4
5 enum UserStatusEnum: int
6 {
7 case DISABLE = 0;
8 case ACTIVATE = 1;
9 case UNSET = 2;
10
11 public function label(): string
12 {
13 return match ($this) {
14 self::DISABLE => '禁用',
15 self::ACTIVATE => '启用',
16 self::UNSET => '注销'
17 };
18 }
19 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Support\ExcelExport;
6
7 class ActivityDemoExcel extends ExcelExport
8 {
9 protected function beforeHandle(): void
10 {
11 $this->builder->with(['project:id,name', 'user:id,real_name,nick_name,identity', 'tags:id,name', 'links:id,nick_name,real_name']);
12 $this->builder->withCount(['viewUsers', 'collectionUsers']);
13 $this->setHeader(['歌曲名称', '曲风标签', '关联厂牌', '词作者', '曲作者', '试听人数', '收藏人数', '创建人艺名', '创建人真名', '创建时间', '通过时间 ', '状态']);
14 $this->setColumns(['A:G' => 18, 'H:M' => 20]);
15 }
16
17 protected function setData($item): array
18 {
19 $lyricistIds = data_get($item, 'expand.lyricist.ids', []);
20 $lyricistNames = data_get($item, 'expand.lyricist.supplement', []);
21 $composerIds = data_get($item, 'expand.composer.ids', []);
22 $composerNames = data_get($item, 'expand.composer.supplement', []);
23
24 return [
25 $item->song_name,
26 $item->getRelation('tags')?->implode('name', ',') ?? '',
27 $item->getRelation('project')?->getAttribute('name') ?? '无关联厂牌',
28 $item->getRelation('links')->whereIn('id', $lyricistIds)->pluck('nick_name')?->unique()?->merge($lyricistNames)?->implode(','),
29 $item->getRelation('links')->whereIn('id', $composerIds)->pluck('nick_name')?->unique()?->merge($composerNames)?->implode(','),
30 $item->view_users_count ?? 0,
31 $item->collection_users_count ?? 0,
32 $item->getRelation('user')?->getAttribute('nick_name'),
33 $item->getRelation('user')?->getAttribute('real_name'),
34 (string)$item->created_at,
35 (string)$item->audit_at,
36 $item->status?->label(),
37 ];
38 }
39 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Models\SystemConfig;
6 use App\Support\ExcelExport;
7 use Illuminate\Database\Eloquent\Builder;
8 use Illuminate\Support\Collection;
9
10 class ActivityExcel extends ExcelExport
11 {
12 protected Collection $config;
13
14 protected function beforeHandle(): void
15 {
16 $this->builder->with(['project:id,name', 'user:id,real_name,nick_name,identity', 'tags:id,name', 'submitUsers:id,nick_name,real_name', 'links:id,nick_name,real_name']);
17 $this->builder->withCount(['viewUsers', 'collectionUsers', 'submitUsers']);
18 $this->setHeader(['歌曲名称', '推荐描述', '曲风标签', '歌曲语种', '歌曲速度', '关联厂牌', '词作者', '曲作者', '编曲', '性别要求', '预期发行日期', '试听人数', '收藏人数', '提交人数', '提交用户', '创建人艺名', '创建人真名', '创建时间', '通过时间 ', '确认时间', '匹配时间', '状态']);
19 $this->setColumns(['A:I' => 18, 'J:U' => 20]);
20
21 $this->config = SystemConfig::query()
22 ->whereHas('parent', fn(Builder $builder) => $builder->whereIn('identifier', ['activity_lang', 'activity_speed', 'activity_sex']))
23 ->pluck('name', 'identifier');
24 }
25
26 protected function setData($item): array
27 {
28
29 $lyricistIds = data_get($item, 'expand.lyricist.ids', []);
30 $lyricistNames = data_get($item, 'expand.lyricist.supplement', []);
31 $composerIds = data_get($item, 'expand.composer.ids', []);
32 $composerNames = data_get($item, 'expand.composer.supplement', []);
33 $arrangerIds = data_get($item, 'expand.arranger.ids', []);
34 $arrangerNames = data_get($item, 'expand.arranger.supplement', []);
35 $lang = data_get($item, 'lang', []);
36
37 return [
38 $item->song_name,
39 $item->sub_title,
40 $item->getRelation('tags')?->implode('name', ',') ?? '',
41 $this->config->only($lang)?->implode(','),
42 $this->config->get($item->speed),
43 $item->getRelation('project')?->getAttribute('name') ?? '无关联厂牌',
44 $item->getRelation('links')->whereIn('id', $lyricistIds)->pluck('nick_name')?->unique()?->merge($lyricistNames)?->implode(','),
45 $item->getRelation('links')->whereIn('id', $composerIds)->pluck('nick_name')?->unique()?->merge($composerNames)?->implode(','),
46 $item->getRelation('links')->whereIn('id', $arrangerIds)->pluck('nick_name')?->unique()?->merge($arrangerNames)?->implode(','),
47 $this->config->get($item->sex),
48 $item->estimate_release_at,
49 $item->view_users_count ?? 0,
50 $item->collection_users_count ?? 0,
51 $item->submit_users_count ?? 0,
52 $item->getRelation('submitUsers')?->implode('nick_name', ','),
53 $item->getRelation('user')?->getAttribute('nick_name'),
54 $item->getRelation('user')?->getAttribute('real_name'),
55 (string)$item->created_at,
56 (string)$item->audit_at,
57 (string)$item->match_at,
58 $item->match_day . ' 天',
59 $item->status?->label(),
60 ];
61 }
62 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Support\ExcelExport;
6
7 class ActivityReListRecordExcel extends ExcelExport
8 {
9 protected function beforeHandle(): void
10 {
11 $this->builder->with(['activity:id,song_name', 'project:id,name', 'user:id,nick_name', 'broker:id,nick_name', 'operator:id,nick_name']);
12 $this->setHeader(['歌曲ID', '歌曲名', '歌曲厂牌', '原合作用户ID', '原合作用户', '经纪人', '经纪人分类', '操作人', '操作时间'], 'A1:I1');
13 $this->setColumns(['A:I' => 28]);
14 }
15
16 protected function setData($item): array
17 {
18 return [
19 $item->activity?->id,
20 $item->activity?->song_name,
21 $item->project?->name,
22 $item->user?->id,
23 $item->user?->nick_name,
24 $item->broker?->nick_name,
25 $item->broker_level ?? '无',
26 $item->operator?->nick_name,
27 (string)$item->created_at
28 ];
29 }
30 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Models\SystemConfig;
6 use App\Support\ExcelExport;
7 use Illuminate\Database\Eloquent\Builder;
8 use Illuminate\Support\Collection;
9
10 class ActivitySongExcel extends ExcelExport
11 {
12 protected Collection $config;
13
14 protected function beforeHandle(): void
15 {
16 $this->builder->with(['project:id,name', 'user:id,real_name,nick_name,identity', 'tags:id,name', 'submitUsers:id,nick_name,real_name', 'links:id,nick_name,real_name']);
17 $this->builder->withCount(['viewUsers', 'collectionUsers', 'submitUsers']);
18 $this->setHeader(['歌曲名称', '推荐描述', '曲风标签', '歌曲语种', '歌曲速度', '关联厂牌', '词作者', '曲作者', '编曲', '性别要求', '预期发行日期', '试听人数', '收藏人数', '提交人数', '提交用户', '创建人艺名', '创建人真名', '创建时间', '通过时间 ', '确认时间', '匹配时间', '状态']);
19 $this->setColumns(['A:I' => 18, 'J:U' => 20]);
20
21 $this->config = SystemConfig::query()
22 ->whereHas('parent', fn(Builder $builder) => $builder->whereIn('identifier', ['activity_lang', 'activity_speed', 'activity_sex']))
23 ->pluck('name', 'identifier');
24 }
25
26 protected function setData($item): array
27 {
28
29 $lyricistIds = data_get($item, 'expand.lyricist.ids', []);
30 $lyricistNames = data_get($item, 'expand.lyricist.supplement', []);
31 $composerIds = data_get($item, 'expand.composer.ids', []);
32 $composerNames = data_get($item, 'expand.composer.supplement', []);
33 $arrangerIds = data_get($item, 'expand.arranger.ids', []);
34 $arrangerNames = data_get($item, 'expand.arranger.supplement', []);
35 $lang = data_get($item, 'lang', []);
36
37 return [
38 $item->song_name,
39 $item->sub_title,
40 $item->getRelation('tags')?->implode('name', ',') ?? '',
41 $this->config->only($lang)?->implode(','),
42 $this->config->get($item->speed),
43 $item->getRelation('project')?->getAttribute('name') ?? '无关联厂牌',
44 $item->getRelation('links')->whereIn('id', $lyricistIds)->pluck('nick_name')?->unique()?->merge($lyricistNames)?->implode(','),
45 $item->getRelation('links')->whereIn('id', $composerIds)->pluck('nick_name')?->unique()?->merge($composerNames)?->implode(','),
46 $item->getRelation('links')->whereIn('id', $arrangerIds)->pluck('nick_name')?->unique()?->merge($arrangerNames)?->implode(','),
47 $this->config->get($item->sex),
48 $item->estimate_release_at,
49 $item->view_users_count ?? 0,
50 $item->collection_users_count ?? 0,
51 $item->submit_users_count ?? 0,
52 $item->getRelation('submitUsers')?->implode('nick_name', ','),
53 $item->getRelation('user')?->getAttribute('nick_name'),
54 $item->getRelation('user')?->getAttribute('real_name'),
55 (string)$item->created_at,
56 (string)$item->audit_at,
57 (string)$item->match_at,
58 $item->match_day . ' 天',
59 $item->status?->label(),
60 ];
61 }
62 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Enums\ActivityWorkStatusEnum;
6 use App\Support\ExcelExport;
7
8 class ActivityWorkExcel extends ExcelExport
9 {
10 protected function beforeHandle(): void
11 {
12 $this->builder->with(['user:id,nick_name,real_name,email,company,rate', 'business:id,nick_name,real_name', 'share:id,nick_name,real_name']);
13 $this->setHeader(['艺名', '真名', '邮箱', '经济公司', '分成模式', '关联队长', '关联分享人', '参与方式', '类型', '状态', '保存/提交时间', 'Demo地址']);
14 $this->setColumns(['A:K' => 18, 'L:L' => 100]);
15 }
16
17 protected function setData($item): array
18 {
19 return [
20 $item->getRelation('user')?->getAttribute('nick_name'),
21 $item->getRelation('user')?->getAttribute('real_name'),
22 $item->getRelation('user')?->getAttribute('email'),
23 $item->getRelation('user')?->getAttribute('company'),
24 $item->getRelation('user')?->getAttribute('rate'),
25 $item->getRelation('business')?->getAttribute('nick_name'),
26 $item->getRelation('share')?->getAttribute('nick_name'),
27 $item->getAttribute('mode')?->label(),
28 $item->getAttribute('type')?->label(),
29 $item->getAttribute('status')?->label(),
30 $item->getAttribute('status') === ActivityWorkStatusEnum::SUCCESS ? (string)$item->getAttribute('submit_at') : (string)$item->getAttribute('created_at'),
31 $item->getAttribute('demo_url'),
32 ];
33 }
34 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Enums\BrokerPushMatchRecordStatusEnum;
6 use App\Support\ExcelExport;
7
8 class BrokerPushMatchRecordExcel extends ExcelExport
9 {
10 protected function beforeHandle(): void
11 {
12 $this->builder->with(['activity:id,song_name', 'project:id,name', 'user:id,nick_name', 'broker:id,nick_name']);
13 $this->setHeader(['经纪人ID', '经纪人', '分类', '试唱用户ID', '试唱用户', '试唱用户认证', '歌曲厂牌', '关联歌曲ID', '关联歌曲', '通知内容', '发送时间', '发送状态', '用户行为'], 'A1:M1');
14 $this->setColumns(['A:E' => 20, 'F:K' => 32, 'L:M' => 20]);
15 }
16
17 protected function setData($item): array
18 {
19 return [
20 $item->broker?->id,
21 $item->broker?->nick_name,
22 $item->broker_level,
23 $item->user?->id,
24 $item->user?->nick_name,
25 $item->user_tag,
26 $item->project?->name,
27 $item->activity?->id,
28 $item->activity?->song_name,
29 $item->content,
30 $item->send_at,
31 BrokerPushMatchRecordStatusEnum::tryFrom($item->status)?->label(),
32 $this->formatReadStatus($item->read_at)
33 ];
34 }
35
36 /**
37 * @param $status
38 * @return string
39 */
40 protected function formatReadStatus($status): string
41 {
42 return is_null($status) ? '未查看' : '已查看';
43 }
44 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Enums\UserSexEnum;
6 use App\Support\ExcelExport;
7 use Illuminate\Support\Arr;
8
9 class BusinessExcel extends ExcelExport
10 {
11 protected function beforeHandle(): void
12 {
13 $this->builder->addSelect(['company'])->with(['styleTags:id,name', 'authTags:id,name']);
14 $this->setHeader([
15 '用户艺名', '用户真名', '性别', '用户邮箱', '手机号码', '注册时间', '认证通过时间', '用户权限',
16 '音乐人能力', '是否关注服务号', '公司', '常居地', '擅长的曲风', '状态'
17 ]);
18 $this->setColumns(['A:N' => 20]);
19 }
20
21 protected function setData($item): array
22 {
23 return Arr::map([
24 $item->getAttribute('nick_name'),
25 $item->getAttribute('real_name'),
26 UserSexEnum::tryFrom($item->getAttribute('sex'))?->label(),
27 $item->getAttribute('email'),
28 sprintf('(%s)%s', $item->getFullPhone()->getPrefixedIDDCode('+'), $item->getFullPhone()->getNumber()),
29 (string)$item->getAttribute('created_at'),
30 (string)$item->getAttribute('audit_at'),
31 $item->getAttribute('scope')?->label(),
32 $item->getRelation('authTags')?->implode('name', '|'),
33 $item->getAttribute('official_status')?->label(),
34 $item->getAttribute('company'),
35 $item->getFullAddress(),
36 $item->getRelation('styleTags')?->implode('name', ','),
37 $item->getAttribute('status')?->label(),
38 ], static fn($value) => empty($value) ? '无' : $value);
39 }
40 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Support\ExcelExport;
6
7 class ProjectExcel extends ExcelExport
8 {
9 protected function beforeHandle(): void
10 {
11 $this->builder->with('master:id,nick_name');
12 $this->setHeader(["厂牌名称", '主理人', "厂牌管理员人数", "厂牌成员人数", "上架歌曲数", "已匹配歌曲数", '已发行歌曲数']);
13 $this->setColumns(['A:B' => 32, 'C:I' => 24]);
14 }
15
16 protected function setData($item): array
17 {
18 return [
19 $item->name,
20 $item->getRelation('master')?->getAttribute('nick_name'),
21 $item->manage_count ?? 0,
22 $item->member_count ?? 0,
23 $item->activity_up_count ?? 0,
24 $item->activity_match_count ?? 0,
25 $item->activity_send_count ?? 0,
26 ];
27 }
28 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Enums\UserSexEnum;
6 use App\Support\ExcelExport;
7 use Illuminate\Support\Arr;
8
9 class SingerExcel extends ExcelExport
10 {
11 protected function beforeHandle(): void
12 {
13 $this->builder->addSelect(['company'])->with(['styleTags:id,name', 'authTags:id,name']);
14 $this->setHeader([
15 '用户艺名', '用户真名', '性别', '用户邮箱', '手机号码', '注册时间', '认证通过时间',
16 '队长艺名', '队长真名', '用户权限', '音乐人能力', '是否关注服务号', '公司', '常居地', '擅长的曲风', '状态'
17 ]);
18 $this->setColumns(['A:P' => 20]);
19 }
20
21 protected function setData($item): array
22 {
23 return Arr::map([
24 $item->getAttribute('nick_name'),
25 $item->getAttribute('real_name'),
26 UserSexEnum::tryFrom($item->getAttribute('sex'))?->label(),
27 $item->getAttribute('email'),
28 sprintf('(%s)%s', $item->getFullPhone()->getPrefixedIDDCode('+'), $item->getFullPhone()->getNumber()),
29 (string)$item->getAttribute('created_at'),
30 (string)$item->getAttribute('audit_at'),
31 $item->getRelation('business')?->getAttribute('nick_name'),
32 $item->getRelation('business')?->getAttribute('real_name'),
33 $item->getAttribute('scope')?->label(),
34 $item->getRelation('authTags')?->implode('name', '|'),
35 $item->getAttribute('official_status')?->label(),
36 $item->getAttribute('company'),
37 $item->getFullAddress(),
38 $item->getRelation('styleTags')?->implode('name', ','),
39 $item->getAttribute('status')?->label(),
40 ], static fn($value) => empty($value) ? '无' : $value);
41 }
42 }
1 <?php
2
3 namespace App\Excel;
4
5 use App\Enums\UserSexEnum;
6 use App\Support\ExcelExport;
7 use Illuminate\Support\Arr;
8
9 class UserRegisterExcel extends ExcelExport
10 {
11 protected function beforeHandle(): void
12 {
13 $this->builder->addSelect(['company'])->with(['styleTags:id,name']);
14 $this->setHeader(['用户艺名', '用户真名', '性别', '用户邮箱', '手机号码', '注册时间', '用户权限', '是否关注公众号', '公司', '常居地', '擅长的曲风', '状态']);
15 $this->setColumns(['A:L' => 20]);
16 }
17
18
19 protected function setData($item): array
20 {
21 return Arr::map([
22 $item->getAttribute('nick_name'),
23 $item->getAttribute('real_name'),
24 UserSexEnum::tryFrom($item->getAttribute('sex'))?->label(),
25 $item->getAttribute('email'),
26 sprintf('(%s)%s', $item->getFullPhone()->getPrefixedIDDCode('+'), $item->getFullPhone()->getNumber()),
27 (string)$item->getAttribute('created_at'),
28 $item->getAttribute('scope')?->label(),
29 $item->getAttribute('official_status')?->label(),
30 $item->getAttribute('company'),
31 $item->getFullAddress(),
32 $item->getRelation('styleTags')?->implode('name', ',') ?? '无',
33 $item->getAttribute('status')?->label(),
34 ], static fn($value) => empty($value) ? '无' : $value);
35 }
36 }
1 <?php
2
3 namespace App\Exceptions;
4
5 use App\Helpers\JsonResource;
6 use App\Support\BusinessCode;
7 use Hikoon\LaravelApi\Exceptions\ValidationException;
8 use Hikoon\LaravelApi\Support\ApiCode;
9 use Hikoon\LaravelApi\Support\ApiResponse;
10 use Hikoon\LaravelJwt\Exceptions\TokenExpiredException;
11 use Hikoon\LaravelJwt\Exceptions\TokenInvalidException;
12 use Illuminate\Auth\AuthenticationException;
13 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
14 use Illuminate\Http\Exceptions\ThrottleRequestsException;
15 use Illuminate\Http\JsonResponse;
16 use Illuminate\Support\Arr;
17 use Symfony\Component\HttpFoundation\Response;
18 use Symfony\Component\HttpKernel\Exception\HttpException;
19 use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
20 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21 use Throwable;
22
23 class Handler extends ExceptionHandler
24 {
25 /**
26 * A list of the exception types that are not reported.
27 *
28 * @var array<int, class-string<\Throwable>>
29 */
30 protected $dontReport = [
31 ValidationException::class,
32 TokenExpiredException::class,
33 UserStatusException::class
34 ];
35
36 /**
37 * Register the exception handling callbacks for the application.
38 *
39 * @return void
40 */
41 public function register(): void
42 {
43 $this->reportable(static function (Throwable $e) {
44
45 });
46 }
47
48 /**
49 * @param $request
50 * @param \Throwable $e
51 * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
52 * @throws \Hikoon\LaravelApi\Exceptions\ValidationException
53 * @throws \Throwable
54 */
55 public function render($request, Throwable $e): \Illuminate\Http\Response|JsonResponse|Response
56 {
57 if ($e instanceof \Illuminate\Validation\ValidationException) {
58 throw new ValidationException(ApiCode::VALIDATION_ERROR, $e->getMessage());
59 }
60
61 if ($e instanceof UserStatusException) {
62 throw new AuthenticationException('账号已被禁用/注销');
63 }
64
65 return parent::render($request, $e);
66 }
67
68 /**
69 * @param $request
70 * @param \Throwable $e
71 * @return \Illuminate\Http\JsonResponse
72 */
73 protected function prepareJsonResponse($request, Throwable $e): JsonResponse
74 {
75 if ($request->is('manage/*') || $request->is('admin/*') || $request->is('user/*')) {
76 return match (true) {
77 $e instanceof \Illuminate\Validation\ValidationException => ApiResponse::fail(ApiCode::VALIDATION_ERROR, $e->getMessage()->errors()->first()),
78 $e instanceof TokenExpiredException, $e instanceof TokenInvalidException => ApiResponse::fail(BusinessCode::AUTHORIZED_EXPIRED_ERROR),
79 $e instanceof HttpException && $e->getStatusCode() === 503 => ApiResponse::fail(ApiCode::SERVICE_UNAVAILABLE_ERROR),
80 $e instanceof NotFoundHttpException => ApiResponse::fail(ApiCode::NOT_FOUND_ERROR),
81 $e instanceof MethodNotAllowedHttpException => ApiResponse::fail(ApiCode::METHOD_NOT_ALLOWED_ERROR),
82 $e instanceof ThrottleRequestsException => ApiResponse::fail(ApiCode::REQUEST_LIMIT_ERROR),
83 default => ApiResponse::make(Arr::except($this->convertExceptionToArray($e), 'message'))->toFail(ApiCode::SERVICE_ERROR)
84 };
85 }
86
87 if ($request->is('app/*')) {
88 return match (true) {
89 $e instanceof TokenExpiredException, $e instanceof TokenInvalidException => JsonResource::unauthenticated(JsonResource::AUTHORIZED_EXP),
90 $e instanceof MethodNotAllowedHttpException => JsonResource::fail('无效的请求方式'),
91 $e instanceof ThrottleRequestsException => JsonResource::fail('请求太频繁,请稍后再访问'),
92 $e instanceof NotFoundHttpException => JsonResource::fail('无法找到对应资源'),
93 default => JsonResource::error('服务器繁忙,请稍后再尝试...')
94 };
95 }
96
97
98 return parent::prepareJsonResponse($request, $e);
99 }
100
101 /**
102 * @inheritDoc
103 */
104 protected function unauthenticated($request, AuthenticationException $exception): Response
105 {
106 if ($request->acceptsJson()) {
107 if ($request->is('manage/*') || $request->is('admin/*') || $request->is('user/*')) {
108 return ApiResponse::fail(ApiCode::UNAUTHORIZED_ERROR, $exception->getMessage())->setStatusCode(401);
109 }
110 if ($request->is('app/*')) {
111 return JsonResource::unauthenticated($exception->getMessage() ?? JsonResource::UNAUTHORIZED);
112 }
113 }
114
115 return parent::unauthenticated($request, $exception);
116 }
117 }
1 <?php
2
3 namespace App\Exceptions;
4
5 use App\Models\User;
6 use Exception;
7 use Throwable;
8
9 class SingerLimitException extends Exception
10 {
11 public User $user;
12
13 public int $masterId;
14
15 public int $maxSingerNum;
16
17 public function __construct(User $user, int $masterId, int $maxSingerNum, string $message = "", int $code = 0, ?Throwable $previous = NULL)
18 {
19 $this->user = $user;
20 $this->masterId = $masterId;
21 $this->maxSingerNum = $maxSingerNum;
22 parent::__construct($message, $code, $previous);
23 }
24 }
1 <?php
2
3 namespace App\Exceptions;
4
5 use Exception;
6
7 class UserStatusException extends Exception
8 {
9 }
1 <?php
2
3 namespace App\Helpers;
4
5 use App\Models\Activity;
6 use App\Models\SystemConfig;
7 use Arr;
8 use Illuminate\Database\Eloquent\Collection as EloquentCollection;
9 use Illuminate\Support\Collection as SupportCollection;
10 use Str;
11
12 class ConfigHelper
13 {
14 public const DEMO_TAG_KEY = 'CtGly-jsLjAu5yJvLrN7L';
15
16 public const DEMO_USER_KEY = 'n3HCbruqL_DemfX9MHj-K';
17
18 public const LISTEN_USER_KEY = '-BIGBAR9rPOMwCrw4hIuQ';
19
20 public const SING_USER_KEY = 'Jy-zgPbcSXSGnvHIx8moq';
21
22 public const SINGER_TAG_KEY = 'auth_singer_tag';
23
24 public const BUSINESS_SINGER_LIMIT = 'svMC9TNFz2Acx8ccPdAIH';
25
26 public const INFO_SCORE_LIMIT_KEY = 'iymo-wZcV2F0UWFUKJR5m';
27
28 public const INFO_SCORE_TEXT_KEY = 'YmSNBXwMB6cUo-zRT26s-';
29
30 public const INFO_SCORE_CONTENT_KEY = 'TeZnnaB-Pfwyz8IADs0SM';
31
32
33 /**
34 * @param string $identifier
35 * @param string $column
36 * @return ?string
37 */
38 public static function getContent(string $identifier, string $column = 'content'): ?string
39 {
40 return SystemConfig::withoutGlobalScope('show')->where('status', 1)->where('identifier', $identifier)->value($column);
41 }
42
43 /**
44 * @param string|array $identifier
45 * @param string $column
46 * @return \Illuminate\Support\Collection
47 */
48 public static function getList(string|array $identifier, string $column = 'content'): SupportCollection
49 {
50 return SystemConfig::withoutGlobalScope('show')->where('status', 1)->whereIn('identifier', Arr::wrap($identifier))->pluck($column, 'identifier');
51 }
52
53 /**
54 * @param string|array $identifier
55 * @param string $column
56 * @return \Illuminate\Support\Collection
57 */
58 public static function getChildrenList(string|array $identifier, string $column = 'content'): SupportCollection
59 {
60 return SystemConfig::withoutGlobalScope('show')->parentKey($identifier)->where('status', 1)->pluck($column, 'identifier');
61 }
62
63 /**
64 * 获取歌手标签Ids
65 * @return array
66 */
67 public static function getSingerTagIds(): array
68 {
69 return array_filter(explode(',', self::getContent(self::SINGER_TAG_KEY)));
70 }
71
72 /**
73 * 获取Demo标签Ids
74 * @return array
75 */
76 public static function getDemoTagIds(): array
77 {
78 return array_filter(explode(',', self::getContent(self::DEMO_TAG_KEY)));
79 }
80
81 /**
82 * 获取Demo白名单用户Ids
83 * @return array
84 */
85 public static function getDemoUserIds(): array
86 {
87 return array_filter(explode(',', self::getContent(self::DEMO_USER_KEY)));
88 }
89
90 /**
91 * @return \Illuminate\Support\Collection
92 */
93 public static function getDemoList(): SupportCollection
94 {
95 return self::getList([self::DEMO_TAG_KEY, self::DEMO_USER_KEY])?->map(fn($item) => array_filter(explode(',', $item)));
96 }
97
98 /**
99 * 获取试听权限白名单用户Ids
100 * @return array
101 */
102 public static function getListenUserIds(): array
103 {
104 return array_filter(explode(',', self::getContent(self::LISTEN_USER_KEY)));
105 }
106
107 /**
108 * 获取试听&试唱权限白名单用户Ids
109 * @return array
110 */
111 public static function getSingUserIds(): array
112 {
113 return array_filter(explode(',', self::getContent(self::SING_USER_KEY)));
114 }
115
116 /**
117 * 获取试听与试唱白名单Ids
118 * @return \Illuminate\Support\Collection
119 */
120 public static function getListenAndSingUserList(): SupportCollection
121 {
122 return self::getList([self::LISTEN_USER_KEY, self::SING_USER_KEY])?->map(fn($item) => array_filter(explode(',', $item)));
123
124 }
125
126 /**
127 * @return int
128 */
129 public static function getProjectLimitDay(): int
130 {
131 return intval(self::getContent('i9V0wD_FitcGXR3HxUK5C')) ?? 7;
132 }
133
134 /**
135 * @return int
136 */
137 public static function getBusinessSingerLimit(): int
138 {
139 return intval(self::getContent(self::BUSINESS_SINGER_LIMIT)) ?? 3;
140 }
141
142 /**
143 * @return array
144 */
145 public static function getTelescopeIpList(): array
146 {
147 return array_filter(explode(',', self::getContent('telescope_ip')));
148 }
149
150 /**
151 * @param array|int $tagIds
152 * @param array|null $singerTagIds
153 * @return bool
154 */
155 public static function hasSingerTag(array|int $tagIds, array $singerTagIds = NULL): bool
156 {
157 return filled(array_intersect(Arr::wrap($tagIds), $singerTagIds ?? self::getSingerTagIds()));
158 }
159
160 /**
161 * @param array|int $tagIds
162 * @param array|NULL $demoTagIds
163 * @return bool
164 */
165 public static function hasDemoTag(array|int $tagIds, array $demoTagIds = NULL): bool
166 {
167 return filled(array_intersect(Arr::wrap($tagIds), $demoTagIds ?? self::getDemoTagIds()));
168 }
169
170 /**
171 * @return \Illuminate\Support\Collection
172 */
173 public static function getInfoScore(): SupportCollection
174 {
175 return self::getList([self::INFO_SCORE_LIMIT_KEY, self::INFO_SCORE_TEXT_KEY])->map(fn($item, $key) => match ($key) {
176 self::INFO_SCORE_LIMIT_KEY => (int)$item,
177 self::INFO_SCORE_TEXT_KEY => Str::isJson($item) ? json_decode($item, false, 512, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) : [],
178 default => $item,
179 })->put(self::INFO_SCORE_CONTENT_KEY, self::getChildrenList(self::INFO_SCORE_CONTENT_KEY));
180 }
181
182 /**
183 * @return \Illuminate\Database\Eloquent\Collection
184 */
185 public static function getActivityDictionary(): EloquentCollection
186 {
187 return SystemConfig::parentKey(['activity_mark', 'activity_lang', 'activity_speed', 'activity_sex'])->where('status', 1)->get(['name', 'identifier', 'content']);
188 }
189
190 /**
191 * @param \App\Models\Activity $activity
192 * @param \Illuminate\Database\Eloquent\Collection|null $dictionary
193 * @return \App\Models\Activity
194 */
195 public static function formatActivityDictionary(Activity $activity, EloquentCollection $dictionary = NULL): Activity
196 {
197 $dictionary = $dictionary ?? self::getActivityDictionary();
198 $activity->loadMissing('linkArranger:id,nick_name');
199 $activity->setAttribute('sex', $dictionary->where('identifier', $activity->getAttribute('sex'))->value('name', ''));
200 $activity->setAttribute('speed', $dictionary->where('identifier', $activity->getAttribute('speed'))->value('name', ''));
201 $activity->setAttribute('mark', $dictionary->where('identifier', $activity->getAttribute('mark'))->value('content', ''));
202 $activity->setAttribute('lang', $dictionary->whereIn('identifier', $activity->getAttribute('lang'))->pluck('name'));
203 $activity->setAttribute('arranger', ['in_side' => $activity->getAttribute('linkArranger'), 'out_side' => data_get($activity->getAttribute('expand'), 'arranger.supplement', [])]);
204
205 return $activity;
206 }
207
208 /**
209 * @param \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection $collection
210 * @return \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
211 */
212 public static function formatManyActivityDictionary(EloquentCollection|SupportCollection $collection): EloquentCollection|SupportCollection
213 {
214 $dictionary = self::getActivityDictionary();
215 return $collection->each(fn($item) => self::formatActivityDictionary($item, $dictionary));
216 }
217 }
1 <?php
2
3 namespace App\Helpers;
4
5 use Arr;
6 use GuzzleHttp\Promise\PromiseInterface;
7 use Illuminate\Http\Client\Response;
8 use Illuminate\Support\Facades\App;
9 use Illuminate\Support\Facades\Http;
10 use Illuminate\Support\Facades\Log;
11 use Str;
12 use Tencent\TLSSigAPIv2;
13
14 class IMHelper
15 {
16 private string $host;
17
18 private string $key;
19
20 private string $secret;
21
22 private string $identifier;
23
24 public const SYSTEM_USER = 'system';
25
26 public function __construct()
27 {
28 $this->host = config('services.im.domain');
29 $this->key = config('services.im.key');
30 $this->secret = config('services.im.secret');
31 $this->identifier = config('services.im.identifier');
32 }
33
34 /**
35 * @throws \Exception
36 */
37 private function createSignature(string $identifier): string
38 {
39 return (new TLSSigAPIv2($this->key, $this->secret))->genUserSig($identifier);
40 }
41
42 /**
43 * @throws \Exception
44 */
45 private function getSystemSignature(): string
46 {
47 return $this->createSignature($this->identifier);
48 }
49
50 /**
51 * @throws \Exception
52 */
53 public static function getUserSignature(int|string $userId): string
54 {
55 return (new static())->createSignature(self::userKeyToAccount($userId));
56 }
57
58 /**
59 * @throws \Exception
60 */
61 public function getUrl(string $uri): string
62 {
63 return $this->host . '/' . $uri . '?' . Arr::query(['sdkappid' => $this->key, 'identifier' => $this->identifier, 'usersig' => $this->getSystemSignature(), 'random' => random_int(0, 4294967295), 'contenttype' => 'json']);
64 }
65
66 /**
67 * @param string $uri
68 * @param array $data
69 * @return \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response
70 * @throws \Exception
71 */
72 public static function send(string $uri, array $data): PromiseInterface|Response
73 {
74 $host = (new static())->getUrl($uri);
75
76 $result = Http::asJson()->post($host, $data);
77
78
79 Log::channel('jpush')->info('IM ' . $uri . ' status[' . $result->json('ActionStatus') . ']:', [
80 'uri' => $uri, 'data' => $data, 'result' => $result->json()
81 ]);
82
83 return $result;
84 }
85
86 /**
87 * @param int|string $userId
88 * @return string
89 */
90 public static function userKeyToAccount(int|string $userId): string
91 {
92 return (App::isProduction() ? '' : 'test') . $userId;
93 }
94
95 /**
96 * @param string $account
97 * @return string
98 */
99 public static function accountToUserKey(string $account): string
100 {
101 return Str::replace('test', '', $account);
102 }
103
104 /**
105 * @param string $title
106 * @param string $desc
107 * @param string|array $ext
108 * @return array
109 * @throws \JsonException
110 */
111 public static function createOfflineMessage(string $title, string $desc = '', string|array $ext = ''): array
112 {
113 return [
114 "PushFlag" => 0,
115 "Title" => $title,
116 "Desc" => $desc,
117 "Ext" => is_array($ext) ? json_encode($ext, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) : $ext,
118 "AndroidInfo" => ["Sound" => "android.mp3"],
119 "ApnsInfo" => ["Sound" => "apns.mp3", "BadgeMode" => 1]
120 ];
121 }
122
123 /**
124 * @param string $type
125 * @param array $content
126 * @return array
127 */
128 public static function createOnlineMessage(string $type, array $content): array
129 {
130 return [
131 'MsgType' => $type,
132 'MsgContent' => [
133 ...Arr::map($content, static fn($item) => is_array($item) ? json_encode($item, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) : $item),
134 'Sound' => 'dingdong.aiff'
135 ]
136 ];
137 }
138
139 /**
140 * @param array $content
141 * @param array $ext
142 * @return array
143 */
144 public static function createCustomMessage(array $content, array $ext = []): array
145 {
146 return self::createOnlineMessage('TIMCustomElem', [...$content, "Ext" => $ext,]);
147 }
148
149 /**
150 * @param string $form
151 * @param string $to
152 * @param array $payload
153 * @return \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response
154 * @throws \Exception
155 */
156 public static function sendSingleChatMessage(string $form, string $to, array $payload): PromiseInterface|Response
157 {
158 return self::send('v4/openim/sendmsg', [
159 "SyncOtherMachine" => 2,
160 'IsNeedReadReceipt' => 1,
161 "MsgRandom" => random_int(0, 4294967295),
162 "From_Account" => $form,
163 "To_Account" => self::userKeyToAccount($to)
164 ] + $payload);
165 }
166
167 /**
168 * @param string $form
169 * @param array $to
170 * @param array $payload
171 * @return \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response
172 * @throws \Exception
173 */
174 public static function sendBatchChatMessage(string $form, array $to, array $payload): PromiseInterface|Response
175 {
176 return self::send('v4/openim/batchsendmsg', [
177 "SyncOtherMachine" => 2,
178 'IsNeedReadReceipt' => 1,
179 'MsgRandom' => random_int(0, 4294967295),
180 "From_Account" => is_numeric($form) ? self::userKeyToAccount($form) : $form,
181 "To_Account" => Arr::map($to, static fn($item) => self::userKeyToAccount($item)),
182 ] + $payload);
183 }
184
185 /**
186 * @param string $form
187 * @param string $to
188 * @param string $msgKey
189 * @return \GuzzleHttp\Promise\PromiseInterface|\Illuminate\Http\Client\Response
190 * @throws \Exception
191 */
192 public static function rollbackChatMessage(string $form, string $to, string $msgKey): PromiseInterface|Response
193 {
194 info('IM_ROLLBACK', [$form, $to, $msgKey]);
195 return self::send('v4/openim/admin_msgwithdraw', ["From_Account" => $form, "To_Account" => is_numeric($to) ? self::userKeyToAccount($to) : $to, "MsgKey" => $msgKey]);
196 }
197
198 /**
199 * @throws \Exception
200 */
201 public static function checkAccount(array $account): PromiseInterface|Response
202 {
203 return self::send('/v4/im_open_login_svc/account_check ', [
204 'CheckItem' => Arr::map($account, static fn($item) => ['UserID' => self::userKeyToAccount($item)])
205 ]);
206 }
207
208 /**
209 * @throws \JsonException
210 * @throws \Exception
211 */
212 public static function importSingleAccount(string $userId, string|array $name, string $avatar = ''): PromiseInterface|Response
213 {
214 return self::send('/v4/im_open_login_svc/account_import', [
215 'UserID' => self::userKeyToAccount($userId),
216 'Nick' => is_array($name) ? json_encode($name, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) : $name,
217 'FaceUrl' => $avatar
218 ]);
219 }
220
221 /**
222 * @throws \Exception
223 */
224 public static function updateSingleAccountInfo(string $userId, array $profileItem): PromiseInterface|Response
225 {
226 return self::send('v4/profile/portrait_set', ['From_Account' => self::userKeyToAccount($userId), 'ProfileItem' => $profileItem]);
227 }
228 }
1 <?php
2
3
4 namespace App\Helpers;
5
6 use Illuminate\Http\JsonResponse;
7 use Illuminate\Pagination\LengthAwarePaginator;
8 use Illuminate\Support\Arr;
9 use Illuminate\Support\Str;
10 use Symfony\Component\HttpFoundation\Response;
11
12 class JsonResource
13 {
14 public const SUCCESS = '获取成功';
15
16 public const CREATE_SUCCESS = '创建成功';
17
18 public const UPDATE_SUCCESS = '更新成功';
19
20 public const DELETE_SUCCESS = '删除成功';
21
22 public const LOGIN_SUCCESS = '登录成功';
23
24 public const LOGOUT_SUCCESS = '登出成功';
25
26 public const UNAUTHORIZED = '未携带身份令牌';
27
28 public const AUTHORIZED_EXP = '身份令牌失效';
29
30 public const NO_PERMISSION = '无权限进行此操作';
31
32 public const USER_FOUND_ERROR = '用户不存在';
33
34 public const LOGIN_FOUND_ERROR = '账号不存在';
35
36 public const LOGIN_FORBID_ERROR = '账户已被封禁,请联系管理员';
37
38 public const LOGIN_PASSWORD_ERROR = '密码错误';
39
40 public const REPEAT_SUBMIT = '请勿重复提交';
41
42 public const IS_TOP_LIMIT = '最多允许置顶3条内容';
43
44 public const INVITE_SUCCESS = '邀请成功';
45
46 public const APPLY_SUCCESS = '申请成功';
47
48 public const INVITE_LIMIT = '1天内仅能向他发起一次,请不要频繁邀请';
49
50 public const APPLY_LIMIT = '1天内仅能向他发起一次,请不要频繁申请';
51
52
53 public const TEAM_INVITE_SINGER_LIMIT = '团队内歌手名额已满,暂无法邀请 ~';
54
55 public const TEAM_JOIN_SINGER_LIMIT = '团队内歌手名额已满';
56
57 public const JOINED_TEAM = '对方已在其他团队';
58
59 public const MEMBER_JOINED_TEAM = 'Ta已加入他人团队,您暂无法同意 ~';
60 public const SELF_MEMBER_JOINED_TEAM = '你已加入他人团队,您暂无法同意 ~';
61
62 public const INVITE_JOINED_TEAM = 'Ta已加入他人团队,您暂无法加入 ~';
63 public const SELF_INVITE_JOINED_TEAM = '你已加入他人团队,您暂无法同意 ~';
64
65
66 public const JOIN_TEAM_INVALID = '超时,已失效';
67
68 public const INVALIDED = '已失效';
69
70
71 public const HANDLE_SUCCESS = '处理成功';
72
73 public const OPERATION_SUCCESS = '操作成功';
74
75 public const MEMBER_TOP_LIMIT = '最多可置顶5人';
76
77 public const MEMBER_OUT_INVALID = '退出申请已被处理';
78
79 public const PROJECT_INVALIDED = '厂牌已被禁用或删除';
80
81 public const HANDEL_FAIL = '操作失败,请刷新后再试';
82
83 public const IS_PROJECT_MEMBER = '操作失败,您已经是厂牌成员了';
84
85 public const IS_NOT_MASTER = '操作失败,对方已经不是该厂牌管理员';
86
87 public const YOU_NOT_MASTER = '操作失败,您已经不是该厂牌管理员';
88
89 public const OUT_IS_HANDLE = '操作失败,该申请已被其他管理员处理';
90
91
92 /**
93 * @param string|null $message
94 * @param int $code
95 * @return JsonResponse
96 */
97 public static function empty(string $message = NULL, int $code = Response::HTTP_OK): JsonResponse
98 {
99 return self::send('success', $code, $message ?? Response::$statusTexts[$code]);
100 }
101
102 /**
103 * @param string|null $message
104 * @param mixed $data
105 * @param int $code
106 * @param array $extend
107 * @return JsonResponse
108 */
109 public static function success(string $message = NULL, $data = NULL, int $code = Response::HTTP_OK, array $extend = []): JsonResponse
110 {
111 return self::send('success', $code, $message ?? Response::$statusTexts[$code], $data, $extend);
112 }
113
114 /**
115 * @param mixed $data
116 * @param string|null $message
117 * @param int $code
118 * @return JsonResponse
119 */
120 public static function fail(string $message = NULL, $data = NULL, int $code = Response::HTTP_UNPROCESSABLE_ENTITY): JsonResponse
121 {
122 return self::send('fail', $code, $message ?? Response::$statusTexts[$code], $data);
123
124 }
125
126 public static function unauthenticated(string $message = NULL, $data = NULL, int $code = 401): JsonResponse
127 {
128 return self::send('fail', $code, $message ?? Response::$statusTexts[$code], $data);
129
130 }
131
132
133 /**
134 * @param mixed $data
135 * @param string|null $message
136 * @param int $code
137 * @return JsonResponse
138 */
139 public static function error(string $message = NULL, $data = NULL, int $code = Response::HTTP_INTERNAL_SERVER_ERROR): JsonResponse
140 {
141 return self::send('error', $code, $message ?? Response::$statusTexts[$code], config('app.debug') && $code >= 500 ? $data : []);
142 }
143
144 /**
145 * @param LengthAwarePaginator|\Illuminate\Contracts\Pagination\LengthAwarePaginator $data
146 * @return array
147 */
148 public static function paginator(LengthAwarePaginator $data): array
149 {
150 return [
151 'data' => $data->items(), 'meta' => ['total' => (int)$data->total(), 'page' => (int)$data->currentPage(), 'size' => (int)$data->perPage()]
152 ];
153 }
154
155
156 /**
157 * @param string $status
158 * @param int $code
159 * @param string $message
160 * @param $data
161 * @param array $extend
162 * @return JsonResponse
163 */
164 public static function send(string $status, int $code, string $message, $data = NULL, array $extend = []): JsonResponse
165 {
166 $resource = array_merge(
167 ['status' => $status, 'code' => $code, 'message' => $message],
168 $data instanceof LengthAwarePaginator ? self::paginator($data) : compact('data'),
169 );
170
171 if (is_null($resource['data']) && Str::startsWith(request()?->getRequestUri(), '/mapi')) {
172 unset($resource['data']);
173 }
174
175 if (count($extend) !== 0) {
176 Arr::set($resource, 'meta', array_merge(Arr::get($resource, 'meta', []), $extend));
177 }
178
179 return response()->json($resource);
180 }
181 }
1 <?php
2
3 namespace App\Helpers;
4
5 use App\Models\SystemOperateLog;
6 use App\Models\User;
7 use Auth;
8 use Illuminate\Database\Eloquent\Model;
9 use Str;
10
11 class OperationLog
12 {
13 protected string $guard;
14
15 protected string $action = '';
16
17 protected string $content = '';
18
19 protected Model $subject;
20
21 protected int $causer;
22
23 protected array $append = [];
24
25
26 private function __construct(string $guard)
27 {
28 $this->guard = $guard;
29 $this->causer = request()?->user()?->getKey() ?? 0;
30 }
31
32 /**
33 * @return self
34 */
35 public static function admin(): self
36 {
37 return new self('Admin');
38 }
39
40 /**
41 * @return self
42 */
43 public static function manage(): self
44 {
45 return new self('Manage');
46 }
47
48 /**
49 * @return self
50 */
51 public static function guard(): self
52 {
53 return new self(Str::ucfirst(Auth::getDefaultDriver()));
54 }
55
56 /**
57 * @return $this
58 */
59 public function createAction(): OperationLog
60 {
61 $this->action = '新增操作';
62 return $this;
63 }
64
65 /**
66 * @return $this
67 */
68 public function updateAction(): OperationLog
69 {
70 $this->action = '内容调整';
71 return $this;
72 }
73
74 /**
75 * @return $this
76 */
77 public function statusAction(): OperationLog
78 {
79 $this->action = '状态变更';
80 return $this;
81 }
82
83 /**
84 * @return $this
85 */
86 public function deleteAction(): OperationLog
87 {
88 $this->action = '删除操作';
89 return $this;
90 }
91
92 /**
93 * @param string $action
94 * @return $this
95 */
96 public function action(string $action): OperationLog
97 {
98 $this->action = $action;
99 return $this;
100 }
101
102 /**
103 * @param \Illuminate\Database\Eloquent\Model $subject
104 * @return $this
105 */
106 public function subject(Model $subject): OperationLog
107 {
108 $this->subject = $subject;
109 return $this;
110 }
111
112 /**
113 * @param int|\App\Models\User $causer
114 * @return $this
115 */
116 public function causer(User|int $causer): OperationLog
117 {
118 $this->causer = $causer instanceof User ? $causer->getKey() : (int)$causer;
119 return $this;
120 }
121
122 /**
123 * @param array $append
124 * @return $this
125 */
126 public function append(array $append): OperationLog
127 {
128 $this->append = $append;
129 return $this;
130 }
131
132 /**
133 * @param string $format
134 * @param mixed ...$values
135 * @return SystemOperateLog
136 */
137 public function content(string $format, mixed ...$values): SystemOperateLog
138 {
139 $this->content = sprintf($format, ...$values);
140 return $this->save();
141 }
142
143 /**
144 * @return SystemOperateLog
145 */
146 private function save(): SystemOperateLog
147 {
148 if (!in_array($this->guard, ['Admin', 'Manage'], true)) {
149 return new SystemOperateLog([
150 'guard' => $this->guard,
151 'user_id' => $this->causer,
152 'action' => $this->action,
153 'content' => $this->content,
154 'subject_type' => $this->subject->getMorphClass(),
155 'subject_id' => $this->subject->getKey(),
156 ] + $this->append);
157 }
158
159 return SystemOperateLog::query()->create([
160 'guard' => $this->guard,
161 'user_id' => $this->causer,
162 'path' => explode(',', request()?->header('Route', '')),
163 'action' => $this->action,
164 'content' => $this->content,
165 'subject_type' => $this->subject->getMorphClass(),
166 'subject_id' => $this->subject->getKey(),
167 ] + $this->append);
168 }
169
170 }
1 <?php
2
3 namespace App\Helpers;
4
5 use Illuminate\Support\Str;
6
7 class PasswordHelper
8 {
9 /**
10 * 密码Hash生成
11 * @param string $password
12 * @return string
13 */
14 public static function make(string $password): string
15 {
16 $header = "pbkdf2_sha256";
17 $iterations = 260000;
18 $salt = Str::random(22);
19 $hash = base64_encode(hash_pbkdf2("SHA256", $password, $salt, $iterations, 0, true));
20 return implode('$', [$header, $iterations, $salt, $hash]);
21 }
22
23
24 /**
25 * 密码Hash验证
26 * @param string $password
27 * @param string $hash
28 * @return bool
29 */
30 public static function verify(string $password, string $hash): bool
31 {
32 if (empty($hash) || !Str::contains($hash, '$')) {
33 return false;
34 }
35
36 [$header, $iterations, $salt, $hash] = explode("$", $hash);
37 $verifyHash = base64_encode(hash_pbkdf2("SHA256", $password, $salt, (int)$iterations, 0, true));
38 return hash_equals($hash, $verifyHash);
39 }
40 }
1 <?php
2
3 namespace App\Helpers;
4
5 use Carbon\Carbon;
6 use Illuminate\Http\Client\Response;
7 use Illuminate\Support\Facades\Http;
8
9 class ServiceHelper
10 {
11 /**
12 * @return string
13 */
14 public static function getServiceHost(): string
15 {
16 return config('services.provider.service_host', '');
17 }
18
19 /**
20 * @return string
21 */
22 public static function getServiceSecret(): string
23 {
24 return config('services.provider.service_secret', '');
25 }
26
27 /**
28 * 推送微信服务号
29 * @param array<string,mixed> $data
30 * @return \Illuminate\Http\Client\Response
31 * @throws \JsonException
32 */
33 public static function sendWechatOfficial(array $data): Response
34 {
35 return self::send('WechatOfficialMessage', $data);
36 }
37
38 /**
39 * 推送微信小程序
40 * @param array<string,mixed> $data
41 * @return \Illuminate\Http\Client\Response
42 * @throws \JsonException
43 */
44 public static function sendWechatApp(array $data): Response
45 {
46 return self::send('WechatAppMessage', $data);
47 }
48
49 /**
50 * 推送极光消息
51 * @param array<string,mixed> $data
52 * @return \Illuminate\Http\Client\Response
53 * @throws \JsonException
54 */
55 public static function sendJPush(array $data): Response
56 {
57 return self::send('JPushMessage', $data);
58 }
59
60 /**
61 * @param string $channel
62 * @param array<string,mixed> $data
63 * @return \Illuminate\Http\Client\Response
64 * @throws \JsonException
65 */
66 public static function send(string $channel, array $data): Response
67 {
68 $payload = ['time' => Carbon::now()->timestamp, 'channel' => $channel, 'data' => $data];
69 $payload['sign'] = self::getSign($payload);
70 return Http::asJson()->post(self::getServiceHost() . '/api/provider', $payload);
71 }
72
73 /**
74 * @param array<string,mixed> $payload
75 * @return string
76 * @throws \JsonException
77 */
78 private static function getSign(array $payload): string
79 {
80 $payload['data'] = json_encode($payload['data'], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
81 $payload['secret'] = self::getServiceSecret();
82 ksort($payload);
83 return md5(base64_encode(implode('&', $payload)));
84 }
85 }
1 <?php
2
3 namespace App\Helpers;
4
5 use App\Enums\UserStatusEnum;
6 use App\Models\SystemSmsLog;
7 use App\Models\User;
8 use Arr;
9 use Illuminate\Database\Eloquent\Builder;
10 use Illuminate\Support\Facades\DB;
11 use Illuminate\Support\Facades\Redis;
12 use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
13 use Overtrue\EasySms\PhoneNumber;
14
15 class SmsHelper
16 {
17 /**
18 * @param \Overtrue\EasySms\PhoneNumber $phoneNumber
19 * @param int|array|NULL $scope
20 * @return bool
21 */
22 public static function checkPhoneExist(PhoneNumber $phoneNumber, int|array $scope = NULL): bool
23 {
24 return User::phoneNumber($phoneNumber)
25 ->when($scope, fn(Builder $query) => $query->whereIn('scope', Arr::wrap($scope)))
26 ->exists();
27 }
28
29 /**
30 * @param \Overtrue\EasySms\PhoneNumber $phoneNumber
31 * @param int|array|UserStatusEnum $status
32 * @param int|array|NULL $scope
33 * @return bool
34 */
35 public static function checkPhoneStatus(PhoneNumber $phoneNumber, int|array|UserStatusEnum $status = 1, int|array $scope = NULL): bool
36 {
37 return User::phoneNumber($phoneNumber)
38 ->when($scope, fn(Builder $query) => $query->whereIn('scope', Arr::wrap($scope)))
39 ->whereIn('status', Arr::wrap($status))
40 ->exists();
41 }
42
43 /**
44 * @param int $phone
45 * @param string $area
46 * @return \Overtrue\EasySms\PhoneNumber
47 */
48 public static function getAreaPhoneNumber(int $phone, string $area = '86'): PhoneNumber
49 {
50 return new PhoneNumber($phone, $area);
51 }
52
53 /**
54 * @param int $length
55 * @return string
56 * @throws \Exception
57 */
58 public static function createNumberCode(int $length = 6): string
59 {
60 return substr((string)(random_int(0, (10 ** $length) - 1) + 10 ** $length), 1, $length);
61 }
62
63 /**
64 * @param \Overtrue\EasySms\PhoneNumber $phone
65 * @return bool
66 */
67 public static function checkPhoneLimit(PhoneNumber $phone): bool
68 {
69 return self::getPhoneHourCount($phone) <= 5 && self::getPhoneDayCount($phone) <= 32;
70 }
71
72 /**
73 * @param \Overtrue\EasySms\PhoneNumber $phone
74 * @return int
75 */
76 public static function getPhoneHourCount(PhoneNumber $phone): int
77 {
78 return DB::table('system_sms_logs')
79 ->where('phone', $phone->getUniversalNumber())
80 ->where('status', 'success')
81 ->whereBetween('created_at', [now()->subHour()->toDateTimeString(), now()->toDateTimeString()])
82 ->count();
83 }
84
85 /**
86 * @param \Overtrue\EasySms\PhoneNumber $phone
87 * @return int
88 */
89 public static function getPhoneDayCount(PhoneNumber $phone): int
90 {
91 return DB::table('system_sms_logs')
92 ->where('phone', $phone->getUniversalNumber())
93 ->where('status', 'success')
94 ->whereBetween('created_at', [now()->subDay()->toDateTimeString(), now()->toDateTimeString()])
95 ->count();
96 }
97
98 /**
99 * @param \Overtrue\EasySms\PhoneNumber $phone
100 * @param string $code
101 * @param string $type
102 * @return string
103 */
104 public static function getSmsKey(string $type, PhoneNumber $phone, string $code, ?string $platform = ''): string
105 {
106 return collect(['sms', $phone->getUniversalNumber(), $platform, $type, $code])->filter()->implode(':');
107 }
108
109 /**
110 * @param string $smsKey
111 * @return bool
112 * @throws \RedisException
113 */
114 public static function checkSmsKey(string $smsKey): bool
115 {
116 return Redis::client()->exists($smsKey);
117 }
118
119 /**
120 * @param string $smsKey
121 * @param int $second
122 * @param string $value
123 * @return bool
124 * @throws \RedisException
125 */
126 public static function saveSmsKey(string $smsKey, int $second = 300, string $value = ''): bool
127 {
128 return Redis::client()->setex($smsKey, $second, $value);
129 }
130 }
1 <?php
2
3 namespace App\Helpers;
4
5 use AlibabaCloud\SDK\Mts\V20140618\Mts;
6 use AlibabaCloud\SDK\Sts\V20150401\Models\AssumeRoleRequest;
7 use AlibabaCloud\SDK\Sts\V20150401\Models\AssumeRoleResponseBody\credentials;
8 use AlibabaCloud\SDK\Sts\V20150401\Sts;
9 use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
10 use Darabonba\OpenApi\Models\Config;
11 use Illuminate\Support\Str;
12 use Illuminate\Support\Stringable;
13 use OSS\OssClient;
14
15 class UploadHelper
16 {
17 /**
18 * @return bool
19 */
20 public static function isInternalServer(): bool
21 {
22 return config('services.oss.internal_server', false);
23 }
24
25 /**
26 * @param array<string,string> $options
27 * @return \Darabonba\OpenApi\Models\Config
28 */
29 protected static function getConfig(array $options = []): Config
30 {
31 return new Config(['accessKeyId' => config('services.oss.sts.key'), 'accessKeySecret' => config('services.oss.sts.secret'), ...$options]);
32 }
33
34 /**
35 * @return \AlibabaCloud\SDK\Sts\V20150401\Sts
36 */
37 public static function getStsClient(): Sts
38 {
39 return new Sts(self::getConfig(['regionId' => config('services.oss.sts.endpoint')]));
40 }
41
42 /**
43 * @param int $second
44 * @return \AlibabaCloud\SDK\Sts\V20150401\Models\AssumeRoleResponseBody\credentials
45 */
46 public static function getStsToken(int $second = 3000): credentials
47 {
48 $assumeRoleRequest = new AssumeRoleRequest([
49 "roleArn" => config('services.oss.sts.role'),
50 "roleSessionName" => Str::uuid()->toString(),
51 "durationSeconds" => $second,
52 ]);
53
54 return self::getStsClient()->assumeRoleWithOptions($assumeRoleRequest, new RuntimeOptions([]))->body->credentials;
55 }
56
57 /**
58 * @return \AlibabaCloud\SDK\Mts\V20140618\Mts
59 */
60 public static function getMtsClient(): Mts
61 {
62 return new Mts(self::getConfig(['regionId' => config('services.oss.mts.endpoint'), 'protocol' => 'HTTPS']));
63 }
64
65 /**
66 * @return \OSS\OssClient
67 */
68 public static function getOssClient(bool $internal = false): OssClient
69 {
70 $token = self::getStsToken();
71 return new OssClient($token->accessKeyId, $token->accessKeySecret, $internal ? self::getOssInternal() : self::getOssEndpoint(), false, $token->securityToken);
72 }
73
74 /**
75 * @param string $object
76 * @param array<string,mixed> $options
77 * @param int $second
78 * @return string
79 * @throws \OSS\Core\OssException
80 */
81 public static function getOssSignUrl(string $object, array $options = [], int $second = 3000): string
82 {
83 return self::getOssClient()->signUrl(self::getOssBucket(), $object, $second, "PUT", [
84 OssClient::OSS_OBJECT_ACL => OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE, ...$options
85 ]);
86 }
87
88 /**
89 * @return string
90 */
91 public static function getOssBucket(): string
92 {
93 return config('services.oss.bucket');
94 }
95
96 public static function getOssSSL(): string
97 {
98 return config('services.oss.ssl') ? 'https://' : 'http://';
99 }
100
101 /**
102 * @return string
103 */
104 public static function getOssDomain(bool $host = true): string
105 {
106 return Str::of(config('services.oss.domain'))->when($host, fn(Stringable $str) => $str->prepend(self::getOssSSL()))->toString();
107 }
108
109 /**
110 * @param bool $useSuper
111 * @param bool $host
112 * @return string
113 */
114 public static function getOssEndpoint(bool $useSuper = false, bool $host = false): string
115 {
116 return Str::of($useSuper ? config('services.oss.super_endpoint') : config('services.oss.endpoint'))
117 ->when($host, fn(Stringable $str) => $str->prepend(self::getOssSSL() . self::getOssBucket() . '.'))->toString();
118 }
119
120 public static function getOssInternal(bool $host = false): string
121 {
122 return Str::of(config('services.oss.internal'))
123 ->when($host, fn(Stringable $str) => $str->prepend(self::getOssSSL() . self::getOssBucket() . '.'))->toString();
124 }
125
126 /**
127 * @param string $prefix
128 * @return string
129 */
130 public static function getFileName(string $prefix = ''): string
131 {
132 return implode('/', array_filter([$prefix, now()->format('Ymd'), Str::ulid()->toBase32()]));
133 }
134
135 /**
136 * @param string $url
137 * @return string
138 */
139 public static function urlToPath(string $url): string
140 {
141 return ltrim(parse_url($url, PHP_URL_PATH), '/');
142 }
143
144 /**
145 * @param string $object
146 * @param string $file
147 * @param array<string,mixed> $options
148 * @return string
149 * @throws \OSS\Core\OssException
150 */
151 public static function put(string $object, string $file, array $options = []): string
152 {
153 self::getOssClient(self::isInternalServer())->uploadFile(self::getOssBucket(), $object, $file, [
154 OssClient::OSS_OBJECT_ACL => OssClient::OSS_ACL_TYPE_PUBLIC_READ_WRITE, ...$options
155 ]);
156
157 return self::getOssDomain() . '/' . $object;
158 }
159
160 /**
161 * CDN域名转 OSS域名
162 * @param string $url
163 * @param bool $internal
164 * @return string
165 */
166 public static function toEndpointUrl(string $url, bool $internal = false): string
167 {
168 return Str::replace(self::getOssDomain(), $internal ? self::getOssInternal(true) : self::getOssEndpoint(false, true), $url);
169 }
170 }
1 <?php
2
3 namespace App\Helpers;
4
5 use App\Models\SystemTag;
6 use App\Models\User;
7 use App\Models\UserTagRelation;
8 use Arr;
9
10 class UserPermissionHelper
11 {
12 public User $user;
13
14 /**
15 * @param \App\Models\User $user
16 */
17 private function __construct(User $user)
18 {
19 $this->user = $user;
20 }
21
22 /**
23 * @param int|\App\Models\User $user
24 * @return \App\Helpers\UserPermissionHelper
25 */
26 public static function user(int|User $user): UserPermissionHelper
27 {
28 return new self(is_numeric($user) ? User::findOrFail($user, ['id', 'identity', 'demo_type', 'scope']) : $user);
29 }
30
31 /**
32 * @param int|array $tagIds
33 * @return bool
34 */
35 public function checkTag(int|array $tagIds): bool
36 {
37 return UserTagRelation::query()->where('type', 4)->where('user_id', $this->user->getKey())->whereIn('tag_id', Arr::wrap($tagIds))->exists();
38 }
39
40 /**
41 * @return bool
42 */
43 public function hasDemoPermission(): bool
44 {
45 if ($this->user->getAttribute('demo_type') === 0) {
46 return false;
47 }
48
49 $config = ConfigHelper::getDemoList();
50
51 if (in_array($this->user->getKey(), $config->get(ConfigHelper::DEMO_USER_KEY), false)) {
52 return true;
53 }
54
55 return $this->checkTag($config->get(ConfigHelper::DEMO_TAG_KEY));
56 }
57
58 /**
59 * @return bool
60 */
61 public function hasSingPermission(): bool
62 {
63 if (in_array($this->user->getKey(), ConfigHelper::getSingUserIds(), false)) {
64 return true;
65 }
66
67 return $this->checkTag(SystemTag::query()->whereJsonContains('expand->permission', [2])->pluck('id')->merge(ConfigHelper::getSingerTagIds())->toArray());
68 }
69
70 /**
71 * @return bool
72 */
73 public function hasListenPermission(): bool
74 {
75 if ($this->hasSingPermission() || in_array($this->user->getAttribute('identity'), [2, 3], false) || in_array($this->user->getKey(), ConfigHelper::getListenUserIds(), false)) {
76 return true;
77 }
78
79 return $this->checkTag(SystemTag::query()->whereJsonContains('expand->permission', [1])->pluck('id')->toArray());
80 }
81
82
83 /**
84 * @return bool
85 */
86 public function isSinger(): bool
87 {
88 return $this->checkTag(ConfigHelper::getSingerTagIds());
89 }
90 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Audition;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Container\AdminSection\Requests\Audition\ActivityManagerCreateRequest;
7 use App\Http\Container\AdminSection\Requests\Audition\ActivityManagerUpdateRequest;
8 use App\Models\Activity;
9 use App\Models\Pivots\ActivityLinkPivot;
10 use App\Support\Controller;
11 use Hikoon\LaravelApi\Facades\Response;
12 use Hikoon\LaravelApi\Support\ApiCode;
13
14 class ActivityManagerController extends Controller
15 {
16 /**
17 * @param \App\Http\Container\AdminSection\Requests\Audition\ActivityManagerCreateRequest $request
18 * @return \Hikoon\LaravelApi\Facades\Response
19 */
20 public function store(ActivityManagerCreateRequest $request): Response
21 {
22 $data = $request->safe()->toArray();
23 $activity = Activity::query()->find($data['activity_id']);
24 $activity->links()->wherePivot('type', 'manager')->syncWithPivotValues($data['user_ids'], ['type' => 'manager', 'expand->permission' => $data['expand->permission']], false);
25 OperationLog::admin()->createAction()->subject($activity)->content('《%s》新增外部管理员', $activity->getAttribute('song_name'));
26 return $this->success(ApiCode::CREATE_SUCCESS);
27 }
28
29 /**
30 * @param \App\Http\Container\AdminSection\Requests\Audition\ActivityManagerUpdateRequest $request
31 * @param \App\Models\Pivots\ActivityLinkPivot $activityManager
32 * @return \Hikoon\LaravelApi\Facades\Response
33 */
34 public function update(ActivityManagerUpdateRequest $request, ActivityLinkPivot $activityManager): Response
35 {
36 $activityManager->update($request->safe()->toArray());
37 $nickName = $activityManager->link()->value('nick_name');
38 $activityName = $activityManager->activity()->value('song_name');
39 OperationLog::admin()->subject($activityManager)->updateAction()->content('《%s》调整外部管理员:%s 的权限', $activityName, $nickName);
40 return $this->successWithData($activityManager, ApiCode::UPDATE_SUCCESS);
41 }
42
43 /**
44 * @param \App\Models\Pivots\ActivityLinkPivot $activityManager
45 * @return \Hikoon\LaravelApi\Facades\Response
46 */
47 public function destroy(ActivityLinkPivot $activityManager): Response
48 {
49 $nickName = $activityManager->link()->value('nick_name');
50 $activityName = $activityManager->activity()->value('song_name');
51 $activityManager->delete();
52 OperationLog::admin()->subject($activityManager)->deleteAction()->content('《%s》移除外部管理员:%s', $activityName, $nickName);
53 return $this->success(ApiCode::DELETE_SUCCESS);
54 }
55 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Audition;
4
5 use App\Excel\ActivityReListRecordExcel;
6 use App\Models\ActivityReListRecord;
7 use Hikoon\LaravelApi\Facades\Response;
8 use Hikoon\LaravelApi\Support\ApiController;
9 use Illuminate\Http\Request;
10 use Symfony\Component\HttpFoundation\BinaryFileResponse;
11
12 class ActivityReListRecordController extends ApiController
13 {
14 /**
15 * @param \Illuminate\Http\Request $request
16 * @return \Hikoon\LaravelApi\Facades\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
17 */
18 public function index(Request $request): Response|BinaryFileResponse
19 {
20 $pageSize = $request->get('pageSize');
21 $filter = $request->except(['page', 'pageSize', 'fetchType']);
22
23 $query = ActivityReListRecord::filter($filter);
24
25 if ($request->get('fetchType') === 'excel') {
26 return ActivityReListRecordExcel::withBuilder($query)->download();
27 }
28
29 return $this->successWithData($query->paginate($pageSize));
30 }
31 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Audition;
4
5 use App\Enums\ActivityWorkStatusEnum;
6 use App\Excel\ActivityWorkExcel;
7 use App\Helpers\OperationLog;
8 use App\Http\Service\ActivityService;
9 use App\Models\Activity;
10 use App\Models\Views\ActivityWork;
11 use Auth;
12 use Hikoon\LaravelApi\Facades\Response;
13 use Hikoon\LaravelApi\Support\ApiCode;
14 use Hikoon\LaravelApi\Support\ApiController;
15 use Illuminate\Http\Request;
16 use Symfony\Component\HttpFoundation\BinaryFileResponse;
17
18 class ActivityWorkController extends ApiController
19 {
20 /**
21 * @param \Illuminate\Http\Request $request
22 * @param \App\Http\Service\ActivityService $service
23 * @return \Hikoon\LaravelApi\Facades\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
24 */
25 public function index(Request $request): Response|BinaryFileResponse
26 {
27 $pageSize = $request->get('pageSize');
28 $fetchType = $request->get('fetchType', 'page');
29 $filter = $request->except(['page', 'pageSize', 'fetchType']);
30
31 $query = ActivityWork::filter($filter);
32
33 return match ($fetchType) {
34 'excel' => ActivityWorkExcel::withBuilder($query)->download(),
35 default => $this->successWithData($query->paginate($pageSize))
36 };
37 }
38
39 /**
40 * @param \Illuminate\Http\Request $request
41 * @param \App\Models\Views\ActivityWork $work
42 * @param \App\Http\Service\ActivityService $service
43 * @return \Hikoon\LaravelApi\Facades\Response
44 * @throws \Hikoon\LaravelApi\Exceptions\ValidationException
45 */
46 public function changeStatus(Request $request, ActivityWork $work, ActivityService $service): Response
47 {
48 $attribute = $request->validate(
49 ['status' => 'bail|required|in:1,2'],
50 ['status.required' => '请选择操作类型', 'status.in' => '操作类型不在指定范围内']
51 );
52
53 $service->auditWork($work, ActivityWorkStatusEnum::from($attribute['status']));
54
55 OperationLog::admin()->updateAction()->causer(Auth::user())->subject(new Activity(['id' => $work->getAttribute('activity_id')]))->content(
56 '歌曲《%s》,%s,作品《%s-V%s版本》',
57 $work->getAttribute('activity_name'),
58 $attribute['status'] === ActivityWorkStatusEnum::REJECT->value ? '标记不合适' : '完成匹配',
59 $work->getAttribute('user_nick_name'),
60 $work->getAttribute('version')
61 );
62
63 return $this->success(ApiCode::UPDATE_SUCCESS);
64 }
65 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Audition;
4
5 use App\Enums\ActivityAuditStatusEnum;
6 use App\Enums\ActivitySongTypeEnum;
7 use App\Enums\ActivityStatusEnum;
8 use App\Helpers\OperationLog;
9 use App\Http\Container\AdminSection\Requests\Audition\ApplyAuditRequest;
10 use App\Http\Container\ManageSection\Requests\Audition\ApplyCreateRequest;
11 use App\Http\Container\ManageSection\Requests\Audition\ApplyUpdateRequest;
12 use App\Http\Service\ActivityService;
13 use App\Jobs\ActivityMakeMediaJob;
14 use App\Jobs\ActivityPublishJob;
15 use App\Models\Activity;
16 use App\Models\ActivityApplyRecord;
17 use App\Notifications\ActivityApplyFailNotification;
18 use App\Notifications\ActivityApplySuccessNotification;
19 use App\Support\Controller;
20 use App\Support\Model;
21 use Arr;
22 use Hikoon\LaravelApi\Facades\Response;
23 use Hikoon\LaravelApi\Support\ApiCode;
24 use Illuminate\Http\Request;
25 use Illuminate\Support\Facades\Auth;
26 use Illuminate\Support\Facades\Bus;
27 use Illuminate\Support\Facades\Cache;
28 use Illuminate\Support\Facades\DB;
29 use Illuminate\Support\Facades\Notification;
30
31 class ApplyController extends Controller
32 {
33 /**
34 * @param \Illuminate\Http\Request $request
35 * @return \Hikoon\LaravelApi\Facades\Response
36 */
37 public function index(Request $request): Response
38 {
39 $pageSize = $request->get('pageSize', 20);
40
41 $data = Activity::filter($request->mergeIfMissing(['audit_status' => [0, 2]])->except(['page', 'pageSize']))
42 ->select(['id', 'song_name', 'cover', 'lang', 'speed', 'sub_title', 'sex', 'user_id', 'weight', 'project_id', 'lyric', 'clip_lyric', 'is_push', 'mark', 'is_official', 'expand', 'audit_status', 'created_at', 'estimate_release_at'])
43 ->addSelect([
44 'record_count' => ActivityApplyRecord::query()->selectRaw('count(*)')->whereColumn('activity_apply_records.activity_id', 'activitys.id'),
45 'last_record' => ActivityApplyRecord::query()->select('current')->whereColumn('activity_apply_records.activity_id', 'activitys.id')->latest()->limit(1)
46 ])
47 ->latest('updated_at')->withCasts(['last_record' => 'json'])->paginate($pageSize);
48
49 return $this->successWithData($data);
50 }
51
52 /**
53 * @param \App\Http\Container\ManageSection\Requests\Audition\ApplyCreateRequest $request
54 * @param \App\Http\Service\ActivityService $service
55 * @return \Hikoon\LaravelApi\Facades\Response
56 * @throws \Hikoon\LaravelApi\Exceptions\ValidationException
57 */
58 public function store(ApplyCreateRequest $request, ActivityService $service): Response
59 {
60 $attribute = array_merge($request->safe()->toArray(), ['user_id' => (int)Auth::id(), 'created_form' => 1]);
61
62 if (Cache::lock('user-' . Auth::id() . '-apply', 5)->get()) {
63
64 $activity = match ($request->integer('song_type')) {
65 ActivitySongTypeEnum::SONG->value => $service->createActivityApply($attribute + ['lang' => [], 'is_push' => 0], false),
66 ActivitySongTypeEnum::DEMO->value => $service->createDemoApply($attribute + ['is_push' => 1], false)
67 };
68
69 return $this->successWithData($activity, ApiCode::CREATE_SUCCESS);
70 }
71
72 return $this->fail(ApiCode::REQUEST_LIMIT_ERROR);
73 }
74
75 /**
76 * @param \App\Http\Container\ManageSection\Requests\Audition\ApplyUpdateRequest $request
77 * @param \App\Models\Activity $apply
78 * @return \Hikoon\LaravelApi\Facades\Response
79 */
80 public function update(ApplyUpdateRequest $request, Activity $apply): Response
81 {
82 $apply->update(Model::dot($request->safe()->merge(['audit_status' => 0, 'status' => 0])));
83
84 OperationLog::manage()->subject($apply)->updateAction()
85 ->content('《%s》,重新申请%s', $apply->getAttribute('song_name'), $apply->getChangeColumn()->except(['audit_status', 'status'])->format(',修改了:'));
86
87 return $this->successWithData($apply, ApiCode::UPDATE_SUCCESS);
88 }
89
90 /**
91 * @param \App\Http\Container\AdminSection\Requests\Audition\ApplyAuditRequest $request
92 * @param \App\Models\Activity $apply
93 * @param \App\Http\Service\ActivityService $service
94 * @return \Hikoon\LaravelApi\Facades\Response
95 */
96 public function audit(ApplyAuditRequest $request, Activity $apply, ActivityService $service): Response
97 {
98 $data = $request->safe()->toArray();
99
100 if ($data['audit_status'] === 1) {
101 $data['publish_at'] = now()->toDateTimeString();
102 $data['status'] = ActivityStatusEnum::PROCESS->value;
103 $data['audit_at'] = now()->toDateTimeString();
104
105 DB::transaction(static function () use ($apply, $data, $service) {
106 $apply->update(Model::dot(Arr::except($data, 'out_side_manages')));
107 $change = $apply->getChangeColumn();
108
109 if (isset($data['out_side_manages'])) {
110 $syncResult = $service->syncManageLink($apply, $data['out_side_manages']);
111
112 if (count($syncResult) !== 0) {
113 $change->add('manage_link');
114 }
115 }
116
117 OperationLog::admin()->statusAction()->subject($apply)->content('歌曲《%s》,审核通过%s', $apply->getAttribute('song_name'), $change->format(',修改了:'));
118
119 Bus::chain([
120 new ActivityMakeMediaJob($apply, ActivityStatusEnum::UP, true),
121 new ActivityPublishJob($apply, data_get($apply, 'expand.push_type', []), data_get($apply, 'expand.push_user', [])),
122 ])->dispatch();
123
124 //推送申请人通知
125 Notification::send($apply->getAttribute('user'), new ActivityApplySuccessNotification($apply));
126 });
127 }
128
129 if ($data['audit_status'] === 2) {
130 DB::transaction(static function () use ($apply, $data) {
131 $apply->applyRecords()->create(['audit_user_id' => Auth::id(), 'audit_msg' => $data['msg'], 'current' => $apply->attributesToArray()]);
132 OperationLog::admin()->statusAction()->subject($apply)->content('歌曲《%s》,审核不通过,理由:%s', $apply->getAttribute('song_name'), $data['msg']);
133 $apply->update(['audit_status' => 2]);
134 //推送申请人通知
135 Notification::send($apply->getAttribute('user'), new ActivityApplyFailNotification($apply, $data['msg']));
136 });
137 }
138
139 return $this->success(ApiCode::UPDATE_SUCCESS);
140 }
141
142 /**
143 * @param \Illuminate\Http\Request $request
144 * @param \App\Models\Activity $apply
145 * @return \Hikoon\LaravelApi\Facades\Response
146 */
147 public function record(Request $request, Activity $apply): Response
148 {
149 $pageSize = $request->get('pageSize', 20);
150 $filter = $request->except('page', 'pageSize');
151
152 $data = ActivityApplyRecord::filter($filter)->where('activity_id', $apply->getKey())->paginate($pageSize);
153 return $this->successWithData($data);
154 }
155
156
157 /**
158 * @param \App\Models\Activity $apply
159 * @param \App\Http\Service\ActivityService $service
160 * @return \Hikoon\LaravelApi\Facades\Response
161 */
162 public function destroy(Activity $apply, ActivityService $service): Response
163 {
164 if ($apply->getAttribute('audit_status') !== ActivityAuditStatusEnum::FAIL->value) {
165 return $this->fail(ApiCode::VALIDATION_ERROR, '只允许删除审核未通过歌曲');
166 }
167
168 $service->delete($apply);
169 OperationLog::admin()->subject($apply)->deleteAction()->content('歌曲《%s》', $apply->getAttribute('song_name'));
170
171 return $this->success(ApiCode::DELETE_SUCCESS);
172 }
173 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Audition;
4
5 use App\Enums\UserScopeEnum;
6 use App\Helpers\OperationLog;
7 use App\Http\Container\AdminSection\Requests\Audition\ProjectManagerRequest;
8 use App\Models\Pivots\UserProjectPivot;
9 use App\Models\Project;
10 use App\Models\User;
11 use App\Support\Controller;
12 use DB;
13 use Hikoon\LaravelApi\Facades\Response;
14 use Hikoon\LaravelApi\Support\ApiCode;
15
16 class ProjectManagerController extends Controller
17 {
18 /**
19 * @param \App\Http\Container\AdminSection\Requests\Audition\ProjectManagerRequest $request
20 * @return \Hikoon\LaravelApi\Facades\Response
21 */
22 public function store(ProjectManagerRequest $request): Response
23 {
24 $attribute = $request->validated();
25 $user = User::query()->find($attribute['user_id'], ['id', 'nick_name']);
26 $projectName = Project::query()->whereKey($attribute['project_id'])->value('name');
27 if (!$user) {
28 return $this->fail(ApiCode::VALIDATION_ERROR, '用户不存在');
29 }
30 if ($user->getAttribute('scope') === UserScopeEnum::SYSTEM) {
31 return $this->fail(ApiCode::VALIDATION_ERROR, '该用户为系统管理员');
32 }
33
34 User::query()->whereKey($attribute['user_id'])->update(['scope' => UserScopeEnum::PROJECT]);
35 $projectManager = UserProjectPivot::query()->updateOrCreate($attribute);
36 OperationLog::admin()->subject($projectManager)->createAction()->content('《%s》新增管理员:%s', $projectName, $user->getAttribute('nick_name'));
37 return $this->success(ApiCode::CREATE_SUCCESS);
38 }
39
40
41 public function destroy(UserProjectPivot $projectManager): Response
42 {
43
44 DB::transaction(static function () use ($projectManager) {
45 $nickName = $projectManager->user()->value('nick_name');
46 $projectName = $projectManager->project()->value('name');
47 $userId = $projectManager->getAttribute('user_id');
48 $projectManager->delete();
49
50 if (UserProjectPivot::query()->where('user_id', $userId)->doesntExist()) {
51 User::query()->whereKey($userId)->update(['scope' => UserScopeEnum::UNSET]);
52 }
53
54 OperationLog::admin()->subject($projectManager)->deleteAction()->content('《%s》移除管理员:%s', $projectName, $nickName);
55 });
56
57 return $this->success(ApiCode::DELETE_SUCCESS);
58 }
59 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers;
4
5 use App\Http\Request\UserChangePwdRequest;
6 use App\Models\SystemPermission;
7 use App\Models\SystemRole;
8 use App\Support\Controller;
9 use Hikoon\LaravelApi\Facades\Response;
10 use Hikoon\LaravelApi\Support\ApiCode;
11 use Illuminate\Http\Request;
12 use Illuminate\Support\Facades\Auth;
13
14 class AuthController extends Controller
15 {
16 /**
17 * @param \Illuminate\Http\Request $request
18 * @return \Hikoon\LaravelApi\Facades\Response
19 */
20 public function show(Request $request): Response
21 {
22 if (Auth::user()?->getAttribute('is_super') === 1) {
23 $permissions = SystemPermission::query()->pluck('system_permissions.name');
24 } else {
25 $roleIds = SystemRole::query()
26 ->join('user_has_roles', 'user_has_roles.role_id', 'system_roles.id')
27 ->where('user_has_roles.user_id', Auth::id())
28 ->where('system_roles.status', 1)
29 ->pluck('system_roles.id')->toArray();
30 $permissions = SystemPermission::query()
31 ->join('system_role_has_permissions', 'system_role_has_permissions.permission_id', 'system_permissions.id')
32 ->where('system_permissions.guard', 'Admin')
33 ->whereIn('system_role_has_permissions.role_id', $roleIds)
34 ->pluck('system_permissions.name');
35 }
36
37 return $this->successWithData([
38 'user' => $request->user()->only(['id', 'nick_name', 'avatar', 'role', 'last_login']),
39 'permissions' => $permissions
40 ]);
41 }
42
43 /**
44 * @param \App\Http\Request\UserChangePwdRequest $request
45 * @return \Hikoon\LaravelApi\Facades\Response
46 */
47 public function changePwd(UserChangePwdRequest $request): Response
48 {
49 $request->user()->update($request->safe()->toArray());
50 return $this->success(ApiCode::UPDATE_SUCCESS);
51 }
52 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Dashboard;
4
5 use App\Enums\ActivityStatusEnum;
6 use App\Models\Activity;
7 use App\Models\Pivots\ActivityTagPivot;
8 use App\Models\SystemTag;
9 use App\Models\UserAction;
10 use App\Support\Controller;
11 use Carbon\Carbon;
12 use Carbon\CarbonPeriod;
13 use DB;
14 use Hikoon\LaravelApi\Facades\Response;
15
16 class ActivityController extends Controller
17 {
18 /**
19 * @return \Hikoon\LaravelApi\Facades\Response
20 */
21 public function total(): Response
22 {
23 $calendar = CarbonPeriod::create(now()->startOfDay()->subDays(7), '1 day', 'now');
24
25 $trend = DB::table('activity_status_records')
26 ->whereBetween('time', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
27 ->pluck('up', 'time');
28
29 $today = [today()->toDateString() => Activity::query()->where('activitys.audit_status', 1)->where('activitys.status', 1)->count()];
30
31 $time = collect();
32 $calendar->forEach(fn(Carbon $item) => $time->put($item->toDateString(), 0));
33
34 return $this->successWithData([
35 'time' => $time->keys()->toArray(),
36 'data' => $time->merge($trend)->merge($today)->values()->toArray()
37 ]);
38 }
39
40
41 /**
42 * @return \Hikoon\LaravelApi\Facades\Response
43 */
44 public function listen(): Response
45 {
46 $calendar = CarbonPeriod::create(now()->startOfDay()->subDays(7), '1 day', 'now');
47
48 $trend = UserAction::query()
49 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(distinct user_id) as num")
50 ->whereIn('event_name', ['listen_clip', 'listen_entire'])
51 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
52 ->groupByRaw('days')->pluck('num', 'days');
53
54 $time = collect();
55 $calendar->forEach(fn(Carbon $item) => $time->put($item->toDateString(), 0));
56
57 return $this->successWithData([
58 'time' => $time->keys()->toArray(),
59 'data' => $time->merge($trend)->values()->toArray()
60 ]);
61 }
62
63 /**
64 * @return \Hikoon\LaravelApi\Facades\Response
65 */
66 public function like(): Response
67 {
68
69 $calendar = CarbonPeriod::create(now()->startOfDay()->subDays(7), '1 day', 'now');
70
71 $trend = UserAction::query()
72 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(distinct user_id) as num")
73 ->whereIn('event_name', ['bottom_like', 'song_like', 'sing_like'])
74 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
75 ->groupByRaw('days')->pluck('num', 'days');
76
77 $time = collect();
78 $calendar->forEach(fn(Carbon $item) => $time->put($item->toDateString(), 0));
79
80 return $this->successWithData([
81 'time' => $time->keys()->toArray(),
82 'data' => $time->merge($trend)->values()->toArray()
83 ]);
84 }
85
86 /**
87 * @return \Hikoon\LaravelApi\Facades\Response
88 */
89 public function style(): Response
90 {
91 SystemTag::resolveRelationUsing('activities', static function (SystemTag $tag) {
92 return $tag->belongsToMany(Activity::class, ActivityTagPivot::class, 'tag_id', 'activity_id')
93 ->where('activitys.status', ActivityStatusEnum::UP);
94 });
95
96 $result = collect();
97
98 SystemTag::query()->select(['id', 'name'])->withCount('activities')->where('type', 1)->get()
99 ->each(fn(SystemTag $tag) => $result->push(['value' => $tag->getAttribute('activities_count'), 'name' => $tag->getAttribute('name')]));
100
101 return $this->successWithData($result);
102 }
103
104
105 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Dashboard;
4
5 use App\Enums\ActivityWorkModeEnum;
6 use App\Enums\ActivityWorkTypeEnum;
7 use App\Models\UserAction;
8 use App\Models\Views\ActivityWork;
9 use App\Support\Controller;
10 use Arr;
11 use Carbon\Carbon;
12 use Carbon\CarbonPeriod;
13 use Hikoon\LaravelApi\Facades\Response;
14 use Illuminate\Database\Eloquent\Collection;
15 use Illuminate\Http\Request;
16 use Illuminate\Support\Str;
17 use Symfony\Component\HttpFoundation\BinaryFileResponse;
18 use Vtiful\Kernel\Excel;
19 use Vtiful\Kernel\Format;
20
21 class IndexController extends Controller
22 {
23 /**
24 * @param \Illuminate\Http\Request $request
25 * @return \Hikoon\LaravelApi\Facades\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
26 */
27 public function submitWork(Request $request): Response|BinaryFileResponse
28 {
29 $pageSize = $request->get('pageSize', 20);
30 $filter = $request->except('page', 'pageSize');
31
32 $build = ActivityWork::filter($filter)
33 ->select([
34 'id', 'activity_id', 'activity_name', 'activity_type', 'activity_title', 'activity_status', 'project_id',
35 'user_id', 'user_nick_name', 'user_real_name', 'user_identity', 'business_id',
36 'price_id', 'mode', 'sing_type', 'demo_url', 'submit_at', 'status'
37 ])
38 ->with([
39 'business' => fn($query) => $query->select(['id', 'nick_name', 'real_name', 'identity'])->where('identity', 3),
40 'project:id,name',
41 'tags:id,name',
42 'price:id,value,is_deduct,is_talk,is_accept_address,address_id',
43 ])
44 ->where('type', ActivityWorkTypeEnum::SUBMIT);
45
46
47 if ($request->get('fetchType') === 'excel') {
48 $fileName = Str::random(32) . '.xlsx';
49 $excel = new Excel(['path' => storage_path('excels')]);
50 $fileObject = $excel->constMemory($fileName, NULL, false);
51 $format = new Format($fileObject->getHandle());
52 $headStyle = $format->align(Format::FORMAT_ALIGN_CENTER, Format::FORMAT_ALIGN_VERTICAL_CENTER)->background(0XF2F2F2)->bold()->toResource();
53
54
55 $row = $fileObject
56 ->header(['名称', '推荐语', '歌曲风格', '厂牌', '用户艺名', '用户真名', '身份', '经纪人艺名', '经纪人真名', '试唱方式', '唱酬', '分成', '价格是否可谈', '录音地点', '是否接受其他录音地点', '提交时间', '试唱音频', '试唱结果'])
57 ->setRow('A1', 22, $headStyle)
58 ->setColumn('A:J', 16)
59 ->setColumn('K:U', 14);
60
61 $build->chunk(1000, static function (Collection $collection) use ($fileObject) {
62 $fileObject->data($collection->map(static function (ActivityWork $work) {
63 $price = $work->getAttribute('price');
64 $userIdentity = data_get($work, 'user_identity', 0);
65
66 return [
67 data_get($work, 'activity_name', ''),
68 // ActivitySongTypeEnum::tryFrom(data_get($work, 'activity_type', 1))?->label(),
69 data_get($work, 'activity_title', ''),
70 $work->getRelation('tags')->implode('name', '、'),
71 data_get($work, 'project.name', ''),
72 data_get($work, 'user_nick_name', ''),
73 data_get($work, 'user_real_name', ''),
74 match ($userIdentity) {
75 0 => '未认证',
76 1 => '音乐人',
77 2, 3 => "经纪人",
78 default => ''
79 },
80 data_get($work, 'business.nick_name', ''),
81 data_get($work, 'business.real_name', ''),
82 $work->getAttribute('mode') === ActivityWorkModeEnum::OFFLINE ? $work->getAttribute('mode')?->label() : $work->getAttribute('sing_type')?->label(),
83 data_get($price, 'value.is_reward', 0) === 1 ? data_get($price, 'value.amounts') : '无',
84 data_get($price, 'value.is_dividend', 0) === 1 ? $price->getDividendFormat() : '无',
85 data_get($price, 'is_talk', 0) === 1 ? '是' : '否',
86 $price ? $price->getAddressFormat() : '',
87 data_get($price, 'is_accept_address', 0) === 1 ? '是' : '否',
88 data_get($work, 'submit_at', ''),
89 data_get($work, 'demo_url', ''),
90 $work->getFormatStatus()
91 ];
92 })->toArray());
93 });
94 return response()->download($row->output(), $fileName)->deleteFileAfterSend();
95 }
96
97 return $this->successWithData($build->paginate($pageSize));
98 }
99
100 /**
101 * @param \Illuminate\Http\Request $request
102 * @return \Hikoon\LaravelApi\Facades\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
103 */
104 public function overview(Request $request): Response|BinaryFileResponse
105 {
106 $request->validate([
107 'createBetween' => 'required|array|size:2',
108 'createBetween.*' => 'date'
109 ], [
110 'createBetween.required' => '请选择查询区间',
111 'createBetween.array' => '查询参数异常',
112 'createBetween.size' => '查询参数异常',
113 'createBetween.*.date' => '查询参数异常'
114 ]);
115 $searchTime = $request->offsetGet('createBetween');
116
117 $startTime = Carbon::parse(Arr::first($searchTime))->startOfDay();
118 $endTime = Carbon::parse(Arr::last($searchTime))->startOfDay();
119
120 $calendar = CarbonPeriod::create($startTime, '1 day', $endTime);
121
122 $listerUserCount = UserAction::query()
123 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(distinct user_id) as num")
124 ->whereIn('event_name', ['listen_clip', 'listen_entire'])
125 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
126 ->groupByRaw('days')->pluck('num', 'days');
127
128 $listerCount = UserAction::query()
129 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(*) as num")
130 ->whereIn('event_name', ['listen_clip', 'listen_entire'])
131 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
132 ->groupByRaw('days')->pluck('num', 'days');
133
134 $likeCount = UserAction::query()
135 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(*) as num")
136 ->whereIn('event_name', ['bottom_like', 'song_like', 'sing_like'])
137 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
138 ->groupByRaw('days')->pluck('num', 'days');
139
140 $submitCount = UserAction::query()
141 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(*) as num")
142 ->whereIn('event_name', ['submit_piece_online', 'submit_sing_offline', 'submit_sing_online'])
143 ->whereBetween('created_at', [$calendar->first()?->startOfDay()->toDateTimeString(), $calendar->last()?->endOfDay()->toDateTimeString()])
144 ->groupByRaw('days')->pluck('num', 'days');
145
146 $time = collect();
147 $calendar->forEach(fn(Carbon $item) => $time->put($item->toDateString(), 0));
148
149 $result = [
150 'time' => $time->keys()->toArray(),
151 'listen_user_count' => $time->merge($listerUserCount)->values()->toArray(),
152 'listen_count' => $time->merge($listerCount)->values()->toArray(),
153 'like_count' => $time->merge($likeCount)->values()->toArray(),
154 'submit_count' => $time->merge($submitCount)->values()->toArray(),
155 ];
156
157 if ($request->get('fetchType') === 'excel') {
158 $fileName = Str::random(32) . '.xlsx';
159 $excel = new Excel(['path' => storage_path('excels')]);
160 $fileObject = $excel->constMemory($fileName, NULL, false);
161 $format = new Format($fileObject->getHandle());
162
163 $headStyle = $format->align(Format::FORMAT_ALIGN_CENTER, Format::FORMAT_ALIGN_VERTICAL_CENTER)->background(0XF2F2F2)->bold()->toResource();
164
165
166 $row = $fileObject
167 ->header(['日期', '试听用户', '试听次数', '收藏次数', '提交作品数'])
168 ->setRow('A1', 22, $headStyle)
169 ->setColumn('A:E', 24);
170
171 $fileObject->data([
172 [
173 sprintf('%s~%s', Arr::first($result['time']), Arr::last($result['time'])),
174 $listerUserCount->sum(),
175 $listerCount->sum(),
176 $likeCount->sum(),
177 $submitCount->sum(),
178 ],
179 ...Arr::map($result['time'], static fn($val, $key) => [
180 $val,
181 Arr::get($result['listen_user_count'], $key, 0),
182 Arr::get($result['listen_count'], $key, 0),
183 Arr::get($result['like_count'], $key, 0),
184 Arr::get($result['submit_count'], $key, 0),
185 ])
186 ]);
187
188
189 return response()->download($row->output(), $fileName)->deleteFileAfterSend();
190 }
191
192
193 return $this->successWithData($result);
194 }
195 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\Dashboard;
4
5 use App\Enums\UserStatusEnum;
6 use App\Models\SystemConfig;
7 use App\Models\SystemTag;
8 use App\Models\User;
9 use App\Models\UserCertify;
10 use App\Models\UserTagRelation;
11 use App\Support\Controller;
12 use Arr;
13 use Carbon\Carbon;
14 use Carbon\CarbonPeriod;
15 use Hikoon\LaravelApi\Facades\Response;
16 use Illuminate\Database\Query\Builder;
17
18 class UserController extends Controller
19 {
20 /**
21 * 认证数统计
22 * @return \Hikoon\LaravelApi\Facades\Response
23 */
24 public function certify(): Response
25 {
26 $calendar = collect();
27
28 CarbonPeriod::create(now()->startOfDay()->subDays(7), '1 day', 'now')->forEach(fn(Carbon $item) => $calendar->put($item->toDateString(), 0));
29
30 $keys = $calendar->keys()->toArray();
31
32 $trend = UserCertify::query()
33 ->selectRaw("DATE_FORMAT(created_at,'%Y-%m-%d') as days,count(distinct user_id) as num")
34 ->whereBetween('created_at', [Carbon::parse(Arr::first($keys))->startOfDay()->toDateTimeString(), Carbon::parse(Arr::last($keys))->endOfDay()->toDateTimeString()])
35 ->groupByRaw('days')->pluck('num', 'days');
36
37 return $this->successWithData([
38 'time' => $keys,
39 'data' => $calendar->merge($trend)->values()
40 ]);
41 }
42
43 /**
44 * 用户数统计
45 * @return \Hikoon\LaravelApi\Facades\Response
46 */
47 public function total(): Response
48 {
49 return $this->successWithData([
50 'type' => ['未认证', '音乐人', '经纪人', '厂牌管理员', '系统管理员'],
51 'data' => [
52 User::query()->where('identity', 0)->where('status', UserStatusEnum::ACTIVATE)->count(),
53 User::query()->whereIn('identity', [1, 3])->where('status', UserStatusEnum::ACTIVATE)->count(),
54 User::query()->whereIn('identity', [2, 3])->where('status', UserStatusEnum::ACTIVATE)->count(),
55 User::query()->where('scope', 2)->where('status', UserStatusEnum::ACTIVATE)->count(),
56 User::query()->where('scope', 1)->where('status', UserStatusEnum::ACTIVATE)->count(),
57 ]
58 ]);
59 }
60
61 /**
62 * @return \Hikoon\LaravelApi\Facades\Response
63 */
64 public function style(): Response
65 {
66 $singerTagIds = explode(',', SystemConfig::query()->where('identifier', 'auth_singer_tag')->value('content') ?? '');
67
68 SystemTag::resolveRelationUsing('users', static function (SystemTag $tag) use ($singerTagIds) {
69 return $tag->belongsToMany(User::class, UserTagRelation::class, 'tag_id', 'user_id')
70 ->whereExists(fn(Builder $query) => $query->from('user_tag_relations', 'r')->selectRaw('1')->whereIn('r.tag_id', $singerTagIds)->where('r.type', 4)->whereColumn('r.user_id', 'users.id'))
71 ->wherePivot('type', 1)->where('users.status', UserStatusEnum::ACTIVATE);
72 });
73
74 $result = collect();
75
76 SystemTag::query()->select(['id', 'name'])->withCount('users')->where('type', 1)->get()
77 ->each(fn(SystemTag $tag) => $result->push(['value' => $tag->getAttribute('users_count'), 'name' => $tag->getAttribute('name')]));
78
79 return $this->successWithData($result);
80 }
81
82 /**
83 * @return \Hikoon\LaravelApi\Facades\Response
84 */
85 public function skill(): Response
86 {
87 SystemTag::resolveRelationUsing('users', static function (SystemTag $tag) {
88 return $tag->belongsToMany(User::class, UserTagRelation::class, 'tag_id', 'user_id')
89 ->wherePivot('type', 4)->whereIn('users.identity', [1, 3])->where('users.status', UserStatusEnum::ACTIVATE);
90 });
91
92 $result = collect();
93
94 SystemTag::query()->select(['id', 'name'])->withCount('users')->where('type', 4)->get()
95 ->each(fn(SystemTag $tag) => $result->push(['value' => $tag->getAttribute('users_count'), 'name' => $tag->getAttribute('name')]));
96
97 return $this->successWithData($result);
98 }
99 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Request\BooleanStatusRequest;
7 use App\Http\Container\AdminSection\Requests\System\BannerRequest;
8 use App\Models\Banner;
9 use App\Support\Controller;
10 use Auth;
11 use Hikoon\LaravelApi\Facades\Response;
12 use Hikoon\LaravelApi\Support\ApiCode;
13 use Illuminate\Http\Request;
14 use Illuminate\Support\Facades\URL;
15
16 class BannerController extends Controller
17 {
18 /**
19 * @param \Illuminate\Http\Request $request
20 * @return \Hikoon\LaravelApi\Facades\Response
21 */
22 public function index(Request $request): Response
23 {
24 $pageSize = $request->get('pageSize', 20);
25 $filter = $request->except('page', 'pageSize');
26 $data = Banner::filter($filter)->paginate($pageSize);
27 return $this->successWithData($data);
28 }
29
30 /**
31 * @param \App\Http\Container\AdminSection\Requests\System\BannerRequest $request
32 * @return \Hikoon\LaravelApi\Facades\Response
33 */
34 public function store(BannerRequest $request): Response
35 {
36 $attribute = $request->safe()->merge(['user_id' => Auth::id()])->toArray();
37 Banner::created(static fn(Banner $banner) => $banner->getAttribute('type') === 4 && $banner->update(['content_picture' => URL::asset('/app/banners/' . $banner->getKey())]));
38 $banner = Banner::query()->create($attribute);
39 OperationLog::admin()->createAction()->subject($banner)->content('轮播图《%s》', $banner->getAttribute('name'));
40 return $this->successWithData($banner, ApiCode::CREATE_SUCCESS);
41 }
42
43 /**
44 * @param \App\Http\Container\AdminSection\Requests\System\BannerRequest $request
45 * @param \App\Models\Banner $banner
46 * @return \Hikoon\LaravelApi\Facades\Response
47 */
48 public function update(BannerRequest $request, Banner $banner): Response
49 {
50 $attribute = $request->safe()->toArray();
51 if ($banner->getAttribute('type') === 4) {
52 $attribute['content_picture'] = URL::asset('/app/banners/' . $banner->getKey());
53 }
54 $banner->update($attribute);
55 OperationLog::admin()->updateAction()->subject($banner)->content('轮播图《%s》', $banner->getAttribute('name'));
56 return $this->successWithData($banner, ApiCode::UPDATE_SUCCESS);
57 }
58
59 /**
60 * @param \App\Http\Request\BooleanStatusRequest $request
61 * @param \App\Models\Banner $banner
62 * @return \Hikoon\LaravelApi\Facades\Response
63 */
64 public function changeStatus(BooleanStatusRequest $request, Banner $banner): Response
65 {
66 $status = $request->safe()->offsetGet('status');
67 $banner->update(['status' => $status]);
68 OperationLog::admin()->statusAction()->subject($banner)->content('轮播图《%s》%s', $banner->getAttribute('name'), $banner->getAttribute('status')?->label());
69 return $this->success(ApiCode::UPDATE_SUCCESS);
70 }
71
72 /**
73 * @param \App\Models\Banner $banner
74 * @return \Hikoon\LaravelApi\Facades\Response
75 */
76 public function destroy(Banner $banner): Response
77 {
78 Banner::query()->whereKey($banner->getKey())->limit(1)->delete();
79 OperationLog::admin()->deleteAction()->subject($banner)->content('轮播图《%s》', $banner->getAttribute('name'));
80 return $this->success(ApiCode::DELETE_SUCCESS);
81 }
82 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System\Broker;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Container\AdminSection\Requests\System\BrokerPushConfigRequest;
7 use App\Models\BrokerPushConfig;
8 use Auth;
9 use Hikoon\LaravelApi\Facades\Response;
10 use Hikoon\LaravelApi\Support\ApiCode;
11 use Hikoon\LaravelApi\Support\ApiController;
12 use Illuminate\Http\Request;
13 use Str;
14
15 class PushConfigController extends ApiController
16 {
17 /**
18 * @param \Illuminate\Http\Request $request
19 * @return \Hikoon\LaravelApi\Facades\Response
20 */
21 public function index(Request $request): Response
22 {
23 $pageSize = $request->get('pageSize', 20);
24 $fetchType = $request->get('fetchType', 'page');
25 $filter = $request->except(['page', 'pageSize', 'fetchType']);
26 $build = BrokerPushConfig::query()->filter($filter);
27
28 if ($fetchType === 'all') {
29 return $this->successWithData($build->get());
30 }
31
32 return $this->successWithData($build->paginate($pageSize));
33 }
34
35 /**
36 * @param \App\Http\Container\AdminSection\Requests\System\BrokerPushConfigRequest $request
37 * @return \Hikoon\LaravelApi\Facades\Response
38 */
39 public function store(BrokerPushConfigRequest $request): Response
40 {
41 $config = BrokerPushConfig::query()->create($request->safe()->merge(['user_id' => Auth::id()])->toArray());
42 OperationLog::admin()->subject($config)->createAction()->content('推送配置《%s》', Str::limit($config->getAttribute('identifier')));
43 return $this->successWithData($config, ApiCode::CREATE_SUCCESS);
44 }
45
46 /**
47 * @param \App\Http\Container\AdminSection\Requests\System\BrokerPushConfigRequest $request
48 * @param \App\Models\BrokerPushConfig $pushConfig
49 * @return \Hikoon\LaravelApi\Facades\Response
50 */
51 public function update(BrokerPushConfigRequest $request, BrokerPushConfig $pushConfig): Response
52 {
53 $pushConfig->update($request->safe()->toArray());
54 OperationLog::admin()->subject($pushConfig)->updateAction()->content('推送配置《%s》', Str::limit($pushConfig->getAttribute('identifier')));
55 return $this->successWithData($pushConfig, ApiCode::UPDATE_SUCCESS);
56 }
57
58 /**
59 * @param \App\Models\BrokerPushConfig $pushConfig
60 * @return \Hikoon\LaravelApi\Facades\Response
61 */
62 public function destroy(BrokerPushConfig $pushConfig): Response
63 {
64 $pushConfig->delete();
65 OperationLog::admin()->subject($pushConfig)->deleteAction()->content('推送配置《%s》', Str::limit($pushConfig->getAttribute('identifier')));
66 return $this->success(ApiCode::DELETE_SUCCESS);
67 }
68 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System\Broker;
4
5 use App\Enums\BrokerPushLevelRecordStatusEnum;
6 use App\Enums\BrokerPushMatchRecordStatusEnum;
7 use App\Helpers\OperationLog;
8 use App\Http\Container\AdminSection\Requests\System\BrokerPushLevelRecordRequest;
9 use App\Jobs\BrokerPushLevelRecordRollBackJob;
10 use App\Jobs\BrokerPushLevelRecordSendJob;
11 use App\Models\BrokerPushLevelRecord;
12 use App\Models\BrokerPushLevelRecordItem;
13 use Hikoon\LaravelApi\Facades\Response;
14 use Hikoon\LaravelApi\Support\ApiCode;
15 use Hikoon\LaravelApi\Support\ApiController;
16 use Illuminate\Http\Request;
17
18 class PushLevelRecordController extends ApiController
19 {
20 /**
21 * @param \Illuminate\Http\Request $request
22 * @return \Hikoon\LaravelApi\Facades\Response
23 */
24 public function index(Request $request): Response
25 {
26 $pageSize = $request->get('pageSize', 20);
27 $filter = $request->except(['page', 'pageSize']);
28
29 $data = BrokerPushLevelRecord::filter($filter)->paginate($pageSize);
30
31 return $this->successWithData($data);
32 }
33
34 /**
35 * @param \App\Http\Container\AdminSection\Requests\System\BrokerPushLevelRecordRequest $request
36 * @param \App\Models\BrokerPushLevelRecord $record
37 * @return \Hikoon\LaravelApi\Facades\Response
38 */
39 public function update(BrokerPushLevelRecordRequest $request, BrokerPushLevelRecord $record): Response
40 {
41 if (!in_array($record->getAttribute('status'), [BrokerPushLevelRecordStatusEnum::WAITING->value, BrokerPushMatchRecordStatusEnum::FAIL->value], false)) {
42 return $this->fail(ApiCode::VALIDATION_ERROR, '消息已发送或发送中,无法编辑');
43 }
44
45 $record->update($request->safe()->toArray());
46 OperationLog::admin()->subject($record)->updateAction()->content('等级推送《%s》', $record->getAttribute('title'));
47
48 return $this->success(ApiCode::UPDATE_SUCCESS);
49 }
50
51 /**
52 * @param \Illuminate\Http\Request $request
53 * @param \App\Models\BrokerPushLevelRecord $record
54 * @return \Hikoon\LaravelApi\Facades\Response
55 */
56 public function children(Request $request, BrokerPushLevelRecord $record): Response
57 {
58 $pageSize = $request->get('pageSize', 20);
59 $filter = $request->except(['page', 'pageSize']);
60
61 $data = BrokerPushLevelRecordItem::filter($filter)->where('record_id', $record->getKey())->paginate($pageSize);
62
63 return $this->successWithData($data);
64 }
65
66 /**
67 * @param \App\Models\BrokerPushLevelRecord $record
68 * @return \Hikoon\LaravelApi\Facades\Response
69 */
70 public function send(BrokerPushLevelRecord $record): Response
71 {
72 if ($record->getAttribute('status') === BrokerPushLevelRecordStatusEnum::WAITING->value) {
73 return $this->fail(ApiCode::VALIDATION_ERROR, '消息尚未到发送时间,无法发送');
74 }
75 if (!in_array($record->getAttribute('status'), [BrokerPushLevelRecordStatusEnum::FAIL->value, BrokerPushLevelRecordStatusEnum::ROLLBACK->value], false)) {
76 return $this->fail(ApiCode::VALIDATION_ERROR, '消息已发送或发送中,无法再次发送');
77 }
78
79 $record->update(['status' => BrokerPushLevelRecordStatusEnum::PROCESSING, 'publish_at' => now()->toDateTimeString()]);
80 OperationLog::admin()->subject($record)->updateAction()->content('发送经纪人等级通知《%s》', $record->getAttribute('title'));
81 BrokerPushLevelRecordSendJob::dispatch($record);
82
83 return $this->success(ApiCode::UPDATE_SUCCESS, '操作成功');
84 }
85
86 /**
87 * @param \App\Models\BrokerPushLevelRecord $record
88 * @return \Hikoon\LaravelApi\Facades\Response
89 */
90 public function rollback(BrokerPushLevelRecord $record): Response
91 {
92 if ($record->getAttribute('status') !== BrokerPushLevelRecordStatusEnum::SUCCESS->value) {
93 return $this->fail(ApiCode::VALIDATION_ERROR, '消息尚未发送成功,无法撤回');
94 }
95
96 $record->update(['status' => BrokerPushLevelRecordStatusEnum::PROCESSING]);
97 BrokerPushLevelRecordRollBackJob::dispatch($record);
98 OperationLog::admin()->subject($record)->updateAction()->content('撤销经纪人等级通知《%s》', $record->getAttribute('title'));
99
100 return $this->success(ApiCode::UPDATE_SUCCESS, '操作成功');
101 }
102
103 public function destroy(BrokerPushLevelRecord $record): Response
104 {
105 BrokerPushLevelRecord::destroy($record->getKey());
106 BrokerPushLevelRecordItem::query()->where('record_id', $record->getKey())->update(['deleted_at' => now()->toDateTimeString()]);
107 OperationLog::admin()->subject($record)->deleteAction()->content('等级推送《%s》', $record->getAttribute('title'));
108 return $this->success(ApiCode::DELETE_SUCCESS);
109 }
110
111 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System\Broker;
4
5 use App\Enums\BrokerPushMatchRecordStatusEnum;
6 use App\Excel\BrokerPushMatchRecordExcel;
7 use App\Helpers\IMHelper;
8 use App\Helpers\OperationLog;
9 use App\Jobs\BrokerPushMatchRecordJob;
10 use App\Models\BrokerPushMatchRecord;
11 use Hikoon\LaravelApi\Facades\Response;
12 use Hikoon\LaravelApi\Support\ApiCode;
13 use Hikoon\LaravelApi\Support\ApiController;
14 use Illuminate\Http\Request;
15 use Illuminate\Support\Facades\DB;
16 use Symfony\Component\HttpFoundation\BinaryFileResponse;
17
18 class PushMatchRecordController extends ApiController
19 {
20 /**
21 * @param \Illuminate\Http\Request $request
22 * @return \Hikoon\LaravelApi\Facades\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse
23 */
24 public function index(Request $request): Response|BinaryFileResponse
25 {
26 $pageSize = $request->get('pageSize', 20);
27 $fetchType = $request->get('fetchType', 'page');
28 $filter = $request->except(['page', 'pageSize', 'fetchType']);
29
30 $build = BrokerPushMatchRecord::query()->filter($filter);
31
32 if ($fetchType === 'excel') {
33 return BrokerPushMatchRecordExcel::withBuilder($build)->download('运营经纪人-合作通知日志.xlsx');
34 }
35
36 return $this->successWithData($build->paginate($pageSize));
37 }
38
39 /**
40 * @param \App\Models\BrokerPushMatchRecord $record
41 * @return \Hikoon\LaravelApi\Facades\Response
42 */
43 public function send(BrokerPushMatchRecord $record): Response
44 {
45 if (!in_array($record->getAttribute('status'), [BrokerPushMatchRecordStatusEnum::FAIL->value, BrokerPushMatchRecordStatusEnum::ROLLBACK->value], false)) {
46 return $this->fail(ApiCode::VALIDATION_ERROR, '消息已发送或发送中,无法再次发送');
47 }
48
49 DB::transaction(static function () use ($record) {
50 $record->update(['status' => 0, 'message_id' => '']);
51 OperationLog::admin()->subject($record)->updateAction()->content('发送合作通知日志:' . $record->getAttribute('content'));
52 BrokerPushMatchRecordJob::dispatch($record);
53 });
54
55 return $this->success(ApiCode::UPDATE_SUCCESS, '操作成功');
56 }
57
58 /**
59 * @param \App\Models\BrokerPushMatchRecord $record
60 * @return \Hikoon\LaravelApi\Facades\Response
61 * @throws \Exception
62 */
63 public function rollback(BrokerPushMatchRecord $record): Response
64 {
65 if ($record->getAttribute('status') !== BrokerPushMatchRecordStatusEnum::SUCCESS->value) {
66 return $this->fail(ApiCode::VALIDATION_ERROR, '消息尚未发送成功,无法撤回');
67 }
68
69 if (IMHelper::rollbackChatMessage(IMHelper::SYSTEM_USER, $record->getAttribute('broker_id'), $record->getAttribute('message_id'))->json('ActionStatus') === 'OK') {
70 $record->update(['status' => BrokerPushMatchRecordStatusEnum::ROLLBACK]);
71 OperationLog::admin()->subject($record)->updateAction()->content('撤销合作通知日志:' . $record->getAttribute('content'));
72 }
73
74 return $this->success(ApiCode::UPDATE_SUCCESS, '操作成功');
75 }
76 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System\Broker;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Container\AdminSection\Requests\System\BrokerPushLevelRecordRequest;
7 use App\Http\Container\AdminSection\Requests\System\BrokerUserConfigRequest;
8 use App\Jobs\BrokerPushLevelRecordCreateJob;
9 use App\Models\BrokerUserConfig;
10 use App\Models\BrokerUserConfigItem;
11 use App\Models\User;
12 use Arr;
13 use Auth;
14 use Carbon\Carbon;
15 use Hikoon\LaravelApi\Facades\Response;
16 use Hikoon\LaravelApi\Support\ApiCode;
17 use Hikoon\LaravelApi\Support\ApiController;
18 use Illuminate\Http\Request;
19 use Illuminate\Support\Facades\DB;
20 use Illuminate\Support\Facades\RateLimiter;
21 use Illuminate\Support\LazyCollection;
22 use Str;
23 use Symfony\Component\HttpFoundation\StreamedResponse;
24 use Vtiful\Kernel\Excel;
25
26 class UserConfigController extends ApiController
27 {
28 /**
29 * @param \Illuminate\Http\Request $request
30 * @return \Hikoon\LaravelApi\Facades\Response
31 */
32 public function index(Request $request): Response
33 {
34 $pageSize = $request->get('pageSize', 20);
35 $fetchType = $request->get('fetchType', 'page');
36 $filter = $request->except(['page', 'pageSize', 'fetchType']);
37 $build = BrokerUserConfig::query()->filter($filter);
38
39 if ($fetchType === 'all') {
40 return $this->successWithData($build->get());
41 }
42
43 return $this->successWithData($build->paginate($pageSize));
44 }
45
46 /**
47 * @param \App\Http\Container\AdminSection\Requests\System\BrokerUserConfigRequest $request
48 * @return \Hikoon\LaravelApi\Facades\Response
49 */
50 public function store(BrokerUserConfigRequest $request): Response
51 {
52 if (!RateLimiter::attempt('user-' . Auth::id() . '-create-broker', $perMinute = 1, static fn() => NULL, 3)) {
53 return $this->fail(ApiCode::REQUEST_LIMIT_ERROR);
54 }
55
56 $attribute = $request->safe(['title', 'begin_at', 'end_at', 'push_type', 'push_at']) + ['user_id' => Auth::id()];
57 $items = $request->input('items', []);
58
59 $userConfig = DB::transaction(static function () use ($attribute, $items) {
60 $config = BrokerUserConfig::query()->create(Arr::except($attribute, ['push_type', 'push_at']));
61
62 BrokerUserConfigItem::query()->upsert(
63 Arr::map($items, static fn($item) => Arr::only($item + ['config_id' => $config->getKey()], ['user_id', 'identifier', 'singer_num', 'config_id'])),
64 ['config_id', 'user_id'], ['singer_num', 'identifier']
65 );
66
67 BrokerUserConfigItem::query()
68 ->leftJoin('users', 'users.id', 'broker_user_config_items.user_id')
69 ->where('broker_user_config_items.config_id', $config->getKey())
70 ->whereIn('users.identity', [2, 3])
71 ->update(['broker_user_config_items.status' => DB::raw('CASE WHEN users.`status`= 1 THEN 1 ELSE 0 END')]);
72
73 BrokerPushLevelRecordCreateJob::dispatchIf($attribute['push_type'] !== 0, $config, Auth::id(), Carbon::parse($attribute['push_at']), $attribute['push_type'] === 2);
74 OperationLog::admin()->subject($config)->createAction()->content('经纪人配置《%s》', Str::limit($config->getAttribute('title')));
75
76 return $config;
77 });
78
79 return $this->successWithData($userConfig, ApiCode::CREATE_SUCCESS);
80 }
81
82 /**
83 * @param \App\Models\BrokerUserConfig $userConfig
84 * @return \Hikoon\LaravelApi\Facades\Response
85 */
86 public function show(BrokerUserConfig $userConfig): Response
87 {
88 $userConfig->load(['user:id,nick_name,real_name,identity', 'items:id,user_id,config_id,identifier,singer_num,status', 'items.user:id,nick_name,real_name,identity']);
89
90 return $this->successWithData($userConfig);
91 }
92
93 /**
94 * @param \App\Http\Container\AdminSection\Requests\System\BrokerUserConfigRequest $request
95 * @param \App\Models\BrokerUserConfig $userConfig
96 * @return \Hikoon\LaravelApi\Facades\Response
97 */
98 public function update(BrokerUserConfigRequest $request, BrokerUserConfig $userConfig): Response
99 {
100 $attribute = $request->safe(['title', 'begin_at', 'end_at']) + ['user_id' => Auth::id()];
101 $items = $request->input('items', []);
102
103 DB::transaction(static function () use ($userConfig, $attribute, $items) {
104 $userConfig->update($attribute);
105 $itemIds = Arr::pluck($items, 'user_id');
106 //删除不在此次用户数据
107 BrokerUserConfigItem::query()->where('config_id', $userConfig->getKey())->whereNotIn('user_id', $itemIds)->delete();
108 //新增与更新数据
109 BrokerUserConfigItem::query()->upsert(
110 Arr::map($items, static fn($item) => Arr::only($item + ['config_id' => $userConfig->getKey()], ['user_id', 'identifier', 'singer_num', 'config_id'])),
111 ['config_id', 'user_id'], ['singer_num', 'identifier']
112 );
113 //同步经纪人匹配状态
114 BrokerUserConfigItem::query()
115 ->leftJoin('users', 'users.id', 'broker_user_config_items.user_id')
116 ->where('broker_user_config_items.config_id', $userConfig->getKey())
117 ->whereIn('users.identity', [2, 3])
118 ->update(['broker_user_config_items.status' => DB::raw('CASE WHEN users.`status`= 1 AND users.`identity` IN (2,3) THEN 1 ELSE 0 END')]);
119 OperationLog::admin()->subject($userConfig)->updateAction()->content('经纪人配置《%s》', Str::limit($userConfig->getAttribute('title')));
120 });
121
122
123 return $this->successWithData($userConfig, ApiCode::CREATE_SUCCESS);
124 }
125
126 /**
127 * @param \App\Models\BrokerUserConfig $userConfig
128 * @return \Hikoon\LaravelApi\Facades\Response
129 */
130 public function destroy(BrokerUserConfig $userConfig): Response
131 {
132 $userConfig->delete();
133 $userConfig->items()->delete();
134 OperationLog::admin()->subject($userConfig)->deleteAction()->content('经纪人配置《%s》', Str::limit($userConfig->getAttribute('title')));
135 return $this->success(ApiCode::DELETE_SUCCESS);
136 }
137
138 /**
139 * @param \App\Http\Container\AdminSection\Requests\System\BrokerPushLevelRecordRequest $request
140 * @param \App\Models\BrokerUserConfig $userConfig
141 * @return \Hikoon\LaravelApi\Facades\Response
142 */
143 public function storeLevelRecord(BrokerPushLevelRecordRequest $request, BrokerUserConfig $userConfig): Response
144 {
145 $attribute = $request->safe()->toArray();
146 BrokerPushLevelRecordCreateJob::dispatchSync($userConfig, Auth::id(), Carbon::parse($attribute['publish_at']), (bool)$attribute['is_alert']);
147 return $this->success(ApiCode::CREATE_SUCCESS);
148 }
149
150 /**
151 * @param \Illuminate\Http\Request $request
152 * @return \Symfony\Component\HttpFoundation\StreamedResponse
153 */
154 public function upload(Request $request): StreamedResponse
155 {
156 ignore_user_abort(true);
157 return \Response::stream(static function () use ($request) {
158 LazyCollection::make(static function () use ($request) {
159 $filePath = $request->file('file')?->path();
160 $excel = new Excel(['path' => dirname($filePath)]);
161 $excel->openFile(basename($filePath))->openSheet()->setSkipRows(0);
162
163 while (($row = $excel->nextRow()) !== NULL) {
164 yield $row;
165 }
166 })->chunk(1000)->each(function (LazyCollection $items) {
167 $users = User::withTrashed()->whereIn('id', Arr::pluck($items, 0))->get(['id', 'identity', 'nick_name', 'status'])->groupBy('id');
168 $items->reject(fn($item) => array_filter($item) === [])->each(function ($item) use ($users) {
169 if (isset($item[0], $item[1], $item[2]) && is_int($item[0]) && (is_int($item[2]) || empty($item[2]))) {
170 $user = $users->get($item[0])?->first();
171
172 echo "id: " . $item[0] . PHP_EOL . "event: item" . PHP_EOL . "data: " . json_encode([
173 'user' => ['id' => $item[0], 'nick_name' => $user?->getRawOriginal('nick_name', '')],
174 'identifier' => $item[1],
175 'singer_num' => $item[2],
176 'status' => ($user?->getRawOriginal('status') === 1 && in_array($user?->getRawOriginal('identity'), [2, 3], false)) ? 1 : 0
177 ], JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) . PHP_EOL . 'retry: 5000' . PHP_EOL . PHP_EOL;
178 ob_flush();
179 flush();
180 }
181 });
182 });
183
184 echo "id: 0" . PHP_EOL . "event: finish" . PHP_EOL . "data: {}" . PHP_EOL . 'retry: 0' . PHP_EOL . PHP_EOL;
185 ob_flush();
186 flush();
187
188 }, 200, ['Cache-Control' => 'no-cache', 'Content-Type' => 'text/event-stream', 'X-Accel-Buffering' => 'no']);
189 }
190 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Container\AdminSection\Requests\System\ConfigCreateRequest;
7 use App\Http\Container\AdminSection\Requests\System\ConfigUpdateRequest;
8 use App\Http\Request\BooleanStatusRequest;
9 use App\Models\SystemConfig;
10 use App\Models\User;
11 use App\Support\Controller;
12 use Hikoon\LaravelApi\Facades\Response;
13 use Hikoon\LaravelApi\Support\ApiCode;
14 use Illuminate\Http\Request;
15
16 class ConfigController extends Controller
17 {
18 /**
19 * @param \Illuminate\Http\Request $request
20 * @return \Hikoon\LaravelApi\Facades\Response
21 */
22 public function index(Request $request): Response
23 {
24 $pageSize = $request->get('pageSize', 20);
25 $filter = $request->mergeIfMissing(['parent_id' => 0])->except('page', 'pageSize', 'fetchType');
26 $fetchType = $request->get('fetchType', 'page');
27 $build = SystemConfig::filter($filter);
28
29 return match ($fetchType) {
30 'all' => $this->successWithData($build->get()),
31 default => $this->successWithData($build->paginate($pageSize))
32 };
33 }
34
35 /**
36 * @param \App\Http\Container\AdminSection\Requests\System\ConfigCreateRequest $request
37 * @return \Hikoon\LaravelApi\Facades\Response
38 */
39 public function store(ConfigCreateRequest $request): Response
40 {
41 $config = SystemConfig::query()->create($request->validated());
42 OperationLog::admin()->createAction()->subject($config)->content('配置字典《%s》', $config->getAttribute('name'));
43 return $this->successWithData($config, ApiCode::CREATE_SUCCESS);
44 }
45
46 /**
47 * @param \App\Models\SystemConfig $config
48 * @return \Hikoon\LaravelApi\Facades\Response
49 */
50 public function show(SystemConfig $config): Response
51 {
52 $config->load('children');
53 return $this->successWithData($config);
54 }
55
56 /**
57 * @param \App\Http\Container\AdminSection\Requests\System\ConfigUpdateRequest $request
58 * @param \App\Models\SystemConfig $config
59 * @return \Hikoon\LaravelApi\Facades\Response
60 */
61 public function update(ConfigUpdateRequest $request, SystemConfig $config): Response
62 {
63 //如果是修改Demo标签
64 if ($config->getAttribute('identifier') === 'CtGly-jsLjAu5yJvLrN7L') {
65 $currentIds = array_filter(explode(',', $config->getAttribute('content')));
66 $nextIds = array_filter(explode(',', $request->get('content')));
67 $diffIds = array_filter($currentIds, static fn($value) => !in_array($value, $nextIds, false));
68 if (count($diffIds) > 0) {
69 User::query()->where('demo_type', 0)
70 ->whereHas('authTags', fn($query) => $query->whereIn('tag_id', $diffIds))
71 ->whereDoesntHave('authTags', fn($query) => $query->whereIn('tag_id', $nextIds))
72 ->update(['demo_type' => 1]);
73 }
74 }
75
76 $config->update($request->validated());
77 OperationLog::admin()->updateAction()->subject($config)->content('配置字典《%s》', $config->getAttribute('name'));
78
79 return $this->successWithData($config, ApiCode::UPDATE_SUCCESS);
80 }
81
82 /**
83 * @param \App\Http\Request\BooleanStatusRequest $request
84 * @param \App\Models\SystemConfig $config
85 * @return \Hikoon\LaravelApi\Facades\Response
86 */
87 public function changeStatus(BooleanStatusRequest $request, SystemConfig $config): Response
88 {
89 $config->update($request->validated());
90 OperationLog::admin()->statusAction()->subject($config)->content('配置字典《%s》', $config->getAttribute('name'));
91 return $this->success(ApiCode::UPDATE_SUCCESS);
92 }
93 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System;
4
5 use App\Helpers\OperationLog;
6 use App\Http\Container\AdminSection\Requests\System\MaterialRequest;
7 use App\Models\SystemMaterial;
8 use App\Support\Controller;
9 use Hikoon\LaravelApi\Facades\Response;
10 use Hikoon\LaravelApi\Support\ApiCode;
11 use Illuminate\Http\Request;
12
13 class MaterialController extends Controller
14 {
15 /**
16 * @param \Illuminate\Http\Request $request
17 * @return \Hikoon\LaravelApi\Facades\Response
18 */
19 public function index(Request $request): Response
20 {
21 $pageSize = $request->get('pageSize', 30);
22 $filter = $request->except('page', 'pageSize');
23 $data = SystemMaterial::filter($filter)->paginate($pageSize);
24
25 return $this->successWithData($data);
26 }
27
28 /**
29 * @param \App\Http\Container\AdminSection\Requests\System\MaterialRequest $request
30 * @return \Hikoon\LaravelApi\Facades\Response
31 */
32 public function store(MaterialRequest $request): Response
33 {
34 $attributes = $request->safe()->offsetGet('data');
35 $ids = [];
36 foreach ($attributes as $attribute) {
37 $ids[] = SystemMaterial::query()->create($attribute)?->getKey();
38 }
39 OperationLog::admin()->createAction()->subject(new SystemMaterial(['id' => 0]))->content('素材:%s', implode(',', $ids));
40 return $this->success(ApiCode::CREATE_SUCCESS);
41 }
42
43 /**
44 * @param \App\Http\Container\AdminSection\Requests\System\MaterialRequest $request
45 * @param \App\Models\SystemMaterial $material
46 * @return \Hikoon\LaravelApi\Facades\Response
47 */
48 public function update(MaterialRequest $request, SystemMaterial $material): Response
49 {
50 $material->update($request->validated());
51 OperationLog::admin()->updateAction()->subject($material)->content('素材:%s', $material->getKey());
52 return $this->success(ApiCode::UPDATE_SUCCESS);
53 }
54
55 /**
56 * @param \App\Models\SystemMaterial $material
57 * @return \Hikoon\LaravelApi\Facades\Response
58 */
59 public function destroy(SystemMaterial $material): Response
60 {
61 $material->delete();
62 OperationLog::admin()->deleteAction()->subject($material)->content('素材:%s', $material->getKey());
63 return $this->success(ApiCode::DELETE_SUCCESS);
64 }
65 }
1 <?php
2
3 namespace App\Http\Container\AdminSection\Controllers\System;
4
5 use App\Enums\NotificationStatusEnum;
6 use App\Helpers\OperationLog;
7 use App\Http\Container\AdminSection\Requests\System\NotificationRequest;
8 use App\Jobs\SystemNotificationRollBackJob;
9 use App\Jobs\SystemNotificationSendJob;
10 use App\Models\Activity;
11 use App\Models\Notification;
12 use App\Models\NotificationUser;
13 use App\Models\Project;
14 use App\Models\User;
15 use App\Support\Controller;
16 use Auth;
17 use DB;
18 use Hikoon\LaravelApi\Facades\Response;
19 use Hikoon\LaravelApi\Support\ApiCode;
20 use Illuminate\Database\Eloquent\Collection;
21 use Illuminate\Http\Request;
22 use Str;
23
24 class NotificationController extends Controller
25 {
26
27 /**
28 * @param \Illuminate\Http\Request $request
29 * @return \Hikoon\LaravelApi\Facades\Response
30 */
31 public function index(Request $request): Response
32 {
33 $pageSize = $request->get('pageSize', 20);
34 $filter = $request->except(['page', 'pageSize']);
35 $data = Notification::filter($filter)->withCount([
36 'items',
37 'items as read_count' => fn($query) => $query->whereNotNull('read_at')
38 ])->paginate($pageSize);
39
40 return $this->successWithData($data);
41 }
42
43 /**
44 * @param \App\Http\Container\AdminSection\Requests\System\NotificationRequest $request
45 * @return \Hikoon\LaravelApi\Facades\Response
46 */
47 public function store(NotificationRequest $request): Response
48 {
49 $attribute = $request->validated();
50
51 $notify = DB::transaction(static function () use ($attribute) {
52 $notify = Notification::query()->create($attribute + ['user_id' => Auth::id()]);
53
54 User::filter(['status' => [0, 1], 'identityRole' => $attribute['publish_to']])->select(['id'])
55 ->chunkById(1000, function (Collection $collection) use ($notify) {
56 NotificationUser::query()->insert($collection->map(fn($item) => [
57 'user_id' => $item->getKey(),
58 'notification_id' => $notify->getKey(),
59 'created_at' => $notify->getAttribute('created_at'),
60 'updated_at' => $notify->getAttribute('updated_at'),
61 'status' => NotificationStatusEnum::WAITING
62 ])->toArray());
63 });
64
65 OperationLog::admin()->subject($notify)->createAction()->content('系统通知《%s》', Str::limit($notify->getAttribute('title')));
66
67 return $notify;
68 });
69
70 if ($notify->getAttribute('publish_type') === 1) {
71 $this->send($notify);
72 }
73
74 return $this->successWithData($notify);
75 }
76
77 /**
78 * @param \App\Models\Notification $notification
79 * @return \Hikoon\LaravelApi\Facades\Response
80 */
81 public function show(Notification $notification): Response
82 {
83 $notification->loadCount('items');
84 $linkType = $notification->getAttribute('link_type');
85 $linkId = $notification->getAttribute('link_id');
86
87 $notification->setAttribute('link_name', match ($linkType) {
88 'user' => User::query()->whereKey($linkId)->value('nick_name'),
89 'project' => Project::query()->whereKey($linkId)->value('name'),
90 'activity' => Activity::query()->whereKey($linkId)->value('song_name'),
91 default => ''
92 });
93
94 return $this->successWithData($notification);
95 }
96
97 /**
98 * @param \App\Http\Container\AdminSection\Requests\System\NotificationRequest $request
99 * @param \App\Models\Notification $notification
100 * @return \Hikoon\LaravelApi\Facades\Response
101 */
102 public function update(NotificationRequest $request, Notification $notification): Response
103 {
104 if (!in_array($notification->getAttribute('status'), [NotificationStatusEnum::CANCEL->value, NotificationStatusEnum::ROLLBACK->value], false)) {
105 return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法修改');
106 }
107
108 $attribute = $request->validated();
109
110 $data = DB::transaction(static function () use ($notification, $attribute) {
111 $notification->update($attribute + ['status' => NotificationStatusEnum::WAITING]);
112
113 NotificationUser::query()->where('notification_id', $notification->getKey())->delete();
114 User::filter(['status' => [0, 1], 'identityRole' => $attribute['publish_to']])->select(['id'])->chunkById(1000, function (Collection $collection) use ($notification) {
115 NotificationUser::query()->insert($collection->map(fn($item) => [
116 'user_id' => $item->getKey(),
117 'notification_id' => $notification->getKey(),
118 'created_at' => $notification->getAttribute('created_at'),
119 'updated_at' => $notification->getAttribute('updated_at')
120 ])->toArray());
121 });
122
123 OperationLog::admin()->subject($notification)->updateAction()
124 ->content('系统通知《%s》%s', Str::limit($notification->getAttribute('title')), $notification->getChangeColumn()->except(['status'])->format(',修改了:'));
125 return $notification;
126 });
127
128 return $this->successWithData($data);
129 }
130
131 /**
132 * @param \App\Models\Notification $notification
133 * @return \Hikoon\LaravelApi\Facades\Response
134 */
135 public function send(Notification $notification): Response
136 {
137 if (!in_array($notification->getAttribute('status'), [NotificationStatusEnum::WAITING->value, NotificationStatusEnum::FAIL->value], false)) {
138 return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法发送');
139 }
140
141 DB::transaction(static function () use ($notification) {
142 $notification->update(['status' => NotificationStatusEnum::PROCESSING, 'publish_at' => now()->toDateTimeString()]);
143 SystemNotificationSendJob::dispatch($notification);
144 });
145
146 return $this->successWithData(ApiCode::UPDATE_SUCCESS);
147 }
148
149 /**
150 * @param \App\Models\Notification $notification
151 * @return \Hikoon\LaravelApi\Facades\Response
152 */
153 public function cancel(Notification $notification): Response
154 {
155 if ($notification->getAttribute('status') !== NotificationStatusEnum::WAITING->value) {
156 return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法取消发送');
157 }
158
159 $notification->update(['status' => NotificationStatusEnum::CANCEL]);
160
161 OperationLog::admin()->subject($notification)->updateAction()->content('系统通知《%s》取消发送', Str::limit($notification->getAttribute('title')));
162 return $this->success(ApiCode::UPDATE_SUCCESS);
163 }
164
165 /**
166 * @param \App\Models\Notification $notification
167 * @return \Hikoon\LaravelApi\Facades\Response
168 * @throws \Throwable
169 */
170 public function rollback(Notification $notification): Response
171 {
172 if ($notification->getAttribute('status') !== NotificationStatusEnum::SUCCESS->value) {
173 return $this->fail(ApiCode::VALIDATION_ERROR, '当前状态无法撤销发送');
174 }
175
176 DB::transaction(static function () use ($notification) {
177 $notification->update(['status' => NotificationStatusEnum::ROLLBACK]);
178 SystemNotificationRollBackJob::dispatch($notification);
179 });
180
181 return $this->success(ApiCode::UPDATE_SUCCESS);
182 }
183
184 /**
185 * @param \Illuminate\Http\Request $request
186 * @param \App\Models\Notification $notification
187 * @return \Hikoon\LaravelApi\Facades\Response
188 */
189 public function user(Request $request, Notification $notification): Response
190 {
191 $pageSize = $request->get('pageSize', 20);
192 $userFilter = $request->only(['nick_name', 'authIds', 'setWith']);
193 $notifyFilter = $request->only(['status', 'read_status']);
194 $data = User::filter($userFilter)->withTrashed($userFilter)->withCasts(['status' => 'int'])
195 ->joinSub(NotificationUser::filter($notifyFilter)->where('notification_id', $notification->getKey()), 'notify', 'notify.user_id', 'users.id')
196 ->selectRaw('users.id,users.nick_name,users.sex,users.avatar,users.official_status,users.identity,notify.status,notify.send_at,notify.read_at')
197 ->orderByRaw('users.created_at')
198 ->paginate($pageSize);
199
200 return $this->successWithData($data);
201 }
202 }
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
No preview for this file type
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
No preview for this file type
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This file is too large to display.