diff --git a/MySQL_change.sql b/MySQL_change.sql index 5a3a75e..5b00304 100644 --- a/MySQL_change.sql +++ b/MySQL_change.sql @@ -241,3 +241,11 @@ ENGINE=InnoDB; ALTER TABLE `order_product_items` ADD COLUMN `num` INT(10) NOT NULL COMMENT '购买数量' AFTER `product_id`, ADD COLUMN `price` DECIMAL(20,2) NOT NULL COMMENT '销售价格' AFTER `num`; + +# 16:42 2021/9/2 +ALTER TABLE `agent_products` + CHANGE COLUMN `type` `type` TINYINT(3) NOT NULL DEFAULT '0' COMMENT '0:单品销售;1:组合销售;2:组团旅行社的云产品;' AFTER `is_rec`, + ADD COLUMN `is_cloud` TINYINT NOT NULL DEFAULT 0 COMMENT '是否是(组团旅行社特有)云产品,。0:否;1:是;' AFTER `earnest_timeout`, + ADD COLUMN `agent_cloud_pid` INT(10) NOT NULL DEFAULT '0' COMMENT '组团版旅行社的云产品ID' AFTER `is_cloud`, + ADD INDEX `agent_cloud_pid` (`agent_cloud_pid`); +UPDATE `agent_products` ap SET `is_cloud`=1 WHERE EXISTS(SELECT 1 FROM `agents` WHERE `id`=ap.`agent_id` AND `type`=3); diff --git a/app/AdminAgent/Controllers/AgentProductController.php b/app/AdminAgent/Controllers/AgentProductController.php index f7d1529..067d52d 100644 --- a/app/AdminAgent/Controllers/AgentProductController.php +++ b/app/AdminAgent/Controllers/AgentProductController.php @@ -2,6 +2,7 @@ namespace App\AdminAgent\Controllers; +use App\AdminAgent\Renderable\SelectAgentCloudProduct; use App\AdminAgent\Renderable\SelectGuide; use App\AdminAgent\Renderable\SelectProduct; use App\AdminAgent\Repositories\AgentProduct; @@ -163,13 +164,19 @@ class AgentProductController extends AdminController } $form->display('id'); + + //组团版旅行社不允许选择组团云产品 + $options = Admin::user()->type == AgentType::CLUSTER ? [1 => '组合销售'] : ['单品销售', '组合销售', '组团云产品']; $form->radio('type') - ->options(['单品销售', '组合销售']) - ->default(0)->required() + ->options($options) + ->default(Admin::user()->type == AgentType::CLUSTER ? 1 : 0)->required() ->help('单品销售无需审核,组合销售需要审核才能上架') ->when(0, function (Form $form) { + //组团版没有单品选择功能 + if (Admin::user()->type == AgentType::CLUSTER) return; + /** 单品销售 **/ - $form->selectTable('product_id', '选择产品') + $form->selectTable('product_id', '供应商产品') ->help('产品列表显示的是该产品的标题和图片') ->title('选择产品') ->dialogWidth('80%;min-width:825px;') @@ -177,7 +184,7 @@ class AgentProductController extends AdminController ->model(Product::class); })->when(1, function (Form $form) { /** 组合销售 **/ - $form->multipleSelectTable('product_ids', '选择产品') + $form->multipleSelectTable('product_ids', '供应商产品') ->help('可单选或多选组合销售') ->title('选择产品') ->dialogWidth('80%;min-width:825px;') @@ -189,6 +196,17 @@ class AgentProductController extends AdminController $form->multipleImage('pictures')->removable(false)->uniqueName(); $form->editor('know'); $form->editor('content'); + })->when(2, function (Form $form) { + //组团版旅行社不允许选择组团云产品 + if (Admin::user()->type == AgentType::CLUSTER) return; + + /** 组团云产品 **/ + $form->selectTable('agent_cloud_pid', '组团云产品') + ->help('产品列表显示的是该产品的标题和图片') + ->title('选择产品') + ->dialogWidth('80%;min-width:825px;') + ->from(SelectAgentCloudProduct::make()) + ->model(Product::class); }); $form->text('price')->required(); $form->text('original_price')->required(); @@ -214,7 +232,7 @@ class AgentProductController extends AdminController ]) ->required(); } - $form->switch('is_rec')->help('推荐后将在“我的”页面下方显示'); + $form->switch('is_rec')->help('推荐后将在小程序“我的”页面下方显示'); //$form->selectTable('verifier') // ->title('选择核销人员') // ->dialogWidth('50%;min-width:600px;') //不起作用 @@ -232,10 +250,14 @@ class AgentProductController extends AdminController ->model(Guide::class, 'id', 'name'); } - $form->number('earnest')->width(2)->default(0)->help('单位:元。不输入或输入 0 则不支持定金支付,必须和定金超时时间同时设置才会生效'); - $form->number('earnest_timeout')->width(2)->default(0)->help('单位:分钟。超过这个时间未支付,订单将自动关闭'); - $form->number('deposit')->default(0)->help('单位:元。不输入或输入 0 则不支持订金支付,必须和订金超时时间同时设置才会生效'); - $form->number('deposit_timeout')->default(0)->help('单位:分钟。超过这个时间未支付,订单将自动关闭'); + $form->number('earnest')->min(0) + ->default(0)->help('单位:元。不输入或输入 0 则不支持定金支付,必须和定金超时时间同时设置才会生效'); + $form->number('earnest_timeout')->min(0) + ->default(0)->help('单位:分钟。超过这个时间未支付,订单将自动关闭'); + $form->number('deposit')->min(0) + ->default(0)->help('单位:元。不输入或输入 0 则不支持订金支付,必须和订金超时时间同时设置才会生效'); + $form->number('deposit_timeout')->min(0) + ->default(0)->help('单位:分钟。超过这个时间未支付,订单将自动关闭'); })->saving(function (Form $form) { //不允许修改非自己的数据 if ($form->isEditing() && $form->model()->agent_id != Admin::user()->id) { @@ -331,6 +353,43 @@ class AgentProductController extends AdminController if ($not_in_id) { return $form->response()->error('产品ID ' . join(',', $not_in_id) . ' 库存小于你设置的库存' . $form->stock . ',或不存在、已下架等'); } + + //如果是组团版旅行社,标记为是云产品 + if (Admin::user()->type == AgentType::CLUSTER) { + $form->hidden('is_cloud'); + $form->is_cloud = 1; + } + } + //组团云产品 + else if ($form->type == 2) { + $form->agent_cloud_pid = (int)$form->agent_cloud_pid; + if (!$form->agent_cloud_pid) { + return $form->response()->error('请选择产品'); + } + + //产品信息预判断 + $cloud_product = \App\Models\AgentProduct::where([ + ['stock', '>', 0], + ['status', '=', ProductStatus::ON_SALE], + ['is_cloud', '=', 1], + ['type', '=', 1], + ['agent_id', '<>', Admin::user()->id], + ])->find($form->agent_cloud_pid); + + if (!$cloud_product) { + return $form->response()->error('你选择的组团云产品库存不足或已下架,请重新选择'); + } else if ($cloud_product->stock < $form->stock) { + return $form->response()->error("组团云产品当前库存为{$cloud_product->stock},你设置的库存不能超过该数值"); + } + + //同步关键字段信息 + $form->product_id = $cloud_product->product_id; + $form->product_ids = $cloud_product->product_ids; + $form->guide_id = $cloud_product->guide_id; + $form->title = $cloud_product->title; + $form->pictures = $cloud_product->pictures; + $form->know = $cloud_product->know; + $form->content = $cloud_product->content; } else { return $form->response()->error('不存在此销售方式'); } @@ -339,6 +398,7 @@ class AgentProductController extends AdminController //处理特殊字段 $form->hidden(['agent_id', 'status']); //表单没有的字段,必须加这句才能够重写 $form->agent_id = $agent_id; + $form->agent_cloud_pid = $form->type ==2 ? ($form->agent_cloud_pid ?? 0) : 0; if (array_key_exists('guide_id', $form->input())) { $form->guide_id = $form->guide_id ?? 0; } @@ -381,8 +441,8 @@ class AgentProductController extends AdminController if ($form->isEditing()) { $where[] = ['id', '<>', $form->getKey()]; } - if ($form->repository()->model()->where($where)->exists()) { - return $form->response()->error('该产品已经存在,请勿重复发布'); + if ($id = $form->repository()->model()->where($where)->value('id')) { + return $form->response()->error("你发布的产品ID {$id} 与本产品重复,请检查"); } })->saved(function (Form $form) { /** 保存到组合产品明细,先查询出之前明细,再跟新的比较,若没有则删除,新的产品原来明细表没有的,则插入 **/ @@ -419,6 +479,21 @@ class AgentProductController extends AdminController ); } } + + //如果是组团云产品,同步信息到其它产品 + if ($form->is_cloud) { + \App\Models\AgentProduct::query() + ->where(['agent_cloud_pid' => $form->getKey(), 'type' => 2]) + ->update([ + 'product_id' => $form->product_id, + 'product_ids' => $form->product_ids, + 'guide_id' => $form->guide_id, + 'title' => $form->title, + 'pictures' => explode(',', $form->pictures), + 'know' => $form->know, + 'content' => $form->content, + ]); + } })->deleting(function (Form $form) { //不允许删除非自己的数据 if (array_filter($form->model()->toArray(), fn($v) => $v['agent_id'] != Admin::user()->id)) { diff --git a/app/AdminAgent/Controllers/OrderController.php b/app/AdminAgent/Controllers/OrderController.php index 37e452f..12771c5 100644 --- a/app/AdminAgent/Controllers/OrderController.php +++ b/app/AdminAgent/Controllers/OrderController.php @@ -7,6 +7,8 @@ use App\AdminAgent\Extensions\Grid\ChangeOrderStatus; use App\AdminAgent\Repositories\Order; use App\Common\OrderStatus; use App\Common\PayType; +use App\Models\OrderProductItem; +use App\Models\Product; use App\Models\Supplier; use Dcat\Admin\Admin; use Dcat\Admin\Form; @@ -14,6 +16,7 @@ use Dcat\Admin\Grid; use Dcat\Admin\Show; use Dcat\Admin\Http\Controllers\AdminController; use Dcat\Admin\Widgets\Table; +use Illuminate\Support\Facades\Storage; class OrderController extends AdminController { @@ -29,6 +32,8 @@ class OrderController extends AdminController $grid->disableBatchDelete(); $grid->disableCreateButton(); $grid->disableRowSelector(); + $grid->disableEditButton(); + $grid->disableQuickEditButton(false); $grid->model()->where('agent_id', Admin::user()->id); @@ -39,13 +44,21 @@ class OrderController extends AdminController $grid->column('product', '产品信息') ->display('查看') ->modal('购买产品信息', function ($modal) { - return Table::make(['产品名称', '产品图片', '购买数量', '所属供应商'], - [[ - $this->title, - '', - $this->num, - $this->product->supplier->name, - ]]); + $item = OrderProductItem::with(['supplier:id,name', 'product:id,title,pictures']) + ->where('order_id', $this->id) + ->get(['num', 'supplier_id', 'product_id']); + + $row = []; + foreach($item as $v) { + $picture = $v->product->picture ? Storage::disk('public')->url($v->product->picture) : ''; + $row[] = [ + $v->product->title ?? '', + '', + $v->num ?? '', + $v->supplier->name ?? '', + ]; + } + return Table::make(['产品名称', '产品图片', '购买数量', '所属供应商'], $row); })->xl(); //状态及退款处理 @@ -125,6 +138,7 @@ class OrderController extends AdminController { return Show::make($id, new Order(['product.supplier:id,name']), function (Show $show) { $show->disableDeleteButton(); + $show->disableEditButton(); //不允许查看非自己的数据 if ($show->model()->agent_id != Admin::user()->id) { @@ -178,15 +192,18 @@ class OrderController extends AdminController return $form->response()->error('数据不存在'); } - //不允许编辑的字段 - $form->ignore(['id', 'user_id', 'agent_id', 'agent_product_id', 'product_id', 'product_ids', 'order_no', - 'pay_type', 'paid_money', 'created_at', 'updated_at', 'deleted_at']); + //仅允许编辑name,mobile字段 + $forbid_field = array_diff(array_keys($form->input()), ['name', 'mobile']); + $form->ignore($forbid_field); + $form->deleteInput($forbid_field); //退款不能直接编辑 - if (in_array($form->status, [OrderStatus::REFUNDED, OrderStatus::REFUSED_REFUND])) { - return $form->response()->error('请通过订单列表的”通过“和”拒绝“按钮来审核退款'); - } else if ($form->status != OrderStatus::OFFLINE_PAID) { - return $form->response()->error('操作禁止'); + if ($form->status !== null) { + if (in_array($form->status, [OrderStatus::REFUNDED, OrderStatus::REFUSED_REFUND])) { + return $form->response()->error('请通过订单列表的”通过“和”拒绝“按钮来审核退款'); + } else if ($form->status != OrderStatus::OFFLINE_PAID) { + return $form->response()->error('操作禁止'); + } } })->saved(function (Form $form) { return $form->response()->success('更新成功')->refresh(); diff --git a/app/AdminAgent/Renderable/SelectAgentCloudProduct.php b/app/AdminAgent/Renderable/SelectAgentCloudProduct.php new file mode 100644 index 0000000..28a9779 --- /dev/null +++ b/app/AdminAgent/Renderable/SelectAgentCloudProduct.php @@ -0,0 +1,53 @@ +id; + Admin::translation('agent-product'); + return Grid::make(new AgentProduct(), function (Grid $grid) { + $grid->disableActions(); + $grid->disableBatchDelete(); + $grid->disableBatchActions(); + + $grid->model()->where([ + ['stock', '>', 0], + ['status', '=', ProductStatus::ON_SALE], + ['is_cloud', '=', 1], + ['type', '=', 1], + ['agent_id', '<>', Admin::user()->id], + ]); + + $grid->quickSearch(['title'])->placeholder('搜索产品名称'); + + $grid->column('id'); + $grid->column('title'); + $grid->column('picture')->image('', 60, 60); + $grid->column('sale'); + $grid->column('stock'); + $grid->column('updated_at'); + + $grid->paginate(15); + + $grid->filter(function (Grid\Filter $filter) { + $filter->panel(); + + $filter->like('title')->width(4); + }); + }); + } +} diff --git a/app/Common/OrderStatus.php b/app/Common/OrderStatus.php index 2efde99..7e170f5 100644 --- a/app/Common/OrderStatus.php +++ b/app/Common/OrderStatus.php @@ -16,7 +16,7 @@ class OrderStatus /** @var int 待付款 */ const UNPAID = 0; - /** @var int 已付定金 */ + /** @var int 已付(订)定金 */ const PAY_EARNEST = 1; /** @var int 已付全款 */ @@ -48,7 +48,7 @@ class OrderStatus return [ self::CANCEL => '已取消', self::UNPAID => '待付款', - self::PAY_EARNEST => '已付定金', + self::PAY_EARNEST => '已付定(订)金', self::PAID => '已付款', self::PAID_RETAINAGE => '已付尾款', self::OFFLINE_UNPAID => '[线下]未付款', diff --git a/app/Http/Controllers/Api/OrderController.php b/app/Http/Controllers/Api/OrderController.php index 18be8cc..0f9bb70 100644 --- a/app/Http/Controllers/Api/OrderController.php +++ b/app/Http/Controllers/Api/OrderController.php @@ -58,7 +58,7 @@ class OrderController extends Controller ->toArray(); $time = time(); - $timeout_ids = []; + $prefix = Storage::disk('public')->url(''); foreach ($order_list['data'] as &$v) { //图片加上域名 @@ -77,26 +77,23 @@ class OrderController extends Controller } //未付款订单提示剩余付款时间 - if ($v['timeout'] !== null && $v['status'] == Status::UNPAID) { + if ($v['timeout'] !== null) { $second = strtotime($v['timeout']) - $time; - if ($second > 0) { + if ($second > 0 && $v['status'] == Status::UNPAID) { $v['status_text'] = '请在' . ceil($second / 60) . '分钟内付款'; - } /*else { //TODO 此部分由定时 + } else if ($second < 0 && $v['status'] == Status::PAY_EARNEST) { + $v['status_text'] = '尾款支付已超时'; + } /*else { //此部分由定时处理 $timeout_ids[] = $v['id']; $v['status'] = Status::CANCEL; $v['status_text'] = '已取消'; - //TODO 加回库存,未考虑到几天/几个月后再打开订单列表页的情况,需要定时任务处理 + //此部分已由定时任务处理 Product::query()->find($v['product_id'])->increment('stock', $v['num']); }*/ } } - //超时订单设置为已取消 TODO 测试阶段暂时注释 - if ($timeout_ids) { - Order::query()->whereIn('id', $timeout_ids)->update(['status' => Status::CANCEL]); - } - return $this->success($order_list); } @@ -325,10 +322,11 @@ class OrderController extends Controller $order = Order::query() ->with('agentProduct') ->where(['user_id' => $this->user_id, 'agent_id' => $this->agent_id]) + ->whereRaw('`timeout` >= NOW()') ->whereIn('status', [Status::UNPAID, Status::PAY_EARNEST]) ->find($id); if (!$order) { - return $this->error('订单不存在或已支付'); + return $this->error('订单已支付或已超时'); } $ap = AgentProduct::with('coupon')->find($order->agent_product_id); diff --git a/app/Models/OrderProductItem.php b/app/Models/OrderProductItem.php index 799ef5d..67d9e3c 100644 --- a/app/Models/OrderProductItem.php +++ b/app/Models/OrderProductItem.php @@ -16,13 +16,23 @@ class OrderProductItem extends BaseModel //$this->timestamps = false; } - function order() + public function order() { return $this->belongsTo(Order::class,'order_id','id'); } - function product() + public function product() { return $this->belongsTo(Product::class,'product_id','id'); } + + public function supplier() + { + return $this->belongsTo(Supplier::class); + } + + public function agent() + { + return $this->belongsTo(Agent::class); + } }