6
0
Fork 0

企业付款 todo

release
panliang 2022-05-12 17:49:04 +08:00
parent eecf325b41
commit 09c53981b5
20 changed files with 199 additions and 76 deletions

View File

@ -69,6 +69,8 @@ class OrderProfitController extends AdminController
$show->field('money'); $show->field('money');
$show->field('status')->using(OrderProfit::$statusMap)->dot(OrderProfit::$statusColor); $show->field('status')->using(OrderProfit::$statusMap)->dot(OrderProfit::$statusColor);
$show->field('paid_at'); $show->field('paid_at');
$show->field('pay_way');
$show->field('pay_no');
$show->field('remarks'); $show->field('remarks');
$show->field('created_at'); $show->field('created_at');
$show->field('updated_at'); $show->field('updated_at');

View File

@ -175,17 +175,19 @@ class StoreController extends AdminController
$form->text('remarks'); $form->text('remarks');
$form->disableDeleteButton(); $form->disableDeleteButton();
// 管理员删除店铺
if ($form->isDeleting() && $canAdmin) {
$info = Store::find($form->getKey());
// 删除店铺关联的数据
$info->adminUsers()->detach();
$info->productSkus()->detach();
}
}); });
} }
public function destroy($id)
{
$info = Store::find($id);
// 删除店铺关联的数据
$info->adminUsers()->detach();
$info->productSkus()->detach();
return parent::destroy($id);
}
protected function canAdmin() protected function canAdmin()
{ {
return Admin::user()->isRole('administrator'); return Admin::user()->isRole('administrator');

View File

@ -2,9 +2,8 @@
namespace App\Admin\Controllers; namespace App\Admin\Controllers;
use App\Admin\Repositories\Vip;
use App\Exceptions\BizException; use App\Exceptions\BizException;
use App\Models\Vip as VipModel; use App\Models\Vip;
use Dcat\Admin\Admin; use Dcat\Admin\Admin;
use Dcat\Admin\Form; use Dcat\Admin\Form;
use Dcat\Admin\Grid; use Dcat\Admin\Grid;
@ -32,20 +31,20 @@ class VipController extends AdminController
/** 操作 **/ /** 操作 **/
//新增 //新增
// if (Admin::user()->can('dcat.admin.vips.create')) { if (Admin::user()->can('dcat.admin.vips.create')) {
// $grid->disableCreateButton(false); $grid->disableCreateButton(false);
// $grid->enableDialogCreate(); $grid->enableDialogCreate();
// } }
//修改 //修改
$grid->showQuickEditButton(Admin::user()->can('dcat.admin.vips.edit')); $grid->showQuickEditButton(Admin::user()->can('dcat.admin.vips.edit'));
//删除以及自定义操作 //删除以及自定义操作
// $grid->actions(function (Grid\Displayers\Actions $actions) { $grid->actions(function (Grid\Displayers\Actions $actions) {
// $actions->disableDelete(Admin::user()->cannot('dcat.admin.vips.destroy')); $actions->disableDelete(Admin::user()->cannot('dcat.admin.vips.destroy'));
// }); });
$grid->filter(function (Grid\Filter $filter) { $grid->filter(function (Grid\Filter $filter) {
$filter->panel(false); $filter->panel(false);
$filter->like('name'); $filter->like('name')->width(3);
}); });
}); });
} }
@ -62,6 +61,8 @@ class VipController extends AdminController
return Show::make($id, new Vip(), function (Show $show) { return Show::make($id, new Vip(), function (Show $show) {
$show->field('id'); $show->field('id');
$show->field('name'); $show->field('name');
$show->field('sort');
$show->filed('slug');
$show->column('ratio')->as(function ($value) { $show->column('ratio')->as(function ($value) {
return $value . '%'; return $value . '%';
}); });
@ -80,18 +81,17 @@ class VipController extends AdminController
{ {
return Form::make(new Vip(), function (Form $form) { return Form::make(new Vip(), function (Form $form) {
$form->display('id'); $form->display('id');
$form->number('sort')->min(1)->required()->help('不可重复');
$form->text('name')->required(); $form->text('name')->required();
$form->radio('slug')->options(Vip::$typeMap)->default(Vip::TYPE_FAVOITE);
$form->number('ratio')->min(0)->max(100)->help('例如: 60%, 填写 60 即可'); $form->number('ratio')->min(0)->max(100)->help('例如: 60%, 填写 60 即可');
$form->number('growth_value')->min(0)->default(0); $form->number('growth_value')->min(0)->default(0)->help('升级到此等级所需的成长值');
$form->display('created_at');
$form->display('updated_at');
}); });
} }
public function destroy($id) public function destroy($id)
{ {
$vip = VipModel::findOrFail($id); $vip = Vip::findOrFail($id);
if ($vip->hasUser()) { if ($vip->hasUser()) {
throw new BizException(__('vip.options.deny_message')); throw new BizException(__('vip.options.deny_message'));
} }

View File

@ -38,9 +38,18 @@ class ProfitSuccessForm extends Form implements LazyRenderable
{ {
$order = OrderProfit::findOrFail($this->payload['id']); $order = OrderProfit::findOrFail($this->payload['id']);
$service = new DistributeService(); $service = new DistributeService();
if ($input['pay_way'] === 'wechat-transfer' && !$order->pay_no) {
$order->update([
'pay_no' => serial_number()
]);
}
try { try {
DB::beginTransaction(); DB::beginTransaction();
$service->success($order, $input); if ($input['pay_way'] === 'wechat-transfer') {
$service->wechatTransfer($order);
} else {
$service->success($order, $input);
}
DB::commit(); DB::commit();
} catch (Throwable $th) { } catch (Throwable $th) {
DB::rollBack(); DB::rollBack();

View File

@ -1,16 +0,0 @@
<?php
namespace App\Admin\Repositories;
use App\Models\Vip as Model;
use Dcat\Admin\Repositories\EloquentRepository;
class Vip extends EloquentRepository
{
/**
* Model.
*
* @var string
*/
protected $eloquentClass = Model::class;
}

View File

@ -4,7 +4,7 @@ namespace App\Endpoint\Api\Http\Controllers\Order;
use App\Endpoint\Api\Http\Controllers\Controller; use App\Endpoint\Api\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\{OrderPre, ProductSku}; use App\Models\{OrderPre, ProductSku, Store};
use App\Services\OrderService; use App\Services\OrderService;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Exceptions\BizException; use App\Exceptions\BizException;
@ -30,19 +30,29 @@ class OrderPreController extends Controller
'products.required' => '未选择商品' 'products.required' => '未选择商品'
]); ]);
// 验证商品是否属于该店铺
$store = Store::findOrFail($request->input('store_id'));
$products = $request->input('products'); $products = $request->input('products');
// 验证商品的购买数量 大于 发货数量
foreach($products as $item) { foreach($products as $item) {
$quantity = $item['quantity']; $quantity = $item['quantity'];
$send = $item['send']; $send = $item['send'];
$sku_id = $item['sku_id'];
// 验证商品是否属于该店铺
if (!$store->productSkus()->wherePivot('product_sku_id', $sku_id)->exists()) {
throw new BizException('商品未在该店铺出售, ' . $sku_id);
}
// 验证商品的购买数量 大于 发货数量
if ($send > $quantity) { if ($send > $quantity) {
throw new BizException('商品购买数量大于发货数量, ' . $item['sku_id']); throw new BizException('商品购买数量大于发货数量, ' . $sku_id);
} }
} }
$order_pre = OrderPre::create([ $order_pre = OrderPre::create([
'user_id' => $user->id, 'user_id' => $user->id,
'store_id' => $request->input('store_id'), 'store_id' => $store->id,
'products' => $request->input('products'), 'products' => $request->input('products'),
'remarks' => $request->input('remarks'), 'remarks' => $request->input('remarks'),
'others' => [ 'others' => [

View File

@ -24,7 +24,7 @@ class OrderProfit extends Model
3 => 'danger' 3 => 'danger'
]; ];
protected $fillable = ['id', 'order_id', 'from_user_id', 'user_id', 'role', 'role_name', 'growth_value', 'ratio', 'money', 'status', 'paid_at', 'pay_data', 'remarks']; protected $fillable = ['id', 'order_id', 'from_user_id', 'user_id', 'role', 'role_name', 'growth_value', 'ratio', 'money', 'status', 'paid_at', 'pay_no', 'pay_way', 'pay_data', 'remarks'];
public function user() public function user()
{ {

View File

@ -15,6 +15,8 @@ class ShippingRule extends Model
public const TYPE_FREE = 1;//包邮 public const TYPE_FREE = 1;//包邮
public const TYPE_WEIGHT = 2;//计算重量 public const TYPE_WEIGHT = 2;//计算重量
protected $fillable = ['info', 'remarks', 'template_id', 'type', 'zones'];
protected $casts = [ protected $casts = [
// 'info' => JsonArray::class, // 'info' => JsonArray::class,
'zones' => JsonArray::class, 'zones' => JsonArray::class,

View File

@ -8,8 +8,17 @@ use Illuminate\Database\Eloquent\Model;
class Vip extends Model class Vip extends Model
{ {
use HasFactory; use HasFactory, HasDateTimeFormatter;
use HasDateTimeFormatter;
const TYPE_FAVOITE = 'favoite';
const TYPE_AGENT = 'agent';
protected $fillable = ['growth_value', 'name', 'ratio', 'slug', 'sort'];
public static $typeMap = [
self::TYPE_FAVOITE => '爱好者',
self::TYPE_AGENT => '代理'
];
public function users() public function users()
{ {

View File

@ -3,6 +3,9 @@
namespace App\Services; namespace App\Services;
use App\Models\{User, Order, SalesValueLog, Vip, OrderProfit}; use App\Models\{User, Order, SalesValueLog, Vip, OrderProfit};
use App\Services\Payment\WxpayService;
use App\Enums\SocialiteType;
use App\Exceptions\BizException;
/** /**
* 分销模块 * 分销模块
@ -94,6 +97,27 @@ class DistributeService
$order->profits()->createMany($profit_list); $order->profits()->createMany($profit_list);
} }
/**
* 使用微信企业付款
* 调用之前, 需要提前生成商户订单号
*/
public function wechatTransfer(OrderProfit $profit)
{
$user = $profit->user;
$openid = $user->socialites()->where('socialite_type', SocialiteType::WechatMiniProgram->value)->value('socialite_id');
$result = (new WxpayService())->transfer([
'partner_trade_no' => $profit->pay_no,
'openid' => $openid,
'check_name' => 'NO_CHECK',
'amount' => $profit->money * 100,
'desc' => '推荐返利',
]);
$this->success($profit, [
'paid_at' => data_get($result, 'payment_time')
]);
}
/** /**
* 返现记录支付成功 * 返现记录支付成功
* *

View File

@ -55,6 +55,56 @@ class WxpayService
}; };
} }
/**
* 微信企业付款到零钱
*
* @param array $params
* @return array
*/
public function transfer(array $params)
{
$config = config('wechat.payment.default');
if (!isset($params['openid']) && config('app.debug')) {
$params['openid'] = 'oU7xS5UspzVvpPEBqKZuW6N9WXDg';
}
$app = Factory::payment($config);
// 服务商模式 (子商户)
$appId = config('wechat.payment.sub.app_id');
$mchId = config('wechat.payment.sub.mch_id');
if ($appId && $mchId) {
$app->setSubMerchant($mchId, $appId);
}
/*
'partner_trade_no' => '1233455', // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
'openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'check_name' => 'FORCE_CHECK', // NO_CHECK不校验真实姓名, FORCE_CHECK强校验真实姓名
're_user_name' => '王小帅', // 如果 check_name 设置为FORCE_CHECK则必填用户真实姓名
'amount' => 10000, // 企业付款金额,单位为分
'desc' => '理赔', // 企业付款操作说明信息。必填
*/
$result = $app->transfer->toBalance($params);
$this->validateResult($result);
return $result;
}
/**
* 查询 微信企业付款到零钱 的订单
*
* @param string $no 商户订单号
* @return array
*/
public function queryTransfer($no)
{
$config = config('wechat.payment.transfer');
$app = Factory::payment($config);
$result = $app->transfer->queryBalanceOrder($no);
$this->validateResult($result);
return $result;
}
/** /**
* 根据商户订单号退款 * 根据商户订单号退款
* *

View File

@ -77,6 +77,15 @@ return [
'app_id' => env('WECHAT_PAYMENT_SUB_APPID'), 'app_id' => env('WECHAT_PAYMENT_SUB_APPID'),
'mch_id' => env('WECHAT_PAYMENT_SUB_MCH_ID'), 'mch_id' => env('WECHAT_PAYMENT_SUB_MCH_ID'),
], ],
// 企业付款
'transfer' => [
'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false),
'app_id' => env('WECHAT_PAYMENT_SUB_APPID'),
'mch_id' => env('WECHAT_PAYMENT_SUB_MCH_ID'),
'key' => env('WECHAT_PAYMENT_SUB_KEY'),
'cert_path' => env('WECHAT_PAYMENT_SUB_CERT_PATH'),
'key_path' => env('WECHAT_PAYMENT_SUB_KEY_PATH'),
],
// 商城 - 微信小程序支付 // 商城 - 微信小程序支付
'mini_program' => [ 'mini_program' => [
'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false), 'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false),

View File

@ -26,7 +26,9 @@ class CreateOrderProfitsTable extends Migration
$table->tinyInteger('status')->default(0)->comment('状态(0: 待付款, 1: 付款中, 2: 已付款)'); $table->tinyInteger('status')->default(0)->comment('状态(0: 待付款, 1: 付款中, 2: 已付款)');
$table->string('remarks')->nullable()->comment('备注'); $table->string('remarks')->nullable()->comment('备注');
$table->timestamp('paid_at')->nullable()->comment('付款时间'); $table->timestamp('paid_at')->nullable()->comment('付款时间');
$table->text('pay_data')->nullable()->comment('支付信息{pay_way, pay_sn}'); $table->string('pay_way')->nullable()->comment('付款方式');
$table->string('pay_no')->nullable()->comment('付款流水号');
$table->text('pay_data')->nullable()->comment('支付信息');
$table->timestamps(); $table->timestamps();
}); });
} }

View File

@ -33,6 +33,7 @@ class AdminMenuSeeder extends Seeder
'icon' => '', 'icon' => '',
'uri' => 'users', 'uri' => 'users',
], ],
['title' => '代理等级', 'icon' => '', 'uri' => 'vips']
], ],
], ],
[ [
@ -248,31 +249,32 @@ class AdminMenuSeeder extends Seeder
'icon' => 'fa fa-jpy', 'icon' => 'fa fa-jpy',
'uri'=> '', 'uri'=> '',
'children'=>[ 'children'=>[
[ // [
'title' => '可提账户', // 'title' => '可提账户',
'icon'=>'', // 'icon'=>'',
'uri' => 'wallet-logs', // 'uri' => 'wallet-logs',
], // ],
[ // [
'title' => '余额账户', // 'title' => '余额账户',
'icon' => '', // 'icon' => '',
'uri' => 'balance-logs', // 'uri' => 'balance-logs',
], // ],
[ // [
'title' => '积分账户', // 'title' => '积分账户',
'icon' => '', // 'icon' => '',
'uri' => 'points-logs', // 'uri' => 'points-logs',
], // ],
[ // [
'title' =>'提现审核', // 'title' =>'提现审核',
'icon' => '', // 'icon' => '',
'uri' =>'wallet-to-bank-logs', // 'uri' =>'wallet-to-bank-logs',
], // ],
[ [
'title' => '售后打款', 'title' => '售后打款',
'icon' => '', 'icon' => '',
'uri' =>'finance-after-sales?state=5', 'uri' =>'finance-after-sales?state=5',
], ],
['title' => '提成管理', 'icon' => '', 'uri' => 'profit']
], ],
], ],
[ [
@ -300,7 +302,6 @@ class AdminMenuSeeder extends Seeder
'icon' => '', 'icon' => '',
'uri' =>'auth/menus', 'uri' =>'auth/menus',
], ],
['title' => '代理等级', 'icon' => '', 'uri' => 'vips'],
[ [
'title' =>'配置管理', 'title' =>'配置管理',
'icon' => '', 'icon' => '',
@ -317,6 +318,7 @@ class AdminMenuSeeder extends Seeder
} catch (Throwable $th) { } catch (Throwable $th) {
DB::rollBack(); DB::rollBack();
report($th); report($th);
dump($th->getMessage());
} }
} }

View File

@ -309,6 +309,17 @@ class AdminPermissionSeeder extends Seeder
'store' => [ 'store' => [
'name' => '店铺管理', 'name' => '店铺管理',
'curd' => ['index', 'create', 'store', 'edit', 'update', 'destroy'], 'curd' => ['index', 'create', 'store', 'edit', 'update', 'destroy'],
],
'vip' => [
'name' => '代理等级管理',
'curd' => ['index', 'create', 'store', 'edit', 'update', 'destroy'],
],
'profit' => [
'name' => '提成管理',
'curd' => ['index', 'create', 'store', 'edit', 'update', 'destroy'],
'children' => [
'pay' => ['name' => '修改支付状态']
]
] ]
]; ];
// try { // try {

View File

@ -23,7 +23,7 @@ class AdminUserSeeder extends Seeder
$user = $userModel::create([ $user = $userModel::create([
'username' => 'admin', 'username' => 'admin',
'password' => 'admin', 'password' => bcrypt('admin'),
'name' => '管理员', 'name' => '管理员',
]); ]);

View File

@ -18,11 +18,13 @@ class ShippingSeeder extends Seeder
'name' => '自提' 'name' => '自提'
]); ]);
ShippingRule::create([ \DB::table('shipping_rules')->insert([
'template_id' => $template->id, 'template_id' => $template->id,
'type' => 1, 'type' => 1,
'info' => json_encode(["threshold" => "100"]), 'info' => json_encode(["threshold" => "0"]),
'zones' => json_encode(Zone::where('type', Zone::TYPE_AREA)->pluck('id')), 'zones' => json_encode(Zone::where('type', Zone::TYPE_AREA)->pluck('id')),
'created_at' => now(),
'updated_at' => now()
]); ]);
} }
} }

View File

@ -23,7 +23,11 @@ class UserSeeder extends Seeder
DB::table('user_vips')->truncate(); DB::table('user_vips')->truncate();
DB::table('socialite_users')->truncate(); DB::table('socialite_users')->truncate();
DB::table('personal_access_tokens')->truncate(); DB::table('personal_access_tokens')->truncate();
DB::table('wallets')->truncate(); DB::table('wallets')->truncate();
DB::table('wallet_logs')->truncate();
DB::table('wallet_to_bank_logs')->truncate();
DB::table('balances')->truncate(); DB::table('balances')->truncate();
DB::table('users')->truncate(); DB::table('users')->truncate();
$this->faker = $this->withFaker(); $this->faker = $this->withFaker();

View File

@ -23,7 +23,7 @@ return [
'remarks' => '备注', 'remarks' => '备注',
'paid_at' => '付款时间', 'paid_at' => '付款时间',
'pay_way' => '支付方式', 'pay_way' => '支付方式',
'pay_no' => '订单号', 'pay_no' => '流水号',
], ],
'options' => [ 'options' => [
], ],

View File

@ -8,11 +8,12 @@ return [
'fields' => [ 'fields' => [
'name' => '等级名称', 'name' => '等级名称',
'growth_value' => '升级', 'growth_value' => '升级',
'sort' => '排序', 'sort' => '等级',
'ratio' => '比例' 'ratio' => '比例',
'slug' => '类别'
], ],
'options' => [ 'options' => [
'deny' => '删除失败', 'deny' => '删除失败',
'deny_message'=>'当前会员等级下会员人数大于0', 'deny_message'=> '该等级下还有用户, 无法删除',
], ],
]; ];