first commit
This commit is contained in:
commit
8f45044411
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_size = 2
|
||||||
48
.env.example
Normal file
48
.env.example
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
APP_NAME={title}
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY={app_key}
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL={app_url}
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST={db_host}
|
||||||
|
DB_PORT={db_port}
|
||||||
|
DB_DATABASE={db_database}
|
||||||
|
DB_USERNAME={db_username}
|
||||||
|
DB_PASSWORD={db_password}
|
||||||
|
|
||||||
|
# redis配置
|
||||||
|
REDIS_HOST={redis_host}
|
||||||
|
REDIS_PASSWORD={redis_password}
|
||||||
|
REDIS_PORT={redis_port}
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
|
||||||
|
# 缓存配置
|
||||||
|
# file为磁盘文件 redis为内存级别
|
||||||
|
# redis为内存需要安装好redis服务端并配置
|
||||||
|
CACHE_DRIVER=redis
|
||||||
|
|
||||||
|
# 异步消息队列
|
||||||
|
# sync为同步 redis为异步
|
||||||
|
# 使用redis异步需要安装好redis服务端并配置
|
||||||
|
QUEUE_CONNECTION=redis
|
||||||
|
|
||||||
|
# 后台语言
|
||||||
|
## zh_CN 简体中文
|
||||||
|
## zh_TW 繁体中文
|
||||||
|
## en 英文
|
||||||
|
DUJIAO_ADMIN_LANGUAGE=zh_CN
|
||||||
|
|
||||||
|
# 后台登录地址
|
||||||
|
ADMIN_ROUTE_PREFIX={admin_path}
|
||||||
|
|
||||||
|
# 是否开启https (前端开启了后端也必须为true)
|
||||||
|
# 后台登录出现0err或者其他登录异常问题,大概率是开启了https而后台没有开启,把下面的false改为true即可
|
||||||
|
ADMIN_HTTPS=false
|
||||||
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
* text=auto
|
||||||
|
*.css linguist-vendored
|
||||||
|
*.scss linguist-vendored
|
||||||
|
*.js linguist-vendored
|
||||||
|
CHANGELOG.md export-ignore
|
||||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/node_modules
|
||||||
|
/public/hot
|
||||||
|
/public/storage
|
||||||
|
/public/uploads
|
||||||
|
/storage/*.key
|
||||||
|
/vendor
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.phpunit.result.cache
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
.idea
|
||||||
|
install.lock
|
||||||
|
.env.buck
|
||||||
13
.styleci.yml
Normal file
13
.styleci.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
php:
|
||||||
|
preset: laravel
|
||||||
|
disabled:
|
||||||
|
- unused_use
|
||||||
|
finder:
|
||||||
|
not-name:
|
||||||
|
- index.php
|
||||||
|
- server.php
|
||||||
|
js:
|
||||||
|
finder:
|
||||||
|
not-name:
|
||||||
|
- webpack.mix.js
|
||||||
|
css: true
|
||||||
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
FROM webdevops/php-nginx:7.4
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
RUN [ "sh", "-c", "composer install --ignore-platform-reqs" ]
|
||||||
|
RUN echo "#!/bin/bash\nphp artisan queue:work >/tmp/work.log 2>&1 &\nsupervisord" > /app/start.sh
|
||||||
|
RUN [ "sh", "-c", "chmod -R 777 /app" ]
|
||||||
|
CMD [ "sh", "-c","/app/start.sh" ]
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 assimon/dujiaoka
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<p align="center"><img src="https://i.loli.net/2020/04/07/nAzjDJlX7oc5qEw.png" width="400"></p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue" alt="license MIT"></a>
|
||||||
|
<a href="https://github.com/assimon/dujiaoka/releases/tag/2.0.4"><img src="https://img.shields.io/badge/version-2.0.4-red" alt="version 2.0.4"></a>
|
||||||
|
<a href="https://www.php.net/releases/7_4_0.php"><img src="https://img.shields.io/badge/PHP-7.4-lightgrey" alt="php74"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://www.vmrack.net/?ref_code=5iXmGUMf5f5">
|
||||||
|
<img src="https://files.seeusercontent.com/2026/03/12/2mXf/photo_2026-03-12_10-13-12.jpg" width="400">
|
||||||
|
</a>
|
||||||
|
<p align="center"><a href="https://www.vmrack.net/?ref_code=5iXmGUMf5f5">vmrack.net - 全球自动化云基础设施服务商
|
||||||
|
提供先进的云服务器、裸金属、CDN、媒体处理、对象存储和网络解决方案,助力企业轻松上云</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 📢停更通知2026/02/12
|
||||||
|
|
||||||
|
### 项目已经停止更新和维护,请前往新版[Dujiao-Next(dujiao-next.com)](https://dujiao-next.com)
|
||||||
53
app/Admin/Actions/Post/BatchRestore.php
Normal file
53
app/Admin/Actions/Post/BatchRestore.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Actions\Post;
|
||||||
|
|
||||||
|
|
||||||
|
use Dcat\Admin\Grid\BatchAction;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class BatchRestore extends BatchAction
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
protected $model;
|
||||||
|
|
||||||
|
// 注意构造方法的参数必须要有默认值
|
||||||
|
public function __construct(string $model = null)
|
||||||
|
{
|
||||||
|
$this->title = admin_trans('dujiaoka.restore');
|
||||||
|
$this->model = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$model = $request->get('model');
|
||||||
|
|
||||||
|
foreach ((array) $this->getKey() as $key) {
|
||||||
|
$model::withTrashed()->findOrFail($key)->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response()->success(admin_trans('dujiaoka.restored'))->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm()
|
||||||
|
{
|
||||||
|
return [admin_trans('dujiaoka.are_you_restore_sure')];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parameters()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'model' => $this->model,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
app/Admin/Actions/Post/Restore.php
Normal file
52
app/Admin/Actions/Post/Restore.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Actions\Post;
|
||||||
|
|
||||||
|
|
||||||
|
use Dcat\Admin\Grid\RowAction;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Restore extends RowAction
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
protected $model;
|
||||||
|
|
||||||
|
// 注意构造方法的参数必须要有默认值
|
||||||
|
public function __construct(string $model = null)
|
||||||
|
{
|
||||||
|
$this->title = admin_trans('dujiaoka.restore');
|
||||||
|
$this->model = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$key = $this->getKey();
|
||||||
|
$model = $request->get('model');
|
||||||
|
|
||||||
|
$model::withTrashed()->findOrFail($key)->restore();
|
||||||
|
|
||||||
|
return $this->response()->success(admin_trans('dujiaoka.restored'))->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm()
|
||||||
|
{
|
||||||
|
return [admin_trans('dujiaoka.are_you_restore_sure')];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parameters()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'model' => $this->model,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
176
app/Admin/Charts/DashBoard.php
Normal file
176
app/Admin/Charts/DashBoard.php
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Charts;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Dcat\Admin\Widgets\Metrics\RadialBar;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class DashBoard extends RadialBar
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化卡片内容
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$this->title(admin_trans('dujiaoka.sales_data'));
|
||||||
|
$this->height(400);
|
||||||
|
$this->chartHeight(300);
|
||||||
|
$this->chartLabels(admin_trans('dujiaoka.order_success_rate'));
|
||||||
|
$this->dropdown([
|
||||||
|
'today' => admin_trans('dujiaoka.last_today'),
|
||||||
|
'seven' => admin_trans('dujiaoka.last_seven_days'),
|
||||||
|
'month' => admin_trans('dujiaoka.last_month'),
|
||||||
|
'year' => admin_trans('dujiaoka.last_year'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return mixed|void
|
||||||
|
*/
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$endTime = Carbon::now();
|
||||||
|
switch ($request->get('option')) {
|
||||||
|
case 'seven':
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
$startTime = Carbon::now()->subDays(30);
|
||||||
|
break;
|
||||||
|
case 'year':
|
||||||
|
$startTime = Carbon::now()->subDays(365);
|
||||||
|
break;
|
||||||
|
case 'today':
|
||||||
|
default:
|
||||||
|
$startTime = Carbon::today();
|
||||||
|
}
|
||||||
|
// 分组查询
|
||||||
|
$orderGroup = Order::query()
|
||||||
|
->where('created_at', '>=', $startTime)
|
||||||
|
->where('created_at', '<=', $endTime)
|
||||||
|
->select('status', DB::raw('count(id) as num'))
|
||||||
|
->groupBy('status')
|
||||||
|
->pluck('num', 'status')
|
||||||
|
->toArray();
|
||||||
|
$pending = $orderGroup[Order::STATUS_PENDING] ?? 0;
|
||||||
|
$processing = $orderGroup[Order::STATUS_PROCESSING] ?? 0;
|
||||||
|
$completed = $orderGroup[Order::STATUS_COMPLETED] ?? 0;
|
||||||
|
$failure = $orderGroup[Order::STATUS_FAILURE] ?? 0;
|
||||||
|
$abnormal = $orderGroup[Order::STATUS_ABNORMAL] ?? 0;
|
||||||
|
$orderCount = array_sum($orderGroup);
|
||||||
|
if ($orderCount == 0) {
|
||||||
|
$successRate = 0;
|
||||||
|
} else {
|
||||||
|
$rate = bcdiv($completed, $orderCount, 2);
|
||||||
|
$successRate = bcmul($rate, 100);
|
||||||
|
}
|
||||||
|
// 订单数
|
||||||
|
$this->withOrderCount($orderCount);
|
||||||
|
// 卡片底部
|
||||||
|
$this->withFooter($pending, $processing, $completed, $failure, $abnormal);
|
||||||
|
// 图表数据
|
||||||
|
$this->withChart($successRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单总数
|
||||||
|
*
|
||||||
|
* @param $count
|
||||||
|
* @return DashBoard
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function withOrderCount($count)
|
||||||
|
{
|
||||||
|
$title = admin_trans('dujiaoka.order_count_number');
|
||||||
|
return $this->content(
|
||||||
|
<<<HTML
|
||||||
|
<div class="d-flex flex-column flex-wrap text-center">
|
||||||
|
<h1 class="font-lg-2 mt-2 mb-0">{$count}</h1>
|
||||||
|
<small>{$title}</small>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成交率.
|
||||||
|
*
|
||||||
|
* @param int $data
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withChart(int $data)
|
||||||
|
{
|
||||||
|
return $this->chart([
|
||||||
|
'series' => [$data],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $pending
|
||||||
|
* @param $processing
|
||||||
|
* @param $completed
|
||||||
|
* @param $failure
|
||||||
|
* @param $abnormal
|
||||||
|
* @return DashBoard
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function withFooter($pending, $processing, $completed, $failure, $abnormal)
|
||||||
|
{
|
||||||
|
$statusPendingTitle = admin_trans('dujiaoka.status_pending_number');
|
||||||
|
$statusProcessingNumber = admin_trans('dujiaoka.status_processing_number');
|
||||||
|
$statusCompletedNumber = admin_trans('dujiaoka.status_completed_number');
|
||||||
|
$statusFailureNumber = admin_trans('dujiaoka.status_failure_number');
|
||||||
|
$statusAbnormalNumber = admin_trans('dujiaoka.status_abnormal_number');
|
||||||
|
return $this->footer(
|
||||||
|
<<<HTML
|
||||||
|
<div class="d-flex justify-content-between p-1" style="padding-top: 0!important;">
|
||||||
|
<div class="text-center">
|
||||||
|
<p>{$statusPendingTitle}</p>
|
||||||
|
<span class="font-lg-1">{$pending}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p>{$statusProcessingNumber}</p>
|
||||||
|
<span class="font-lg-1">{$processing}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p>{$statusCompletedNumber}</p>
|
||||||
|
<span class="font-lg-1">{$completed}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p>{$statusFailureNumber}</p>
|
||||||
|
<span class="font-lg-1">{$failure}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<p>{$statusAbnormalNumber}</p>
|
||||||
|
<span class="font-lg-1">{$abnormal}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
137
app/Admin/Charts/PayoutRateCard.php
Normal file
137
app/Admin/Charts/PayoutRateCard.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Charts;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Widgets\Metrics\Donut;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class PayoutRateCard extends Donut
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $labels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化卡片内容
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$this->labels = [admin_trans('dujiaoka.payment_successful_number'), admin_trans('dujiaoka.unpaid_number')];
|
||||||
|
$color = Admin::color();
|
||||||
|
$colors = [$color->primary(), $color->alpha('blue2', 0.5)];
|
||||||
|
|
||||||
|
$this->title(admin_trans('dujiaoka.payment_chart'));
|
||||||
|
$this->chartLabels($this->labels);
|
||||||
|
// 设置图表颜色
|
||||||
|
$this->chartColors($colors);
|
||||||
|
$this->dropdown([
|
||||||
|
'seven' => admin_trans('dujiaoka.last_seven_days'),
|
||||||
|
'today' => admin_trans('dujiaoka.last_today'),
|
||||||
|
'month' => admin_trans('dujiaoka.last_month'),
|
||||||
|
'year' => admin_trans('dujiaoka.last_year'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return mixed|void
|
||||||
|
*/
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$endTime = Carbon::now();
|
||||||
|
switch ($request->get('option')) {
|
||||||
|
case 'seven':
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
$startTime = Carbon::now()->subDays(30);
|
||||||
|
break;
|
||||||
|
case 'year':
|
||||||
|
$startTime = Carbon::now()->subDays(365);
|
||||||
|
break;
|
||||||
|
case 'today':
|
||||||
|
$startTime = Carbon::today();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
}
|
||||||
|
// 成功的数量
|
||||||
|
$success = Order::query()
|
||||||
|
->where('created_at', '>=', $startTime)
|
||||||
|
->where('created_at', '<=', $endTime)
|
||||||
|
->where('status', '>', Order::STATUS_WAIT_PAY)
|
||||||
|
->count();
|
||||||
|
// 待支付的数量
|
||||||
|
$unpaid = Order::query()
|
||||||
|
->where('created_at', '>=', $startTime)
|
||||||
|
->where('created_at', '<=', $endTime)
|
||||||
|
->where('status', '<=', Order::STATUS_WAIT_PAY)
|
||||||
|
->count();
|
||||||
|
$this->withContent($success, $unpaid);
|
||||||
|
// 图表数据
|
||||||
|
$this->withChart([$success, $unpaid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图表数据.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withChart(array $data)
|
||||||
|
{
|
||||||
|
return $this->chart([
|
||||||
|
'series' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置卡片头部内容.
|
||||||
|
*
|
||||||
|
* @param mixed $success
|
||||||
|
* @param mixed $unpaid
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
protected function withContent($success, $unpaid)
|
||||||
|
{
|
||||||
|
$blue = Admin::color()->alpha('blue2', 0.5);
|
||||||
|
|
||||||
|
$style = 'margin-bottom: 8px';
|
||||||
|
$labelWidth = 120;
|
||||||
|
|
||||||
|
return $this->content(
|
||||||
|
<<<HTML
|
||||||
|
<div class="d-flex pl-1 pr-1 pt-1" style="{$style}">
|
||||||
|
<div style="width: {$labelWidth}px">
|
||||||
|
<i class="fa fa-circle text-primary"></i> {$this->labels[0]}
|
||||||
|
</div>
|
||||||
|
<div>{$success}</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex pl-1 pr-1" style="{$style}">
|
||||||
|
<div style="width: {$labelWidth}px">
|
||||||
|
<i class="fa fa-circle" style="color: $blue"></i> {$this->labels[1]}
|
||||||
|
</div>
|
||||||
|
<div>{$unpaid}</div>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
139
app/Admin/Charts/SalesCard.php
Normal file
139
app/Admin/Charts/SalesCard.php
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Charts;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Widgets\Metrics\Bar;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SalesCard extends Bar
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化卡片内容
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$color = Admin::color();
|
||||||
|
|
||||||
|
$dark35 = $color->dark35();
|
||||||
|
|
||||||
|
// 卡片内容宽度
|
||||||
|
$this->contentWidth(5, 7);
|
||||||
|
// 标题
|
||||||
|
$this->title(admin_trans('dujiaoka.sales_chart'));
|
||||||
|
// 设置下拉选项
|
||||||
|
$this->dropdown([
|
||||||
|
'seven' => admin_trans('dujiaoka.last_seven_days'),
|
||||||
|
'today' => admin_trans('dujiaoka.last_today'),
|
||||||
|
'month' => admin_trans('dujiaoka.last_month'),
|
||||||
|
]);
|
||||||
|
// 设置图表颜色
|
||||||
|
$this->chartColors([
|
||||||
|
$dark35,
|
||||||
|
$color->primary(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return mixed|void
|
||||||
|
*/
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$endTime = Carbon::now();
|
||||||
|
switch ($request->get('option')) {
|
||||||
|
case 'seven':
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
$startTime = Carbon::now()->subDays(30);
|
||||||
|
break;
|
||||||
|
case 'today':
|
||||||
|
$startTime = Carbon::today();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
}
|
||||||
|
// 分组查询
|
||||||
|
$orderGroup = Order::query()
|
||||||
|
->where('created_at', '>=', $startTime)
|
||||||
|
->where('created_at', '<=', $endTime)
|
||||||
|
->where('status', '>', Order::STATUS_PENDING)
|
||||||
|
->select(DB::raw('DATE(created_at) as date'), DB::raw('sum(actual_price) as actual_price'))
|
||||||
|
->groupBy('date')
|
||||||
|
->pluck('actual_price')
|
||||||
|
->toArray();
|
||||||
|
$totalPrice = array_sum($orderGroup);
|
||||||
|
$this->withContent($totalPrice, '', '', $startTime, $endTime);
|
||||||
|
$this->withChart([
|
||||||
|
[
|
||||||
|
'name' => admin_trans('dujiaoka.sales_chart'),
|
||||||
|
'data' => $orderGroup,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图表数据.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withChart(array $data)
|
||||||
|
{
|
||||||
|
return $this->chart([
|
||||||
|
'series' => $data,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置卡片内容.
|
||||||
|
*
|
||||||
|
* @param string $title
|
||||||
|
* @param string $value
|
||||||
|
* @param string $style
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withContent($title, $value, $style = 'success', $startTime, $endTime)
|
||||||
|
{
|
||||||
|
$minHeight = '183px';
|
||||||
|
$uri = admin_route('order.index', [
|
||||||
|
'created_at[start]' => $startTime->format('Y-m-d H:i:s'),
|
||||||
|
'created_at[end]' => $endTime->format('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
return $this->content(
|
||||||
|
<<<HTML
|
||||||
|
<div class="d-flex p-1 flex-column justify-content-between" style="padding-top: 0;width: 100%;height: 100%;min-height: {$minHeight}">
|
||||||
|
<div class="text-left">
|
||||||
|
<h1 class="font-lg-2 mt-2 mb-0">{$title}¥</h1>
|
||||||
|
<h5 class="font-medium-2" style="margin-top: 10px;">
|
||||||
|
<span class="text-{$style}">{$value} </span>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="{$uri}" class="btn btn-primary shadow waves-effect waves-light">View Details <i class="feather icon-chevrons-right"></i></a>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
116
app/Admin/Charts/SuccessOrderCard.php
Normal file
116
app/Admin/Charts/SuccessOrderCard.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Charts;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Dcat\Admin\Widgets\Metrics\Line;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SuccessOrderCard extends Line
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化卡片内容
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$this->title(admin_trans('dujiaoka.status_completed_number'));
|
||||||
|
$this->dropdown([
|
||||||
|
'seven' => admin_trans('dujiaoka.last_seven_days'),
|
||||||
|
'today' => admin_trans('dujiaoka.last_today'),
|
||||||
|
'month' => admin_trans('dujiaoka.last_month'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return mixed|void
|
||||||
|
*/
|
||||||
|
public function handle(Request $request)
|
||||||
|
{
|
||||||
|
$endTime = Carbon::now();
|
||||||
|
switch ($request->get('option')) {
|
||||||
|
case 'seven':
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
$startTime = Carbon::now()->subDays(30);
|
||||||
|
break;
|
||||||
|
case 'today':
|
||||||
|
$startTime = Carbon::today();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$startTime = Carbon::now()->subDays(7);
|
||||||
|
}
|
||||||
|
// 分组查询
|
||||||
|
$orderGroup = Order::query()
|
||||||
|
->where('created_at', '>=', $startTime)
|
||||||
|
->where('created_at', '<=', $endTime)
|
||||||
|
->where('status', Order::STATUS_COMPLETED)
|
||||||
|
->select(DB::raw('DATE(created_at) as date'), DB::raw('count(id) as num'))
|
||||||
|
->groupBy('date')
|
||||||
|
->pluck('num')
|
||||||
|
->toArray();
|
||||||
|
$successCount = array_sum($orderGroup);
|
||||||
|
// 卡片内容
|
||||||
|
$this->withContent($successCount);
|
||||||
|
// 图表数据
|
||||||
|
$this->withChart($orderGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图表数据.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withChart(array $data)
|
||||||
|
{
|
||||||
|
return $this->chart([
|
||||||
|
'series' => [
|
||||||
|
[
|
||||||
|
'name' => $this->title,
|
||||||
|
'data' => $data,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置卡片内容.
|
||||||
|
*
|
||||||
|
* @param string $content
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function withContent($content)
|
||||||
|
{
|
||||||
|
return $this->content(
|
||||||
|
<<<HTML
|
||||||
|
<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
|
||||||
|
<h2 class="ml-1 font-lg-1">{$content}</h2>
|
||||||
|
<span class="mb-0 mr-1 text-80">{$this->title}</span>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
9
app/Admin/Controllers/AuthController.php
Normal file
9
app/Admin/Controllers/AuthController.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use Dcat\Admin\Http\Controllers\AuthController as BaseAuthController;
|
||||||
|
|
||||||
|
class AuthController extends BaseAuthController
|
||||||
|
{
|
||||||
|
}
|
||||||
124
app/Admin/Controllers/CarmisController.php
Normal file
124
app/Admin/Controllers/CarmisController.php
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Forms\ImportCarmis;
|
||||||
|
use App\Admin\Repositories\Carmis;
|
||||||
|
use App\Models\Goods;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Layout\Content;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Carmis as CarmisModel;
|
||||||
|
use Dcat\Admin\Widgets\Card;
|
||||||
|
|
||||||
|
class CarmisController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Carmis(['goods']), function (Grid $grid) {
|
||||||
|
$grid->model()->orderBy('id', 'DESC');
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('goods.gd_name', admin_trans('carmis.fields.goods_id'));
|
||||||
|
$grid->column('status')->select(CarmisModel::getStatusMap());
|
||||||
|
$grid->column('is_loop')->display(function($v){return $v==1?admin_trans('carmis.fields.yes'):"";});
|
||||||
|
$grid->column('carmi')->limit(20);
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->equal('goods_id')->select(
|
||||||
|
Goods::query()->where('type', Goods::AUTOMATIC_DELIVERY)->pluck('gd_name', 'id')
|
||||||
|
);
|
||||||
|
$filter->equal('status')->select(CarmisModel::getStatusMap());
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(CarmisModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(CarmisModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->export()->titles(['goods.gd_name' => admin_trans('carmis.fields.goods_id'), 'carmi' => admin_trans('carmis.fields.carmi'), 'created_at' => admin_trans('admin.created_at')]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Carmis(['goods']), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('goods.gd_name', admin_trans('carmis.fields.goods_id'));
|
||||||
|
$show->field('status')->as(function ($type) {
|
||||||
|
if ($type == CarmisModel::STATUS_UNSOLD) {
|
||||||
|
return admin_trans('carmis.fields.status_unsold');
|
||||||
|
} else {
|
||||||
|
return admin_trans('carmis.fields.status_sold');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('is_loop')->as(function ($v) {return $v==1?admin_trans('carmis.fields.yes'):"";});
|
||||||
|
$show->field('carmi');
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new Carmis(), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->select('goods_id')->options(
|
||||||
|
Goods::query()->where('type', Goods::AUTOMATIC_DELIVERY)->pluck('gd_name', 'id')
|
||||||
|
)->required();
|
||||||
|
$form->radio('status')
|
||||||
|
->options(CarmisModel::getStatusMap())
|
||||||
|
->default(CarmisModel::STATUS_UNSOLD);
|
||||||
|
$form->switch('is_loop')->default(false);
|
||||||
|
$form->textarea('carmi')->required();
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入卡密
|
||||||
|
*
|
||||||
|
* @param Content $content
|
||||||
|
* @return Content
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function importCarmis(Content $content)
|
||||||
|
{
|
||||||
|
return $content
|
||||||
|
->title(admin_trans('carmis.fields.import_carmis'))
|
||||||
|
->body(new Card(new ImportCarmis()));
|
||||||
|
}
|
||||||
|
}
|
||||||
116
app/Admin/Controllers/CouponController.php
Normal file
116
app/Admin/Controllers/CouponController.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\Coupon;
|
||||||
|
use App\Models\Goods;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Coupon as CouponModel;
|
||||||
|
|
||||||
|
class CouponController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Coupon(['goods']), function (Grid $grid) {
|
||||||
|
$grid->model()->orderBy('id', 'DESC');
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('discount');
|
||||||
|
$grid->column('is_use')->select(CouponModel::getStatusUseMap());
|
||||||
|
$grid->column('is_open')->switch();
|
||||||
|
$grid->column('coupon')->copyable();
|
||||||
|
$grid->column('ret');
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(CouponModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(CouponModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->equal('goods.goods_id', admin_trans('coupon.fields.goods_id'))->select(
|
||||||
|
Goods::query()->pluck('gd_name', 'id')
|
||||||
|
);
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Coupon(), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('discount');
|
||||||
|
$show->field('is_use')->as(function ($isUse) {
|
||||||
|
if ($isUse == CouponModel::STATUS_UNUSED) {
|
||||||
|
return admin_trans('coupon.fields.status_unused');
|
||||||
|
} else {
|
||||||
|
return admin_trans('coupon.fields.status_use');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('is_open')->as(function ($isOPen) {
|
||||||
|
if ($isOPen == CouponModel::STATUS_OPEN) {
|
||||||
|
return admin_trans('dujiaoka.status_open');
|
||||||
|
} else {
|
||||||
|
return admin_trans('dujiaoka.status_close');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('coupon');
|
||||||
|
$show->field('ret');
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(Coupon::with('goods'), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->multipleSelect('goods', admin_trans('coupon.fields.goods_id'))
|
||||||
|
->options(Goods::all()->pluck('gd_name', 'id'))
|
||||||
|
->customFormat(function ($v) {
|
||||||
|
if (! $v) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// 从数据库中查出的二维数组中转化成ID
|
||||||
|
return array_column($v, 'id');
|
||||||
|
});
|
||||||
|
$form->currency('discount')->default(0)->required();
|
||||||
|
$form->text('coupon')->required();
|
||||||
|
$form->number('ret')->default(1);
|
||||||
|
$form->radio('is_use')->options(CouponModel::getStatusUseMap())->default(CouponModel::STATUS_UNUSED);
|
||||||
|
$form->switch('is_open')->default(CouponModel::STATUS_OPEN);
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Admin/Controllers/EmailTestController.php
Normal file
38
app/Admin/Controllers/EmailTestController.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author ZhangYiQiu<me@zhangyiqiu.net>
|
||||||
|
* @copyright ZhangYiQiu<me@zhangyiqiu.net>
|
||||||
|
* @link http://zhangyiqiu.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Admin\Forms\EmailTest;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use Dcat\Admin\Layout\Content;
|
||||||
|
use Dcat\Admin\Widgets\Card;
|
||||||
|
|
||||||
|
class EmailTestController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统设置
|
||||||
|
*
|
||||||
|
* @param Content $content
|
||||||
|
* @return Content
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function emailTest(Content $content)
|
||||||
|
{
|
||||||
|
return $content
|
||||||
|
->title(admin_trans('menu.titles.email_test'))
|
||||||
|
->body(new Card(new EmailTest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
app/Admin/Controllers/EmailtplController.php
Normal file
94
app/Admin/Controllers/EmailtplController.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\Emailtpl;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Emailtpl as EmailTplModel;
|
||||||
|
|
||||||
|
class EmailtplController extends AdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Emailtpl(), function (Grid $grid) {
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('tpl_name');
|
||||||
|
$grid->column('tpl_token');
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->disableViewButton();
|
||||||
|
$grid->disableDeleteButton();
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->like('tpl_name');
|
||||||
|
$filter->like('tpl_token');
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(EmailTplModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(EmailTplModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Emailtpl(), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('tpl_name');
|
||||||
|
$show->field('tpl_content');
|
||||||
|
$show->field('tpl_token');
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new Emailtpl(), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->text('tpl_name')->required();
|
||||||
|
$form->editor('tpl_content')->required();
|
||||||
|
if ($form->isCreating()) {
|
||||||
|
$form->text('tpl_token')->required();
|
||||||
|
} else {
|
||||||
|
$form->text('tpl_token')->disable();
|
||||||
|
}
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
$form->disableViewButton();
|
||||||
|
$form->disableDeleteButton();
|
||||||
|
$form->footer(function ($footer) {
|
||||||
|
// 去掉`查看`checkbox
|
||||||
|
$footer->disableViewCheck();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
160
app/Admin/Controllers/GoodsController.php
Normal file
160
app/Admin/Controllers/GoodsController.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\Goods;
|
||||||
|
use App\Models\Carmis;
|
||||||
|
use App\Models\Coupon;
|
||||||
|
use App\Models\GoodsGroup as GoodsGroupModel;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Goods as GoodsModel;
|
||||||
|
|
||||||
|
class GoodsController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Goods(['group', 'coupon']), function (Grid $grid) {
|
||||||
|
$grid->model()->orderBy('id', 'DESC');
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('picture')->image('', 100, 100);
|
||||||
|
$grid->column('gd_name');
|
||||||
|
$grid->column('gd_description');
|
||||||
|
$grid->column('gd_keywords');
|
||||||
|
$grid->column('group.gp_name', admin_trans('goods.fields.group_id'));
|
||||||
|
$grid->column('type')
|
||||||
|
->using(GoodsModel::getGoodsTypeMap())
|
||||||
|
->label([
|
||||||
|
GoodsModel::AUTOMATIC_DELIVERY => Admin::color()->success(),
|
||||||
|
GoodsModel::MANUAL_PROCESSING => Admin::color()->info(),
|
||||||
|
]);
|
||||||
|
$grid->column('retail_price');
|
||||||
|
$grid->column('actual_price')->sortable();
|
||||||
|
$grid->column('in_stock')->display(function () {
|
||||||
|
// 如果为自动发货,则加载库存卡密
|
||||||
|
if ($this->type == GoodsModel::AUTOMATIC_DELIVERY) {
|
||||||
|
return Carmis::query()->where('goods_id', $this->id)
|
||||||
|
->where('status', Carmis::STATUS_UNSOLD)
|
||||||
|
->count();
|
||||||
|
} else {
|
||||||
|
return $this->in_stock;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->column('sales_volume');
|
||||||
|
$grid->column('ord')->editable()->sortable();
|
||||||
|
$grid->column('is_open')->switch();
|
||||||
|
$grid->column('created_at')->sortable();
|
||||||
|
$grid->column('updated_at');
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->like('gd_name');
|
||||||
|
$filter->equal('type')->select(GoodsModel::getGoodsTypeMap());
|
||||||
|
$filter->equal('group_id')->select(GoodsGroupModel::query()->pluck('gp_name', 'id'));
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
$filter->equal('coupon.coupons_id', admin_trans('goods.fields.coupon_id'))->select(
|
||||||
|
Coupon::query()->pluck('coupon', 'id')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(GoodsModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(GoodsModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Goods(), function (Show $show) {
|
||||||
|
$show->id('id');
|
||||||
|
$show->field('gd_name');
|
||||||
|
$show->field('gd_description');
|
||||||
|
$show->field('gd_keywords');
|
||||||
|
$show->field('picture')->image();
|
||||||
|
$show->field('retail_price');
|
||||||
|
$show->field('actual_price');
|
||||||
|
$show->field('in_stock');
|
||||||
|
$show->field('ord');
|
||||||
|
$show->field('sales_volume');
|
||||||
|
$show->field('type')->as(function ($type) {
|
||||||
|
if ($type == GoodsModel::AUTOMATIC_DELIVERY) {
|
||||||
|
return admin_trans('goods.fields.automatic_delivery');
|
||||||
|
} else {
|
||||||
|
return admin_trans('goods.fields.manual_processing');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('is_open')->as(function ($isOpen) {
|
||||||
|
if ($isOpen == GoodsGroupModel::STATUS_OPEN) {
|
||||||
|
return admin_trans('dujiaoka.status_open');
|
||||||
|
} else {
|
||||||
|
return admin_trans('dujiaoka.status_close');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->wholesale_price_cnf()->unescape()->as(function ($wholesalePriceCnf) {
|
||||||
|
return "<textarea class=\"form-control field_wholesale_price_cnf _normal_\" rows=\"10\" cols=\"30\">" . $wholesalePriceCnf . "</textarea>";
|
||||||
|
});
|
||||||
|
$show->other_ipu_cnf()->unescape()->as(function ($otherIpuCnf) {
|
||||||
|
return "<textarea class=\"form-control field_wholesale_price_cnf _normal_\" rows=\"10\" cols=\"30\">" . $otherIpuCnf . "</textarea>";
|
||||||
|
});
|
||||||
|
$show->api_hook()->unescape()->as(function ($apiHook) {
|
||||||
|
return "<textarea class=\"form-control field_wholesale_price_cnf _normal_\" rows=\"10\" cols=\"30\">" . $apiHook . "</textarea>";
|
||||||
|
});;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new Goods(), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->text('gd_name')->required();
|
||||||
|
$form->text('gd_description')->required();
|
||||||
|
$form->text('gd_keywords')->required();
|
||||||
|
$form->select('group_id')->options(
|
||||||
|
GoodsGroupModel::query()->pluck('gp_name', 'id')
|
||||||
|
)->required();
|
||||||
|
$form->image('picture')->autoUpload()->uniqueName()->help(admin_trans('goods.helps.picture'));
|
||||||
|
$form->radio('type')->options(GoodsModel::getGoodsTypeMap())->default(GoodsModel::AUTOMATIC_DELIVERY)->required();
|
||||||
|
$form->currency('retail_price')->default(0)->help(admin_trans('goods.helps.retail_price'));
|
||||||
|
$form->currency('actual_price')->default(0)->required();
|
||||||
|
$form->number('in_stock')->help(admin_trans('goods.helps.in_stock'));
|
||||||
|
$form->number('sales_volume');
|
||||||
|
$form->number('buy_limit_num')->help(admin_trans('goods.helps.buy_limit_num'));
|
||||||
|
$form->editor('buy_prompt');
|
||||||
|
$form->editor('description');
|
||||||
|
$form->textarea('other_ipu_cnf')->help(admin_trans('goods.helps.other_ipu_cnf'));
|
||||||
|
$form->textarea('wholesale_price_cnf')->help(admin_trans('goods.helps.wholesale_price_cnf'));
|
||||||
|
$form->textarea('api_hook');
|
||||||
|
$form->number('ord')->default(1)->help(admin_trans('dujiaoka.ord'));
|
||||||
|
$form->switch('is_open')->default(GoodsModel::STATUS_OPEN);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
96
app/Admin/Controllers/GoodsGroupController.php
Normal file
96
app/Admin/Controllers/GoodsGroupController.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\GoodsGroup;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\GoodsGroup as GoodsGroupModel;
|
||||||
|
|
||||||
|
class GoodsGroupController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new GoodsGroup(), function (Grid $grid) {
|
||||||
|
$grid->model()->orderBy('id', 'DESC');
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('gp_name')->editable();
|
||||||
|
$grid->column('is_open')->switch();
|
||||||
|
$grid->column('ord')->editable();
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->disableViewButton();
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(GoodsGroupModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(GoodsGroupModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new GoodsGroup(), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('gp_name');
|
||||||
|
$show->field('is_open')->as(function ($isOpen) {
|
||||||
|
if ($isOpen == GoodsGroupModel::STATUS_OPEN) {
|
||||||
|
return admin_trans('dujiaoka.status_open');
|
||||||
|
} else {
|
||||||
|
return admin_trans('dujiaoka.status_close');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('ord');
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new GoodsGroup(), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->text('gp_name');
|
||||||
|
$form->switch('is_open')->default(GoodsGroupModel::STATUS_OPEN);
|
||||||
|
$form->number('ord')->default(1)->help(admin_trans('dujiaoka.ord'));
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
$form->disableViewButton();
|
||||||
|
$form->footer(function ($footer) {
|
||||||
|
// 去掉`查看`checkbox
|
||||||
|
$footer->disableViewCheck();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Admin/Controllers/HomeController.php
Normal file
44
app/Admin/Controllers/HomeController.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Charts\DashBoard;
|
||||||
|
use App\Admin\Charts\PayoutRateCard;
|
||||||
|
use App\Admin\Charts\PopularGoodsCard;
|
||||||
|
use App\Admin\Charts\SalesCard;
|
||||||
|
use App\Admin\Charts\SuccessOrderCard;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Dcat\Admin\Layout\Column;
|
||||||
|
use Dcat\Admin\Layout\Content;
|
||||||
|
use Dcat\Admin\Layout\Row;
|
||||||
|
|
||||||
|
class HomeController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
public function index(Content $content)
|
||||||
|
{
|
||||||
|
return $content
|
||||||
|
->header(admin_trans('dujiaoka.dashboard'))
|
||||||
|
->description(admin_trans('dujiaoka.dashboard_description'))
|
||||||
|
->body(function (Row $row) {
|
||||||
|
$row->column(6, function (Column $column) {
|
||||||
|
$column->row(self::title());
|
||||||
|
$column->row(new DashBoard());
|
||||||
|
});
|
||||||
|
|
||||||
|
$row->column(6, function (Column $column) {
|
||||||
|
$column->row(function (Row $row) {
|
||||||
|
$row->column(6, new SuccessOrderCard());
|
||||||
|
$row->column(6, new PayoutRateCard());
|
||||||
|
});
|
||||||
|
|
||||||
|
$column->row(new SalesCard());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function title()
|
||||||
|
{
|
||||||
|
return view('admin.dashboard.title');
|
||||||
|
}
|
||||||
|
}
|
||||||
156
app/Admin/Controllers/OrderController.php
Normal file
156
app/Admin/Controllers/OrderController.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\Order;
|
||||||
|
use App\Models\Coupon;
|
||||||
|
use App\Models\Goods;
|
||||||
|
use App\Models\Pay;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Order as OrderModel;
|
||||||
|
|
||||||
|
class OrderController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Order(['goods', 'coupon', 'pay']), function (Grid $grid) {
|
||||||
|
$grid->model()->orderBy('id', 'DESC');
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('order_sn')->copyable();
|
||||||
|
$grid->column('title');
|
||||||
|
$grid->column('type')->using(OrderModel::getTypeMap())
|
||||||
|
->label([
|
||||||
|
OrderModel::AUTOMATIC_DELIVERY => Admin::color()->success(),
|
||||||
|
OrderModel::MANUAL_PROCESSING => Admin::color()->info(),
|
||||||
|
]);
|
||||||
|
$grid->column('email')->copyable();
|
||||||
|
$grid->column('goods.gd_name', admin_trans('order.fields.goods_id'));
|
||||||
|
$grid->column('goods_price');
|
||||||
|
$grid->column('buy_amount');
|
||||||
|
$grid->column('total_price');
|
||||||
|
$grid->column('coupon.coupon', admin_trans('order.fields.coupon_id'));
|
||||||
|
$grid->column('coupon_discount_price');
|
||||||
|
$grid->column('wholesale_discount_price');
|
||||||
|
$grid->column('actual_price');
|
||||||
|
$grid->column('pay.pay_name', admin_trans('order.fields.pay_id'));
|
||||||
|
$grid->column('buy_ip');
|
||||||
|
$grid->column('search_pwd')->copyable();
|
||||||
|
$grid->column('trade_no')->copyable();
|
||||||
|
$grid->column('status')
|
||||||
|
->select(OrderModel::getStatusMap());
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->disableCreateButton();
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('order_sn');
|
||||||
|
$filter->like('title');
|
||||||
|
$filter->equal('status')->select(OrderModel::getStatusMap());
|
||||||
|
$filter->equal('email');
|
||||||
|
$filter->equal('trade_no');
|
||||||
|
$filter->equal('type')->select(OrderModel::getTypeMap());
|
||||||
|
$filter->equal('goods_id')->select(Goods::query()->pluck('gd_name', 'id'));
|
||||||
|
$filter->equal('coupon_id')->select(Coupon::query()->pluck('coupon', 'id'));
|
||||||
|
$filter->equal('pay_id')->select(Pay::query()->pluck('pay_name', 'id'));
|
||||||
|
$filter->whereBetween('created_at', function ($q) {
|
||||||
|
$start = $this->input['start'] ?? null;
|
||||||
|
$end = $this->input['end'] ?? null;
|
||||||
|
$q->where('created_at', '>=', $start)
|
||||||
|
->where('created_at', '<=', $end);
|
||||||
|
})->datetime();
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(OrderModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(OrderModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Order(['goods', 'coupon', 'pay']), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('order_sn');
|
||||||
|
$show->field('title');
|
||||||
|
$show->field('email');
|
||||||
|
$show->field('goods.gd_name', admin_trans('order.fields.goods_id'));
|
||||||
|
$show->field('goods_price');
|
||||||
|
$show->field('buy_amount');
|
||||||
|
$show->field('coupon.coupon', admin_trans('order.fields.coupon_id'));
|
||||||
|
$show->field('coupon_discount_price');
|
||||||
|
$show->field('wholesale_discount_price');
|
||||||
|
$show->field('total_price');
|
||||||
|
$show->field('actual_price');
|
||||||
|
$show->field('buy_ip');
|
||||||
|
$show->field('info')->unescape()->as(function ($info) {
|
||||||
|
return "<textarea class=\"form-control field_wholesale_price_cnf _normal_\" rows=\"10\" cols=\"30\">" . $info . "</textarea>";
|
||||||
|
});
|
||||||
|
$show->field('pay.pay_name', admin_trans('order.fields.pay_id'));
|
||||||
|
$show->field('status')->using(OrderModel::getStatusMap());
|
||||||
|
$show->field('search_pwd');
|
||||||
|
$show->field('trade_no');
|
||||||
|
$show->field('type')->using(OrderModel::getTypeMap());
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
$show->disableEditButton();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new Order(['goods', 'coupon', 'pay']), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->display('order_sn');
|
||||||
|
$form->text('title');
|
||||||
|
$form->display('goods.gd_name', admin_trans('order.fields.goods_id'));
|
||||||
|
$form->display('goods_price');
|
||||||
|
$form->display('buy_amount');
|
||||||
|
$form->display('coupon.coupon', admin_trans('order.fields.coupon_id'));
|
||||||
|
$form->display('coupon_discount_price');
|
||||||
|
$form->display('wholesale_discount_price');
|
||||||
|
$form->display('total_price');
|
||||||
|
$form->display('actual_price');
|
||||||
|
$form->display('email');
|
||||||
|
$form->textarea('info');
|
||||||
|
$form->display('buy_ip');
|
||||||
|
$form->display('pay.pay_name', admin_trans('order.fields.pay_id'));
|
||||||
|
$form->radio('status')->options(OrderModel::getStatusMap());
|
||||||
|
$form->text('search_pwd');
|
||||||
|
$form->display('trade_no');
|
||||||
|
$form->radio('type')->options(OrderModel::getTypeMap());
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
130
app/Admin/Controllers/PayController.php
Normal file
130
app/Admin/Controllers/PayController.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Admin\Actions\Post\BatchRestore;
|
||||||
|
use App\Admin\Actions\Post\Restore;
|
||||||
|
use App\Admin\Repositories\Pay;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use App\Models\Pay as PayModel;
|
||||||
|
|
||||||
|
class PayController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a grid builder.
|
||||||
|
*
|
||||||
|
* @return Grid
|
||||||
|
*/
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(new Pay(), function (Grid $grid) {
|
||||||
|
$grid->column('id')->sortable();
|
||||||
|
$grid->column('pay_name');
|
||||||
|
$grid->column('pay_check');
|
||||||
|
$grid->column('pay_method')->select(PayModel::getMethodMap());
|
||||||
|
$grid->column('merchant_id')->limit(20);
|
||||||
|
$grid->column('merchant_key')->limit(20);
|
||||||
|
$grid->column('merchant_pem')->limit(20);
|
||||||
|
$grid->column('pay_client')->select(PayModel::getClientMap());
|
||||||
|
$grid->column('pay_handleroute');
|
||||||
|
$grid->column('is_open')->switch();
|
||||||
|
$grid->column('created_at');
|
||||||
|
$grid->column('updated_at')->sortable();
|
||||||
|
$grid->disableDeleteButton();
|
||||||
|
$grid->filter(function (Grid\Filter $filter) {
|
||||||
|
$filter->equal('id');
|
||||||
|
$filter->equal('pay_check');
|
||||||
|
$filter->like('pay_name');
|
||||||
|
$filter->scope(admin_trans('dujiaoka.trashed'))->onlyTrashed();
|
||||||
|
});
|
||||||
|
$grid->actions(function (Grid\Displayers\Actions $actions) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$actions->append(new Restore(PayModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid->batchActions(function (Grid\Tools\BatchActions $batch) {
|
||||||
|
if (request('_scope_') == admin_trans('dujiaoka.trashed')) {
|
||||||
|
$batch->add(new BatchRestore(PayModel::class));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a show builder.
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
*
|
||||||
|
* @return Show
|
||||||
|
*/
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, new Pay(), function (Show $show) {
|
||||||
|
$show->field('id');
|
||||||
|
$show->field('pay_name');
|
||||||
|
$show->field('merchant_id');
|
||||||
|
$show->field('merchant_key');
|
||||||
|
$show->field('merchant_pem');
|
||||||
|
$show->field('pay_check');
|
||||||
|
$show->field('pay_client')->as(function ($payClient) {
|
||||||
|
if ($payClient == PayModel::PAY_CLIENT_PC) {
|
||||||
|
return admin_trans('pay.fields.pay_client_pc');
|
||||||
|
} else {
|
||||||
|
return admin_trans('pay.fields.pay_client_mobile');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('pay_handleroute');
|
||||||
|
$show->field('pay_method')->as(function ($payMethod) {
|
||||||
|
if ($payMethod == PayModel::METHOD_JUMP) {
|
||||||
|
return admin_trans('pay.fields.method_jump');
|
||||||
|
} else {
|
||||||
|
return admin_trans('pay.fields.method_scan');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('is_open')->as(function ($isOpen) {
|
||||||
|
if ($isOpen == PayModel::STATUS_OPEN) {
|
||||||
|
return admin_trans('dujiaoka.status_open');
|
||||||
|
} else {
|
||||||
|
return admin_trans('dujiaoka.status_close');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('updated_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a form builder.
|
||||||
|
*
|
||||||
|
* @return Form
|
||||||
|
*/
|
||||||
|
protected function form()
|
||||||
|
{
|
||||||
|
return Form::make(new Pay(), function (Form $form) {
|
||||||
|
$form->display('id');
|
||||||
|
$form->text('pay_name')->required();
|
||||||
|
$form->text('merchant_id')->required();
|
||||||
|
$form->textarea('merchant_key');
|
||||||
|
$form->textarea('merchant_pem')->required();
|
||||||
|
$form->text('pay_check')->required();
|
||||||
|
$form->radio('pay_client')
|
||||||
|
->options(PayModel::getClientMap())
|
||||||
|
->default(PayModel::PAY_CLIENT_PC)
|
||||||
|
->required();
|
||||||
|
$form->radio('pay_method')
|
||||||
|
->options(PayModel::getMethodMap())
|
||||||
|
->default(PayModel::METHOD_JUMP)
|
||||||
|
->required();
|
||||||
|
$form->text('pay_handleroute')->required();
|
||||||
|
$form->switch('is_open')->default(PayModel::STATUS_OPEN);
|
||||||
|
$form->display('created_at');
|
||||||
|
$form->display('updated_at');
|
||||||
|
$form->disableDeleteButton();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Admin/Controllers/SystemSettingController.php
Normal file
38
app/Admin/Controllers/SystemSettingController.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Admin\Forms\SystemSetting;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use Dcat\Admin\Layout\Content;
|
||||||
|
use Dcat\Admin\Widgets\Card;
|
||||||
|
|
||||||
|
class SystemSettingController extends AdminController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统设置
|
||||||
|
*
|
||||||
|
* @param Content $content
|
||||||
|
* @return Content
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function systemSetting(Content $content)
|
||||||
|
{
|
||||||
|
return $content
|
||||||
|
->title(admin_trans('menu.titles.system_setting'))
|
||||||
|
->body(new Card(new SystemSetting()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
85
app/Admin/Forms/EmailTest.php
Normal file
85
app/Admin/Forms/EmailTest.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author ZhangYiQiu<me@zhangyiqiu.net>
|
||||||
|
* @copyright ZhangYiQiu<me@zhangyiqiu.net>
|
||||||
|
* @link http://zhangyiqiu.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Admin\Forms;
|
||||||
|
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
use Dcat\Admin\Widgets\Form;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Mail\MailServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class EmailTest extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the form request.
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(array $input)
|
||||||
|
{
|
||||||
|
$to = $input['to'];
|
||||||
|
$title = $input['title'];
|
||||||
|
$body = $input['body'];
|
||||||
|
$sysConfig = cache('system-setting');
|
||||||
|
$mailConfig = [
|
||||||
|
'driver' => $sysConfig['driver'] ?? 'smtp',
|
||||||
|
'host' => $sysConfig['host'] ?? '',
|
||||||
|
'port' => $sysConfig['port'] ?? '465',
|
||||||
|
'username' => $sysConfig['username'] ?? '',
|
||||||
|
'from' => [
|
||||||
|
'address' => $sysConfig['from_address'] ?? '',
|
||||||
|
'name' => $sysConfig['from_name'] ?? '独角发卡'
|
||||||
|
],
|
||||||
|
'password' => $sysConfig['password'] ?? '',
|
||||||
|
'encryption' => $sysConfig['encryption'] ?? 'ssl'
|
||||||
|
];
|
||||||
|
// 覆盖 mail 配置
|
||||||
|
config([
|
||||||
|
'mail' => array_merge(config('mail'), $mailConfig)
|
||||||
|
]);
|
||||||
|
// 重新注册驱动
|
||||||
|
(new MailServiceProvider(app()))->register();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Mail::send(['html' => 'email.mail'], ['body' => $body], function ($message) use ($to, $title){
|
||||||
|
$message->to($to)->subject($title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(\Exception $e)
|
||||||
|
{
|
||||||
|
return $this
|
||||||
|
->response()
|
||||||
|
->error($e->getMessage());
|
||||||
|
}
|
||||||
|
return $this
|
||||||
|
->response()
|
||||||
|
->success(admin_trans('email-test.labels.success'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a form here.
|
||||||
|
*/
|
||||||
|
public function form()
|
||||||
|
{
|
||||||
|
$this->tab(admin_trans('menu.titles.email_test'), function () {
|
||||||
|
$this->text('to', admin_trans('email-test.labels.to'))->required();
|
||||||
|
$this->text('title', admin_trans('email-test.labels.title'))->default('这是一条测试邮件')->required();
|
||||||
|
$this->editor('body', admin_trans('email-test.labels.body'))->default("这是一条测试邮件的正文内容<br/><br/>正文比较长<br/><br/>非常长<br/><br/>测试测试测试")->required();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function default()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
78
app/Admin/Forms/ImportCarmis.php
Normal file
78
app/Admin/Forms/ImportCarmis.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Forms;
|
||||||
|
|
||||||
|
use App\Models\Carmis;
|
||||||
|
use App\Models\Goods;
|
||||||
|
use Dcat\Admin\Widgets\Form;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class ImportCarmis extends Form
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the form request.
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(array $input)
|
||||||
|
{
|
||||||
|
if (empty($input['carmis_list']) && empty($input['carmis_txt'])) {
|
||||||
|
return $this->response()->error(admin_trans('carmis.rule_messages.carmis_list_and_carmis_txt_can_not_be_empty'));
|
||||||
|
}
|
||||||
|
$carmisContent = "";
|
||||||
|
if (!empty($input['carmis_txt'])) {
|
||||||
|
$carmisContent = Storage::disk('public')->get($input['carmis_txt']);
|
||||||
|
}
|
||||||
|
if (!empty($input['carmis_list'])) {
|
||||||
|
$carmisContent = $input['carmis_list'];
|
||||||
|
}
|
||||||
|
$carmisData = [];
|
||||||
|
$tempList = explode(PHP_EOL, $carmisContent);
|
||||||
|
foreach ($tempList as $val) {
|
||||||
|
if (trim($val) != "") {
|
||||||
|
$carmisData[] = [
|
||||||
|
'goods_id' => $input['goods_id'],
|
||||||
|
'carmi' => trim($val),
|
||||||
|
'status' => Carmis::STATUS_UNSOLD,
|
||||||
|
'created_at' => date('Y-m-d H:i:s'),
|
||||||
|
'updated_at' => date('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($input['remove_duplication'] == 1) {
|
||||||
|
$carmisData = assoc_unique($carmisData, 'carmi');
|
||||||
|
}
|
||||||
|
Carmis::query()->insert($carmisData);
|
||||||
|
// 删除文件
|
||||||
|
Storage::disk('public')->delete($input['carmis_txt']);
|
||||||
|
return $this
|
||||||
|
->response()
|
||||||
|
->success(admin_trans('carmis.rule_messages.import_carmis_success'))
|
||||||
|
->location('/carmis');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a form here.
|
||||||
|
*/
|
||||||
|
public function form()
|
||||||
|
{
|
||||||
|
$this->confirm(admin_trans('carmis.fields.are_you_import_sure'));
|
||||||
|
$this->select('goods_id')->options(
|
||||||
|
Goods::query()->where('type', Goods::AUTOMATIC_DELIVERY)->pluck('gd_name', 'id')
|
||||||
|
)->required();
|
||||||
|
$this->textarea('carmis_list')
|
||||||
|
->rows(20)
|
||||||
|
->help(admin_trans('carmis.helps.carmis_list'));
|
||||||
|
$this->file('carmis_txt')
|
||||||
|
->disk('public')
|
||||||
|
->uniqueName()
|
||||||
|
->accept('txt')
|
||||||
|
->maxSize(5120)
|
||||||
|
->help(admin_trans('carmis.helps.carmis_list'));
|
||||||
|
$this->switch('remove_duplication');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
102
app/Admin/Forms/SystemSetting.php
Normal file
102
app/Admin/Forms/SystemSetting.php
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Forms;
|
||||||
|
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
use Dcat\Admin\Widgets\Form;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class SystemSetting extends Form
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the form request.
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(array $input)
|
||||||
|
{
|
||||||
|
Cache::put('system-setting', $input);
|
||||||
|
return $this
|
||||||
|
->response()
|
||||||
|
->success(admin_trans('system-setting.rule_messages.save_system_setting_success'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a form here.
|
||||||
|
*/
|
||||||
|
public function form()
|
||||||
|
{
|
||||||
|
$this->tab(admin_trans('system-setting.labels.base_setting'), function () {
|
||||||
|
$this->text('title', admin_trans('system-setting.fields.title'))->required();
|
||||||
|
$this->image('img_logo', admin_trans('system-setting.fields.img_logo'));
|
||||||
|
$this->text('text_logo', admin_trans('system-setting.fields.text_logo'));
|
||||||
|
$this->text('keywords', admin_trans('system-setting.fields.keywords'));
|
||||||
|
$this->textarea('description', admin_trans('system-setting.fields.description'));
|
||||||
|
$this->select('template', admin_trans('system-setting.fields.template'))
|
||||||
|
->options(config('dujiaoka.templates'))
|
||||||
|
->required();
|
||||||
|
$this->select('language', admin_trans('system-setting.fields.language'))
|
||||||
|
->options(config('dujiaoka.language'))
|
||||||
|
->required();
|
||||||
|
$this->text('manage_email', admin_trans('system-setting.fields.manage_email'));
|
||||||
|
$this->number('order_expire_time', admin_trans('system-setting.fields.order_expire_time'))
|
||||||
|
->default(5)
|
||||||
|
->required();
|
||||||
|
$this->switch('is_open_anti_red', admin_trans('system-setting.fields.is_open_anti_red'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->switch('is_open_img_code', admin_trans('system-setting.fields.is_open_img_code'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->switch('is_open_search_pwd', admin_trans('system-setting.fields.is_open_search_pwd'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->switch('is_open_google_translate', admin_trans('system-setting.fields.is_open_google_translate'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->editor('notice', admin_trans('system-setting.fields.notice'));
|
||||||
|
$this->textarea('footer', admin_trans('system-setting.fields.footer'));
|
||||||
|
});
|
||||||
|
$this->tab(admin_trans('system-setting.labels.order_push_setting'), function () {
|
||||||
|
$this->switch('is_open_server_jiang', admin_trans('system-setting.fields.is_open_server_jiang'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->text('server_jiang_token', admin_trans('system-setting.fields.server_jiang_token'));
|
||||||
|
$this->switch('is_open_telegram_push', admin_trans('system-setting.fields.is_open_telegram_push'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->text('telegram_bot_token', admin_trans('system-setting.fields.telegram_bot_token'));
|
||||||
|
$this->text('telegram_userid', admin_trans('system-setting.fields.telegram_userid'));
|
||||||
|
$this->switch('is_open_bark_push', admin_trans('system-setting.fields.is_open_bark_push'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->switch('is_open_bark_push_url', admin_trans('system-setting.fields.is_open_bark_push_url'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->text('bark_server', admin_trans('system-setting.fields.bark_server'));
|
||||||
|
$this->text('bark_token', admin_trans('system-setting.fields.bark_token'));
|
||||||
|
$this->switch('is_open_qywxbot_push', admin_trans('system-setting.fields.is_open_qywxbot_push'))
|
||||||
|
->default(BaseModel::STATUS_CLOSE);
|
||||||
|
$this->text('qywxbot_key', admin_trans('system-setting.fields.qywxbot_key'));
|
||||||
|
});
|
||||||
|
$this->tab(admin_trans('system-setting.labels.mail_setting'), function () {
|
||||||
|
$this->text('driver', admin_trans('system-setting.fields.driver'))->default('smtp')->required();
|
||||||
|
$this->text('host', admin_trans('system-setting.fields.host'));
|
||||||
|
$this->text('port', admin_trans('system-setting.fields.port'))->default(587);
|
||||||
|
$this->text('username', admin_trans('system-setting.fields.username'));
|
||||||
|
$this->text('password', admin_trans('system-setting.fields.password'));
|
||||||
|
$this->text('encryption', admin_trans('system-setting.fields.encryption'));
|
||||||
|
$this->text('from_address', admin_trans('system-setting.fields.from_address'));
|
||||||
|
$this->text('from_name', admin_trans('system-setting.fields.from_name'));
|
||||||
|
});
|
||||||
|
$this->tab(admin_trans('system-setting.labels.geetest'), function () {
|
||||||
|
$this->text('geetest_id', admin_trans('system-setting.fields.geetest_id'));
|
||||||
|
$this->text('geetest_key', admin_trans('system-setting.fields.geetest_key'));
|
||||||
|
$this->switch('is_open_geetest', admin_trans('system-setting.fields.is_open_geetest'))->default(BaseModel::STATUS_CLOSE);
|
||||||
|
});
|
||||||
|
$this->confirm(
|
||||||
|
admin_trans('dujiaoka.warning_title'),
|
||||||
|
admin_trans('system-setting.rule_messages.change_reboot_php_worker')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function default()
|
||||||
|
{
|
||||||
|
return Cache::get('system-setting');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
18
app/Admin/Repositories/Carmis.php
Normal file
18
app/Admin/Repositories/Carmis.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Carmis as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Carmis extends EloquentRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
|
||||||
|
}
|
||||||
16
app/Admin/Repositories/Coupon.php
Normal file
16
app/Admin/Repositories/Coupon.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Coupon as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Coupon extends EloquentRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
}
|
||||||
16
app/Admin/Repositories/Emailtpl.php
Normal file
16
app/Admin/Repositories/Emailtpl.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Emailtpl as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Emailtpl extends EloquentRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
}
|
||||||
16
app/Admin/Repositories/Goods.php
Normal file
16
app/Admin/Repositories/Goods.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Goods as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Goods extends EloquentRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
}
|
||||||
18
app/Admin/Repositories/GoodsGroup.php
Normal file
18
app/Admin/Repositories/GoodsGroup.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\GoodsGroup as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class GoodsGroup extends EloquentRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
|
||||||
|
}
|
||||||
16
app/Admin/Repositories/Order.php
Normal file
16
app/Admin/Repositories/Order.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Order as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Order extends EloquentRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
}
|
||||||
16
app/Admin/Repositories/Pay.php
Normal file
16
app/Admin/Repositories/Pay.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use App\Models\Pay as Model;
|
||||||
|
use Dcat\Admin\Repositories\EloquentRepository;
|
||||||
|
|
||||||
|
class Pay extends EloquentRepository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $eloquentClass = Model::class;
|
||||||
|
}
|
||||||
26
app/Admin/bootstrap.php
Normal file
26
app/Admin/bootstrap.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Form;
|
||||||
|
use Dcat\Admin\Grid\Filter;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dcat-admin - admin builder based on Laravel.
|
||||||
|
* @author jqh <https://github.com/jqhph>
|
||||||
|
*
|
||||||
|
* Bootstraper for Admin.
|
||||||
|
*
|
||||||
|
* Here you can remove builtin form field:
|
||||||
|
*
|
||||||
|
* extend custom field:
|
||||||
|
* Dcat\Admin\Form::extend('php', PHPEditor::class);
|
||||||
|
* Dcat\Admin\Grid\Column::extend('php', PHPEditor::class);
|
||||||
|
* Dcat\Admin\Grid\Filter::extend('php', PHPEditor::class);
|
||||||
|
*
|
||||||
|
* Or require js and css assets:
|
||||||
|
* Admin::css('/packages/prettydocs/css/styles.css');
|
||||||
|
* Admin::js('/packages/prettydocs/js/main.js');
|
||||||
|
*
|
||||||
|
*/
|
||||||
25
app/Admin/routes.php
Normal file
25
app/Admin/routes.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Routing\Router;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
|
||||||
|
Admin::routes();
|
||||||
|
|
||||||
|
Route::group([
|
||||||
|
'prefix' => config('admin.route.prefix'),
|
||||||
|
'namespace' => config('admin.route.namespace'),
|
||||||
|
'middleware' => config('admin.route.middleware'),
|
||||||
|
], function (Router $router) {
|
||||||
|
$router->get('/', 'HomeController@index');
|
||||||
|
$router->resource('goods', 'GoodsController');
|
||||||
|
$router->resource('goods-group', 'GoodsGroupController');
|
||||||
|
$router->resource('carmis', 'CarmisController');
|
||||||
|
$router->resource('coupon', 'CouponController');
|
||||||
|
$router->resource('emailtpl', 'EmailtplController');
|
||||||
|
$router->resource('pay', 'PayController');
|
||||||
|
$router->resource('order', 'OrderController');
|
||||||
|
$router->get('import-carmis', 'CarmisController@importCarmis');
|
||||||
|
$router->get('system-setting', 'SystemSettingController@systemSetting');
|
||||||
|
$router->get('email-test', 'EmailTestController@emailTest');
|
||||||
|
});
|
||||||
42
app/Console/Kernel.php
Normal file
42
app/Console/Kernel.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The Artisan commands provided by your application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $commands = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the application's command schedule.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function schedule(Schedule $schedule)
|
||||||
|
{
|
||||||
|
// $schedule->command('inspire')
|
||||||
|
// ->hourly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the commands for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function commands()
|
||||||
|
{
|
||||||
|
$this->load(__DIR__.'/Commands');
|
||||||
|
|
||||||
|
require base_path('routes/console.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Events/GoodsDeleted.php
Normal file
39
app/Events/GoodsDeleted.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Goods;
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class GoodsDeleted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $goods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Goods $goods)
|
||||||
|
{
|
||||||
|
$this->goods = $goods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Broadcasting\Channel|array
|
||||||
|
*/
|
||||||
|
public function broadcastOn()
|
||||||
|
{
|
||||||
|
return new PrivateChannel('channel-name');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Events/GoodsGroupDeleted.php
Normal file
39
app/Events/GoodsGroupDeleted.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\GoodsGroup;
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class GoodsGroupDeleted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $goodsGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(GoodsGroup $goodsGroup)
|
||||||
|
{
|
||||||
|
$this->goodsGroup = $goodsGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Broadcasting\Channel|array
|
||||||
|
*/
|
||||||
|
public function broadcastOn()
|
||||||
|
{
|
||||||
|
return new PrivateChannel('channel-name');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Events/OrderUpdated.php
Normal file
39
app/Events/OrderUpdated.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class OrderUpdated
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
public $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Broadcasting\Channel|array
|
||||||
|
*/
|
||||||
|
public function broadcastOn()
|
||||||
|
{
|
||||||
|
return new PrivateChannel('channel-name');
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/Exceptions/AppException.php
Normal file
15
app/Exceptions/AppException.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
class AppException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct($message = "", $code = 0, \Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
app/Exceptions/Handler.php
Normal file
60
app/Exceptions/Handler.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A list of the exception types that are not reported.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dontReport = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of the inputs that are never flashed for validation exceptions.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dontFlash = [
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report or log an exception.
|
||||||
|
*
|
||||||
|
* @param \Exception $exception
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function report(Exception $exception)
|
||||||
|
{
|
||||||
|
parent::report($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an exception into an HTTP response.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Exception $exception
|
||||||
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function render($request, Exception $exception)
|
||||||
|
{
|
||||||
|
if ($exception instanceof AppException) {
|
||||||
|
$layout = dujiaoka_config_get('template', 'layui');
|
||||||
|
$tplPath = $layout . '/errors/error';
|
||||||
|
return view($tplPath, ['title' => __('dujiaoka.error_title'), 'content' => $exception->getMessage(), 'url' => ""]);
|
||||||
|
}
|
||||||
|
return parent::render($request, $exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/Exceptions/RuleValidationException.php
Normal file
15
app/Exceptions/RuleValidationException.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
|
||||||
|
class RuleValidationException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct($message = "", $code = 400, \Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
236
app/Helpers/functions.php
Normal file
236
app/Helpers/functions.php
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\AppException;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
if (! function_exists('replace_mail_tpl')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换邮件模板
|
||||||
|
*
|
||||||
|
* @param array $mailtpl 模板
|
||||||
|
* @param array $data 内容
|
||||||
|
* @return array|false|mixed
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
function replace_mail_tpl($mailtpl = [], $data = [])
|
||||||
|
{
|
||||||
|
if (!$mailtpl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($data) {
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
$title = str_replace('{' . $key . '}', $val, isset($title) ? $title : $mailtpl['tpl_name']);
|
||||||
|
$content = str_replace('{' . $key . '}', $val, isset($content) ? $content : $mailtpl['tpl_content']);
|
||||||
|
}
|
||||||
|
return ['tpl_name' => $title, 'tpl_content' => $content];
|
||||||
|
}
|
||||||
|
return $mailtpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (! function_exists('dujiaoka_config_get')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置获取
|
||||||
|
*
|
||||||
|
* @param string $key 要获取的key
|
||||||
|
* @param $default 默认
|
||||||
|
* @return mixed|null
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
function dujiaoka_config_get(string $key, $default = null)
|
||||||
|
{
|
||||||
|
$sysConfig = Cache::get('system-setting');
|
||||||
|
return $sysConfig[$key] ?? $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('format_wholesale_price')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化批发价
|
||||||
|
*
|
||||||
|
* @param string $wholesalePriceArr 批发价配置
|
||||||
|
* @return array|null
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
function format_wholesale_price(string $wholesalePriceArr): ?array
|
||||||
|
{
|
||||||
|
$waitArr = explode(PHP_EOL, $wholesalePriceArr);
|
||||||
|
$formatData = [];
|
||||||
|
foreach ($waitArr as $key => $val) {
|
||||||
|
if ($val != "") {
|
||||||
|
$explodeFormat = explode('=', delete_html_code($val));
|
||||||
|
if (count($explodeFormat) != 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$formatData[$key]['number'] = $explodeFormat[0];
|
||||||
|
$formatData[$key]['price'] = $explodeFormat[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort($formatData);
|
||||||
|
return $formatData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('delete_html_code')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除html内容
|
||||||
|
* @param string $str 需要去掉的字符串
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function delete_html_code(string $str): string
|
||||||
|
{
|
||||||
|
$str = trim($str); //清除字符串两边的空格
|
||||||
|
$str = preg_replace("/\t/", "", $str); //使用正则表达式替换内容,如:空格,换行,并将替换为空。
|
||||||
|
$str = preg_replace("/\r\n/", "", $str);
|
||||||
|
$str = preg_replace("/\r/", "", $str);
|
||||||
|
$str = preg_replace("/\n/", "", $str);
|
||||||
|
$str = preg_replace("/ /", "", $str);
|
||||||
|
$str = preg_replace("/ /", "", $str); //匹配html中的空格
|
||||||
|
return trim($str); //返回字符串
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('format_charge_input')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化代充框
|
||||||
|
*
|
||||||
|
* @param string $charge
|
||||||
|
* @return array|null
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
function format_charge_input(string $charge): ?array
|
||||||
|
{
|
||||||
|
$inputArr = explode(PHP_EOL, $charge);
|
||||||
|
$formatData = [];
|
||||||
|
foreach ($inputArr as $key => $val) {
|
||||||
|
if ($val != "") {
|
||||||
|
$explodeFormat = explode('=', delete_html_code($val));
|
||||||
|
if (count($explodeFormat) < 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$formatData[$key]['field'] = $explodeFormat[0];
|
||||||
|
$formatData[$key]['desc'] = $explodeFormat[1];
|
||||||
|
$formatData[$key]['rule'] = filter_var($explodeFormat[2], FILTER_VALIDATE_BOOLEAN);
|
||||||
|
if(count($explodeFormat) > 3){
|
||||||
|
$formatData[$key]['placeholder'] = $explodeFormat[3];
|
||||||
|
}else{
|
||||||
|
$formatData[$key]['placeholder'] = $formatData[$key]['desc'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $formatData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('site_url')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取顶级域名 带协议
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function site_url()
|
||||||
|
{
|
||||||
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||||
|
$domainName = $_SERVER['HTTP_HOST'] . '/';
|
||||||
|
return $protocol . $domainName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('md5_signquery')) {
|
||||||
|
|
||||||
|
function md5_signquery(array $parameter, string $signKey)
|
||||||
|
{
|
||||||
|
ksort($parameter); //重新排序$data数组
|
||||||
|
reset($parameter); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = '';
|
||||||
|
$urls = '';
|
||||||
|
foreach ($parameter as $key => $val) {
|
||||||
|
if ($val == '') continue;
|
||||||
|
if ($key != 'sign') {
|
||||||
|
if ($sign != '') {
|
||||||
|
$sign .= "&";
|
||||||
|
$urls .= "&";
|
||||||
|
}
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
$urls .= "$key=" . urlencode($val); //拼接为url参数形式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sign = md5($sign . $signKey);//密码追加进入开始MD5签名
|
||||||
|
$query = $urls . '&sign=' . $sign; //创建订单所需的参数
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! function_exists('signquery_string')) {
|
||||||
|
|
||||||
|
function signquery_string(array $data)
|
||||||
|
{
|
||||||
|
ksort($data); //排序post参数
|
||||||
|
reset($data); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = ''; //加密字符串初始化
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
if ($val == '' || $key == 'sign') continue; //跳过这些不签名
|
||||||
|
if ($sign) $sign .= '&'; //第一个字符串签名不加& 其他加&连接起来参数
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
}
|
||||||
|
return $sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('picture_ulr')) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成前台图片链接 不存在使用默认图
|
||||||
|
* @param string $file 图片地址
|
||||||
|
* @param false $getHost 是否只获取图片前缀域名
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\UrlGenerator|string
|
||||||
|
*/
|
||||||
|
function picture_ulr($file, $getHost = false)
|
||||||
|
{
|
||||||
|
if ($getHost) return Storage::disk('admin')->url('');
|
||||||
|
return $file ? Storage::disk('admin')->url($file) : url('assets/common/images/default.jpg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('assoc_unique')) {
|
||||||
|
function assoc_unique($arr, $key)
|
||||||
|
{
|
||||||
|
$tmp_arr = array();
|
||||||
|
foreach ($arr as $k => $v) {
|
||||||
|
if (in_array($v[$key], $tmp_arr)) {//搜索$v[$key]是否在$tmp_arr数组中存在,若存在返回true
|
||||||
|
unset($arr[$k]);
|
||||||
|
} else {
|
||||||
|
$tmp_arr[] = $v[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort($arr); //sort函数对数组进行排序
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
app/Http/Controllers/BaseController.php
Normal file
54
app/Http/Controllers/BaseController.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
class BaseController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染模板
|
||||||
|
*
|
||||||
|
* @param string $tpl 模板名称
|
||||||
|
* @param array $data 数据
|
||||||
|
* @param array $pageTitle 页面标题
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
protected function render(string $tpl, $data = [], string $pageTitle = '')
|
||||||
|
{
|
||||||
|
$layout = dujiaoka_config_get('template', 'unicorn');
|
||||||
|
$tplPath = $layout . '/' .$tpl;
|
||||||
|
return view($tplPath, $data)->with('page_title', $pageTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*
|
||||||
|
* @param string $content 提示内容
|
||||||
|
* @param string $jumpUri 跳转url
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
protected function err(string $content, $jumpUri = '')
|
||||||
|
{
|
||||||
|
$layout = dujiaoka_config_get('template', 'unicorn');
|
||||||
|
$tplPath = $layout . '/errors/error';
|
||||||
|
return view($tplPath, ['title' => __('dujiaoka.error_title'), 'content' => $content, 'url' => $jumpUri])
|
||||||
|
->with('page_title', __('dujiaoka.error_title'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||||
|
}
|
||||||
189
app/Http/Controllers/Home/HomeController.php
Normal file
189
app/Http/Controllers/Home/HomeController.php
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Home;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Models\Pay;
|
||||||
|
use Germey\Geetest\Geetest;
|
||||||
|
use Illuminate\Database\DatabaseServiceProvider;
|
||||||
|
use Illuminate\Database\QueryException;
|
||||||
|
use Illuminate\Encryption\Encrypter;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
|
||||||
|
class HomeController extends BaseController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品服务层.
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $goodsService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付服务层
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $payService;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->goodsService = app('Service\GoodsService');
|
||||||
|
$this->payService = app('Service\PayService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 首页.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$goods = $this->goodsService->withGroup();
|
||||||
|
return $this->render('static_pages/home', ['data' => $goods], __('dujiaoka.page-title.home'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品详情
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function buy(int $id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$goods = $this->goodsService->detail($id);
|
||||||
|
$this->goodsService->validatorGoodsStatus($goods);
|
||||||
|
// 有没有优惠码可以展示
|
||||||
|
if (count($goods->coupon)) {
|
||||||
|
$goods->open_coupon = 1;
|
||||||
|
}
|
||||||
|
$formatGoods = $this->goodsService->format($goods);
|
||||||
|
// 加载支付方式.
|
||||||
|
$client = Pay::PAY_CLIENT_PC;
|
||||||
|
if (app('Jenssegers\Agent')->isMobile()) {
|
||||||
|
$client = Pay::PAY_CLIENT_MOBILE;
|
||||||
|
}
|
||||||
|
$formatGoods->payways = $this->payService->pays($client);
|
||||||
|
return $this->render('static_pages/buy', $formatGoods, $formatGoods->gd_name);
|
||||||
|
} catch (RuleValidationException $ruleValidationException) {
|
||||||
|
return $this->err($ruleValidationException->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 极验行为验证
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function geetest(Request $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'user_id' => @Auth::user()?@Auth::user()->id:'UnLoginUser',
|
||||||
|
'client_type' => 'web',
|
||||||
|
'ip_address' => \Illuminate\Support\Facades\Request::ip()
|
||||||
|
];
|
||||||
|
$status = Geetest::preProcess($data);
|
||||||
|
session()->put('gtserver', $status);
|
||||||
|
session()->put('user_id', $data['user_id']);
|
||||||
|
return Geetest::getResponseStr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装页面
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function install(Request $request)
|
||||||
|
{
|
||||||
|
return view('common/install');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行安装
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function doInstall(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$dbConfig = config('database');
|
||||||
|
$mysqlDB = [
|
||||||
|
'host' => $request->input('db_host'),
|
||||||
|
'port' => $request->input('db_port'),
|
||||||
|
'database' => $request->input('db_database'),
|
||||||
|
'username' => $request->input('db_username'),
|
||||||
|
'password' => $request->input('db_password'),
|
||||||
|
];
|
||||||
|
$dbConfig['connections']['mysql'] = array_merge($dbConfig['connections']['mysql'], $mysqlDB);
|
||||||
|
// Redis
|
||||||
|
$redisDB = [
|
||||||
|
'host' => $request->input('redis_host'),
|
||||||
|
'password' => $request->input('redis_password', 'null'),
|
||||||
|
'port' => $request->input('redis_port'),
|
||||||
|
];
|
||||||
|
$dbConfig['redis']['default'] = array_merge($dbConfig['redis']['default'], $redisDB);
|
||||||
|
config(['database' => $dbConfig]);
|
||||||
|
DB::purge();
|
||||||
|
// db测试
|
||||||
|
DB::connection()->select('select 1 limit 1');
|
||||||
|
// redis测试
|
||||||
|
Redis::set('dujiaoka_com', 'ok');
|
||||||
|
Redis::get('dujiaoka_com');
|
||||||
|
// 获得文件模板
|
||||||
|
$envExamplePath = base_path() . DIRECTORY_SEPARATOR . '.env.example';
|
||||||
|
$envPath = base_path() . DIRECTORY_SEPARATOR . '.env';
|
||||||
|
$installLock = base_path() . DIRECTORY_SEPARATOR . 'install.lock';
|
||||||
|
$installSql = database_path() . DIRECTORY_SEPARATOR . 'sql' . DIRECTORY_SEPARATOR . 'install.sql';
|
||||||
|
$envTemp = file_get_contents($envExamplePath);
|
||||||
|
$postData = $request->all();
|
||||||
|
// 临时写入key
|
||||||
|
$postData['app_key'] = 'base64:' . base64_encode(
|
||||||
|
Encrypter::generateKey(config('app.cipher'))
|
||||||
|
);
|
||||||
|
foreach ($postData as $key => $item) {
|
||||||
|
$envTemp = str_replace('{' . $key . '}', $item, $envTemp);
|
||||||
|
}
|
||||||
|
// 写入配置
|
||||||
|
file_put_contents($envPath, $envTemp);
|
||||||
|
// 导入sql
|
||||||
|
DB::unprepared(file_get_contents($installSql));
|
||||||
|
// 写入安装锁
|
||||||
|
file_put_contents($installLock, 'install ok');
|
||||||
|
return 'success';
|
||||||
|
} catch (\RedisException $exception) {
|
||||||
|
return 'Redis配置错误 :' . $exception->getMessage();
|
||||||
|
} catch (QueryException $exception) {
|
||||||
|
return '数据库配置错误 :' . $exception->getMessage();
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return $exception->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
259
app/Http/Controllers/Home/OrderController.php
Normal file
259
app/Http/Controllers/Home/OrderController.php
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Home;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
use App\Models\Order;
|
||||||
|
use App\Service\OrderProcessService;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cookie;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单控制器
|
||||||
|
*
|
||||||
|
* Class OrderController
|
||||||
|
* @package App\Http\Controllers\Home
|
||||||
|
* @author: Assimon
|
||||||
|
* @email: Ashang@utf8.hk
|
||||||
|
* @blog: https://utf8.hk
|
||||||
|
* Date: 2021/5/30
|
||||||
|
*/
|
||||||
|
class OrderController extends BaseController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单服务层
|
||||||
|
* @var \App\Service\OrderService
|
||||||
|
*/
|
||||||
|
private $orderService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单处理层.
|
||||||
|
* @var OrderProcessService
|
||||||
|
*/
|
||||||
|
private $orderProcessService;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->orderService = app('Service\OrderService');
|
||||||
|
$this->orderProcessService = app('Service\OrderProcessService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建订单
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
* @throws \Illuminate\Validation\ValidationException
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function createOrder(Request $request)
|
||||||
|
{
|
||||||
|
DB::beginTransaction();
|
||||||
|
try {
|
||||||
|
$this->orderService->validatorCreateOrder($request);
|
||||||
|
$goods = $this->orderService->validatorGoods($request);
|
||||||
|
$this->orderService->validatorLoopCarmis($request);
|
||||||
|
// 设置商品
|
||||||
|
$this->orderProcessService->setGoods($goods);
|
||||||
|
// 优惠码
|
||||||
|
$coupon = $this->orderService->validatorCoupon($request);
|
||||||
|
// 设置优惠码
|
||||||
|
$this->orderProcessService->setCoupon($coupon);
|
||||||
|
$otherIpt = $this->orderService->validatorChargeInput($goods, $request);
|
||||||
|
$this->orderProcessService->setOtherIpt($otherIpt);
|
||||||
|
// 数量
|
||||||
|
$this->orderProcessService->setBuyAmount($request->input('by_amount'));
|
||||||
|
// 支付方式
|
||||||
|
$this->orderProcessService->setPayID($request->input('payway'));
|
||||||
|
// 下单邮箱
|
||||||
|
$this->orderProcessService->setEmail($request->input('email'));
|
||||||
|
// ip地址
|
||||||
|
$this->orderProcessService->setBuyIP($request->getClientIp());
|
||||||
|
// 查询密码
|
||||||
|
$this->orderProcessService->setSearchPwd($request->input('search_pwd', ''));
|
||||||
|
// 创建订单
|
||||||
|
$order = $this->orderProcessService->createOrder();
|
||||||
|
DB::commit();
|
||||||
|
// 设置订单cookie
|
||||||
|
$this->queueCookie($order->order_sn);
|
||||||
|
return redirect(url('/bill', ['orderSN' => $order->order_sn]));
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
DB::rollBack();
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置订单cookie.
|
||||||
|
* @param string $orderSN 订单号.
|
||||||
|
*/
|
||||||
|
private function queueCookie(string $orderSN) : void
|
||||||
|
{
|
||||||
|
// 设置订单cookie
|
||||||
|
$cookies = Cookie::get('dujiaoka_orders');
|
||||||
|
if (empty($cookies)) {
|
||||||
|
Cookie::queue('dujiaoka_orders', json_encode([$orderSN]));
|
||||||
|
} else {
|
||||||
|
$cookies = json_decode($cookies, true);
|
||||||
|
array_push($cookies, $orderSN);
|
||||||
|
Cookie::queue('dujiaoka_orders', json_encode($cookies));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结账
|
||||||
|
*
|
||||||
|
* @param string $orderSN
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function bill(string $orderSN)
|
||||||
|
{
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
if (empty($order)) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.order_does_not_exist'));
|
||||||
|
}
|
||||||
|
if ($order->status == Order::STATUS_EXPIRED) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.order_is_expired'));
|
||||||
|
}
|
||||||
|
return $this->render('static_pages/bill', $order, __('dujiaoka.page-title.bill'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态监测
|
||||||
|
*
|
||||||
|
* @param string $orderSN 订单号
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function checkOrderStatus(string $orderSN)
|
||||||
|
{
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
// 订单不存在或者已经过期
|
||||||
|
if (!$order || $order->status == Order::STATUS_EXPIRED) {
|
||||||
|
return response()->json(['msg' => 'expired', 'code' => 400001]);
|
||||||
|
}
|
||||||
|
// 订单已经支付
|
||||||
|
if ($order->status == Order::STATUS_WAIT_PAY) {
|
||||||
|
return response()->json(['msg' => 'wait....', 'code' => 400000]);
|
||||||
|
}
|
||||||
|
// 成功
|
||||||
|
if ($order->status > Order::STATUS_WAIT_PAY) {
|
||||||
|
return response()->json(['msg' => 'success', 'code' => 200]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过订单号展示订单详情
|
||||||
|
*
|
||||||
|
* @param string $orderSN 订单号.
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function detailOrderSN(string $orderSN)
|
||||||
|
{
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
// 订单不存在或者已经过期
|
||||||
|
if (!$order) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.order_does_not_exist'));
|
||||||
|
}
|
||||||
|
return $this->render('static_pages/orderinfo', ['orders' => [$order]], __('dujiaoka.page-title.order-detail'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单号查询
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function searchOrderBySN(Request $request)
|
||||||
|
{
|
||||||
|
return $this->detailOrderSN($request->input('order_sn'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过邮箱查询
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function searchOrderByEmail(Request $request)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!$request->has('email') ||
|
||||||
|
(
|
||||||
|
dujiaoka_config_get('is_open_search_pwd', \App\Models\BaseModel::STATUS_CLOSE) == \App\Models\BaseModel::STATUS_OPEN &&
|
||||||
|
!$request->has('search_pwd')
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.server_illegal_request'));
|
||||||
|
}
|
||||||
|
$orders = $this->orderService->withEmailAndPassword($request->input('email'), $request->input('search_pwd',''));
|
||||||
|
if (!$orders) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.no_related_order_found'));
|
||||||
|
}
|
||||||
|
return $this->render('static_pages/orderinfo', ['orders' => $orders], __('dujiaoka.page-title.order-detail'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过浏览器缓存查询
|
||||||
|
* @param Request $request
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function searchOrderByBrowser(Request $request)
|
||||||
|
{
|
||||||
|
$cookies = Cookie::get('dujiaoka_orders');
|
||||||
|
if (empty($cookies)) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.no_related_order_found_for_cache'));
|
||||||
|
}
|
||||||
|
$orderSNS = json_decode($cookies, true);
|
||||||
|
$orders = $this->orderService->byOrderSNS($orderSNS);
|
||||||
|
return $this->render('static_pages/orderinfo', ['orders' => $orders], __('dujiaoka.page-title.order-detail'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单查询页
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function orderSearch(Request $request)
|
||||||
|
{
|
||||||
|
return $this->render('static_pages/searchOrder', [], __('dujiaoka.page-title.order-search'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
111
app/Http/Controllers/Pay/AlipayController.php
Normal file
111
app/Http/Controllers/Pay/AlipayController.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Yansongda\Pay\Pay;
|
||||||
|
|
||||||
|
class AlipayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付宝支付网关
|
||||||
|
*
|
||||||
|
* @param string $payway
|
||||||
|
* @param string $orderSN
|
||||||
|
*/
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
$config = [
|
||||||
|
'app_id' => $this->payGateway->merchant_id,
|
||||||
|
'ali_public_key' => $this->payGateway->merchant_key,
|
||||||
|
'private_key' => $this->payGateway->merchant_pem,
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
'return_url' => url('detail-order-sn', ['orderSN' => $this->order->order_sn]),
|
||||||
|
'http' => [ // optional
|
||||||
|
'timeout' => 10.0,
|
||||||
|
'connect_timeout' => 10.0,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$order = [
|
||||||
|
'out_trade_no' => $this->order->order_sn,
|
||||||
|
'total_amount' => (float)$this->order->actual_price,
|
||||||
|
'subject' => $this->order->order_sn
|
||||||
|
];
|
||||||
|
switch ($payway){
|
||||||
|
case 'zfbf2f':
|
||||||
|
case 'alipayscan':
|
||||||
|
try{
|
||||||
|
$result = Pay::alipay($config)->scan($order)->toArray();
|
||||||
|
$result['payname'] = $this->order->order_sn;
|
||||||
|
$result['actual_price'] = (float)$this->order->actual_price;
|
||||||
|
$result['orderid'] = $this->order->order_sn;
|
||||||
|
$result['jump_payuri'] = $result['qr_code'];
|
||||||
|
return $this->render('static_pages/qrpay', $result, __('dujiaoka.scan_qrcode_to_pay'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
case 'aliweb':
|
||||||
|
try{
|
||||||
|
$result = Pay::alipay($config)->web($order);
|
||||||
|
return $result;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
case 'aliwap':
|
||||||
|
try{
|
||||||
|
$result = Pay::alipay($config)->wap($order);
|
||||||
|
return $result;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步通知
|
||||||
|
*/
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$orderSN = $request->input('out_trade_no');
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
if (!$order) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/alipay'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$config = [
|
||||||
|
'app_id' => $payGateway->merchant_id,
|
||||||
|
'ali_public_key' => $payGateway->merchant_key,
|
||||||
|
'private_key' => $payGateway->merchant_pem,
|
||||||
|
];
|
||||||
|
$pay = Pay::alipay($config);
|
||||||
|
try{
|
||||||
|
// 验证签名
|
||||||
|
$result = $pay->verify();
|
||||||
|
if ($result->trade_status == 'TRADE_SUCCESS' || $result->trade_status == 'TRADE_FINISHED') {
|
||||||
|
$this->orderProcessService->completedOrder($result->out_trade_no, $result->total_amount, $result->trade_no);
|
||||||
|
}
|
||||||
|
return 'success';
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
143
app/Http/Controllers/Pay/CoinbaseController.php
Normal file
143
app/Http/Controllers/Pay/CoinbaseController.php
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class CoinbaseController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
switch ($payway) {
|
||||||
|
case 'coinbase':
|
||||||
|
default:
|
||||||
|
try {
|
||||||
|
$createOrderUrl="https://api.commerce.coinbase.com/charges";
|
||||||
|
$price_amount = sprintf('%.2f', (float)$this->order->actual_price);// 只取小数点后两位
|
||||||
|
$fees = (double)$this->payGateway->merchant_id;//手续费费率 比如 0.05
|
||||||
|
if($fees>0.00)
|
||||||
|
{
|
||||||
|
$price_amount =(double)$price_amount * (1.00+$fees);// 价格 * (1 + 0.05)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$redirect_url = url('detail-order-sn', ['orderSN' => $this->order->order_sn]); //同步地址
|
||||||
|
$cancel_url = url('detail-order-sn', ['orderSN' => $this->order->order_sn]); //同步地址
|
||||||
|
$config = [
|
||||||
|
'name'=>$this->order->title,
|
||||||
|
'description'=>$this->order->title.'需付款'.$price_amount.'元',
|
||||||
|
'pricing_type' => 'fixed_price',
|
||||||
|
'local_price' => [
|
||||||
|
'amount' => $price_amount,
|
||||||
|
'currency' => 'CNY'
|
||||||
|
],
|
||||||
|
'metadata' => [
|
||||||
|
'customer_id' => $this->order->order_sn,
|
||||||
|
'customer_name' => $this->order->title
|
||||||
|
],
|
||||||
|
'redirect_url' =>$redirect_url,
|
||||||
|
'cancel_url'=> $cancel_url
|
||||||
|
];
|
||||||
|
$header = array();
|
||||||
|
$header[] = 'Content-Type:application/json';
|
||||||
|
$header[] = 'X-CC-Api-Key:'.$this->payGateway->merchant_key; //APP key
|
||||||
|
$header[] = 'X-CC-Version: 2018-03-22';
|
||||||
|
|
||||||
|
$ch = curl_init(); //使用curl请求
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $createOrderUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($config));
|
||||||
|
$coinbase_json = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$coinbase_date=json_decode($coinbase_json,true);
|
||||||
|
if(is_array($coinbase_date))
|
||||||
|
{
|
||||||
|
$payment_url = $coinbase_date['data']['hosted_url'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 'fail|Coinbase支付接口请求失败';
|
||||||
|
}
|
||||||
|
return redirect()->away($payment_url);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$payload = file_get_contents( 'php://input' );
|
||||||
|
$sig = $_SERVER['HTTP_X_CC_WEBHOOK_SIGNATURE'];
|
||||||
|
$data = json_decode( $payload, true );
|
||||||
|
$event_data = $data['event']['data'];
|
||||||
|
$order = $this->orderService->detailOrderSN($event_data['metadata']['customer_id']);//
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != 'pay/coinbase'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$secret = $payGateway->merchant_pem;//共享密钥
|
||||||
|
$sig2 = hash_hmac( 'sha256', $payload, $secret );
|
||||||
|
$result_str=array("confirmed","resolved");//返回的结果字符串数组
|
||||||
|
if (!empty( $payload ) && ($sig === $sig2))
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach ($event_data['payments'] as $payment) {
|
||||||
|
//if ((strtolower($payment['status']) === 'confirmed')||(strtolower($payment['status']) === 'resolved')) {
|
||||||
|
if(in_array(strtolower($payment['status']),$result_str)){
|
||||||
|
$return_pay_amount = $payment['value']['local']['amount'];
|
||||||
|
$return_currency=$payment['value']['local']['currency'];
|
||||||
|
$return_status=strtolower($payment['status']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($return_currency !== 'CNY')
|
||||||
|
{
|
||||||
|
return 'error|Notify: Wrong currency:'.$return_currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bccomp = bccomp($order->actual_price, $return_pay_amount, 2); //如果订单金额 大于 实际支付金额 返回1,抛出异常
|
||||||
|
if ($bccomp == 1) {
|
||||||
|
throw new \Exception(__('Coinbase付款金额不足'));
|
||||||
|
}
|
||||||
|
$return_merchant_order_id = $event_data['metadata']['customer_id'];//卡网订单号
|
||||||
|
$tradeid = $event_data['code'];//Coinbase订单号
|
||||||
|
//if($return_status === 'confirmed'||$return_status === 'resolved')
|
||||||
|
if(in_array(strtolower($payment['status']),$result_str)) {
|
||||||
|
$this->orderProcessService->completedOrder($return_merchant_order_id, $order->actual_price, $tradeid);// 卡网订单号,订单金额(不能传入支付金额,否则抛出订单金额不一致异常),收款平台订单号
|
||||||
|
return "{\"status\": 200}";
|
||||||
|
} else {
|
||||||
|
//不合法的数据
|
||||||
|
return 'fail';
|
||||||
|
//返回失败 继续补单
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//不合法的数据
|
||||||
|
return 'fail|wrong sig';
|
||||||
|
//返回失败 继续补单
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
104
app/Http/Controllers/Pay/EpusdtController.php
Normal file
104
app/Http/Controllers/Pay/EpusdtController.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EpusdtController extends PayController
|
||||||
|
{
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
$parameter = [
|
||||||
|
"amount" => (float)$this->order->actual_price,//原价
|
||||||
|
"order_id" => $this->order->order_sn, //可以是用户ID,站内商户订单号,用户名
|
||||||
|
'redirect_url' => route('epusdt-return', ['order_id' => $this->order->order_sn]),
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
];
|
||||||
|
$parameter['signature'] = $this->epusdtSign($parameter, $this->payGateway->merchant_id);
|
||||||
|
$client = new Client([
|
||||||
|
'headers' => [ 'Content-Type' => 'application/json' ]
|
||||||
|
]);
|
||||||
|
$response = $client->post($this->payGateway->merchant_pem, ['body' => json_encode($parameter)]);
|
||||||
|
$body = json_decode($response->getBody()->getContents(), true);
|
||||||
|
if (!isset($body['status_code']) || $body['status_code'] != 200) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $body['message']);
|
||||||
|
}
|
||||||
|
return redirect()->away($body['data']['payment_url']);
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
} catch (GuzzleException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function epusdtSign(array $parameter, string $signKey)
|
||||||
|
{
|
||||||
|
ksort($parameter);
|
||||||
|
reset($parameter); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = '';
|
||||||
|
$urls = '';
|
||||||
|
foreach ($parameter as $key => $val) {
|
||||||
|
if ($val == '') continue;
|
||||||
|
if ($key != 'signature') {
|
||||||
|
if ($sign != '') {
|
||||||
|
$sign .= "&";
|
||||||
|
$urls .= "&";
|
||||||
|
}
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
$urls .= "$key=" . urlencode($val); //拼接为url参数形式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sign = md5($sign . $signKey);//密码追加进入开始MD5签名
|
||||||
|
return $sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['order_id']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != 'pay/epusdt'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$signature = $this->epusdtSign($data, $payGateway->merchant_id);
|
||||||
|
if ($data['signature'] != $signature) { //不合法的数据
|
||||||
|
return 'fail'; //返回失败 继续补单
|
||||||
|
} else {
|
||||||
|
//合法的数据
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($data['order_id'], $data['amount'], $data['trade_id']);
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$oid = $request->get('order_id');
|
||||||
|
// 异步通知还没到就跳转了,所以这里休眠2秒
|
||||||
|
sleep(2);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $oid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
83
app/Http/Controllers/Pay/MapayController.php
Normal file
83
app/Http/Controllers/Pay/MapayController.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class MapayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
$parameter = array(
|
||||||
|
"id" => (int)$this->payGateway->merchant_id,//平台ID号
|
||||||
|
"price" => (float)$this->order->actual_price,//原价
|
||||||
|
"pay_id" => $this->order->order_sn, //可以是用户ID,站内商户订单号,用户名
|
||||||
|
"param" => $this->payGateway->pay_check,//自定义参数
|
||||||
|
"act" => 0,//是否开启认证版的免挂机功能
|
||||||
|
"outTime" => 120,//二维码超时设置
|
||||||
|
"page" => 1,//付款页面展示方式
|
||||||
|
'return_url' => url('detail-order-sn', ['orderSN' => $this->order->order_sn]),
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
"pay_type" => 0,//支付宝使用官方接口
|
||||||
|
"chart" => 'utf-8'//字符编码方式
|
||||||
|
//其他业务参数根据在线开发文档,添加参数.文档地址:https://codepay.fateqq.com/apiword/
|
||||||
|
//如"参数名"=>"参数值"
|
||||||
|
);
|
||||||
|
switch ($payway){
|
||||||
|
case 'mqq':
|
||||||
|
$parameter['type'] = 2;
|
||||||
|
break;
|
||||||
|
case 'mzfb':
|
||||||
|
$parameter['type'] = 1;
|
||||||
|
break;
|
||||||
|
case 'mwx':
|
||||||
|
default:
|
||||||
|
$parameter['type'] = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$quri = md5_signquery($parameter, $this->payGateway->merchant_pem);
|
||||||
|
$payurl = $this->payGateway->merchant_key . $quri; //支付页面
|
||||||
|
return redirect()->away($payurl);
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->post();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['pay_id']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/mapay'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$query = signquery_string($data);
|
||||||
|
if (!$data['pay_no'] || md5($query . $payGateway->merchant_pem ) != $data['sign']) { //不合法的数据
|
||||||
|
return 'fail'; //返回失败 继续补单
|
||||||
|
} else { //合法的数据
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($data['pay_id'], $data['money'], $data['pay_id']);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
70
app/Http/Controllers/Pay/PayjsController.php
Normal file
70
app/Http/Controllers/Pay/PayjsController.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Xhat\Payjs\Facades\Payjs;
|
||||||
|
|
||||||
|
|
||||||
|
class PayjsController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
// 构造订单基础信息
|
||||||
|
$data = [
|
||||||
|
'body' => $this->order->order_sn, // 订单标题
|
||||||
|
'total_fee' => bcmul($this->order->actual_price, 100, 0), // 订单金额
|
||||||
|
'out_trade_no' => $this->order->order_sn, // 订单号
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
];
|
||||||
|
config(['payjs.mchid' => $this->payGateway->merchant_id, 'payjs.key' => $this->payGateway->merchant_pem]);
|
||||||
|
switch ($payway){
|
||||||
|
case 'payjswescan':
|
||||||
|
try{
|
||||||
|
$payres = Payjs::native($data);
|
||||||
|
if ($payres['return_code'] != 1) {
|
||||||
|
throw new RuleValidationException($payres['return_msg']);
|
||||||
|
}
|
||||||
|
$result['payname'] = $this->payGateway->pay_name;
|
||||||
|
$result['actual_price'] = (float)$this->order->actual_price;
|
||||||
|
$result['orderid'] = $this->order->order_sn;
|
||||||
|
$result['qr_code'] = $payres['code_url'];
|
||||||
|
return $this->render('static_pages/qrpay', $result, __('dujiaoka.scan_qrcode_to_pay'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$orderSN = $request->input('out_trade_no');
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
if (!$order) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/payjs'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
config(['payjs.mchid' => $payGateway->merchant_id, 'payjs.key' => $payGateway->merchant_pem]);
|
||||||
|
$notify_info = Payjs::notify();
|
||||||
|
$totalFee = bcdiv($notify_info['total_fee'], 100, 2);
|
||||||
|
$this->orderProcessService->completedOrder($notify_info['out_trade_no'], $totalFee, $notify_info['payjs_order_id']);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
148
app/Http/Controllers/Pay/PaypalPayController.php
Normal file
148
app/Http/Controllers/Pay/PaypalPayController.php
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use AmrShawky\LaravelCurrency\Facade\Currency;
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use PayPal\Api\Amount;
|
||||||
|
use PayPal\Api\Details;
|
||||||
|
use PayPal\Api\Item;
|
||||||
|
use PayPal\Api\ItemList;
|
||||||
|
use PayPal\Api\Payer;
|
||||||
|
use PayPal\Api\Payment;
|
||||||
|
use PayPal\Api\PaymentExecution;
|
||||||
|
use PayPal\Api\RedirectUrls;
|
||||||
|
use PayPal\Api\Transaction;
|
||||||
|
use PayPal\Auth\OAuthTokenCredential;
|
||||||
|
use PayPal\Exception\PayPalConnectionException;
|
||||||
|
use PayPal\Rest\ApiContext;
|
||||||
|
|
||||||
|
class PaypalPayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
const Currency = 'USD'; //货币单位
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
$paypal = new ApiContext(
|
||||||
|
new OAuthTokenCredential(
|
||||||
|
$this->payGateway->merchant_key,
|
||||||
|
$this->payGateway->merchant_pem
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$paypal->setConfig(['mode' => 'live']);
|
||||||
|
$product = $this->order->title;
|
||||||
|
// 得到汇率
|
||||||
|
$total = Currency::convert()
|
||||||
|
->from('CNY')
|
||||||
|
->to('USD')
|
||||||
|
->amount($this->order->actual_price)
|
||||||
|
->round(2)
|
||||||
|
->get();
|
||||||
|
$shipping = 0;
|
||||||
|
$description = $this->order->title;
|
||||||
|
$payer = new Payer();
|
||||||
|
$payer->setPaymentMethod('paypal');
|
||||||
|
$item = new Item();
|
||||||
|
$item->setName($product)->setCurrency(self::Currency)->setQuantity(1)->setPrice($total);
|
||||||
|
$itemList = new ItemList();
|
||||||
|
$itemList->setItems([$item]);
|
||||||
|
$details = new Details();
|
||||||
|
$details->setShipping($shipping)->setSubtotal($total);
|
||||||
|
$amount = new Amount();
|
||||||
|
$amount->setCurrency(self::Currency)->setTotal($total)->setDetails($details);
|
||||||
|
$transaction = new Transaction();
|
||||||
|
$transaction->setAmount($amount)->setItemList($itemList)->setDescription($description)->setInvoiceNumber($this->order->order_sn);
|
||||||
|
$redirectUrls = new RedirectUrls();
|
||||||
|
$redirectUrls->setReturnUrl(route('paypal-return', ['success' => 'ok', 'orderSN' => $this->order->order_sn]))->setCancelUrl(route('paypal-return', ['success' => 'no', 'orderSN' => $this->order->order_sn]));
|
||||||
|
$payment = new Payment();
|
||||||
|
$payment->setIntent('sale')->setPayer($payer)->setRedirectUrls($redirectUrls)->setTransactions([$transaction]);
|
||||||
|
$payment->create($paypal);
|
||||||
|
$approvalUrl = $payment->getApprovalLink();
|
||||||
|
return redirect($approvalUrl);
|
||||||
|
} catch (PayPalConnectionException $payPalConnectionException) {
|
||||||
|
return $this->err($payPalConnectionException->getMessage());
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*paypal 同步回调
|
||||||
|
*/
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$success = $request->input('success');
|
||||||
|
$paymentId = $request->input('paymentId');
|
||||||
|
$payerID = $request->input('PayerID');
|
||||||
|
$orderSN = $request->input('orderSN');
|
||||||
|
if ($success == 'no' || empty($paymentId) || empty($payerID)) {
|
||||||
|
// 取消支付
|
||||||
|
redirect(url('detail-order-sn', ['orderSN' => $payerID]));
|
||||||
|
}
|
||||||
|
$order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
if (!$order) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/paypal'){
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$paypal = new ApiContext(
|
||||||
|
new OAuthTokenCredential(
|
||||||
|
$payGateway->merchant_key,
|
||||||
|
$payGateway->merchant_pem
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$paypal->setConfig(['mode' => 'live']);
|
||||||
|
$payment = Payment::get($paymentId, $paypal);
|
||||||
|
$execute = new PaymentExecution();
|
||||||
|
$execute->setPayerId($payerID);
|
||||||
|
try {
|
||||||
|
$payment->execute($execute, $paypal);
|
||||||
|
$this->orderProcessService->completedOrder($orderSN, $order->actual_price, $paymentId);
|
||||||
|
Log::info("paypal支付成功", ['支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $payerID . '】']);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error("paypal支付失败", ['支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $payerID . '】']);
|
||||||
|
}
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $orderSN]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步通知
|
||||||
|
* TODO: 暂未实现,但是好像只实现同步回调即可。这个可以放在后面实现
|
||||||
|
*/
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
//获取回调结果
|
||||||
|
$json_data = $this->get_JsonData();
|
||||||
|
if(!empty($json_data)){
|
||||||
|
Log::debug("paypal notify info:\r\n" . json_encode($json_data));
|
||||||
|
}else{
|
||||||
|
Log::debug("paypal notify fail:参加为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_JsonData()
|
||||||
|
{
|
||||||
|
$json = file_get_contents('php://input');
|
||||||
|
if ($json) {
|
||||||
|
$json = str_replace("'", '', $json);
|
||||||
|
$json = json_decode($json,true);
|
||||||
|
}
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
113
app/Http/Controllers/Pay/PaysapiController.php
Normal file
113
app/Http/Controllers/Pay/PaysapiController.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class PaysapiController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
const PAY_URI = 'https://pay.bearsoftware.net.cn/';
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//从网页传入price:支付价格, istype:支付渠道:1-支付宝;2-微信支付
|
||||||
|
$price = (float)$this->order->actual_price;
|
||||||
|
$orderuid = $this->order->email; //此处传入您网站用户的用户名,方便在paysapi后台查看是谁付的款,强烈建议加上。可忽略。
|
||||||
|
//校验传入的表单,确保价格为正常价格(整数,1位小数,2位小数都可以),支付渠道只能是1或者2,orderuid长度不要超过33个中英文字。
|
||||||
|
//此处就在您服务器生成新订单,并把创建的订单号传入到下面的orderid中。
|
||||||
|
$goodsname = $this->order->order_sn;
|
||||||
|
$orderid = $this->order->order_sn; //每次有任何参数变化,订单号就变一个吧。
|
||||||
|
$uid = $this->payGateway->merchant_id; //"此处填写PaysApi的uid";
|
||||||
|
$token = $this->payGateway->merchant_pem; //"此处填写PaysApi的Token";
|
||||||
|
$return_url = route('paysapi-return', ['order_id' => $this->order->order_sn]);
|
||||||
|
$notify_url = url($this->payGateway->pay_handleroute . '/notify_url');
|
||||||
|
switch ($payway){
|
||||||
|
case 'pszfb':
|
||||||
|
$istype = 1;
|
||||||
|
break;
|
||||||
|
case 'pswx':
|
||||||
|
default:
|
||||||
|
$istype = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$key = md5($goodsname. $istype . $notify_url . $orderid . $orderuid . $price . $return_url . $token . $uid);
|
||||||
|
//经常遇到有研发问为啥key值返回错误,大多数原因:1.参数的排列顺序不对;2.上面的参数少传了,但是这里的key值又带进去计算了,导致服务端key算出来和你的不一样。
|
||||||
|
$html = "
|
||||||
|
<html><head>
|
||||||
|
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">
|
||||||
|
<title>loading pay...</title>
|
||||||
|
<style type=\"text/css\">
|
||||||
|
body {margin:0;padding:0;}
|
||||||
|
p {position:absolute;
|
||||||
|
left:50%;top:50%;
|
||||||
|
width:330px;height:30px;
|
||||||
|
margin:-35px 0 0 -160px;
|
||||||
|
padding:20px;font:bold 14px/30px \"宋体\", Arial;
|
||||||
|
text-indent:22px;border:1px solid #c5d0dc;}
|
||||||
|
#waiting {font-family:Arial;}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function open_without_referrer(link){
|
||||||
|
document.body.appendChild(document.createElement('iframe')).src='javascript:\"<script>top.location.replace(\''+link+'\')<\/script>\"';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body style=\"\">
|
||||||
|
<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"".self::PAY_URI."\" method=\"post\">
|
||||||
|
<input type=\"hidden\" name=\"goodsname\" value=\"".$goodsname."\">
|
||||||
|
<input type=\"hidden\" name=\"istype\" value=\"".$istype."\">
|
||||||
|
<input type=\"hidden\" name=\"key\" value=\"".$key."\">
|
||||||
|
<input type=\"hidden\" name=\"notify_url\" value=\"".$notify_url."\">
|
||||||
|
<input type=\"hidden\" name=\"orderid\" value=\"".$orderid."\">
|
||||||
|
<input type=\"hidden\" name=\"orderuid\" value=\"".$orderuid."\">
|
||||||
|
<input type=\"hidden\" name=\"price\" value=\"".$price."\">
|
||||||
|
<input type=\"hidden\" name=\"return_url\" value=\"".$return_url."\">
|
||||||
|
<input type=\"hidden\" name=\"uid\" value=\"".$uid."\">
|
||||||
|
<input type=\"submit\" value=\"正在跳转\">
|
||||||
|
</form><script>document.forms['alipaysubmit'].submit();</script></body></html>
|
||||||
|
";
|
||||||
|
return $html;
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->post();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['orderid']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/paysapi'){
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$temps = md5($data['orderid'] . $data['orderuid'] . $data['paysapi_id'] . $data['price'] . $data['realprice'] . $payGateway->merchant_pem);
|
||||||
|
if ($temps != $data['key']){
|
||||||
|
return 'fail';
|
||||||
|
}else{
|
||||||
|
//校验key成功,是自己人。执行自己的业务逻辑:加余额,订单付款成功,装备购买成功等等。
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($data['orderid'], $data['price'], $data['paysapi_id']);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$oid = $request->input('order_id');
|
||||||
|
sleep(1);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $oid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
540
app/Http/Controllers/Pay/StripeController.php
Normal file
540
app/Http/Controllers/Pay/StripeController.php
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
use URL;
|
||||||
|
|
||||||
|
class StripeController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
switch ($payway) {
|
||||||
|
case 'wx':
|
||||||
|
case 'alipay':
|
||||||
|
default:
|
||||||
|
try {
|
||||||
|
\Stripe\Stripe::setApiKey($this->payGateway->merchant_id);
|
||||||
|
$amount = bcmul($this->order->actual_price, 100, 2);
|
||||||
|
$price = $this->order->actual_price;
|
||||||
|
$usd = bcmul($this->getUsdCurrency($this->order->actual_price), 100, 2);
|
||||||
|
$orderid = $this->order->order_sn;
|
||||||
|
$pk = $this->payGateway->merchant_id;
|
||||||
|
$return_url = site_url() . $this->payGateway->pay_handleroute . '/return_url/?orderid=' . $this->order->order_sn;
|
||||||
|
$html = "<html class=\"js cssanimations\">
|
||||||
|
<head lang=\"en\">
|
||||||
|
<meta charset=\"UTF-8\">
|
||||||
|
<title>收银台</title>
|
||||||
|
<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">
|
||||||
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
|
||||||
|
<meta name=\"format-detection\" content=\"telephone=no\">
|
||||||
|
<meta name=\"renderer\" content=\"webkit\">
|
||||||
|
<meta http-equiv=\"Cache-Control\" content=\"no-siteapp\">
|
||||||
|
<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/amazeui@2.7.2/dist/css/amazeui.min.css\">
|
||||||
|
<script src=\"https://cdn.jsdelivr.net/npm/jquery@2.1.4/dist/jquery.min.js\"></script>
|
||||||
|
<script src=\"https://cdn.jsdelivr.net/npm/jquery.qrcode@1.0.3/jquery.qrcode.min.js\"></script>
|
||||||
|
<script src=\"https://cdn.jsdelivr.net/npm/amazeui@2.7.2/dist/js/amazeui.min.js\"></script>
|
||||||
|
<script src=\"https://js.stripe.com/v3/\"></script>
|
||||||
|
<style>
|
||||||
|
@media only screen and (min-width: 641px) {
|
||||||
|
.am-offcanvas {
|
||||||
|
display: block;
|
||||||
|
position: static;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar {
|
||||||
|
position: static;
|
||||||
|
width: auto;
|
||||||
|
background: none;
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
-ms-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 640px) {
|
||||||
|
.am-offcanvas-bar .am-nav > li > a {
|
||||||
|
color: #ccc;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, .3);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05)
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar .am-nav > li > a:hover {
|
||||||
|
background: #404040;
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar .am-nav > li.am-nav-header {
|
||||||
|
color: #777;
|
||||||
|
background: #404040;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
|
||||||
|
text-shadow: 0 1px 0 rgba(0, 0, 0, .5);
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, .3);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 75%
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar .am-nav > li.am-active > a {
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .3)
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-offcanvas-bar .am-nav > li + li {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-head {
|
||||||
|
margin-top: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.am-tab-panel {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-footer {
|
||||||
|
border-top: 1px solid #eeeeee;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
display: inline;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-table {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-tr {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-td {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 61%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.StripeElement {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
padding: 10px 12px;
|
||||||
|
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
box-shadow: 0 1px 3px 0 #e6ebf1;
|
||||||
|
-webkit-transition: box-shadow 150ms ease;
|
||||||
|
transition: box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.StripeElement--focus {
|
||||||
|
box-shadow: 0 1px 3px 0 #cfd7df;
|
||||||
|
}
|
||||||
|
|
||||||
|
.StripeElement--invalid {
|
||||||
|
border-color: #fa755a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.StripeElement--webkit-autofill {
|
||||||
|
background-color: #fefde5 !important;
|
||||||
|
}
|
||||||
|
.form-row {
|
||||||
|
width: 70%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
width: 670px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
background: #32325d;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
padding: 0 14px;
|
||||||
|
box-shadow: 0 4px 6px rgba(50, 50, 93, .11), 0 1px 3px rgba(0, 0, 0, .08);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
text-decoration: none;
|
||||||
|
-webkit-transition: all 150ms ease;
|
||||||
|
transition: all 150ms ease;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class=\"am-g my-head\">
|
||||||
|
<div class=\"am-u-sm-12 am-article\">
|
||||||
|
<h1 class=\"am-article-title\">收银台</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<hr class=\"am-article-divider\">
|
||||||
|
<div class=\"am-container\">
|
||||||
|
<h2>付款信息
|
||||||
|
<div class=\"am-topbar-right\">¥{$price}</div>
|
||||||
|
</h2>
|
||||||
|
<p><small>订单编号:$orderid</small></p>
|
||||||
|
<div class=\"am-tabs\" data-am-tabs=\"\">
|
||||||
|
<ul class=\"am-tabs-nav am-nav am-nav-tabs\">
|
||||||
|
<li class=\"am-active\"><a href=\"#alipay\">Alipay 支付宝</a></li>
|
||||||
|
<li class=\"request-wechat-pay\"><a href=\"#wcpay\">微信支付</a></li>
|
||||||
|
<li class=\"request-card-pay\"><a href=\"#cardpay\">银行卡支付</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class=\"am-tabs-bd am-tabs-bd-ofv\"
|
||||||
|
style=\"touch-action: pan-y; user-select: none; -webkit-user-drag: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\">
|
||||||
|
<div class=\"am-tab-panel am-active\" id=\"alipay\">
|
||||||
|
<a class=\"am-btn am-btn-lg am-btn-warning am-btn-primary\" id=\"alipaybtn\" href=\"#\">进入支付宝付款</a>
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div class=\"am-tab-panel am-fade\" id=\"wcpay\">
|
||||||
|
<div class=\"text-align:center; margin:0 auto; width:60%\">
|
||||||
|
<div class=\"wcpay-qrcode\" style=\"text-align: center; \" data-requested=\"0\">
|
||||||
|
正在加载中...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"am-tab-panel am-fade\" id=\"cardpay\">
|
||||||
|
<div class=\"text-align:center; margin:0 auto; width:60%\">
|
||||||
|
<div class=\"wrapper cardpay_content\" style=\"max-width:500px\">
|
||||||
|
<div class=\"am-alert am-alert-danger\" style=\"display:none\">支付失败,请更换卡片或检查输入信息</div>
|
||||||
|
<form action=\"/pay/stripe/charge\" method=\"post\" id=\"payment-form\">
|
||||||
|
<div class=\"form-row\">
|
||||||
|
<label for=\"card-element\">
|
||||||
|
<p class='am-alert am-alert-secondary'>借记卡或信用卡</p>
|
||||||
|
</label>
|
||||||
|
<div id=\"card-element\">
|
||||||
|
<!-- A Stripe Element will be inserted here. -->
|
||||||
|
</div>
|
||||||
|
<!-- Used to display form errors. -->
|
||||||
|
<div id=\"card-errors\" role=\"alert\"></div>
|
||||||
|
</div>
|
||||||
|
<div class=\"form-row\">
|
||||||
|
<button class=\"button\">支付</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var stripe = Stripe('$pk');
|
||||||
|
var source = '';
|
||||||
|
// Create a Stripe client.
|
||||||
|
|
||||||
|
// Create an instance of Elements.
|
||||||
|
var elements = stripe.elements();
|
||||||
|
|
||||||
|
// Custom styling can be passed to options when creating an Element.
|
||||||
|
// (Note that this demo uses a wider set of styles than the guide below.)
|
||||||
|
var style = {
|
||||||
|
base: {
|
||||||
|
color: '#32325d',
|
||||||
|
fontFamily: '\"Helvetica Neue\", Helvetica, sans-serif',
|
||||||
|
fontSmoothing: 'antialiased',
|
||||||
|
fontSize: '16px',
|
||||||
|
'::placeholder': {
|
||||||
|
color: '#aab7c4'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
invalid: {
|
||||||
|
color: '#fa755a',
|
||||||
|
iconColor: '#fa755a'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an instance of the card Element.
|
||||||
|
var card = elements.create('card', {style: style});
|
||||||
|
|
||||||
|
// Add an instance of the card Element into the `card-element` <div>.
|
||||||
|
card.mount('#card-element');
|
||||||
|
|
||||||
|
// Handle real-time validation errors from the card Element.
|
||||||
|
card.on('change', function (event) {
|
||||||
|
var displayError = document.getElementById('card-errors');
|
||||||
|
if (event.error) {
|
||||||
|
displayError.textContent = event.error.message;
|
||||||
|
} else {
|
||||||
|
displayError.textContent = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle form submission.
|
||||||
|
var form = document.getElementById('payment-form');
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$(\".button\").attr(\"disabled\",\"true\");
|
||||||
|
$(\".button\").html(\"请稍后\");
|
||||||
|
stripe.createToken(card).then(function (result) {
|
||||||
|
if (result.error) {
|
||||||
|
// Inform the user if there was an error.
|
||||||
|
var errorElement = document.getElementById('card-errors');
|
||||||
|
errorElement.textContent = result.error.message;
|
||||||
|
} else {
|
||||||
|
// Send the token to your server.
|
||||||
|
stripeTokenHandler(result.token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Submit the form with the token ID.
|
||||||
|
function stripeTokenHandler(token) {
|
||||||
|
// Insert the token ID into the form so it gets submitted to the server
|
||||||
|
var form = document.getElementById('payment-form');
|
||||||
|
var hiddenInput = document.createElement('input');
|
||||||
|
var hiddenInput1 = document.createElement('input');
|
||||||
|
hiddenInput.setAttribute('type', 'hidden');
|
||||||
|
hiddenInput.setAttribute('name', 'stripeToken');
|
||||||
|
hiddenInput.setAttribute('value', token.id);
|
||||||
|
hiddenInput1.setAttribute('type', 'hidden');
|
||||||
|
hiddenInput1.setAttribute('name', 'orderid');
|
||||||
|
hiddenInput1.setAttribute('value', '$orderid');
|
||||||
|
form.appendChild(hiddenInput);
|
||||||
|
form.appendChild(hiddenInput1);
|
||||||
|
// Submit the form
|
||||||
|
//form.submit();
|
||||||
|
$.ajax({
|
||||||
|
url: '/pay/stripe/charge/?orderid=$orderid&stripeToken=' + token.id,
|
||||||
|
type: 'GET',
|
||||||
|
success: function (result) {
|
||||||
|
if (result == \"success\") {
|
||||||
|
$(\".cardpay_content\").html(\"\");
|
||||||
|
$(\".cardpay_content\").html(\"<p class='am-alert am-alert-success'>支付成功,正在跳转页面</p>\");
|
||||||
|
window.setTimeout(function () {
|
||||||
|
location.href = \"/detail-order-sn/$orderid\"
|
||||||
|
}, 800);
|
||||||
|
} else {
|
||||||
|
$(\".am-alert\").show();
|
||||||
|
$(\".button\").removeAttr(\"disabled\");
|
||||||
|
$(\".button\").html(\"支付\");
|
||||||
|
setTimeout(\" $('.am-alert').hide();\", 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
stripe.createSource({
|
||||||
|
type: 'alipay',
|
||||||
|
amount: $amount,
|
||||||
|
currency: 'cny',
|
||||||
|
// 这里你需要渲染出一些用户的信息,不然后期没法知道是谁在付钱
|
||||||
|
owner: {
|
||||||
|
name: '$orderid',
|
||||||
|
},
|
||||||
|
redirect: {
|
||||||
|
return_url: '$return_url',
|
||||||
|
},
|
||||||
|
}).then(function (result) {
|
||||||
|
$(\"#alipaybtn\").attr(\"href\", result.source.redirect.url);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
function paymentcheck() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/pay/stripe/check/?orderid=$orderid&source=' + source,
|
||||||
|
type: 'GET',
|
||||||
|
success: function (result) {
|
||||||
|
if (result == \"success\") {
|
||||||
|
$(\".wcpay-qrcode\").html(\"\");
|
||||||
|
$(\".wcpay-qrcode\").html(\"<p class='am-alert am-alert-success'>支付成功,正在跳转页面</p>\");
|
||||||
|
window.setTimeout(function () {
|
||||||
|
location.href = \"/detail-order-sn/$orderid\"
|
||||||
|
}, 800);
|
||||||
|
} else {
|
||||||
|
setTimeout(\"paymentcheck()\", 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(\".request-wechat-pay\").click(function () {
|
||||||
|
if ($(\".wcpay-qrcode\").data(\"requested\") == 0) {
|
||||||
|
stripe.createSource({
|
||||||
|
type: 'wechat',
|
||||||
|
amount: $usd,
|
||||||
|
currency: 'usd',
|
||||||
|
owner: {
|
||||||
|
name: '$orderid'
|
||||||
|
},
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.source.id) {
|
||||||
|
$(\".wcpay-qrcode\").html(\"<p class='am-alert am-alert-success'>打开微信 - 扫一扫</p>\");
|
||||||
|
$(\".wcpay-qrcode\").qrcode(result.source.wechat.qr_code_url);
|
||||||
|
$(\".wcpay-qrcode\").data(\"requested\", 1);
|
||||||
|
$(\".wcpay-qrcode\").data(\"sid\", result.source.id);
|
||||||
|
$(\".wcpay-qrcode\").data(\"scs\", result.source.client_secret);
|
||||||
|
source = result.source.id;
|
||||||
|
setTimeout(\"paymentcheck()\", 3000);
|
||||||
|
} else {
|
||||||
|
alert(\"微信支付加载失败\");
|
||||||
|
$(\".wcpay-qrcode\").html(\"<p class='am-alert am-alert-danger'>加载失败,请刷新页面。</p>\");
|
||||||
|
}
|
||||||
|
// handle result.error or result.source
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = $request->all();
|
||||||
|
$cacheord = $this->orderService->detailOrderSN($data['orderid']);
|
||||||
|
if (!$cacheord) {
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $data['orderid']]));
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($cacheord->pay_id);
|
||||||
|
\Stripe\Stripe::setApiKey($payGateway -> merchant_pem);
|
||||||
|
$source_object = \Stripe\Source::retrieve($data['source']);
|
||||||
|
//die($source_object);
|
||||||
|
if ($source_object->status == 'chargeable') {
|
||||||
|
\Stripe\Charge::create([
|
||||||
|
'amount' => $source_object->amount,
|
||||||
|
'currency' => $source_object->currency,
|
||||||
|
'source' => $data['source'],
|
||||||
|
]);
|
||||||
|
if ($source_object->owner->name == $data['orderid']) {
|
||||||
|
$this->orderProcessService->completedOrder($data['orderid'], $source_object->amount / 100, $source_object->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $data['orderid']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check(Request $request)
|
||||||
|
{
|
||||||
|
|
||||||
|
$data = $request->all();
|
||||||
|
$cacheord = $this->orderService->detailOrderSN($data['orderid']);
|
||||||
|
if (!$cacheord) {
|
||||||
|
//可能已异步回调成功,跳转
|
||||||
|
return 'fail';
|
||||||
|
} else {
|
||||||
|
$payGateway = $this->payService->detail($cacheord->pay_id);
|
||||||
|
\Stripe\Stripe::setApiKey($payGateway -> merchant_pem);
|
||||||
|
$source_object = \Stripe\Source::retrieve($data['source']);
|
||||||
|
if ($source_object->status == 'chargeable') {
|
||||||
|
\Stripe\Charge::create([
|
||||||
|
'amount' => $source_object->amount,
|
||||||
|
'currency' => $source_object->currency,
|
||||||
|
'source' => $data['source'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($source_object->status == 'consumed' && $source_object->owner->name == $data['orderid']) {
|
||||||
|
$this->orderProcessService->completedOrder($data['orderid'], $cacheord->actual_price, $source_object->id);
|
||||||
|
return 'success';
|
||||||
|
} else {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function charge(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$cacheord = $this->orderService->detailOrderSN($data['orderid']);
|
||||||
|
if (!$cacheord) {
|
||||||
|
//可能已异步回调成功,跳转
|
||||||
|
return 'fail';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$payGateway = $this->payService->detail($cacheord->pay_id);
|
||||||
|
\Stripe\Stripe::setApiKey($payGateway -> merchant_pem);
|
||||||
|
$result = \Stripe\Charge::create([
|
||||||
|
'amount' => bcmul($this->getUsdCurrency($cacheord->actual_price), 100,0),
|
||||||
|
'currency' => 'usd',
|
||||||
|
'source' => $data['stripeToken'],
|
||||||
|
]);
|
||||||
|
if ($result->status == 'succeeded') {
|
||||||
|
$this->orderProcessService->completedOrder($data['orderid'], $cacheord->actual_price, $data['stripeToken']);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据RMB获取美元
|
||||||
|
* @param $cny
|
||||||
|
* @return float|int
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getUsdCurrency($cny)
|
||||||
|
{
|
||||||
|
$client = new Client();
|
||||||
|
$res = $client->get('https://m.cmbchina.com/api/rate/fx-rate');
|
||||||
|
$fxrate = json_decode($res->getBody(), true);
|
||||||
|
$data = $fxrate['body']['data'];
|
||||||
|
if (!isset($data)) {
|
||||||
|
throw new \Exception('汇率接口异常');
|
||||||
|
}
|
||||||
|
$dfFxrate = 0.13;
|
||||||
|
foreach ($data as $item) {
|
||||||
|
if ($item['ccyNbr'] == "美元") {
|
||||||
|
$dfFxrate = bcdiv(100, $item['rtcOfr'], 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bcmul($cny , $dfFxrate , 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
105
app/Http/Controllers/Pay/TokenPayController.php
Normal file
105
app/Http/Controllers/Pay/TokenPayController.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by LightCountry.
|
||||||
|
*
|
||||||
|
* @author https://github.com/LightCountry
|
||||||
|
* @copyright https://github.com/LightCountry
|
||||||
|
* @link https://github.com/LightCountry/TokenPay
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TokenPayController extends PayController
|
||||||
|
{
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
$parameter = [
|
||||||
|
"ActualAmount" => (float)$this->order->actual_price,//原价
|
||||||
|
"OutOrderId" => $this->order->order_sn,
|
||||||
|
"OrderUserKey" => $this->order->email,
|
||||||
|
"Currency" => $this->payGateway->merchant_id,
|
||||||
|
'RedirectUrl' => route('tokenpay-return', ['order_id' => $this->order->order_sn]),
|
||||||
|
'NotifyUrl' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
];
|
||||||
|
$parameter['Signature'] = $this->VerifySign($parameter, $this->payGateway->merchant_key);
|
||||||
|
$client = new Client([
|
||||||
|
'headers' => [ 'Content-Type' => 'application/json' ]
|
||||||
|
]);
|
||||||
|
$response = $client->post($this->payGateway->merchant_pem.'/CreateOrder', ['body' => json_encode($parameter)]);
|
||||||
|
$body = json_decode($response->getBody()->getContents(), true);
|
||||||
|
if (!isset($body['success']) || $body['success'] != true) {
|
||||||
|
return $this->err(__('dujiaoka.prompt.abnormal_payment_channel') . $body['message']);
|
||||||
|
}
|
||||||
|
return redirect()->away($body['data']);
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
} catch (GuzzleException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function VerifySign(array $parameter, string $signKey)
|
||||||
|
{
|
||||||
|
ksort($parameter);
|
||||||
|
reset($parameter); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = '';
|
||||||
|
$urls = '';
|
||||||
|
foreach ($parameter as $key => $val) {
|
||||||
|
if ($key != 'Signature') {
|
||||||
|
if ($sign != '') {
|
||||||
|
$sign .= "&";
|
||||||
|
$urls .= "&";
|
||||||
|
}
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
$urls .= "$key=" . urlencode($val); //拼接为url参数形式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sign = md5($sign . $signKey);//密码追加进入开始MD5签名
|
||||||
|
return $sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['OutOrderId']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != 'pay/tokenpay'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
//合法的数据
|
||||||
|
$signature = $this->VerifySign($data, $payGateway->merchant_key);
|
||||||
|
if ($data['Signature'] != $signature) { //不合法的数据
|
||||||
|
return 'fail'; //返回失败 继续补单
|
||||||
|
} else {
|
||||||
|
//合法的数据
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($data['OutOrderId'], $data['ActualAmount'], $data['Id']);
|
||||||
|
return 'ok';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$oid = $request->get('order_id');
|
||||||
|
// 异步通知还没到就跳转了,所以这里休眠2秒
|
||||||
|
sleep(2);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $oid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
app/Http/Controllers/Pay/VpayController.php
Normal file
94
app/Http/Controllers/Pay/VpayController.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* VpayController.php
|
||||||
|
* V免签
|
||||||
|
* Author iLay1678
|
||||||
|
* Created on 2020/5/1 11:59
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class VpayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
|
||||||
|
//构造要请求的参数数组,无需改动
|
||||||
|
$parameter = array(
|
||||||
|
"payId" => date('YmdHis') . rand(1, 65535),//平台ID号
|
||||||
|
"price" => (float)$this->order->actual_price,//原价
|
||||||
|
'param' => $this->order->order_sn,
|
||||||
|
'returnUrl' => route('vpay-return', ['order_id' => $this->order->order_sn]),
|
||||||
|
'notifyUrl' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
"isHtml" => 1,
|
||||||
|
);
|
||||||
|
switch ($payway) {
|
||||||
|
case 'vzfb':
|
||||||
|
$parameter['type'] = 2;
|
||||||
|
break;
|
||||||
|
case 'vwx':
|
||||||
|
default:
|
||||||
|
$parameter['type'] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$parameter['sign'] = md5($parameter['payId'] . $parameter['param'] . $parameter['type'] . $parameter['price'] . $this->payGateway->merchant_id);
|
||||||
|
$payurl = $this->payGateway->merchant_pem . 'createOrder?' . http_build_query($parameter); //支付页面
|
||||||
|
return redirect()->away($payurl);
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['param']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
|
||||||
|
if($payGateway->pay_handleroute != 'pay/vpay'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $payGateway->merchant_id;//通讯密钥
|
||||||
|
$payId = $data['payId'];//商户订单号
|
||||||
|
$param = $data['param'];//创建订单的时候传入的参数
|
||||||
|
$type = $data['type'];//支付方式 :微信支付为1 支付宝支付为2
|
||||||
|
$price = $data['price'];//订单金额
|
||||||
|
$reallyPrice = $data['reallyPrice'];//实际支付金额
|
||||||
|
$sign = $data['sign'];//校验签名,计算方式 = md5(payId + param + type + price + reallyPrice + 通讯密钥)
|
||||||
|
//开始校验签名
|
||||||
|
$_sign = md5($payId . $param . $type . $price . $reallyPrice . $key);
|
||||||
|
if ($_sign != $sign) { //不合法的数据
|
||||||
|
return 'fail'; //返回失败 继续补单
|
||||||
|
} else { //合法的数据
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($param, $price, $payId);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$oid = $request->get('order_id');
|
||||||
|
// 异步通知还没到就跳转了,所以这里休眠2秒
|
||||||
|
sleep(2);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $oid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
app/Http/Controllers/Pay/WepayController.php
Normal file
89
app/Http/Controllers/Pay/WepayController.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Yansongda\Pay\Pay;
|
||||||
|
|
||||||
|
class WepayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
$config = [
|
||||||
|
'app_id' => $this->payGateway->merchant_id,
|
||||||
|
'mch_id' => $this->payGateway->merchant_key,
|
||||||
|
'key' => $this->payGateway->merchant_pem,
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
'return_url' => url('detail-order-sn', ['orderSN' => $this->order->order_sn]),
|
||||||
|
'http' => [ // optional
|
||||||
|
'timeout' => 10.0,
|
||||||
|
'connect_timeout' => 10.0,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$order = [
|
||||||
|
'out_trade_no' => $this->order->order_sn,
|
||||||
|
'total_fee' => bcmul($this->order->actual_price, 100, 0),
|
||||||
|
'body' => $this->order->order_sn
|
||||||
|
];
|
||||||
|
switch ($payway){
|
||||||
|
case 'wescan':
|
||||||
|
try{
|
||||||
|
$result = Pay::wechat($config)->scan($order)->toArray();
|
||||||
|
$result['qr_code'] = $result['code_url'];
|
||||||
|
$result['payname'] =$this->payGateway->pay_name;
|
||||||
|
$result['actual_price'] = (float)$this->order->actual_price;
|
||||||
|
$result['orderid'] = $this->order->order_sn;
|
||||||
|
return $this->render('static_pages/qrpay', $result, __('dujiaoka.scan_qrcode_to_pay'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.abnormal_payment_channel') . $e->getMessage());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步通知
|
||||||
|
*/
|
||||||
|
public function notifyUrl()
|
||||||
|
{
|
||||||
|
$xml = file_get_contents('php://input');
|
||||||
|
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||||||
|
$oid = $arr['out_trade_no'];
|
||||||
|
$order = $this->orderService->detailOrderSN($oid);
|
||||||
|
if (!$order) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/wepay'){
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
$config = [
|
||||||
|
'app_id' => $payGateway->merchant_id,
|
||||||
|
'mch_id' => $payGateway->merchant_key,
|
||||||
|
'key' => $payGateway->merchant_pem,
|
||||||
|
];
|
||||||
|
$pay = Pay::wechat($config);
|
||||||
|
try{
|
||||||
|
// 验证签名
|
||||||
|
$result = $pay->verify();
|
||||||
|
$total_fee = bcdiv($result->total_fee, 100, 2);
|
||||||
|
$this->orderProcessService->completedOrder($result->out_trade_no, $total_fee, $result->transaction_id);
|
||||||
|
return 'success';
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
103
app/Http/Controllers/Pay/YipayController.php
Normal file
103
app/Http/Controllers/Pay/YipayController.php
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Controllers\Pay;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Http\Controllers\PayController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class YipayController extends PayController
|
||||||
|
{
|
||||||
|
|
||||||
|
public function gateway(string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 加载网关
|
||||||
|
$this->loadGateWay($orderSN, $payway);
|
||||||
|
//组装支付参数
|
||||||
|
$parameter = [
|
||||||
|
'pid' => $this->payGateway->merchant_id,
|
||||||
|
'type' => $payway,
|
||||||
|
'out_trade_no' => $this->order->order_sn,
|
||||||
|
'return_url' => route('yipay-return', ['order_id' => $this->order->order_sn]),
|
||||||
|
'notify_url' => url($this->payGateway->pay_handleroute . '/notify_url'),
|
||||||
|
'name' => $this->order->order_sn,
|
||||||
|
'money' => (float)$this->order->actual_price,
|
||||||
|
'sign' => $this->payGateway->merchant_pem,
|
||||||
|
'sign_type' =>'MD5'
|
||||||
|
];
|
||||||
|
ksort($parameter); //重新排序$data数组
|
||||||
|
reset($parameter); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = '';
|
||||||
|
foreach ($parameter as $key => $val) {
|
||||||
|
if ($key == "sign" || $key == "sign_type" || $val == "") continue;
|
||||||
|
if ($key != 'sign') {
|
||||||
|
if ($sign != '') {
|
||||||
|
$sign .= "&";
|
||||||
|
}
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sign = md5($sign . $this->payGateway->merchant_pem);//密码追加进入开始MD5签名
|
||||||
|
$parameter['sign'] = $sign;
|
||||||
|
//待请求参数数组
|
||||||
|
$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='" . $this->payGateway->merchant_key . "' method='get'>";
|
||||||
|
|
||||||
|
foreach($parameter as $key => $val) {
|
||||||
|
$sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
//submit按钮控件请不要含有name属性
|
||||||
|
$sHtml = $sHtml."<input type='submit' value=''></form>";
|
||||||
|
$sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
|
||||||
|
return $sHtml;
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifyUrl(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->all();
|
||||||
|
$order = $this->orderService->detailOrderSN($data['out_trade_no']);
|
||||||
|
if (!$order) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
$payGateway = $this->payService->detail($order->pay_id);
|
||||||
|
if (!$payGateway) {
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
if($payGateway->pay_handleroute != '/pay/yipay'){
|
||||||
|
return 'fail';
|
||||||
|
}
|
||||||
|
ksort($data); //重新排序$data数组
|
||||||
|
reset($data); //内部指针指向数组中的第一个元素
|
||||||
|
$sign = '';
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
if ($key == "sign" || $key == "sign_type" || $val == "") continue;
|
||||||
|
if ($key != 'sign') {
|
||||||
|
if ($sign != '') {
|
||||||
|
$sign .= "&";
|
||||||
|
}
|
||||||
|
$sign .= "$key=$val"; //拼接为url参数形式
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$data['trade_no'] || md5($sign . $payGateway->merchant_pem) != $data['sign']) { //不合法的数据
|
||||||
|
return 'fail'; //返回失败 继续补单
|
||||||
|
} else {
|
||||||
|
//合法的数据
|
||||||
|
//业务处理
|
||||||
|
$this->orderProcessService->completedOrder($data['out_trade_no'], $data['money'], $data['trade_no']);
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function returnUrl(Request $request)
|
||||||
|
{
|
||||||
|
$oid = $request->get('order_id');
|
||||||
|
// 有些易支付太垃了,异步通知还没到就跳转了,导致订单显示待支付,其实已经支付了,所以这里休眠2秒
|
||||||
|
sleep(2);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $oid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
130
app/Http/Controllers/PayController.php
Normal file
130
app/Http/Controllers/PayController.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Exceptions\RuleValidationException;
|
||||||
|
use App\Models\Order;
|
||||||
|
use App\Service\OrderProcessService;
|
||||||
|
|
||||||
|
class PayController extends BaseController
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付网关
|
||||||
|
* @var \App\Models\Pay
|
||||||
|
*/
|
||||||
|
protected $payGateway;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单
|
||||||
|
* @var \App\Models\Order
|
||||||
|
*/
|
||||||
|
protected $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单服务层
|
||||||
|
* @var \App\Service\OrderService
|
||||||
|
*/
|
||||||
|
protected $orderService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付服务层
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
protected $payService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单处理层.
|
||||||
|
* @var OrderProcessService
|
||||||
|
*/
|
||||||
|
protected $orderProcessService;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->orderService = app('Service\OrderService');
|
||||||
|
$this->payService = app('Service\PayService');
|
||||||
|
$this->orderProcessService = app('Service\OrderProcessService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单检测
|
||||||
|
*
|
||||||
|
* @param string $orderSN
|
||||||
|
* @throws RuleValidationException
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function checkOrder(string $orderSN)
|
||||||
|
{
|
||||||
|
// 订单
|
||||||
|
$this->order = $this->orderService->detailOrderSN($orderSN);
|
||||||
|
if (!$this->order) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.order_does_not_exist'));
|
||||||
|
}
|
||||||
|
// 订单过期
|
||||||
|
if ($this->order->status == Order::STATUS_EXPIRED) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.order_is_expired'));
|
||||||
|
}
|
||||||
|
// 已经支付了
|
||||||
|
if ($this->order->status > Order::STATUS_WAIT_PAY) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.order_already_paid'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载支付网关
|
||||||
|
*
|
||||||
|
* @param string $orderSN 订单号
|
||||||
|
* @param string $payCheck 支付标识
|
||||||
|
* @throws RuleValidationException
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function loadGateWay(string $orderSN, string $payCheck)
|
||||||
|
{
|
||||||
|
$this->checkOrder($orderSN);
|
||||||
|
// 支付配置
|
||||||
|
$this->payGateway = $this->payService->detailByCheck($payCheck);
|
||||||
|
if (!$this->payGateway) {
|
||||||
|
throw new RuleValidationException(__('dujiaoka.prompt.pay_gateway_does_not_exist'));
|
||||||
|
}
|
||||||
|
// 临时保存支付方式
|
||||||
|
$this->order->pay_id = $this->payGateway->id;
|
||||||
|
$this->order->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关处理.
|
||||||
|
*
|
||||||
|
* @param string $handle 跳转方法
|
||||||
|
* @param string $payway 支付标识
|
||||||
|
* @param string $orderSN 订单.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function redirectGateway(string $handle,string $payway, string $orderSN)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->checkOrder($orderSN);
|
||||||
|
$bccomp = bccomp($this->order->actual_price, 0.00, 2);
|
||||||
|
// 如果订单金额为0 代表无需支付,直接成功
|
||||||
|
if ($bccomp == 0) {
|
||||||
|
$this->orderProcessService->completedOrder($this->order->order_sn, 0.00);
|
||||||
|
return redirect(url('detail-order-sn', ['orderSN' => $this->order->order_sn]));
|
||||||
|
}
|
||||||
|
return redirect(url(urldecode($handle), ['payway' => $payway, 'orderSN' => $orderSN]));
|
||||||
|
} catch (RuleValidationException $exception) {
|
||||||
|
return $this->err($exception->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
app/Http/Kernel.php
Normal file
89
app/Http/Kernel.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use App\Http\Middleware\DujiaoBoot;
|
||||||
|
use App\Http\Middleware\InstallCheck;
|
||||||
|
use App\Http\Middleware\PayGateWay;
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The application's global HTTP middleware stack.
|
||||||
|
*
|
||||||
|
* These middleware are run during every request to your application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middleware = [
|
||||||
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
|
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
\App\Http\Middleware\DujiaoSystem::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware groups.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
'throttle:60,1',
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware.
|
||||||
|
*
|
||||||
|
* These middleware may be assigned to groups or used individually.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $routeMiddleware = [
|
||||||
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
|
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||||
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
|
'dujiaoka.boot' => DujiaoBoot::class,
|
||||||
|
'dujiaoka.pay_gate_way' => PayGateWay::class,
|
||||||
|
'install.check' => InstallCheck::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The priority-sorted list of middleware.
|
||||||
|
*
|
||||||
|
* This forces non-global middleware to always be in the given order.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middlewarePriority = [
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\Authenticate::class,
|
||||||
|
\Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
\Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
21
app/Http/Middleware/Authenticate.php
Normal file
21
app/Http/Middleware/Authenticate.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||||
|
|
||||||
|
class Authenticate extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function redirectTo($request)
|
||||||
|
{
|
||||||
|
if (! $request->expectsJson()) {
|
||||||
|
return route('login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
|
||||||
|
|
||||||
|
class CheckForMaintenanceMode extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be reachable while maintenance mode is enabled.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
58
app/Http/Middleware/DujiaoBoot.php
Normal file
58
app/Http/Middleware/DujiaoBoot.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
use Closure;
|
||||||
|
use Germey\Geetest\GeetestServiceProvider;
|
||||||
|
|
||||||
|
class DujiaoBoot
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
// 安装检查
|
||||||
|
$installLock = base_path() . DIRECTORY_SEPARATOR . 'install.lock';
|
||||||
|
if (!file_exists($installLock)) {
|
||||||
|
return redirect(url('install'));
|
||||||
|
}
|
||||||
|
// 浏览器检测
|
||||||
|
$userAgent = $request->header('user-agent');
|
||||||
|
$nowUri = site_url() . $request->path();
|
||||||
|
$tplPath = 'common/notencent';
|
||||||
|
if (
|
||||||
|
(strpos($userAgent, 'QQ/')
|
||||||
|
||
|
||||||
|
strpos($userAgent, 'MicroMessenger') !== false)
|
||||||
|
&&
|
||||||
|
dujiaoka_config_get('is_open_anti_red', BaseModel::STATUS_OPEN) == BaseModel::STATUS_OPEN
|
||||||
|
) {
|
||||||
|
return response()->view($tplPath, ['nowUri' => $nowUri]);
|
||||||
|
}
|
||||||
|
// 语言检测
|
||||||
|
$lang = dujiaoka_config_get('language', 'zh_CN');
|
||||||
|
app()->setLocale($lang);
|
||||||
|
// 极验
|
||||||
|
$geetest = dujiaoka_config_get('is_open_geetest', BaseModel::STATUS_CLOSE);
|
||||||
|
if ($geetest == BaseModel::STATUS_OPEN) {
|
||||||
|
$geetestConfig = [
|
||||||
|
'key' => dujiaoka_config_get('geetest_key'),
|
||||||
|
'id' => dujiaoka_config_get('geetest_id'),
|
||||||
|
'lang' => $lang
|
||||||
|
];
|
||||||
|
// 覆盖 配置
|
||||||
|
config([
|
||||||
|
'geetest' => array_merge(config('mail'), $geetestConfig)
|
||||||
|
]);
|
||||||
|
// 重新注册服务
|
||||||
|
(new GeetestServiceProvider(app()))->register();
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Http/Middleware/DujiaoSystem.php
Normal file
31
app/Http/Middleware/DujiaoSystem.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\AppServiceProvider;
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class DujiaoSystem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
// 检测https
|
||||||
|
if ($request->getScheme() == 'https') {
|
||||||
|
$httpsConfig = [
|
||||||
|
'https' => true
|
||||||
|
];
|
||||||
|
config([
|
||||||
|
'admin' => array_merge(config('admin'), $httpsConfig)
|
||||||
|
]);
|
||||||
|
(new AppServiceProvider(app()))->register();
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||||
|
|
||||||
|
class EncryptCookies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the cookies that should not be encrypted.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
25
app/Http/Middleware/InstallCheck.php
Normal file
25
app/Http/Middleware/InstallCheck.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class InstallCheck
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
// 安装检查
|
||||||
|
$installLock = base_path() . DIRECTORY_SEPARATOR . 'install.lock';
|
||||||
|
if (file_exists($installLock)) {
|
||||||
|
return redirect(url('/'));
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Http/Middleware/PayGateWay.php
Normal file
21
app/Http/Middleware/PayGateWay.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class PayGateWay
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
27
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class RedirectIfAuthenticated
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param string|null $guard
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle($request, Closure $next, $guard = null)
|
||||||
|
{
|
||||||
|
if (Auth::guard($guard)->check()) {
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app/Http/Middleware/TrimStrings.php
Normal file
18
app/Http/Middleware/TrimStrings.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||||
|
|
||||||
|
class TrimStrings extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the attributes that should not be trimmed.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
}
|
||||||
23
app/Http/Middleware/TrustProxies.php
Normal file
23
app/Http/Middleware/TrustProxies.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TrustProxies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The trusted proxies for this application.
|
||||||
|
*
|
||||||
|
* @var array|string
|
||||||
|
*/
|
||||||
|
protected $proxies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The headers that should be used to detect proxies.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||||
|
}
|
||||||
24
app/Http/Middleware/VerifyCsrfToken.php
Normal file
24
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||||
|
|
||||||
|
class VerifyCsrfToken extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $addHttpCookie = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URIs that should be excluded from CSRF verification.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'pay/*',
|
||||||
|
];
|
||||||
|
}
|
||||||
87
app/Jobs/ApiHook.php
Normal file
87
app/Jobs/ApiHook.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ApiHook implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Order
|
||||||
|
*/
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品服务层.
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $goodsService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
$this->goodsService = app('Service\GoodsService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$goodInfo = $this->goodsService->detail($this->order->goods_id);
|
||||||
|
// 判断是否有配置支付回调
|
||||||
|
if(empty($goodInfo->api_hook)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$postdata = [
|
||||||
|
'title' => $this->order->title,
|
||||||
|
'order_sn' => $this->order->order_sn,
|
||||||
|
'email' => $this->order->email,
|
||||||
|
'actual_price' => $this->order->actual_price,
|
||||||
|
'order_info' => $this->order->info,
|
||||||
|
'good_id' => $goodInfo->id,
|
||||||
|
'gd_name' => $goodInfo->gd_name
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
$opts = [
|
||||||
|
'http' => [
|
||||||
|
'method' => 'POST',
|
||||||
|
'header' => 'Content-type: application/json',
|
||||||
|
'content' => json_encode($postdata,JSON_UNESCAPED_UNICODE)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$context = stream_context_create($opts);
|
||||||
|
file_get_contents($goodInfo->api_hook, false, $context);
|
||||||
|
}
|
||||||
|
}
|
||||||
86
app/Jobs/BarkPush.php
Normal file
86
app/Jobs/BarkPush.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
|
||||||
|
|
||||||
|
class BarkPush implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Order
|
||||||
|
*/
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品服务层.
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $goodsService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
$this->goodsService = app('Service\GoodsService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$goodInfo = $this->goodsService->detail($this->order->goods_id);
|
||||||
|
$client = new Client();
|
||||||
|
$apiUrl = dujiaoka_config_get('bark_server') .'/'. dujiaoka_config_get('bark_token');
|
||||||
|
$params = [
|
||||||
|
"title" => __('dujiaoka.prompt.new_order_push').'('.$this->order->actual_price.'元)',
|
||||||
|
"body" => __('order.fields.order_id') .': '.$this->order->id."\n"
|
||||||
|
. __('order.fields.order_sn') .': '.$this->order->order_sn."\n"
|
||||||
|
. __('order.fields.pay_id') .': '.$this->order->pay->pay_name."\n"
|
||||||
|
. __('order.fields.title') .': '.$this->order->title."\n"
|
||||||
|
. __('order.fields.actual_price') .': '.$this->order->actual_price."\n"
|
||||||
|
. __('order.fields.email') .': '.$this->order->email."\n"
|
||||||
|
. __('goods.fields.gd_name') .': '.$goodInfo->gd_name."\n"
|
||||||
|
. __('goods.fields.in_stock') .': '.$goodInfo->in_stock."\n"
|
||||||
|
. __('order.fields.order_created') .': '.$this->order->created_at,
|
||||||
|
"icon"=>url('assets/common/images/default.jpg'),
|
||||||
|
"level"=>"timeSensitive",
|
||||||
|
"group"=>dujiaoka_config_get('text_logo', '独角数卡')
|
||||||
|
];
|
||||||
|
if (dujiaoka_config_get('is_open_bark_push_url', 0) == BaseModel::STATUS_OPEN) {
|
||||||
|
$params["url"] = url('detail-order-sn/'.$this->order->order_sn);
|
||||||
|
}
|
||||||
|
$client->post($apiUrl,['form_params' => $params, 'verify' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
app/Jobs/CouponBack.php
Normal file
57
app/Jobs/CouponBack.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CouponBack implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务可以执行的最大秒数 (超时时间)。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 20;
|
||||||
|
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// 如果订单有使用优惠码
|
||||||
|
if ($this->order->coupon_id) {
|
||||||
|
// 优惠码次数+1
|
||||||
|
app('Service\CouponService')->retIncrByID($this->order->coupon_id);
|
||||||
|
// 设置订单优惠码已回退
|
||||||
|
app('Service\OrderService')->couponIsBack($this->order->order_sn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
app/Jobs/MailSend.php
Normal file
84
app/Jobs/MailSend.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Mail\MailServiceProvider;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class MailSend implements ShouldQueue
|
||||||
|
{
|
||||||
|
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
private $to;
|
||||||
|
|
||||||
|
private $content;
|
||||||
|
|
||||||
|
private $title;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(string $to, string $title, string $content)
|
||||||
|
{
|
||||||
|
$this->to = $to;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$body = $this->content;
|
||||||
|
$title = $this->title;
|
||||||
|
$sysConfig = cache('system-setting');
|
||||||
|
$mailConfig = [
|
||||||
|
'driver' => $sysConfig['driver'] ?? 'smtp',
|
||||||
|
'host' => $sysConfig['host'] ?? '',
|
||||||
|
'port' => $sysConfig['port'] ?? '465',
|
||||||
|
'username' => $sysConfig['username'] ?? '',
|
||||||
|
'from' => [
|
||||||
|
'address' => $sysConfig['from_address'] ?? '',
|
||||||
|
'name' => $sysConfig['from_name'] ?? '独角发卡'
|
||||||
|
],
|
||||||
|
'password' => $sysConfig['password'] ?? '',
|
||||||
|
'encryption' => $sysConfig['encryption'] ?? ''
|
||||||
|
];
|
||||||
|
$to = $this->to;
|
||||||
|
// 覆盖 mail 配置
|
||||||
|
config([
|
||||||
|
'mail' => array_merge(config('mail'), $mailConfig)
|
||||||
|
]);
|
||||||
|
// 重新注册驱动
|
||||||
|
(new MailServiceProvider(app()))->register();
|
||||||
|
Mail::send(['html' => 'email.mail'], ['body' => $body], function ($message) use ($to, $title){
|
||||||
|
$message->to($to)->subject($title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
63
app/Jobs/OrderExpired.php
Normal file
63
app/Jobs/OrderExpired.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class OrderExpired implements ShouldQueue
|
||||||
|
{
|
||||||
|
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务可以执行的最大秒数 (超时时间)。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单号
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $orderSN;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(string $orderSN)
|
||||||
|
{
|
||||||
|
$this->orderSN = $orderSN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// 如果x分钟后还没支付就算过期
|
||||||
|
$order = app('Service\OrderService')->detailOrderSN($this->orderSN);
|
||||||
|
if ($order && $order->status == Order::STATUS_WAIT_PAY) {
|
||||||
|
app('Service\OrderService')->expiredOrderSN($this->orderSN);
|
||||||
|
// 回退优惠券
|
||||||
|
CouponBack::dispatch($order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
app/Jobs/ServerJiang.php
Normal file
72
app/Jobs/ServerJiang.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ServerJiang implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Order
|
||||||
|
*/
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$postdata = http_build_query([
|
||||||
|
'text' => __('dujiaoka.prompt.new_order_push') . ":{$this->order['ord_title']}",
|
||||||
|
'desp' => "
|
||||||
|
- ". __('order.fields.title') .":{$this->order->title}
|
||||||
|
- ". __('order.fields.order_sn') .":{$this->order->order_sn}
|
||||||
|
- ". __('order.fields.email') .":{$this->order->email}
|
||||||
|
- ". __('order.fields.actual_price') .":{$this->order->actual_price}
|
||||||
|
"
|
||||||
|
]);
|
||||||
|
$opts = [
|
||||||
|
'http' => [
|
||||||
|
'method' => 'POST',
|
||||||
|
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||||
|
'content' => $postdata
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$context = stream_context_create($opts);
|
||||||
|
$apiToken = dujiaoka_config_get('server_jiang_token');
|
||||||
|
file_get_contents('https://sctapi.ftqq.com/' . $apiToken . '.send', false, $context);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
app/Jobs/TelegramPush.php
Normal file
81
app/Jobs/TelegramPush.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramPush implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Order
|
||||||
|
*/
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品服务层.
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $goodsService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
$this->goodsService = app('Service\GoodsService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$goodInfo = $this->goodsService->detail($this->order->goods_id);
|
||||||
|
$formatText = '*'. __('dujiaoka.prompt.new_order_push').'('.$this->order->actual_price.'元)*%0A'
|
||||||
|
. __('order.fields.order_id') .': `'.$this->order->id.'`%0A'
|
||||||
|
. __('order.fields.order_sn') .': `'.$this->order->order_sn.'`%0A'
|
||||||
|
. __('order.fields.pay_id') .': `'.$this->order->pay->pay_name.'`%0A'
|
||||||
|
. __('order.fields.title') .': '.$this->order->title.'%0A'
|
||||||
|
. __('order.fields.actual_price') .': '.$this->order->actual_price.'%0A'
|
||||||
|
. __('order.fields.email') .': `'.$this->order->email.'`%0A'
|
||||||
|
. __('goods.fields.gd_name') .': `'.$goodInfo->gd_name.'`%0A'
|
||||||
|
. __('goods.fields.in_stock') .': `'.$goodInfo->in_stock.'`%0A'
|
||||||
|
. __('order.fields.order_created') .': '.$this->order->created_at;
|
||||||
|
$client = new Client([
|
||||||
|
'timeout' => 30,
|
||||||
|
'proxy'=> ''
|
||||||
|
]);
|
||||||
|
$apiUrl = 'https://api.telegram.org/bot' . dujiaoka_config_get('telegram_bot_token') .
|
||||||
|
'/sendMessage?chat_id=' . dujiaoka_config_get('telegram_userid') . '&parse_mode=Markdown&text='.$formatText;
|
||||||
|
$client->post($apiUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
app/Jobs/WorkWeiXinPush.php
Normal file
82
app/Jobs/WorkWeiXinPush.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
|
||||||
|
class WorkWeiXinPush implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务最大尝试次数。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务运行的超时时间。
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $timeout = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Order
|
||||||
|
*/
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品服务层.
|
||||||
|
* @var \App\Service\PayService
|
||||||
|
*/
|
||||||
|
private $goodsService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Order $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
$this->goodsService = app('Service\GoodsService');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$goodInfo = $this->goodsService->detail($this->order->goods_id);
|
||||||
|
$client = new Client();
|
||||||
|
$apiUrl = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key='. dujiaoka_config_get('qywxbot_key');
|
||||||
|
$params = [
|
||||||
|
"msgtype"=>"markdown",
|
||||||
|
"markdown"=>[
|
||||||
|
"content"=>__('dujiaoka.prompt.new_order_push').'(<font color="warning">'.$this->order->actual_price."</font>元)\n"
|
||||||
|
.'>'.__('order.fields.order_id') .': <font color="comment">'.$this->order->id."</font>\n"
|
||||||
|
.'>'.__('order.fields.order_sn') .': <font color="comment">'.$this->order->order_sn."</font>\n"
|
||||||
|
.'>'.__('order.fields.pay_id') .': <font color="comment">'.$this->order->pay->pay_name."</font>\n"
|
||||||
|
.'>'.__('order.fields.title') .': <font color="comment">'.$this->order->title."</font>\n"
|
||||||
|
.'>'.__('order.fields.actual_price') .': <font color="comment">'.$this->order->actual_price."</font>\n"
|
||||||
|
.'>'.__('order.fields.email') .': <font color="comment">'.$this->order->email."</font>\n"
|
||||||
|
.'>'.__('goods.fields.gd_name') .': <font color="comment">'.$goodInfo->gd_name."</font>\n"
|
||||||
|
.'>'.__('goods.fields.in_stock') .': <font color="comment">'.$goodInfo->in_stock."</font>\n"
|
||||||
|
.'>'.__('order.fields.order_created') .': <font color="comment">'.$this->order->created_at."</font>"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$client->post($apiUrl,['json' => $params, 'verify' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Listeners/GoodsDeleted.php
Normal file
32
app/Listeners/GoodsDeleted.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Models\Carmis;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use App\Events\GoodsDeleted as GoodsDeletedEvent;
|
||||||
|
|
||||||
|
class GoodsDeleted
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(GoodsDeletedEvent $event)
|
||||||
|
{
|
||||||
|
Carmis::query()->where('goods_id', $event->goods->id)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Listeners/GoodsGroupDeleted.php
Normal file
32
app/Listeners/GoodsGroupDeleted.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Models\Goods;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use App\Events\GoodsGroupDeleted as GoodsGroupDeletedEvent;
|
||||||
|
|
||||||
|
class GoodsGroupDeleted
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param GoodsGroupDeletedEvent $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(GoodsGroupDeletedEvent $event)
|
||||||
|
{
|
||||||
|
Goods::query()->where('group_id', $event->goodsGroup->id)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
app/Listeners/OrderUpdated.php
Normal file
80
app/Listeners/OrderUpdated.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Jobs\MailSend;
|
||||||
|
use App\Models\Emailtpl;
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use App\Events\OrderUpdated as OrderUpdatedEvent;
|
||||||
|
|
||||||
|
class OrderUpdated
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*
|
||||||
|
* @param object $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle(OrderUpdatedEvent $event)
|
||||||
|
{
|
||||||
|
$sysCache = cache('system-setting');
|
||||||
|
// 当代充商品状态,将会对顾客进行订单内容推送
|
||||||
|
$order = [
|
||||||
|
'created_at' => date('Y-m-d H:i'),
|
||||||
|
'ord_title' => $event->order->title,
|
||||||
|
'webname' => $sysCache['text_logo'] ?? '独角数卡',
|
||||||
|
'weburl' => config('app.url'),
|
||||||
|
'order_id' => $event->order->order_sn,
|
||||||
|
'ord_price' => $event->order->actual_price,
|
||||||
|
'ord_info' => str_replace(PHP_EOL, '<br/>', $event->order->info)
|
||||||
|
];
|
||||||
|
$to = $event->order->email;
|
||||||
|
// 邮件
|
||||||
|
if ($event->order->type == Order::MANUAL_PROCESSING) {
|
||||||
|
switch ($event->order->status) {
|
||||||
|
case Order::STATUS_PENDING:
|
||||||
|
$mailtpl = Emailtpl::query()->where('tpl_token', 'pending_order')->first()->toArray();
|
||||||
|
self::sendMailToOrderStatus($mailtpl, $order, $to);
|
||||||
|
break;
|
||||||
|
case Order::STATUS_COMPLETED:
|
||||||
|
$mailtpl = Emailtpl::query()->where('tpl_token', 'completed_order')->first()->toArray();
|
||||||
|
self::sendMailToOrderStatus($mailtpl, $order, $to);
|
||||||
|
break;
|
||||||
|
case Order::STATUS_FAILURE:
|
||||||
|
$mailtpl = Emailtpl::query()->where('tpl_token', 'failed_order')->first()->toArray();
|
||||||
|
self::sendMailToOrderStatus($mailtpl, $order, $to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件发送
|
||||||
|
*
|
||||||
|
* @param array $mailtpl 模板
|
||||||
|
* @param array $order 内容
|
||||||
|
* @param string $to 接受者
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
private static function sendMailToOrderStatus(array $mailtpl, array $order, string $to) :void
|
||||||
|
{
|
||||||
|
$info = replace_mail_tpl($mailtpl, $order);
|
||||||
|
MailSend::dispatch($to, $info['tpl_name'], $info['tpl_content']);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Models/BaseModel.php
Normal file
40
app/Models/BaseModel.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* The file was created by Assimon.
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class BaseModel extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
const STATUS_OPEN = 1; // 状态开启
|
||||||
|
const STATUS_CLOSE = 0; // 状态关闭
|
||||||
|
|
||||||
|
const AUTOMATIC_DELIVERY = 1; // 自动发货
|
||||||
|
const MANUAL_PROCESSING = 2; // 人工处理
|
||||||
|
|
||||||
|
/**
|
||||||
|
* map
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public static function getIsOpenMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::STATUS_OPEN => admin_trans('dujiaoka.status_open'),
|
||||||
|
self::STATUS_CLOSE => admin_trans('dujiaoka.status_close')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
55
app/Models/Carmis.php
Normal file
55
app/Models/Carmis.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Carmis extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'carmis';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未售出
|
||||||
|
*/
|
||||||
|
const STATUS_UNSOLD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已售出
|
||||||
|
*/
|
||||||
|
const STATUS_SOLD = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组建映射
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public static function getStatusMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::STATUS_UNSOLD => admin_trans('carmis.fields.status_unsold'),
|
||||||
|
self::STATUS_SOLD => admin_trans('carmis.fields.status_sold')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联商品
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function goods()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Goods::class, 'goods_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
app/Models/Coupon.php
Normal file
58
app/Models/Coupon.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Coupon extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'coupons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一次性使用
|
||||||
|
*/
|
||||||
|
const TYPE_ONE_TIME = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重复使用
|
||||||
|
*/
|
||||||
|
const TYPE_REPEAT = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未使用
|
||||||
|
*/
|
||||||
|
const STATUS_UNUSED = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已使用
|
||||||
|
*/
|
||||||
|
const STATUS_USE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联商品
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function goods()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Goods::class, 'coupons_goods', 'coupons_id', 'goods_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getStatusUseMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::STATUS_USE => admin_trans('coupon.fields.status_use'),
|
||||||
|
self::STATUS_UNUSED => admin_trans('coupon.fields.status_unused'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
15
app/Models/Emailtpl.php
Normal file
15
app/Models/Emailtpl.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Emailtpl extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'emailtpls';
|
||||||
|
|
||||||
|
}
|
||||||
97
app/Models/Goods.php
Normal file
97
app/Models/Goods.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Events\GoodsDeleted;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Goods extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'goods';
|
||||||
|
|
||||||
|
protected $dispatchesEvents = [
|
||||||
|
'deleted' => GoodsDeleted::class
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联分类
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function group()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(GoodsGroup::class, 'group_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联优惠券
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function coupon()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Coupon::class, 'coupons_goods', 'goods_id', 'coupons_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联卡密
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function carmis()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Carmis::class, 'goods_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 库存读取器,将自动发货的库存更改为未出售卡密的数量
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function getInStockAttribute()
|
||||||
|
{
|
||||||
|
if (isset($this->attributes['carmis_count'])
|
||||||
|
&&
|
||||||
|
$this->attributes['type'] == self::AUTOMATIC_DELIVERY
|
||||||
|
) {
|
||||||
|
$this->attributes['in_stock'] = $this->attributes['carmis_count'];
|
||||||
|
}
|
||||||
|
return $this->attributes['in_stock'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组建映射
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public static function getGoodsTypeMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::AUTOMATIC_DELIVERY => admin_trans('goods.fields.automatic_delivery'),
|
||||||
|
self::MANUAL_PROCESSING => admin_trans('goods.fields.manual_processing')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
app/Models/GoodsGroup.php
Normal file
34
app/Models/GoodsGroup.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Events\GoodsGroupDeleted;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class GoodsGroup extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'goods_group';
|
||||||
|
|
||||||
|
protected $dispatchesEvents = [
|
||||||
|
'deleted' => GoodsGroupDeleted::class
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联商品
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function goods()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Goods::class, 'group_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
146
app/Models/Order.php
Normal file
146
app/Models/Order.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Events\OrderUpdated;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Order extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'orders';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待支付
|
||||||
|
*/
|
||||||
|
const STATUS_WAIT_PAY = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待处理
|
||||||
|
*/
|
||||||
|
const STATUS_PENDING = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理中
|
||||||
|
*/
|
||||||
|
const STATUS_PROCESSING = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成
|
||||||
|
*/
|
||||||
|
const STATUS_COMPLETED = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*/
|
||||||
|
const STATUS_FAILURE = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过期
|
||||||
|
*/
|
||||||
|
const STATUS_EXPIRED = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常
|
||||||
|
*/
|
||||||
|
const STATUS_ABNORMAL = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券未回退
|
||||||
|
*/
|
||||||
|
const COUPON_BACK_WAIT = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优惠券已回退
|
||||||
|
*/
|
||||||
|
const COUPON_BACK_OK = 1;
|
||||||
|
|
||||||
|
protected $dispatchesEvents = [
|
||||||
|
'updated' => OrderUpdated::class
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态映射
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public static function getStatusMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::STATUS_WAIT_PAY => admin_trans('order.fields.status_wait_pay'),
|
||||||
|
self::STATUS_PENDING => admin_trans('order.fields.status_pending'),
|
||||||
|
self::STATUS_PROCESSING => admin_trans('order.fields.status_processing'),
|
||||||
|
self::STATUS_COMPLETED => admin_trans('order.fields.status_completed'),
|
||||||
|
self::STATUS_FAILURE => admin_trans('order.fields.status_failure'),
|
||||||
|
self::STATUS_ABNORMAL => admin_trans('order.fields.status_abnormal'),
|
||||||
|
self::STATUS_EXPIRED => admin_trans('order.fields.status_expired')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型映射
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public static function getTypeMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::AUTOMATIC_DELIVERY => admin_trans('goods.fields.automatic_delivery'),
|
||||||
|
self::MANUAL_PROCESSING => admin_trans('goods.fields.manual_processing')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联商品
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function goods()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Goods::class, 'goods_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联优惠券
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function coupon()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Coupon::class, 'coupon_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联支付
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*
|
||||||
|
* @author assimon<ashang@utf8.hk>
|
||||||
|
* @copyright assimon<ashang@utf8.hk>
|
||||||
|
* @link http://utf8.hk/
|
||||||
|
*/
|
||||||
|
public function pay()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Pay::class, 'pay_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
app/Models/Pay.php
Normal file
57
app/Models/Pay.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Pay extends BaseModel
|
||||||
|
{
|
||||||
|
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'pays';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转
|
||||||
|
*/
|
||||||
|
const METHOD_JUMP = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扫码
|
||||||
|
*/
|
||||||
|
const METHOD_SCAN = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电脑
|
||||||
|
*/
|
||||||
|
const PAY_CLIENT_PC = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机
|
||||||
|
*/
|
||||||
|
const PAY_CLIENT_MOBILE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用
|
||||||
|
*/
|
||||||
|
const PAY_CLIENT_ALL = 3;
|
||||||
|
|
||||||
|
public static function getMethodMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::METHOD_JUMP => admin_trans('pay.fields.method_jump'),
|
||||||
|
self::METHOD_SCAN => admin_trans('pay.fields.method_scan'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getClientMap()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::PAY_CLIENT_PC => admin_trans('pay.fields.pay_client_pc'),
|
||||||
|
self::PAY_CLIENT_MOBILE => admin_trans('pay.fields.pay_client_mobile'),
|
||||||
|
self::PAY_CLIENT_ALL => admin_trans('pay.fields.pay_client_all'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
app/Providers/AppServiceProvider.php
Normal file
60
app/Providers/AppServiceProvider.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Service\CarmisService;
|
||||||
|
use App\Service\CouponService;
|
||||||
|
use App\Service\EmailtplService;
|
||||||
|
use App\Service\GoodsService;
|
||||||
|
use App\Service\OrderProcessService;
|
||||||
|
use App\Service\OrderService;
|
||||||
|
use App\Service\PayService;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Jenssegers\Agent\Agent;
|
||||||
|
|
||||||
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
$this->app->singleton('Service\GoodsService', function () {
|
||||||
|
return $this->app->make(GoodsService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\PayService', function () {
|
||||||
|
return $this->app->make(PayService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\CarmisService', function () {
|
||||||
|
return $this->app->make(CarmisService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\OrderService', function () {
|
||||||
|
return $this->app->make(OrderService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\CouponService', function () {
|
||||||
|
return $this->app->make(CouponService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\OrderProcessService', function () {
|
||||||
|
return $this->app->make(OrderProcessService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Service\EmailtplService', function () {
|
||||||
|
return $this->app->make(EmailtplService::class);
|
||||||
|
});
|
||||||
|
$this->app->singleton('Jenssegers\Agent', function () {
|
||||||
|
return $this->app->make(Agent::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Providers/AuthServiceProvider.php
Normal file
30
app/Providers/AuthServiceProvider.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
class AuthServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The policy mappings for the application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $policies = [
|
||||||
|
// 'App\Model' => 'App\Policies\ModelPolicy',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any authentication / authorization services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
$this->registerPolicies();
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Providers/BroadcastServiceProvider.php
Normal file
21
app/Providers/BroadcastServiceProvider.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class BroadcastServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap any application services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
Broadcast::routes();
|
||||||
|
|
||||||
|
require base_path('routes/channels.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Providers/EventServiceProvider.php
Normal file
45
app/Providers/EventServiceProvider.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Events\GoodsDeleted;
|
||||||
|
use App\Events\GoodsGroupDeleted;
|
||||||
|
use App\Events\OrderUpdated;
|
||||||
|
use Illuminate\Auth\Events\Registered;
|
||||||
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
|
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
|
class EventServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The event listener mappings for the application.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $listen = [
|
||||||
|
Registered::class => [
|
||||||
|
SendEmailVerificationNotification::class,
|
||||||
|
],
|
||||||
|
GoodsGroupDeleted::class => [
|
||||||
|
\App\Listeners\GoodsGroupDeleted::class,
|
||||||
|
],
|
||||||
|
GoodsDeleted::class => [
|
||||||
|
\App\Listeners\GoodsDeleted::class,
|
||||||
|
],
|
||||||
|
OrderUpdated::class => [
|
||||||
|
\App\Listeners\OrderUpdated::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any events for your application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
80
app/Providers/RouteServiceProvider.php
Normal file
80
app/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
class RouteServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This namespace is applied to your controller routes.
|
||||||
|
*
|
||||||
|
* In addition, it is set as the URL generator's root namespace.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $namespace = 'App\Http\Controllers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the "home" route for your application.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const HOME = '/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define your route model bindings, pattern filters, etc.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
|
||||||
|
parent::boot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the routes for the application.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function map()
|
||||||
|
{
|
||||||
|
$this->mapApiRoutes();
|
||||||
|
|
||||||
|
$this->mapWebRoutes();
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the "web" routes for the application.
|
||||||
|
*
|
||||||
|
* These routes all receive session state, CSRF protection, etc.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function mapWebRoutes()
|
||||||
|
{
|
||||||
|
Route::middleware('web')
|
||||||
|
->namespace($this->namespace)
|
||||||
|
->group(base_path('routes/web.php'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the "api" routes for the application.
|
||||||
|
*
|
||||||
|
* These routes are typically stateless.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function mapApiRoutes()
|
||||||
|
{
|
||||||
|
Route::prefix('api')
|
||||||
|
->middleware('api')
|
||||||
|
->namespace($this->namespace)
|
||||||
|
->group(base_path('routes/api.php'));
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Rules/SearchPwd.php
Normal file
44
app/Rules/SearchPwd.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
class SearchPwd implements Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new rule instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the validation rule passes.
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
if (dujiaoka_config_get('is_open_search_pwd') == BaseModel::STATUS_OPEN && empty($value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation error message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return __('dujiaoka.prompt.search_password_can_not_be_empty');
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Rules/VerifyImg.php
Normal file
44
app/Rules/VerifyImg.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use App\Models\BaseModel;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
class VerifyImg implements Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new rule instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the validation rule passes.
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
if (dujiaoka_config_get('is_open_img_code') == BaseModel::STATUS_OPEN && !captcha_check($value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation error message.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return __('dujiaoka.prompt.image_verify_code_error');
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user