1
0

提交代码

This commit is contained in:
2020-08-06 14:50:07 +08:00
parent 9d0d5f4be9
commit d7a848c824
11299 changed files with 1321854 additions and 0 deletions

74
vendor/rulong/areas/README.md vendored Normal file
View File

@@ -0,0 +1,74 @@
# 用户收货地址管理
## 使用方法
### 1.Trait模式
```
use RuLong\Area\Traits\UserHasAddress;
class User extends Authenticatable
{
use UserHasAddress;
public $guarded = [];
}
```
#### // 用户地址列表
```
$user->addresses;
```
#### // 用户默认地址
```
$user->getDefaultAddress;
```
### 2.Facade模式
#### //地址数据
```
$data = [
'name' => $name, //收货人姓名
'mobile' => $mobile, //收货人电话
'address' => $address, //收货人地址
'province_sn' => $province_sn, //省份编码
'city_sn' => $city_sn, //城市编码
'area_sn' => $area_sn, //区域编码
];
```
#### // 新增地址
```
Address::store($data);
```
#### // 更新地址
```
Address::update(UserAddress $address, $data);
```
#### // 删除地址
```
Address::destroy($id);
```
#### // 获取区域列表
```
//$psn=0 返回所有省份列表,$psn=省份sn 返回省份所有城市列表,$psn=城市sn 返回城市所有区域列表。
Area::index($psn);
```

30
vendor/rulong/areas/composer.json vendored Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "rulong/areas",
"description": "",
"license": "MIT",
"authors": [
{
"name": "C.Jason",
"email": "chenjxlg@163.com"
}
],
"require": {
"php": ">=7.1.3"
},
"autoload": {
"psr-0": {
"RuLong\\Area": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"RuLong\\Area\\ServiceProvider"
],
"aliases": {
"Address": "RuLong\\Area\\Facades\\Address",
"Area": "RuLong\\Area\\Facades\\Area"
}
}
}
}

View File

@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRulongAreasTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('rulong_areas', function (Blueprint $table) {
$table->increments('id');
$table->integer('sn')->unsigned();
$table->integer('psn')->unsigned();
$table->string('province');
$table->string('city');
$table->string('area');
$table->string('name');
$table->string('shortname');
$table->string('type', 50);
$table->string('cnname');
$table->string('enname');
$table->string('info');
$table->string('shortinfo');
$table->integer('zone')->unsigned();
$table->integer('zip')->unsigned();
$table->decimal('lng', 10, 7);
$table->decimal('lat', 10, 7);
$table->integer('depth')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('rulong_areas');
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserAddressesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_addresses', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->index('user_id');
$table->string('name', 32)->nullable();
$table->string('mobile', 32)->nullable();
$table->integer('province_sn')->unsigned();
$table->integer('city_sn')->unsigned();
$table->integer('area_sn')->unsigned();
$table->string('address')->nullable();
$table->boolean('is_default')->default(0);
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('user_addresses');
}
}

File diff suppressed because it is too large Load Diff

82
vendor/rulong/areas/src/Address.php vendored Normal file
View File

@@ -0,0 +1,82 @@
<?php
namespace RuLong\Area;
use Illuminate\Support\Facades\DB;
use RuLong\Area\Exceptions\AddressException;
use RuLong\Area\Models\UserAddress;
use Validator;
// 地址管理
class Address
{
public function store($data)
{
$validator = Validator::make($data, [
'name' => 'required',
'mobile' => 'required',
'province_sn' => 'required',
'city_sn' => 'required',
'area_sn' => 'required',
'address' => 'required',
], [
'name.required' => '收货人必须填写',
'mobile.required' => '手机号必须填写',
'province_sn.required' => '省份必须选择',
'city_sn.required' => '城市必须选择',
'area_sn.required' => '区域必须选择',
'address.required' => '收货地址必须填写',
]);
if ($validator->fails()) {
throw new AddressException($validator->errors()->first());
}
return UserAddress::create($data);
}
public function update(UserAddress $address, $data)
{
$validator = Validator::make($data, [
'name' => 'required',
'mobile' => 'required',
'province_sn' => 'required',
'city_sn' => 'required',
'area_sn' => 'required',
'address' => 'required',
], [
'name.required' => '收货人必须填写',
'mobile.required' => '手机号必须填写',
'province_sn.required' => '省份必须选择',
'city_sn.required' => '城市必须选择',
'area_sn.required' => '区域必须选择',
'address.required' => '收货地址必须填写',
]);
if ($validator->fails()) {
throw new AddressException($validator->errors()->first());
}
if (isset($data['is_default'])) {
UserAddress::where('user_id', $address->user_id)->update(['is_default' => 0]);
}
return $address->update($data);
}
public function destroy($id)
{
return UserAddress::where('id', $id)->delete();
}
public function setDefault(UserAddress $address)
{
try {
DB::transaction(function () use ($address) {
$address->is_default = 1;
$address->save();
UserAddress::where('user_id', $address->user_id)->where('id', '<>', $address->id)->update(['is_default' => 0]);
});
return true;
} catch (\Exception $e) {
throw new AddressException($e->getMessage());
}
}
}

18
vendor/rulong/areas/src/Area.php vendored Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Area;
use RuLong\Area\Models\Area as AreaModel;
/**
* 三联动,查询
*/
class Area
{
public function index($psn)
{
return AreaModel::where(['psn' => $psn])->select('sn', 'name')->get() ?? [];
}
}

