├── public
├── favicon.ico
├── robots.txt
├── assets
│ ├── morpho
│ │ ├── banner
│ │ │ ├── 1.webp
│ │ │ ├── 2.webp
│ │ │ └── 3.webp
│ │ ├── app-icons
│ │ │ ├── icon-180x180.png
│ │ │ └── icon-32x32.png
│ │ ├── icons
│ │ │ └── cartzilla-icons.woff2
│ │ ├── fonts
│ │ │ └── inter-variable-latin.woff2
│ │ └── js
│ │ │ └── scroll-to-top.js
│ └── common
│ │ └── images
│ │ ├── 3dian.png
│ │ ├── logo.png
│ │ ├── android.png
│ │ ├── banner.png
│ │ ├── default.jpg
│ │ ├── iphone.png
│ │ └── logo.svg
├── js
│ └── filament
│ │ └── forms
│ │ └── components
│ │ ├── textarea.js
│ │ ├── tags-input.js
│ │ └── key-value.js
├── .htaccess
├── web.config
└── index.php
├── resources
├── sass
│ └── app.scss
├── js
│ ├── app.js
│ └── bootstrap.js
├── views
│ ├── email
│ │ └── mail.blade.php
│ ├── filament
│ │ └── pages
│ │ │ ├── login.blade.php
│ │ │ └── manage-email-test.blade.php
│ ├── themes
│ │ └── morpho
│ │ │ ├── theme.json
│ │ │ └── views
│ │ │ └── layouts
│ │ │ ├── default.blade.php
│ │ │ ├── _script.blade.php
│ │ │ ├── _cart_dropdown.blade.php
│ │ │ ├── seo.blade.php
│ │ │ └── _footer.blade.php
│ ├── admin
│ │ └── pages
│ │ │ └── manage-theme-settings.blade.php
│ └── vendor
│ │ └── geetest
│ │ └── geetest.blade.php
└── lang
│ ├── zh_CN
│ ├── articles.php
│ ├── emailtpl.php
│ ├── goods-group.php
│ ├── extension.php
│ ├── article.php
│ ├── theme-setting.php
│ ├── email-test.php
│ ├── coupon.php
│ ├── pay.php
│ ├── remote-server.php
│ ├── menu.php
│ ├── global.php
│ ├── luna.php
│ ├── order.php
│ ├── carmis.php
│ ├── goods.php
│ └── system-setting.php
│ ├── zh_TW
│ ├── articles.php
│ ├── emailtpl.php
│ ├── goods-group.php
│ ├── menu.php
│ ├── coupon.php
│ ├── extension.php
│ ├── article.php
│ ├── theme-setting.php
│ ├── email-test.php
│ ├── pay.php
│ ├── carmis.php
│ ├── order.php
│ ├── remote-server.php
│ ├── luna.php
│ ├── global.php
│ ├── goods.php
│ └── system-setting.php
│ └── en
│ ├── pagination.php
│ ├── auth.php
│ └── passwords.php
├── storage
├── framework
│ ├── views
│ │ └── .gitignore
│ ├── testing
│ │ └── .gitignore
│ ├── sessions
│ │ └── .gitignore
│ └── .gitignore
├── logs
│ └── .gitignore
└── app
│ ├── public
│ └── .gitignore
│ └── .gitignore
├── bootstrap
├── cache
│ └── .gitignore
└── app.php
├── database
├── .gitignore
├── seeds
│ ├── DatabaseSeeder.php
│ └── OrderTableSeeder.php
└── factories
│ └── OrderFactory.php
├── .gitattributes
├── Dockerfile
├── docker-compose.yml
├── .gitignore
├── app
├── Admin
│ ├── Resources
│ │ ├── Payments
│ │ │ └── Pages
│ │ │ │ ├── CreatePay.php
│ │ │ │ ├── ListPays.php
│ │ │ │ └── EditPay.php
│ │ ├── Servers
│ │ │ └── Pages
│ │ │ │ ├── CreateRemoteServer.php
│ │ │ │ ├── EditRemoteServer.php
│ │ │ │ └── ListRemoteServers.php
│ │ ├── Cards
│ │ │ └── Pages
│ │ │ │ ├── CreateCarmis.php
│ │ │ │ ├── EditCarmis.php
│ │ │ │ └── ListCarmis.php
│ │ ├── Orders
│ │ │ └── Pages
│ │ │ │ ├── CreateOrder.php
│ │ │ │ ├── EditOrder.php
│ │ │ │ └── ListOrders.php
│ │ ├── Coupons
│ │ │ └── Pages
│ │ │ │ ├── CreateCoupon.php
│ │ │ │ ├── EditCoupon.php
│ │ │ │ └── ListCoupons.php
│ │ ├── Products
│ │ │ └── Pages
│ │ │ │ ├── CreateGoods.php
│ │ │ │ ├── EditGoods.php
│ │ │ │ └── ListGoods.php
│ │ ├── Categories
│ │ │ └── Pages
│ │ │ │ ├── CreateGoodsGroup.php
│ │ │ │ ├── EditGoodsGroup.php
│ │ │ │ └── ListGoodsGroups.php
│ │ ├── ArticleAdmin
│ │ │ └── Pages
│ │ │ │ ├── CreateArticle.php
│ │ │ │ ├── ViewArticle.php
│ │ │ │ ├── ListArticles.php
│ │ │ │ └── EditArticle.php
│ │ ├── EmailTemplates
│ │ │ └── Pages
│ │ │ │ ├── CreateEmailtpl.php
│ │ │ │ ├── ViewEmailtpl.php
│ │ │ │ ├── ListEmailtpls.php
│ │ │ │ └── EditEmailtpl.php
│ │ ├── ArticleCategoryResource
│ │ │ └── Pages
│ │ │ │ ├── CreateArticleCategory.php
│ │ │ │ ├── ViewArticleCategory.php
│ │ │ │ ├── ListArticleCategories.php
│ │ │ │ └── EditArticleCategory.php
│ │ ├── Users
│ │ │ └── Pages
│ │ │ │ ├── CreateUser.php
│ │ │ │ ├── EditUser.php
│ │ │ │ └── ListUsers.php
│ │ ├── UserLevels
│ │ │ └── Pages
│ │ │ │ ├── CreateUserLevel.php
│ │ │ │ ├── ListUserLevels.php
│ │ │ │ └── EditUserLevel.php
│ │ ├── Roles
│ │ │ └── Pages
│ │ │ │ ├── ListRoles.php
│ │ │ │ ├── CreateRole.php
│ │ │ │ └── EditRole.php
│ │ └── AdminUsers
│ │ │ └── Pages
│ │ │ ├── ListAdminUsers.php
│ │ │ ├── CreateAdminUser.php
│ │ │ └── EditAdminUser.php
│ ├── Widgets
│ │ ├── CustomFilamentInfoWidget.php
│ │ └── SalesOverviewWidget.php
│ └── Pages
│ │ └── Login.php
├── Exceptions
│ ├── AppException.php
│ ├── RuleValidationException.php
│ └── Handler.php
├── Services
│ ├── Contracts
│ │ ├── PaymentServiceInterface.php
│ │ └── OrderServiceInterface.php
│ ├── Coupons.php
│ ├── Cards.php
│ ├── ThemeService.php
│ ├── Payment.php
│ ├── ConfigService.php
│ └── Email.php
├── Http
│ ├── Middleware
│ │ ├── EncryptCookies.php
│ │ ├── CheckForMaintenanceMode.php
│ │ ├── TrimStrings.php
│ │ ├── PayGateWay.php
│ │ ├── Authenticate.php
│ │ ├── VerifyCsrfToken.php
│ │ ├── InstallCheck.php
│ │ ├── RedirectIfAuthenticated.php
│ │ ├── TrustProxies.php
│ │ ├── DujiaoBoot.php
│ │ └── Challenge.php
│ └── Controllers
│ │ ├── Controller.php
│ │ ├── BaseController.php
│ │ ├── Home
│ │ ├── ArticleController.php
│ │ └── CartController.php
│ │ └── InstallController.php
├── Providers
│ ├── SimpleThemeServiceProvider.php
│ ├── BroadcastServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── ConfigServiceProvider.php
│ ├── EventServiceProvider.php
│ ├── RouteServiceProvider.php
│ ├── PaymentServiceProvider.php
│ └── AppServiceProvider.php
├── Settings
│ ├── ThemeSettings.php
│ ├── MailSettings.php
│ ├── ShopSettings.php
│ └── SystemSettings.php
├── Models
│ ├── Emailtpl.php
│ ├── GoodsGroup.php
│ ├── BaseModel.php
│ ├── ArticleCategory.php
│ ├── RemoteServer.php
│ ├── Coupon.php
│ ├── OrderItem.php
│ ├── GoodsSub.php
│ ├── Carmis.php
│ ├── AdminUser.php
│ ├── Pay.php
│ └── Articles.php
├── Listeners
│ ├── GoodsDeleted.php
│ └── GoodsGroupDeleted.php
├── Rules
│ ├── SearchPwd.php
│ └── VerifyImg.php
├── Events
│ ├── GoodsDeleted.php
│ ├── OrderUpdated.php
│ └── GoodsGroupDeleted.php
├── Helpers
│ └── CdnHelper.php
├── Console
│ └── Kernel.php
├── Jobs
│ ├── CouponBack.php
│ ├── OrderExpired.php
│ ├── ServerJiang.php
│ ├── ApiHook.php
│ ├── MailSend.php
│ ├── TelegramPush.php
│ └── BarkPush.php
├── PaymentGateways
│ └── Contracts
│ │ └── PaymentDriverInterface.php
└── Config
│ └── ConfigManager.php
├── config
├── settings.php
├── dujiaoka.php
├── services.php
├── view.php
├── hashing.php
├── broadcasting.php
└── filesystems.php
├── routes
├── common
│ └── pay.php
├── channels.php
├── console.php
├── web.php
└── api.php
├── server.php
├── .env.default
├── index.html
├── package.json
├── LICENSE
├── phpunit.xml
├── artisan
└── composer.json
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/sass/app.scss:
--------------------------------------------------------------------------------
1 | //
2 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/resources/js/app.js:
--------------------------------------------------------------------------------
1 | require('./bootstrap');
2 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite
2 | *.sqlite-journal
3 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/resources/views/email/mail.blade.php:
--------------------------------------------------------------------------------
1 | {!! $body !!}
2 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/articles.php:
--------------------------------------------------------------------------------
1 | '文章链接'
4 | ];
--------------------------------------------------------------------------------
/resources/lang/zh_TW/articles.php:
--------------------------------------------------------------------------------
1 | '文章連結'
4 | ];
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
4 | Allow: /$
5 | Allow: /buy
6 | Allow: /article
--------------------------------------------------------------------------------
/resources/views/filament/pages/login.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/assets/morpho/banner/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/banner/1.webp
--------------------------------------------------------------------------------
/public/assets/morpho/banner/2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/banner/2.webp
--------------------------------------------------------------------------------
/public/assets/morpho/banner/3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/banner/3.webp
--------------------------------------------------------------------------------
/public/assets/common/images/3dian.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/3dian.png
--------------------------------------------------------------------------------
/public/assets/common/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/logo.png
--------------------------------------------------------------------------------
/public/assets/common/images/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/android.png
--------------------------------------------------------------------------------
/public/assets/common/images/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/banner.png
--------------------------------------------------------------------------------
/public/assets/common/images/default.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/default.jpg
--------------------------------------------------------------------------------
/public/assets/common/images/iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/common/images/iphone.png
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.css linguist-vendored
3 | *.scss linguist-vendored
4 | *.js linguist-vendored
5 | CHANGELOG.md export-ignore
6 |
--------------------------------------------------------------------------------
/public/assets/morpho/app-icons/icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/app-icons/icon-180x180.png
--------------------------------------------------------------------------------
/public/assets/morpho/app-icons/icon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/app-icons/icon-32x32.png
--------------------------------------------------------------------------------
/public/assets/morpho/icons/cartzilla-icons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/icons/cartzilla-icons.woff2
--------------------------------------------------------------------------------
/storage/framework/.gitignore:
--------------------------------------------------------------------------------
1 | config.php
2 | routes.php
3 | schedule-*
4 | compiled.php
5 | services.json
6 | events.scanned.php
7 | routes.scanned.php
8 | down
--------------------------------------------------------------------------------
/public/assets/morpho/fonts/inter-variable-latin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hiouttime/dujiaoka/HEAD/public/assets/morpho/fonts/inter-variable-latin.woff2
--------------------------------------------------------------------------------
/resources/views/themes/morpho/theme.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "morpho",
3 | "display_name": "morpho 霓虹主题",
4 | "version": "2.0.0",
5 | "author": "Riniba"
6 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM webdevops/php-nginx:7.4
2 | COPY . /app
3 | WORKDIR /app
4 | RUN [ "sh", "-c", "composer install --ignore-platform-reqs" ]
5 | RUN [ "sh", "-c", "chmod -R 777 /app" ]
6 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.2"
2 | services:
3 | web:
4 | build: .
5 | ports:
6 | - "80:80"
7 | - "9000:9000"
8 | environment:
9 | WEB_DOCUMENT_ROOT: "/app/public"
10 | tty: true
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /public/hot
2 | /public/storage
3 | /public/uploads
4 | public/vendor/dcat-admin-extensions
5 | /storage/*.key
6 | /vendor
7 | /dcat-admin-extensions
8 | install.lock
9 | .env
10 | .user.ini
11 | .htaccess
12 | .DS_Store
13 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Payments/Pages/CreatePay.php:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Servers/Pages/CreateRemoteServer.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Emailtpl' => '邮件模板',
6 | 'emailtpl' => '邮件模板',
7 | ],
8 | 'fields' => [
9 | 'tpl_name' => '邮件标题',
10 | 'tpl_content' => '邮件内容',
11 | 'tpl_token' => '邮件标识',
12 | ],
13 | 'options' => [
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/goods-group.php:
--------------------------------------------------------------------------------
1 | [
5 | 'GoodsGroup' => '分类',
6 | 'goods-group' => '分类',
7 | ],
8 | 'fields' => [
9 | 'gp_name' => '分类名称',
10 | 'is_open' => '是否启用',
11 | 'ord' => '排序权重 越大越靠前',
12 | ],
13 | 'options' => [
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/emailtpl.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Emailtpl' => '郵件模板',
6 | 'emailtpl' => '郵件模板',
7 | ],
8 | 'fields' => [
9 | 'tpl_name' => '郵件標題',
10 | 'tpl_content' => '郵件內容',
11 | 'tpl_token' => '郵件標識',
12 | ],
13 | 'options' => [
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/goods-group.php:
--------------------------------------------------------------------------------
1 | [
5 | 'GoodsGroup' => '分類',
6 | 'goods-group' => '分類',
7 | ],
8 | 'fields' => [
9 | 'gp_name' => '分類名稱',
10 | 'is_open' => '是否啟用',
11 | 'ord' => '排序權重 越大越靠前',
12 | ],
13 | 'options' => [
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/resources/views/admin/pages/manage-theme-settings.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ $this->form }}
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Coupons/Pages/CreateCoupon.php:
--------------------------------------------------------------------------------
1 | call(OrderTableSeeder::class);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Products/Pages/CreateGoods.php:
--------------------------------------------------------------------------------
1 | create();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/config/settings.php:
--------------------------------------------------------------------------------
1 | 'database',
5 |
6 | 'repositories' => [
7 | 'database' => [
8 | 'type' => Spatie\LaravelSettings\SettingsRepositories\DatabaseSettingsRepository::class,
9 | 'connection' => null,
10 | 'table' => 'settings',
11 | ],
12 | ],
13 | ];
--------------------------------------------------------------------------------
/app/Admin/Resources/Categories/Pages/CreateGoodsGroup.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
15 | }
16 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | app->singleton(ThemeService::class);
13 | }
14 |
15 | public function boot()
16 | {
17 | $this->app->make(ThemeService::class);
18 | }
19 | }
--------------------------------------------------------------------------------
/app/Settings/ThemeSettings.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
15 | }
16 | }
--------------------------------------------------------------------------------
/app/Admin/Resources/Roles/Pages/ListRoles.php:
--------------------------------------------------------------------------------
1 | [
5 | 'index' => '首頁',
6 | 'admin' => '系統',
7 | 'users' => '管理員',
8 | 'roles' => '角色',
9 | 'permission' => '權限',
10 | 'menu' => '菜單',
11 | 'operation_log' => '操作日誌',
12 | 'helpers' => '開發工具',
13 | 'extensions' => '擴展',
14 | 'scaffold' => '代碼生成器',
15 | 'icons' => '圖示',
16 | ],
17 | ];
18 |
--------------------------------------------------------------------------------
/app/Admin/Resources/AdminUsers/Pages/ListAdminUsers.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @include('morpho::layouts._header')
5 |
6 |
7 |
8 | @include('morpho::layouts._nav')
9 | @yield('content')
10 | @include('morpho::layouts._footer')
11 |
12 | @include('morpho::layouts._script')
13 | @include('morpho::layouts._cart_dropdown')
14 | @section('js')
15 | @show
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/Admin/Resources/ArticleAdmin/Pages/EditArticle.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright assimon
7 | * @link http://utf8.hk/
8 | */
9 | use Illuminate\Support\Facades\Route;
10 |
11 | // 支付网关入口
12 | Route::get('pay/{driver}/{payway}/{orderSN}', 'UnifiedPaymentController@gateway')
13 | ->middleware('dujiaoka.pay_gate_way');
14 |
15 | // 支付回调统一入口
16 | Route::post('pay/{driver}/notify', 'UnifiedPaymentController@notify');
17 | Route::get('pay/{driver}/return', 'UnifiedPaymentController@return');
18 |
--------------------------------------------------------------------------------
/app/Admin/Resources/ArticleCategoryResource/Pages/ViewArticleCategory.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Coupon' => '折扣碼',
6 | 'coupon' => '折扣碼',
7 | ],
8 | 'fields' => [
9 | 'type' => '折扣類型',
10 | 'discount' => '折扣金額/系数',
11 | 'is_open' => '是否啟用',
12 | 'coupon' => '折扣碼',
13 | 'ret' => '剩余使用次數',
14 | 'type_one_time' => '一次性使用',
15 | 'type_repeat' => '重復使用',
16 | 'type_percent' => '系数折扣 (0-1)',
17 | 'type_fixed' => '固定金额',
18 | 'goods_id' => '可用商品'
19 | ],
20 | 'options' => [
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/routes/channels.php:
--------------------------------------------------------------------------------
1 | id === (int) $id;
16 | });
17 |
--------------------------------------------------------------------------------
/app/Admin/Resources/ArticleCategoryResource/Pages/ListArticleCategories.php:
--------------------------------------------------------------------------------
1 | expectsJson()) {
18 | return route('login');
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Payments/Pages/EditPay.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Extensions' => '扩展',
6 | ],
7 | 'fields' => [
8 | 'alias' => '别名',
9 | 'description' => '描述',
10 | 'authors' => '开发者',
11 | 'homepage' => '主页',
12 | 'require' => '依赖',
13 | 'require_dev' => '开发环境依赖',
14 | 'name' => '包名',
15 | 'version' => '版本',
16 | 'enable' => '启用',
17 | 'config' => '配置',
18 | 'imported' => '导入',
19 | ],
20 | 'options' => [
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/extension.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Extensions' => '擴展',
6 | ],
7 | 'fields' => [
8 | 'alias' => '別名',
9 | 'description' => '說明',
10 | 'authors' => '作者',
11 | 'homepage' => '首頁',
12 | 'require' => '依賴',
13 | 'require_dev' => '開發環境依賴',
14 | 'name' => '名稱',
15 | 'version' => '版本',
16 | 'enable' => '啟用',
17 | 'config' => '設定',
18 | 'imported' => '導入',
19 | ],
20 | 'options' => [
21 | ],
22 | ];
23 |
--------------------------------------------------------------------------------
/config/dujiaoka.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright assimon
7 | * @link http://utf8.hk/
8 | */
9 |
10 | return [
11 | 'dujiaoka_version' => 'v3.0.0',
12 | // 模板集合
13 | 'templates' => [
14 | 'morpho' => 'morpho[Author: @Riniba]',
15 | ],
16 | // 语言
17 | 'language' => [
18 | 'zh_CN' => '简体中文',
19 | 'zh_TW' => '繁体中文',
20 | ],
21 | // 货币
22 | 'currencies' => [
23 | 'cny' => '人民币',
24 | 'usd' => '美元',
25 | ]
26 | ];
27 |
--------------------------------------------------------------------------------
/app/Services/Contracts/OrderServiceInterface.php:
--------------------------------------------------------------------------------
1 | '文章',
4 | 'total' => '文章列表',
5 | 'newest' => '最新文章',
6 | 'recommend' => '推薦文章',
7 | 'more' => '更多',
8 | 'labels' => [
9 | 'Article' => '文章管理',
10 | 'article' => '文章管理',
11 | ],
12 | 'fields' => [
13 | 'link' => '文章連結',
14 | 'title' => '文章標題',
15 | 'content' => '文章內容',
16 | 'category' => '文章分類'
17 | ],
18 | 'options' => [
19 | ],
20 | 'helps' => [
21 | 'link' => '設置訪問文章的連結,如果留空則會自動生成。',
22 | 'category' => '商品將能夠指派相同分類的文章,顯示在商品詳細頁。'
23 | ]
24 | ];
--------------------------------------------------------------------------------
/app/Admin/Resources/ArticleCategoryResource/Pages/EditArticleCategory.php:
--------------------------------------------------------------------------------
1 | '文章',
4 | 'total' => '文章列表',
5 | 'newest' => '最新文章',
6 | 'recommend' => '推荐文章',
7 | 'more' => '更多',
8 | 'labels' => [
9 | 'Article' => '文章管理',
10 | 'article' => '文章管理',
11 | ],
12 | 'fields' => [
13 | 'link' => '文章链接',
14 | 'title' => '文章标题',
15 | 'content' => '文章内容',
16 | 'category' => '文章分类'
17 | ],
18 | 'options' => [
19 | ],
20 | 'helps' => [
21 | 'link' => '设置访问文章的链接,如果留空则会自动生成。',
22 | 'category' => '商品将能够指派相同分类的文章,显示在商品详细页。'
23 | ]
24 | ];
25 |
--------------------------------------------------------------------------------
/resources/views/themes/morpho/views/layouts/_script.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{-- 页脚自定义代码 --}}
9 | @if(!empty(app(\App\Settings\ShopSettings::class)->footer))
10 | {!! app(\App\Settings\ShopSettings::class)->footer !!}
11 | @endif
12 |
--------------------------------------------------------------------------------
/resources/lang/en/pagination.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/theme-setting.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright outtime
7 | * @link https://outti.me
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'ThemeSetting' => '主題設置',
13 | 'theme_setting' => '主題設置',
14 | ],
15 | 'fields' => [
16 | 'notice' => '頂部輪播公告',
17 | 'invert_logo' => '圖片Logo顏色填充反轉'
18 | ],
19 | 'helps' => [
20 | 'notice' => '站點頂部輪播公告,一行一個;留空則不顯示。',
21 | 'invert_logo' => '使用 filter:brightness 來使Logo適配亮/暗色模式。'
22 | ],
23 | ];
--------------------------------------------------------------------------------
/routes/console.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
18 | })->describe('Display an inspiring quote');
19 |
--------------------------------------------------------------------------------
/app/Admin/Resources/UserLevels/Pages/EditUserLevel.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
23 | }
24 | }
--------------------------------------------------------------------------------
/resources/lang/zh_CN/theme-setting.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright outtime
7 | * @link https://outti.me
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'ThemeSetting' => '主题设置',
13 | 'theme_setting' => '主题设置',
14 | ],
15 | 'fields' => [
16 | 'notice' => '顶部轮播公告',
17 | 'invert_logo' => 'Logo适配模式'
18 | ],
19 | 'helps' => [
20 | 'notice' => '站点顶部轮播公告,一行一个;留空则不显示。',
21 | 'invert_logo' => '开启后,浅色模式下保持Logo原始亮度,深色模式下不应用滤镜。适用于深色Logo在不同主题下的显示适配。'
22 | ],
23 | ];
--------------------------------------------------------------------------------
/app/Settings/MailSettings.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright ZhangYiQiu
7 | * @link http://zhangyiqiu.net/
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'EmailTest' => '郵件測試',
13 | 'email-test' => '郵件測試',
14 | 'success' => '發送成功',
15 | 'to' => '收件人',
16 | 'title' => '郵件標題',
17 | 'body' => '郵件內容',
18 | 'send_test_email' => '發送測試郵件',
19 | ],
20 | 'fields' => [
21 |
22 | ],
23 | 'options' => [
24 | ],
25 | 'rule_messages' => [
26 | ]
27 | ];
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 |
10 | $uri = urldecode(
11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
12 | );
13 |
14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the
15 | // built-in PHP web server. This provides a convenient way to test a Laravel
16 | // application without having installed a "real" web server software here.
17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
18 | return false;
19 | }
20 |
21 | require_once __DIR__.'/public/index.php';
22 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/email-test.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright ZhangYiQiu
7 | * @link http://zhangyiqiu.net/
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'EmailTest' => '邮件测试',
13 | 'email-test' => '邮件测试',
14 | 'success' => '发送成功',
15 | 'to' => '收件人',
16 | 'title' => '邮件标题',
17 | 'body' => '邮件内容',
18 | 'send_test_email' => '发送测试邮件',
19 | ],
20 | 'fields' => [
21 |
22 | ],
23 | 'options' => [
24 | ],
25 | 'rule_messages' => [
26 | ]
27 | ];
28 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Users/Pages/EditUser.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
24 | }
25 | }
--------------------------------------------------------------------------------
/app/Http/Middleware/InstallCheck.php:
--------------------------------------------------------------------------------
1 | {this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default};
2 |
--------------------------------------------------------------------------------
/.env.default:
--------------------------------------------------------------------------------
1 | APP_NAME=独角数卡
2 | APP_ENV=local
3 | APP_KEY=base64:5w/wNQ0mfhDgd6xzUktH2RRh/yedU0HV0puVCjJN26o=
4 | APP_DEBUG=true
5 | APP_URL=http://localhost
6 |
7 | LOG_CHANNEL=stack
8 |
9 | DB_CONNECTION=mysql
10 | DB_HOST=127.0.0.1
11 | DB_PORT=3306
12 | DB_DATABASE=laravel
13 | DB_USERNAME=root
14 | DB_PASSWORD=
15 |
16 | REDIS_HOST=127.0.0.1
17 | REDIS_PASSWORD=null
18 | REDIS_PORT=6379
19 |
20 | BROADCAST_DRIVER=log
21 | SESSION_DRIVER=file
22 | SESSION_LIFETIME=120
23 |
24 | CACHE_DRIVER=file
25 |
26 | QUEUE_CONNECTION=sync
27 |
28 | # 后台语言
29 | ## zh_CN 简体中文
30 | ## zh_TW 繁体中文
31 | ## en 英文
32 | DUJIAO_ADMIN_LANGUAGE=zh_CN
33 |
34 | # 后台登录地址
35 | ADMIN_ROUTE_PREFIX=/admin
36 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | ['auth:api'],'namespace' => 'Api'], function () {
17 | Route::get('/user', function (Request $request) {
18 | return $request->user();
19 | });
20 | });
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Handle Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/resources/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/coupon.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Coupon' => '优惠码',
6 | 'coupon' => '优惠码',
7 | ],
8 | 'fields' => [
9 | 'type' => '优惠类型',
10 | 'discount' => '优惠金额/系数',
11 | 'is_open' => '是否启用',
12 | 'coupon' => '优惠码',
13 | 'ret' => '剩余使用次数',
14 | 'type_one_time' => '一次性使用',
15 | 'type_repeat' => '重复使用',
16 | 'type_percent' => '系数折扣 (0-1)',
17 | 'type_fixed' => '整体固定金额',
18 | 'type_each' => '每件固定金额',
19 | 'goods_id' => '可用商品'
20 | ],
21 | 'options' => [
22 | ],
23 | 'helps' =>[
24 | 'discount' => '系数折扣:价格在下单时会乘以这个数字,0.9就是九折。固定金额:下单时价格会直接减去。'
25 | ]
26 | ];
27 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Roles/Pages/CreateRole.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
16 | }
17 |
18 | protected function getCreatedNotification(): ?Notification
19 | {
20 | return Notification::make()
21 | ->success()
22 | ->title('角色创建成功')
23 | ->body('新的角色已经创建。');
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/app/Models/Emailtpl.php:
--------------------------------------------------------------------------------
1 | tpl_token);
22 | });
23 |
24 | static::deleted(function ($emailtpl) {
25 | CacheManager::forgetEmailTemplate($emailtpl->tpl_token);
26 | });
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Middleware/RedirectIfAuthenticated.php:
--------------------------------------------------------------------------------
1 | check()) {
22 | return redirect(RouteServiceProvider::HOME);
23 | }
24 |
25 | return $next($request);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | 'App\Policies\ModelPolicy',
17 | ];
18 |
19 | /**
20 | * Register any authentication / authorization services.
21 | *
22 | * @return void
23 | */
24 | public function boot()
25 | {
26 | $this->registerPolicies();
27 |
28 | //
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Admin/Resources/AdminUsers/Pages/CreateAdminUser.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
16 | }
17 |
18 | protected function getCreatedNotification(): ?Notification
19 | {
20 | return Notification::make()
21 | ->success()
22 | ->title('管理员创建成功')
23 | ->body('新的管理员账户已经创建。');
24 | }
25 | }
--------------------------------------------------------------------------------
/app/Listeners/GoodsDeleted.php:
--------------------------------------------------------------------------------
1 | where('goods_id', $event->goods->id)->delete();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/Models/GoodsGroup.php:
--------------------------------------------------------------------------------
1 | GoodsGroupDeleted::class
24 | ];
25 |
26 | /**
27 | * 关联商品
28 | *
29 | * @return \Illuminate\Database\Eloquent\Relations\HasMany
30 | *
31 | */
32 | public function goods()
33 | {
34 | return $this->hasMany(Goods::class, 'group_id');
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrustProxies.php:
--------------------------------------------------------------------------------
1 | where('group_id', $event->goodsGroup->id)->delete();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/Models/BaseModel.php:
--------------------------------------------------------------------------------
1 | __('dujiaoka.status_open'),
31 | self::STATUS_CLOSE => __('dujiaoka.status_close')
32 | ];
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/Models/ArticleCategory.php:
--------------------------------------------------------------------------------
1 | 'boolean',
19 | 'sort' => 'integer',
20 | ];
21 |
22 | /**
23 | * 关联文章
24 | */
25 | public function articles()
26 | {
27 | return $this->hasMany(Articles::class, 'category_id');
28 | }
29 |
30 | /**
31 | * 获取启用的分类
32 | */
33 | public static function active()
34 | {
35 | return self::where('is_active', true)->orderBy('sort', 'desc')->orderBy('id', 'asc');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/resources/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Your password has been reset!',
17 | 'sent' => 'We have e-mailed your password reset link!',
18 | 'throttled' => 'Please wait before retrying.',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that e-mail address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/app/Settings/ShopSettings.php:
--------------------------------------------------------------------------------
1 | e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{"x-on:blur":"createTag()","x-model":"newTag","x-on:keydown"(t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},"x-on:paste"(){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};
2 |
--------------------------------------------------------------------------------
/resources/views/themes/morpho/views/layouts/_cart_dropdown.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Roles/Pages/EditRole.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
24 | }
25 |
26 | protected function getSavedNotification(): ?Notification
27 | {
28 | return Notification::make()
29 | ->success()
30 | ->title('角色更新成功')
31 | ->body('角色信息已经更新。');
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/resources/lang/zh_TW/pay.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Pay' => '支付通道',
6 | 'pay' => '支付通道',
7 | ],
8 | 'fields' => [
9 | 'merchant_id' => '商戶 ID',
10 | 'merchant_key' => '商戶 KEY',
11 | 'merchant_pem' => '商戶金鑰',
12 | 'pay_check' => '支付標識',
13 | 'pay_client' => '支付場景',
14 | 'pay_handleroute' => '支付處理模組',
15 | 'pay_method' => '支付方式',
16 | 'pay_name' => '支付名稱',
17 | 'is_open' => '是否啟用',
18 | 'enable' => '是否啟用',
19 | 'pay_fee' => '通道費率',
20 | 'merchant_key_64' => '商戶密鑰(Base64)',
21 | 'china_only' => '僅允許中國大陸下單',
22 | 'method_jump' => '跳躍',
23 | 'method_scan' => '掃碼',
24 | 'pay_client_pc' => '計算機PC',
25 | 'pay_client_mobile' => '行動電話',
26 | 'pay_client_all' => '通用',
27 | ],
28 | 'options' => [
29 | ],
30 | ];
31 |
--------------------------------------------------------------------------------
/app/Admin/Resources/AdminUsers/Pages/EditAdminUser.php:
--------------------------------------------------------------------------------
1 | getResource()::getUrl('index');
24 | }
25 |
26 | protected function getSavedNotification(): ?Notification
27 | {
28 | return Notification::make()
29 | ->success()
30 | ->title('管理员更新成功')
31 | ->body('管理员信息已经更新。');
32 | }
33 | }
--------------------------------------------------------------------------------
/app/Models/RemoteServer.php:
--------------------------------------------------------------------------------
1 |
23 | * @copyright outtime
24 | * @link https://outti.me
25 | */
26 | public static function getServerTypeMap()
27 | {
28 | return [
29 | self::HTTP_SERVER => __('remote-server.types.http_server'),
30 | self::RCON_COMMAND => __('remote-server.types.rcon_command'),
31 | self::SQL_EXECUTE => __('remote-server.types.sql_execute'),
32 | ];
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/carmis.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Carmis' => '卡密',
6 | 'carmis' => '卡密',
7 | ],
8 | 'fields' => [
9 | 'goods_id' => '所屬商品',
10 | 'status' => '狀態',
11 | 'carmi' => '卡密內容',
12 | 'status_unsold' => '未售出',
13 | 'status_sold' => '已售出',
14 | 'is_loop' => '循環卡密',
15 | 'yes'=>'是',
16 | 'import_carmis' => '匯入卡密',
17 | 'carmis_list' => '卡密清單',
18 | 'carmis_txt' => '卡密文字',
19 | 'are_you_import_sure' => '確定要匯入卡密嗎?',
20 | 'remove_duplication' => '是否去重',
21 | ],
22 | 'options' => [
23 | ],
24 | 'helps' => [
25 | 'carmis_list' => '一行一個,輸入鍵分隔。請勿匯入單個文字長度過大的卡密,容易導致記憶體溢出。如果卡密過大建議修改商品為人工處理'
26 | ],
27 | 'rule_messages' => [
28 | 'carmis_list_and_carmis_txt_can_not_be_empty' => '請填寫需要匯入的卡密或選取需要上傳的卡密檔案',
29 | 'import_carmis_success' => '匯入卡密成功!'
30 | ]
31 | ];
32 |
--------------------------------------------------------------------------------
/app/Http/Middleware/DujiaoBoot.php:
--------------------------------------------------------------------------------
1 | setLocale($lang);
30 | } catch (\Exception $e) {
31 | app()->setLocale('zh_CN');
32 | }
33 |
34 | return $next($request);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 恭喜,站点创建成功!
6 |
27 |
28 |
29 |
30 |
恭喜, 站点创建成功!
31 |
这是默认index.html,本页面由系统自动生成
32 |
33 | - 本页面在FTP根目录下的index.html
34 | - 您可以修改、删除或覆盖本页面
35 | - FTP相关信息,请到“面板系统后台 > FTP” 查看
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/Models/Coupon.php:
--------------------------------------------------------------------------------
1 | belongsToMany(Goods::class, 'coupons_goods', 'coupons_id', 'goods_id');
27 | }
28 |
29 |
30 | public static function getTypeMap()
31 | {
32 | return [
33 | self::TYPE_PERCENT => __('coupon.fields.type_percent'),
34 | self::TYPE_FIXED => __('coupon.fields.type_fixed'),
35 | self::TYPE_EACH => __('coupon.fields.type_each'),
36 | ];
37 | }
38 |
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/resources/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | window._ = require('lodash');
2 |
3 | /**
4 | * We'll load the axios HTTP library which allows us to easily issue requests
5 | * to our Laravel back-end. This library automatically handles sending the
6 | * CSRF token as a header based on the value of the "XSRF" token cookie.
7 | */
8 |
9 | window.axios = require('axios');
10 |
11 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
12 |
13 | /**
14 | * Echo exposes an expressive API for subscribing to channels and listening
15 | * for events that are broadcast by Laravel. Echo and event broadcasting
16 | * allows your team to easily build robust real-time web applications.
17 | */
18 |
19 | // import Echo from 'laravel-echo';
20 |
21 | // window.Pusher = require('pusher-js');
22 |
23 | // window.Echo = new Echo({
24 | // broadcaster: 'pusher',
25 | // key: process.env.MIX_PUSHER_APP_KEY,
26 | // cluster: process.env.MIX_PUSHER_APP_CLUSTER,
27 | // forceTLS: true
28 | // });
29 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/pay.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Pay' => '支付通道',
6 | 'pay' => '支付通道',
7 | ],
8 | 'fields' => [
9 | 'merchant_id' => '商户 ID',
10 | 'merchant_key' => '商户 KEY',
11 | 'merchant_pem' => '商户密钥',
12 | 'merchant_key_64' => '商户密钥(Base64)',
13 | 'pay_check' => '支付标识',
14 | 'pay_client' => '支付场景',
15 | 'pay_fee' => '通道费率',
16 | 'pay_handleroute' => '支付处理模块',
17 | 'pay_method' => '支付方式',
18 | 'pay_name' => '支付名称',
19 | 'china_only' => '仅允许中国大陆下单',
20 | 'is_open' => '是否启用',
21 | 'enable' => '是否启用',
22 | 'method_jump' => '跳转',
23 | 'method_scan' => '扫码',
24 | 'pay_client_pc' => '电脑PC',
25 | 'pay_client_mobile' => '手机',
26 | 'pay_client_all' => '通用',
27 | ],
28 | 'options' => [
29 | ],
30 | 'helps' =>[
31 | 'pay_fee' => '单位百分比。如填写0.38,则代表0.38%的通道手续费,下单时价格会自动加上这笔手续费。'
32 | ]
33 | ];
34 |
--------------------------------------------------------------------------------
/app/Providers/ConfigServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(ConfigManager::class, function ($app) {
13 | return new ConfigManager();
14 | });
15 |
16 | $this->app->alias(ConfigManager::class, 'config.manager');
17 | }
18 |
19 | public function boot()
20 | {
21 | if (!function_exists('config_get')) {
22 | function config_get(string $key, $default = null)
23 | {
24 | return app('config.manager')->get($key, $default);
25 | }
26 | }
27 |
28 | if (!function_exists('config_set')) {
29 | function config_set(string $key, $value): void
30 | {
31 | app('config.manager')->set($key, $value);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/Rules/SearchPwd.php:
--------------------------------------------------------------------------------
1 | {let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};
2 |
--------------------------------------------------------------------------------
/app/Events/GoodsDeleted.php:
--------------------------------------------------------------------------------
1 | goods = $goods;
28 | }
29 |
30 | /**
31 | * Get the channels the event should broadcast on.
32 | *
33 | * @return \Illuminate\Broadcasting\Channel|array
34 | */
35 | public function broadcastOn()
36 | {
37 | return new PrivateChannel('channel-name');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Events/OrderUpdated.php:
--------------------------------------------------------------------------------
1 | order = $order;
28 | }
29 |
30 | /**
31 | * Get the channels the event should broadcast on.
32 | *
33 | * @return \Illuminate\Broadcasting\Channel|array
34 | */
35 | public function broadcastOn()
36 | {
37 | return new PrivateChannel('channel-name');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Helpers/CdnHelper.php:
--------------------------------------------------------------------------------
1 | cdn_url;
18 |
19 | // 如果没有配置CDN URL,直接返回原URL
20 | if (empty($cdnUrl)) {
21 | return $url;
22 | }
23 |
24 | // 移除CDN URL末尾的斜杠
25 | $cdnUrl = rtrim($cdnUrl, '/');
26 |
27 | // 如果是绝对URL且是本站资源,替换域名部分
28 | if (str_starts_with($url, request()->getSchemeAndHttpHost())) {
29 | return str_replace(request()->getSchemeAndHttpHost(), $cdnUrl, $url);
30 | }
31 |
32 | // 如果是相对URL,添加CDN域名
33 | if (str_starts_with($url, '/')) {
34 | return $cdnUrl . $url;
35 | }
36 |
37 | // 其他情况直接返回原URL
38 | return $url;
39 | }
40 | }
--------------------------------------------------------------------------------
/resources/views/themes/morpho/views/layouts/seo.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @include('morpho::layouts._header')
5 | @if(isset($gd_keywords))
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | @endif
14 |
15 |
16 |
17 | @include('morpho::layouts._nav')
18 | @yield('content')
19 | @include('morpho::layouts._footer')
20 |
21 | @include('morpho::layouts._script')
22 | @include('morpho::layouts._cart_dropdown')
23 | @section('js')
24 | @show
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | command('inspire')
28 | // ->hourly();
29 | }
30 |
31 | /**
32 | * Register the commands for the application.
33 | *
34 | * @return void
35 | */
36 | protected function commands()
37 | {
38 | $this->load(__DIR__.'/Commands');
39 |
40 | require base_path('routes/console.php');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "npm run development",
5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js",
6 | "watch": "npm run development -- --watch",
7 | "watch-poll": "npm run watch -- --watch-poll",
8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js",
9 | "prod": "npm run production",
10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
11 | },
12 | "devDependencies": {
13 | "axios": "^0.19",
14 | "cross-env": "^7.0",
15 | "laravel-mix": "^5.0.1",
16 | "lodash": "^4.17.19",
17 | "resolve-url-loader": "^3.1.0",
18 | "sass": "^1.15.2",
19 | "sass-loader": "^8.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Events/GoodsGroupDeleted.php:
--------------------------------------------------------------------------------
1 | goodsGroup = $goodsGroup;
28 | }
29 |
30 | /**
31 | * Get the channels the event should broadcast on.
32 | *
33 | * @return \Illuminate\Broadcasting\Channel|array
34 | */
35 | public function broadcastOn()
36 | {
37 | return new PrivateChannel('channel-name');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Models/OrderItem.php:
--------------------------------------------------------------------------------
1 | 'decimal:2',
26 | 'subtotal' => 'decimal:2',
27 | 'quantity' => 'integer',
28 | 'type' => 'integer',
29 | ];
30 |
31 | public function order(): BelongsTo
32 | {
33 | return $this->belongsTo(Order::class);
34 | }
35 |
36 | public function goods(): BelongsTo
37 | {
38 | return $this->belongsTo(Goods::class);
39 | }
40 |
41 | public function goodsSub(): BelongsTo
42 | {
43 | return $this->belongsTo(GoodsSub::class, 'sub_id');
44 | }
45 | }
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'domain' => env('MAILGUN_DOMAIN'),
19 | 'secret' => env('MAILGUN_SECRET'),
20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
21 | ],
22 |
23 | 'postmark' => [
24 | 'token' => env('POSTMARK_TOKEN'),
25 | ],
26 |
27 | 'ses' => [
28 | 'key' => env('AWS_ACCESS_KEY_ID'),
29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
31 | ],
32 |
33 | ];
34 |
--------------------------------------------------------------------------------
/app/Models/GoodsSub.php:
--------------------------------------------------------------------------------
1 | belongsTo(Goods::class);
25 | }
26 |
27 | /**
28 | * 自动发货自动计算库存
29 | *
30 | * @author outtime
31 | * @copyright outtime
32 | * @link https://outti.me
33 | */
34 | public function getStockAttribute()
35 | {
36 |
37 | if ($this->goods->type == self::AUTOMATIC_DELIVERY) {
38 | return Carmis::where('sub_id', $this->id)
39 | ->where('status', Carmis::STATUS_UNSOLD)
40 | ->count();
41 | }
42 | return $this->attributes['stock'];
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 assimon/dujiaoka
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
17 | resource_path('views'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Compiled View Path
23 | |--------------------------------------------------------------------------
24 | |
25 | | This option determines where all the compiled Blade templates will be
26 | | stored for your application. Typically, this is within the storage
27 | | directory. However, as usual, you are free to change this value.
28 | |
29 | */
30 |
31 | 'compiled' => env(
32 | 'VIEW_COMPILED_PATH',
33 | realpath(storage_path('framework/views'))
34 | ),
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/app/Admin/Pages/Login.php:
--------------------------------------------------------------------------------
1 | schema([
16 | TextInput::make('username')
17 | ->label('用户名')
18 | ->required()
19 | ->autocomplete('username'),
20 |
21 | $this->getPasswordFormComponent(),
22 | $this->getRememberFormComponent(),
23 | ])
24 | ->statePath('data');
25 | }
26 |
27 | protected function getCredentialsFromFormData(array $data): array
28 | {
29 | return [
30 | 'username' => $data['username'],
31 | 'password' => $data['password'],
32 | ];
33 | }
34 |
35 | protected function throwFailureValidationException(): never
36 | {
37 | throw ValidationException::withMessages([
38 | 'data.username' => '登录信息有误。',
39 | ]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/order.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Order' => '訂單',
6 | 'order' => '訂單',
7 | ],
8 | 'fields' => [
9 | 'actual_price' => '實際支付價格',
10 | 'buy_amount' => '購買數量',
11 | 'buy_ip' => '購買者下單IP地址',
12 | 'coupon_discount_price' => '折扣碼折扣價格',
13 | 'coupon_id' => '折扣碼',
14 | 'email' => '下單信箱',
15 | 'goods_id' => '所屬商品',
16 | 'goods_price' => '商品單價',
17 | 'info' => '訂單詳情',
18 | 'order_sn' => '訂單號',
19 | 'pay_id' => '支付通道',
20 | 'status' => '訂單狀態',
21 | 'search_pwd' => '查詢密碼',
22 | 'title' => '訂單名稱',
23 | 'total_price' => '訂單總價',
24 | 'trade_no' => '第三方支付訂單號',
25 | 'type' => '訂單類型',
26 | 'wholesale_discount_price' => '批發價折扣',
27 | 'status_wait_pay' => '待支付',
28 | 'status_pending' => '待處理',
29 | 'status_processing' => '處理中',
30 | 'status_completed' => '已完成',
31 | 'status_failure' => '失敗',
32 | 'status_abnormal' => '異常',
33 | 'status_expired' => '已逾期',
34 | 'order_created' => '訂單建立時間',
35 | 'order_detail' => '訂單詳情',
36 | ],
37 | 'options' => [
38 | ],
39 | ];
40 |
--------------------------------------------------------------------------------
/app/Admin/Resources/Users/Pages/ListUsers.php:
--------------------------------------------------------------------------------
1 | Tab::make('全部用户'),
27 | 'active' => Tab::make('正常用户')
28 | ->modifyQueryUsing(fn (Builder $query) => $query->where('status', User::STATUS_ACTIVE)),
29 | 'disabled' => Tab::make('禁用用户')
30 | ->modifyQueryUsing(fn (Builder $query) => $query->where('status', User::STATUS_DISABLED)),
31 | 'vip' => Tab::make('VIP用户')
32 | ->modifyQueryUsing(fn (Builder $query) => $query->whereHas('level', fn($q) => $q->where('min_spent', '>', 0))),
33 | ];
34 | }
35 | }
--------------------------------------------------------------------------------
/resources/lang/zh_CN/remote-server.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright outtime
7 | * @link https://outti.me
8 | */
9 |
10 | return [
11 | "types" => [
12 | "http_server" => "HTTP请求",
13 | "rcon_command" => "RCON执行",
14 | "sql_execute" => "Mysql执行"
15 | ],
16 | 'labels' => [
17 | 'RemoteServer' => '回调服务器',
18 | 'remote_server' => '回调服务器',
19 | ],
20 | "fields" => [
21 | "name" => "服务器名称",
22 | "type" => "回调类型",
23 | "url" => "URL地址",
24 | "host" => "主机地址",
25 | "port" => "端口",
26 | "username" => "用户名",
27 | "password" => "密码",
28 | "database" => "数据库名",
29 | "command" => "执行命令",
30 | "headers" => "请求头",
31 | "body" => "请求体",
32 | "is_active" => "是否启用",
33 | "description" => "描述",
34 | ],
35 | "helps" => [
36 | "command" => "要执行的命令,支持变量替换",
37 | "headers" => "HTTP请求头,格式:key: value",
38 | "body" => "HTTP请求体,支持JSON格式",
39 | "type" => "您可以向您的应用开发者了解应该选择何种回调服务器",
40 | ],
41 | "you_can_use" => "你可以在执行的命令中使用如下变量:"
42 | ];
--------------------------------------------------------------------------------
/resources/lang/zh_TW/remote-server.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright outtime
7 | * @link https://outti.me
8 | */
9 |
10 | return [
11 | "types" => [
12 | "http_server" => "HTTP請求",
13 | "rcon_command" => "RCON執行",
14 | "sql_execute" => "Mysql執行"
15 | ],
16 | 'labels' => [
17 | 'RemoteServer' => '回調服務器',
18 | 'remote_server' => '回調服務器',
19 | ],
20 | "fields" => [
21 | "name" => "服務器名稱",
22 | "type" => "回調類型",
23 | "url" => "URL地址",
24 | "host" => "主機地址",
25 | "port" => "端口",
26 | "username" => "用戶名",
27 | "password" => "密碼",
28 | "database" => "數據庫名",
29 | "command" => "執行命令",
30 | "headers" => "請求頭",
31 | "body" => "請求體",
32 | "is_active" => "是否啟用",
33 | "description" => "描述",
34 | ],
35 | "helps" => [
36 | "command" => "要執行的命令,支持變量替換",
37 | "headers" => "HTTP請求頭,格式:key: value",
38 | "body" => "HTTP請求體,支持JSON格式",
39 | "type" => "您可以向您的應用開發者了解應該選擇何種回調服務器",
40 | ],
41 | "you_can_use" => "你可以在執行的命令中使用如下變量:"
42 | ];
--------------------------------------------------------------------------------
/resources/lang/zh_CN/menu.php:
--------------------------------------------------------------------------------
1 | [
5 | 'index' => '主页',
6 | 'admin' => '系统',
7 | 'users' => '管理员',
8 | 'roles' => '角色',
9 | 'permission' => '权限',
10 | 'menu' => '菜单',
11 | 'operation_log' => '操作日志',
12 | 'helpers' => '开发工具',
13 | 'extensions' => '扩展',
14 | 'scaffold' => '代码生成器',
15 | 'icons' => '图标',
16 | 'goods_manage' => '商品管理',
17 | 'goods' => '商品列表',
18 | 'goods_group' => '商品分类',
19 | 'carmis_manage' => '卡密管理',
20 | 'carmis' => '卡密列表',
21 | 'import_carmis' => '导入卡密',
22 | 'coupon_manage' => '优惠管理',
23 | 'coupon' => '优惠码列表',
24 | 'article_manage' => '文章管理',
25 | 'configuration' => '配置',
26 | 'email_template_configuration' => '邮件模板配置',
27 | 'pay_configuration' => '支付配置',
28 | 'order_manage' => '销售数据',
29 | 'order' => '订单列表',
30 | 'system_setting' => '系统设置',
31 | 'email_test' => '邮件测试',
32 | 'remote_server' => '回调服务器',
33 | 'theme_setting' => '主题设置',
34 | ],
35 | ];
36 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | ./tests/Unit
9 |
10 |
11 |
12 | ./tests/Feature
13 |
14 |
15 |
16 |
17 | ./app
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/Jobs/CouponBack.php:
--------------------------------------------------------------------------------
1 | order = $order;
40 | }
41 |
42 | /**
43 | * Execute the job.
44 | *
45 | * @return void
46 | */
47 | public function handle()
48 | {
49 | // 如果订单有使用优惠码
50 | if ($this->order->coupon_id) {
51 | // 优惠码次数+1
52 | app('App\Services\Coupons')->retIncrByID($this->order->coupon_id);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/Settings/SystemSettings.php:
--------------------------------------------------------------------------------
1 | [
5 | 'id' => 'ID',
6 | 'name' => '名称',
7 | 'username' => '用户名',
8 | 'email' => '邮箱',
9 | 'http_path' => 'HTTP路径',
10 | 'password' => '密码',
11 | 'password_confirmation' => '确认密码',
12 | 'created_at' => '创建时间',
13 | 'updated_at' => '更新时间',
14 | 'permissions' => '权限',
15 | 'slug' => '标识',
16 | 'user' => '用户',
17 | 'order' => '排序',
18 | 'ip' => 'IP',
19 | 'method' => '方法',
20 | 'uri' => 'URI',
21 | 'roles' => '角色',
22 | 'path' => '路径',
23 | 'input' => '输入',
24 | 'type' => '类型',
25 | ],
26 | 'labels' => [
27 | 'list' => '列表',
28 | 'edit' => '编辑',
29 | 'detail' => '详细',
30 | 'create' => '创建',
31 | 'root' => '顶级',
32 | 'scaffold' => '代码生成器',
33 | ],
34 | 'options' => [
35 | //
36 | ],
37 | ];
38 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/luna.php:
--------------------------------------------------------------------------------
1 | '选择分类',
7 | 'home_choice_goods' => '选择商品',
8 | 'order_search_m' => '查单',
9 | 'goods_num' => '商品数量',
10 | 'goods_disc_1' => '满',
11 | 'goods_disc_2' => '个 单价',
12 | 'goods_disc_3' => '元/件',
13 | 'goods_surplus' => '剩余',
14 | 'goods_unit' => '件',
15 |
16 | 'buy_goods_msg' => '商品详情',
17 | 'buy_num' => '购买数量',
18 | 'buy_email' => '邮 箱',
19 | 'buy_email_tips' => '填写你的邮箱',
20 | 'buy_disc' => '优 惠 码',
21 | 'buy_disc_tips' => '填写你的优惠码',
22 | 'buy_pass' => '查询密码',
23 | 'buy_pass_tips' => '请填写订单的查询密码',
24 | 'buy_code' => '验 证 码',
25 | 'buy_code_tips' => '请输入验证码',
26 | 'buy_loading_verification' => '正在加载验证码...',
27 | 'buy_order_m' => '手机下单',
28 |
29 | 'query_no_order' => '没有找到订单信息。',
30 | 'query_no_order_tips' => '或许可以检查核对下输入的订单号,邮箱,或者查询密码是否正确哦!!',
31 |
32 | 'order_number' => '订单编号',
33 | 'email' => '订单邮箱',
34 | 'order_status' => '订单状态',
35 |
36 |
37 | 'least_one' => '购买数量不能低于1件',
38 | 'exceeds' => '购买数量不能多于库存数量',
39 | 'exceeds_limit' => '购买数量不能多于限购数量',
40 | 'mobile_order' => '扫描二维码 手机下单'
41 | ];
42 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/luna.php:
--------------------------------------------------------------------------------
1 | '選取分類',
7 | 'home_choice_goods' => '選取商品',
8 | 'order_search_m' => '查單',
9 | 'goods_num' => '商品數量',
10 | 'goods_disc_1' => '滿',
11 | 'goods_disc_2' => '個 單價',
12 | 'goods_disc_3' => '元/件',
13 | 'goods_surplus' => '剩余',
14 | 'goods_unit' => '件',
15 |
16 | 'buy_goods_msg' => '商品詳情',
17 | 'buy_num' => '購買數量',
18 | 'buy_email' => '信 箱',
19 | 'buy_email_tips' => '填寫你的信箱',
20 | 'buy_disc' => '折 扣 碼',
21 | 'buy_disc_tips' => '填寫你的折扣碼',
22 | 'buy_pass' => '查詢密碼',
23 | 'buy_pass_tips' => '請填寫訂單的查詢密碼',
24 | 'buy_code' => '驗 證 碼',
25 | 'buy_code_tips' => '請輸入驗證碼',
26 | 'buy_loading_verification' => '正在加載驗證碼...',
27 | 'buy_order_m' => '行動電話下單',
28 |
29 | 'query_no_order' => '沒有找到訂單資訊。',
30 | 'query_no_order_tips' => '或許可以檢查核實下輸入的訂單號,信箱,或者查詢密碼是否正確哦!!',
31 |
32 | 'order_number' => '訂單編號',
33 | 'email' => '訂單信箱',
34 | 'order_status' => '訂單狀態',
35 |
36 |
37 | 'least_one' => '購買數量不能低於1件',
38 | 'exceeds' => '購買數量不能多於庫存數量',
39 | 'exceeds_limit' => '購買數量不能多於限購數量',
40 | 'mobile_order' => '掃描QRcode 行動電話下單'
41 | ];
42 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
22 | SendEmailVerificationNotification::class,
23 | ],
24 | GoodsGroupDeleted::class => [
25 | \App\Listeners\GoodsGroupDeleted::class,
26 | ],
27 | GoodsDeleted::class => [
28 | \App\Listeners\GoodsDeleted::class,
29 | ],
30 | OrderUpdated::class => [
31 | \App\Listeners\OrderUpdated::class,
32 | ],
33 | ];
34 |
35 | /**
36 | * Register any events for your application.
37 | *
38 | * @return void
39 | */
40 | public function boot()
41 | {
42 | parent::boot();
43 | //
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/public/web.config:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/order.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Order' => '订单',
6 | 'order' => '订单',
7 | ],
8 | 'fields' => [
9 | 'actual_price' => '实际支付价格',
10 | 'buy_amount' => '购买数量',
11 | 'buy_ip' => '购买者下单IP地址',
12 | 'coupon_discount_price' => '优惠码优惠价格',
13 | 'coupon_id' => '优惠码',
14 | 'preselection' => '自选卡密',
15 | 'email' => '下单邮箱',
16 | 'goods_id' => '所属商品',
17 | 'sub_id' => '所属规格',
18 | 'goods_price' => '商品单价',
19 | 'info' => '订单详情',
20 | 'order_id' => '订单ID',
21 | 'order_sn' => '订单号',
22 | 'pay_id' => '支付通道',
23 | 'status' => '订单状态',
24 | 'search_pwd' => '查询密码',
25 | 'title' => '订单名称',
26 | 'total_price' => '订单总价',
27 | 'trade_no' => '第三方支付订单号',
28 | 'type' => '订单类型',
29 | 'wholesale_discount_price' => '批发价优惠',
30 | 'status_wait_pay' => '待支付',
31 | 'status_pending' => '待处理',
32 | 'status_processing' => '处理中',
33 | 'status_completed' => '已完成',
34 | 'status_failure' => '失败',
35 | 'status_abnormal' => '异常',
36 | 'status_expired' => '已过期',
37 | 'order_created' => '订单创建时间',
38 | 'order_detail' => '订单详情',
39 | ],
40 | 'options' => [
41 | ],
42 | ];
43 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/global.php:
--------------------------------------------------------------------------------
1 | [
5 | 'id' => 'ID',
6 | 'name' => '名稱',
7 | 'username' => '用戶名',
8 | 'email' => '信箱',
9 | 'http_path' => 'HTTP路徑',
10 | 'password' => '密碼',
11 | 'password_confirmation' => '確認密碼',
12 | 'created_at' => '建立時間',
13 | 'updated_at' => '更新時間',
14 | 'permissions' => '權限',
15 | 'slug' => '標示',
16 | 'user' => '用戶',
17 | 'order' => '排序',
18 | 'ip' => 'IP',
19 | 'method' => '方法',
20 | 'uri' => 'URI',
21 | 'roles' => '角色',
22 | 'path' => '路徑',
23 | 'input' => '輸入',
24 | 'type' => '类型',
25 | ],
26 | 'labels' => [
27 | 'list' => '列表',
28 | 'edit' => '編輯',
29 | 'detail' => '詳細',
30 | 'create' => '創建',
31 | 'root' => 'root',
32 | 'scaffold' => '代碼生成器',
33 | ],
34 |
35 | 'options' => [
36 | 'permissions' => [
37 |
38 | ],
39 | ],
40 |
41 | ];
42 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/carmis.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Carmis' => '卡密',
6 | 'carmis' => '卡密',
7 | ],
8 | 'fields' => [
9 | 'goods_id' => '所属商品',
10 | 'status' => '状态',
11 | 'carmi' => '卡密内容',
12 | 'info' => '卡密说明',
13 | 'info_preg' => '卡密说明正则/分隔符',
14 | 'status_unsold' => '未售出',
15 | 'status_sold' => '已售出',
16 | 'is_loop' => '循环卡密',
17 | 'yes' => '是',
18 | 'no' => '否',
19 | 'import_carmis' => '导入卡密',
20 | 'carmis_list' => '卡密列表',
21 | 'carmis_txt' => '卡密文本',
22 | 'are_you_import_sure' => '确定要导入卡密吗?',
23 | 'remove_duplication' => '是否去重',
24 | 'sub_id' => '对应规格'
25 | ],
26 | 'options' => [
27 | 'non_sub' => '默认规格'
28 | ],
29 | 'helps' => [
30 | 'carmis_list' => '一行一个,回车分隔。请勿导入单个文本长度过大的卡密,容易导致内存溢出。如果卡密过大建议修改商品为人工处理',
31 | 'carmis_info' => '若商品支持自选,卡密信息将会展示在购买页供顾客自行选择',
32 | 'info_preg' => '支持正则表达式(优先判定)或用分隔符来筛选卡密。
正则表达式将会将符合条件的第一个字符串作为卡密信息。
分隔符将会使用最后一个字符串作为卡密信息,如c1---c2---c3,使用---作为分隔符,c3会作为卡密信息。',
33 | ],
34 | 'rule_messages' => [
35 | 'carmis_list_and_carmis_txt_can_not_be_empty' => '请填写需要导入的卡密或选择需要上传的卡密文件',
36 | 'import_carmis_success' => '导入卡密成功!'
37 | ]
38 | ];
39 |
--------------------------------------------------------------------------------
/database/factories/OrderFactory.php:
--------------------------------------------------------------------------------
1 | define(\App\Models\Order::class, function (Faker $faker) {
10 | return [
11 | 'order_sn' => strtoupper(Str::random(12)),
12 | 'goods_id' => rand(1, 3),
13 | 'coupon_id' => rand(1, 3),
14 | 'title' => $faker->words(3, true),
15 | 'type' => rand(1,2),
16 | 'goods_price' => $faker->randomFloat(2, 10, 100),
17 | 'buy_amount' => rand(1, 10),
18 | 'coupon_discount_price' => $faker->randomFloat(2, 0, 100),
19 | 'wholesale_discount_price' => $faker->randomFloat(2, 0, 100),
20 | 'total_price' => $faker->randomFloat(2, 10, 100),
21 | 'actual_price' => $faker->randomFloat(2, 10, 100),
22 | 'search_pwd' => $faker->password(6, 10),
23 | 'email' => $faker->email,
24 | 'info' => $faker->words(3, true),
25 | 'pay_id' => rand(1, 20),
26 | 'buy_ip' => $faker->ipv4,
27 | 'trade_no' => strtoupper(Str::random(12)),
28 | 'status' => rand(1, 5),
29 | 'created_at' => $faker->dateTimeBetween('-7 days', 'now', 'PRC'),
30 | 'updated_at' => $faker->dateTimeBetween('-7 days', 'now', 'PRC'),
31 | ];
32 | });
33 |
--------------------------------------------------------------------------------
/app/Services/Coupons.php:
--------------------------------------------------------------------------------
1 | whereHas('goods', function ($query) use ($goodsID) {
26 | $query->where('goods_id', $goodsID);
27 | })->where('is_open', Coupon::STATUS_OPEN)->where('coupon', $coupon)->first();
28 | return $coupon;
29 | }
30 |
31 | /**
32 | * 设置优惠券使用次数 -1
33 | * @param string $coupon
34 | * @return int
35 | *
36 | */
37 | public function retDecr(string $coupon)
38 | {
39 | return Coupon::query()
40 | ->where('coupon', $coupon)
41 | ->decrement('ret', 1);
42 | }
43 |
44 | /**
45 | * 设置优惠券次数+1
46 | *
47 | * @param int $id
48 | * @return int
49 | *
50 | */
51 | public function retIncrByID(int $id)
52 | {
53 | return Coupon::query()->where('id', $id)->increment('ret', 1);
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/goods.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Goods' => '商品',
6 | 'goods' => '商品',
7 | ],
8 | 'fields' => [
9 | 'sell_price' => '售價',
10 | 'group_id' => '所屬分類',
11 | 'api_hook' => '回調事件',
12 | 'buy_prompt' => '購買提示',
13 | 'description' => '商品描述',
14 | 'gd_name' => '商品名稱',
15 | 'gd_description' => '商品描述',
16 | 'gd_keywords' => '商品關鍵字',
17 | 'in_stock' => '庫存',
18 | 'ord' => '排序權重',
19 | 'other_ipu_cnf' => '其他輸入框配置',
20 | 'picture' => '商品圖片',
21 | 'sales_volume' => '銷量',
22 | 'type' => '商品類型',
23 | 'buy_limit_num' => '限製單次購買最大數量',
24 | 'wholesale_price_cnf' => '批發價配置',
25 | 'automatic_delivery' => '自動發貨',
26 | 'manual_processing' => '人工處理',
27 | 'is_open' => '是否上架',
28 | 'coupon_id' => '可用折扣碼'
29 | ],
30 | 'options' => [
31 | ],
32 | 'helps' => [
33 | 'picture' => '可不上傳,為預設圖片',
34 | 'in_stock' => '當商品類型為"人工處理"時,手動填寫的庫存數量才會生效。"自動發貨"類型的商品系統會自動識別庫存數量',
35 | 'buy_limit_num' => '防止惡意刷庫存,0為不限製客戶單次下單最大數量',
36 | 'other_ipu_cnf' => '格式為[唯一標識(英文)=輸入框名字=是否必填],例如:填寫 line_account=Line賬戶=true 表示產品詳情頁會新增一個 [Line賬戶] 輸入框,客戶可在其中輸入 [Line賬戶],true 為必填,false 為選填。(一行一個)',
37 | 'wholesale_price_cnf' => '例如:填寫 5=3 表示客戶購買 5 件或以上時,每件價格為 3 元。一行一個',
38 |
39 | ]
40 | ];
41 |
--------------------------------------------------------------------------------
/app/Models/Carmis.php:
--------------------------------------------------------------------------------
1 | __('carmis.fields.status_unsold'),
40 | self::STATUS_SOLD => __('carmis.fields.status_sold')
41 | ];
42 | }
43 |
44 | /**
45 | * 关联商品规格
46 | *
47 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
48 | */
49 | public function goodsSub()
50 | {
51 | return $this->belongsTo(GoodsSub::class, 'sub_id');
52 | }
53 |
54 | /**
55 | * 关联商品(通过规格)
56 | *
57 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
58 | */
59 | public function goods()
60 | {
61 | return $this->belongsTo(Goods::class, 'goods_id')->withDefault(function () {
62 | return $this->goodsSub?->goods;
63 | });
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/PaymentGateways/Contracts/PaymentDriverInterface.php:
--------------------------------------------------------------------------------
1 | where('goods_id', $goodsID)
27 | ->where('sub_id', $sub_id)
28 | ->where('status', Carmis::STATUS_UNSOLD)
29 | ->take($byAmount)
30 | ->get();
31 | return $carmis ? $carmis->toArray() : null;
32 | }
33 |
34 | /**
35 | * 通过卡密ID获得卡密
36 | *
37 | * @param int $id 卡密id
38 | * @return string|null
39 | *
40 | * @author outtime
41 | * @copyright outtime
42 | * @link https://outti.me
43 | */
44 | public function getCarmiById(int $id){
45 | return Carmis::find($id);
46 | }
47 |
48 | /**
49 | * 通过id集合设置卡密已售出
50 | *
51 | * @param array $ids 卡密id集合
52 | * @return bool
53 | *
54 | */
55 | public function soldByIDS(array $ids): bool
56 | {
57 | return Carmis::query()->whereIn('id', $ids)->where('is_loop', 0)->update(['status' => Carmis::STATUS_SOLD]);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/app/Http/Controllers/BaseController.php:
--------------------------------------------------------------------------------
1 | with('page_title', $pageTitle);
28 | }
29 |
30 | /**
31 | * 错误提示
32 | *
33 | * @param string $content 提示内容
34 | * @param string $jumpUri 跳转url
35 | * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
36 | *
37 | */
38 | protected function err(string $content, $jumpUri = '')
39 | {
40 | $theme = current_theme();
41 | return view("{$theme}::errors/error", [
42 | 'title' => __('dujiaoka.error_title'),
43 | 'content' => $content,
44 | 'url' => $jumpUri
45 | ])->with('page_title', __('dujiaoka.error_title'));
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/Services/ThemeService.php:
--------------------------------------------------------------------------------
1 | currentTheme = cfg('template', 'morpho');
15 | $this->registerViews();
16 | }
17 |
18 | public function getCurrentTheme(): string
19 | {
20 | return $this->currentTheme;
21 | }
22 |
23 | /**
24 | * 注册主题视图路径
25 | */
26 | protected function registerViews(): void
27 | {
28 | $themePath = resource_path("views/themes/{$this->currentTheme}/views");
29 |
30 | if (is_dir($themePath)) {
31 | View::addNamespace($this->currentTheme, $themePath);
32 | }
33 | }
34 |
35 | /**
36 | * 获取主题配置值
37 | */
38 | public function getConfig(string $key, $default = null)
39 | {
40 | return Cache::get("theme.{$this->currentTheme}.{$key}", $default);
41 | }
42 |
43 | /**
44 | * 设置主题配置值
45 | */
46 | public function setConfig(string $key, $value): void
47 | {
48 | Cache::put("theme.{$this->currentTheme}.{$key}", $value);
49 | }
50 |
51 | /**
52 | * 获取主题资源URL
53 | */
54 | public function asset(string $path): string
55 | {
56 | $url = "/assets/{$this->currentTheme}/{$path}";
57 | return \App\Helpers\CdnHelper::asset($url);
58 | }
59 | }
--------------------------------------------------------------------------------
/app/Models/AdminUser.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | protected $fillable = [
29 | 'name',
30 | 'username',
31 | 'password',
32 | ];
33 |
34 | /**
35 | * The attributes that should be hidden for serialization.
36 | *
37 | * @var array
38 | */
39 | protected $hidden = [
40 | 'password',
41 | 'remember_token',
42 | ];
43 |
44 | /**
45 | * Get the attributes that should be cast.
46 | *
47 | * @return array
48 | */
49 | protected function casts(): array
50 | {
51 | return [
52 | 'password' => 'hashed',
53 | ];
54 | }
55 |
56 | /**
57 | * Determine if the user can access the Filament admin panel.
58 | */
59 | public function canAccessPanel(Panel $panel): bool
60 | {
61 | return true;
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 | , \Psr\Log\LogLevel::*>
10 | */
11 | protected $levels = [
12 | //
13 | ];
14 | /**
15 | * A list of the exception types that are not reported.
16 | *
17 | * @var array>
18 | */
19 | protected $dontReport = [
20 | //
21 | ];
22 | /**
23 | * A list of the inputs that are never flashed to the session on validation exceptions.
24 | *
25 | * @var array
26 | */
27 | protected $dontFlash = [
28 | 'current_password',
29 | 'password',
30 | 'password_confirmation',
31 | ];
32 | /**
33 | * Render an exception into an HTTP response.
34 | *
35 | * @param \Illuminate\Http\Request $request
36 | * @param \Throwable $exception
37 | * @return \Symfony\Component\HttpFoundation\Response
38 | *
39 | * @throws \Throwable
40 | */
41 | public function render($request, Throwable $exception) {
42 | if ($exception instanceof AppException) {
43 | $layout = cfg('template', 'layui');
44 | $tplPath = $layout . '/errors/error';
45 | return view($tplPath, ['title' => __('dujiaoka.error_title'), 'content' => $exception->getMessage(), 'url' => ""]);
46 | }
47 | return parent::render($request, $exception);
48 | }
49 | }
--------------------------------------------------------------------------------
/app/Services/Payment.php:
--------------------------------------------------------------------------------
1 | whereIn('pay_client', [$payClient, Pay::CLIENT_ALL])
26 | ->where('enable', Pay::ENABLED)
27 | ->get();
28 | return $payGateway ? $payGateway->toArray() : null;
29 | }
30 |
31 | /**
32 | * 通过支付标识获得支付配置
33 | *
34 | * @param string $check 支付标识
35 | * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
36 | *
37 | */
38 | public function detailByCheck(string $check)
39 | {
40 | $gateway = Pay::query()
41 | ->where('pay_check', $check)
42 | ->where('enable', Pay::ENABLED)
43 | ->first();
44 | return $gateway;
45 | }
46 |
47 | /**
48 | * 通过id查询支付网关
49 | *
50 | * @param int $id 支付网关id
51 | * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
52 | *
53 | */
54 | public function detail(int $id)
55 | {
56 | $gateway = Pay::query()
57 | ->where('id', $id)
58 | ->where('enable', Pay::ENABLED)
59 | ->first();
60 | return $gateway;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Home/ArticleController.php:
--------------------------------------------------------------------------------
1 | get()
13 | ->map(function ($article) {
14 | $article->summary = $article->getSummary();
15 | return $article;
16 | });
17 | $articles_sort = $articles->groupBy('category');
18 | $categories = $articles_sort->keys();
19 |
20 | return $this->render('static_pages/article', [
21 | 'articles' => $articles_sort,
22 | 'category' => $categories
23 | ], __('dujiaoka.page-title.article'));
24 | }
25 |
26 | public function show($link) {
27 |
28 | // 根据 $link 查询文章内容
29 | $article = Articles::with(['goods' => function($query) {
30 | $query->where('is_open', true)->select('goods.id', 'goods.gd_name', 'goods.gd_description', 'goods.picture');
31 | }])->where('link', $link)->first();
32 |
33 | if (!$article) {
34 | abort(404);
35 | }
36 |
37 | $title = $article->title;
38 | $content = $article->content;
39 | $relatedGoods = $article->goods;
40 |
41 | return $this->render('static_pages/article', [
42 | 'title' => $title,
43 | 'content' => $content,
44 | 'relatedGoods' => $relatedGoods
45 | ],
46 | $title." | ". __('dujiaoka.page-title.article'));
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/app/Jobs/OrderExpired.php:
--------------------------------------------------------------------------------
1 | orderSN = $orderSN;
47 | }
48 |
49 | /**
50 | * Execute the job.
51 | *
52 | * @return void
53 | */
54 | public function handle()
55 | {
56 | // 如果x分钟后还没支付就算过期
57 | $order = app('App\Services\Orders')->detailOrderSN($this->orderSN);
58 | if ($order && $order->status == Order::STATUS_WAIT_PAY) {
59 | $stockMode = cfg('stock_mode', 2);
60 | // 如果是下单即减库存模式,释放锁定的库存
61 | if ($stockMode == 1) {
62 | CacheManager::unlockStock($this->orderSN);
63 | }
64 |
65 | app('App\Services\Orders')->expiredOrderSN($this->orderSN);
66 | // 回退优惠券
67 | CouponBack::dispatch($order);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Challenge.php:
--------------------------------------------------------------------------------
1 | route()->getAction();
26 | $controller = $routeAction['controller'] ?? null;
27 | if($controller && in_array(explode('@', $controller)[0], $this->whiteClass))
28 | return $next($request);
29 |
30 | if(cfg('is_cn_challenge') == BaseModel::STATUS_CLOSE)
31 | return $next($request);
32 |
33 | $status = session('challenge');
34 | if($status === "pass")
35 | return $next($request);
36 |
37 | if(isset($_REQUEST['_challenge'])){
38 | if (substr(sha1($_REQUEST['_challenge']), -4) == $status){
39 | session(['challenge' => 'pass']);
40 | return $next($request);
41 | }
42 | }
43 |
44 | $isoCode = get_ip_country($request->ip());
45 | if($isoCode != 'CN'){
46 | session(['challenge' => 'pass']);
47 | return $next($request);
48 | }
49 | $challenge = substr(sha1(rand()), -4);
50 | session(['challenge' => $challenge]);
51 | return response()->view('common/challenge',['code' => $challenge]);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/config/hashing.php:
--------------------------------------------------------------------------------
1 | 'bcrypt',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Bcrypt Options
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify the configuration options that should be used when
26 | | passwords are hashed using the Bcrypt algorithm. This will allow you
27 | | to control the amount of time it takes to hash the given password.
28 | |
29 | */
30 |
31 | 'bcrypt' => [
32 | 'rounds' => env('BCRYPT_ROUNDS', 10),
33 | ],
34 |
35 | /*
36 | |--------------------------------------------------------------------------
37 | | Argon Options
38 | |--------------------------------------------------------------------------
39 | |
40 | | Here you may specify the configuration options that should be used when
41 | | passwords are hashed using the Argon algorithm. These will allow you
42 | | to control the amount of time it takes to hash the given password.
43 | |
44 | */
45 |
46 | 'argon' => [
47 | 'memory' => 1024,
48 | 'threads' => 2,
49 | 'time' => 2,
50 | ],
51 |
52 | ];
53 |
--------------------------------------------------------------------------------
/resources/views/themes/morpho/views/layouts/_footer.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
31 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Http\Kernel::class,
31 | App\Http\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Console\Kernel::class,
36 | App\Console\Kernel::class
37 | );
38 |
39 | $app->singleton(
40 | Illuminate\Contracts\Debug\ExceptionHandler::class,
41 | App\Exceptions\Handler::class
42 | );
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Return The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | This script returns the application instance. The instance is given to
50 | | the calling script so we can separate the building of the instances
51 | | from the actual running of the application and sending responses.
52 | |
53 | */
54 |
55 | return $app;
56 |
--------------------------------------------------------------------------------
/config/broadcasting.php:
--------------------------------------------------------------------------------
1 | env('BROADCAST_DRIVER', 'null'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Broadcast Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the broadcast connections that will be used
26 | | to broadcast events to other systems or over websockets. Samples of
27 | | each available type of connection are provided inside this array.
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'pusher' => [
34 | 'driver' => 'pusher',
35 | 'key' => env('PUSHER_APP_KEY'),
36 | 'secret' => env('PUSHER_APP_SECRET'),
37 | 'app_id' => env('PUSHER_APP_ID'),
38 | 'options' => [
39 | 'cluster' => env('PUSHER_APP_CLUSTER'),
40 | 'useTLS' => true,
41 | ],
42 | ],
43 |
44 | 'redis' => [
45 | 'driver' => 'redis',
46 | 'connection' => 'default',
47 | ],
48 |
49 | 'log' => [
50 | 'driver' => 'log',
51 | ],
52 |
53 | 'null' => [
54 | 'driver' => 'null',
55 | ],
56 |
57 | ],
58 |
59 | ];
60 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class);
50 | $response = $kernel->handle(
51 | $request = Request::capture()
52 | )->send();
53 |
54 | $kernel->terminate($request, $response);
--------------------------------------------------------------------------------
/resources/lang/zh_CN/goods.php:
--------------------------------------------------------------------------------
1 | [
5 | 'Goods' => '商品',
6 | 'goods' => '商品',
7 | 'soldout' => '已售完'
8 | ],
9 | 'fields' => [
10 | 'price' => '售价',
11 | 'preselection' => '自选加价',
12 | 'group_id' => '所属分类',
13 | 'api_hook' => '回调事件',
14 | 'buy_prompt' => '购买提示',
15 | 'description' => '商品描述',
16 | 'gd_name' => '商品名称',
17 | 'gd_description' => '商品描述',
18 | 'gd_keywords' => '商品关键字',
19 | 'goods_sub' => '商品规格',
20 | 'stock' => '库存',
21 | 'ord' => '排序权重',
22 | 'other_ipu_cnf' => '其他输入框配置',
23 | 'picture' => '商品图片',
24 | 'picture_url' => '商品图片URL',
25 | 'sales_volume' => '销量',
26 | 'type' => '发货类型',
27 | 'buy_limit_num' => '限制单次购买最大数量',
28 | 'wholesale_price_cnf' => '批发价配置',
29 | 'automatic_delivery' => '自动发货',
30 | 'manual_processing' => '人工处理',
31 | 'automatic_processing' => '自动处理',
32 | 'is_open' => '是否上架',
33 | 'is_sub' => '商品类型',
34 | 'coupon_id' => '可用优惠码',
35 | 'payment_limit' => '限制支付方式'
36 | ],
37 | 'options' => [
38 | 'is_sub' => '多规格商品',
39 | 'not_sub' => '单规格商品'
40 | ],
41 | 'helps' => [
42 | 'picture' => '可不上传,为默认图片',
43 | 'picture_url' => '输入站外图片链接,将自动替换商品图片。',
44 | 'in_stock' => '"自动发货"类型的商品会自动计算库存卡密,其他类型需要主动设置库存。',
45 | 'buy_limit_num' => '防止恶意刷库存,0为不限制客户单次下单最大数量',
46 | 'other_ipu_cnf' => '格式为唯一标识(英文)=输入框名字=是否必填,例如:填写 qq_account=QQ账号=true 表示产品详情页会新增一个 QQ账号 输入框。true 为必填,false 为选填。(一行一个)',
47 | 'wholesale_price_cnf' => '例如:填写 5=3 表示客户购买 5 件或以上时,每件价格为 3 元;批发价会覆盖多规格商品价格。(一行一个)',
48 | 'payment_limit' => '仅允许使用这些支付方式购买此商品,若为空,则支持全部已启用的的支付方式',
49 | 'preselection' => '自动发货的商品支持在下单时预先选择想要的卡密,填写一个价格则代表开启自选加价。需在卡密处完成设置。'
50 | ]
51 | ];
52 |
--------------------------------------------------------------------------------
/app/Models/Pay.php:
--------------------------------------------------------------------------------
1 | __('pay.fields.method_jump'),
33 | self::METHOD_SCAN => __('pay.fields.method_scan'),
34 | ];
35 | }
36 |
37 | public static function getClientMap()
38 | {
39 | return [
40 | self::CLIENT_PC => __('pay.fields.pay_client_pc'),
41 | self::CLIENT_MOBILE => __('pay.fields.pay_client_mobile'),
42 | self::CLIENT_ALL => __('pay.fields.pay_client_all'),
43 | ];
44 | }
45 |
46 | // 作用域:获取启用的支付方式
47 | public function scopeEnabled($query)
48 | {
49 | return $query->where('enable', self::ENABLED);
50 | }
51 |
52 | // 作用域:根据客户端类型筛选
53 | public function scopeForClient($query, $client)
54 | {
55 | return $query->whereIn('pay_client', [$client, self::CLIENT_ALL]);
56 | }
57 |
58 | protected static function boot()
59 | {
60 | parent::boot();
61 |
62 | static::updated(function ($pay) {
63 | CacheManager::forgetPayMethod($pay->id);
64 | CacheManager::forgetPayMethods();
65 | });
66 |
67 | static::deleted(function ($pay) {
68 | CacheManager::forgetPayMethod($pay->id);
69 | CacheManager::forgetPayMethods();
70 | });
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/resources/lang/zh_TW/system-setting.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright assimon
7 | * @link http://utf8.hk/
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'SystemSetting' => '系統設定',
13 | 'system_setting' => '系統設定',
14 | 'base_setting' => '基本設定',
15 | 'mail_setting' => '信箱服務',
16 | 'order_push_setting' => '訂單推送配置',
17 | 'geetest' => '極驗驗證',
18 | ],
19 |
20 | 'fields' => [
21 | 'title' => '網站標題',
22 | 'text_logo' => '文字LOGO',
23 | 'img_logo' => '圖片LOGO',
24 | 'keywords' => '網站關鍵詞',
25 | 'description' => '網站描述',
26 | 'notice' => '站點公告',
27 | 'footer' => '頁尾自訂代碼',
28 | 'manage_email' => '管理員信箱',
29 | 'is_open_anti_red' => '是否開啟Wechat/QQ防紅',
30 | 'is_open_img_code' => '是否開啟圖形驗證碼',
31 | 'is_open_search_pwd' => '是否開啟查詢密碼',
32 | 'is_open_server_jiang' => '是否開啟server醬',
33 | 'server_jiang_token' => 'server醬通訊token',
34 | 'is_open_telegram_push' => '是否開啟Telegram推送',
35 | 'telegram_userid' => 'Telegram用戶id',
36 | 'telegram_bot_token' => 'Telegram通訊token',
37 | 'template' => '站點模板',
38 | 'language' => '站點語言',
39 | 'order_expire_time' => '訂單逾期時間(分鐘)',
40 |
41 | 'driver' => '信箱驅動',
42 | 'host' => 'smtp伺服器地址',
43 | 'port' => '通訊埠',
44 | 'username' => '賬戶',
45 | 'password' => '密碼',
46 | 'encryption' => '協議',
47 | 'from_address' => '發件地址',
48 | 'from_name' => '發件名稱',
49 |
50 | 'geetest_id' => '極驗id',
51 | 'geetest_key' => '極驗key',
52 | 'is_open_geetest' => '是否開啟極驗',
53 | ],
54 | 'options' => [
55 | ],
56 | 'rule_messages' => [
57 | 'save_system_setting_success' => '系統配置套用成功!',
58 | 'change_reboot_php_worker' => '修改部分配置需要重新啓動[supervisor]或php進程管理工具才會生效,例如信箱服務,server醬等。'
59 | ]
60 | ];
61 |
--------------------------------------------------------------------------------
/app/Jobs/ServerJiang.php:
--------------------------------------------------------------------------------
1 | order = $order;
43 | }
44 |
45 | /**
46 | * Execute the job.
47 | *
48 | * @return void
49 | */
50 | public function handle()
51 | {
52 | $postdata = http_build_query([
53 | 'text' => __('dujiaoka.prompt.new_order_push') . ":{$this->order['ord_title']}",
54 | 'desp' => "
55 | - ". __('order.fields.title') .":{$this->order->title}
56 | - ". __('order.fields.order_sn') .":{$this->order->order_sn}
57 | - ". __('order.fields.email') .":{$this->order->email}
58 | - ". __('order.fields.actual_price') .":{$this->order->actual_price}
59 | "
60 | ]);
61 | $opts = [
62 | 'http' => [
63 | 'method' => 'POST',
64 | 'header' => 'Content-type: application/x-www-form-urlencoded',
65 | 'content' => $postdata
66 | ]
67 | ];
68 | $context = stream_context_create($opts);
69 | $apiToken = cfg('server_jiang_token');
70 | file_get_contents('https://sctapi.ftqq.com/' . $apiToken . '.send', false, $context);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | mapApiRoutes();
46 |
47 | $this->mapWebRoutes();
48 |
49 | //
50 | }
51 |
52 | /**
53 | * Define the "web" routes for the application.
54 | *
55 | * These routes all receive session state, CSRF protection, etc.
56 | *
57 | * @return void
58 | */
59 | protected function mapWebRoutes()
60 | {
61 | Route::middleware('web')
62 | ->namespace($this->namespace)
63 | ->group(base_path('routes/web.php'));
64 | }
65 |
66 | /**
67 | * Define the "api" routes for the application.
68 | *
69 | * These routes are typically stateless.
70 | *
71 | * @return void
72 | */
73 | protected function mapApiRoutes()
74 | {
75 | Route::prefix('api')
76 | ->middleware('api')
77 | ->namespace($this->namespace)
78 | ->group(base_path('routes/api.php'));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "outtimes/dujiaoka",
3 | "type": "project",
4 | "description": "The Laravel Framework.",
5 | "keywords": ["framework", "laravel"],
6 | "license": "MIT",
7 | "require": {
8 | "php": "^8.2",
9 | "bezhansalleh/filament-shield": "^3.3",
10 | "filament/filament": "^3.2",
11 | "filament/spatie-laravel-settings-plugin": "^3.3",
12 | "geoip2/geoip2": "^2.13",
13 | "jenssegers/agent": "^2.6",
14 | "laravel/framework": "^12.0",
15 | "laravel/tinker": "^2.8",
16 | "mews/captcha": "^3.0",
17 | "mgcodeur/laravel-currency-converter": "^1.0",
18 | "overtrue/pinyin": "^5.3",
19 | "paypal/paypal-server-sdk": "^0.6",
20 | "simplesoftwareio/simple-qrcode": "^4.0",
21 | "stripe/stripe-php": "^7.84",
22 | "xhat/payjs-laravel": "^1.6",
23 | "yansongda/pay": "^3.2"
24 | },
25 | "require-dev": {
26 | "spatie/laravel-ignition": "^2.0",
27 | "fakerphp/faker": "^1.23",
28 | "mockery/mockery": "^1.6",
29 | "nunomaduro/collision": "^8.0",
30 | "phpunit/phpunit": "^10.5|^11.0"
31 | },
32 | "config": {
33 | "optimize-autoloader": true,
34 | "preferred-install": "dist",
35 | "sort-packages": true,
36 | "platform-check": false
37 | },
38 | "extra": {
39 | "laravel": {
40 | "dont-discover": []
41 | }
42 | },
43 | "autoload": {
44 | "psr-4": {
45 | "App\\": "app/"
46 | },
47 | "classmap": [
48 | "database/seeds",
49 | "database/factories"
50 | ],
51 | "files": [
52 | "app/Helpers/functions.php"
53 | ]
54 | },
55 | "autoload-dev": {
56 | "psr-4": {
57 | "Tests\\": "tests/"
58 | }
59 | },
60 | "minimum-stability": "dev",
61 | "prefer-stable": true,
62 | "scripts": {
63 | "post-autoload-dump": [
64 | "@php artisan filament:upgrade"
65 | ]
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/public/assets/morpho/js/scroll-to-top.js:
--------------------------------------------------------------------------------
1 | // 控制置顶按钮显示/隐藏和进度动画
2 | document.addEventListener('DOMContentLoaded', function() {
3 | const scrollTopBtn = document.querySelector('.btn-scroll-top');
4 | if (!scrollTopBtn) return;
5 |
6 | const svgRect = scrollTopBtn.querySelector('svg rect');
7 | const dashArray = 155.201; // SVG圆角矩形周长
8 |
9 | // 初始隐藏
10 | scrollTopBtn.style.opacity = '0';
11 | scrollTopBtn.style.visibility = 'hidden';
12 | scrollTopBtn.style.transform = 'translateX(100%)';
13 | scrollTopBtn.style.transition = 'all 0.3s ease';
14 |
15 | if (svgRect) {
16 | svgRect.style.strokeDasharray = dashArray;
17 | svgRect.style.strokeDashoffset = dashArray;
18 | }
19 |
20 | function updateScrollProgress() {
21 | const scrollTop = window.scrollY;
22 | const docHeight = document.documentElement.scrollHeight - window.innerHeight;
23 | const scrollPercent = scrollTop / docHeight;
24 |
25 | // 显示/隐藏按钮
26 | if (scrollTop > 300) {
27 | scrollTopBtn.style.opacity = '1';
28 | scrollTopBtn.style.visibility = 'visible';
29 | scrollTopBtn.style.transform = 'translateX(0)';
30 | } else {
31 | scrollTopBtn.style.opacity = '0';
32 | scrollTopBtn.style.visibility = 'hidden';
33 | scrollTopBtn.style.transform = 'translateX(100%)';
34 | }
35 |
36 | // 更新进度圆环
37 | if (svgRect && scrollTop > 300) {
38 | const offset = dashArray - (scrollPercent * dashArray);
39 | svgRect.style.strokeDashoffset = offset;
40 | }
41 | }
42 |
43 | // 节流函数
44 | let ticking = false;
45 | function requestTick() {
46 | if (!ticking) {
47 | requestAnimationFrame(updateScrollProgress);
48 | ticking = true;
49 | setTimeout(() => ticking = false, 16);
50 | }
51 | }
52 |
53 | window.addEventListener('scroll', requestTick);
54 |
55 | // 平滑滚动到顶部
56 | scrollTopBtn.addEventListener('click', function(e) {
57 | e.preventDefault();
58 | window.scrollTo({
59 | top: 0,
60 | behavior: 'smooth'
61 | });
62 | });
63 | });
--------------------------------------------------------------------------------
/app/Providers/PaymentServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(PaymentManager::class, function ($app) {
21 | return new PaymentManager();
22 | });
23 |
24 | // 注册支付管理器别名
25 | $this->app->alias(PaymentManager::class, 'payment.manager');
26 | }
27 |
28 | /**
29 | * 启动服务
30 | */
31 | public function boot()
32 | {
33 | $this->registerPaymentRoutes();
34 | $this->registerPaymentDrivers();
35 | }
36 |
37 | /**
38 | * 注册支付路由
39 | */
40 | protected function registerPaymentRoutes()
41 | {
42 | Route::macro('paymentRoutes', function () {
43 | $paymentManager = app(PaymentManager::class);
44 |
45 | foreach ($paymentManager->getRegisteredDrivers() as $driver) {
46 | Route::group([
47 | 'prefix' => "pay/{$driver}",
48 | 'middleware' => ['dujiaoka.pay_gate_way']
49 | ], function () use ($driver) {
50 | Route::get('{payway}/{orderSN}', 'UnifiedPaymentController@gateway')
51 | ->name("payment.{$driver}.gateway");
52 |
53 | Route::post('notify_url', 'UnifiedPaymentController@notify')
54 | ->name("payment.{$driver}.notify");
55 |
56 | Route::get('return_url', 'UnifiedPaymentController@returnUrl')
57 | ->name("payment.{$driver}.return");
58 | });
59 | }
60 | });
61 | }
62 |
63 | /**
64 | * 注册默认支付驱动
65 | */
66 | protected function registerPaymentDrivers()
67 | {
68 | $paymentManager = app(PaymentManager::class);
69 |
70 | // 可以在这里手动注册额外的驱动
71 | // $paymentManager->registerDriver('custom_payment', CustomPaymentDriver::class);
72 | }
73 | }
--------------------------------------------------------------------------------
/app/Jobs/ApiHook.php:
--------------------------------------------------------------------------------
1 | order = $order;
50 | $this->goodsService = app('App\Services\Shop');
51 | }
52 |
53 | /**
54 | * Execute the job.
55 | *
56 | * @return void
57 | */
58 | public function handle()
59 | {
60 | $goodInfo = $this->goodsService->detail($this->order->goods_id);
61 | // 判断是否有配置支付回调
62 | if(empty($goodInfo->api_hook)){
63 | return;
64 | }
65 | $postdata = [
66 | 'title' => $this->order->title,
67 | 'order_sn' => $this->order->order_sn,
68 | 'email' => $this->order->email,
69 | 'actual_price' => $this->order->actual_price,
70 | 'order_info' => $this->order->info,
71 | 'good_id' => $goodInfo->id,
72 | 'gd_name' => $goodInfo->gd_name
73 |
74 | ];
75 |
76 |
77 | $opts = [
78 | 'http' => [
79 | 'method' => 'POST',
80 | 'header' => 'Content-type: application/json',
81 | 'content' => json_encode($postdata,JSON_UNESCAPED_UNICODE)
82 | ]
83 | ];
84 | $context = stream_context_create($opts);
85 | file_get_contents($goodInfo->api_hook, false, $context);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(Shop::class);
27 | $this->app->singleton(Payment::class);
28 | $this->app->singleton(Cards::class);
29 | $this->app->singleton(Orders::class);
30 | $this->app->singleton(Coupons::class);
31 | $this->app->singleton(OrderProcess::class);
32 | $this->app->singleton(Email::class);
33 | $this->app->singleton(Validator::class);
34 | $this->app->singleton(CacheManager::class);
35 | $this->app->singleton('Jenssegers\Agent', function () {
36 | return $this->app->make(Agent::class);
37 | });
38 |
39 | $this->app->singleton('App\\Services\\ConfigService', function ($app) {
40 | return new \App\Services\ConfigService();
41 | });
42 |
43 | $this->app->singleton('App\\Services\\ThemeService');
44 | }
45 |
46 | /**
47 | * Bootstrap any application services.
48 | *
49 | * @return void
50 | */
51 | public function boot()
52 | {
53 | // 动态设置语言文件中的货币符号
54 | $this->app->booted(function () {
55 | $currency = shop_cfg('currency', 'cny');
56 | $symbols = [
57 | 'cny' => '¥',
58 | 'usd' => '$',
59 | ];
60 | $symbol = $symbols[$currency] ?? '¥';
61 |
62 | // 设置所有语言文件的货币符号
63 | app('translator')->addLines(['dujiaoka.money_symbol' => $symbol], 'zh_CN');
64 | app('translator')->addLines(['dujiaoka.money_symbol' => $symbol], 'zh_TW');
65 | app('translator')->addLines(['dujiaoka.money_symbol' => $symbol], 'en');
66 | });
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/Services/ConfigService.php:
--------------------------------------------------------------------------------
1 | loaded) {
16 | $this->loadConfig();
17 | }
18 |
19 | return data_get($this->config, $key, $default);
20 | }
21 |
22 | public function set(string $key, $value): void
23 | {
24 | data_set($this->config, $key, $value);
25 | $this->saveConfig();
26 | }
27 |
28 | public function all(): array
29 | {
30 | if (!$this->loaded) {
31 | $this->loadConfig();
32 | }
33 |
34 | return $this->config;
35 | }
36 |
37 | protected function loadConfig(): void
38 | {
39 | $this->config = Cache::get('system-setting', []);
40 |
41 | // Redis 没数据时从数据库读取一次
42 | if (empty($this->config)) {
43 | try {
44 | $settings = DB::table('settings')->get();
45 | $config = [];
46 |
47 | foreach ($settings as $setting) {
48 | $payload = json_decode($setting->payload, true);
49 | // 构建嵌套结构:config[group][name] = payload
50 | if (!isset($config[$setting->group])) {
51 | $config[$setting->group] = [];
52 | }
53 | $config[$setting->group][$setting->name] = $payload;
54 | }
55 |
56 | $this->config = $config;
57 | if (!empty($config)) {
58 | Cache::put('system-setting', $config);
59 | }
60 | } catch (\Exception $e) {
61 | $this->config = [];
62 | }
63 | }
64 |
65 | $this->loaded = true;
66 | }
67 |
68 | protected function saveConfig(): void
69 | {
70 | Cache::put('system-setting', $this->config);
71 | }
72 |
73 | public function refresh(): void
74 | {
75 | Cache::forget('system-setting');
76 | $this->loaded = false;
77 | $this->config = [];
78 | }
79 | }
--------------------------------------------------------------------------------
/resources/views/vendor/geetest/geetest.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | loading...
5 |
6 | @define use Illuminate\Support\Facades\Config
7 |
51 |
56 |
--------------------------------------------------------------------------------
/app/Jobs/MailSend.php:
--------------------------------------------------------------------------------
1 | to = $to;
47 | $this->title = $title;
48 | $this->content = $content;
49 | }
50 |
51 | /**
52 | * Execute the job.
53 | *
54 | * @return void
55 | */
56 | public function handle()
57 | {
58 | $body = $this->content;
59 | $title = $this->title;
60 | $sysConfig = cache('system-setting');
61 | $mailConfig = [
62 | 'driver' => $sysConfig['driver'] ?? 'smtp',
63 | 'host' => $sysConfig['host'] ?? '',
64 | 'port' => $sysConfig['port'] ?? '465',
65 | 'username' => $sysConfig['username'] ?? '',
66 | 'from' => [
67 | 'address' => $sysConfig['from_address'] ?? '',
68 | 'name' => $sysConfig['from_name'] ?? '独角发卡'
69 | ],
70 | 'password' => $sysConfig['password'] ?? '',
71 | 'encryption' => $sysConfig['encryption'] ?? ''
72 | ];
73 | $to = $this->to;
74 | // 覆盖 mail 配置
75 | config([
76 | 'mail' => array_merge(config('mail'), $mailConfig)
77 | ]);
78 | // 重新注册驱动
79 | (new MailServiceProvider(app()))->register();
80 | Mail::send(['html' => 'email.mail'], ['body' => $body], function ($message) use ($to, $title){
81 | $message->to($to)->subject($title);
82 | });
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/Services/Email.php:
--------------------------------------------------------------------------------
1 | '购买收据',
21 | 'manual_send_manage_mail' => '新订单通知',
22 | 'failed_order' => '订单失败',
23 | 'completed_order' => '订单完成',
24 | 'pending_order' => '订单待处理'
25 | ];
26 |
27 | public function getTemplate(string $token): Emailtpl
28 | {
29 | return cache()->remember("email_{$token}", 86400, function () use ($token) {
30 | $dbTemplate = Emailtpl::where('tpl_token', $token)->first();
31 |
32 | if (isset(self::$templates[$token])) {
33 | $template = $this->loadTemplate($token);
34 | if ($dbTemplate) {
35 | $template->tpl_name = $dbTemplate->tpl_name;
36 | }
37 | return $template;
38 | }
39 |
40 | return $dbTemplate;
41 | });
42 | }
43 |
44 | private function loadTemplate(string $token): Emailtpl
45 | {
46 | $path = resource_path("email-templates/{$token}.html");
47 | $content = file_exists($path) ? file_get_contents($path) : '模板不存在
';
48 |
49 | $template = new Emailtpl();
50 | $template->tpl_name = self::$templates[$token];
51 | $template->tpl_content = $content;
52 | $template->tpl_token = $token;
53 |
54 | return $template;
55 | }
56 |
57 | public static function getTemplates(): array
58 | {
59 | return self::$templates;
60 | }
61 |
62 | public function parse(string $template, Order $order): string
63 | {
64 | $context = EmailVariableResolver::createContext($order);
65 | return EmailVariableResolver::resolve($template, $context);
66 | }
67 |
68 |
69 | public function getEmail(string $token, Order $order): array
70 | {
71 | $template = $this->getTemplate($token);
72 |
73 | if (!$template) {
74 | throw new \Exception("模板 {$token} 不存在");
75 | }
76 |
77 | return [
78 | 'title' => $this->parse($template->tpl_name, $order),
79 | 'content' => $this->parse($template->tpl_content, $order),
80 | ];
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/resources/lang/zh_CN/system-setting.php:
--------------------------------------------------------------------------------
1 |
6 | * @copyright assimon
7 | * @link http://utf8.hk/
8 | */
9 |
10 | return [
11 | 'labels' => [
12 | 'SystemSetting' => '系统设置',
13 | 'system_setting' => '系统设置',
14 | 'base_setting' => '基本设置',
15 | 'mail_setting' => '邮件服务',
16 | 'order_push_setting' => '订单推送配置',
17 | 'order_setting' => '下单设置',
18 | 'geetest' => '极验验证',
19 | ],
20 |
21 | 'fields' => [
22 | 'title' => '网站标题',
23 | 'text_logo' => '文字LOGO',
24 | 'img_logo' => '图片LOGO',
25 | 'keywords' => '网站关键词',
26 | 'description' => '网站描述',
27 | 'notice' => '站点公告',
28 | 'footer' => '页脚自定义代码',
29 | 'manage_email' => '管理员邮箱',
30 | 'is_open_anti_red' => '是否开启微信/QQ防红',
31 | 'is_open_search_pwd' => '是否开启查询密码',
32 | 'is_open_google_translate' => '是否开启google翻译',
33 |
34 | 'is_open_img_code' => '是否开启图形验证码',
35 | 'is_cn_challenge' => '中国大陆访问需要人机验证',
36 | 'order_expire_time' => '订单过期时间(分钟)',
37 | 'order_ip_limits' => '单IP同时待支付订单数量',
38 |
39 | 'is_open_server_jiang' => '是否开启server酱',
40 | 'server_jiang_token' => 'server酱通讯token',
41 | 'is_open_telegram_push' => '是否开启Telegram推送',
42 | 'telegram_userid' => 'Telegram用户id',
43 | 'telegram_bot_token' => 'Telegram通讯token',
44 | 'is_open_bark_push' => '是否开启Bark推送',
45 | 'is_open_bark_push_url' => '是否推送订单URL',
46 | 'bark_server' => 'Bark服务器',
47 | 'bark_token' => 'Bark通讯Token',
48 | 'is_open_qywxbot_push' => '是否开启企业微信Bot推送',
49 | 'qywxbot_key' => '企业微信Bot通讯Key',
50 |
51 | 'template' => '站点模板',
52 | 'language' => '站点语言',
53 | 'currency' => '站点货币',
54 |
55 | 'driver' => '邮件驱动',
56 | 'host' => 'smtp服务器地址',
57 | 'port' => '端口',
58 | 'username' => '账号',
59 | 'password' => '密码',
60 | 'encryption' => '协议',
61 | 'from_address' => '发件地址',
62 | 'from_name' => '发件名称',
63 |
64 | 'geetest_id' => '极验id',
65 | 'geetest_key' => '极验key',
66 | 'is_open_geetest' => '是否开启极验',
67 | ],
68 | 'options' => [
69 | ],
70 | 'rule_messages' => [
71 | 'save_system_setting_success' => '系统配置保存成功!',
72 | 'change_reboot_php_worker' => '修改部分配置需要重启[supervisor]或php进程管理工具才会生效,例如邮件服务,server酱等。'
73 | ]
74 | ];
75 |
--------------------------------------------------------------------------------
/app/Jobs/TelegramPush.php:
--------------------------------------------------------------------------------
1 | order = $order;
52 | $this->goodsService = app('App\Services\Shop');
53 | }
54 |
55 | /**
56 | * Execute the job.
57 | *
58 | * @return void
59 | */
60 | public function handle()
61 | {
62 | $goodInfo = $this->goodsService->detail($this->order->goods_id);
63 | $formatText = '*'. __('dujiaoka.prompt.new_order_push').'('.$this->order->actual_price.'元)*%0A'
64 | . __('order.fields.order_id') .': `'.$this->order->id.'`%0A'
65 | . __('order.fields.order_sn') .': `'.$this->order->order_sn.'`%0A'
66 | . __('order.fields.pay_id') .': `'.$this->order->pay->pay_name.'`%0A'
67 | . __('order.fields.title') .': '.$this->order->title.'%0A'
68 | . __('order.fields.actual_price') .': '.$this->order->actual_price.'%0A'
69 | . __('order.fields.email') .': `'.$this->order->email.'`%0A'
70 | . __('goods.fields.gd_name') .': `'.$goodInfo->gd_name.'`%0A'
71 | . __('goods.fields.stock') .': `'.$goodInfo->stock.'`%0A'
72 | . __('order.fields.order_created') .': '.$this->order->created_at;
73 | $client = new Client([
74 | 'timeout' => 30,
75 | 'proxy'=> ''
76 | ]);
77 | $apiUrl = 'https://api.telegram.org/bot' . cfg('telegram_bot_token') .
78 | '/sendMessage?chat_id=' . cfg('telegram_userid') . '&parse_mode=Markdown&text='.$formatText;
79 | $client->post($apiUrl);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/app/Models/Articles.php:
--------------------------------------------------------------------------------
1 | link) && !empty($article->title)) {
28 | $article->link = static::generateSlug($article->title);
29 | }
30 | });
31 |
32 | static::updating(function ($article) {
33 | if (empty($article->link) && !empty($article->title)) {
34 | $article->link = static::generateSlug($article->title);
35 | }
36 | });
37 | }
38 |
39 | public static function generateSlug($title)
40 | {
41 | $slug = Pinyin::permalink($title, '-');
42 |
43 | if (empty($slug)) {
44 | $slug = preg_replace('/[^a-zA-Z0-9\s]/', '', $title);
45 | $slug = str_replace(' ', '-', trim($slug));
46 | $slug = strtolower($slug);
47 |
48 | if (empty($slug)) {
49 | $slug = 'article-' . time();
50 | }
51 | }
52 |
53 | // 确保唯一性
54 | $originalSlug = $slug;
55 | $counter = 1;
56 | while (static::where('link', $slug)->exists()) {
57 | $slug = $originalSlug . '-' . $counter;
58 | $counter++;
59 | }
60 |
61 | return $slug;
62 | }
63 |
64 | /**
65 | * 关联分类
66 | */
67 | public function category()
68 | {
69 | return $this->belongsTo(ArticleCategory::class, 'category_id');
70 | }
71 |
72 | /**
73 | * 关联商品
74 | */
75 | public function goods()
76 | {
77 | return $this->belongsToMany(Goods::class, 'article_goods', 'article_id', 'goods_id')
78 | ->withTimestamps()
79 | ->withPivot('sort')
80 | ->orderBy('pivot_sort', 'desc');
81 | }
82 |
83 | public function getSummary()
84 | {
85 | //简单地输出摘要
86 | $summary = substr($this->content, 0, 200);
87 | return strip_tags($summary);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/Admin/Widgets/SalesOverviewWidget.php:
--------------------------------------------------------------------------------
1 | startOfDay();
22 | $todayEnd = $now->copy()->endOfDay();
23 | $todayOrders = Order::whereBetween('created_at', [$todayStart, $todayEnd])
24 | ->where('status', 4) // 已完成
25 | ->count();
26 | $todayRevenue = Order::whereBetween('created_at', [$todayStart, $todayEnd])
27 | ->where('status', 4)
28 | ->sum('actual_price');
29 |
30 | // 本周数据
31 | $weekStart = $now->copy()->startOfWeek();
32 | $weekEnd = $now->copy()->endOfWeek();
33 | $weekOrders = Order::whereBetween('created_at', [$weekStart, $weekEnd])
34 | ->where('status', 4)
35 | ->count();
36 | $weekRevenue = Order::whereBetween('created_at', [$weekStart, $weekEnd])
37 | ->where('status', 4)
38 | ->sum('actual_price');
39 |
40 | // 本月数据
41 | $monthStart = $now->copy()->startOfMonth();
42 | $monthEnd = $now->copy()->endOfMonth();
43 | $monthOrders = Order::whereBetween('created_at', [$monthStart, $monthEnd])
44 | ->where('status', 4)
45 | ->count();
46 | $monthRevenue = Order::whereBetween('created_at', [$monthStart, $monthEnd])
47 | ->where('status', 4)
48 | ->sum('actual_price');
49 |
50 | return [
51 | Stat::make('今日销售', '¥' . number_format($todayRevenue, 2))
52 | ->description('订单数: ' . $todayOrders)
53 | ->icon('heroicon-m-currency-dollar')
54 | ->color('success'),
55 |
56 | Stat::make('本周销售', '¥' . number_format($weekRevenue, 2))
57 | ->description('订单数: ' . $weekOrders)
58 | ->icon('heroicon-m-chart-bar')
59 | ->color('info'),
60 |
61 | Stat::make('本月销售', '¥' . number_format($monthRevenue, 2))
62 | ->description('订单数: ' . $monthOrders)
63 | ->icon('heroicon-m-chart-pie')
64 | ->color('warning'),
65 | ];
66 | }
67 | }
--------------------------------------------------------------------------------
/app/Jobs/BarkPush.php:
--------------------------------------------------------------------------------
1 | order = $order;
53 | $this->goodsService = app('App\Services\Shop');
54 | }
55 |
56 | /**
57 | * Execute the job.
58 | *
59 | * @return void
60 | */
61 | public function handle()
62 | {
63 | $goodInfo = $this->goodsService->detail($this->order->goods_id);
64 | $client = new Client();
65 | $apiUrl = cfg('bark_server') .'/'. cfg('bark_token');
66 | $params = [
67 | "title" => __('dujiaoka.prompt.new_order_push').'('.$this->order->actual_price.'元)',
68 | "body" => __('order.fields.order_id') .': '.$this->order->id."\n"
69 | . __('order.fields.order_sn') .': '.$this->order->order_sn."\n"
70 | . __('order.fields.pay_id') .': '.$this->order->pay->pay_name."\n"
71 | . __('order.fields.title') .': '.$this->order->title."\n"
72 | . __('order.fields.actual_price') .': '.$this->order->actual_price."\n"
73 | . __('order.fields.email') .': '.$this->order->email."\n"
74 | . __('goods.fields.gd_name') .': '.$goodInfo->gd_name."\n"
75 | . __('goods.fields.stock') .': '.$goodInfo->stock."\n"
76 | . __('order.fields.order_created') .': '.$this->order->created_at,
77 | "icon"=>url('assets/common/images/default.jpg'),
78 | "level"=>"timeSensitive",
79 | "group"=>cfg('text_logo', '独角数卡')
80 | ];
81 | if (cfg('is_open_bark_push_url', 0) == BaseModel::STATUS_OPEN) {
82 | $params["url"] = url('detail-order-sn/'.$this->order->order_sn);
83 | }
84 | $client->post($apiUrl,['form_params' => $params, 'verify' => false]);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/public/assets/common/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/Config/ConfigManager.php:
--------------------------------------------------------------------------------
1 | cachePrefix . $key;
16 |
17 | return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $default) {
18 | return $this->resolveConfig($key, $default);
19 | });
20 | }
21 |
22 | public function set(string $key, $value): void
23 | {
24 | $this->configs[$key] = $value;
25 | Cache::put($this->cachePrefix . $key, $value, $this->cacheTime);
26 | }
27 |
28 | public function forget(string $key): void
29 | {
30 | unset($this->configs[$key]);
31 | Cache::forget($this->cachePrefix . $key);
32 | }
33 |
34 | public function flush(): void
35 | {
36 | $this->configs = [];
37 | Cache::flush();
38 | }
39 |
40 | protected function resolveConfig(string $key, $default)
41 | {
42 | if (isset($this->configs[$key])) {
43 | return $this->configs[$key];
44 | }
45 |
46 | $parts = explode('.', $key);
47 | $type = $parts[0];
48 |
49 | return match ($type) {
50 | 'system' => $this->getSystemConfig($key, $default),
51 | 'theme' => $this->getThemeConfig($key, $default),
52 | 'payment' => $this->getPaymentConfig($key, $default),
53 | default => $default,
54 | };
55 | }
56 |
57 | protected function getSystemConfig(string $key, $default)
58 | {
59 | $systemConfig = Cache::get('system-setting', []);
60 | $configKey = str_replace('system.', '', $key);
61 |
62 | return $systemConfig[$configKey] ?? $default;
63 | }
64 |
65 | protected function getThemeConfig(string $key, $default)
66 | {
67 | $parts = explode('.', $key);
68 | $theme = $parts[1] ?? app('theme.manager')->getActiveTheme();
69 | $configKey = implode('.', array_slice($parts, 2));
70 |
71 | $themeConfig = Cache::get("theme-{$theme}-config", []);
72 |
73 | return data_get($themeConfig, $configKey, $default);
74 | }
75 |
76 | protected function getPaymentConfig(string $key, $default)
77 | {
78 | $paymentConfig = Cache::get('payment-config', []);
79 | $configKey = str_replace('payment.', '', $key);
80 |
81 | return $paymentConfig[$configKey] ?? $default;
82 | }
83 | }
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DRIVER', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Default Cloud Filesystem Disk
21 | |--------------------------------------------------------------------------
22 | |
23 | | Many applications store files both locally and in the cloud. For this
24 | | reason, you may specify a default "cloud" driver here. This driver
25 | | will be bound as the Cloud disk implementation in the container.
26 | |
27 | */
28 |
29 | 'cloud' => env('FILESYSTEM_CLOUD', 's3'),
30 |
31 | /*
32 | |--------------------------------------------------------------------------
33 | | Filesystem Disks
34 | |--------------------------------------------------------------------------
35 | |
36 | | Here you may configure as many filesystem "disks" as you wish, and you
37 | | may even configure multiple disks of the same driver. Defaults have
38 | | been setup for each driver as an example of the required options.
39 | |
40 | | Supported Drivers: "local", "ftp", "sftp", "s3"
41 | |
42 | */
43 |
44 | 'disks' => [
45 |
46 | 'local' => [
47 | 'driver' => 'local',
48 | 'root' => storage_path('app'),
49 | ],
50 |
51 | 'public' => [
52 | 'driver' => 'local',
53 | 'root' => storage_path('app/public'),
54 | 'url' => env('APP_URL').'/storage',
55 | 'visibility' => 'public',
56 | ],
57 |
58 | 's3' => [
59 | 'driver' => 's3',
60 | 'key' => env('AWS_ACCESS_KEY_ID'),
61 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
62 | 'region' => env('AWS_DEFAULT_REGION'),
63 | 'bucket' => env('AWS_BUCKET'),
64 | 'url' => env('AWS_URL'),
65 | 'endpoint' => env('AWS_ENDPOINT'),
66 | ],
67 |
68 | 'admin' => [
69 | 'driver' => 'local',
70 | 'root' => public_path('uploads'),
71 | 'visibility' => 'public',
72 | 'url' => env('APP_URL').'/uploads',
73 | ],
74 | ],
75 |
76 | ];
77 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Home/CartController.php:
--------------------------------------------------------------------------------
1 | render('static_pages/cart', [], '购物车');
17 | }
18 |
19 | public function validateItem(Request $request)
20 | {
21 | $params = $request->only(['goods_id', 'sub_id', 'quantity']);
22 | $qty = (int) ($params['quantity'] ?? 1);
23 |
24 | $goods = cache()->remember("goods_with_sub_{$params['goods_id']}", 21600, function () use ($params) {
25 | return Goods::with('goods_sub')->find($params['goods_id']);
26 | });
27 |
28 | if (!$goods?->is_open) {
29 | return $this->fail('商品不存在或已下架');
30 | }
31 |
32 | $sub = $goods->goods_sub()->find($params['sub_id']);
33 | if (!$sub) {
34 | return $this->fail('商品规格不存在');
35 | }
36 |
37 | $stock = $goods->type == Goods::AUTOMATIC_DELIVERY
38 | ? Carmis::where('sub_id', $params['sub_id'])->where('status', 1)->count()
39 | : $sub->stock;
40 |
41 | if ($qty > $stock) {
42 | return $this->fail("库存不足,当前库存:{$stock}");
43 | }
44 |
45 | if ($goods->buy_limit_num > 0 && $qty > $goods->buy_limit_num) {
46 | return $this->fail("超出限购数量:{$goods->buy_limit_num}");
47 | }
48 |
49 | $enabledPays = cache()->remember('enabled_pay_methods', 43200, function () {
50 | return Pay::enabled()->get();
51 | });
52 | $payways = empty($goods->payment_limit)
53 | ? $enabledPays->toArray()
54 | : $enabledPays->whereIn('id', $goods->payment_limit)->values()->toArray();
55 |
56 | return $this->success([
57 | 'goods_id' => $goods->id,
58 | 'sub_id' => $sub->id,
59 | 'name' => "{$goods->gd_name} [{$sub->name}]",
60 | 'price' => $sub->price,
61 | 'image' => pictureUrl($goods->picture),
62 | 'stock' => $stock,
63 | 'max_quantity' => min($stock, $goods->buy_limit_num ?: $stock),
64 | 'payways' => $payways
65 | ]);
66 | }
67 |
68 | private function success($data = [])
69 | {
70 | return response()->json(['success' => true, 'data' => $data]);
71 | }
72 |
73 | private function fail($message)
74 | {
75 | return response()->json(['success' => false, 'message' => $message]);
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/app/Http/Controllers/InstallController.php:
--------------------------------------------------------------------------------
1 | validate([
30 | 'db_host' => 'required|string',
31 | 'db_port' => 'required|integer',
32 | 'db_database' => 'required|string',
33 | 'db_username' => 'required|string',
34 | 'db_password' => 'nullable|string',
35 | 'redis_host' => 'required|string',
36 | 'redis_port' => 'required|integer',
37 | 'redis_password' => 'nullable|string',
38 | 'title' => 'required|string',
39 | 'app_url' => 'required|url',
40 | 'admin_path' => 'required|string|regex:/^\/[a-zA-Z0-9_-]+$/',
41 | ]);
42 |
43 | // 测试数据库连接
44 | $dbTestResult = Installer::testDatabase([
45 | 'host' => $validated['db_host'],
46 | 'port' => $validated['db_port'],
47 | 'database' => $validated['db_database'],
48 | 'username' => $validated['db_username'],
49 | 'password' => $validated['db_password'] ?? '',
50 | ]);
51 |
52 | if ($dbTestResult !== true) {
53 | $errorMessage = '数据库连接失败: ' . ($dbTestResult['error'] ?? '请检查配置');
54 | return response()->json(['error' => $errorMessage], 400);
55 | }
56 |
57 | // 测试Redis连接
58 | if (!Installer::testRedis([
59 | 'host' => $validated['redis_host'],
60 | 'port' => $validated['redis_port'],
61 | 'password' => $validated['redis_password'] ?? '',
62 | ])) {
63 | return response()->json(['error' => 'Redis连接失败,请检查配置'], 400);
64 | }
65 | $validated['cache_driver'] = 'redis';
66 | $validated['queue_connection'] = 'redis';
67 |
68 | // 执行安装
69 | $result = Installer::install($validated);
70 |
71 | if ($result === 'success') {
72 | return response()->json(['success' => true, 'message' => '安装成功!']);
73 | }
74 |
75 | return response()->json(['error' => $result], 500);
76 | }
77 | }
--------------------------------------------------------------------------------