Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced #1

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d7207c2
升级 Laravel-Admin
leo108 Sep 6, 2018
04d44ca
忽略微信支付证书
leo108 Sep 6, 2018
a3d680e
初始化类目模型
leo108 Sep 6, 2018
abd0171
类目 Seeder 文件
leo108 Sep 6, 2018
7f7bbe6
商品类目管理后台
leo108 Sep 6, 2018
24c5dfa
商品后台添加类目字段
leo108 Sep 6, 2018
8d968fc
前台商品列表支持按类目筛选
leo108 Sep 6, 2018
54bf26a
顶部导航加入商品类目树
leo108 Sep 6, 2018
bfcc829
初始化众筹模型
leo108 Sep 6, 2018
6c4ec1c
后台众筹商品管理
leo108 Sep 6, 2018
16623ca
管理后台商品控制器封装
leo108 Sep 6, 2018
9a8d547
众筹商品详情页
leo108 Sep 6, 2018
d193946
订单模块调整
leo108 Sep 6, 2018
a4cb5ad
开发环境加入 Ngrok
leo108 Sep 6, 2018
59e9cd9
众筹结束逻辑
leo108 Sep 6, 2018
3eb5628
前台分期付款列表页
leo108 Sep 6, 2018
f6783aa
前台分期付款详情页
leo108 Sep 6, 2018
ec8ba81
支付宝分期还款
leo108 Sep 6, 2018
8b8e46b
微信支付分期还款
leo108 Sep 6, 2018
dc96b88
计算逾期费
leo108 Sep 6, 2018
a4e9faf
分期付款退款
leo108 Sep 7, 2018
eef1a5e
商品属性模型
leo108 Sep 7, 2018
ca2f386
商品添加长标题字段
leo108 Sep 7, 2018
e40f876
导入内存条商品数据
leo108 Sep 7, 2018
81e7ffd
将商品模型转为 Elasticsearch 文档数组
leo108 Sep 7, 2018
01188d7
同步商品数据至 Elasticsearch
leo108 Sep 7, 2018
2e813ba
使用 Elasticsearch 重构商品搜索功能
leo108 Sep 7, 2018
38e1b28
分面搜索
leo108 Sep 7, 2018
4198692
修复分面搜索 Bug
leo108 Sep 7, 2018
e8ca2d8
封装 Elasticsearch 查询代码
leo108 Sep 7, 2018
b5f031e
推荐相似商品
leo108 Sep 7, 2018
b314bc6
Elasticsearch 索引迁移命令
leo108 Sep 7, 2018
2b6a9af
移除无用路由
leo108 Sep 7, 2018
27d2402
引入 Horizon
leo108 Sep 7, 2018
982a6e1
初始化秒杀模型
leo108 Sep 7, 2018
0624d50
秒杀商品管理后台
leo108 Sep 7, 2018
4dc65c5
秒杀商品详情页
leo108 Sep 7, 2018
f9ef44f
减少秒杀接口 SQL 查询
leo108 Sep 7, 2018
0285d4f
减少秒杀接口 SQL 查询
leo108 Sep 7, 2018
3483945
随机拒绝
leo108 Sep 7, 2018
36691a0
update readme
summerblue Sep 10, 2018
605fd2c
bug
leo108 Sep 13, 2018
115fde5
使用 copy_to 使 multi_match 支持 nested 对象
leo108 Oct 4, 2018
f6b506a
Merge branch 'master' into advanced
leo108 Oct 11, 2018
7e3de29
分期详情页权限验证
leo108 Oct 22, 2018
cadb014
支付分期还款加上事务
leo108 Nov 11, 2018
ca2ec1e
Merge branch 'master' into advanced
leo108 Nov 13, 2018
98961a1
增加支付宝回调状态校验
leo108 Nov 13, 2018
0129fd8
Merge branch 'master' into advanced
leo108 Nov 16, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

ES_HOSTS=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ yarn-error.log
/public/css
/public/fonts
/public/mix-manifest.json
/resources/wechat_pay
106 changes: 106 additions & 0 deletions app/Admin/Controllers/CategoriesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
namespace App\Admin\Controllers;

use App\Models\Category;
use App\Http\Controllers\Controller;
use Encore\Admin\Controllers\HasResourceActions;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Http\Request;