25
vendor/rulong/areas/src/Command.php vendored Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace RuLong\Area;
use Illuminate\Console\Command as LaravelCommand;
class Command extends LaravelCommand
{
protected $signature = 'rulong:areas';
protected $description = 'Install RuLong-Area';
public function handle()
{
// 创建数据库结构
$this->call('migrate');
// 迁移数据
$this->call('db:seed', [
'--class' => RulongAreasTableSeeder::class,
]);
$this->info('Database migrate success');
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace RuLong\Area\Exceptions;
use RuntimeException;
class AddressException extends RuntimeException
{
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Area\Facades;
use Illuminate\Support\Facades\Facade;
class Address extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Area\Address::class;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Area\Facades;
use Illuminate\Support\Facades\Facade;
class Area extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Area\Area::class;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Area\Facades;
use Illuminate\Support\Facades\Facade;
class Tencentlbs extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Area\TencentIbs::class;
}
}

47
vendor/rulong/areas/src/Models/Area.php vendored Normal file
View File

@@ -0,0 +1,47 @@
<?php
namespace RuLong\Area\Models;
use App\Models\Mobile;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
class Area extends Model
{
protected $guarded = [];
protected $table = 'rulong_areas';
public function user()
{
return $this->belongsTo(User::class);
}
public function mobiles()
{
if ($this->type == '省级') {
return $this->hasMany(Mobile::class, 'province_sn', 'sn');
} elseif ($this->type == '地级') {
return $this->hasMany(Mobile::class, 'city_sn', 'sn');
} else {
return $this->hasMany(Mobile::class, 'area_sn', 'sn');
}
}
public function children()
{
return $this->hasMany(Area::class, 'psn', 'sn')->where('type', '地级');
}
public function allchildren()
{
return $this->hasMany(Area::class, 'psn', 'sn');
}
public function parent()
{
return $this->hasOne(Area::class, 'sn', 'psn');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace RuLong\Area\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use RuLong\Order\Contracts\Addressbook;
class UserAddress extends Model implements Addressbook
{
use SoftDeletes;
protected $guarded = [];
/**
* 收件人姓名
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 收件人电话
* @return string
*/
public function getMobile()
{
return $this->mobile;
}
/**
* 收件人详细地址
* @return string
*/
public function getAddress()
{
return str_replace(",", "-", $this->Area->info) . '-' . $this->address;
}
public function Area()
{
return $this->belongsTo(Area::class, 'area_sn', 'sn');
}
public function Province()
{
return $this->belongsTo(Area::class, 'province_sn', 'sn');
}
public function addresses()
{
return $this->hasMany(UserAddress::class);
}
public function getDefaultAddress()
{
return $this->addresses()->orderBy('is_default', 'desc')->first();
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace RuLong\Area;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class RulongAreasTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$SQL = file_get_contents(__DIR__ . '/../database/seeds/database_seeder.stub');
DB::statement($SQL);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace RuLong\Area;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class ServiceProvider extends LaravelServiceProvider
{
protected $commands = [
Command::class,
];
/**
* [部署时加载]
* @Author:<C.Jason>
* @Date:2018-10-17T17:09:22+0800
* @return [type] [description]
*/
public function boot()
{
$this->commands($this->commands);
if ($this->app->runningInConsole()) {
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations/');
}
}
}

121
vendor/rulong/areas/src/TencentIbs.php vendored Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace RuLong\Area;
use GuzzleHttp\Client;
/**
* 腾讯位置服务
*/
class TencentIbs
{
protected $baseURL = 'https://apis.map.qq.com/ws/';
protected $key = 'M2CBZ-LRN3X-CKU4Q-7TN6N-AJZQT-BVFAT';
protected $params = [];
/**
* 地址解析(地址转坐标)
* @param [type] $address [description]
* @return array [经纬度坐标]
*/
public function getLocation($address)
{
$apiUrl = $this->baseURL . 'geocoder/v1/';
$data['address'] = $address;
$this->setParams($data);
$result = $this->dopost($apiUrl);
if (is_object($result)) {
$location = [
'lng' => $result->location->lng,
'lat' => $result->location->lat,
];
return $location;
} else {
return $result;
}
}
/**
* 地址解析(地址转坐标)
* @param [type] $keywords [description]
* @return array [地址解析内容]
*/
public function getAddressCode($keywords)
{
$apiUrl = $this->baseURL . 'geocoder/v1/';
$data['address'] = $keywords;
$this->setParams($data);
$result = $this->dopost($apiUrl);
if (is_object($result)) {
$address_code = [
'title' => $result->title,
'location' => [
'lng' => $result->location->lng,
'lat' => $result->location->lat,
],
'address' => [
'province' => $result->address_components->province,
'city' => $result->address_components->city,
'district' => $result->address_components->district,
'street' => $result->address_components->street,
'street_number' => $result->address_components->street_number,
],
];
return $address_code;
} else {
return $result;
}
}
/**
* 距离两点计算
* @param $from_lat, $from_lon, [起点经纬度]
* @param $to_lat,$to_lon [终点经纬度]
* @param $radius [可选,默认为地球的半径]
* @return float [返回两地距离,单位千米]
*/
public function getDistance($from_lat, $from_lon, $to_lat, $to_lon, $radius = 6378.137)
{
$rad = floatval(M_PI / 180.0);
$from_lat = floatval($from_lat) * $rad;
$from_lon = floatval($from_lon) * $rad;
$to_lat = floatval($to_lat) * $rad;
$to_lon = floatval($to_lon) * $rad;
$theta = $to_lon - $from_lon;
$dist = acos(sin($from_lat) * sin($to_lat) +
cos($from_lat) * cos($to_lat) * cos($theta)
);
if ($dist < 0) {
$dist += M_PI;
}
return $dist = $dist * $radius; //返回千米
}
public function setParams(array $params)
{
$this->params = $params;
$this->params['key'] = $this->key;
}
private function dopost($url)
{
try {
$Client = new Client();
$response = $Client->get($url, ['query' => $this->params]);
$result = json_decode($response->getBody()->getContents());
if ($result->status == 0) {
return $result->result;
} else {
return $result->message;
}
} catch (\Exception $e) {
return $e->getmessage();
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace RuLong\Area\Traits;
use RuLong\Area\Models\UserAddress;
trait UserHasAddress
{
public function addresses()
{
return $this->hasMany(UserAddress::class);
}
public function getDefaultAddress()
{
return $this->addresses()->orderBy('is_default', 'desc')->firstOrFail();
}
}

24
vendor/rulong/order/README.md vendored Normal file
View File

@@ -0,0 +1,24 @@
# RuLong-Order
## 简介
订单管理系统
## 安装
### Composer 安装扩展包
~~~
$ composer require rulong/order
~~~
### 初始化数据库
~~~
$ php artisan migrate
~~~
### 发布配置文件
~~~
$ php artisan vendor:publish --provider="RuLong\Order\ServiceProvider"
~~~
## 在系统中使用

31
vendor/rulong/order/composer.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "rulong/order",
"description": "",
"license": "MIT",
"authors": [
{
"name": "C.Jason",
"email": "chenjxlg@163.com"
}
],
"require": {
"php": ">=7.1.3",
"laravel/framework": "*"
},
"autoload": {
"psr-0": {
"RuLong\\Order": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"RuLong\\Order\\ServiceProvider"
],
"aliases": {
"Orders": "RuLong\\Order\\Facades\\Orders",
"Refunds": "RuLong\\Order\\Facades\\Refunds"
}
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
return [
/**
* 用户模型
*/
'user_model' => config('user_account.user_model'),
/**
* 订单编号规则
*/
'order_orderid' => [
'length' => 20,
'prefix' => '',
],
/**
* 退款单号规则
*/
'refund_orderid' => [
'length' => 20,
'prefix' => 'R',
],
/**
* 订单自动审核
*/
'auto_audit' => true,
/**
* N天后无事件的订单 可完成
*/
'completed_days' => 7,
'admin_guard' => config('admin.auth.guard') ?: 'admin',
];

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderDetailsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_details', function(Blueprint $table)
{
$table->increments('id');
$table->integer('order_id')->unsigned()->index('order_id');
$table->integer('item_id')->unsigned()->comment('商品编号');
$table->string('item_type');
$table->integer('number')->unsigned()->comment('数量');
$table->decimal('price', 20)->unsigned()->comment('单价');
$table->decimal('score', 20);
$table->dateTime('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_details');
}
}

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderExpressLogsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_express_logs', function(Blueprint $table)
{
$table->increments('id');
$table->integer('order_id')->unsigned()->index('order_id');
$table->string('company', 32)->nullable()->comment('物流公司');
$table->string('number', 32)->nullable()->comment('物流单号');
$table->integer('goods_id')->unsigned();
$table->integer('goods_num')->unsigned()->default(1);
$table->dateTime('deliver_at')->nullable()->comment('发货时间');
$table->dateTime('receive_at')->nullable()->comment('签收时间');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_express_logs');
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderExpressesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_expresses', function(Blueprint $table)
{
$table->increments('id');
$table->integer('order_id')->unsigned()->index('order_id');
$table->string('name', 32)->nullable();
$table->string('mobile', 32)->nullable();
$table->string('address')->nullable();
$table->string('company', 32)->nullable()->comment('物流公司');
$table->string('number', 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::drop('order_expresses');
}
}

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderLogsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_logs', function(Blueprint $table)
{
$table->increments('id');
$table->integer('order_id')->unsigned()->index('order_id');
$table->integer('user_id')->unsigned()->default(0);
$table->string('user_type')->nullable();
$table->string('state');
$table->string('status');
$table->text('logs', 65535)->nullable();
$table->dateTime('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_logs');
}
}

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderRefundExpressesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_refund_expresses', function(Blueprint $table)
{
$table->increments('id');
$table->integer('refund_id')->unsigned()->index('refund_id');
$table->string('company', 32)->nullable()->comment('快递公司');
$table->string('number', 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::drop('order_refund_expresses');
}
}

View File

@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderRefundItemsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_refund_items', function(Blueprint $table)
{
$table->increments('id');
$table->integer('refund_id')->unsigned()->comment('退款单ID');
$table->integer('order_id')->unsigned()->comment('订单ID');
$table->integer('order_detail_id')->unsigned()->comment('订单详情ID');
$table->integer('item_id')->unsigned()->comment('产品ID');
$table->string('item_type');
$table->integer('number')->unsigned()->comment('退货数量');
$table->decimal('price', 20, 3)->unsigned();
$table->dateTime('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_refund_items');
}
}

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderRefundLogsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_refund_logs', function(Blueprint $table)
{
$table->increments('id');
$table->integer('refund_id')->unsigned();
$table->integer('order_id')->unsigned();
$table->decimal('price', 10)->comment('退款金额');
$table->text('source', 65535)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_refund_logs');
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrderRefundsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('order_refunds', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('order_id')->unsigned()->comment('关联订单id');
$table->string('orderid', 32)->unique('orderid')->comment('退款单号');
$table->decimal('refund_total', 20, 3)->unsigned()->comment('退款金额');
$table->decimal('actual_total', 20, 3)->unsigned()->default(0.000)->comment('实退金额');
$table->string('state', 16);
$table->string('remark')->nullable();
$table->dateTime('refunded_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('order_refunds');
}
}

View File

@@ -0,0 +1,45 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateOrdersTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('orders', function(Blueprint $table)
{
$table->increments('id');
$table->string('orderid', 32)->unique('orderid')->comment('订单编号');
$table->boolean('express_type')->default(1);
$table->integer('user_id')->unsigned();
$table->string('type', 16)->nullable();
$table->decimal('amount', 20)->unsigned()->comment('商品金额');
$table->decimal('score', 20);
$table->decimal('freight', 10)->unsigned()->nullable()->comment('运费');
$table->string('status', 16)->default('0000')->comment('4码订单状态');
$table->string('state', 16)->comment('状态');
$table->string('remark')->nullable()->comment('订单备注');
$table->dateTime('paid_at')->nullable()->comment('支付时间');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('orders');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace RuLong\Order\Contracts;
/**
* 可购买商品 契约
*/
interface Addressbook
{
/**
* 收件人姓名
* @return string
*/
public function getName();
/**
* 收件人电话
* @return string
*/
public function getMobile();
/**
* 收件人详细地址
* @return string
*/
public function getAddress();
}

View File

@@ -0,0 +1,39 @@
<?php
namespace RuLong\Order\Contracts;
/**
* 可购买商品 契约
*/
interface Orderable
{
/**
* 获取商品名称
* @return string
*/
public function getTitle();
/**
* 获取商品单价
* @return string
*/
public function getPrice();
/**
* 获取商品库存
* @return string
*/
public function getStock();
/**
* 扣除库存方法
*/
public function deductStock($stock);
/**
* 增加库存方法
*/
public function addStock($stock);
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单支付完成事件
*/
class OrderCanceled
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单关闭
*/
class OrderClosed
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单完毕
*/
class OrderCompleted
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单支付完成事件
*/
class OrderCreated
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 延迟收货
*/
class OrderDelaied
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单发货完成
*/
class OrderDelivered
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单支付完成事件
*/
class OrderPaid
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 订单签收完成
*/
class OrderSignined
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace RuLong\Order\Events;
use RuLong\Order\Models\Order;
/**
* 未收到货物
*/
class OrderUnreceived
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
<?php
namespace RuLong\Order\Exceptions;
use RuntimeException;
class OrderException extends RuntimeException
{
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace RuLong\Order\Exceptions;
use RuntimeException;
class RefundException extends RuntimeException
{
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Order\Facades;
use Illuminate\Support\Facades\Facade;
class Orders extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Order\Orders::class;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Order\Facades;
use Illuminate\Support\Facades\Facade;
class Refunds extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Order\Refunds::class;
}
}

149
vendor/rulong/order/src/Models/Order.php vendored Normal file
View File

@@ -0,0 +1,149 @@
<?php
namespace RuLong\Order\Models;
use App\Models\Card;
use App\Models\Payment;
use Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use RuLong\Order\Traits\OrderCando;
use RuLong\Order\Traits\OrderHasActions;
use RuLong\Order\Traits\OrderHasAttributes;
use RuLong\Order\Traits\OrderHasScopes;
use RuLong\Order\Utils\Helper;
class Order extends Model
{
use OrderCando, OrderHasActions, OrderHasAttributes, OrderHasScopes, SoftDeletes;
const ORDER_INIT = 'INIT'; // 订单初始化
const ORDER_UNPAID = 'UNPAID'; // 待支付
const ORDER_PAID = 'PAID'; // 已支付
const ORDER_DELIVER = 'DELIVER'; // 发货处理中
const ORDER_DELIVERED = 'DELIVERED'; // 已发货
const ORDER_SIGNED = 'SIGNED'; // 已签收
const REFUND_APPLY = 'REFUND_APPLY'; // 申请退款
const REFUND_AGREE = 'REFUND_AGREE'; // 同意退款
const REFUND_REFUSE = 'REFUND_REFUSE'; // 拒绝退款
const REFUND_PROCESS = 'REFUND_PROCESS'; // 退款中
const REFUND_COMPLETED = 'REFUND_COMPLETED'; // 退款完成
const ORDER_CLOSED = 'CLOSED'; // 已关闭
const ORDER_CANCEL = 'CANCEL'; // 取消
const ORDER_COMPLETED = 'COMPLETED'; // 已完成
const CANCEL_USER = 2; // 买家取消
const CANCEL_SELLER = 3; // 卖家取消
const CANCEL_SYSTEM = 4; // 系统取消
protected $guarded = [];
protected $dates = [
'paid_at',
];
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->orderid = Helper::orderid(config('rulong_order.order_orderid.length'), config('rulong_order.order_orderid.prefix'));
});
self::updated(function ($model) {
$model->logs()->create([
'user' => self::detectUser(),
'status' => $model->getOriginal('status', '0000') . '|' . $model->status,
'state' => $model->getOriginal('state') . '|' . $model->state,
]);
});
}
/**
* 侦测当前操作用户
* @Author:<C.Jason>
* @Date:2018-10-26T14:29:52+0800
* @return Auth
*/
public static function detectUser()
{
return Auth::user() ?: Auth::guard(config('rulong_order.admin_guard'))->user();
}
/**
* 关联所属用户
* @Author:<C.Jason>
* @Date:2018-10-19T14:05:42+0800
* @return User
*/
public function user()
{
return $this->belongsTo(config('rulong_order.user_model'));
}
/**
* 订单详情
* @Author:<C.Jason>
* @Date:2018-10-19T10:35:55+0800
* @return OrderDetail
*/
public function details()
{
return $this->hasMany(OrderDetail::class);
}
/**
* 订单物流
* @Author:<C.Jason>
* @Date:2018-10-19T10:36:03+0800
* @return OrderExpress
*/
public function express()
{
return $this->hasOne(OrderExpress::class);
}
/**
* 订单日志
* @Author:<C.Jason>
* @Date:2018-10-19T10:36:11+0800
* @return OrderLog
*/
public function logs()
{
return $this->hasMany(OrderLog::class);
}
/**
* 退款单
* @Author:<C.Jason>
* @Date:2018-10-19T13:15:02+0800
* @return OrderRefund
*/
public function refund()
{
return $this->hasOne(Refund::class)->orderBy('id', 'desc');
}
/**
* 全部退款单
* @Author:<C.Jason>
* @Date:2018-10-22T14:26:18+0800
* @return [type] [description]
*/
public function refunds()
{
return $this->hasMany(Refund::class);
}
public function payment()
{
return $this->hasOne(Payment::class);
}
public function card()
{
return $this->belongsTo(Card::class);
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace RuLong\Order\Models;
use Illuminate\Database\Eloquent\Model;
use RuLong\Order\Contracts\Orderable;
use RuLong\Order\Exceptions\OrderException;
class OrderDetail extends Model
{
const UPDATED_AT = null;
protected $guarded = [];
public $goodsCanBuy;
public $goodsTitle;
/**
* 设置订单详情
* @Author:<C.Jason>
* @Date:2018-10-25T14:30:50+0800
* @param Orderable $goods 商品实例
*/
public function setGoodsAttribute($goods)
{
if (!($goods instanceof Orderable)) {
throw new OrderException('购买的商品必须实现 Orderable 接口');
}
$this->goodsCanBuy = $goods->getStock();
$this->goodsTitle = $goods->getTitle();
$this->attributes['price'] = $goods->getPrice();
$this->attributes['item_id'] = $goods->id;
$this->attributes['item_type'] = get_class($goods);
}
/**
* 所属订单
* @Author:<C.Jason>
* @Date:2018-10-19T13:48:40+0800
* @return Order
*/
public function order()
{
return $this->belongsTo(Order::class);
}
/**
* 所属商品
* @Author:<C.Jason>
* @Date:2018-10-19T13:48:51+0800
* @return
*/
public function item()
{
return $this->morphTo();
}
/**
* 获取最大可退数量
* @Author:<C.Jason>
* @Date:2018-10-23T11:36:43+0800
* @return integer
*/
public function getMaxRefundAttribute(): int
{
$refundNumbers = $this->refundItems()->whereHas('refund', function ($query) {
$query->whereNotIn('state', [Order::REFUND_REFUSE]);
})->sum('number');
return $this->number - $refundNumbers;
}
/**
* 判断订单条目,是否可退款
* @Author:<C.Jason>
* @Date:2018-10-26T10:43:08+0800
* @return [type] [description]
*/
public function canRefund()
{
return $this->max_refund > 0;
}
/**
* 关联退款详单
* @Author:<C.Jason>
* @Date:2018-10-23T11:44:22+0800
* @return RefundItem
*/
public function refundItems()
{
return $this->hasMany(RefundItem::class);
}
/**
* 获取单个商品总价
* @Author:<C.Jason>
* @Date:2018-10-19T13:51:19+0800
* @return string
*/
public function getTotalAttribute(): string
{
return bcmul($this->price, $this->number, 3);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace RuLong\Order\Models;
use Illuminate\Database\Eloquent\Model;
use Config;
class OrderExpress extends Model
{
protected $guarded = [];
protected $dates = [
'deliver_at',
'receive_at',
];
/**
* 所属订单
* @Author:<C.Jason>
* @Date:2018-10-19T13:49:06+0800
* @return Order
*/
public function order()
{
return $this->belongsTo(Order::class);
}
/**
* 设置收货地址详细内容
* @Author:<C.Jason>
* @Date:2018-10-22T10:10:02+0800
* @param Addressbook $Addressbook
*/
public function setInstanceAttribute($Addressbook)
{
$this->attributes['name'] = $Addressbook->getName();
$this->attributes['mobile'] = $Addressbook->getMobile();
$this->attributes['address'] = $Addressbook->getAddress();
$this->attributes['province_sn'] = $Addressbook->province_sn;
$this->attributes['city_sn'] = $Addressbook->city_sn;
$this->attributes['area_sn'] = $Addressbook->area_sn;
}
public function getCompanyTextAttribute()
{
$deliver_list = Config::get('deliver_list');
$array = preg_split('/[\r\n]+/', trim($deliver_list, "\r\n"));
if (strpos($deliver_list, ':')) {
$options = [];
foreach ($array as $val) {
[$k, $v] = explode(':', $val, 2);
$options[$k] = $v;
}
} else {
$options = $array;
}
return $options[$this->company]??'未知';
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace RuLong\Order\Models;
use Auth;
use Illuminate\Database\Eloquent\Model;
class OrderLog extends Model
{
const UPDATED_AT = null;
protected $guarded = [];
public function setUserAttribute($user)
{
if (!is_null($user)) {
$this->attributes['user_id'] = $user->id ?? 0;
$this->attributes['user_type'] = get_class($user) ?? null;
}
}
/**
* 所属订单
* @Author:<C.Jason>
* @Date:2018-10-19T13:49:06+0800
* @return Order
*/
public function order()
{
return $this->belongsTo(Order::class);
}
/**
* 操作用户
* @Author:<C.Jason>
* @Date:2018-10-26T11:42:37+0800
* @return
*/
public function user()
{
return $this->morphTo();
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace RuLong\Order\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use RuLong\Order\Traits\RefundCando;
use RuLong\Order\Traits\RefundHasActions;
use RuLong\Order\Utils\Helper;
class Refund extends Model
{
use RefundHasActions, RefundCando, SoftDeletes;
const REFUND_APPLY = 'REFUND_APPLY'; // 申请退款
const REFUND_AGREE = 'REFUND_AGREE'; // 同意退款
const REFUND_REFUSE = 'REFUND_REFUSE'; // 拒绝退款
const REFUND_PROCESS = 'REFUND_PROCESS'; // 退款中
const REFUND_COMPLETED = 'REFUND_COMPLETED'; // 退款完成
protected $table = 'order_refunds';
protected $guarded = [];
protected $dates = [
'refunded_at',
];
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->orderid = Helper::orderid(config('rulong_order.refund_orderid.length'), config('rulong_order.refund_orderid.prefix'));
});
}
/**
* 所属订单
* @Author:<C.Jason>
* @Date:2018-10-19T13:45:04+0800
* @return Order
*/
public function order()
{
return $this->belongsTo(Order::class);
}
/**
* 退款单详情
* @Author:<C.Jason>
* @Date:2018-10-19T13:45:26+0800
* @return OrderRefundItem
*/
public function items()
{
return $this->hasMany(RefundItem::class);
}
/**
* 退款单物流
* @Author:<C.Jason>
* @Date:2018-10-19T10:36:03+0800
* @return RefundExpress
*/
public function express()
{
return $this->hasOne(RefundExpress::class);
}
/**
* 获取退款状态 $this->state_text
* @Author:<C.Jason>
* @Date:2018-10-19T10:56:24+0800
* @return string
*/
protected function getStateTextAttribute(): string
{
switch ($this->state) {
case self::REFUND_APPLY:
$state = '退款申请中';
break;
case self::REFUND_AGREE:
$state = '同意退款';
break;
case self::REFUND_PROCESS:
$state = '退款中';
break;
case self::REFUND_COMPLETED:
$state = '退款完毕';
break;
case self::REFUND_REFUSE:
$state = '拒绝退款';
break;
default:
$state = '未知状态';
break;
}
return $state;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace RuLong\Order\Models;
use Illuminate\Database\Eloquent\Model;
class RefundExpress extends Model
{
protected $table = 'order_refund_expresses';
protected $guarded = [];
protected $dates = [
'deliver_at',
'receive_at',
];
/**
* 所属退款单
* @Author:<C.Jason>
* @Date:2018-10-23T10:56:16+0800
* @return Refund
*/
public function refund()
{
return $this->belongsTo(Refund::class);
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace RuLong\Order\Models;
use Illuminate\Database\Eloquent\Model;
class RefundItem extends Model
{
const UPDATED_AT = null;
protected $table = 'order_refund_items';
protected $guarded = [];
/**
* 所属退款单
* @Author:<C.Jason>
* @Date:2018-10-19T13:44:57+0800
* @return Refund
*/
public function refund()
{
return $this->belongsTo(Refund::class);
}
/**
* 所属订单
* @Author:<C.Jason>
* @Date:2018-10-19T13:45:59+0800
* @return Order
*/
public function order()
{
return $this->belongsTo(Order::class);
}
/**
* 所属订单详情
* @Author:<C.Jason>
* @Date:2018-10-19T13:46:04+0800
* @return OrderDetail
*/
public function detail()
{
return $this->belongsTo(OrderDetail::class);
}
/**
* 商品详情
* @Author:<C.Jason>
* @Date:2018-10-19T13:46:20+0800
* @return
*/
public function item()
{
return $this->morphTo();
}
/**
* 获取单个商品总价
* @Author:<C.Jason>
* @Date:2018-10-19T13:51:19+0800
* @return string
*/
public function getItemTotalAttribute(): string
{
return bcmul($this->price, $this->number, 3);
}
/**
* 设置退款单详情使用
* @Author:<C.Jason>
* @Date:2018-10-25T14:28:11+0800
* @param OrderDetail $detail 订单详情实例
*/
public function setDetailAttribute($detail)
{
$this->attributes['order_id'] = $detail->order_id;
$this->attributes['order_detail_id'] = $detail->id;
$this->attributes['item_id'] = $detail->item_id;
$this->attributes['item_type'] = $detail->item_type;
$this->attributes['price'] = $detail->price;
}
}

228
vendor/rulong/order/src/Orders.php vendored Normal file
View File

@@ -0,0 +1,228 @@
<?php
namespace RuLong\Order;
use Illuminate\Support\Facades\DB;
use RuLong\Order\Contracts\Addressbook;
use RuLong\Order\Contracts\Orderable;
use RuLong\Order\Events\OrderCreated;
use RuLong\Order\Exceptions\OrderException;
use RuLong\Order\Models\Order;
use RuLong\Order\Models\OrderDetail;
use RuLong\Order\Models\OrderExpress;
use RuLong\Order\Models\Refund;
class Orders
{
public $createOrderid = '';
/**
* 创建订单
* @Author:<C.Jason>
* @Date:2018-10-19T16:20:27+0800
* @param integer $userID 用户ID
* @param $items 商品详情
* array[
* new OrderDetail(['goods' => RuLong\Order\Contracts\Orderable, 'number' => int]),
* new OrderDetail(['goods' => RuLong\Order\Contracts\Orderable, 'number' => int]),
* ]
* @param Addressbook $address 收获地址
* @param string $remark 订单备注
* @param float $amount 总金额auto的时候自动计算
* @param float $freight 运费
* @return boolean|OrderException
*/
public function create(int $userID, int $sellerID, string $type, array $items, int $express_type, Addressbook $address = null, string $remark = null, float $amount = null, float $score = null, float $freight = null, $cardId = null)
{
try {
if (is_null($amount) && !empty($items)) {
$amount = 0;
foreach ($items as $item) {
if ($item->goodsCanBuy < $item->number) {
throw new OrderException('【' . $item->goodsTitle . '】商品库存不足');
}
$amount += $item->price * $item->number;
}
} elseif (is_null($amount) && !is_numeric($amount)) {
throw new OrderException('订单金额必须是数字类型');
}
DB::transaction(function () use ($userID, $sellerID, $type, $items, $address, $amount, $score, $freight, $remark, $express_type, $cardId) {
// 创建订单
$order = Order::create([
'user_id' => $userID,
'seller_id' => $sellerID,
'card_id' => $cardId,
'type' => $type,
'amount' => $amount,
'score' => $score,
'express_type' => $express_type,
'freight' => $freight,
'state' => Order::ORDER_INIT,
'remark' => $remark,
]);
$this->createOrderid = $order->orderid;
// 创建订单详情
$order->details()->saveMany($items);
// 自动扣除库存
foreach ($order->details as $detail) {
$detail->item->deductStock($detail->number);
}
// 保存收获地址如果收获地址是null说明不用发货
if (!is_null($address)) {
$express = new OrderExpress(['instance' => $address]);
$order->express()->save($express);
}
event(new OrderCreated($order));
if (config('rulong_order.auto_audit') === true) {
$order->audit(true);
}
});
return $this->createOrderid;
} catch (\Exception $e) {
throw new OrderException($e->getMessage());
}
}
/**
* 订单审核
* @Author:<C.Jason>
* @Date:2018-10-25T16:16:06+0800
* @param Order $order 订单实例
* @param boolean $result 审核结果
* @return boolean|OrderException
*/
public function audit(Order $order, $result = true)
{
return $order->audit($result);
}
/**
* 取消订单
* @param Order $order 订单实例
* @param integer $channel 取消渠道
* [CANCEL_USER, CANCEL_SELLER, CANCEL_SYSTEM]
* @return boolean|OrderCancelException
*/
public function cancel(Order $order, $channel = Order::CANCEL_SYSTEM)
{
return $order->cancel($channel);
}
/**
* 订单支付
* @return [type] [description]
*/
public function paid(Order $order)
{
return $order->paid();
}
/**
* 标记订单 发货处理中
* @Author:<C.Jason>
* @Date:2018-10-22T13:29:56+0800
* @param Order $order [description]
* @return [type] [description]
*/
public function delivering(Order $order)
{
$order->delivering();
}
/**
* 订单发货
* @Author:<C.Jason>
* @Date:2018-10-22T13:43:38+0800
* @param Order $order 订单实例
* @param string $company 物流公司
* @param string $number 物流单号
* @return [type] [description]
*/
public function deliver(Order $order, $company = null, $number = null)
{
return $order->deliver($company, $number);
}
/**
* 签收
* @Author:<C.Jason>
* @Date:2018-10-22T13:46:21+0800
* @param Order $order 订单实例
* @return [type] [description]
*/
public function signin(Order $order)
{
return $order->signin();
}
/**
* 延迟收货
* @Author:<C.Jason>
* @Date:2018-10-22T14:10:04+0800
* @return [type] [description]
*/
public function delay(Order $order)
{
return $order->delay();
}
/**
* 未收到
* @Author:<C.Jason>
* @Date:2018-10-22T14:10:04+0800
* @return [type] [description]
*/
public function unreceive(Order $order)
{
return $order->unreceive();
}
/**
* 交易完成
* @Author:<C.Jason>
* @Date:2018-10-22T14:15:48+0800
*/
public function complete(Order $order)
{
return $order->complete();
}
/**
* 关闭订单
* @Author:<C.Jason>
* @Date:2018-10-22T14:13:13+0800
* @param Order $order [description]
* @return [type] [description]
*/
public function close(Order $order)
{
return $order->close();
}
/**
* 申请退款
* @Author:<C.Jason>
* @Date:2018-10-23T10:35:42+0800
* @param Order $order 要退款的订单
* @param array $items 退款项目
* [
* ['item_id' => integer, 'number' => integer],
* ['item_id' => integer, 'number' => integer],
* ]
* @param float $total 申请退款金额
* @return [type] [description]
*/
public function refund(Order $order, array $items, float $total = null)
{
return \Refunds::create($order, $items, $total);
}
}

148
vendor/rulong/order/src/Refunds.php vendored Normal file
View File

@@ -0,0 +1,148 @@
<?php
namespace RuLong\Order;
use Illuminate\Support\Facades\DB;
use RuLong\Order\Events\RefundApplied;
use RuLong\Order\Exceptions\OrderException;
use RuLong\Order\Models\Order;
use RuLong\Order\Models\OrderDetail;
use RuLong\Order\Models\Refund;
use RuLong\Order\Models\RefundItem;
class Refunds
{
/**
* 申请退款
* @Author:<C.Jason>
* @Date:2018-10-23T10:35:42+0800
* @param Order $order 要退款的订单
* @param array $items 退款项目
* [
* ['item_id' => integer, 'number' => integer],
* ['item_id' => integer, 'number' => integer],
* ]
* @param float $total 申请退款金额
* @return [type] [description]
*/
public function create(Order $order, array $items, float $total = null)
{
try {
if (!$order->canRefund()) {
throw new OrderException('订单状态不可退款');
}
if (empty($items)) {
throw new OrderException('至少选择一项退款商品');
}
$maxAmount = 0;
$refundItems = [];
//判断最大可退数量
foreach ($items as $item) {
$detail = OrderDetail::find($item['item_id']);
if ($item['number'] <= 0) {
throw new OrderException('【' . $detail->item->getTitle() . '】退货数量必须大于0');
}
if ($item['number'] > $detail->max_refund) {
throw new OrderException('【' . $detail->item->getTitle() . '】超过最大可退数量');
}
$maxAmount += $detail->price * $item['number'];
$refundItems[] = new RefundItem(['detail' => $detail, 'number' => $item['number']]);
}
// 自动计算退款金额
if (is_null($total)) {
$total = $maxAmount;
} elseif (!in_array($order->getOrderStatus('deliver'), [0, 1, 4]) && $total > $maxAmount) {
throw new OrderException('超过最大可退金额');
}
DB::transaction(function () use ($order, $total, $refundItems) {
// 判断退款金额
if (in_array($order->getOrderStatus('deliver'), [0, 1, 4, 6]) && $order->amount == $total) {
$total = $order->total;
// 如果是未发货,无需发货,未收到的,直接退全款
$order->setOrderStatus('pay', 4);
} elseif ($order->total == $total) {
$order->setOrderStatus('pay', 4);
} elseif ($order->amount == $total) {
$order->setOrderStatus('pay', 2);
} else {
$order->setOrderStatus('pay', 3);
}
if (in_array($order->getOrderStatus('deliver'), [0, 1, 8])) {
$order->setOrderStatus('deliver', 6);
}
$order->state = Order::REFUND_APPLY;
$order->save();
$refund = $order->refund()->create([
'refund_total' => $total,
'state' => Refund::REFUND_APPLY,
]);
$refund->items()->saveMany($refundItems);
event(new RefundApplied($order, $refund));
});
return true;
} catch (\Exception $e) {
throw new OrderException($e->getMessage());
}
}
/**
* 同意退款
* @Author:<C.Jason>
* @Date:2018-10-23T14:20:41+0800
* @param Refund $refund 退款单实例
* @return RefundException|boolean
*/
public function agree(Refund $refund)
{
return $refund->agree();
}
/**
* 拒绝退款
* @Author:<C.Jason>
* @Date:2018-10-23T15:17:18+0800
* @param Refund $refund 退款单实例
* @param string|null $remark 拒绝备注
* @return RefundException|boolean
*/
public function refuse(Refund $refund, string $remark = null)
{
return $refund->refuse($remark);
}
/**
* 退款中
* @Author:<C.Jason>
* @Date:2018-10-23T15:17:21+0800
* @param Refund $refund 退款单实例
* @return RefundException|boolean
*/
public function deliver(Refund $refund)
{
return $refund->deliver();
}
/**
* 退款完成
* @Author:<C.Jason>
* @Date:2018-10-23T15:17:23+0800
* @param Refund $refund 退款单实例
* @return RefundException|boolean
*/
public function complete(Refund $refund)
{
return $refund->complete();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace RuLong\Order;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class ServiceProvider extends LaravelServiceProvider
{
/**
* 部署时加载
* @Author:<C.Jason>
* @Date:2018-06-22T16:01:20+0800
* @return [type] [description]
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([__DIR__ . '/../config/rulong_order.php' => config_path('rulong_order.php')]);
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations/');
}
}
/**
* 注册服务提供者
* @Author:<C.Jason>
* @Date:2018-06-22T16:01:12+0800
* @return [type] [description]
*/
public function register()
{
$this->mergeConfigFrom(__DIR__ . '/../config/rulong_order.php', 'rulong_order');
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace RuLong\Order\Traits;
use Carbon\Carbon;
use RuLong\Order\Models\Order;
trait OrderCando
{
/**
* 是否可以审核
* @Author:<C.Jason>
* @Date:2018-10-25T16:37:32+0800
* @return boolean
*/
public function canAudit(): bool
{
return ($this->state == Order::ORDER_INIT);
}
/**
* 是否可支付
* @Author:<C.Jason>
* @Date:2018-10-22T17:13:08+0800
* @return boolean
*/
public function canPay(): bool
{
return ($this->state == Order::ORDER_UNPAID)
&& ($this->getOrderStatus('status') == 1)
&& ($this->getOrderStatus('pay') == 0);
}
/**
* 是否可取消
* @Author:<C.Jason>
* @Date:2018-10-22T17:11:14+0800
* @return boolean
*/
public function canCancel(): bool
{
return (in_array($this->state, [Order::ORDER_INIT, Order::ORDER_UNPAID]))
&& (in_array($this->getOrderStatus('status'), [0, 1]))
&& ($this->getOrderStatus('pay') == 0);
}
/**
* 可发货
* @Author:<C.Jason>
* @Date:2018-10-22T17:12:13+0800
* @return boolean
*/
public function canDeliver(): bool
{
return (in_array($this->state, [Order::ORDER_PAID, Order::ORDER_DELIVER]))
&& ($this->express_type == 1)
&& ($this->getOrderStatus('status') == 1)
&& ($this->getOrderStatus('pay') == 1)
&& ($this->getOrderStatus('deliver') == 0);
}
/**
* 可签收
* @Author:<C.Jason>
* @Date:2018-10-22T17:12:43+0800
* @return boolean
*/
public function canSingin(): bool
{
return ($this->state == Order::ORDER_DELIVERED)
&& ($this->express_type == 1)
&& ($this->getOrderStatus('status') == 1)
&& ($this->getOrderStatus('pay') == 1)
&& (in_array($this->getOrderStatus('deliver'), [1, 2]));
}
/**
* 可延迟收货
* @Author:<C.Jason>
* @Date:2018-10-25T17:17:01+0800
* @return boolean
*/
public function canDelay(): bool
{
return ($this->state == Order::ORDER_DELIVERED)
&& ($this->getOrderStatus('status') == 1)
&& ($this->getOrderStatus('pay') == 1)
&& ($this->getOrderStatus('deliver') == 2);
}
/**
* 可设置未收到
* @Author:<C.Jason>
* @Date:2018-10-25T17:17:32+0800
* @return boolean
*/
public function canUnreceive(): bool
{
return ($this->state == Order::ORDER_DELIVERED)
&& ($this->getOrderStatus('status') == 1)
&& ($this->getOrderStatus('pay') == 1)
&& (in_array($this->getOrderStatus('deliver'), [2, 3]));
}
/**
* 可完成订单
* @Author:<C.Jason>
* @Date:2018-10-25T17:35:12+0800
* @return boolean
*/
public function canComplete(): bool
{
return (in_array($this->state, [Order::ORDER_SIGNED]))
&& ($this->getOrderStatus('status') == 1)
&& (in_array($this->getOrderStatus('pay'), [1, 7]))
&& (in_array($this->getOrderStatus('deliver'), [5, 6, 8]))
&& ($this->updated_at->diffInDays(Carbon::now(), false) > config('rulong_order.completed_days'));
}
/**
* 可关闭订单
* @Author:<C.Jason>
* @Date:2018-10-25T17:37:03+0800
* @return boolean
*/
public function canClose(): bool
{
return (in_array($this->state, [Order::ORDER_INIT, Order::ORDER_UNPAID, Order::ORDER_CANCEL]))
&& (in_array($this->getOrderStatus('status'), [0, 1, 2, 3, 4]))
&& (in_array($this->getOrderStatus('pay'), [0]))
&& (in_array($this->getOrderStatus('deliver'), [0, 1]));
}
/**
* 可申请退款
* @Author:<C.Jason>
* @Date:2018-10-22T17:11:45+0800
* @return boolean
*/
public function canRefund(): bool
{
return (in_array($this->state, [Order::ORDER_PAID, Order::ORDER_DELIVER, Order::ORDER_DELIVERED, Order::ORDER_SIGNED, Order::REFUND_APPLY, Order::REFUND_AGREE, Order::REFUND_REFUSE, Order::REFUND_PROCESS, Order::REFUND_COMPLETED]))
&& ($this->getOrderStatus('status') == 1)
&& (in_array($this->getOrderStatus('pay'), [1, 2, 3, 5, 6, 7]))
&& (in_array($this->getOrderStatus('deliver'), [0, 1, 2, 3, 4, 5, 6]));
}
}

View File

@@ -0,0 +1,269 @@
<?php
namespace RuLong\Order\Traits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use RuLong\Order\Events\OrderCanceled;
use RuLong\Order\Events\OrderClosed;
use RuLong\Order\Events\OrderCompleted;
use RuLong\Order\Events\OrderDelaied;
use RuLong\Order\Events\OrderDelivered;
use RuLong\Order\Events\OrderPaid;
use RuLong\Order\Events\OrderSignined;
use RuLong\Order\Events\OrderUnreceived;
use RuLong\Order\Exceptions\OrderException;
use RuLong\Order\Models\Order;
trait OrderHasActions
{
/**
* 订单审核
* @Author:<C.Jason>
* @Date:2018-10-25T16:18:05+0800
* @param boolean $result 审核结果
* @return OrderException|boolean
*/
public function audit(bool $result)
{
if (!$this->canAudit()) {
throw new OrderException("订单状态不可审核");
}
if ($result === true) {
// 审核通过,设置订单为 未支付状态,分状态 进行中
$this->state = Order::ORDER_UNPAID;
$this->setOrderStatus('status', 1);
} else {
// 审核不通过,设置订单为 取消状态,分状态 系统取消
$this->state = Order::ORDER_CANCEL;
$this->setOrderStatus('status', 4);
}
return $this->save();
}
/**
* 标记订单已支付状态
* @Author:<C.Jason>
* @Date:2018-10-22T10:16:02+0800
* @return OrderException|boolean
*/
public function paid()
{
if (!$this->canPay()) {
throw new OrderException("订单状态不可支付");
}
$this->setOrderStatus('pay', 1);
$this->state = Order::ORDER_PAID;
$this->paid_at = Carbon::now();
$this->save();
event(new OrderPaid($this));
return true;
}
/**
* 取消订单
* @Author:<C.Jason>
* @Date:2018-10-25T16:57:43+0800
* @param integer $channel 订单取消渠道
* @return OrderException|boolean
*/
public function cancel(int $channel)
{
if (!$this->canCancel()) {
throw new OrderException("订单状态不可取消");
}
$this->setOrderStatus('status', $channel);
$this->state = Order::ORDER_CANCEL;
$this->save();
event(new OrderCanceled($this));
return true;
}
/**
* 标记发货处理中
* @Author:<C.Jason>
* @Date:2018-10-22T13:27:34+0800
* @return void
*/
public function delivering()
{
if (!$this->canDeliver()) {
throw new OrderException('订单状态不可发货');
}
if ($this->state = Order::ORDER_PAID) {
$this->state = Order::ORDER_DELIVER;
$this->save();
}
}
/**
* 订单发货
* @Author:<C.Jason>
* @Date:2018-10-25T17:14:53+0800
* @param string|null $company 物流名称
* @param string|null $number 物流单号
* @return OrderException|boolean
*/
public function deliver($company = null, $number = null)
{
if (!$this->canDeliver()) {
throw new OrderException('订单状态不可发货');
}
DB::transaction(function () use ($company, $number) {
if ($this->express) {
$this->express->update([
'company' => $company,
'number' => $number,
'deliver_at' => Carbon::now(),
]);
$this->setOrderStatus('deliver', 2);
} else {
$this->setOrderStatus('deliver', 1);
}
$this->state = Order::ORDER_DELIVERED;
$this->save();
event(new OrderDelivered($this));
});
return true;
}
/**
* 签收订单
* @Author:<C.Jason>
* @Date:2018-10-22T13:47:06+0800
* @return OrderException|boolean
*/
public function signin()
{
if (!$this->canSingin()) {
throw new OrderException('订单状态不可签收');
}
DB::transaction(function () {
if ($this->express) {
$this->express->update([
'receive_at' => Carbon::now(),
]);
}
$this->setOrderStatus('deliver', 5);
$this->state = Order::ORDER_SIGNED;
$this->save();
event(new OrderSignined($this));
});
return true;
}
/**
* 延迟收货
* @Author:<C.Jason>
* @Date:2018-10-22T14:09:15+0800
* @return OrderException|boolean
*/
public function delay()
{
if (!$this->canDelay()) {
throw new OrderException('订单状态不可延迟收货');
}
$this->setOrderStatus('deliver', 3);
$this->save();
event(new OrderDelaied($this));
return true;
}
/**
* 未收到
* @Author:<C.Jason>
* @Date:2018-10-22T14:11:42+0800
* @return OrderException|boolean
*/
public function unreceive()
{
if (!$this->canUnreceive()) {
throw new OrderException('订单状态不可延迟收货');
}
$this->setOrderStatus('deliver', 4);
$this->save();
event(new OrderUnreceived($this));
return true;
}
/**
* 标记订单完成状态
* @Author:<C.Jason>
* @Date:2018-10-25T17:28:01+0800
* @return OrderException|boolean
*/
public function complete()
{
if (!$this->canComplete()) {
throw new OrderException('订单状态不可完成');
}
$this->setOrderStatus('status', 9);
$this->state = Order::ORDER_COMPLETED;
$this->save();
event(new OrderCompleted($this));
return true;
}
/**
* 关闭订单
* @Author:<C.Jason>
* @Date:2018-10-22T14:14:34+0800
* @return OrderException|boolean
*/
public function close()
{
if (!$this->canClose()) {
throw new OrderException('订单状态不可关闭');
}
$this->setOrderStatus('status', 8);
$this->state = Order::ORDER_CLOSED;
$this->save();
event(new OrderClosed($this));
return true;
}
/**
* 申请退款,创建退款单
* @Author:<C.Jason>
* @Date:2018-10-23T14:10:54+0800
* @param array $items 退款项目
* [
* ['item_id' => integer, 'number' => integer],
* ['item_id' => integer, 'number' => integer],
* ]
* @param float $total 申请退款金额
*/
public function createRefund(array $items, float $total = null)
{
return \Refunds::create($order, $items, $total);
}
}

View File

@@ -0,0 +1,226 @@
<?php
namespace RuLong\Order\Traits;
use RuLong\Order\Models\Order;
trait OrderHasAttributes
{
/**
* 设置订单分状态
* @Author:<C.Jason>
* @Date:2018-10-22T10:40:44+0800
* @param [type] $type [description]
* @param [type] $change [description]
*/
public function setOrderStatus($type, $change)
{
$status = sprintf('%04d', $this->status);
switch ($type) {
case 'status':
$this->status = substr_replace($status, $change, 0, 1);
break;
case 'pay':
$this->status = substr_replace($status, $change, 1, 1);
break;
case 'deliver':
$this->status = substr_replace($status, $change, 2, 1);
break;
case 'comment':
$this->status = substr_replace($status, $change, 3, 1);
break;
}
}
/**
* 获取订单分状态
* @Author:<C.Jason>
* @Date:2018-10-22T10:40:52+0800
* @param [type] $type [description]
* @param [type] $change [description]
* @return [type] [description]
*/
public function getOrderStatus($type)
{
$status = sprintf('%04d', $this->status);
switch ($type) {
case 'status':
return substr($status, 0, 1) ?: 0;
break;
case 'pay':
return substr($status, 1, 1) ?: 0;
break;
case 'deliver':
return substr($status, 2, 1) ?: 0;
break;
case 'comment':
return substr($status, 3, 1) ?: 0;
break;
}
}
/**
* 获取订单总金额,使用 bcmath 来确保运算精度
* @Author:<C.Jason>
* @Date:2018-10-19T11:19:55+0800
* @return float
*/
public function getTotalAttribute(): string
{
return bcadd($this->amount, $this->freight, 3);
}
/**
* 获取订单详细状态
* 订单状态;金钱状态;发货状态;评论状态,
* @Author:<C.Jason>
* @Date:2018-10-19T10:57:27+0800
* @return string
*/
protected function getStatusTextAttribute(): string
{
$status = $this->getOrderStatus('status');
$pay = $this->getOrderStatus('pay');
$deliver = $this->getOrderStatus('deliver');
$comment = $this->getOrderStatus('comment');
$statusStatus = [
0 => '初始化;',
1 => '进行中;',
2 => '买家取消;',
3 => '卖家取消;',
4 => '系统取消;',
8 => '已关闭;',
9 => '已完成;',
];
$payStatus = [
0 => '未支付;',
1 => '已支付;',
2 => '仅退货款;',
3 => '部分退款;',
4 => '全额退款;',
5 => '拒绝退款;',
6 => '退款中;',
7 => '退款完成;',
];
$deliverStatus = [
0 => '未发货;',
1 => '无需发货;',
2 => '已发货;',
3 => '延迟收货;',
4 => '未收到;',
5 => '已签收;',
6 => '无需退货;',
7 => '退货中;',
8 => '收到退货;',
9 => '未收到退货;',
];
$commentStatus = [
0 => '未评价;',
1 => '买家已评;',
2 => '卖家已评;',
3 => '双方互评;',
];
$statusText = $statusStatus[$status] ?? $statusStatus[0];
$payText = $payStatus[$pay] ?? $payStatus[0];
$deliverText = $deliverStatus[$deliver] ?? $deliverStatus[0];
$commentText = $commentStatus[$comment] ?? $commentStatus[0];
return $statusText . $payText . $deliverText . $commentText;
}
/**
* 获取订单状态 $this->state_text
* @Author:<C.Jason>
* @Date:2018-10-19T10:56:24+0800
* @return string
*/
protected function getStateTextAttribute(): string
{
switch ($this->state) {
case Order::ORDER_INIT:
$state = '订单初始化';
break;
case Order::ORDER_UNPAID:
$state = '待支付';
break;
case Order::ORDER_PAID:
$state = '已支付';
break;
case Order::ORDER_DELIVER:
$state = '发货处理中';
break;
case Order::ORDER_DELIVERED:
$state = '已发货';
break;
case Order::ORDER_SIGNED:
$state = '已签收';
break;
case Order::ORDER_COMPLETED:
$state = '已完成';
break;
case Order::ORDER_CLOSED:
$state = '已关闭';
break;
case Order::ORDER_CANCEL:
$state = '已取消';
break;
case Order::REFUND_APPLY:
$state = '申请退款';
break;
case Order::REFUND_AGREE:
$state = '同意退款';
break;
case Order::REFUND_PROCESS:
$state = '退款中';
break;
case Order::REFUND_COMPLETED:
$state = '退款完毕';
break;
case Order::REFUND_REFUSE:
$state = '拒绝退款';
break;
default:
$state = '未知状态';
break;
}
return $state;
}
public function getTypeTextAttribute()
{
switch ($this->type) {
case 'MEMBER':
return '会员商城';
break;
case 'PICK':
return '提货商城';
break;
default:
return '未知';
break;
}
}
//是否是手机号订单
public function getIsMobileAttribute()
{
foreach ($this->details as $key => $detail) {
if ($detail->item_type == 'App\Models\Mobile') {
return true;
break;
}
}
return false;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace RuLong\Order\Traits;
use RuLong\Order\Models\Order;
trait OrderHasScopes
{
public function scopeUnpaid($query)
{
return $query->where('state', Order::ORDER_UNPAID);
}
public function scopeUnDeliver($query)
{
return $query->whereIn('state', [Order::ORDER_PAID, Order::ORDER_DELIVER]);
}
public function scopeDelivered($query)
{
return $query->where('state', Order::ORDER_DELIVERED);
}
public function scopeSigned($query)
{
return $query->where('state', Order::ORDER_SIGNED);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace RuLong\Order\Traits;
use RuLong\Order\Models\Order;
use RuLong\Order\Models\Refund;
trait RefundCando
{
/**
* 可同意退款
* @Author:<C.Jason>
* @Date:2018-10-26T11:18:00+0800
* @return boolean
*/
public function canAgree(): bool
{
return ($this->state == Refund::REFUND_APPLY)
&& ($this->order->state == Order::REFUND_APPLY)
&& ($this->order->getOrderStatus('status') == 1)
&& (in_array($this->order->getOrderStatus('pay'), [2, 3, 4]))
&& (in_array($this->order->getOrderStatus('deliver'), [0, 1, 2, 3, 4, 5, 6]));
}
/**
* 可以拒绝退款
* @Author:<C.Jason>
* @Date:2018-10-26T16:13:46+0800
* @return boolean
*/
public function canRefuse(): bool
{
return $this->canAgree();
}
/**
* 可以退货
* @Author:<C.Jason>
* @Date:2018-10-26T16:13:56+0800
* @return boolean
*/
public function canDeliver(): bool
{
return ($this->state == Refund::REFUND_AGREE)
&& ($this->order->state == Order::REFUND_AGREE)
&& ($this->order->getOrderStatus('status') == 1)
&& ($this->order->getOrderStatus('pay') == 6)
&& ($this->order->getOrderStatus('deliver') == 7);
}
public function canReceive(): bool
{
return true;
}
public function canUnreceive(): bool
{
return true;
}
/**
* 是否可以完成退款流程
* 完成之后可以走退款接口了
* @Author:<C.Jason>
* @Date:2018-10-26T16:28:21+0800
* @return boolean
*/
public function canComplete(): bool
{
return (
($this->state == Refund::REFUND_PROCESS)
&& ($this->order->state == Order::REFUND_PROCESS)
&& ($this->order->getOrderStatus('status') == 1)
&& ($this->order->getOrderStatus('pay') == 6)
&& ($this->order->getOrderStatus('deliver') == 8)
) || (
($this->state == Refund::REFUND_AGREE)
&& ($this->order->getOrderStatus('status') == 1)
&& ($this->order->getOrderStatus('pay') == 6)
&& ($this->order->getOrderStatus('status') == 6)
);
return true;
}
/**
* 是否可以取消退款单
* @Author:<C.Jason>
* @Date:2018-10-26T16:28:52+0800
* @return [type] [description]
*/
public function canCancel(): bool
{
return true;
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace RuLong\Order\Traits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use RuLong\Order\Events\RefundAgreed;
use RuLong\Order\Events\RefundCompleted;
use RuLong\Order\Events\RefundProcessed;
use RuLong\Order\Events\RefundRefused;
use RuLong\Order\Exceptions\RefundException;
use RuLong\Order\Models\Order;
use RuLong\Order\Models\Refund;
trait RefundHasActions
{
/**
* 取消退款单,想办法变回原来的状态
* @Author:<C.Jason>
* @Date:2018-10-26T11:27:29+0800
* @return [type] [description]
*/
public function cancel()
{
if (!$this->canCancel()) {
throw new OrderException("退款单状态不可取消");
}
}
/**
* 同意退款
* @Author:<C.Jason>
* @Date:2018-10-23T14:39:04+0800
* @return RefundException|boolean
*/
public function agree()
{
if (!$this->canAgree()) {
throw new OrderException("退款单状态不可以同意退款");
}
DB::transaction(function () {
$this->state = Refund::REFUND_AGREE;
$this->actual_total = $this->refund_total;
$this->save();
if (in_array($this->order->getOrderStatus('deliver'), [2, 3, 5])) {
if ($this->order->express) {
// 如果已经发货,签收的,创建一个退款物流订单
$this->express()->create();
}
$this->order->setOrderStatus('deliver', 7);
} elseif (in_array($this->order->getOrderStatus('deliver'), [0, 1, 4])) {
$this->order->setOrderStatus('deliver', 6);
}
$this->order->setOrderStatus('pay', 6);
$this->order->state = Order::REFUND_AGREE;
$this->order->save();
event(new RefundAgreed($this));
});
return true;
}
/**
* 拒绝退款
* @Author:<C.Jason>
* @Date:2018-10-23T14:40:07+0800
* @param string|null $remark 拒绝原因
* @return RefundException|boolean
*/
public function refuse(string $remark = null)
{
if (!$this->canRefuse()) {
throw new OrderException("退款单状态不可以拒绝退款");
}
DB::transaction(function () use ($remark) {
$this->state = Refund::REFUND_REFUSE;
$this->remark = $remark;
$this->save();
$this->order->setOrderStatus('pay', 5);
$this->order->state = Order::REFUND_REFUSE;
$this->order->save();
event(new RefundRefused($this));
});
return true;
}
/**
* 退货退款中
* @Author:<C.Jason>
* @Date:2018-10-23T14:40:29+0800
* @return RefundException|boolean
*/
public function deliver($company = null, $number = null)
{
if (!$this->canDeliver()) {
throw new OrderException("退款单状态不可以拒绝退款");
}
DB::transaction(function () {
$this->state = Refund::REFUND_PROCESS;
$this->refunded_at = Carbon::now();
$this->save();
$this->order->setOrderStatus('pay', 6);
$this->order->state = Order::REFUND_PROCESS;
$this->order->save();
event(new RefundProcessed($this));
});
return true;
}
/**
* 确认收货
* @Author:<C.Jason>
* @Date:2018-10-22T14:11:42+0800
* @return OrderException|boolean
*/
public function receive()
{
if (!$this->canReceive()) {
throw new OrderException('退款单状态不可以确认收货');
}
$this->order->setOrderStatus('deliver', 8);
$this->order->save();
return true;
}
/**
* 未收到
* @Author:<C.Jason>
* @Date:2018-10-22T14:11:42+0800
* @return OrderException|boolean
*/
public function unreceive()
{
if (!$this->canUnreceive()) {
throw new OrderException('退款单状态不可以未收到商品');
}
$this->order->setOrderStatus('deliver', 9);
$this->order->save();
return true;
}
/**
* 标记退款完成
* @Author:<C.Jason>
* @Date:2018-10-23T14:40:36+0800
* @return RefundException|boolean
*/
public function complete()
{
if (!$this->canComplete()) {
throw new OrderException("订单状态不可审核");
}
DB::transaction(function () {
$this->state = Refund::REFUND_COMPLETED;
$this->save();
$this->order->state = Order::REFUND_COMPLETED;
$this->order->save();
event(new RefundCompleted($this));
});
return true;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace RuLong\Order\Traits;
use RuLong\Order\Models\Order;
trait UserHasOrders
{
/**
* 用户订单
* @Author:<C.Jason>
* @Date:2018-10-15T14:56:07+0800
* @return \RuLong\Order\Models\Order::class
*/
public function orders()
{
return $this->hasOne(hasMany::class);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace RuLong\Order\Utils;
class Helper
{
/**
* 创建一个订单号
* @Author:<C.Jason>
* @Date:2018-10-23T13:50:33+0800
* @param integer $length 订单号长度
* @param string $prefix 订单号前缀
* @return string
*/
public static function orderid($length = 20, $prefix = '')
{
if ($length > 30) {
$length = 30;
}
$fixed = $length - 12;
if (strlen($prefix) >= $fixed) {
$prefix = substr($prefix, 0, $fixed);
}
$code = date('ymdHis') . sprintf("%0" . $fixed . "d", mt_rand(0, pow(10, $fixed) - 1));
if (!empty($prefix)) {
$code = $prefix . substr($code, 0, $length - strlen($prefix));
}
return $code;
}
}

31
vendor/rulong/sms/README.md vendored Normal file
View File

@@ -0,0 +1,31 @@
# RuLong/Sms 短信发送扩展
## 安装
```
$ composer require rulong/sms
```
## 短信发送
```
\Sms::send($mobile, $channel = 'DEFAULT');
```
## 短信验证
```
\Sms::check($mobile, $code, $channel = 'DEFAULT');
```
扩展验证规则
```
mobile 验证是否是合法的手机号
sms_check:MOBILEFIELD,CHANNEL 短信验证码验证
MOBILEFIELD手机号码字段名称
CHANNEL验证通道
```

31
vendor/rulong/sms/composer.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "rulong/sms",
"description": "",
"license": "MIT",
"authors": [
{
"name": "C.Jason",
"email": "chenjxlg@163.com"
}
],
"require": {
"php": ">=7.1.3",
"overtrue/easy-sms": "^1.1",
"laravel/framework": "*"
},
"autoload": {
"psr-0": {
"RuLong\\Sms": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"RuLong\\Sms\\ServiceProvider"
],
"aliases": {
"Sms": "RuLong\\Sms\\Facades\\Sms"
}
}
}
}

58
vendor/rulong/sms/config/rulong_sms.php vendored Normal file
View File

@@ -0,0 +1,58 @@
<?php
return [
// 模拟调试,不会真正发送验证码,验证后也不会失效
'debug' => true,
// HTTP 请求的超时时间(秒)
'timeout' => 5.0,
// 默认发送配置
'default' => [
// 网关调用策略,默认:顺序调用
'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,
// 默认可用的发送网关
'gateways' => [
'aliyun',
],
],
// 验证码长度
'length' => 4,
// 验证后立即失效
'once_used' => true,
// 模板与通道映射
'template' => [
'DEFAULT' => '',
'LOGIN' => '',
'REGISTER' => '',
],
// 可用的网关配置
'gateways' => [
'errorlog' => [
'file' => storage_path('logs/easy-sms.log'),
],
// 阿里云 AccessKeyID
'aliyun' => [
'access_key_id' => '',
'access_key_secret' => '',
'sign_name' => '',
],
// 阿里云Rest
'aliyunrest' => [
'app_key' => '',
'app_secret_key' => '',
'sign_name' => '',
],
// 云片
'yunpian' => [
'api_key' => '',
'signature' => '',
],
// ... 具体参数请参考 https://github.com/overtrue/easy-sms/blob/master/README.md
],
];

View File

@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSmsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('sms', function (Blueprint $table) {
$table->increments('id');
$table->string('mobile', 16);
$table->string('channel', 16);
$table->string('code', 16);
$table->boolean('used')->default(0);
$table->timestamps();
$table->index(['mobile', 'channel']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('sms');
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace RuLong\Sms\Exceptions;
use Exception;
class SmsSendException extends Exception
{
}

85
vendor/rulong/sms/src/Facade.php vendored Normal file
View File

@@ -0,0 +1,85 @@
<?php
namespace RuLong\Sms;
use Illuminate\Support\Facades\DB;
use Overtrue\EasySms\EasySms;
use RuLong\Sms\Exceptions\SmsSendException;
use RuLong\Sms\Models\Sms;
class Facade
{
/**
* 发送短信
* @Author:<C.Jason>
* @Date:2018-11-07T14:25:37+0800
* @param string $mobile 手机号码
* @param string $channel 验证通道
* @return [type] [description]
*/
public function send(string $mobile, string $channel = 'DEFAULT')
{
try {
$config = config('rulong_sms');
if (!isset($config['template'][$channel]) || empty($config['template'][$channel])) {
throw new SmsSendException('不合法的验证通道');
}
DB::transaction(function () use ($mobile, $channel, $config) {
$code = sprintf("%0" . $config['length'] . "d", mt_rand(1, pow(10, $config['length']) - 1));
if ($config['debug'] != true) {
$easySms = new EasySms($config);
$easySms->send($mobile, [
'template' => $config['template'][$channel],
'data' => [
'code' => $code,
],
]);
}
Sms::create([
'mobile' => $mobile,
'channel' => $channel,
'code' => $code,
]);
});
return true;
} catch (\Exception $e) {
throw new SmsSendException($e->getMessage());
}
}
/**
* 验证短信
* @Author:<C.Jason>
* @Date:2018-11-07T14:26:38+0800
* @param string $mobile [description]
* @param string $code [description]
* @param string $channel [description]
* @return
*/
public function check(string $mobile, string $code, string $channel = 'DEFAULT')
{
$Sms = Sms::where('mobile', $mobile)->where('channel', $channel)->orderBy('id', 'desc')->first();
if ($Sms) {
if ($Sms->code == $code) {
if ($Sms->used == 1 && config('rulong_sms.once_used') && config('rulong_sms.debug') == false) {
return false;
}
$Sms->used = 1;
$Sms->save();
return true;
} else {
return false;
}
} else {
return false;
}
}
}

13
vendor/rulong/sms/src/Facades/Sms.php vendored Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\Sms\Facades;
use Illuminate\Support\Facades\Facade;
class Sms extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\Sms\Facade::class;
}
}

28
vendor/rulong/sms/src/Models/Sms.php vendored Normal file
View File

@@ -0,0 +1,28 @@
<?php
namespace RuLong\Sms\Models;
use Illuminate\Database\Eloquent\Model;
class Sms extends Model
{
protected $guarded = [];
public static function verify_code($mobile)
{
$minutes = date('Y-m-d H:i:s', time() - 60);
$hours = date('Y-m-d H:i:s', time() - 3600);
$days = date('Y-m-d H:i:s', time() - 86400);
if (self::where('mobile', $mobile)->where('created_at', '>', $minutes)->count() >= 1) {
return '一分钟内仅可获取一条';
} elseif (self::where('mobile', $mobile)->where('created_at', '>', $hours)->count() >= 5) {
return '一小时内仅可获取五条';
} elseif (self::where('mobile', $mobile)->where('created_at', '>', $days)->count() >= 10) {
return '一天只能获取十条';
} else {
return true;
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace RuLong\Sms;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
class ServiceProvider extends LaravelServiceProvider
{
/**
* 部署时加载
* @Author:<C.Jason>
* @Date:2018-06-22T16:01:20+0800
* @return [type] [description]
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([__DIR__ . '/../config/rulong_sms.php' => config_path('rulong_sms.php')]);
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations/');
}
/**
* 短信验证码验证
*/
Validator::extend('sms_check', function ($attribute, $code, $parameters) {
if (empty($code)) {
return false;
}
$mobileFiled = $parameters[0] ?? 'mobile';
$channel = $parameters[1] ?? 'DEFAULT';
$mobile = request()->input($mobileFiled);
return \Sms::check($mobile, $code, $channel);
});
/**
* 手机号验证
*/
Validator::extend('mobile', function ($attribute, $mobile, $parameters) {
if (preg_match("/^1[3578]{1}[0-9]{9}$|14[57]{1}[0-9]{8}$|^[0][9]\d{8}$/", $mobile)) {
return true;
} else {
return false;
}
});
}
/**
* 注册服务提供者
* @Author:<C.Jason>
* @Date:2018-06-22T16:01:12+0800
* @return [type] [description]
*/
public function register()
{
$this->mergeConfigFrom(__DIR__ . '/../config/rulong_sms.php', 'rulong_sms');
}
}

92
vendor/rulong/user-account/README.md vendored Normal file
View File

@@ -0,0 +1,92 @@
# 用户账户管理
## 1.安装
> composer require rulong/user-account
## 2.创建配置文件
> php artisan vendor:publish --provider="RuLong\UserAccount\ServiceProvider"
## 3.创建数据库
> php artisan migrate
## 4.初始化账户
> php artisan user:account
## 5.在系统中使用
### 1.Trait模式
~~~
use RuLong\UserAccount\Traits\UserHasAccount;
class User extends Authenticatable
{
use UserHasAccount;
public $guarded = [];
}
~~~
#### 可用方法
~~~
// 执行规则
$user->rule($rule, $variable = 0, $frozen = true, $source = []);
// 转账
$user->transfer($toUser, $type, $variable);
// 增余额
$user->increase($type, $variable);
// 减余额
$user->decrease($type, $variable);
~~~
### 2.Facade模式
~~~
// 执行规则
Account::executeRule($user, $rule, $variable = 0, $frozen = false, $source = []);
// 解冻
Account::thaw(UserAccountLog $log);
// 冻结
Account::frozen(UserAccountLog $log);
// 转账
Account::transfer($fromUser, $toUser, $type, $variable);
// 增余额
Account::increase($user, $type, $variable);
// 减余额
Account::decrease($user, $type, $variable);
~~~
## 6.规则管理
Facade模式
~~~
$data = [
'title' => $title, // string 规则名称
'name' => $name, // string 调用标记
'type' => $type, // 账户类型,参见配置文件
'variable' => $variable, // numeric 增减变量
'trigger' => $trigger, // 执行次数限制小于0则终身一次等于0不限制大于0每日N次
'deductions' => $deductions, // 0|1 直接处理,不冻结
'remark' => $remark, // nullable 备注信息
];
// 新增规则
AccountRule::store($data);
// 更新规则
AccountRule::update(UserAccountRule $rule, $data);
// 删除规则
AccountRule::destroy($id);
~~~
## 7.事件
~~~
// 扣除账户余额
AccountDecreased($account, $log);
// 增加账户余额
AccountIncreased($account, $log);
// 账户记录冻结
AccountLogFrozened($log);
// 账户记录解冻
AccountLogThawed($account, $log);
// 账户规则执行完毕
AccountRuleExecuted($account, $log);
// 账户转账完成
AccountTransfered($fromUser, $toUser, $fromLog, $toLog);
// 用户注册完成
UserCreated($user);
~~~

View File

@@ -0,0 +1,31 @@
{
"name": "rulong/user-account",
"description": "",
"license": "MIT",
"authors": [
{
"name": "C.Jason",
"email": "chenjxlg@163.com"
}
],
"require": {
"php": ">=7.1.3",
"laravel/framework": "*"
},
"autoload": {
"psr-0": {
"RuLong\\UserAccount": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"RuLong\\UserAccount\\ServiceProvider"
],
"aliases": {
"Account": "RuLong\\UserAccount\\Facades\\Account",
"AccountRule": "RuLong\\UserAccount\\Facades\\AccountRule"
}
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* 用户关系扩展配置
*/
return [
/**
* 用户模型
*/
'user_model' => \App\Models\User::class,
/**
* 账户是否可以为负数
*/
'can_minus' => [
'cash' => false,
'score' => true,
'act_a' => true,
'act_b' => true,
'act_c' => true,
'act_d' => true,
],
/**
* 是否立即扣款
*/
'deductions' => false,
/**
* 账户类型
*/
'account_type' => [
'cash' => '现金账户',
'score' => '积分账户',
'act_a' => '预留账户',
'act_b' => '预留账户',
'act_c' => '预留账户',
'act_d' => '预留账户',
],
];

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateUserAccountLogsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_account_logs', function(Blueprint $table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->integer('rule_id')->unsigned();
$table->string('type', 20);
$table->decimal('variable', 20, 3);
$table->decimal('balance', 20, 3);
$table->boolean('frozen')->default(0);
$table->text('source', 65535)->nullable();
$table->timestamps();
$table->index(['user_id','rule_id','created_at'], 'trigger_key');
$table->index(['user_id','frozen'], 'user_frozen');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('user_account_logs');
}
}

View File

@@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateUserAccountRulesTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_account_rules', function(Blueprint $table)
{
$table->increments('id');
$table->string('title', 50);
$table->string('name', 50)->index('name');
$table->string('type', 20);
$table->decimal('variable', 20, 3)->default(0.000);
$table->integer('trigger')->default(0);
$table->boolean('deductions')->default(0);
$table->string('remark')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('user_account_rules');
}
}

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateUserAccountsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_accounts', function(Blueprint $table)
{
$table->integer('user_id')->unsigned()->primary();
$table->decimal('cash', 20, 3)->default(0.000);
$table->decimal('score', 20, 3)->default(0.000);
$table->decimal('act_a', 20, 3)->default(0.000);
$table->decimal('act_b', 20, 3)->default(0.000);
$table->decimal('act_c', 20, 3)->default(0.000);
$table->decimal('act_d', 20, 3)->default(0.000);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('user_accounts');
}
}

View File

@@ -0,0 +1,324 @@
<?php
namespace RuLong\UserAccount;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use RuLong\UserAccount\Events\AccountDecreased;
use RuLong\UserAccount\Events\AccountIncreased;
use RuLong\UserAccount\Events\AccountLogFrozened;
use RuLong\UserAccount\Events\AccountLogThawed;
use RuLong\UserAccount\Events\AccountRuleExecuted;
use RuLong\UserAccount\Events\AccountTransfered;
use RuLong\UserAccount\Exceptions\ExecuteRuleException;
use RuLong\UserAccount\Models\UserAccountLog;
use RuLong\UserAccount\Models\UserAccountRule;
class Account
{
/**
* 判断是否可执行,并且执行规则
* @Author:<C.Jason>
* @Date:2018-10-15T16:52:44+0800
* @param User $user 用户模型
* @param string|integer $rule 账户规则
* @param numeric $variable 自定义增减量
* @param boolean $frozen 是否冻结
* @param array $source 溯源信息
* @return
*/
public function executeRule($user, $rule, $variable = 0, $frozen = false, $source = [])
{
$this->isUserModel($user);
if (is_numeric($rule)) {
$rule = UserAccountRule::find($rule);
} else {
$rule = UserAccountRule::where('name', $rule)->first();
}
if (!$rule) {
throw new ExecuteRuleException('账户规则不存在');
}
if ($rule->trigger == 0) {
// 不限制执行的
return $this->accountExecute($user, $rule, $variable, $frozen, $source);
} elseif ($rule->trigger > $user->account->logs()->where('rule_id', $rule->id)->whereDate('created_at', Carbon::today())->count()) {
// 每日执行 trigger 次
return $this->accountExecute($user, $rule, $variable, $frozen, $source);
} elseif ($rule->trigger < 0 && !$user->account->logs()->where('rule_id', $rule->id)->first()) {
// 终身只能执行一次
return $this->accountExecute($user, $rule, $variable, $frozen, $source);
}
throw new ExecuteRuleException('达到最大可执行次数');
}
/**
* 账户记录解冻
* @Author:<C.Jason>
* @Date:2018-10-16T09:16:13+0800
* @param UserAccountLog $log 账户记录
* @return ExecuteRuleException|boolean
*/
public function thaw(UserAccountLog $log)
{
try {
if ($log->frozen == 1) {
DB::transaction(function () use ($log) {
$log->account->increment($log->type, $log->variable);
$log->frozen = 0;
$log->balance = $log->account->{$log->type};
$log->save();
});
event(new AccountLogThawed($log));
return true;
} else {
throw new ExecuteRuleException('账目已解冻');
}
} catch (\Exception $e) {
throw new ExecuteRuleException($e->getMessage());
}
}
/**
* 冻结一条账户记录
* @Author:<C.Jason>
* @Date:2018-10-16T09:40:23+0800
* @param UserAccountLog $log 账户记录
* @return ExecuteRuleException|boolean
*/
public function frozen(UserAccountLog $log)
{
try {
if ($log->frozen == 0) {
DB::transaction(function () use ($log) {
$log->account->decrement($log->type, $log->variable);
$log->frozen = 1;
$log->balance = $log->account->{$log->type};
$log->save();
});
event(new AccountLogFrozened($log));
return true;
} else {
throw new ExecuteRuleException('账目已冻结');
}
} catch (\Exception $e) {
throw new ExecuteRuleException($e->getMessage());
}
}
/**
* 转账给用户
* @Author:<C.Jason>
* @Date:2018-10-16T09:47:35+0800
* @param User $fromUser 发起转账用户
* @param User $toUser 目标用户
* @param string $type 账户类型
* @param numeric $variable 自定义转账量
* @return ExecuteRuleException|boolean
*/
public function transfer($fromUser, $toUser, $type, $variable)
{
$this->isUserModel($fromUser);
$this->isUserModel($toUser);
$this->isLegalType($type);
if ($variable <= 0) {
throw new ExecuteRuleException('转账金额不能为负数');
}
if (($fromUser->account->{$type}) - $variable < 0) {
throw new ExecuteRuleException('【 ' . config('user_account.account_type')[$type] . ' 】 余额不足');
}
DB::transaction(function () use ($fromUser, $toUser, $type, $variable) {
$feature = Str::uuid();
$fromUser->account->decrement($type, $variable);
$fromLog = [
'rule_id' => 0,
'type' => $type,
'variable' => -$variable,
'frozen' => 0,
'balance' => $fromUser->account->{$type},
'source' => ['type' => 'transfer', 'fromUser' => $fromUser->id, 'toUser' => $toUser->id, 'feature' => $feature],
];
$fromUser->account->logs()->create($fromLog);
$toUser->account->increment($type, $variable);
$toLog = [
'rule_id' => 0,
'type' => $type,
'variable' => $variable,
'frozen' => 0,
'balance' => $toUser->account->{$type},
'source' => ['type' => 'transfer', 'fromUser' => $fromUser->id, 'toUser' => $toUser->id, 'feature' => $feature],
];
$toUser->account->logs()->create($toLog);
event(new AccountTransfered($fromUser->account, $toUser->account, $fromLog, $toLog));
});
return true;
}
/**
* 增加账户余额
* @Author:<C.Jason>
* @Date:2018-10-16T10:20:58+0800
* @param User $user 用户模型
* @param string $type 账户类型
* @param numeric $variable 自定义增减量
* @return ExecuteRuleException|boolean
*/
public function increase($user, $type, $variable)
{
$this->isUserModel($user);
$this->isLegalType($type);
DB::transaction(function () use ($user, $type, $variable) {
$user->account->increment($type, $variable);
$log = [
'rule_id' => 0,
'type' => $type,
'variable' => $variable,
'frozen' => 0,
'balance' => $user->account->{$type},
'source' => ['type' => 'increase'],
];
$user->account->logs()->create($log);
event(new AccountIncreased($this->account, $log));
});
return true;
}
/**
* 扣除账户金额
* @Author:<C.Jason>
* @Date:2018-10-16T09:49:36+0800
* @param User $user 用户模型
* @param string $type 账户类型
* @param numeric $variable 自定义增减量
* @return ExecuteRuleException|boolean
*/
public function decrease($user, $type, $variable)
{
$this->isUserModel($user);
$this->isLegalType($type);
// 如果账户类型不可以为负数
if (config('user_account.can_minus')[$type] === false && ($user->account->$type + $variable < 0)) {
throw new ExecuteRuleException('【 ' . config('user_account.account_type')[$type] . ' 】 余额不足');
}
DB::transaction(function () use ($user, $type, $variable) {
$user->account->decrement($type, $variable);
$log = [
'rule_id' => 0,
'type' => $type,
'variable' => -$variable,
'frozen' => 0,
'balance' => $user->account->{$type},
'source' => ['type' => 'deduct'],
];
$user->account->logs()->create($log);
event(new AccountDecreased($this->account, $log));
});
return true;
}
/**
* 判断模型是否是用户模型
* @Author:<C.Jason>
* @Date:2018-10-16T10:01:55+0800
* @param object $model 被判断的模型
* @return ExecuteRuleException|void
*/
private function isUserModel($model)
{
$userModel = config('user_account.user_model');
if (!($model instanceof $userModel)) {
throw new ExecuteRuleException('不正确的用户模型');
}
}
/**
* 判断是否是合法的账户类型
* @Author:<C.Jason>
* @Date:2018-10-16T10:43:41+0800
* @param string $type 账户类型
* @return ExecuteRuleException|void
*/
private function isLegalType($type)
{
$typeList = config('user_account.account_type');
if (!in_array($type, array_keys($typeList))) {
throw new ExecuteRuleException('不合法的账户类型');
}
}
/**
* 账户增减的实际操作
* @Author:<C.Jason>
* @Date:2018-05-30
* @param User $user 用户模型
* @param AccountRule $rule 规则模型
* @param numeric $variable 自定义增减量
* @param boolean $frozen 是否冻结
* @param array $source 溯源信息
* @return ExecuteRuleException|boolean
*/
private function accountExecute($user, UserAccountRule $rule, $variable, $frozen, $source)
{
try {
if ($variable != 0) {
$rule->variable = $variable;
}
// 账户余额不允许为负数的时候判断余额是否充足
if ((config('user_account.can_minus')[$rule->type] == false) && ($rule->variable < 0) && ($rule->variable + $user->account->{$rule->type} < 0)) {
throw new ExecuteRuleException('【 ' . config('user_account.account_type')[$rule->type] . ' 】 余额不足');
}
DB::transaction(function () use ($user, $rule, $frozen, $source) {
// 如果是扣款,立即执行,如果非冻结,也立即执行
if (($rule->variable < 0 && config('user_account.deductions')) || $rule->deductions == 1 || $frozen === false) {
$user->account->increment($rule->type, $rule->variable);
$frozen = false;
}
$log = [
'rule_id' => $rule->id,
'type' => $rule->type,
'variable' => $rule->variable,
'frozen' => $frozen,
'balance' => $user->account->{$rule->type},
'source' => $source ?: null,
];
// 写入记录
$user->account->logs()->create($log);
event(new AccountRuleExecuted($user->account, $log));
});
return true;
} catch (\Exception $e) {
throw new ExecuteRuleException($e->getMessage());
}
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace RuLong\UserAccount;
use Illuminate\Validation\Rule;
use RuLong\UserAccount\Exceptions\AccountRuleException;
use RuLong\UserAccount\Models\UserAccountRule;
use Validator;
class AccountRule
{
protected $rule;
public function __construct(UserAccountRule $rule)
{
$this->rule = $rule;
}
/**
* 新增规则
* @Author:<C.Jason>
* @Date:2018-10-16T12:59:48+0800
* @param array $data 规则数组
* @return AccountRuleException|boolean
*/
public function store($data)
{
$validator = Validator::make($data, [
'title' => 'required|between:2,50',
'name' => 'required|alpha_dash|between:2,50|unique:user_account_rules',
'type' => ['required', Rule::in(array_keys(config('user_account.account_type')))],
'variable' => 'required|numeric',
'trigger' => 'required|integer',
'deductions' => ['required', 'integer', Rule::in([0, 1])],
'remark' => 'nullable|max:255',
], [
'title.required' => '规则标题 必须填写',
'title.between' => '规则标题 长度应在:min-:max之间',
'name.required' => '规则名称 必须填写',
'name.alpha_dash' => '规则名称 只能是字母、数字、( - ) 或 ( _ ) 组成',
'name.between' => '规则名称 长度应在:min-:max之间',
'name.unique' => '规则名称 已经存在',
'type.required' => '账户类型 必须填写',
'type.in' => '账户类型 不合法',
'type.required' => '账户类型 必须填写',
'variable.required' => '增减变量 必须填写',
'variable.numeric' => '增减变量 只能是数字',
'trigger.required' => '账户类型 必须填写',
'trigger.integer' => '账户类型 只能是整数',
'deductions.required' => '是否直达 必须填写',
'deductions.integer' => '是否直达 只能是整数',
'deductions.in' => '是否直达 只能是 0 或 1',
'remark.max' => '规则备注 最大长度不能超过:max',
]);
if ($validator->fails()) {
throw new AccountRuleException($validator->errors()->first());
}
return UserAccountRule::create($data);
}
/**
* 更新规则
* @Author:<C.Jason>
* @Date:2018-10-16T13:00:15+0800
* @param UserAccountRule $rule 要编辑的规则
* @param array $data 规则数组
* @return AccountRuleException|boolean
*/
public function update(UserAccountRule $rule, $data)
{
$validator = Validator::make($data, [
'title' => 'required|between:2,50',
'name' => ['required', 'alpha_dash', 'between:2,50', Rule::unique('user_account_rules')->ignore($rule->id)],
'type' => ['required', Rule::in(array_keys(config('user_account.account_type')))],
'variable' => 'required|numeric',
'trigger' => 'required|integer',
'deductions' => ['required', 'integer', Rule::in([0, 1])],
'remark' => 'nullable|max:255',
], [
'title.required' => '规则标题 必须填写',
'title.between' => '规则标题 长度应在:min-:max之间',
'name.required' => '规则名称 必须填写',
'name.alpha_dash' => '规则名称 只能是字母、数字、( - ) 或 ( _ ) 组成',
'name.between' => '规则名称 长度应在:min-:max之间',
'name.unique' => '规则名称 已经存在',
'type.required' => '账户类型 必须填写',
'type.in' => '账户类型 不合法',
'type.required' => '账户类型 必须填写',
'variable.required' => '增减变量 必须填写',
'variable.numeric' => '增减变量 只能是数字',
'trigger.required' => '账户类型 必须填写',
'trigger.integer' => '账户类型 只能是整数',
'deductions.required' => '是否直达 必须填写',
'deductions.integer' => '是否直达 只能是整数',
'deductions.in' => '是否直达 只能是 0 或 1',
'remark.max' => '规则备注 最大长度不能超过:max',
]);
if ($validator->fails()) {
throw new AccountRuleException($validator->errors()->first());
}
return $rule->update($data);
}
/**
* 删除规则
* @Author:<C.Jason>
* @Date:2018-10-16T13:00:43+0800
* @param integer $id 规则ID
* @return integer
*/
public function destroy($id)
{
return UserAccountRule::where('id', $id)->delete();
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace RuLong\UserAccount\Commands;
use Illuminate\Console\Command;
use RuLong\UserAccount\Models\UserAccount;
class InitAccounts extends Command
{
protected $signature = 'user:account';
protected $description = 'Init users account';
private $directory;
public function handle()
{
$this->info('Find all users.');
$class = config('user_account.user_model');
$model = new $class;
$this->info('There are ' . $model->count() . ' users');
foreach ($model->get() as $user) {
UserAccount::firstOrCreate([
'user_id' => $user->id,
], [
'cash' => 0,
]);
$this->info('Synced user account : ' . $user->id);
}
$this->info('Init users account success.');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountDecreased
{
use SerializesModels;
public $account;
public $log;
public function __construct($account, $log)
{
$this->account = $account;
$user->log = $log;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountIncreased
{
use SerializesModels;
public $account;
public $log;
public function __construct($account, $log)
{
$this->account = $account;
$user->log = $log;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountLogFrozened
{
use SerializesModels;
public $log;
public function __construct($log)
{
$user->log = $log;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountLogThawed
{
use SerializesModels;
public $log;
public function __construct($log)
{
$user->log = $log;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountRuleExecuted
{
use SerializesModels;
public $account;
public $log;
public function __construct($account, $log)
{
$this->account = $account;
$this->log = $log;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class AccountTransfered
{
use SerializesModels;
public $fromUser;
public $toUser;
public $fromLog;
public $toLog;
public function __construct($fromUser, $toUser, $fromLog, $toLog)
{
$this->fromUser = $fromUser;
$this->toUser = $toUser;
$this->fromLog = $fromLog;
$this->toLog = $toLog;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace RuLong\UserAccount\Events;
use Illuminate\Queue\SerializesModels;
class UserCreated
{
use SerializesModels;
public $user;
public function __construct($user)
{
$this->user = $user;
$user->account()->create();
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace RuLong\UserAccount\Exceptions;
use RuntimeException;
class AccountRuleException extends RuntimeException
{
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace RuLong\UserAccount\Exceptions;
use RuntimeException;
class ExecuteRuleException extends RuntimeException
{
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace RuLong\UserAccount\Exceptions;
use RuntimeException;
class UserRuleException extends RuntimeException
{
public function __construct($message)
{
parent::__construct($message);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\UserAccount\Facades;
use Illuminate\Support\Facades\Facade;
class Account extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\UserAccount\Account::class;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace RuLong\UserAccount\Facades;
use Illuminate\Support\Facades\Facade;
class AccountRule extends Facade
{
protected static function getFacadeAccessor()
{
return \RuLong\UserAccount\AccountRule::class;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace RuLong\UserAccount\Models;
use Illuminate\Database\Eloquent\Model;
class UserAccount extends Model
{
protected $guarded = [];
protected $primaryKey = 'user_id';
public function logs()
{
return $this->hasMany(UserAccountLog::class, 'user_id', 'user_id');
}
public function user()
{
return $this->belongsTo(config('user_account.user_model'));
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace RuLong\UserAccount\Models;
use App\Models\User;
use App\Models\UserInfo;
use Illuminate\Database\Eloquent\Model;
class UserAccountLog extends Model
{
protected $guarded = [];
protected $casts = [
'source' => 'array',
];
public function account()
{
return $this->belongsTo(UserAccount::class, 'user_id', 'user_id');
}
public function rule()
{
return $this->belongsTo(UserAccountRule::class)->withDefault();
}
public function user()
{
return $this->belongsTo(config('user_account.user_model'));
}
public function userinfo()
{
return $this->belongsTo(UserInfo::class, 'user_id', 'user_id');
}
public function getFrozenTextAttribute()
{
switch ($this->frozen) {
case '0':
return "";
break;
case '1':
return "";
break;
default:
return "未知";
break;
}
}
public function getTypeTextAttribute()
{
return config('user_account.account_type')[$this->type] ?? '未知';
}
public function getVariableFormartAttribute()
{
if ($this->variable > 0) {
return '+' . $this->variable;
} else {
return $this->variable;
}
}
public function getSourceFormartAttribute()
{
$str = '';
if ($this->source) {
$remarkConfig = config('user_account.remark');
if ($this->source) {
foreach ($this->source as $key => $value) {
if (isset($remarkConfig[$key])) {
$str .= $remarkConfig[$key] . '' . $value . '&nbsp;&nbsp;';
}
}
}
}
return $str;
}
}

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