You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
430 lines
13 KiB
430 lines
13 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
// +----------------------------------------------------------------------
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types=1);
|
|
|
|
namespace app\common\model;
|
|
|
|
use app\common\enum\order\DeliveryType;
|
|
use app\common\enum\payment\Method;
|
|
use cores\BaseModel;
|
|
use app\common\model\store\shop\Order as ShopOrder;
|
|
use app\common\service\Order as OrderService;
|
|
use app\common\service\order\Shipping as ShippingService;
|
|
use app\common\service\order\Complete as OrderCompleteService;
|
|
use app\common\service\order\source\Factory as OrderSourceFactory;
|
|
use app\common\enum\order\PayStatus as PayStatusEnum;
|
|
use app\common\enum\order\OrderStatus as OrderStatusEnum;
|
|
use app\common\enum\order\DeliveryType as DeliveryTypeEnum;
|
|
use app\common\enum\order\ReceiptStatus as ReceiptStatusEnum;
|
|
use app\common\enum\order\DeliveryStatus as DeliveryStatusEnum;
|
|
use app\common\library\helper;
|
|
use think\model\relation\HasOne;
|
|
use think\model\relation\HasMany;
|
|
use think\model\relation\BelongsTo;
|
|
|
|
/**
|
|
* 订单模型
|
|
* Class Order
|
|
* @package app\common\model
|
|
*/
|
|
class Order extends BaseModel
|
|
{
|
|
// 定义表名
|
|
protected $name = 'order';
|
|
|
|
// 定义表名(外部引用)
|
|
public static string $tableName = 'order';
|
|
|
|
// 定义主键
|
|
protected $pk = 'order_id';
|
|
|
|
// 定义别名
|
|
protected string $alias = 'order';
|
|
|
|
/**
|
|
* 追加字段
|
|
* @var array
|
|
*/
|
|
protected $append = [
|
|
'state_text', // 售后单状态文字描述
|
|
'pay_method_text', //订单支付方式文字描述
|
|
'delivery_type_text', //配送方式文字描述
|
|
'time_text', //倒计时文字
|
|
'store_phone'//商家电话
|
|
];
|
|
|
|
/**
|
|
* 订单商品列表
|
|
* @return HasMany
|
|
*/
|
|
public function goods(): HasMany
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->hasMany("app\\{$module}\\model\\OrderGoods")->withoutField('content');
|
|
}
|
|
|
|
/**
|
|
* 关联订单发货单
|
|
* @return hasMany
|
|
*/
|
|
public function delivery(): HasMany
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->hasMany("app\\{$module}\\model\\order\\Delivery");
|
|
}
|
|
|
|
/**
|
|
* 关联订单收货地址表
|
|
* @return HasOne
|
|
*/
|
|
public function address(): HasOne
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->hasOne("app\\{$module}\\model\\OrderAddress");
|
|
}
|
|
|
|
/**
|
|
* 关联订单收货地址表
|
|
* @return HasOne
|
|
*/
|
|
public function extract(): HasOne
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->hasOne("app\\{$module}\\model\\OrderExtract");
|
|
}
|
|
|
|
/**
|
|
* 关联自提门店表
|
|
* @return BelongsTo
|
|
*/
|
|
public function extractShop(): BelongsTo
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->belongsTo("app\\{$module}\\model\\store\\Shop", 'extract_shop_id');
|
|
}
|
|
|
|
/**
|
|
* 关联门店店员表
|
|
* @return BelongsTo
|
|
*/
|
|
public function extractClerk(): BelongsTo
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->belongsTo("app\\{$module}\\model\\store\\shop\\Clerk", 'extract_clerk_id');
|
|
}
|
|
|
|
/**
|
|
* 关联用户表
|
|
* @return BelongsTo
|
|
*/
|
|
public function user(): BelongsTo
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->belongsTo("app\\{$module}\\model\\User");
|
|
}
|
|
|
|
/**
|
|
* 关联物流公司表 (仅用于兼容旧物流数据)
|
|
* @return BelongsTo
|
|
*/
|
|
public function express(): BelongsTo
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->belongsTo("app\\{$module}\\model\\Express");
|
|
}
|
|
|
|
/**
|
|
* 关联模型:第三方交易记录
|
|
* @return BelongsTo
|
|
*/
|
|
public function trade(): BelongsTo
|
|
{
|
|
$module = self::getCalledModule();
|
|
return $this->belongsTo("app\\{$module}\\model\\PaymentTrade", 'trade_id', 'trade_id');
|
|
}
|
|
|
|
/**
|
|
* 获取器:订单状态文字描述
|
|
* @param $value
|
|
* @param $data
|
|
* @return string
|
|
*/
|
|
public function getStateTextAttr($value, $data): string
|
|
{
|
|
// 订单状态
|
|
if ($data['order_status'] != OrderStatusEnum::NORMAL) {
|
|
return OrderStatusEnum::data()[$data['order_status']]['name'];
|
|
}
|
|
// 付款状态
|
|
if ($data['pay_status'] == PayStatusEnum::PENDING) {
|
|
return '待支付';
|
|
}
|
|
// 发货状态
|
|
if ($data['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
|
|
$enum = [DeliveryStatusEnum::NOT_DELIVERED => '待发货', DeliveryStatusEnum::PART_DELIVERED => '部分发货'];
|
|
return $enum[$data['delivery_status']];
|
|
}
|
|
// 收货状态
|
|
if ($data['receipt_status'] == ReceiptStatusEnum::NOT_RECEIVED) {
|
|
return '待收货';
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* 获取器:订单金额(含优惠折扣)
|
|
* @param $value
|
|
* @param $data
|
|
* @return string
|
|
*/
|
|
public function getOrderPriceAttr($value, $data): string
|
|
{
|
|
// 兼容旧数据:订单金额
|
|
if ($value == 0) {
|
|
return helper::bcadd(helper::bcsub($data['total_price'], $data['coupon_money']), $data['update_price']);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* 获取器:改价金额(差价)
|
|
* @param $value
|
|
* @return array
|
|
*/
|
|
public function getUpdatePriceAttr($value): array
|
|
{
|
|
return [
|
|
'symbol' => $value < 0 ? '-' : '+',
|
|
'value' => sprintf('%.2f', abs((float)$value))
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 获取器:付款时间
|
|
* @param $value
|
|
* @return false|string
|
|
*/
|
|
public function getPayTimeAttr($value)
|
|
{
|
|
return format_time($value);
|
|
}
|
|
|
|
/**
|
|
* 获取器:发货时间
|
|
* @param $value
|
|
* @return false|string
|
|
*/
|
|
public function getDeliveryTimeAttr($value)
|
|
{
|
|
return format_time($value);
|
|
}
|
|
|
|
/**
|
|
* 获取器:收货时间
|
|
* @param $value
|
|
* @return false|string
|
|
*/
|
|
public function getReceiptTimeAttr($value)
|
|
{
|
|
return format_time($value);
|
|
}
|
|
|
|
/**
|
|
* 获取器:来源记录的参数
|
|
* @param $json
|
|
* @return array
|
|
*/
|
|
public function getOrderSourceDataAttr($json): array
|
|
{
|
|
return $json ? helper::jsonDecode($json) : [];
|
|
}
|
|
|
|
/**
|
|
* 修改器:来源记录的参数
|
|
* @param array $data
|
|
* @return string
|
|
*/
|
|
public function setOrderSourceDataAttr(array $data): string
|
|
{
|
|
return helper::jsonEncode($data);
|
|
}
|
|
|
|
/**
|
|
* 生成订单号
|
|
* @return string
|
|
*/
|
|
public function orderNo(): string
|
|
{
|
|
return OrderService::createOrderNo();
|
|
}
|
|
|
|
/**
|
|
* 订单详情
|
|
* @param $where
|
|
* @param array $with
|
|
* @return static|array|null
|
|
*/
|
|
public static function detail($where, array $with = [])
|
|
{
|
|
is_array($where) ? $filter = $where : $filter['order_id'] = (int)$where;
|
|
return self::get($filter, $with);
|
|
}
|
|
|
|
/**
|
|
* 批量获取订单列表
|
|
* @param $orderIds
|
|
* @param array $with
|
|
* @return array
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public function getListByIds(array $orderIds, array $with = [])
|
|
{
|
|
$data = $this->getListByInArray('order_id', $orderIds, $with);
|
|
return helper::arrayColumn2Key($data, 'order_id');
|
|
}
|
|
|
|
/**
|
|
* 批量获取订单列表
|
|
* @param $field
|
|
* @param $data
|
|
* @param array $with
|
|
* @return \think\Collection
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
private function getListByInArray($field, $data, array $with = []): \think\Collection
|
|
{
|
|
return $this->with($with)
|
|
->where($field, 'in', $data)
|
|
->where('is_delete', '=', 0)
|
|
->select();
|
|
}
|
|
|
|
/**
|
|
* 根据订单号批量查询
|
|
* @param $orderNos
|
|
* @param array $with
|
|
* @return \think\Collection
|
|
* @throws \think\db\exception\DataNotFoundException
|
|
* @throws \think\db\exception\DbException
|
|
* @throws \think\db\exception\ModelNotFoundException
|
|
*/
|
|
public function getListByOrderNos($orderNos, array $with = []): \think\Collection
|
|
{
|
|
return $this->getListByInArray('order_no', $orderNos, $with);
|
|
}
|
|
|
|
/**
|
|
* 批量更新订单
|
|
* @param $orderIds
|
|
* @param $data
|
|
* @return bool|false
|
|
*/
|
|
public function onBatchUpdate($orderIds, $data): bool
|
|
{
|
|
return static::updateBase($data, [['order_id', 'in', $orderIds]]);
|
|
}
|
|
|
|
/**
|
|
* 确认核销(自提订单)
|
|
* @param int $clerkId 门店店员ID
|
|
* @param bool $syncMpWeixinShipping 同步至微信小程序
|
|
* @return bool|false|int
|
|
*/
|
|
public function confirmExtract(int $clerkId, bool $syncMpWeixinShipping = false)
|
|
{
|
|
if (
|
|
$this['pay_status'] != PayStatusEnum::SUCCESS
|
|
|| $this['delivery_type'] != DeliveryTypeEnum::EXTRACT
|
|
|| $this['delivery_status'] == DeliveryStatusEnum::DELIVERED
|
|
|| in_array($this['order_status'], [OrderStatusEnum::CANCELLED, OrderStatusEnum::APPLY_CANCEL])
|
|
) {
|
|
$this->error = '该订单不满足核销条件';
|
|
return false;
|
|
}
|
|
// 验证订单是否满足发货条件
|
|
if (!$this->verifyDeliveryForExtract($this)) {
|
|
return false;
|
|
}
|
|
return $this->transaction(function () use ($clerkId, $syncMpWeixinShipping) {
|
|
// 更新订单状态:已发货、已收货
|
|
$status = $this->save([
|
|
'extract_clerk_id' => $clerkId, // 核销员
|
|
'delivery_status' => DeliveryStatusEnum::DELIVERED,
|
|
'delivery_time' => time(),
|
|
'receipt_status' => ReceiptStatusEnum::RECEIVED,
|
|
'receipt_time' => time(),
|
|
'order_status' => OrderStatusEnum::COMPLETED
|
|
]);
|
|
// 新增订单核销记录
|
|
ShopOrder::add($this['order_id'], $this['extract_shop_id'], $this['extract_clerk_id']);
|
|
// 执行订单完成后的操作
|
|
$OrderCompleteService = new OrderCompleteService();
|
|
$OrderCompleteService->complete([$this], static::$storeId);
|
|
// 发货信息同步微信平台
|
|
(new ShippingService)->syncMpWeixinShipping($this, [
|
|
// 同步至微信小程序《发货信息录入》
|
|
'syncMpWeixinShipping' => (int)$syncMpWeixinShipping,
|
|
// 物流模式:1物流配送 3虚拟商品 4用户自提
|
|
'logisticsType' => ShippingService::DELIVERY_EXTRACT,
|
|
]);
|
|
return $status;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 验证订单是否满足发货条件
|
|
* @param $order
|
|
* @return bool
|
|
*/
|
|
private function verifyDeliveryForExtract($order): bool
|
|
{
|
|
$orderSource = OrderSourceFactory::getFactory($order['order_source']);
|
|
if (!$orderSource->checkOrderByDelivery($order)) {
|
|
$this->error = $orderSource->getError();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
public function getPayMethodTextAttr($value,$data): string
|
|
{
|
|
return !empty(Method::data()[$data['pay_method']])?Method::data()[$data['pay_method']]['name']:'';//支付方式
|
|
}
|
|
|
|
public function getDeliveryTypeTextAttr($value,$data): string
|
|
{
|
|
return !empty(DeliveryType::data()[$data['delivery_type']])? DeliveryType::data()[$data['delivery_type']]['name']: '';//配送方式
|
|
}
|
|
|
|
public function getTimeTextAttr($value, $data) : int
|
|
{
|
|
$diffTime = 60*60*24;//24小时倒计时
|
|
// 付款状态
|
|
if ($data['pay_status'] == PayStatusEnum::PENDING) {
|
|
return (($data['create_time'] + $diffTime) - time()) * 1000;
|
|
}
|
|
|
|
// 收货状态
|
|
if ($data['receipt_status'] == ReceiptStatusEnum::NOT_RECEIVED) {
|
|
return (($data['delivery_time'] + $diffTime) - time()) * 1000;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function getStorePhoneAttr($value,$data) {
|
|
return Store::get($data['store_id'])->phone ?: '';
|
|
}
|
|
}
|
|
|