class CategoriesController extends Controller
{
use HasResourceActions;

public function index(Content $content)
{
return $content
->header('商品类目列表')
->body($this->grid());
}

public function edit($id, Content $content)
{
return $content
->header('编辑商品类目')
->body($this->form(true)->edit($id));
}


public function create(Content $content)
{
return $content
->header('创建商品类目')
->body($this->form(false));
}

protected function grid()
{
// Laravel-Admin 1.5.19 之后的新写法,原写法也仍然可用
$grid = new Grid(new Category);

$grid->id('ID')->sortable();
$grid->name('名称');
$grid->level('层级');
$grid->is_directory('是否目录')->display(function ($value) {
return $value ? '是' : '否';
});
$grid->path('类目路径');
$grid->actions(function ($actions) {
// 不展示 Laravel-Admin 默认的查看按钮
$actions->disableView();
});

return $grid;
}

protected function form($isEditing = false)
{
// Laravel-Admin 1.5.19 之后的新写法,原写法也仍然可用
$form = new Form(new Category);

$form->text('name', '类目名称')->rules('required');

// 如果是编辑的情况
if ($isEditing) {
// 不允许用户修改『是否目录』和『父类目』字段的值
// 用 display() 方法来展示值,with() 方法接受一个匿名函数,会把字段值传给匿名函数并把返回值展示出来
$form->display('is_directory', '是否目录')->with(function ($value) {
return $value ? '是' :'否';
});
// 支持用符号 . 来展示关联关系的字段
$form->display('parent.name', '父类目');
} else {
// 定义一个名为『是否目录』的单选框
$form->radio('is_directory', '是否目录')
->options(['1' => '是', '0' => '否'])
->default('0')
->rules('required');

// 定义一个名为父类目的下拉框
$form->select('parent_id', '父类目')->ajax('/admin/api/categories');
}

return $form;
}

// 定义下拉框搜索接口
public function apiIndex(Request $request)
{
// 用户输入的值通过 q 参数获取
$search = $request->input('q');
$result = Category::query()
// 通过 is_directory 参数来控制
->where('is_directory', boolval($request->input('is_directory', true)))
->where('name', 'like', '%'.$search.'%')
->paginate();

// 把查询出来的结果重新组装成 Laravel-Admin 需要的格式
$result->setCollection($result->getCollection()->map(function (Category $category) {
return ['id' => $category->id, 'text' => $category->full_name];
}));

return $result;
}
}
113 changes: 113 additions & 0 deletions app/Admin/Controllers/CommonProductsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace App\Admin\Controllers;

use App\Http\Controllers\Controller;
use App\Jobs\SyncOneProductToES;
use App\Models\Product;
use Encore\Admin\Controllers\HasResourceActions;
use Encore\Admin\Layout\Content;
use App\Models\Category;
use Encore\Admin\Grid;
use Encore\Admin\Form;

abstract class CommonProductsController extends Controller
{
use HasResourceActions;

// 定义一个抽象方法,返回当前管理的商品类型
abstract public function getProductType();

public function index(Content $content)
{
return $content
->header(Product::$typeMap[$this->getProductType()].'列表')
->body($this->grid());
}

public function edit($id, Content $content)
{
return $content
->header('编辑'.Product::$typeMap[$this->getProductType()])
->body($this->form()->edit($id));
}

public function create(Content $content)
{
return $content
->header('创建'.Product::$typeMap[$this->getProductType()])
->body($this->form());
}

protected function grid()
{
$grid = new Grid(new Product());

// 筛选出当前类型的商品,默认 ID 倒序排序
$grid->model()->where('type', $this->getProductType())->orderBy('id', 'desc');
// 调用自定义方法
$this->customGrid($grid);

$grid->actions(function ($actions) {
$actions->disableView();
$actions->disableDelete();
});
$grid->tools(function ($tools) {
$tools->batch(function ($batch) {
$batch->disableDelete();
});
});

return $grid;
}

// 定义一个抽象方法,各个类型的控制器将实现本方法来定义列表应该展示哪些字段
abstract protected function customGrid(Grid $grid);

protected function form()
{
$form = new Form(new Product());
// 在表单页面中添加一个名为 type 的隐藏字段,值为当前商品类型
$form->hidden('type')->value($this->getProductType());
$form->text('title', '商品名称')->rules('required');
// 放在商品名称后面
$form->text('long_title', '商品长标题')->rules('required');
$form->select('category_id', '类目')->options(function ($id) {
$category = Category::find($id);
if ($category) {
return [$category->id => $category->full_name];
}
})->ajax('/admin/api/categories?is_directory=0');
$form->image('image', '封面图片')->rules('required|image');
$form->editor('description', '商品描述')->rules('required');
$form->radio('on_sale', '上架')->options(['1' => '是', '0' => '否'])->default('0');

// 调用自定义方法
$this->customForm($form);

$form->hasMany('skus', '商品 SKU', function (Form\NestedForm $form) {
$form->text('title', 'SKU 名称')->rules('required');
$form->text('description', 'SKU 描述')->rules('required');
$form->text('price', '单价')->rules('required|numeric|min:0.01');
$form->text('stock', '剩余库存')->rules('required|integer|min:0');
});
// 放在 SKU 下面
$form->hasMany('properties', '商品属性', function (Form\NestedForm $form) {
$form->text('name', '属性名')->rules('required');
$form->text('value', '属性值')->rules('required');
});
$form->saving(function (Form $form) {
$form->model()->price = collect($form->input('skus'))->where(Form::REMOVE_FLAG_NAME, 0)->min('price') ?: 0;
});

$form->saved(function (Form $form) {
$product = $form->model();
$this->dispatch(new SyncOneProductToES($product));
});

return $form;
}

// 定义一个抽象方法,各个类型的控制器将实现本方法来定义表单应该有哪些额外的字段
abstract protected function customForm(Form $form);
}
38 changes: 38 additions & 0 deletions app/Admin/Controllers/CrowdfundingProductsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace App\Admin\Controllers;

