阶段更新

This commit is contained in:
2023-01-12 14:47:38 +08:00
parent 088dd64b2f
commit 5b8901281c
626 changed files with 39326 additions and 12 deletions

View File

@@ -0,0 +1,168 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| 模块名称
|--------------------------------------------------------------------------
*/
'name' => 'Mall',
/*
|--------------------------------------------------------------------------
| 订单编号计数器位数,取值范围 6 - 16
|--------------------------------------------------------------------------
*/
'order_no_counter_length' => 8,
/*
|--------------------------------------------------------------------------
| 退款单编号前缀
|--------------------------------------------------------------------------
*/
'refund_no_counter_prefix' => 'RF',
/*
|--------------------------------------------------------------------------
| 退款单编号计数器位数,取值范围 6 - 16 - 退款编号前缀位数
|--------------------------------------------------------------------------
*/
'refund_no_counter_length' => 6,
/*
|--------------------------------------------------------------------------
| 商品规格的扩展属性
|--------------------------------------------------------------------------
*/
'sku_extends_attributes' => [
'market_fee' => '平台手续费',
],
'administrator_max_id' => 10000,
'workflow' => [
'orders' => [
'type' => 'state_machine',
'marking_store' => [
'type' => 'single_state',
'property' => 'currentState',
],
'supports' => [Modules\Mall\Models\Order::class],
'places' => [
'INIT',
'CANCEL',
'PAID',
'DELIVERED',
'SIGNED',
'COMPLETED',
'REFUND_APPLY',
'REFUND_AGREE',
'REFUND_REFUSE',
'REFUND_DELIVER',
'REFUND_DELIVERD',
'REFUND_PROCESS',
'REFUND_COMPLETED',
],
'transitions' => [
'cancel' => [
'from' => ['INIT', 'PAID'],
'to' => 'CANCEL',
],
'pay' => [
'from' => 'INIT',
'to' => 'PAID',
],
'deliver' => [
'from' => 'PAID',
'to' => 'DELIVERED',
],
'sign' => [
'from' => 'DELIVERED',
'to' => 'SIGNED',
],
'complete' => [
'from' => 'SIGNED',
'to' => 'COMPLETED',
],
'refund' => [
'from' => ['PAID', 'SIGNED'],
'to' => 'REFUND_APPLY',
],
'agree' => [
'from' => ['PAID', 'SIGNED', 'REFUND_APPLY'],
'to' => 'REFUND_AGREE',
],
'refuse' => [
'from' => 'REFUND_APPLY',
'to' => 'REFUND_REFUSE',
],
'user_deliver' => [
'from' => 'REFUND_AGREE',
'to' => 'REFUND_DELIVER',
],
'user_deliverd' => [
'from' => 'REFUND_DELIVER',
'to' => 'REFUND_DELIVERD',
],
'process' => [
'from' => ['REFUND_AGREE', 'REFUND_DELIVERD'],
'to' => 'REFUND_PROCESS',
],
'completed' => [
'from' => ['PAID', 'DELIVERED', 'SIGNED', 'REFUND_PROCESS', 'REFUND_DELIVERD'],
'to' => 'REFUND_COMPLETED',
],
],
],
'refunds' => [
'type' => 'state_machine',
'marking_store' => [
'type' => 'single_state',
'property' => 'currentState',
],
'supports' => [Modules\Mall\Models\Refund::class],
'places' => [
'REFUND_APPLY',
'REFUND_AGREE',
'REFUND_REFUSE',
'REFUND_PROCESS',
'REFUND_DELIVER',
'REFUND_DELIVERED',
'REFUND_SIGNED',
'REFUND_COMPLETED',
],
'transitions' => [
'agree' => [
'from' => 'REFUND_APPLY',
'to' => 'REFUND_AGREE',
],
'refuse' => [
'from' => 'REFUND_APPLY',
'to' => 'REFUND_REFUSE',
],
'deliver' => [
'from' => 'REFUND_AGREE',
'to' => 'REFUND_DELIVER',
],
'delivered' => [
'from' => 'REFUND_DELIVER',
'to' => 'REFUND_DELIVERED',
],
'sign' => [
'from' => 'REFUND_DELIVERED',
'to' => 'REFUND_SIGNED',
],
'process' => [
'from' => ['REFUND_AGREE', 'REFUND_SIGNED'],
'to' => 'REFUND_PROCESS',
],
'completed' => [
'from' => 'REFUND_PROCESS',
'to' => 'REFUND_COMPLETED',
],
],
],
],
];

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallAddressesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_addresses', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('user_id')->index();
$table->string('name', 32);
$table->string('mobile', 32);
$table->unsignedInteger('province_id');
$table->unsignedInteger('city_id');
$table->unsignedInteger('district_id');
$table->string('address');
$table->boolean('is_default')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_addresses');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallBannersTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_banners', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->default(0)->index()->comment('所属店铺');
$table->string('title');
$table->string('cover');
$table->integer('position')->unsigned();
$table->boolean('status')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_banners');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallBrandsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_brands', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->string('name');
$table->string('cover')->nullable();
$table->string('description')->nullable();
$table->boolean('status')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_brands');
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallCartsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_carts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->unsignedBigInteger('user_id')->index();
$table->unsignedBigInteger('sku_id')->comment('加入购物车的商品条目');
$table->unsignedInteger('qty')->default(1)->comment('数量');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_carts');
}
}

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallCategoriesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_categories', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->unsignedBigInteger('parent_id')->default(0)->index()->comment('父级id');
$table->string('name');
$table->string('slug')->nullable();
$table->string('description')->nullable();
$table->string('cover')->nullable();
$table->integer('order')->default(0);
$table->boolean('status')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_categories');
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallDeliveriesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_deliveries', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->string('name')->comment('模板名称');
$table->unsignedTinyInteger('type')->comment('计费方式1按件数 2按重量');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_deliveries');
}
}

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMallDeliveryRulesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('mall_delivery_rules', function (Blueprint $table) {
$table->id();
$table->integer('delivery_id')->comment('配送模板id');
$table->json('regions')->nullable()->comment('可配送区域(城市id集)');
$table->decimal('first', 8, 2)->default(0)->comment('首件(个)/首重(Kg)');
$table->decimal('first_fee', 8, 2)->default(0)->comment('运费(元)');
$table->decimal('additional', 8, 2)->default(0)->comment('续件/续重');
$table->decimal('additional_fee', 8, 2)->default(0)->comment('续费(元)');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_delivery_rules');
}
}

View File

@@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
use Modules\Mall\Database\Seeders\ExpressSeeder;
class CreateMallExpressesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_expresses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->nullable()->comment('简称');
$table->string('cover')->nullable();
$table->string('description')->nullable();
$table->boolean('status')->default(0);
$table->timestamps();
$table->softDeletes();
});
Artisan::call('db:seed', [
'--class' => ExpressSeeder::class,
]);
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_expresses');
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallGoodsSkusTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_goods_skus', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('goods_id')->index();
$table->string('unit')->nullable()->comment('商品单元编号');
$table->string('cover')->nullable();
$table->decimal('price');
$table->decimal('score')->comment('积分/原石');
$table->decimal('assets', 8, 2)->comment('资产');
$table->integer('stock')->default(0);
$table->decimal('weight')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_goods_skus');
Schema::dropIfExists('mall_goods_sku_spec');
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallGoodsSpecValuesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_goods_spec_values', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('spec_id')->index();
$table->string('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_goods_spec_values');
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallGoodsSpecsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_goods_specs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('goods_id')->index();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_goods_specs');
}
}

View File

@@ -0,0 +1,52 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallGoodsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('mall_goods', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->unsignedBigInteger('category_id')->index()->comment('分类');
$table->unsignedBigInteger('brand_id')->nullable()->index()->comment('品牌ID');
$table->unsignedBigInteger('delivery_id')->nullable()->comment('物流模板');
$table->unsignedTinyInteger('type')->default(1)->comment('规格类型');
$table->unsignedTinyInteger('pay_type')->default(1)->comment('支付方式');
$table->unsignedTinyInteger('deduct_stock_type')->default(1)->comment('库存扣减方式');
$table->unsignedTinyInteger('channel')->default(1)->comment('商品类型');
$table->boolean('status')->default(0);
$table->boolean('is_post_sale')->default(1)->comment('是否可以申请售后');
$table->unsignedInteger('clicks')->default(0)->comment('浏览量');
$table->unsignedInteger('sales')->default(0)->comment('总销量');
$table->decimal('original_price', 10, 2)->default(0)->comment('商品显示的原价');
$table->string('name');
$table->string('cover')->nullable();
$table->json('pictures')->nullable();
$table->string('description')->nullable();
$table->text('content')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_goods');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallGoodsTagTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_goods_tag', function (Blueprint $table) {
$table->unsignedBigInteger('goods_id')->index();
$table->unsignedBigInteger('tag_id')->index();
$table->timestamps();
$table->primary(['goods_id', 'tag_id']);
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_goods_tag');
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallJobsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_jobs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index();
$table->string('name');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_jobs');
}
}

View File

@@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallOrderExpressesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_order_expresses', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('order_id')->index();
$table->string('name', 32)->nullable();
$table->string('mobile', 32)->nullable();
$table->unsignedInteger('province_id')->index()->nullable();
$table->unsignedInteger('city_id')->index()->nullable();
$table->unsignedInteger('district_id')->index()->nullable();
$table->string('address')->nullable();
$table->unsignedBigInteger('express_id')->nullable()->comment('物流公司');
$table->string('express_no', 32)->nullable()->comment('物流单号');
$table->dateTime('deliver_at')->nullable()->comment('发货时间');
$table->dateTime('receive_at')->nullable()->comment('签收时间');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_order_expresses');
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallOrderItemsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_order_items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('order_id')->index('order_id');
$table->morphs('item');
$table->unsignedInteger('qty')->comment('数量');
$table->unsignedDecimal('price', 20, 2)->comment('单价');
$table->json('source')->nullable();
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_order_items');
}
}

View File

@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallOrdersTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->string('order_no', 32)->unique()->comment('订单编号');
$table->unsignedInteger('user_id')->index()->comment('下单用户');
$table->unsignedDecimal('amount', 20, 2)->comment('订单金额');
$table->unsignedDecimal('freight', 10, 2)->default(0)->comment('运费');
$table->timestamp('expired_at')->nullable()->comment('订单过期时间');
$table->timestamp('paid_at')->nullable()->comment('支付时间');
$table->string('state', 16)->comment('状态')->nullable();
$table->boolean('type')->comment('订单类型1 正常 2试用');
$table->string('remark')->nullable()->comment('用户留言');
$table->string('description')->nullable()->comment('备注');
$table->json('source')->comment('附加数据')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_orders');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallReasonsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_reasons', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->boolean('status')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('reasons');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallRefundExpressesTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_refund_expresses', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->unsignedBigInteger('refund_id')->index()->comment('所属退款单');
$table->string('company')->nullable()->comment('退款物流');
$table->string('number')->nullable()->comment('退款单号');
$table->timestamp('deliver_at')->nullable()->comment('寄回时间');
$table->timestamp('receive_at')->nullable()->comment('收到时间');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_refund_expresses');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallRefundItemsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_refund_items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('refund_id')->index()->comment('所属退款单');
$table->unsignedBigInteger('order_id')->comment('所属订单');
$table->unsignedBigInteger('order_item_id')->comment('详情ID');
$table->unsignedInteger('qty')->comment('数量');
$table->unsignedDecimal('price', 20, 2)->comment('单价');
$table->json('source')->nullable();
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_refund_items');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallRefundLogsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_refund_logs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('refund_id');
$table->morphs('userable');
$table->string('pictures')->nullable()->comment('图片');
$table->string('title')->nullable()->comment('原因');
$table->string('remark')->nullable()->comment('备注');
$table->string('state', 16)->comment('状态')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_tags');
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallRefundsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_refunds', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->unsignedBigInteger('order_id')->index()->comment('所属订单');
$table->string('refund_no', 32)->comment('退款单号');
$table->unsignedBigInteger('user_id')->comment('下单用户');
$table->unsignedDecimal('refund_total', 20, 2)->comment('申请退款金额');
$table->unsignedDecimal('actual_total', 20, 2)->comment('实退金额');
$table->string('state', 16)->comment('状态')->nullable();
$table->string('remark')->nullable()->comment('备注');
$table->timestamp('refunded_at')->nullable()->comment('退款时间');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_refunds');
}
}

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
use Modules\Mall\Database\Seeders\RegionSeeder;
class CreateMallRegionsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_regions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('parent_id')->index();
$table->string('name');
$table->boolean('depth');
});
Artisan::call('db:seed', [
'--class' => RegionSeeder::class,
]);
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_regions');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallShopExpressTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_shop_express', function (Blueprint $table) {
$table->unsignedBigInteger('shop_id')->index();
$table->unsignedBigInteger('express_id')->index();
$table->timestamps();
$table->primary(['shop_id', 'express_id']);
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_shop_express');
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMallShopReasonsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_shop_reasons', function (Blueprint $table) {
$table->unsignedBigInteger('shop_id')->index();
$table->unsignedBigInteger('reason_id')->index();
$table->timestamps();
$table->primary(['shop_id', 'reason_id']);
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_shop_reasons');
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallShopStaffersTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_shop_staffers', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index();
$table->unsignedBigInteger('user_id')->index();
$table->unsignedBigInteger('job_id')->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_shop_staffers');
}
}

