Files
xk-api-old-laravel/app/Models/YiiModels/ProductOrder.php
2025-01-11 14:16:20 +08:00

495 lines
16 KiB
PHP

<?php
namespace App\Models\YiiModels;
use App\Models\ExpressNo;
use App\Models\Pay\Bill;
use App\Models\Pay\MultiBill;
use App\Models\Pay\RefundBill;
use App\Models\ProductOrderItem;
use Exception;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Yansongda\LaravelPay\Facades\Pay;
/**
* App\Models\YiiModels\ProductOrder
*
* @property int $id
* @property int $store_id 门店id
* @property int|null $drugstore_id 仓库id
* @property int $su_id
* @property int $user_id 用户id
* @property int $up_id 就诊人id
* @property string $order_no 订单号
* @property int|null $is_online 是否互医订单 1是
* @property int|null $online_prescription_status 互医处方状态 1已通过 2已作废
* @property string|null $sync_order_no
* @property int $p_id 处方id
* @property int $order_type 订单类型 1处方订单 2商城订单 2商城处方订单
* @property int $type 支付类型 1微信2支付宝3银行卡4其他
* @property int $is_pay 是否支付0否1是
* @property string $total_pay_price 支付总价
* @property string $items_price 商品总价
* @property int $pay_time
* @property int $address_id
* @property string|null $address
* @property int $cancel_status
* @property int $cancel_time
* @property string|null $cancel_remark 取消备注(未支付超时取消,主动取消,拒绝取消)
* @property int $refund_status
* @property int $refund_time
* @property int $received_time
* @property int $auto_cancel_time
* @property int $status 产品订单状态0未支付1待发货2待收货3待评价4已退款5退款中6已收货7确认收货
* @property int $pay_method 支付方式1线上2线下
* @property string $trans_expenses 运费(默认包邮)
* @property int $free_ship 是否包邮
* @property string|null $remark 留言备注
* @property string|null $express_name 收货人
* @property string|null $express_mobile 收货电话
* @property string|null $express_region 收货省市区
* @property string|null $express_address 收货地址
* @property int|null $express_no_id 快递单信息id
* @property \Illuminate\Support\Carbon $created_at
* @property \Illuminate\Support\Carbon $updated_at
* @property-read \App\Models\Pay\Bill|null $billed 已支付的支付单
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Pay\Bill[] $bills 支付单
* @property-read int|null $bills_count
* @property-read \App\Models\ExpressNo|null $expressNo 物流信息
* @property-read string $cancel_status_string 取消状态
* @property-read string $created_at_format 创建时间的格式化
* @property-read string $refund_status_string 退款状态
* @property-read string $status_string 状态名称
* @property-read string $type_string 支付类型
* @property-read string $updated_at_format 更新时间的格式化
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProductOrderItem[] $items 订单商品明细
* @property-read int|null $items_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\YiiModels\ProductOrderLog[] $logs 订单日志
* @property-read int|null $logs_count
* @property-read \App\Models\YiiModels\Prescription|null $prescription 处方
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\YiiModels\ProductOrderRefund[] $refunds 退款单
* @property-read int|null $refunds_count
* @property-read \App\Models\YiiModels\Store|null $store 门店
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\YiiModels\ProductOrder newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\YiiModels\ProductOrder newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\YiiModels\ProductOrder query()
* @mixin \Eloquent
*/
class ProductOrder extends YiiBaseModel
{
// 订单类型
public const ORDER_TYPE_PRESCRIPTION = 1;
public const ORDER_TYPE_SHOP = 2;
public const ORDER_TYPE_SHOP_PRESCRIPTION = 3;
public const ORDER_TYPE = [
self::ORDER_TYPE_PRESCRIPTION => '处方订单',
self::ORDER_TYPE_SHOP => '商城订单',
self::ORDER_TYPE_SHOP_PRESCRIPTION => '商城处方订单',
];
// 订单类型
public const TYPE_WECHAT = 1;
public const TYPE_ALIPAY = 2;
public const TYPE_BANK = 3;
public const TYPE_OTHER = 4;
public const TYPE = [
self::TYPE_WECHAT => '微信',
self::TYPE_ALIPAY => '支付宝',
self::TYPE_BANK => '银行卡',
self::TYPE_OTHER => '其他',
];
// 是否支付
public const IS_PAY_FALSE = 0;
public const IS_PAY_TRUE = 1;
public const IS_PAY = [
self::IS_PAY_FALSE => '否',
self::IS_PAY_TRUE => '是',
];
// 是否互医订单
public const IS_ONLINE_FALSE = 0;
public const IS_ONLINE_TRUE = 1;
public const IS_ONLINE = [
self::IS_ONLINE_FALSE => '否',
self::IS_ONLINE_TRUE => '是',
];
// 订单状态
const STATUS_UNPAID = 0;
const STATUS_WAIT_SEND = 1;
const STATUS_WAIT_ACCEPT = 2;
const STATUS_WAIT_COMMENT = 3;
const STATUS_REFUND = 4;
const STATUS_REFUNDING = 5;
const STATUS_ACCEPTED = 6;
const STATUS_CONFIRM = 7;
const STATUS_CANCEL = 9;
const STATUS = [
self::STATUS_UNPAID => '未支付',
self::STATUS_WAIT_SEND => '待发货',
self::STATUS_WAIT_ACCEPT => '待收货',
self::STATUS_WAIT_COMMENT => '待评价',
self::STATUS_REFUND => '已退款',
self::STATUS_REFUNDING => '退款中',
self::STATUS_ACCEPTED => '已收货',
self::STATUS_CONFIRM => '确认收货',
self::STATUS_CANCEL => '已取消',
];
// 取消状态
const CANCEL_STATUS_FALSE = 0;
const CANCEL_STATUS_TRUE = 1;
const CANCEL_STATUS = [
self::CANCEL_STATUS_FALSE => '未取消',
self::CANCEL_STATUS_TRUE => '已取消',
];
// 退款状态
const REFUND_STATUS_NORMAL = 0;
const REFUND_STATUS_APPLYING = 1;
const REFUND_STATUS_AGREE = 2;
const REFUND_STATUS_END = 3;
const REFUND_STATUS_PART = 4;
const REFUND_STATUS_CANCEL = 9;
const REFUND_STATUS = [
self::REFUND_STATUS_NORMAL => '正常',
self::REFUND_STATUS_APPLYING => '申请中',
self::REFUND_STATUS_AGREE => '同意申请',
self::REFUND_STATUS_END => '退款完成',
self::REFUND_STATUS_PART => '部分退款完成',
self::REFUND_STATUS_CANCEL => '取消退款申请',
];
// 支付方式
public const PAY_METHOD_ONLINE = 1;
public const PAY_METHOD_OFFLINE = 2;
public const PAY_METHOD = [
self::PAY_METHOD_ONLINE => '线上',
self::PAY_METHOD_OFFLINE => '线下',
];
// 支付方式
public const FREE_SHIP_FALSE = 0;
public const FREE_SHIP_TRUE = 1;
public const FREE_SHIP = [
self::FREE_SHIP_FALSE => '不包邮',
self::FREE_SHIP_TRUE => '包邮',
];
/**
* @comment 门店
* @return BelongsTo
*/
public function store(): BelongsTo
{
return $this->belongsTo(Store::class, 'store_id');
}
/**
* @comment 订单商品明细
* @return HasMany
*/
public function items(): HasMany
{
return $this->hasMany(ProductOrderItem::class, 'product_order_id');
}
/**
* @comment 退款单
* @return HasMany
*/
public function refunds(): HasMany
{
return $this->hasMany(ProductOrderRefund::class, 'order_id');
}
/**
* @comment 订单日志
* @return HasMany
*/
public function logs(): HasMany
{
return $this->hasMany(ProductOrderLog::class, 'p_id');
}
/**
* @comment 支付单
* @return MorphMany
*/
public function bills(): MorphMany
{
return $this->morphMany(Bill::class, 'billable');
}
/**
* @comment 已支付的支付单
* @return MorphOne
*/
public function billed(): MorphOne
{
return $this->morphOne(Bill::class, 'billable')->where('bill_status', Bill::BILL_STATUS_SUCCESSFUL)->latest();
}
/**
* @comment 物流信息
* @return BelongsTo
*/
public function expressNo(): BelongsTo
{
return $this->belongsTo(ExpressNo::class, 'express_no_id');
}
/**
* @comment 处方
* @return BelongsTo
*/
public function prescription(): BelongsTo
{
return $this->belongsTo(Prescription::class, 'p_id');
}
/**
* status_string
*
* @comment 状态名称
* @return string
*/
public function getStatusStringAttribute(): string
{
return self::STATUS[$this['status']] ?? '';
}
/**
* type_string
*
* @comment 支付类型
* @return string
*/
public function getTypeStringAttribute(): string
{
return self::TYPE[$this->type] ?? '';
}
/**
* cancel_status_string
*
* @comment 取消状态
* @return string
*/
public function getCancelStatusStringAttribute(): string
{
return self::CANCEL_STATUS[$this->cancel_status] ?? '';
}
/**
* refund_status_string
*
* @comment 退款状态
* @return string
*/
public function getRefundStatusStringAttribute(): string
{
return self::CANCEL_STATUS[$this->refund_status] ?? '';
}
/**
* 支付成功处理
*
* @param Bill $bill
* @return void
*/
public function handlePied(Bill $bill): void
{
$this->is_pay = self::IS_PAY_TRUE;
$this->pay_time = strtotime($bill->pay_at);
$this->status = self::STATUS_WAIT_SEND;
$this->save();
$this->logs()->create([
'content' => '订单支付成功 - ' . $this->type_string,
]);
$this->createFundWater($this);
$this->createNotice($this, '您的订单已支付成功');
$this->loadMissing(['items.drugStoreRelation' => fn(Relation $relation) => $relation->where('store_id', $this->store_id)]);
foreach ($this->items as $item) {
if ($item->drugStoreRelation) {
$item->drugStoreRelation->true_sales_volume += $item->number;
$item->drugStoreRelation->save();
}
}
// // 同步数据
// $this->refresh();
// $this->load(['items']);
// Artisan::call('data:sync', [
// '--type' => 'product_order_sync',
// '--data' => $this->toArray(),
// ]);
if ($this->is_online == self::IS_ONLINE_TRUE && $this->sync_order_no) {
// 同步数据(老)
Artisan::call('data:sync-old', [
'--type' => 'syncOrder',
'--data' => json_encode([
'order_no' => $this->sync_order_no,
'status' => 1, // 1已支付 2已取消 3已退款
'time' => $bill->pay_at,
]),
]);
}
}
/**
* 退款操作
*
* @throws \Throwable
*/
public function toRefund(int|string|float $refundAmount, $refundNo = null): array
{
if ($this->is_pay == ProductOrder::IS_PAY_FALSE) {
$message = '订单未支付:' . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
if ($this->cancel_status == ProductOrder::CANCEL_STATUS_TRUE) {
$message = '订单已取消:' . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
if ($this->refund_status == ProductOrder::REFUND_STATUS_PART) {
$message = '订单已退款完成:' . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
if (in_array($this->status, [
ProductOrder::STATUS_UNPAID,
ProductOrder::STATUS_REFUND,
ProductOrder::STATUS_CANCEL,
])) {
$message = '退款失败,订单状态为:' . $this->status_string . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
$this->loadMissing('billed');
if (!$this->billed) {
$message = '退款失败,无已支付的支付单:' . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
if ($this->billed->can_refund_amount < $refundAmount) {
$message = '退款失败,申请金额超过可退金额不足:' . "[productOrderId:$this->id]";
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
DB::beginTransaction();
try {
$data = [
'refund_amount' => $refundAmount,
];
if ($refundNo) {
$data['refund_no'] = $refundNo;
}
/* @var RefundBill $refundBill */
$refundBill = $this->billed->refunds()->create($data);
$refundNo = $refundBill->refund_no;
$order = [
'out_trade_no' => $this->billed->pay_no,
'out_refund_no' => $refundBill->refund_no,
'amount' => [
'refund' => $refundBill->refund_amount * 100,
'total' => $this->billed->pay_amount * 100,
'currency' => 'CNY',
],
];
$result = Pay::wechat()->refund($order);
if (isset($result['code'])) {
$message = '退款失败:' . ($result['message'] ?? '');
log_s($message, 'order', 'refund');
return json_result($message, [], 1);
}
$this->logs()->create([
'content' => "开始退款:{$this->billed->pay_way_string}[$refundBill->refund_amount]"
]);
DB::commit();
$message = '退款申请成功:' . var_export($result, true);
log_s($message, 'order', 'refund');
return json_result('退款申请成功', ['refund_no' => $refundNo]);
} catch (Exception $exception) {
DB::rollBack();
$message = '退款错误:' . "[productOrderId:$this->id]" . $exception->getMessage() . $exception->getTraceAsString();
log_s($message, 'order', 'error');
return json_result($message, [], 1);
}
}
/**
* 退款成功处理
*
* @param RefundBill $refundBill
* @return void
*/
public function handleRefunded(RefundBill $refundBill): void
{
$this->status = self::STATUS_REFUND;
$this->refund_status = ProductOrder::REFUND_STATUS_END;
$this->save();
$refund = ProductOrderRefund::where('refund_no', $refundBill->refund_no)->first();
$refund->update([
'is_refund' => ProductOrderRefund::IS_REFUND_TRUE,
'refund_time' => $refundBill->refund_at,
'status' => ProductOrderRefund::STATUS_PASS,
]);
$this->logs()->create([
'content' => '订单退款成功 - ' . $this->type_string,
]);
$this->createNotice($this, '您的订单已退款成功');
$this->loadMissing(['items.drugstoreDrug' => fn(Relation $relation) => $relation->where('drugstore_id', $this->drugstore_id)]);
foreach ($this->items as $item) {
if ($item->drugstoreDrug) {
$item->drugstoreDrug->stock += $item->number;
$item->drugstoreDrug->frozen_number -= $item->number;
$item->drugstoreDrug->save();
}
}
// // 同步数据
// $this->refresh();
// $this->load(['items']);
// Artisan::call('data:sync', [
// '--type' => 'product_order_sync',
// '--data' => $this->toArray(),
// ]);
if ($this->is_online == self::IS_ONLINE_TRUE && $this->sync_order_no) {
// 同步数据(老)
Artisan::call('data:sync-old', [
'--type' => 'syncOrder',
'--data' => json_encode([
'order_no' => $this->sync_order_no,
'status' => 3, // 1已支付 2已取消 3已退款
'time' => $refundBill->refund_at,
]),
]);
}
}
}