use App\Models\CrowdfundingProduct;
use App\Models\Product;
use Encore\Admin\Form;
use Encore\Admin\Grid;

class CrowdfundingProductsController extends CommonProductsController
{
public function getProductType()
{
return Product::TYPE_CROWDFUNDING;
}

protected function customGrid(Grid $grid)
{
$grid->id('ID')->sortable();
$grid->title('商品名称');
$grid->on_sale('已上架')->display(function ($value) {
return $value ? '是' : '否';
});
$grid->price('价格');
$grid->column('crowdfunding.target_amount', '目标金额');
$grid->column('crowdfunding.end_at', '结束时间');
$grid->column('crowdfunding.total_amount', '目前金额');
$grid->column('crowdfunding.status', ' 状态')->display(function ($value) {
return CrowdfundingProduct::$statusMap[$value];
});
}

protected function customForm(Form $form)
{
// 众筹相关字段
$form->text('crowdfunding.target_amount', '众筹目标金额')->rules('required|numeric|min:0.01');
$form->datetime('crowdfunding.end_at', '众筹结束时间')->rules('required|date');
}
}
67 changes: 9 additions & 58 deletions app/Admin/Controllers/OrdersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace App\Admin\Controllers;

use App\Exceptions\InternalException;
use App\Models\CrowdfundingProduct;
use App\Models\Order;
use App\Services\OrderService;
use Illuminate\Http\Request;
use App\Exceptions\InvalidRequestException;
use Encore\Admin\Form;
Expand Down Expand Up @@ -41,6 +43,11 @@ public function ship(Order $order, Request $request)
if (!$order->paid_at) {
throw new InvalidRequestException('该订单未付款');
}
// 众筹订单只有在众筹成功之后发货
if ($order->type === Order::TYPE_CROWDFUNDING &&
$order->items[0]->product->crowdfunding->status !== CrowdfundingProduct::STATUS_SUCCESS) {
throw new InvalidRequestException('众筹订单只能在众筹成功之后发货');
}
// 判断当前订单发货状态是否为未发货
if ($order->ship_status !== Order::SHIP_STATUS_PENDING) {
throw new InvalidRequestException('该订单已发货');
Expand Down Expand Up @@ -98,7 +105,7 @@ protected function grid()
});
}

public function handleRefund(Order $order, HandleRefundRequest $request)
public function handleRefund(Order $order, HandleRefundRequest $request, OrderService $orderService)
{
// 判断订单状态是否正确
if ($order->refund_status !== Order::REFUND_STATUS_APPLIED) {
Expand All @@ -113,7 +120,7 @@ public function handleRefund(Order $order, HandleRefundRequest $request)
'extra' => $extra,
]);
// 调用退款逻辑
$this->_refundOrder($order);
$orderService->refundOrder($order);
} else {
// 将拒绝退款理由放到订单的 extra 字段中
$extra = $order->extra ?: [];
Expand All @@ -127,60 +134,4 @@ public function handleRefund(Order $order, HandleRefundRequest $request)

return $order;
}

protected function _refundOrder(Order $order)
{
// 判断该订单的支付方式
switch ($order->payment_method) {
case 'wechat':
// 生成退款订单号
$refundNo = Order::getAvailableRefundNo();
app('wechat_pay')->refund([
'out_trade_no' => $order->no, // 之前的订单流水号
'total_fee' => $order->total_amount * 100, //原订单金额,单位分
'refund_fee' => $order->total_amount * 100, // 要退款的订单金额,单位分
'out_refund_no' => $refundNo, // 退款订单号
// 微信支付的退款结果并不是实时返回的,而是通过退款回调来通知,因此这里需要配上退款回调接口地址
'notify_url' => route('payment.wechat.refund_notify'),
]);
// 将订单状态改成退款中
$order->update([
'refund_no' => $refundNo,
'refund_status' => Order::REFUND_STATUS_PROCESSING,
]);
break;
case 'alipay':
// 用我们刚刚写的方法来生成一个退款订单号
$refundNo = Order::getAvailableRefundNo();
// 调用支付宝支付实例的 refund 方法
$ret = app('alipay')->refund([
'out_trade_no' => $order->no, // 之前的订单流水号
'refund_amount' => $order->total_amount, // 退款金额,单位元
'out_request_no' => $refundNo, // 退款订单号
]);
// 根据支付宝的文档,如果返回值里有 sub_code 字段说明退款失败
if ($ret->sub_code) {
// 将退款失败的保存存入 extra 字段
$extra = $order->extra;
$extra['refund_failed_code'] = $ret->sub_code;
// 将订单的退款状态标记为退款失败
$order->update([
'refund_no' => $refundNo,
'refund_status' => Order::REFUND_STATUS_FAILED,
'extra' => $extra,
]);
} else {
// 将订单的退款状态标记为退款成功并保存退款订单号
$order->update([
'refund_no' => $refundNo,
'refund_status' => Order::REFUND_STATUS_SUCCESS,
]);
}
break;
default:
// 原则上不可能出现,这个只是为了代码健壮性
throw new InternalException('未知订单支付方式:'.$order->payment_method);
break;
}
}
}
Loading