diff --git a/README.md b/README.md index f13aca45a..7872e6d86 100644 --- a/README.md +++ b/README.md @@ -46,25 +46,26 @@ ## 当前功能概览 - [x] [微信小程序](https://github.com/Meedu/wechat-mini) -- [x] 视频在线点播/视频加密播放 -- [x] 手机号注册 -- [x] 会员/课程单独购买两种收费模式 +- [x] 阿里云视频点播服务/腾讯云视频点播服务/视频直链三种视频播放方式 +- [x] 手机号注册/登录/密码找回 +- [x] 课程单独购买/视频单独购买/全站会员三种收费模式 - [x] 站内消息 - [x] 支付宝支付/微信支付/手动转账 +- [x] 优惠码功能 +- [x] 邀请功能,一级返现 - [x] 单元测试 -- [x] 系统公告 - [x] SEO优化 -- [x] 数据定时备份 - [x] 友情链接 - [x] QQ登录/Github登录/微信登录 - [x] 插件系统 -- [x] Swagger Api文档管理 +- [x] 模板系统 ## FAQ - [API接口](https://meedu-v2-xiaoteng.doc.coding.io/) - [安装教程](docs/安装教程.md) - [MeEdu使用教程](docs/使用教程.md) +- [MeEdu常见问题总结及功能介绍](https://www.yuque.com/meedu/yr7rek) ## License diff --git a/app/Http/Controllers/Api/V2/CaptchaController.php b/app/Http/Controllers/Api/V2/CaptchaController.php index ac90ea52c..9b42e3e40 100644 --- a/app/Http/Controllers/Api/V2/CaptchaController.php +++ b/app/Http/Controllers/Api/V2/CaptchaController.php @@ -109,7 +109,7 @@ public function sentSms(SmsRequest $request) ['mobile' => $mobile, 'scene' => $scene] = $request->filldata(); $code = str_pad(mt_rand(0, 999999), 6, 0, STR_PAD_LEFT); $this->smsService->sendCode($mobile, $code, $scene); - $this->cacheService->put(sprintf(ApiV2Constant::MOBILE_OR_PASSWORD_ERROR, $mobile), $code, ApiV2Constant::SMS_CODE_EXPIRE); + $this->cacheService->put(sprintf(ApiV2Constant::MOBILE_CODE_CACHE_KEY, $mobile), $code, ApiV2Constant::SMS_CODE_EXPIRE); return $this->success(); } } diff --git a/app/Http/Controllers/Api/V2/OtherController.php b/app/Http/Controllers/Api/V2/OtherController.php new file mode 100644 index 000000000..f372cd668 --- /dev/null +++ b/app/Http/Controllers/Api/V2/OtherController.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace App\Http\Controllers\Api\V2; + +use App\Services\Base\Services\ConfigService; +use App\Services\Base\Interfaces\ConfigServiceInterface; + +class OtherController extends BaseController +{ + + /** + * @var ConfigService + */ + protected $configService; + + public function __construct(ConfigServiceInterface $configService) + { + $this->configService = $configService; + } + + /** + * @OA\Get( + * path="/other/userProtocol", + * summary="用户协议", + * tags={"其它"}, + * @OA\Response( + * description="",response=200, + * @OA\JsonContent( + * @OA\Property(property="code",type="integer",description="状态码"), + * @OA\Property(property="message",type="string",description="消息"), + * @OA\Property(property="data",type="string",description="用户协议内容"), + * ) + * ) + * ) + * + * @return \Illuminate\Http\JsonResponse + */ + public function userProtocol() + { + $protocol = $this->configService->getMemberProtocol(); + return $this->data($protocol); + } +} diff --git a/app/Http/Controllers/Backend/Api/V1/AliyunVideoUploadController.php b/app/Http/Controllers/Backend/Api/V1/AliyunVideoUploadController.php index 2a2d67c14..d28cb78e7 100644 --- a/app/Http/Controllers/Backend/Api/V1/AliyunVideoUploadController.php +++ b/app/Http/Controllers/Backend/Api/V1/AliyunVideoUploadController.php @@ -37,7 +37,7 @@ public function createVideoToken(Request $request) } catch (Exception $exception) { exception_record($exception); - return exception_response($exception); + return $this->error($exception->getMessage()); } } @@ -59,7 +59,7 @@ public function refreshVideoToken(Request $request) } catch (Exception $exception) { exception_record($exception); - return exception_response($exception); + return $this->error($exception->getMessage()); } } } diff --git a/app/Http/Controllers/Backend/Api/V1/VideoUploadController.php b/app/Http/Controllers/Backend/Api/V1/VideoUploadController.php index cee4f94c9..497333e69 100644 --- a/app/Http/Controllers/Backend/Api/V1/VideoUploadController.php +++ b/app/Http/Controllers/Backend/Api/V1/VideoUploadController.php @@ -44,7 +44,7 @@ public function aliyunCreateVideoToken(Request $request) } catch (Exception $exception) { exception_record($exception); - return exception_response($exception); + return $this->error($exception->getMessage()); } } @@ -66,7 +66,7 @@ public function aliyunRefreshVideoToken(Request $request) } catch (Exception $exception) { exception_record($exception); - return exception_response($exception); + return $this->error($exception->getMessage()); } } } diff --git a/app/Http/Controllers/Frontend/PaymentController.php b/app/Http/Controllers/Frontend/PaymentController.php index 32fbef168..e8391985a 100644 --- a/app/Http/Controllers/Frontend/PaymentController.php +++ b/app/Http/Controllers/Frontend/PaymentController.php @@ -11,10 +11,14 @@ namespace App\Http\Controllers\Frontend; +use App\Services\Base\Services\ConfigService; use App\Services\Base\Interfaces\ConfigServiceInterface; class PaymentController extends FrontendController { + /** + * @var ConfigService + */ protected $configService; public function __construct(ConfigServiceInterface $configService) diff --git a/app/Http/Controllers/Frontend/SmsController.php b/app/Http/Controllers/Frontend/SmsController.php index bf4d68937..ba4b1f7a4 100644 --- a/app/Http/Controllers/Frontend/SmsController.php +++ b/app/Http/Controllers/Frontend/SmsController.php @@ -38,7 +38,7 @@ public function send(SmsSendRequest $request) } catch (Exception $exception) { exception_record($exception); - return exception_response($exception, __('error')); + return $this->error(__('error')); } } diff --git a/app/helper.php b/app/helper.php index 04804a673..d4403c5d7 100644 --- a/app/helper.php +++ b/app/helper.php @@ -49,24 +49,6 @@ function menu_active($routeName) } } -if (!function_exists('exception_response')) { - /** - * 异常响应. - * - * @param Exception $exception - * @param string $message - * - * @return \Illuminate\Http\JsonResponse - */ - function exception_response(Exception $exception, string $message = '') - { - return response()->json([ - 'message' => $message ?: $exception->getMessage(), - 'code' => $exception->getCode() ?: 500, - ]); - } -} - if (!function_exists('exception_record')) { /** * 记录异常. @@ -77,9 +59,9 @@ function exception_record(Exception $exception): void { $request = request(); $data = [ + 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'code' => $exception->getCode(), - 'message' => $exception->getMessage(), 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString(), 'params' => $request->all(), @@ -91,18 +73,6 @@ function exception_record(Exception $exception): void } } -if (!function_exists('admin')) { - /** - * 获取当前登录的管理员. - * - * @return \App\Models\Administrator - */ - function admin() - { - return \Illuminate\Support\Facades\Auth::guard('administrator')->user(); - } -} - if (!function_exists('markdown_to_html')) { /** * markdown转换为html. @@ -121,39 +91,6 @@ function markdown_to_html($content) } } -if (!function_exists('markdown_clean')) { - /** - * 过滤markdown非法字符串. - * - * @param string $markdownContent - * - * @return string - */ - function markdown_clean(string $markdownContent) - { - $html = markdown_to_html($markdownContent); - $safeHtml = clean($html, null); - - return (new \League\HTMLToMarkdown\HtmlConverter())->convert($safeHtml); - } -} - -if (!function_exists('image_url')) { - /** - * 给图片添加参数. - * - * @param $url - * - * @return string - */ - function image_url($url) - { - $params = config('meedu.upload.image.params', ''); - - return strstr('?', $url) !== false ? $url . $params : $url . '?' . $params; - } -} - if (!function_exists('aliyun_play_auth')) { /** * 获取阿里云视频的播放Auth. @@ -281,12 +218,9 @@ function is_wechat() */ function duration_humans($duration) { - if ($duration instanceof \App\Models\Video) { - $duration = $duration->duration; - } $minute = intdiv($duration, 60); $second = $duration % 60; - if ($minute > 60) { + if ($minute >= 60) { $hours = intdiv($minute, 60); $minute = $minute % 60; @@ -316,8 +250,7 @@ function enabled_socialites() if (!function_exists('get_payments')) { /** - * 获取可用的Payment. - * + * @param $scene * @return \Illuminate\Support\Collection */ function get_payments($scene) @@ -325,7 +258,7 @@ function get_payments($scene) /** * @var \App\Services\Base\Services\ConfigService */ - $configService = app()->make(\App\Services\Base\Services\ConfigService::class); + $configService = app()->make(\App\Services\Base\Interfaces\ConfigServiceInterface::class); $payments = collect($configService->getPayments())->filter(function ($payment) use ($scene) { $enabled = $payment['enabled'] ?? false; $isSet = $payment[$scene] ?? false; @@ -337,40 +270,6 @@ function get_payments($scene) } } -if (!function_exists('app_menu_is_active')) { - function app_menu_is_active($menu) - { - $request = request(); - $const = [ - 'index' => [ - 'index', - ], - 'courses' => [ - 'courses', - 'videos', - 'course.show', - 'video.show', - 'search', - 'member.course.buy', - 'member.video.buy', - ], - 'role' => [ - 'role.index', - 'member.role.buy', - ], - ]; - $menus = $const[$menu] ?? []; - if (!$menus) { - return false; - } - if ($request->routeIs(...$menus)) { - return true; - } - - return false; - } -} - if (!function_exists('get_at_users')) { /** * @param string $content diff --git a/config/generator.php b/config/generator.php deleted file mode 100644 index 79ff4089b..000000000 --- a/config/generator.php +++ /dev/null @@ -1,46 +0,0 @@ - '首页导航', - 'model' => 'Nav', - 'request' => [ - 'rules' => [ - 'sort' => 'required', - 'name' => 'required', - 'url' => 'required', - ], - 'messages' => [ - 'sort.required' => '请输入排序值', - 'name.required' => '请输入链接名', - 'url.required' => '请输入链接地址', - ], - 'filldata' => [ - 'sort', - 'name', - 'url', - ], - ], - 'template' => [ - 'edit' => [ - 'fields' => [ - 'sort' => '排序值', - 'name' => '链接名', - 'url' => '链接地址', - ], - ], - 'index' => [ - 'fields' => [ - 'sort' => '排序值', - 'name' => '链接名', - 'url' => '链接地址', - ], - ], - ], - ] -]; \ No newline at end of file diff --git a/config/laravel-page-speed.php b/config/laravel-page-speed.php deleted file mode 100644 index c43bd5ae3..000000000 --- a/config/laravel-page-speed.php +++ /dev/null @@ -1,63 +0,0 @@ - env('LARAVEL_PAGE_SPEED_ENABLE', true), - - /* - |-------------------------------------------------------------------------- - | Skip Routes - |-------------------------------------------------------------------------- - | - | Skip Routes paths to exclude. - | You can use * as wildcard. - | - */ - 'skip' => [ - '*.xml', - '*.less', - '*.pdf', - '*.doc', - '*.txt', - '*.ico', - '*.rss', - '*.zip', - '*.mp3', - '*.rar', - '*.exe', - '*.wmv', - '*.doc', - '*.avi', - '*.ppt', - '*.mpg', - '*.mpeg', - '*.tif', - '*.wav', - '*.mov', - '*.psd', - '*.ai', - '*.xls', - '*.mp4', - '*.m4a', - '*.swf', - '*.dat', - '*.dmg', - '*.iso', - '*.flv', - '*.m4v', - '*.torrent', - // 跳过二维码支付界面,如果压缩可能导致二维码无法展示 - 'member/order/pay/*', - 'backend/*' - ], - -]; diff --git "a/docs/\345\256\211\350\243\205\346\225\231\347\250\213.md" "b/docs/\345\256\211\350\243\205\346\225\231\347\250\213.md" index 5b5b46ba8..a85fe792d 100644 --- "a/docs/\345\256\211\350\243\205\346\225\231\347\250\213.md" +++ "b/docs/\345\256\211\350\243\205\346\225\231\347\250\213.md" @@ -128,7 +128,7 @@ php artisan install backend_menu 到这里,MeEdu 基本上就安装完成了。但是,想要访问 MeEdu 的后台,您还需要安装 MeEdu 的后台的前端项目,地址: [https://github.com/Meedu/backend](https://github.com/Meedu/backend) 。请按照该项目的说明进行安装。 -# 下面的步骤是可选步骤,如果您想要在生成环境中使用,建议您还需要进行下面的配置 +# 下面的步骤是可选步骤,如果您想要在正式环境中使用,建议您还需要进行下面的配置 ### 步骤九 diff --git a/routes/apiv2.php b/routes/apiv2.php index bd4afbbb4..69439c93a 100644 --- a/routes/apiv2.php +++ b/routes/apiv2.php @@ -39,6 +39,10 @@ // 优惠码 Route::get('/promoCode/{code}', 'PromoCodeController@detail'); +Route::group(['prefix' => 'other'], function () { + Route::get('/userProtocol', 'OtherController@userProtocol'); +}); + Route::group(['middleware' => ['auth:apiv2'], 'prefix' => 'member'], function () { Route::get('detail', 'MemberController@detail'); Route::post('detail/password', 'MemberController@passwordChange'); diff --git a/tests/Feature/Api/V2/UserProtocolTest.php b/tests/Feature/Api/V2/UserProtocolTest.php new file mode 100644 index 000000000..61f79043e --- /dev/null +++ b/tests/Feature/Api/V2/UserProtocolTest.php @@ -0,0 +1,19 @@ + $protocol]); + $response = $this->getJson('/api/v2/other/userProtocol'); + $response = $this->assertResponseSuccess($response); + $this->assertEquals($protocol, $response['data']); + } + +} \ No newline at end of file diff --git a/tests/Unit/HelperTest.php b/tests/Unit/HelperTest.php new file mode 100644 index 000000000..a8a12c2f9 --- /dev/null +++ b/tests/Unit/HelperTest.php @@ -0,0 +1,61 @@ +assertFalse(false); + } + + public function test_is_wechat() + { + $this->assertFalse(is_wechat()); + } + + public function test_is_h5() + { + $this->assertFalse(is_h5()); + } + + public function test_duration_humans() + { + $this->assertEquals('00:01', duration_humans(1)); + $this->assertEquals('00:29', duration_humans(29)); + $this->assertEquals('01:01', duration_humans(61)); + $this->assertEquals('03:59', duration_humans(239)); + $this->assertEquals('01:00:01', duration_humans(3601)); + $this->assertEquals('01:01:01', duration_humans(3661)); + } + + public function test_array_compress() + { + $arr = [ + 1 => [ + 1 => 1, + 2 => 2, + ], + ]; + $arr = array_compress($arr); + $this->assertEquals(1, $arr['1.1']); + $this->assertEquals(2, $arr['1.2']); + } + + public function test_random_number() + { + $str = random_number('C', 10); + $this->assertEquals(10, mb_strlen($str)); + } + +} \ No newline at end of file