View File

@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallShopsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_shops', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('user_id')->index()->default(0);
$table->string('name');
$table->boolean('is_self')->unsigned()->default(0)->comment('是否自营');
$table->string('description')->nullable();
$table->string('mobile')->nullable();
$table->string('cover')->nullable();
$table->boolean('status')->default(0);
$table->unsignedInteger('province_id')->index()->default(0);
$table->unsignedInteger('city_id')->index()->default(0);
$table->unsignedInteger('district_id')->index()->default(0);
$table->string('address')->nullable();
$table->json('configs')->nullable();
$table->integer('order')->default(9999)->comment('排序用的,后台控制');
$table->string('reject_reason')->comment('驳回原因')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_shops');
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMallTagsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('mall_tags', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('shop_id')->index()->comment('所属店铺');
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_tags');
}
}

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMallActivitiesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('mall_activities', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('description')->nullable();
$table->string('cover')->nullable();
$table->text('content')->nullable();
$table->boolean('status')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_activities');
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddChannelToMallOrdersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('mall_orders', function (Blueprint $table) {
$table->boolean('channel')->default(1)->comment('渠道')->after('type');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('mall_orders', function (Blueprint $table) {
});
}
}

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddTypeAndPersonToMallOrdersExpressesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('mall_order_expresses', function (Blueprint $table) {
$table->boolean('type')->default(1)->comment('方式')->after('receive_at');
$table->boolean('person')->nullable()->comment('经办人')->after('type');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('mall_orders_expresses', function (Blueprint $table) {
});
}
}

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMallVideosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('mall_videos', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('cover')->nullable();
$table->string('path');
$table->boolean('status')->default(1);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('mall_videos');
}
}

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddAreaCodeIdToMallOrdersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('mall_orders', function (Blueprint $table) {
$table->unsignedBigInteger('area_code_id')->nullable()->after('id')->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('mall_orders', function (Blueprint $table) {
});
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Modules\Mall\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class ExpressSeeder extends Seeder
{
/**
* 基础物流公司数据
*/
public function run()
{
DB::insert("INSERT INTO `mall_expresses` (`name`, `slug`, `status`, `created_at`, `updated_at`) VALUES
('圆通速递', 'yuantong', 1, now(), now()),
('韵达快递', 'yunda', 1, now(), now()),
('中通快递', 'zhongtong', 1, now(), now()),
('百世快递', 'huitongkuaidi', 1, now(), now()),
('申通快递', 'shentong', 1, now(), now()),
('顺丰速运', 'shunfeng', 1, now(), now()),
('邮政快递包裹', 'youzhengguonei', 1, now(), now()),
('京东物流', 'jd', 1, now(), now()),
('EMS', 'ems', 1, now(), now()),
('极兔速递', 'jtexpress', 1, now(), now()),
('邮政标准快递', 'youzhengbk', 1, now(), now()),
('德邦', 'debangwuliu', 1, now(), now()),
('德邦快递', 'debangkuaidi', 1, now(), now()),
('宅急送', 'zhaijisong', 1, now(), now()),
('优速快递', 'youshuwuliu', 1, now(), now()),
('圆通快运', 'yuantongkuaiyun', 1, now(), now()),
('百世快运', 'baishiwuliu', 1, now(), now()),
('顺丰快运', 'shunfengkuaiyun', 1, now(), now()),
('众邮快递', 'zhongyouex', 1, now(), now()),
('中通快运', 'zhongtongkuaiyun', 1, now(), now()),
('UPS', 'ups', 1, now(), now()),
('安能快运', 'annengwuliu', 1, now(), now()),
('韵达快运', 'yundakuaiyun', 1, now(), now()),
('中通国际', 'zhongtongguoji', 1, now(), now()),
('D速快递', 'dsukuaidi', 1, now(), now()),
('特急送', 'lntjs', 1, now(), now()),
('九曳供应链', 'jiuyescm', 1, now(), now()),
('丹鸟', 'danniao', 1, now(), now())");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单取消事件
*/
class OrderCanceled extends OrderEvent
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单关闭事件
*/
class OrderClosed extends OrderEvent
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单完结事件
*/
class OrderCompleted extends OrderEvent
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单创建完成事件
*/
class OrderCreated extends OrderEvent
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单已发货完成
*/
class OrderDelivered extends OrderEvent
{
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Order;
class OrderEvent
{
use SerializesModels;
/**
* @var \Modules\Mall\Models\Order
*/
public Order $order;
/**
* 创建事件实例,传播订单模型
* @param \Modules\Mall\Models\Order $order
*/
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单支付完成事件
*/
class OrderPaid extends OrderEvent
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Modules\Mall\Events;
/**
* 订单签收完成
*/
class OrderSigned extends OrderEvent
{
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Refund;
/**
* 同意退款
*/
class RefundAgreed
{
use SerializesModels;
public Refund $refund;
public function __construct(Refund $refund)
{
$this->refund = $refund;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\Refund;
/**
* 订单申请退款
*/
class RefundApplied
{
use SerializesModels;
public Order $order;
public Refund $refund;
public function __construct(Order $order, Refund $refund)
{
$this->order = $order;
$this->refund = $refund;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Refund;
/**
* 退款完成事件
*/
class RefundCompleted
{
use SerializesModels;
public Refund $refund;
public function __construct(Refund $refund)
{
$this->refund = $refund;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Refund;
/**
* 退款中事件,可选择在此处切入退款功能
*/
class RefundProcessed
{
use SerializesModels;
public Refund $refund;
public function __construct(Refund $refund)
{
$this->refund = $refund;
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Modules\Mall\Events;
use Illuminate\Queue\SerializesModels;
use Modules\Mall\Models\Refund;
/**
* 拒绝退款
*/
class RefundRefused
{
use SerializesModels;
public Refund $refund;
public function __construct(Refund $refund)
{
$this->refund = $refund;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Modules\Mall\Facades;
use Illuminate\Contracts\Support\Arrayable;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\GoodsSku;
class Item implements Arrayable
{
public GoodsSku $model;
public int $sku_id;
public int $qty;
public float $price;
public int $shop_id;
public array $source;
public string $orderby;
public $address;
/**
* Item constructor.
*
* @param \Modules\Mall\Models\GoodsSku $item
* @param Address|null $address
* @param int $qty
*/
public function __construct(GoodsSku $item, $address = null, int $qty = 1)
{
$this->model = $item;
$this->sku_id = $item->id;
$this->shop_id = $item->getShopIdAttribute();
$this->orderby = $item->getShopTypeAttribute().'_'.$item->getShopIdAttribute();
$this->qty = $qty;
$this->price = $item->getItemPrice();
$this->address = $address;
}
/**
* Notes: 获取条目总价
*
* @Author: <C.Jason>
* @Date : 2019/11/21 11:03 上午
* @return float
*/
public function total(): float
{
return bcmul($this->price, $this->qty, 2);
}
/**
* Notes: 获取总总量
*
* @Author: 玄尘
* @Date : 2021/5/14 13:20
* @return float
*/
public function weight(): float
{
return bcmul($this->model->getGoodsWeight(), $this->qty, 2);
}
/**
* Notes: 获取运费
*
* @Author: 玄尘
* @Date : 2021/5/14 13:34
*/
public function freight(): int
{
return 0;
}
/**
* Notes: 获取shop
*
* @Author: 玄尘
* @Date : 2021/5/14 14:15
*/
public function shop(): array
{
return [
'shop_id' => $this->model->getShopIdAttribute(),
'name' => $this->model->getShopNameAttribute(),
];
}
/**
* Notes: 下单存入order_item表
*
* @Author: 玄尘
* @Date : 2021/5/18 8:37
*/
public function getSource(): array
{
return $this->model->getSource();
}
/**
* Notes: 转换成数组
*
* @Author: <C.Jason>
* @Date : 2020/9/23 4:00 下午
* @return array
*/
public function toArray(): array
{
return [
'goods_id' => $this->model->goods_id,
'goods_sku_id' => $this->sku_id,
'title' => $this->model->getGoodsName(),
'value' => $this->model->getItemValue(),
'cover' => $this->model->cover_url,
'price' => $this->price,
'qty' => $this->qty,
'score' => $this->model->score,
];
}
}

View File

@@ -0,0 +1,408 @@
<?php
namespace Modules\Mall\Facades;
use App\Models\AreaCode;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Modules\Mall\Events\OrderCreated;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\Order as OrderModel;
use Modules\User\Models\User;
class Order
{
/**
* @var int
*/
protected int $user_id;
/**
* @var float
*/
protected float $total;
/**
* @var Address
*/
protected $address;
/**
* @var array
*/
protected Collection $items;
/**
* @var string
*/
protected string $remark;
/**
* @var int
*/
protected int $type;
protected int $channel = 1;
/**
* @var array
*/
protected array $source;
protected int $area_code_id;
/**
* Notes: 设置当前用户
*
* @Author: 玄尘
* @Date : 2021/5/19 10:40
* @param User $user
* @return $this
*/
public function user(User $user): Order
{
$this->user_id = $user->getAuthIdentifier();;
return $this;
}
/**
* Notes: 提货码
*
* @Author: 玄尘
* @Date: 2023/1/12 14:19
* @param AreaCode $areaCode
* @return $this
*/
public function areaCode(AreaCode $areaCode): Order
{
$this->area_code_id = $areaCode->getKey();
return $this;
}
/**
* Notes: 设置类型
*
* @Author: 玄尘
* @Date : 2021/10/18 10:03
* @param $type
* @return $this
*/
public function type($type): Order
{
$this->type = $type;
return $this;
}
/**
* Notes: 订单渠道
*
* @Author: 玄尘
* @Date: 2022/9/8 15:51
*/
public function channel($channel): self
{
$this->channel = $channel;
return $this;
}
/**
* Notes: 设置订单备注信息
*
* @Author: 玄尘
* @Date : 2021/5/19 10:40
* @param string|null $remark
* @return $this
*/
public function remark(?string $remark = ''): Order
{
$this->remark = $remark;
return $this;
}
/**
* Notes: 附加数据
*
* @Author: 玄尘
* @Date : 2021/5/19 10:40
* @param array $source
* @return $this
*/
public function source(array $source): Order
{
$this->source = $source;
return $this;
}
/**
* Notes: 设置商品数据
*
* @Author: 玄尘
* @Date : 2021/5/14 13:58
* @param $items
* @return $this
*/
public function items($items): Order
{
$this->items = new Collection($items);
return $this;
}
/**
* Notes: 设置订单收货地址
*
* @Author: 玄尘
* @Date : 2021/5/19 10:41
* @param Address $address
* @return $this
*/
public function address(Address $address): Order
{
$this->address = $address;
return $this;
}
/**
* Notes: 创建订单
*
* @Author: 玄尘
* @Date : 2021/5/19 10:42
* @return Collection
* @throws \Exception
*/
public function create(): Collection
{
if ($this->items->isEmpty()) {
throw new Exception('无法创建无内容的订单');
}
if (! is_numeric($this->user_id)) {
throw new Exception('必须先设置订单用户');
}
$splits = $this->splitOrderByShop();
DB::beginTransaction();
// try {
$orders = [];
foreach ($splits as $split) {
$orders[] = $this->createOne($split['items']);
}
DB::commit();
$result = new Collection($orders);
$result->total = $this->total();
return $result;
// } catch (Exception $exception) {
// DB::rollBack();
// throw new Exception($exception->getMessage());
// }
}
/**
* Notes: 按照商户,对订单进行分组
*
* @Author: 玄尘
* @Date : 2021/5/19 10:43
* @return mixed
* @throws \Exception
*/
public function splitOrderByShop()
{
if (empty($this->items)) {
throw new Exception('无法创建无内容的订单');
}
return $this->items->groupBy('orderby')->map(function ($items, $key) {
/**
* 计算分订单总价格
*/
$items->amount = $items->reduce(function ($total, $item) {
return $total + $item->total();
});
/**
* 计算分订单总数量
*/
$items->qty = $items->reduce(function ($qty, $item) {
return $qty + $item->qty;
});
/**
* 计算分订单运费
*/
$items->freight = $items->reduce(function ($total, $item) {
return $total + $item->freight();
});
/**
* 回传店铺ID
*/
$items->shop_id = $items->first()->shop_id;
return [
'shop' => $items->first()->shop(),
'items' => $items,
];
});
}
/**
* Notes: 创建一条订单记录
*
* @Author: 玄尘
* @Date : 2021/5/21 10:24
* @param Collection $split
* @return Builder|Model
* @throws \Exception
*/
protected function createOne(Collection $split)
{
/**
* 创建主订单
*/
$order = OrderModel::query()
->create([
'shop_id' => $split->shop_id,
'area_code_id' => $this->area_code_id,
'user_id' => $this->user_id,
'amount' => $split->amount,
'type' => $this->type,
'channel' => $this->channel,
'freight' => $split->freight,
'remark' => $this->remark,
'source' => $this->source ?? null,
]);
/**
* 创建订单子条目
*/
foreach ($split as $item) {
// 库存校验
if ($item->qty > $item->model->getGoodsStock()) {
throw new Exception(sprintf('[%s]库存不足', $item->model->getGoodsName()));
}
$order->items()
->create([
'user_id' => $order->user_id ?? 0,
'item_type' => get_class($item->model),
'item_id' => $item->sku_id,
'qty' => $item->qty,
'price' => $item->price,
'source' => $item->getSource(),
]);
if ($item->model->goods->deduct_stock_type == Goods::DEDUCT_STOCK_ORDER) {
// 扣减库存
$item->model->deductStock($item->qty, $order->user_id);
}
}
/**
* 自动更新地址
*/
if ($this->address && $this->address instanceof Address) {
$this->setOrderAddress($order, $this->address);
}
/**
* 订单自动审核
*/
if (config('order.auto_audit')) {
$order->audit();
}
event(new OrderCreated($order));
return $order;
}
/**
* Notes: 计算订单总价格
*
* @Author: 玄尘
* @Date : 2021/5/21 10:26
* @return float|int|string
*/
public function total()
{
$this->total = 0;
foreach ($this->items as $item) {
$this->total = bcadd($this->total, $item->total(), 2);
}
return $this->total;
}
/**
* Notes: 设置订单收货地址
*
* @Author: 玄尘
* @Date : 2021/5/21 10:26
* @param $order
* @param $address
*/
protected function setOrderAddress($order, $address)
{
$order->express()
->create([
'name' => $address->name,
'mobile' => $address->mobile,
'province_id' => $address->province_id,
'city_id' => $address->city_id,
'district_id' => $address->district_id,
'address' => $address->address,
]);
}
/**
* Notes: 魔术方法,获取一些参数
*
* @Author: 玄尘
* @Date : 2021/5/21 10:27
* @param $attr
* @return float|int|string|null
*/
public function __get($attr)
{
switch ($attr) {
case 'total':
return $this->total();
}
return null;
}
/**
* Notes: 获取订单详情
*
* @Author: <C.Jason>
* @Date : 2019/11/22 1:51 下午
* @param string $order_no
* @return mixed
*/
public function get(string $order_no)
{
return OrderModel::where('user_id', $this->user_id)
->with(['user', 'items', 'express', 'seller', 'logs'])
->where('order_no', $order_no)
->first();
}
}

View File

@@ -0,0 +1,250 @@
<?php
namespace Modules\Mall\Facades;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Facades\DB;
use Modules\Mall\Events\RefundApplied;
use Modules\Mall\Models\Order as OrderModel;
use Modules\Mall\Models\Refund as RefundModel;
class Refund
{
protected $user;
/**
* @var string
*/
protected string $remark;
/**
* @var array
*/
protected array $logs;
protected $refund;
protected $order;
protected $items;
protected $total;
/**
* @throws \Exception
*/
public function user($user): Refund
{
if ($user instanceof Authenticatable) {
$this->user = $user;
} else {
throw new Exception('非法用户');
}
return $this;
}
/**
* Notes: 订单
*
* @Author: 玄尘
* @Date : 2021/5/17 8:46
* @param OrderModel $order
* @return $this
*/
public function order(OrderModel $order): Refund
{
$this->order = $order;
return $this;
}
/**
* Notes:商品
*
* @Author: 玄尘
* @Date : 2021/5/17 8:46
* @param array $items
* @return Refund
*/
public function items(array $items): Refund
{
$this->items = $items;
return $this;
}
/**
* Notes: 退款金额
*
* @Author: 玄尘
* @Date : 2021/5/17 8:48
* @param float $total
* @return Refund
*/
public function total(float $total): Refund
{
$this->total = $total;
return $this;
}
/**
* Notes: 设置订单备注信息
*
* @Author: 玄尘
* @Date : 2021/5/21 10:31
* @param string $remark
* @return $this
*/
public function remark(string $remark): Refund
{
$this->remark = $remark;
return $this;
}
/**
* Notes: 退款日志
*
* @Author: 玄尘
* @Date : 2021/5/17 11:37
* @param $logs
* @return $this
*/
public function logs($logs): Refund
{
if (! isset($logs['remark']) && ! empty($this->remark)) {
$logs['remark'] = $this->remark;
}
$this->logs = $logs;
return $this;
}
/**
* Notes: 设置退款商品
*
* @Author: 玄尘
* @Date : 2021/5/17 8:51
*/
public function setItems(): Refund
{
$this->items = $this->order->items->map(function ($item) {
return [
'order_item_id' => $item->id,
'qty' => $item->qty,
];
});
return $this;
}
/**
* Notes: 创建退款单
*
* @Author: 玄尘
* @Date : 2021/5/17 8:49
* @return mixed
* @throws \Exception
*/
public function create()
{
if (! $this->order) {
throw new Exception('未设置订单');
}
if (empty($this->items)) {
$this->setItems();
}
if (! $this->order->can('refund')) {
throw new Exception('订单状态不可退款');
}
$maxAmount = 0;
$refundItems = [];
//判断最大可退数量
foreach ($this->items as $item) {
$orderItem = $this->order->items()->find($item['order_item_id']);
if (! $orderItem) {
throw new Exception('未找到可退商品');
}
if (! $orderItem->canRefund()) {
throw new \Exception('退款/货失败,此商品不可退款/货');
}
if ($item['qty'] <= 0) {
throw new Exception('【'.$orderItem->source['goods_name'].'】退货数量必须大于0');
}
if ($item['qty'] > $orderItem->qty) {
throw new Exception('【'.$orderItem->source['goods_name'].'】超过最大可退数量');
}
$maxAmount += $orderItem->price * $item['qty'];
$refundItems[] = new RefundItem($orderItem, $item['qty']);
}
// 自动计算退款金额
if (is_null($this->total)) {
$this->total = $maxAmount;
} elseif ($this->total > $maxAmount) {
throw new Exception('超过最大可退金额');
}
DB::transaction(function () use ($refundItems) {
$this->refund = $this->order->refunds()
->create([
'refund_total' => $this->total,
'actual_total' => 0,
'user_id' => $this->user->id,
'shop_id' => $this->order->shop_id,
'state' => RefundModel::REFUND_APPLY,
'remark' => $this->remark,
]);
foreach ($refundItems as $item) {
$this->refund->items()->create($item->toArray());
}
if ($this->logs) {
$this->createLog();
}
event(new RefundApplied($this->order, $this->refund));
});
return $this->refund;
}
/**
* Notes: 添加日志
*
* @Author: 玄尘
* @Date : 2020/12/11 11:43
*/
public function createLog()
{
$logs = $this->logs;
if ($this->user) {
$logs['userable_type'] = get_class($this->user);
$logs['userable_id'] = $this->user->id;
$logs['state'] = $this->refund->state;
}
$this->refund->logs()->create($logs);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Modules\Mall\Facades;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
class RefundItem implements Arrayable, Jsonable
{
public int $qty;
public float $price;
public $order_item_id;
public $order_id;
public $source;
/**
* RefundItem constructor.
* @param $item
* @param int $qty
*/
public function __construct($item, int $qty = 1)
{
$this->qty = $qty;
$this->price = $item->price;
$this->order_id = $item->order_id;
$this->order_item_id = $item->id;
$this->source = $item->source;
}
/**
* Notes: 获取条目总价
* @Author: 玄尘
* @Date : 2020/12/10 9:43
* @return float
*/
public function total(): float
{
return bcmul($this->price, $this->qty, 2);
}
/**
* Notes: 返回数组
* @Author: 玄尘
* @Date : 2021/5/21 10:40
* @return array
*/
public function toArray(): array
{
return [
'qty' => $this->qty,
'price' => $this->price,
'order_id' => $this->order_id,
'order_item_id' => $this->order_item_id,
'source' => $this->source,
];
}
public function toJson($options = 0)
{
return json_encode($this->toArray(), $options);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Modules\Mall\Facades;
use Illuminate\Support\Facades\Facade;
class Workflow extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'workflow';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Modules\Mall\Models\Order;
class Audit extends RowAction
{
public $name = '通过审核';
public function handle(Order $order): Response
{
try {
$order->pay();
return $this->response()->success('审核通过')->refresh();
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Illuminate\Http\Request;
use Modules\Mall\Models\Express;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderExpress;
class Delivered extends RowAction
{
public $name = '商品发货';
public function handle(Order $order, Request $request): Response
{
if ($request->type == OrderExpress::TYPE_EXPRESS && (empty($request->express_id) || empty($request->express_no))) {
return $this->response()->error('缺少快递公司或快递单号');
}
$result = $order->deliver($request->express_id, $request->express_no, $request->type, $request->person);
if ($result === true) {
return $this->response()->success('发货成功')->refresh();
} else {
return $this->response()->error('失败');
}
}
public function form()
{
$order = Order::find($this->getKey());
$expresses = Express::query()->pluck('name', 'id');
$this->select('type', '方式')
->options([
OrderExpress::TYPE_LOGISTICS => '物流',
])
->required();
$this->select('express_id', '物流')
->options($expresses);
$this->text('express_no', '物流单号');
$this->text('person', '经办人');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Modules\Mall\Models\Order;
class Pay extends RowAction
{
public $name = '支付测试-正式版本移除';
public function handle(Order $order): Response
{
try {
$order->pay();
return $this->response()->success('支付状态调整成功')->refresh();
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Encore\Admin\Facades\Admin;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
class RefundAudit extends RowAction
{
public $name = '审核';
public function handle(Model $model, Request $request): Response
{
$admin = Admin::user();
$state = $request->state;
$remark = $request->remark;
$res = false;
if (!$model->can('agree')) {
return $this->response()->error('不可操作')->refresh();
}
if ($state == 'agree') {
$res = $model->setOperator($admin)->agree($remark);
}
if ($state == 'refuse') {
$res = $model->setOperator($admin)->refuse($remark);
}
if ($res === true) {
return $this->response()->success('操作成功')->refresh();
}
return $this->response()->error('操作失败')->refresh();
}
public function form(Model $model)
{
$this->select('state', '状态')
->options([
'agree' => '通过',
'refuse' => '驳回',
])
->required();
$this->text('remark', '说明')
->default($express->number ?? '')
->required();
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Encore\Admin\Facades\Admin;
use Modules\Mall\Models\Refund;
class RefundReturns extends RowAction
{
public $name = '退款';
public function handle(Refund $refund): Response
{
try {
$admin = Admin::user();
if (!$refund->can('completed')) {
return $this->response()->error('不可操作')->refresh();
}
$res = $refund->setOperator($admin)->returns();
if ($res === true) {
$refund->setOperator($admin)->complete();//设置完成
return $this->response()->success('操作成功')->refresh();
}
return $this->response()->error('操作失败')->refresh();
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function dialog()
{
$this->confirm('您确定要打款吗');
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Order;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Encore\Admin\Facades\Admin;
use Illuminate\Database\Eloquent\Model;
class RefundSign extends RowAction
{
public $name = '签收';
public function handle(Model $model): Response
{
try {
$admin = Admin::user();
if (!$model->can('sign')) {
return $this->response()->error('不可操作')->refresh();
}
$res = $model->setOperator($admin)->receive();
if ($res === true) {
return $this->response()->success('操作成功')->refresh();
}
return $this->response()->error('操作失败')->refresh();
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function dialog()
{
$this->confirm('您确定已经收到货了吗');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Shop;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Modules\Mall\Models\Shop;
class Close extends RowAction
{
public $name = '关闭店铺';
public function handle(Shop $shop): Response
{
try {
if ($shop->close()) {
return $this->response()->success('店铺关闭成功')->refresh();
} else {
return $this->response()->error('店铺关闭失败')->refresh();
}
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function dialog()
{
$this->confirm('确定关闭店铺吗?');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Shop;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Modules\Mall\Models\Shop;
class Open extends RowAction
{
public $name = '开启店铺';
public function handle(Shop $shop): Response
{
try {
if ($shop->open()) {
return $this->response()->success('店铺开启成功')->refresh();
} else {
return $this->response()->error('店铺开启失败')->refresh();
}
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function dialog()
{
$this->confirm('确定开启店铺吗?');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Shop;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Modules\Mall\Models\Shop;
class Pass extends RowAction
{
public $name = '审核通过';
public function handle(Shop $shop): Response
{
try {
if ($shop->pass()) {
return $this->response()->success('审核通过成功')->refresh();
} else {
return $this->response()->error('审核通过失败')->refresh();
}
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function dialog()
{
$this->confirm('确定审核通过?');
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Action\Shop;
use Encore\Admin\Actions\Response;
use Encore\Admin\Actions\RowAction;
use Illuminate\Http\Request;
use Modules\Mall\Models\Shop;
class Reject extends RowAction
{
public $name = '驳回申请';
public function handle(Shop $shop, Request $request): Response
{
try {
if ($shop->reject($request->reject_reason)) {
return $this->response()->success('驳回申请成功')->refresh();
} else {
return $this->response()->error('驳回申请失败')->refresh();
}
} catch (\Exception $exception) {
return $this->response()->error($exception->getMessage())->refresh();
}
}
public function form()
{
$this->textarea('reject_reason', '驳回原因')->rules('required');
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Modules\Mall\Models\Activity;
class ActivityController extends AdminController
{
use WithUploads;
public $title = "活动列表";
protected function grid(): Grid
{
$grid = new Grid(new Activity());
$grid->disableFilter();
$grid->column('id', '#ID#');
$grid->column('title', '活动名称');
$grid->column('description', '简介');
$grid->column('status', '状态')->switch();
$grid->column('created_at', '创建时间');
return $grid;
}
protected function form(): Form
{
$form = new Form(new Activity());
$form->text('title', '活动名称');
$this->cover($form);
$form->textarea('description', '简介');
$form->ueditor('content', '详情')->required();
$form->switch('status', '状态')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Linker\Traits\WithLinker;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Banner;
use Modules\Mall\Models\Region;
use Modules\User\Models\User;
class AddressController extends AdminController
{
use WithUploads,
WithLinker;
public $title = "收货地址";
protected function grid(): Grid
{
$grid = new Grid(new Address());
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('user_id', '所属用户')->select()->ajax(route('admin.user.users.ajax'));
});
});
$grid->model()->with(['user']);
$grid->column('id', '#ID#');
$grid->column('user.username', '所属用户');
$grid->column('name', '姓名');
$grid->column('mobile', '手机号');
$grid->column('province.name', '省');
$grid->column('city.name', '市');
$grid->column('district.name', '区');
$grid->column('address', '详细地址');
$grid->column('is_default', '默认?')->bool();
$grid->column('created_at', '创建时间');
return $grid;
}
protected function form(): Form
{
$form = new Form(new Address);
$form->select('user_id', '所属用户')
->options(function ($userId) {
$user = User::find($userId);
if ($user) {
return [$user->id => $user->username.' ['.$user->info->nickname.']'];
}
})
->value(request()->user_id ?? '')
->ajax(route('admin.user.users.ajax'))
->required();
$form->text('name', '姓名');
$form->text('mobile', '手机号');
$form->select('province_id', '省份')
->options(Region::where('parent_id', 1)->pluck('name', 'id'))
->load('city_id', route('admin.mall.regions.region'))
->required();
$form->select('city_id', '城市')
->options(function ($option) {
$parent = Region::where('id', $option)->value('parent_id');
return Region::where(['parent_id' => $parent])->pluck('name', 'id');
})
->load('district_id', route('admin.mall.regions.region'))
->required();
$form->select('district_id', '区/县')
->options(function ($option) {
$parent = Region::where('id', $option)->value('parent_id');
return Region::where(['parent_id' => $parent])->pluck('name', 'id');
})
->required();
$form->text('address', '详细地址')
->required();
$form->switch('is_default', '默认?')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Models\Activity;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSku;
class AjaxController
{
/**
* Notes: 获取用户收货地址
*
* @Author: 玄尘
* @Date: 2022/7/29 13:33
* @param Request $request
* @return mixed
*/
public function address(Request $request)
{
$user_id = $request->get('q');
return Address::ByUserId($user_id)
->oldest('is_default')
->get()
->map(function ($address) {
return [
'id' => $address->id,
'text' => $address->getFullAddress(),
];
});
}
/**
* Notes: 获取商品
*
* @Author: 玄尘
* @Date: 2022/7/29 13:46
* @param Request $request
*/
public function goods(Request $request)
{
$address_id = $request->q;
if (!$address_id){
return [];
}
$address = Address::find($address_id);
return GoodsSku::query()
->whereHas('goods', function ($q) {
$q->where('channel', Goods::CHANNEL_FREE)->where('status', Goods::STATUS_UP);
})
->get()
->map(function ($sku) use ($address) {
$stockData = $address->user->getStockData();
return [
'id' => $sku->id,
'text' => $sku->goods->name."(库存:{$stockData['residue']})",
];
});
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Modules\Linker\Traits\WithLinker;
use Modules\Mall\Models\Banner;
class BannerController extends AdminController
{
use WithUploads,
WithShop,
WithLinker;
public $title = "轮播图管理";
protected function grid(): Grid
{
$grid = new Grid(new Banner);
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('title', '轮播标题');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('status', '状态')->radio([
0 => '禁用',
1 => '正常',
]);
});
});
$grid->model()->with(['shop']);
$grid->column('id', '#ID#');
$grid->column('shop.name', '所属店铺');
$grid->column('cover', '封面图片')->image('', 100, 100);
$grid->column('展示位置')->display(function () {
$data = [];
foreach ($this->position as $position) {
$data[] = $this->position_map[$position];
}
return $data;
})->label();
$grid->column('title', '轮播标题');
$grid->column('status', '状态')->bool();
$grid->column('created_at', '创建时间');
return $grid;
}
protected function form(): Form
{
$form = new Form((new Banner)->disableModelCaching());
$this->shop($form)->help('不选择,默认为平台轮播');
$form->text('title', '标题')
->required();
$form->multipleSelect('position', '展示位置')
->options($form->model()->position_map)
->required();
$this->cover($form);
$this->withUrl($form);
$form->switch('status', '显示')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Models\Brand;
class BrandController extends AdminController
{
use WithUploads,
WithShop;
protected $title = '品牌管理';
public function grid(): Grid
{
$grid = new Grid(new Brand());
$grid->model()->with(['shop']);
$grid->column('cover', 'LOGO')->image('', 80);
$grid->column('shop.name', '所属店铺');
$grid->column('name', '品牌名称');
$grid->column('status', '状态')->bool();
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Brand());
$this->shop($form)->required();
$form->text('name', '品牌名称')
->required();
$this->cover($form);
$form->textarea('description', '品牌简介');
$form->switch('status', '状态')->default(1);
return $form;
}
public function ajax(Request $request)
{
$key = $request->q;
return Brand::where('name', 'like', "%$key%")->paginate(null, ['id', 'name as text']);
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Models\Category;
class CategoryController extends AdminController
{
use WithShop,
WithUploads;
protected $title = "分类管理";
protected function grid(): Grid
{
$grid = new Grid(new Category());
$parentId = request()->get('parent_id');
$grid->model()
->with(['shop'])
->withCount('goods')
->when($parentId, function ($query) use ($parentId) {
$query->where('parent_id', $parentId);
});
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('trashed', '回收站')->onlyTrashed();
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '分类名称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('slug', '分类简称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('status', '状态')->select([
0 => '禁用',
1 => '正常',
]);
});
});
$grid->column('shop.name', '所属店铺');
$grid->column('parent.name', '上级分类')
->link(function () {
return route('admin.mall.categories.index', ['parent_id' => $this->parent_id]);
}, '_self');;
$grid->column('name', '分类名称')
->link(function () {
return route('admin.mall.categories.index', ['parent_id' => $this->id]);
}, '_self');
$grid->column('slug', '分类简称');
$grid->column('goods_count', '分类商品');
$grid->column('order', '排序');
$grid->column('status', '状态')->bool();
return $grid;
}
/**
* Notes : 编辑表单
* @Date : 2021/4/25 1:41 下午
* @Author : < Jason.C >
* @return \Encore\Admin\Form
*/
protected function form(): Form
{
$form = new Form(new Category);
$this->shop($form)->required();
$form->select('parent_id', '上级分类')
->options(Category::selectOptions(function ($model) {
return $model->where('status', 1);
}, '一级分类'))
->required();
$form->text('name', '分类名称')
->required()
->rules('required');
$form->text('slug', '分类简称');
$form->textarea('description', '分类简介');
$this->cover($form, 'cover', '分类图片');
$form->number('order', '排序')->default(0);
$form->switch('status', '显示')->default(1);
return $form;
}
public function ajax(Request $request)
{
$key = $request->q;
return Category::where('name', 'like', "%$key%")->paginate(null, ['id', 'name as text']);
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Layout\Column;
use Encore\Admin\Layout\Content;
use Encore\Admin\Layout\Row;
use Encore\Admin\Widgets\InfoBox;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\Refund;
use Modules\Mall\Models\Shop;
class DashboardController extends Controller
{
public function index(Content $content): Content
{
return $content
->header('商城看板')
->description('商城数据概览')
->row(function (Row $row) {
$row->column(3, function (Column $column) {
$column->append(new InfoBox('店铺数量',
'anchor',
'blue',
route('admin.mall.shops.index'),
Shop::count()
)
);
});
$row->column(3, function (Column $column) {
$column->append(new InfoBox('商品数量',
'anchor',
'blue',
route('admin.mall.goods.index'),
Goods::count()
)
);
});
$row->column(3, function (Column $column) {
$column->append(new InfoBox('订单总数',
'anchor',
'blue',
route('admin.mall.orders.index'),
Order::count()
)
);
});
$row->column(3, function (Column $column) {
$column->append(new InfoBox('退款订单',
'anchor',
'blue',
route('admin.mall.refunds.index'),
Refund::count()
)
);
});
});
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Models\Delivery;
class DeliveryController extends AdminController
{
use WithShop;
protected $title = '运费模板';
public function grid(): Grid
{
$grid = new Grid(new Delivery());
$grid->model()
->with(['shop'])
->withCount('rules');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('trashed', '回收站')->onlyTrashed();
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('shop.name', '所属店铺');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '模板名称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('name', '计费方式')->select(Delivery::TYPE_MAP);
});
});
$grid->column('shop.name', '所属店铺');
$grid->column('name', '模板名称');
$grid->column('type', '计费方式')->using(Delivery::TYPE_MAP);
$grid->column('rules_count', '模板规则')->link(function () {
return route('admin.mall.deliveries.rules.index', $this);
}, '_self');
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Delivery());
$this->shop($form)->required();
$form->text('name', '模板名称')->required();
$form->radioButton('type', '计费方式')
->options(Delivery::TYPE_MAP)
->default(Delivery::TYPE_BY_COUNT)
->required();
return $form;
}
/**
* Notes : 选择运费模板
* @Date : 2021/5/11 11:36 上午
* @Author : < Jason.C >
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function ajax(Request $request)
{
$key = $request->get('q');
return Delivery::where('name', 'like', "%$key%")->paginate(null, ['id', 'name as text']);
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Modules\Mall\Models\Delivery;
use Modules\Mall\Models\DeliveryRule;
use Modules\Mall\Models\Region;
class DeliveryRuleController extends AdminController
{
protected $title = '模板规则';
public function index(Content $content): Content
{
$delivery = Delivery::find(request()->delivery);
return $content
->header($delivery->name)
->description('模板规则')
->body($this->grid($delivery));
}
public function grid($delivery): Grid
{
$grid = new Grid(new DeliveryRule());
$grid->model()->where('delivery_id', $delivery->id);
if ($delivery->type === Delivery::TYPE_BY_COUNT) {
$firstTitle = '首件(个)';
$additionalTitle = '续件';
} else {
$firstTitle = '首重(Kg)';
$additionalTitle = '续重';
}
$grid->column('regions', '配送区域')
->display(function () {
return Region::find($this->regions);
})
->pluck('name')
->label();
$grid->column('first', $firstTitle);
$grid->column('first_fee', '运费(元)');
$grid->column('additional', $additionalTitle);
$grid->column('additional_fee', '续费(元)');
return $grid;
}
public function edit($id, Content $content): Content
{
return $content
->title($this->title())
->description($this->description['edit'] ?? trans('admin.edit'))
->body($this->form()->edit(request()->rule));
}
public function update($id)
{
return $this->form()->update(request()->rule);
}
public function form(): Form
{
$delivery = Delivery::find(request()->delivery);
if ($delivery->type === Delivery::TYPE_BY_COUNT) {
$firstTitle = '首件(个)';
$additionalTitle = '续件';
} else {
$firstTitle = '首重(Kg)';
$additionalTitle = '续重';
}
$form = new Form(new DeliveryRule());
$form->multipleSelect('regions', '配送区域')
->options(function ($regions) {
if ($regions) {
return Region::find($regions)->pluck('name', 'id');
}
})
->ajax(route('admin.mall.regions.ajax'));
$form->text('first', $firstTitle)
->required();
$form->currency('first_fee')
->symbol('¥')
->required();
$form->text('additional', $additionalTitle)
->required();
$form->currency('additional_fee')
->required()
->symbol('¥');
$form->hidden('delivery_id')->value($delivery->id);
return $form;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Modules\Mall\Models\Express;
class ExpressController extends AdminController
{
use WithUploads;
protected $title = '物流管理';
protected function grid(): Grid
{
$grid = new Grid(new Express());
$grid->model()->withCount('shops');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('trashed', '回收站')->onlyTrashed();
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '物流名称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('slug', '物流简称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('status', '状态')->select([
0 => '禁用',
1 => '正常',
]);
});
});
$grid->column('id');
$grid->column('cover', 'LOGO')->image('', 60);
$grid->column('name', '物流名称');
$grid->column('slug', '物流简称');
$grid->column('description', '物流简介');
$grid->column('status', '状态')->bool();
// $grid->column('shops_count', '使用商户');
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Express());
$form->text('name', '物流名称')
->required();
$form->text('slug', '物流简称')
->required();
$form->textarea('description', '物流简介');
$this->cover($form);
$form->switch('status', '状态')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,228 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Support\Arr;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Str;
use Modules\Mall\Models\Brand;
use Modules\Mall\Models\Category;
use Modules\Mall\Models\Delivery;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\Shop;
use Modules\Mall\Models\Tag;
class GoodsController extends AdminController
{
use WithUploads,
WithShop;
protected $title = '商品管理';
protected function grid(): Grid
{
$grid = new Grid(new Goods);
$grid->quickSearch('id')->placeholder('商品编号快速搜索');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('trashed', '回收站')->onlyTrashed();
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '商品名称');
$filter->equal('channel', '类型')->select(Goods::Channels);
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('shop_id', '所属店铺')->select(function ($shopId) {
if ($shopId) {
return Shop::where('id', $shopId)->pluck('name', 'id');
}
})->ajax(route('admin.mall.shops.ajax'));
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('category_id', '分类')->select(Category::shown()->pluck('name', 'id'));
});
});
$grid->model()
->with(['category', 'shop', 'delivery'])
->withCount(['specs', 'skus'])
->orderByDesc('updated_at');
$grid->column('id', '#ID#');
$grid->column('brand.name', '品牌名称');
$grid->column('category.name', '商品分类');
// $grid->column('tags', '标签')
// ->pluck('name')
// ->label();
$grid->column('shop.name', '所属店铺');
$grid->column('name', '商品名称');
$grid->column('type', '规格类型')->using(Goods::TYPE_MAP);
$grid->column('sales', '销量');
$grid->column('original_price', '原价');
$grid->column('channel', '类型')->using(Goods::Channels)->label();
$grid->column('规格属性')
->display(function () {
if ($this->type === Goods::TYPE_MULTIPLE) {
return $this->specs_count;
}
})
->link(function () {
return route('admin.mall.goods.specs.index', $this);
}, '_self');
$grid->column('价格条目')
->display(function () {
return $this->skus_count;
})
->link(function () {
return route('admin.mall.goods.skus.index', $this);
}, '_self');
$grid->column('delivery.name', '运费模板');
$grid->column('status', '状态')->switch([
'on' => ['value' => 1, 'text' => '上架', 'color' => 'primary'],
'off' => ['value' => 3, 'text' => '下架', 'color' => 'default'],
]);
// $grid->column('status', '状态')->display(function () {
// return $this->status_text;
// });
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Goods());
$form->text('name', '商品名称')->required();
$form->textarea('description', '商品简介')->required();
$this->shop($form)->required();
$form->select('category_id', '商品分类')
->options(function ($categoryId) {
$category = Category::find($categoryId);
if ($category) {
return [$category->id => $category->name];
}
})
->ajax(route('admin.mall.categories.ajax'))
->required();
$form->select('brand_id', '所属品牌')
->options(function ($brandId) {
$brand = Brand::find($brandId);
if ($brand) {
return [$brand->id => $brand->name];
}
})
->ajax(route('admin.mall.brands.ajax'))
->required();
// $form->multipleSelect('tags', '商品标签')
// ->options(function ($tagIds) {
// if ($tagIds) {
// return Tag::find($tagIds)->pluck('name', 'id');
// }
// })
// ->ajax(route('admin.mall.tags.ajax'));
$this->cover($form);
$this->pictures($form);
$states = [
'on' => ['value' => 1, 'text' => '是', 'color' => 'success'],
'off' => ['value' => 0, 'text' => '否', 'color' => 'danger'],
];
$form->switch('is_post_sale', '是否允许售后')->states($states)->default(1);
$form->radioButton('deduct_stock_type', '库存扣减')
->default(1)
->options(Goods::DEDUCT_STOCK_MAP)
->required();
$form->radioButton('pay_type', '支付方式')
->default(1)
->options(Goods::PAY_TYPE_MAP)
->required();
$form->select('delivery_id', '运费模板')
->options(function ($deliveryId) {
$delivery = Delivery::find($deliveryId);
if ($delivery) {
return [$delivery->id => $delivery->name];
}
})
->ajax(route('admin.mall.deliveries.ajax'))
->required();
$form->currency('original_price', '展示原价')
->default(0)
->help('仅作展示使用');
$form->radioButton('channel', '商品类型')
->options(Goods::Channels)
->required();
$form->radioButton('type', '规格类型')
->options(Goods::TYPE_MAP)
->default(Goods::CHANNEL_NORMAL)
->required()
->when(Goods::TYPE_SINGLE, function (Form $form) {
$form->hasMany('skus', '单规格', function (Form\NestedForm $form) {
$form->currency('price', '销售价格')
->default(0)
->required();
$form->hidden('score', '积分')
->default(0)
->required();
$form->hidden('assets', '资产')
->default(0)
->required();
$form->number('stock', '商品库存')
->default(0)
->required();
$form->text('weight', '商品重量')
->default(0)
->setWidth(2)
->required();
});
})
->when(Goods::TYPE_MULTIPLE, function ($form) {
})
->default(function () use ($form) {
return $form->isCreating() ? Goods::TYPE_SINGLE : '';
});
$form->ueditor('content', '商品详情')->required();
$form->switch('status', '状态')
->states([
'on' => ['value' => 1, 'text' => '上架', 'color' => 'success'],
'off' => ['value' => 3, 'text' => '下架', 'color' => 'danger'],
])
->default(1);
$form->ignore(['single']);
$form->saving(function (Form $form) {
if ($form->type === Goods::TYPE_SINGLE) {
$form->skus = [Arr::first($form->skus)];
}
if ($form->channel == Goods::CHANNEL_FREE && $form->isCreating()) {
$hasOne = Goods::where('channel', Goods::CHANNEL_FREE)->exists();
if ($hasOne) {
$error = new MessageBag([
'title' => '错误',
'message' => '免费产品只有一个',
]);
return back()->withInput()->with(compact('error'));
}
}
$form->content = Str::of(request()->get('content'))->replace('style=""', '');
});
return $form;
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Modules\Mall\Models\Job;
class JobController extends AdminController
{
use WithShop;
protected $title = '职位管理';
public function grid(): Grid
{
$grid = new Grid(new Job());
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('shop.name', '所属店铺');
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('name', '职位名称');
});
});
$grid->column('shop.name', '所属店铺');
$grid->column('name', '职位名称');
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Job());
$this->shop($form)->required();
$form->text('name', '职位名称')->required();
return $form;
}
}

View File

@@ -0,0 +1,277 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Support\MessageBag;
use Modules\Mall\Facades\Item;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Audit;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Delivered;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Pay;
use Modules\Mall\Http\Exporter\OrderExporter;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Express;
use Modules\Mall\Models\GoodsSku;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\Shop;
use Modules\User\Models\User;
use Modules\Mall\Facades\Order as OrderFacade;
class OrderController extends AdminController
{
protected $title = '订单管理';
/**
* Index interface.
*
* @param Content $content
* @return Content
*/
public function index(Content $content)
{
return $content->header('订单列表')->body($this->grid());
}
/**
* 已支付
*
* @param Content $content [description]
* @return Content [type] [description]
* @author 玄尘 2020-03-03
*/
public function paid(Content $content)
{
return $content->header('待发货')->body($this->grid(Order::STATUS_PAID));
}
/**
* 已发货
*
* @param Content $content [description]
* @return \Encore\Admin\Layout\Content [type] [description]
* @author 玄尘 2020-03-03
*/
public function delivered(Content $content)
{
return $content->header('已发货')->body($this->grid(Order::STATUS_DELIVERED));
}
protected function grid($state = ''): Grid
{
$grid = new Grid(new Order());
$grid->model()
->when($state, function ($q) use ($state) {
$q->where('state', $state);
})
->whereIn('type', [Order::TYPE_NORMAL, Order::TYPE_SCORE])
->with(['shop', 'items', 'user'])
->withCount('versions');
$grid->disableExport(false);
$grid->exporter(new OrderExporter);
$grid->disableCreateButton();
$grid->quickSearch('order_no')->placeholder('搜索订单编号');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('unPay', '未付款')->unPay();
$filter->scope('paid', '待发货')->paid();
$filter->scope('delivered', '已发货')->delivered();
$filter->scope('signed', '已签收')->signed();
$filter->scope('completed', '已完成')->completed();
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('order_no', '订单编号');
$filter->equal('shop_id', '所属店铺')->select(Shop::pluck('name', 'id'));
$filter->equal('user_id', '下单用户')->select()->ajax(route('admin.user.users.ajax'));
$filter->equal('type', '订单类型')->select(Order::TYPE_MAP);
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->between('created_at', '下单时间')->datetime();
$filter->between('paid_at', '付款时间')->datetime();
$filter->between('expired_at', '过期时间')->datetime();
});
});
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->can('pay') && $this->row->type == Order::TYPE_NORMAL) {
$actions->add(new Pay());
}
if ($actions->row->can('deliver')) {
$actions->add(new Delivered);
}
});
$grid->model()->with(['shop', 'user.info']);
$grid->column('order_no', '订单编号')->display(function () {
return sprintf('<a href="%s">%s</a>', route('admin.mall.orders.show', $this), $this->order_no);
});
$grid->column('user.username', '下单用户')
->display(function () {
return $this->user->username."({$this->user->info->nickname})";
});
if ($state == Order::STATUS_PAID) {
$grid->column('收货地址')
->display(function () {
return $this->express ? $this->express->getAllAddress() : '';
});
} else {
$grid->column('type', '订单类型')->using(Order::TYPE_MAP)->label();
$grid->column('expired_at', '过期时间')->sortable();
}
$grid->column('amount', '商品金额');
$grid->column('freight', '运费');
$grid->column('qty', '发货数量')
->display(function () {
return $this->items()->sum('qty');
});
$grid->column('订单金额')->display(function () {
return $this->total;
});
$grid->column('state', '订单状态')
->display(function () {
return $this->state_text;
})
->label();
$grid->column('paid_at', '支付时间')->sortable();
$grid->column('versions_count', '操作日志')->link(function () {
return route('admin.mall.versions', [
'model' => get_class($this),
'key' => $this->id,
]);
}, '_self');
$grid->column('created_at', '下单时间')->sortable();
return $grid;
}
/**
* Notes: 添加订单
*
* @Author: 玄尘
* @Date: 2022/7/29 13:21
* @return Form
*/
public function form(): Form
{
$form = new Form(new Order());
$form->select('user_id', '用户')
->options(function ($userId) {
$user = User::find($userId);
if ($user) {
return [$user->id => $user->username.' ['.$user->info->nickname.']'];
}
})
->ajax(route('admin.user.users.ajax'))
->load('address_id', route('admin.mall.ajax.address'))
->required();
$form->select('address_id', '收货地址')
->load('goods_sku_id', route('admin.mall.ajax.goods'))
->required();
$form->select('goods_sku_id', '商品')
->required();
$form->number('qty', '数量')->default(1);
$form->select('express_id', '物流')
->options(function () {
return Express::pluck('name', 'id');
})
->rules('required', ['required' => '物流公司必须选择']);
$form->text('express_no', '物流单号')->rules('required');
return $form;
}
/**
* Notes: 获取提交数据
*
* @Author: 玄尘
* @Date: 2022/7/29 14:16
* @return mixed|void
* @throws \Exception
*/
public function store()
{
$user_id = request('user_id', 0);
$remark = request('remark', '后台录入');
$address_id = request('address_id', 0);
$goods_sku_id = request('goods_sku_id', 0);
$qty = request('qty', 0);
$express_id = request('express_id');
$express_no = request('express_no');
$user = User::find($user_id);
$address = Address::find($address_id);
$goods_sku = GoodsSku::find($goods_sku_id);
if (! $goods_sku) {
$error = new MessageBag([
'title' => '错误',
'message' => '未找到商品',
]);
return back()->withInput()->with(compact('error'));
}
$stockData = $user->getStockData();
if ($qty > $stockData['residue']) {
$error = new MessageBag([
'title' => '错误',
'message' => '用户库存不足',
]);
return back()->withInput()->with(compact('error'));
} else {
$detail = collect();
$item = new Item($goods_sku, $address, $qty);
$detail->push($item);
$orders = (new OrderFacade)->user($user)
->remark($remark)
->type(Order::TYPE_SAMPLE)
->items($detail)
->address($address)
->create();
foreach ($orders as $order) {
$order->pay();//设置已支付
$order->deliver($express_id, $express_no);//发货
}
admin_toastr('操作完成');
return redirect()->to('/admin/mall/orders?user_id='.$user->id);
}
}
/**
* Notes : 订单详情
*
* @Date : 2021/3/16 1:46 下午
* @Author : < Jason.C >
* @param string $orderNo 订单的 order_no
*/
protected function detail(string $orderNo)
{
$order = Order::query()->firstWhere(['order_no' => $orderNo]);
return view('mall::admin.order.detail', compact('order'));
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Database\Eloquent\Model;
use Modules\Mall\Models\Express;
use Modules\Mall\Models\Reason;
class ReasonController extends AdminController
{
use WithUploads;
protected $title = '退款/货原因';
protected function grid(): Grid
{
$grid = new Grid(new Reason());
$grid->model()->withCount('shops');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('trashed', '回收站')->onlyTrashed();
});
$grid->column('id');
$grid->column('title', '名称');
$grid->column('status', '状态')->bool();
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Reason());
$form->text('title', '名称')->required();
$form->switch('status', '状态')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Grid;
use Modules\Mall\Events\RefundCompleted;
use Modules\Mall\Http\Controllers\Admin\Action\Order\RefundAudit;
use Modules\Mall\Http\Controllers\Admin\Action\Order\RefundReturns;
use Modules\Mall\Http\Controllers\Admin\Action\Order\RefundSign;
use Modules\Mall\Models\Refund;
class RefundController extends AdminController
{
protected $title = '退款订单';
public function grid(): Grid
{
$grid = new Grid(new Refund());
$grid->disableCreateButton();
$grid->actions(function ($actions) {
$actions->disableDelete();
$actions->disableEdit();
$actions->disableView();
if ($actions->row->can('agree')) {
$actions->add(new RefundAudit());
}
if ($actions->row->can('sign')) {
$actions->add(new RefundSign());
}
if ($actions->row->can('completed')) {
$actions->add(new RefundReturns());
}
});
$grid->filter(function (Grid\Filter $filter) {
$filter->disableIdFilter();
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->where(function ($query) {
$query->whereHas('order', function ($query) {
$query->where('order_no', 'like', "%{$this->input}%");
});
}, '订单编号');
$filter->equal('state', '状态')->select(Refund::STATUS_MAP);
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->where(function ($query) {
$query->whereHas('user', function ($query) {
$query->whereHas('info', function ($query) {
$query->where('nickname', 'like', "%{$this->input}%");
});
});
}, '下单用户');
$filter->where(function ($query) {
$query->whereHas('user', function ($query) {
$query->where('username', $this->input);
});
}, '手机号');
});
});
$grid->model()
->with(['shop', 'order', 'user.info'])
->withCount('versions');
$grid->column('id');
$grid->column('shop.name', '所属店铺');
$grid->column('order.order_no', '订单编号')->display(function () {
return sprintf('<a href="%s">%s</a>', route('admin.mall.orders.show', $this->order),
$this->order->order_no);
});
$grid->column('user.username', '下单用户');
$grid->column('refund_total', '退款金额');
$grid->column('actual_total', '实退金额');
$grid->column('状态')
->display(function () {
return $this->status_text;
})
->label();
$grid->column('remark', '备注');
$grid->column('versions_count', '操作日志')->link(function () {
return route('admin.mall.versions', [
'model' => get_class($this),
'key' => $this->id,
]);
}, '_self');
$grid->column('created_at', '申请时间');
return $grid;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Mall\Models\Region;
class RegionController extends AdminController
{
public function ajax(Request $request)
{
$q = $request->q;
return Region::where('name', 'like', "%$q%")->paginate(null, ['id', 'name as text']);
}
public function region(Request $request)
{
$regionId = $request->get('q');
return Region::where('parent_id', $regionId)->get(['id', DB::raw('name as text')]);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Selectable;
use Encore\Admin\Grid\Filter;
use Encore\Admin\Grid\Selectable;
use Modules\Mall\Models\Express;
class Expresses extends Selectable
{
public $model = Express::class;
public function make()
{
$this->column('id', '#ID#');
$this->column('name', '物流名称');
$this->column('status', '状态')->bool();
$this->column('created_at', '时间');
$this->filter(function (Filter $filter) {
$filter->like('name', '物流名称');
});
}
public static function display()
{
return function ($value) {
if (is_array($value)) {
return implode(';', array_column($value, 'name'));
}
return optional($this->expresses)->name;
};
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin\Selectable;
use Encore\Admin\Grid\Selectable;
use Modules\Mall\Models\Reason;
class Reasons extends Selectable
{
public $model = Reason::class;
public function make()
{
$this->column('id', '#ID#');
$this->column('title', '名称');
$this->column('status', '状态')->bool();
$this->column('created_at', '时间');
}
public static function display()
{
return function ($value) {
if (is_array($value)) {
return implode(';', array_column($value, 'title'));
}
return optional($this->reaons)->name;
};
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Http\Controllers\Admin\Action\Shop\Close;
use Modules\Mall\Http\Controllers\Admin\Action\Shop\Open;
use Modules\Mall\Http\Controllers\Admin\Action\Shop\Pass;
use Modules\Mall\Http\Controllers\Admin\Action\Shop\Reject;
use Modules\Mall\Http\Controllers\Admin\Selectable\Expresses;
use Modules\Mall\Http\Controllers\Admin\Selectable\Reasons;
use Modules\Mall\Models\Region;
use Modules\Mall\Models\Shop;
use Modules\User\Models\User;
class ShopController extends AdminController
{
use WithUploads;
protected $title = '店铺管理';
public function grid(): Grid
{
$grid = new Grid(new Shop());
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableView();
// $actions->disableEdit();
$actions->disableDelete();
if ($actions->row->status == Shop::STATUS_APPLYING) {
$actions->add(new Pass);
$actions->add(new Reject);
}
if ($actions->row->status == Shop::STATUS_NORMAL) {
$actions->add(new Close);
}
if ($actions->row->status == Shop::STATUS_CLOSE) {
$actions->add(new Open);
}
});
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '店铺名称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('user.info.nickname', '用户昵称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('mobile', '联系电话');
});
});
$grid->model()->withCount(['versions', 'staffers'])->with(['user.info']);
$grid->column('name', '店铺名称');
$grid->column('所属用户')->display(fn() => $this->user->username . '[' . $this->user->info->nickname . ']');
$grid->column('is_self', '类型')
->using([
0 => '合作',
1 => '自营',
])->label([
0 => 'info',
1 => 'danger',
]);
$grid->column('mobile', '联系电话');
$grid->column('address', '地址')->display(fn() => $this->getFullAddress());
$grid->column('status', '状态')
->using(Shop::STATUS_MAP)
->label(Shop::LABEL_MAP);
$grid->column('order', '排序')->sortable()->editable();
$grid->column('versions_count', '操作记录')->link(fn() => route('admin.mall.versions', [
'model' => get_class($this),
'key' => $this->id,
]), '_self');
$grid->column('staffers_count', '员工数量')->link(fn() => route('admin.mall.shops.staffers.index', $this), '_self');
$grid->column('created_at', '创建时间');
return $grid;
}
public function form(): Form
{
$form = new Form(new Shop());
$form->select('user_id', '所属用户')
->options(function ($userId) {
$user = User::find($userId);
if ($user) {
return [$user->id => $user->username . ' [' . $user->info->nickname . ']'];
}
})
->ajax(route('admin.user.users.ajax'))
->creationRules([
'required',
"unique:mall_shops",
], [
'unique' => '用户已存在店铺',
])
->updateRules([
'required',
"unique:mall_shops,user_id,{{id}}",
], [
'unique' => '用户已存在店铺',
])
->required();
$form->text('name', '店铺名称')
->creationRules(['required', "unique:mall_shops"], ['unique' => '店铺名称已存在'])
->updateRules(['required', "unique:mall_shops,name,{{id}}"], ['unique' => '店铺名称已存在'])
->required();
$form->switch('is_self', '自营店铺');
$form->textarea('description', '店铺简介')
->required();
$form->text('mobile', '联系电话')
->rules([
'size:11',
'phone:CN',
], [
'size' => '手机号格式不正确',
'phone' => '手机号格式不正确',
])
->creationRules(['required', "unique:mall_shops"], ['unique' => '店铺联系电话已存在'])
->updateRules(['required', "unique:mall_shops,name,{{id}}"], ['unique' => '店铺联系电话已存在'])
->required();
$form->divider('地址信息');
$form->select('province_id', '省份')
->options(Region::where('parent_id', 1)->pluck('name', 'id'))
->load('city_id', route('admin.mall.regions.region'))
->required();
$form->select('city_id', '城市')
->options(function ($option) {
$parent = Region::where('id', $option)->value('parent_id');
return Region::where(['parent_id' => $parent])->pluck('name', 'id');
})
->load('district_id', route('admin.mall.regions.region'))
->required();
$form->select('district_id', '区/县')
->options(function ($option) {
$parent = Region::where('id', $option)->value('parent_id');
return Region::where(['parent_id' => $parent])->pluck('name', 'id');
})
->required();
$form->text('address', '详细地址')
->required();
$form->divider('其他信息');
$this->cover($form, 'cover', '店铺LOGO');
$form->radio('status', '店铺状态')
->options(Shop::STATUS_MAP);
$form->number('order', '排序')
->default('9999')
->help('仅后台可见,用于店铺推荐序列');
$form->belongsToMany('expresses', Expresses::class, '发货物流');
$form->belongsToMany('reasons', Reasons::class, '退款原因');
return $form;
}
/**
* Notes : 获取店铺AJAX
* @Date : 2021/5/7 4:39 下午
* @Author : < Jason.C >
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function ajax(Request $request)
{
$key = $request->get('q');
return Shop::where('name', 'like', "%$key%")->paginate(null, ['id', 'name as text']);
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSku;
class SkuController extends Controller
{
use WithUploads;
public function index(Content $content, Goods $good): Content
{
return $content
->header($good->name)
->description('价格表')
->body($this->grid($good));
}
public function grid($goods): Grid
{
$grid = new Grid(new GoodsSku());
$grid->model()->where('goods_id', $goods->id);
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableView();
$actions->disableDelete();
});
$grid->disableCreateButton();
$grid->column('cover', '封面图')->image('', 80);
$grid->column('unit', '产品单元');
$grid->column('price', '售价');
$grid->column('score', '积分/原石');
$grid->column('assets', '资产');
$grid->column('stock', '库存');
$grid->column('weight', '重量');
$grid->column('created_at', '创建时间');
return $grid;
}
public function edit(Content $content, Goods $good, GoodsSku $sku)
{
return $content
->header($good->name)
->description('编辑价格')
->body($this->form($good)->edit($sku->id));
}
public function form($good): Form
{
$form = new Form(new GoodsSku());
$this->cover($form);
$form->currency('price', '销售价格');
$form->currency('score', '积分/原石');
$form->currency('assets', '资产');
$form->number('stock', '库存');
$form->text('weight', '重量')->setWidth(2);
return $form;
}
public function update(Goods $good, GoodsSku $sku)
{
return $this->form($good)->update($sku->id);
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Form;
use Encore\Admin\Form\NestedForm;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSpec;
class SpecController extends Controller
{
public function index(Content $content, Goods $good): Content
{
return $content
->header($good->name)
->description('属性列表')
->body($this->grid($good));
}
public function grid($good): Grid
{
$grid = new Grid(new GoodsSpec());
$grid->model()
->where('goods_id', $good->id)
->withCount('values');
$grid->column('name', '属性名称');
$grid->column('values_count', '属性值数量')
->link(function () use ($good) {
return route('admin.mall.goods.specs.values.index', [$good, $this]);
}, '_self');
return $grid;
}
public function create(Content $content, Goods $good): Content
{
return $content
->header($good->name)
->description('属性列表')
->body($this->form($good));
}
public function form($good): Form
{
$form = new Form(new GoodsSpec());
$form->text('name', '属性名称');
$form->hidden('goods_id')->value($good->id);
$form->hasMany('values', '属性值列表', function (NestedForm $form) {
$form->text('value', '属性值');
});
return $form;
}
public function store(Goods $good)
{
return $this->form($good)->store();
}
public function edit(Content $content, Goods $good, GoodsSpec $spec): Content
{
return $content
->header($good->name)
->description('属性列表')
->body($this->form($good)->edit($spec->id));
}
public function update(Goods $good, GoodsSpec $spec)
{
return $this->form($good)->update($spec->id);
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSpec;
use Modules\Mall\Models\GoodsSpecValue;
class SpecValueController extends Controller
{
public function index(Content $content, Goods $good, GoodsSpec $spec): Content
{
return $content
->header($spec->name)
->description('属性值列表')
->body($this->grid($spec));
}
public function grid($spec): Grid
{
$grid = new Grid(new GoodsSpecValue());
$grid->model()->where('spec_id', $spec->id);
$grid->column('value', '属性值');
return $grid;
}
public function create(Content $content, Goods $good, GoodsSpec $spec): Content
{
return $content
->header($spec->name)
->description('属性列表')
->body($this->form($spec));
}
public function form($spec): Form
{
$form = new Form(new GoodsSpecValue());
$form->text('value', '属性值');
$form->hidden('spec_id')->value($spec->id);
return $form;
}
public function store(Goods $good, GoodsSpec $spec)
{
return $this->form($spec)->store();
}
public function edit(Content $content, Goods $good, GoodsSpec $spec, GoodsSpecValue $value): Content
{
return $content
->header($spec->name)
->description('属性列表')
->body($this->form($spec)->edit($value->id));
}
public function update(Goods $good, GoodsSpec $spec, GoodsSpecValue $value)
{
return $this->form($spec)->update($value->id);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Job;
use Modules\Mall\Models\Shop;
use Modules\Mall\Models\ShopStaffer;
use Modules\User\Models\User;
class StafferController extends Controller
{
public function index(Content $content, Shop $shop): Content
{
return $content
->header($shop->name)
->description('员工管理')
->body($this->grid($shop));
}
public function grid($shop): Grid
{
$grid = new Grid(new ShopStaffer());
$grid->model()->byShop($shop);
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('user.username', '用户名');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('user.info.nickname', '用户昵称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('job.name', '职位名称');
});
});
$grid->column('所属用户')->display(fn() => $this->user->username . '[' . $this->user->info->nickname . ']');
$grid->column('job.name', '职位名称');
$grid->column('created_at', '创建时间');
return $grid;
}
public function create(Content $content, Shop $shop): Content
{
return $content
->header($shop->name)
->description('新增员工')
->body($this->form($shop));
}
public function store(Shop $shop)
{
return $this->form($shop)->store();
}
public function form(Shop $shop): Form
{
$form = new Form(new ShopStaffer());
$form->hidden('shop_id')->value($shop->getKey());
$form->select('user_id', '所属用户')
->options(function ($userId) {
$user = User::find($userId);
if ($user) {
return [$user->id => $user->username . ' [' . $user->info->nickname . ']'];
}
})
->ajax(route('admin.user.users.ajax'))
->required();
$form->select('job_id', '职位')->options(fn() => Job::byShop($shop)->pluck('name', 'id'));
return $form;
}
public function edit(Content $content, Shop $shop, ShopStaffer $staffer): Content
{
return $content
->header($shop->name)
->description('编辑员工')
->body($this->form($shop)->edit($staffer->getKey()));
}
public function update(Shop $shop, ShopStaffer $staffer)
{
return $this->form($shop)->update($staffer->getKey());
}
}

View File

@@ -0,0 +1,254 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Support\MessageBag;
use Modules\Mall\Facades\Item;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Delivered;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Pay;
use Modules\Mall\Http\Exporter\OrderExporter;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Express;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\GoodsSku;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderExpress;
use Modules\Mall\Models\Shop;
use Modules\User\Models\User;
use Modules\Mall\Facades\Order as OrderFacade;
class StockOrderBySystemController extends AdminController
{
protected $title = '公司线下发货';
protected function grid(): Grid
{
$grid = new Grid(new Order());
$grid->model()
->where('type', Order::TYPE_SAMPLE)
->where('channel', Order::CHANNEL_SYSTEM)
->with(['shop', 'user', 'items'])
->withCount('versions');
$grid->quickSearch('order_no')->placeholder('搜索订单编号');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('unPay', '未付款')->unPay();
$filter->scope('paid', '待发货')->paid();
$filter->scope('delivered', '已发货')->delivered();
$filter->scope('signed', '已签收')->signed();
$filter->scope('completed', '已完成')->completed();
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('order_no', '订单编号');
// $filter->equal('shop_id', '所属店铺')->select(Shop::pluck('name', 'id'));
$filter->equal('user_id', '提货用户')->select()->ajax(route('admin.user.users.ajax'));
// $filter->equal('type', '订单类型')->select(Order::TYPE_MAP);
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
// $filter->between('created_at', '下单时间')->datetime();
// $filter->between('paid_at', '付款时间')->datetime();
// $filter->between('expired_at', '过期时间')->datetime();
});
});
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->can('pay') && $this->row->type == Order::TYPE_NORMAL) {
$actions->add(new Pay());
}
if ($actions->row->can('deliver')) {
$actions->add(new Delivered);
}
});
$grid->model()->with(['shop', 'user.info']);
$grid->column('order_no', '订单编号')->display(function () {
return sprintf('<a href="%s">%s</a>', route('admin.mall.stock_orders.show', $this), $this->order_no);
});
// $grid->column('shop.name', '所属店铺');
$grid->column('user.username', '提货用户');
// $grid->column('amount', '商品金额');
// $grid->column('freight', '运费');
$grid->column('qty', '提货数量')
->display(function () {
return $this->items()->sum('qty');
});
// $grid->column('订单金额')->display(function () {
// return $this->total;
// });
$grid->column('state', '订单状态')
->display(function () {
return $this->state_text;
})
->label();
$grid->column('type', '订单类型')->using(Order::TYPE_MAP)->label();
// $grid->column('paid_at', '支付时间')->sortable();
// $grid->column('expired_at', '过期时间')->sortable();
$grid->column('versions_count', '操作日志')->link(function () {
return route('admin.mall.versions', [
'model' => get_class($this),
'key' => $this->id,
]);
}, '_self');
$grid->column('created_at', '下单时间')->sortable();
$grid->disableExport(false);
$grid->export(function ($export) {
$export->column('order_no', function ($value, $original) {
return strip_tags($value)."\n";
});
$export->column('type', function ($value, $original) {
return strip_tags($value);
});
$export->column('state', function ($value, $original) {
return strip_tags($value);
});
$export->except(['versions_count', 'paid_at', 'expired_at']);
$export->filename($this->title.date("YmdHis"));
});
return $grid;
}
/**
* Notes: 添加订单
*
* @Author: 玄尘
* @Date: 2022/7/29 13:21
* @return Form
*/
public function form(): Form
{
$form = new Form(new Order());
$form->select('user_id', '会员')
->options(function ($userId) {
$user = User::find($userId);
if ($user) {
return [$user->id => $user->username.' ['.$user->info->nickname.']'];
}
})
->ajax(route('admin.user.users.ajax'))
->load('address_id', route('admin.mall.ajax.address'))
->required();
$form->select('address_id', '收货地址')
->load('goods_sku_id', route('admin.mall.ajax.goods'))
->required();
$form->html("<div id='showImage' style='margin-top:10px'><a href='/admin/mall/addresses/create'>添加收货地址</a></div>",
'');
$form->select('goods_sku_id', '剩余箱数')->required();
$form->number('qty', '发货箱数')->default(1);
$form->radioButton('type', '方式')
->options(OrderExpress::TYPE_MAP)
->required()
->when(OrderExpress::TYPE_EXPRESS, function ($form) {
$form->select('express_id', '物流')
->options(function () {
return Express::pluck('name', 'id');
})
->rules('required', ['required' => '物流公司必须选择']);
$form->text('express_no', '物流单号')->rules('required');
})
->when(OrderExpress::TYPE_LOGISTICS, function ($form) {
$form->text('person', '经办人');
});
return $form;
}
/**
* Notes: 获取提交数据
*
* @Author: 玄尘
* @Date: 2022/7/29 14:16
* @return mixed|void
* @throws \Exception
*/
public function store()
{
$user_id = request('user_id', 0);
$remark = request('remark', '后台录入');
$address_id = request('address_id', 0);
$goods_sku_id = request('goods_sku_id', 0);
$qty = request('qty', 0);
$express_id = request('express_id');
$express_no = request('express_no');
$type = request('type');
$person = request('person');
$user = User::find($user_id);
$address = Address::find($address_id);
$goods_sku = GoodsSku::find($goods_sku_id);
if (! $goods_sku) {
$error = new MessageBag([
'title' => '错误',
'message' => '未找到商品',
]);
return back()->withInput()->with(compact('error'));
}
$stockData = $user->getStockData();
if ($qty > $stockData['residue']) {
$error = new MessageBag([
'title' => '错误',
'message' => '用户库存不足',
]);
return back()->withInput()->with(compact('error'));
} else {
$detail = collect();
$item = new Item($goods_sku, $address, $qty);
$detail->push($item);
$orders = (new OrderFacade)->user($user)
->remark($remark)
->channel(Order::CHANNEL_SYSTEM)
->type(Order::TYPE_SAMPLE)
->items($detail)
->address($address)
->create();
foreach ($orders as $order) {
$order->pay();//设置已支付
$order->deliver($express_id, $express_no, $type, $person);//发货
}
admin_toastr('操作完成');
return redirect()->to('/admin/mall/stock_orders_by_system?user_id='.$user->id);
}
}
/**
* Notes : 订单详情
*
* @Date : 2021/3/16 1:46 下午
* @Author : < Jason.C >
* @param string $orderNo 订单的 order_no
*/
protected function detail(string $orderNo)
{
$order = Order::query()->firstWhere(['order_no' => $orderNo]);
return view('mall::admin.stock_order.detail', compact('order'));
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Support\MessageBag;
use Modules\Mall\Facades\Item;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Delivered;
use Modules\Mall\Http\Controllers\Admin\Action\Order\Pay;
use Modules\Mall\Http\Exporter\OrderExporter;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Express;
use Modules\Mall\Models\GoodsSku;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\Shop;
use Modules\User\Models\User;
use Modules\Mall\Facades\Order as OrderFacade;
class StockOrderController extends AdminController
{
protected $title = '免费领取订单';
protected function grid(): Grid
{
$grid = new Grid(new Order());
$grid->model()
->where('type', Order::TYPE_SAMPLE)
->with(['shop', 'user', 'items'])
->withCount('versions');
$grid->disableCreateButton();
$grid->quickSearch('order_no')->placeholder('搜索订单编号');
$grid->filter(function (Grid\Filter $filter) {
$filter->scope('unPay', '未付款')->unPay();
$filter->scope('paid', '待发货')->paid();
$filter->scope('delivered', '已发货')->delivered();
$filter->scope('signed', '已签收')->signed();
$filter->scope('completed', '已完成')->completed();
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('order_no', '订单编号');
// $filter->equal('shop_id', '所属店铺')->select(Shop::pluck('name', 'id'));
$filter->equal('user_id', '提货用户')->select()->ajax(route('admin.user.users.ajax'));
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->between('created_at', '下单时间')->datetime();
$filter->equal('state', '订单状态')->select([
Order::STATUS_CANCEL => '已取消',
Order::STATUS_PAID => '待发货',
Order::STATUS_DELIVERED => '已发货',
Order::STATUS_SIGNED => '已签收',
]);
// $filter->between('paid_at', '付款时间')->datetime();
// $filter->between('expired_at', '过期时间')->datetime();
});
});
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->can('pay') && $this->row->type == Order::TYPE_NORMAL) {
$actions->add(new Pay());
}
if ($actions->row->can('deliver')) {
$actions->add(new Delivered);
}
});
$grid->model()->with(['shop', 'user.info']);
$grid->column('order_no', '订单编号')->display(function () {
return sprintf('<a href="%s">%s</a>', route('admin.mall.stock_orders.show', $this), $this->order_no);
});
// $grid->column('shop.name', '所属店铺');
$grid->column('user.username', '提货用户');
// $grid->column('amount', '商品金额');
// $grid->column('freight', '运费');
$grid->column('qty', '提货数量')
->display(function () {
return $this->items()->sum('qty');
});
// $grid->column('订单金额')->display(function () {
// return $this->total;
// });
$grid->column('state', '订单状态')
->display(function () {
return $this->state_text;
})
->label();
// $grid->column('type', '订单类型')->using(Order::TYPE_MAP)->label();
// $grid->column('paid_at', '支付时间')->sortable();
// $grid->column('expired_at', '过期时间')->sortable();
$grid->column('versions_count', '操作日志')->link(function () {
return route('admin.mall.versions', [
'model' => get_class($this),
'key' => $this->id,
]);
}, '_self');
$grid->column('created_at', '下单时间')->sortable();
$grid->disableExport(false);
$grid->export(function ($export) {
$export->column('order_no', function ($value, $original) {
return strip_tags($value)."\n";
});
$export->column('type', function ($value, $original) {
return strip_tags($value);
});
$export->column('state', function ($value, $original) {
return strip_tags($value);
});
$export->except(['versions_count', 'paid_at', 'expired_at']);
$export->filename($this->title.date("YmdHis"));
});
return $grid;
}
/**
* Notes : 订单详情
*
* @Date : 2021/3/16 1:46 下午
* @Author : < Jason.C >
* @param string $orderNo 订单的 order_no
*/
protected function detail(string $orderNo)
{
$order = Order::query()->firstWhere(['order_no' => $orderNo]);
return view('mall::admin.stock_order.detail', compact('order'));
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Illuminate\Http\Request;
use Modules\Mall\Models\Tag;
class TagController extends AdminController
{
use WithShop;
protected $title = '商品标签';
public function grid(): Grid
{
$grid = new Grid(new Tag());
$grid->model()->with(['shop']);
$grid->column('shop.name', '所属店铺');
$grid->column('name', '标签名称');
return $grid;
}
public function form(): Form
{
$form = new Form(new Tag());
$this->shop($form)->required();
$form->text('name', '标签名称')
->required();
return $form;
}
public function ajax(Request $request)
{
$key = $request->q;
return Tag::where('name', 'like', "%$key%")->paginate(null, ['id', 'name as text']);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Encore\Admin\Auth\Database\Administrator;
use Encore\Admin\Grid;
use Encore\Admin\Layout\Content;
use Encore\Admin\Widgets\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Routing\Controller;
use Overtrue\LaravelVersionable\Version;
class VersionController extends Controller
{
public function index(Content $content, $model, $key): Content
{
return $content
->header($model . ' => ' . $key)
->description('数据操作日志')
->body($this->grid($model, $key));
}
public function grid($model, $key): Grid
{
$grid = new Grid(new Version());
$grid->disableCreateButton();
$grid->disableActions();
$grid->disableTools();
$grid->model()->whereHasMorph(
'versionable',
$model,
function (Builder $query) use ($key) {
$query->where('id', $key);
}
)->orderByDesc('id');
$grid->column('操作用户')->display(function () {
if ($this->user_id < config('mall.administrator_max_id')) {
config(['versionable.user_model' => Administrator::class]);
return '[Admin] ' . ($this->user->name ?? '--SYSTEM--');
} else {
return '[USER] ' . ($this->user->username ?? '--USER--');
}
});
$grid->column('versionable_type', '数据变动')->expand(function () {
$data = [];
foreach ($this->contents as $key => $item) {
$data[] = [
$key,
is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE) : $item,
];
}
return new Table(['字段名称', '变更值'], $data);
});
$grid->column('操作时间')->display(function () {
return $this->created_at->toDateTimeString();
});
return $grid;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use App\Admin\Traits\WithUploads;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Modules\Linker\Traits\WithLinker;
use Modules\Mall\Models\Banner;
use Modules\Mall\Models\Video;
class VideoController extends AdminController
{
use WithUploads,
WithShop,
WithLinker;
public $title = "视频管理";
protected function grid(): Grid
{
$grid = new Grid(new Video());
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('name', '视频名称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('status', '状态')->radio([
0 => '禁用',
1 => '正常',
]);
});
});
$grid->column('id', '#ID#');
$grid->column('name', '视频名称');
$grid->column('cover', '封面地址')->image('', 60, 60);
$grid->column('path', '视频地址')
->display(function () {
return $this->path_url;
});
$grid->column('status', '状态')->switch();
$grid->column('created_at', '创建时间');
return $grid;
}
protected function form(): Form
{
$form = new Form((new Video())->disableModelCaching());
$form->text('name', '视频标题')->required();
$this->cover($form);
$this->video($form);
$form->switch('status', '显示')->default(1);
return $form;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Modules\Mall\Http\Controllers\Admin;
use Modules\Mall\Models\Shop;
trait WithShop
{
public function shop($form)
{
return $form->select('shop_id', '所属店铺')
->options(function ($shopId) {
$shop = Shop::find($shopId);
if ($shop) {
return [$shop->id => $shop->name];
}
})
->ajax(route('admin.mall.shops.ajax'));
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\Mall\Http\Controllers\Agent;
use App\Api\Controllers\Controller;
use Illuminate\Http\Request;
class IndexController extends Controller
{
public function index(Request $request)
{
return $this->success($request->shop);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Modules\Mall\Http\Controllers\Agent;
use App\Api\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Jason\Api\Api;
class ShopController extends Controller
{
public function index(): JsonResponse
{
$user = Api::user();
return $this->success();
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Mall\Http\Controllers\Api;
use App\Api\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Modules\Mall\Http\Resources\Api\Activity\ActivityBaseResource;
use Modules\Mall\Http\Resources\Api\Activity\ActivityResource;
use Modules\Mall\Models\Activity;
class ActivityController extends Controller
{
/**
* Notes: 活动列表
*
* @Author: 玄尘
* @Date: 2022/7/27 11:26
* @return JsonResponse
*/
public function index(): JsonResponse
{
$activities = Activity::shown()->get();
return $this->success(ActivityBaseResource::collection($activities));
}
public function show(Activity $activity)
{
return $this->success(new ActivityResource($activity));
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace Modules\Mall\Http\Controllers\Api;
use App\Api\Controllers\Controller;
use Exception;
use Illuminate\Http\Request;
use Jason\Api\Api;
use Modules\Mall\Http\Requests\Address\AddressRequest;
use Modules\Mall\Http\Resources\Api\Address\AddressResource;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Region;
class AddressController extends Controller
{
/**
* Notes: 地址列表
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:09 下午
* @return mixed
*/
public function index()
{
$addresses = Address::byUser(Api::user())->orderBy('is_default', 'desc')->get();
return $this->success(AddressResource::collection($addresses));
}
/**
* Notes: 创建地址,展示地区联动数据
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:10 下午
*/
public function create(Request $request)
{
$parentId = $request->parent_id ?? 1;
$list = Region::where('parent_id', $parentId)->get();
return $this->success($list);
}
/**
* Notes: 保存地址
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:13 下午
* @param \Modules\Mall\Http\Requests\Address\AddressRequest $request
* @return mixed
*/
public function store(AddressRequest $request)
{
$result = Address::create([
'user_id' => Api::userId(),
'name' => $request->name,
'mobile' => $request->mobile,
'province_id' => $request->province_id,
'city_id' => $request->city_id,
'district_id' => $request->district_id,
'address' => $request->address,
'is_default' => $request->is_default,
]);
if ($result) {
return $this->success('操作成功');
} else {
return $this->failed('失败');
}
}
/**
* Notes: 地址详情,如果不是自己的地址,不显示
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:31 下午
* @param Address $address
* @return mixed
*/
public function show(Address $address)
{
if ($address->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
return $this->success(new AddressResource($address));
}
/**
* Notes: 设置默认地址
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:34 下午
* @param Address $address
* @return mixed
*/
public function setDefault(Address $address)
{
if ($address->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
try {
$address->setDefault();
return $this->success('操作成功');
} catch (Exception $exception) {
return $this->failed('失败');
}
}
/**
* Notes: 更新地址
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:40 下午
*/
public function update(AddressRequest $request, Address $address)
{
if ($address->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
$address->update([
'name' => $request->name,
'mobile' => $request->mobile,
'province_id' => $request->province_id,
'city_id' => $request->city_id,
'district_id' => $request->district_id,
'address' => $request->address,
'is_default' => $request->is_default,
]);
return $this->success('操作成功');
}
/**
* Notes: 删除地址
*
* @Author: <C.Jason>
* @Date : 2020/11/5 5:52 下午
* @param Address $address
* @return mixed
*/
public function destroy(Address $address)
{
if ($address->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
try {
$address->delete();
return $this->success('操作成功');
} catch (Exception$exception) {
return $this->failed('失败');
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Modules\Mall\Http\Controllers\Api;
use App\Api\Controllers\Controller;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Modules\Mall\Http\Resources\Api\Banner\BannerResource;
use Modules\Mall\Models\Banner;
class BannerController extends Controller
{
/**
* Notes : 获取banner的接口
* @Date : 2021/6/3 11:28 上午
* @Author : <Jason.C>
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request): JsonResponse
{
$position = $request->position;
$shopId = $request->shop_id;
// 店铺ID为null的是平台轮播
$banners = Banner::when($shopId, function (Builder $query) use ($shopId) {
$query->where('shop_id', $shopId);
})->when(is_numeric($position), function (Builder $query) use ($position) {
$query->ofPosition($position);
})->get();
return $this->success(BannerResource::collection($banners));
}
}

View File

@@ -0,0 +1,159 @@
<?php
namespace Modules\Mall\Http\Controllers\Api;
use App\Api\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Jason\Api\Api;
use Modules\Mall\Http\Requests\Cart\CartRequest;
use Modules\Mall\Http\Resources\Api\Goods\CartResource;
use Modules\Mall\Http\Resources\Api\Shop\ShopBaseInfoResource;
use Modules\Mall\Models\Cart;
use Modules\Mall\Models\GoodsSku;
use Modules\Mall\Models\Shop;
class CartController extends Controller
{
/**
* Notes : 购物车内商品列表
* @Date : 2021/8/26 3:42 下午
* @Author : <Jason.C>
* @return \Illuminate\Http\JsonResponse
*/
public function index(): JsonResponse
{
$list = Cart::byUser(Api::user())->with('sku')->orderByDesc('updated_at')->get();
$collection = $list->groupBy('shop_id');
$items = [];
foreach ($collection as $shopId => $item) {
$items[] = [
'shop' => new ShopBaseInfoResource(Shop::find($shopId)),
'items' => CartResource::collection($item),
];
}
return $this->success($items);
}
/**
* Notes : 加入购物车
* @Date : 2021/5/14 2:17 下午
* @Author : <Jason.C>
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function store(Request $request)
{
$skuId = $request->sku_id;
$qty = $request->qty ?? 1;
$sku = GoodsSku::findOrFail($skuId);
if (!$sku->canBuy($qty)) {
return $this->failed('超过最大可购买数量');
}
$cart = Cart::where('user_id', Api::userId())->where('sku_id', $skuId)->first();
if ($cart) {
$cart->qty = $cart->qty + $qty;
$cart->save();
} else {
$cart = Cart::create([
'user_id' => Api::userId(),
'shop_id' => $sku->goods->shop_id,
'sku_id' => $skuId,
'qty' => $qty,
]);
}
return $this->success($cart->qty);
}
/**
* Notes : 获取购物车商品数量
* @Date : 2021/5/14 1:47 下午
* @Author : <Jason.C>
*/
public function count(): JsonResponse
{
if (Api::userId()) {
$count = Cart::where('user_id', Api::userId())->count();
} else {
$count = 0;
}
return $this->success($count);
}
/**
* Notes : 更新购物车商品数量
* @Date : 2021/5/13 2:52 下午
* @Author : <Jason.C>
* @param \Modules\Mall\Http\Requests\Cart\CartRequest $request
* @param \Modules\Mall\Models\Cart $cart
* @return mixed
*/
public function update(CartRequest $request, Cart $cart)
{
$skuId = $request->sku_id;
$qty = $request->qty;
if ($cart->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
if ($skuId && ($skuId != $cart->sku_id)) {
$sku = GoodsSku::find($skuId);
if (!$cart->sku->goods->is($sku->goods)) {
return $this->failed('错误的商品');
}
$cart->sku_id = $skuId;
} else {
$sku = $cart->sku;
}
if ($qty < 1 || $qty > $sku->stock) {
return $this->failed('库存不足');
}
$cart->qty = $qty;
$cart->save();
return $this->success($qty);
}
/**
* Notes : 删除购物车商品
* @Date : 2021/5/17 4:00 下午
* @Author : <Jason.C>
* @param string $cart
* @return mixed|string
*/
public function destroy(string $cart)
{
if (Str::contains($cart, ',')) {
$count = Cart::query()->where('user_id', Api::userId())->whereIn('id', explode(',', $cart))->delete();
if (!$count) {
return $this->failed('empty');
}
} else {
$cart = Cart::find($cart);
if ($cart->user()->isNot(Api::user())) {
return $this->failed('', 404);
}
$cart->delete();
}
return $this->success('删除成功');
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Modules\Mall\Http\Controllers\Api;
use App\Api\Controllers\Controller;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Modules\Mall\Http\Resources\Api\Category\CategoryResource;
use Modules\Mall\Models\Category;
class CategoryController extends Controller
{
/**
* Notes : 商品分类列表
*
* @Date : 2021/6/3 12:08 下午
* @Author : <Jason.C>
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request): JsonResponse
{
$shopId = $request->shop_id;
$list = Category::query()
->where('id', '<>', 2)
->when($shopId, function (Builder $query) use ($shopId) {
$query->where('shop_id', $shopId);
})->where('parent_id', 0)->get();
return $this->success(CategoryResource::collection($list));
}
}

Some files were not shown because too many files have changed in this diff Show More