一些优化和bug修改

This commit is contained in:
邓皓元 2019-03-25 16:44:41 +08:00
parent 1392e2ee52
commit 93d5a177b3
44 changed files with 427206 additions and 481 deletions

View File

@ -25,6 +25,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
\App\Domains\Virtual\Exports\FlowPoolExport::class => '流量池列表',
\App\Domains\Virtual\Exports\FlowPoolExportDetailExport::class => '流量池明细',
\App\Domains\Virtual\Exports\OrderCardExport::class => '订单卡清单',
\App\Domains\Virtual\Exports\OrderExport::class => '订单导出',
\App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计',
\App\Domains\Stats\Exports\OrderExport::class => '订单统计',
\App\Domains\Stats\Exports\OrderDetailExport::class => '订单明细',

View File

@ -215,3 +215,6 @@ class OrderBaseSync extends Command
return $orderItems;
}
}
echo strtotime('2018-10-01 00:00:00') - strtotime('2018-08-01 00:00:00');

View File

@ -3,6 +3,8 @@
namespace App\Domains\Real\Commands\Sync;
use Illuminate\Support\Facades\DB;
use App\Domains\Real\Repositories\OrderCardPartitionRepository;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository as VirtualOrderCardPartitionRepository;
class RefundSync extends Command
{
@ -26,15 +28,29 @@ class RefundSync extends Command
->where('create_time', '<=', $endtime)
->get();
$refunds->map(function ($item) {
$simArray = [];
$refunds->map(function ($item) use (&$simArray) {
$item->sim = str_to_array($item->sim, ',');
$simArray = array_merge($simArray, $item->sim);
});
DB::transaction(function () use ($refunds) {
foreach ($refunds as $item) {
DB::table('real_order_cards')->whereIn('sim', $item['sim'])->where('created_at', '<=', $item['create_time'])->delete();
DB::table('virtual_order_cards')->whereIn('sim', $item['sim'])->where('created_at', '<=', $item['create_time'])->delete();
DB::table('real_order_cards')
->whereIn('sim', $item->sim)
->where('created_at', '<=', $item->create_time)
->update(['refunded_at' => $item->create_time]);
DB::table('virtual_order_cards_partition')
->whereIn('sim', $item->sim)
->where('created_at', '<=', $item->create_time)
->whereNull('service_start_at')
->update(['refunded_at' => $item->create_time]);
}
});
app(OrderCardPartitionRepository::class)->forgetCached();
app(VirtualOrderCardPartitionRepository::class)->forgetCached();
}
}

View File

@ -49,6 +49,8 @@ class OrderController extends Controller
'pay_channel_name',
'counts',
'shipments',
'refunds',
'unit_price',
'total_price',
'order_at',
'transaction_no',

View File

@ -57,7 +57,8 @@ class OrderCardPartitionRepository extends Repository
real_order_cards_partition.virtual_order_id,
real_order_cards_partition.counts,
virtual_order_cards_partition.company_id,
virtual_order_cards_partition.package_id
virtual_order_cards_partition.package_id,
real_order_cards_partition.refunded_at
';
$this->model = $this->model->selectRaw($select);

View File

@ -42,7 +42,11 @@ class OrderService extends Service
$res = $this->orderRepository->withConditions($conditions)->applyConditions()->paginate($limit);
if (!$res->isEmpty()) {
$cards = $this->orderCardPartitionRepository->selectRaw('order_id,SUM(counts) as counts, SUM(CASE virtual_order_id WHEN 0 THEN 0 ELSE counts END) as shipments')
$cards = $this->orderCardPartitionRepository->selectRaw('
order_id,SUM(counts) as counts,
SUM(CASE virtual_order_id WHEN 0 THEN 0 ELSE counts END) as shipments,
SUM(CASE WHEN refunded_at IS NULL THEN 0 ELSE counts END) as refunds
')
->withConditions(['type' => $conditions['type'], 'order_id', $res->pluck('id')->toArray()])
->groupBy('order_id')->get()->keyBy('order_id')->toArray();
}
@ -58,6 +62,7 @@ class OrderService extends Service
$item->total_price = sprintf('%.02f', $item->total_price/100);
$item->shipments = $cards[$item->id]['shipments'] ?? 0;
$item->counts = $cards[$item->id]['counts'] ?? 0;
$item->refunds = $cards[$item->id]['refunds'] ?? 0;
});
return $res->sortByDesc('order_at')->values();

View File

@ -57,25 +57,12 @@ class CompanyCountService extends Service
$orderRenewalCard = $this->orderCardPartitionRepository->select($select)->where('type', 1)->withConditions($conditions)->groupBy($groupBy)
->get()->pluck('counts', 'company_id')->toArray();
// $orderRenewalPackage = $this->orderCardPartitionRepository->select($select)->where('type', 2)->withConditions($conditions)->groupBy($groupBy)
// ->get()->pluck('counts', 'company_id')->toArray();
// $renewed_counts = array_merge_sum($orderRenewalCard, $orderRenewalPackage);
$renewed_counts = $orderRenewalCard;
if ($conditions['starttime'] && $conditions['endtime']) {
$valid_counts = $this->orderCardPartitionRepository->select($select)->where('service_start_at', '>=', $conditions['starttime'])->where('service_end_at', '<=', $conditions['endtime'])
->groupBy($groupBy)->get()->pluck('counts', 'company_id')->toArray();
} else {
$valid_counts = $this->orderCardPartitionRepository->select($select)->whereNotNull('service_start_at')->groupBy($groupBy)->get()->pluck('counts', 'company_id')->toArray();
}
$companies->map(function ($item) use ($total, $counts, $renewed_counts, $valid_counts) {
$item->total = $total[$item['id']] ?? 0;
$item->counts = $counts[$item['id']] ?? 0;
$item->renewed_counts = $renewed_counts[$item['id']] ?? 0;
$item->valid_counts = $valid_counts[$item['id']] ?? 0;
});
return $companies;

View File

@ -3,17 +3,14 @@
namespace App\Domains\Virtual\Exports;
use App\Core\AbstractExport;
use Illuminate\Support\Facades\DB;
use Dipper\Excel\Concerns\WithRows;
use Dipper\Excel\Concerns\FromQuery;
use Dipper\Excel\Concerns\WithHeadings;
use Illuminate\Database\Eloquent\Collection;
use App\Domains\Virtual\Services\CardService;
use Dipper\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
class OrderCardExport extends AbstractExport implements FromQuery, WithHeadings, WithColumnFormatting
class OrderCardExport extends AbstractExport implements FromQuery, WithHeadings, WithRows, WithColumnFormatting
{
public $conditions;
@ -25,7 +22,7 @@ class OrderCardExport extends AbstractExport implements FromQuery, WithHeadings,
public function query()
{
$builder = app(OrderCardPartitionRepository::class)->forceNoReset()->select(['sim', 'counts'])
$builder = app(OrderCardPartitionRepository::class)->forceNoReset()->select(['sim', 'counts', 'refunded_at'])
->withConditions($this->conditions)->orderBy('sim');
return $builder;
@ -36,16 +33,37 @@ class OrderCardExport extends AbstractExport implements FromQuery, WithHeadings,
return [
'SIM',
'数量',
'退货',
];
}
/**
* @param mixed $row
*
* @return mixed
*/
public function rows($rows)
{
$array = [];
foreach ($rows as $item) {
$array[] = [
$item['sim'],
$item['counts'],
$item['refunded_at'] ? '是' : '',
];
}
return $array;
}
/**
* @return array
*/
public function columnFormats(): array
{
return [
'A' => NumberFormat::FORMAT_NUMBER_00,
'A' => NumberFormat::FORMAT_NUMBER,
'B' => NumberFormat::FORMAT_NUMBER,
];
}

View File

@ -0,0 +1,114 @@
<?php
namespace App\Domains\Virtual\Exports;
use Carbon\Carbon;
use App\Core\AbstractExport;
use Dipper\Excel\Concerns\WithRows;
use Dipper\Excel\Concerns\FromQuery;
use Dipper\Excel\Concerns\WithHeadings;
use App\Domains\Virtual\Services\CommonService;
use Dipper\Excel\Concerns\WithColumnFormatting;
use App\Domains\Virtual\Services\CompanyService;
use App\Domains\Virtual\Services\PackageService;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
use App\Dicts;
class OrderExport extends AbstractExport implements FromQuery, WithHeadings, WithRows, WithColumnFormatting
{
public $conditions;
public function __construct(array $conditions = [])
{
$this->conditions = $conditions;
parent::__construct();
}
public function query()
{
$builder = app(OrderCardPartitionRepository::class)->with('order')->forceNoReset()->select(['sim', 'order_id', 'counts', 'refunded_at'])
->withConditions($this->conditions)->orderBy('order_id');
return $builder;
}
public function headings(): array
{
$headings = [
'订单编号',
'企业名称',
'运营商',
'套餐名称',
'套餐单价',
'支付方式',
'支付流水号',
'订单时间',
'SIM',
'数量',
'退货',
];
if ($this->conditions['type'] == 0) {
array_splice($headings, 6, 0, [
'订单状态',
'收款状态',
]);
}
return $headings;
}
/**
* @param mixed $row
*
* @return mixed
*/
public function rows($rows)
{
$array = [];
$carrierOperators = app(Dicts::class)->get('carrier_operator');
$orderStatues = app(Dicts::class)->get('order_status');
$transactionStatuses = app(Dicts::class)->get('transaction_status');
foreach ($rows as $item) {
$carrier_operator = PackageService::load($item['order']['package_id'])['carrier_operator'];
$data = [
"{$item['order']['sn']}\t",
CompanyService::load($item['order']['company_id'])['name'] ?? '',
$carrierOperators[$carrier_operator],
PackageService::load($item['order']['package_id'])['name'] ?? '',
sprintf('%.02f', $item['order']['unit_price']/100),
CommonService::namePayChannel($item['order']['pay_channel']),
"{$item['order']['transaction_no']}\t",
Carbon::parse($item['order']['order_at'])->format('Y-m-d'),
"{$item['sim']}\t",
$item['counts'],
$item['refunded_at'] ? '是' : '',
];
if ($this->conditions['type'] == 0) {
array_splice($data, 6, 0, [
$orderStatues[$item['order']['order_status']],
$transactionStatuses[$item['order']['transaction_status']],
]);
}
$array[] = $data;
}
return $array;
}
/**
* @return array
*/
public function columnFormats(): array
{
return [
'E' => NumberFormat::FORMAT_NUMBER_00,
];
}
}

View File

@ -5,6 +5,8 @@ use App\Dicts;
use App\Core\Controller;
use Illuminate\Http\Request;
use App\Exceptions\NotExistException;
use App\Exceptions\NotAllowedException;
use App\Domains\Virtual\Exports\OrderExport;
use App\Domains\Config\Services\ConfigService;
use App\Domains\Export\Services\ExportService;
use App\Domains\Virtual\Services\OrderService;
@ -13,6 +15,7 @@ use App\Domains\Virtual\Exports\OrderCardExport;
use App\Domains\Virtual\Services\CompanyService;
use App\Domains\Virtual\Services\PackageService;
use App\Domains\Virtual\Repositories\OrderRepository;
use App\Domains\Virtual\Repositories\OrderCardPartitionRepository;
class OrderController extends Controller
{
@ -38,32 +41,56 @@ class OrderController extends Controller
$conditions = $this->request->all();
$conditions['limit'] = $this->request->get('limit', 20);
if (isset($conditions['sim'])) {
$conditions['sim'] = array_map('intval', array_map('trim', str_to_array($conditions['sim'], "\n")));
}
$orders = $this->orderService->paginate($conditions);
$carrierOperators = $dicts->get('carrier_operator');
$orderStatues = $dicts->get('order_status');
$transactionStatuses = $dicts->get('transaction_status');
$logistics = app(ConfigService::class)->get('logistics');
$orders->transform(function ($item) use ($carrierOperators, $orderStatues, $transactionStatuses) {
$orders->transform(function ($item) use ($carrierOperators, $orderStatues, $transactionStatuses, $logistics) {
return collect([
'id' => $item->id,
'sn' => $item->sn,
'transaction_no' => $item->transaction_no,
'package_id' => $item->package_id,
'package_name' => $item->package->name,
'company_id' => $item->company_id,
'company_name' => $item->company->name,
'pay_channel' => CommonService::namePayChannel($item->pay_channel),
'carrier_operator' => $carrierOperators[$item->package->carrier_operator],
'pay_channel' => $item->pay_channel,
'pay_channel_name' => CommonService::namePayChannel($item->pay_channel),
'carrier_operator' => $item->package->carrier_operator,
'carrier_operator_name' => $carrierOperators[$item->package->carrier_operator],
'unit_price' => $item->unit_price,
'counts' => $item->counts,
'total_price' => $item->total_price,
'custom_price' => $item->custom_price,
'shipments' => $item->shipments,
'refunds' => $item->refunds,
'order_status' => $item->order_status,
'order_status_name' => $orderStatues[$item->order_status],
'transaction_status' => $item->transaction_status,
'transaction_status_name' => $transactionStatuses[$item->transaction_status],
'remark' => $item->remark ?? '',
'order_at' => (string) $item->order_at,
'deleted_at' => (string) $item->deleted_at,
'area' => $item->area ?? [],
'address' => $item->address ?? '',
'contacts' => $item->contacts,
'mobile' => $item->mobile,
'logistics_remark' => $item->logistics_remark ?? '',
'logistics_company_name' => $logistics[$item->logistics_company] ?? '',
'logistics_no' => $item->logistics_no,
'extends' => [
'cancel_remark' => $item->extends['cancel_remark'] ?? '',
'refund_channel' => $item->extends['refund_channel'] ?? '',
'refund_account' => $item->extends['refund_account'] ?? '',
'refund_remark' => $item->extends['refund_remark'] ?? '',
],
]);
});
@ -168,6 +195,37 @@ class OrderController extends Controller
return res(true, '重置成功');
}
/**
* 导出订单.
*
* @return \Illuminate\Http\Response
*/
public function export()
{
$conditions = $this->request->all();
if (isset($conditions['sim'])) {
$conditions['sim'] = array_map('intval', array_map('trim', str_to_array($conditions['sim'], "\n")));
}
try {
$total = app(OrderCardPartitionRepository::class)->withConditions($conditions)->count();
if ($total > 200000) {
throw new NotAllowedException('数据量过大,请筛选后再进行导出');
}
$queue = $total > 30000;
$export = new OrderExport($conditions);
$url = ExportService::store($export, 'public', $queue);
} catch (\Exception $e) {
throw $e;
}
return res($url, '导出成功', 201);
}
/**
* 卡清单.
*

View File

@ -107,6 +107,18 @@ trait OrderCardConcern
// $conditions['month'] = (int)Carbon::parse($conditions['month'])->format('Ym');
// $query->whereRaw("timelines_index(id, sim) @> '{{$conditions['month']}}'");
}
if (isset($conditions['sn'])) {
$query->whereHas('order', function ($relation) use ($conditions) {
$relation->where('sn', 'like', "%{$conditions['sn']}%");
});
}
if (isset($conditions['transaction_no'])) {
$query->whereHas('order', function ($relation) use ($conditions) {
$relation->where('transaction_no', 'like', "%{$conditions['transaction_no']}%");
});
}
});
return $this;

View File

@ -77,7 +77,11 @@ class OrderRepository extends Repository
}
if (!empty($conditions['sn'])) {
$query->where('sn', "%{$conditions['sn']}%");
$query->where('sn', 'like', "%{$conditions['sn']}%");
}
if (!empty($conditions['transaction_no'])) {
$query->where('transaction_no', 'like', "%{$conditions['transaction_no']}%");
}
if (isset($conditions['order_status'])) {
@ -110,6 +114,14 @@ class OrderRepository extends Repository
});
}
if (isset($conditions['sim'])) {
$conditions['sim'] = array_wrap($conditions['sim']);
$query->whereHas('cards', function ($relation) use ($conditions) {
$relation->whereIn('sim', $conditions['sim']);
});
}
if (isset($conditions['starttime'])) {
$query->where('order_at', '>=', Carbon::parse($conditions['starttime']));
}

View File

@ -49,6 +49,7 @@ $router->group(['prefix' => 'virtual', 'as' => 'virtual', 'middleware' => ['admi
$router->post('/orders/update/{id}', ['as' => 'orders.update', 'uses' => 'OrderController@update']);
$router->post('/orders/destroy', ['as' => 'orders.destroy', 'uses' => 'OrderController@destroy']);
$router->post('/orders/reset', ['as' => 'orders.reset', 'uses' => 'OrderController@reset']);
$router->get('/orders/export', ['as' => 'orders.export', 'uses' => 'OrderController@export']);
$router->get('/orders/cards', ['as' => 'orders.cards', 'uses' => 'OrderController@cards']);
$router->get('/orders/cards-export', ['as' => 'orders.cardsExport', 'uses' => 'OrderController@cardsExport']);

View File

@ -61,7 +61,10 @@ class OrderService extends Service
$orderShipments = $this->orderCardPartitionRepository->select([
'order_id',
DB::raw('SUM(counts) as counts'),
])->withConditions($conditions)->groupBy('order_id')->get()->keyBy('order_id');
DB::raw('SUM(CASE WHEN refunded_at IS NULL THEN 0 ELSE 1 END) as refunds')
])->withConditions([
'order_id' => $res->pluck('id')->toArray(),
])->groupBy('order_id')->get()->keyBy('order_id');
$res->map(function ($item) use ($orderShipments) {
$item->company = CompanyService::load($item->company_id);
@ -70,6 +73,7 @@ class OrderService extends Service
$item->total_price = sprintf('%.02f', $item->total_price/100);
$item->custom_price = sprintf('%.02f', $item->custom_price/100);
$item->shipments = $orderShipments[$item->id]['counts'] ?? 0;
$item->refunds = $orderShipments[$item->id]['refunds'] ?? 0;
});
return $res;
@ -134,12 +138,15 @@ class OrderService extends Service
'address.required' => '请选择收货地址',
];
if (isset($attributes['unit_price'])) {
$attributes['unit_price'] = intval($attributes['unit_price'] * 100);
}
if (!$attributes['id']) {
$attributes['sn'] = $attributes['sn'] ?: $this->generateSn();
$attributes['transaction_no'] = $attributes['transaction_no'] ?: $this->generateTransactionNo($attributes['pay_channel']);
if ($attributes['company_id'] && $attributes['package_id'] && isset($attributes['unit_price'])) {
$attributes['unit_price'] = intval($attributes['unit_price'] * 100);
$product = ProductService::getProduct($attributes['company_id'], $attributes['package_id'], $attributes['unit_price']);
} elseif ($attributes['product_id']) {
$product = app(ProductRepository::class)->find($attributes['product_id']);
@ -185,7 +192,7 @@ class OrderService extends Service
return ['sim' => $item['sim'], 'virtual_activated_at' => $attributes['order_at']];
}, $attributes['selected']);
Card::upsert($array, ['sim', 'deleted'], ['virtual_activated_at']);
Card::upsert($array, 'sim', ['virtual_activated_at']);
$attributes['type'] = 0;
$originType = $attributes['type'];
@ -208,7 +215,20 @@ class OrderService extends Service
$attributes['selected'] = $selected;
}
if (!$attributes['id']) {
if ($attributes['id']) {
if (!$node = $this->orderRepository->find($attributes['id'])) {
throw new NotExistException('订单不存在或已删除');
}
if (!empty($attributes['extends']) && is_array($attributes['extends'])) {
$attributes['extends'] = array_merge($node->extends ?: [], $attributes['extends']);
}
$this->orderRepository->setModel($node)->update($attributes);
} else {
$maxId = Order::withTrashed()->max('id');
$attributes['id'] = ++$maxId;
if ($product->company_id != $attributes['company_id']) {
throw new NotAllowedException('非法操作');
}
@ -222,18 +242,6 @@ class OrderService extends Service
$node = $this->orderRepository->create($attributes);
}
if ($attributes['id']) {
if (!$node = $this->orderRepository->find($attributes['id'])) {
throw new NotExistException('订单不存在或已删除');
}
if (!empty($attributes['extends']) && is_array($attributes['extends'])) {
$attributes['extends'] = array_merge($node->extends ?: [], $attributes['extends']);
}
$this->orderRepository->setModel($node)->update($attributes);
}
if ($attributes['selected']) {
try {
$this->upsertOrderCards($attributes['selected'], $node);
@ -552,6 +560,16 @@ class OrderService extends Service
}
if ($node->type === 0) {
$sql = 'SELECT COUNT(*) as counts FROM virtual_order_cards_partition WHERE type != 0 AND sim In (
SELECT sim FROM virtual_order_cards_partition WHERE order_id = ?
)';
$counts = DB::select($sql, [$id])[0]->counts;
if ($counts) {
throw new NotAllowedException('订单中的卡已存在于其他类型订单中,不能进行重置');
}
// 转销售重置
$sql = 'UPDATE virtual_order_cards_partition SET sim=original_sim,original_sim=0
WHERE original_sim IN (
@ -655,7 +673,7 @@ class OrderService extends Service
{
$conditions['limit'] = $conditions['limit'] ?? 20;
$cards = $this->orderCardPartitionRepository->select(['sim', 'counts'])
$cards = $this->orderCardPartitionRepository->withRefunded()->select(['sim', 'counts', 'refunded_at'])
->withConditions($conditions)->orderBy('sim')
->paginate($conditions['limit']);

View File

@ -129,6 +129,6 @@ class Order extends Model
public function cards()
{
return $this->belongsToMany(Card::class, 'virtual_order_cards', 'sim', 'sim');
return $this->hasMany(OrderCardPartition::class, 'order_id', 'id');
}
}

View File

@ -5,6 +5,7 @@ namespace App\Models\Virtual;
use App\Core\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\Virtual\Relations\OrderRelations;
use App\Models\Virtual\Scopes\OrderCardPartitionNotRefundedScope;
/**
* App\Models\Virtual\OrderCardPartition
@ -56,4 +57,16 @@ class OrderCardPartition extends Model
use SoftDeletes, OrderRelations;
protected $table = 'virtual_order_cards_partition';
/**
* 模型的「启动」方法
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::addGlobalScope(new OrderCardPartitionNotRefundedScope);
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Models\Virtual\Scopes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Builder;
class OrderCardPartitionNotRefundedScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
$builder->whereNull('refunded_at');
}
/**
* Extend the query builder with the needed functions.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return void
*/
public function extend(Builder $builder)
{
$builder->macro('refunded', function (Builder $builder) {
return $builder->withoutGlobalScope($this)->whereNotNull('refunded_at');
});
$builder->macro('withRefunded', function (Builder $builder, $withRefunded = true) {
if (! $withRefunded) {
return $builder->withoutRefunded();
}
return $builder->withoutGlobalScope($this);
});
}
}

View File

@ -27,7 +27,7 @@ class CreateRealOrderCardsTable extends Migration
$table->integer('counts')->unsigned()->default(0)->comment('数量');
$table->integer('unit_price')->unsigned()->default(0)->comment('单价');
$table->integer('virtual_order_id')->unsigned()->default(0)->comment('VD 订单ID');
$table->tinyInteger('refunded')->unsigned()->default(0)->comment('退货标志 0:未退货 1:已退货');
$table->timestamp('refunded_at')->nullable()->comment('退货时间');
$table->timestamps();
$table->softDeletes();

View File

@ -35,7 +35,7 @@ class CreateVirtualOrderCardsTables extends Migration
$table->timestamp('service_start_at')->nullable()->comment('服务开始时间');
$table->timestamp('service_end_at')->nullable()->comment('服务结束时间');
$table->bigInteger('original_sim')->unsigned()->default(0)->comment('原始sim号');
$table->tinyInteger('refunded')->unsigned()->default(0)->comment('退货标志 0:未退货 1:已退货');
$table->timestamp('refunded_at')->nullable()->comment('退货时间');
$table->timestamps();
$table->softDeletes();

View File

@ -167,12 +167,14 @@ BEGIN
OVERLAY(cards.sim::TEXT PLACING (4 + t.counts)::TEXT FROM 4 FOR 1)::INT8 as sim,
OVERLAY(imsi::TEXT PLACING (1 + t.counts)::TEXT FROM 3 FOR 1) as imsi,
OVERLAY(iccid::TEXT PLACING (1 + t.counts)::TEXT FROM 5 FOR 1) as iccid,
carrier_operator, activated_at, virtual_activated_at, type, real_company_id, cancelled_at, created_at, updated_at
carrier_operator, activated_at, virtual_activated_at, 1 as type, cancelled_at, created_at, updated_at
FROM cards JOIN (
SELECT OVERLAY(sim::TEXT PLACING $1 FROM 4 FOR 1), COUNT ( * ) AS counts, MIN ( sim ) AS sim FROM cards WHERE sim::TEXT SIMILAR TO $2 GROUP BY 1
) AS t ON t.sim = cards.sim
), new_inserts AS (
INSERT INTO cards SELECT sim,imsi,iccid, carrier_operator, activated_at, virtual_activated_at, type, real_company_id, cancelled_at, created_at, updated_at FROM new_cards
INSERT INTO cards
(sim,imsi,iccid, carrier_operator, activated_at, virtual_activated_at, type, cancelled_at, created_at, updated_at)
SELECT sim,imsi,iccid, carrier_operator, activated_at, virtual_activated_at, type, cancelled_at, created_at, updated_at FROM new_cards
)
UPDATE virtual_order_cards_partition SET original_sim=t.original_sim,sim=t.sim FROM (SELECT sim,original_sim FROM new_cards) as t
WHERE virtual_order_cards_partition.sim=t.original_sim';

View File

@ -13,6 +13,17 @@ export function index(data) {
});
}
/**
* [exportOrders 订单列表]
* @param {[type]} data [description]
* @return {[type]} [description]
*/
export function exportOrders(data) {
return service.get('api/virtual/orders/export', {
params: data
});
}
/**
* [show 订单详情]
* @param {[type]} id [description]

View File

@ -1057,6 +1057,7 @@ export default {
}, 1800);
},
clear() {
this.scrollTop = 0;
this.currentIndex = -1;
this.placeholderHeight = 0;
this.topPlaceholderHeight = 0;
@ -1161,6 +1162,7 @@ export default {
watch: {
data: {
handler() {
this.clear();
const oldDataLen = this.rebuildData.length;
this.objData = this.makeObjData();
this.rebuildData = this.makeDataWithSortAndFilter();

View File

@ -20,6 +20,8 @@ export default {
return array;
}
console.log(array);
const pinyinEngine = new PinyinEngine(array, [key]);
let res = [];

View File

@ -48,7 +48,7 @@ const getters = {
}, 0);
},
getFilterUsedCards: () => (cards) => {
return cards.filter(item => item.virtual_order_id === 0);
return cards.filter(item => item.virtual_order_id === 0 && !item.refunded_at);
},
getSelectedByOrderId: (state) => (order_id) => {
if (typeof order_id !== 'object') {

View File

@ -11,15 +11,17 @@
<ui-loading :show="page_loading.show"></ui-loading>
<Steps :current="current" :status="status">
<Step
:title="item.title"
:content="item.content"
v-for="(item, index) in steps"
:key="index"
></Step>
<Step v-for="(item, index) in steps" :key="index" :title="item.title">
<div class="ivu-steps-content" @click="changeStep(index)">{{item.content}}</div>
</Step>
</Steps>
<Row type="flex" justify="center" class="umar-t15" v-if="steps[current] && steps[current]['datePicker']">
<Row
type="flex"
justify="center"
class="umar-t15"
v-if="steps[current] && steps[current]['datePicker']"
>
<DatePicker
:editable="false"
placeholder="请选择时间"

View File

@ -14,6 +14,10 @@
<Button @click="openEdit(true)" icon="md-add" type="primary">执行同步</Button>
</div>
<div class="handle-item">
<Button @click="openRefund(true)" icon="md-arrow-dropleft" type="primary">退货同步</Button>
</div>
<div class="handle-item">
<Button @click="search.show=!search.show" ghost icon="ios-search" type="primary">搜索</Button>
</div>
@ -28,7 +32,7 @@
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable placeholder="命令类型" v-model="options.command">
<Option :value="index" v-for="(name, index) in commands" :key="index">{{name}}</Option>
<Option :value="index" v-for="(name, index) in commands" :key="index">{{name}}</Option>
</Select>
</li>
@ -70,12 +74,16 @@
</div>
<ui-edit
:data="editObj.data"
:isUpdate.sync="editObj.isUpdate"
:show.sync="editObj.show"
@add-success="index"
@update-success="index(list_data.current_page)"
></ui-edit>
<ui-refund
:show.sync="refundObj.show"
@add-success="index"
@update-success="index(list_data.current_page)"
></ui-refund>
</div>
</template>

View File

@ -61,7 +61,7 @@ export default {
content: '未开始'
},
status: 'wait',
month: this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM')
month: this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM')
};
},
methods: {
@ -123,7 +123,9 @@ export default {
clearInterval(interval);
});
},
changeStep(value) {
this.current = value;
},
visibleChange(bool) {
if (!bool) {
this.$emit('update:show', false);

View File

@ -1,7 +1,8 @@
export default {
name: 'RealSync',
components: {
UiEdit: resolve => require(['views/artisan/real-sync/edit'], resolve)
UiEdit: resolve => require(['views/artisan/real-sync/edit'], resolve),
UiRefund: resolve => require(['views/artisan/real-sync/refund'], resolve)
},
data() {
return {
@ -10,7 +11,8 @@ export default {
'real:sync-company': '同步RD企业数据',
'real:sync-mongo': '同步卡基础信息数据',
'real:sync-order': '同步RD基础订单数据',
'real:sync-package': '同步RD套餐数据'
'real:sync-package': '同步RD套餐数据',
'real:sync-refund': '同步RD退货数据'
},
options: {
command: null,
@ -18,8 +20,10 @@ export default {
},
list_data: null,
editObj: {
show: false,
data: null
show: false
},
refundObj: {
show: false
},
search: {
show: false
@ -87,6 +91,16 @@ export default {
};
},
/**
* [openRefund 打开编辑弹窗]
* @return {[type]} [description]
*/
openRefund(bool) {
this.refundObj = {
show: bool
};
},
/**
* [request 刷新]
* @return {[type]} [description]

View File

@ -0,0 +1,115 @@
export default {
props: {
show: {
type: Boolean,
default: false
}
},
watch: {
show(bool) {
this.my_show = bool;
if (bool) {
this.current = 0;
this.status = 'wait';
this.circle.percent = 0;
this.circle.content = '未开始';
}
}
},
data() {
return {
my_show: false,
loading: false,
disabled: false,
steps: [
{
'title': '同步退货',
'content': '指定月份的退货数据',
'command': 'real:sync-refund',
'max': 100,
'datePicker': true
}
],
current: 0,
circle: {
percent: 0,
content: '未开始'
},
status: 'wait',
month: this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM')
};
},
methods: {
call() {
if (!this.steps[this.current]) {
return;
}
this.disabled = true;
let params = {};
params.command = this.steps[this.current]['command'];
if (!params.command) {
return this.$Message.error('命令错误');
}
if (this.steps[this.current]['datePicker']) {
if (!this.month) {
return this.$Message.error('请选择要同步的月份');
}
params.parameters = {
month: this.moment(this.month).format('YYYY-MM')
};
}
let max = this.steps[this.current]['max'];
this.status = 'process';
this.circle.content = '正在' + this.steps[this.current]['title'];
let interval = setInterval(() => {
if (this.circle.percent < max) {
this.circle.percent++;
}
}, 1000);
service.post('/api/artisan/call', params).then(res => {
if (res.code == 0) {
this.circle.content = this.steps[this.current]['title'] + '完成';
this.circle.percent = max;
this.status = (max == 100) ? 'finish' : 'wait';
this.current++;
} else {
this.circle.content = '同步失败';
this.circle.percent = this.steps[this.current - 1] ? this.steps[this.current - 1]['max'] : 0;
this.status = 'error';
}
this.disabled = false;
clearInterval(interval);
}).catch((err) => {
this.circle.content = '同步失败';
this.circle.percent = this.steps[this.current - 1]['max'];
this.status = 'error';
this.disabled = false;
clearInterval(interval);
});
},
changeStep(value) {
this.current = value + 1;
},
visibleChange(bool) {
if (!bool) {
this.$emit('update:show', false);
}
},
clear() {
this.my_show = false;
}
}
};

View File

@ -0,0 +1,64 @@
<template>
<Modal
:closable="false"
:mask-closable="false"
:title="'RD数据同步'"
@on-visible-change="visibleChange"
v-model="my_show"
:width="1200"
>
<div class="page-edit-wrap uinn-lr20">
<ui-loading :show="page_loading.show"></ui-loading>
<Steps :current="current" :status="status">
<Step
:title="item.title"
:content="item.content"
v-for="(item, index) in steps"
:key="index"
></Step>
</Steps>
<Row type="flex" justify="center" class="umar-t15" v-if="steps[current] && steps[current]['datePicker']">
<DatePicker
:editable="false"
placeholder="请选择时间"
placement="bottom-start"
type="month"
v-model.trim="month"
></DatePicker>
</Row>
<Row type="flex" justify="center" class="umar-t15">
<Circle :size="250" :percent="circle.percent" stroke-linecap="square">
<div class="circle-text">
<h1>{{circle.percent}}%</h1>
<br>
<p>{{circle.content}}</p>
</div>
</Circle>
</Row>
</div>
<footer class="ta-c" slot="footer">
<Button @click="clear" class="w-80" ghost type="primary" :disabled="disabled">取消</Button>
<Button
:loading="loading"
@click="call"
class="w-80"
type="primary"
v-if="this.status === 'wait'"
:disabled="disabled"
>{{ current ? '下一步' : '开始同步'}}</Button>
<Button
:loading="loading"
@click="clear"
class="w-80"
type="primary"
v-if="this.status === 'finish'"
>完成</Button>
</footer>
</Modal>
</template>
<script src="./js/refund.js"></script>

View File

@ -61,7 +61,6 @@
<th><div class="ivu-table-cell">{{stats.total}}</div></th>
<th><div class="ivu-table-cell">{{stats.counts}}</div></th>
<th><div class="ivu-table-cell">{{stats.renewed_counts}}</div></th>
<th><div class="ivu-table-cell">{{stats.valid_counts}}</div></th>
<th v-if="page.limit > 12" rowspan="1"></th>
</tr>
</thead>

View File

@ -4,11 +4,14 @@ export default {
data() {
return {
search: {
show: false
show: true
},
options: {
name: '',
time: ''
time: [
this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('1', 'months').endOf('month').format('YYYY-MM-DD')
]
},
data: [],
list: [],
@ -42,11 +45,6 @@ export default {
title: '续费用户数',
key: 'renewed_counts',
width: 120
},
{
title: '服务期内用户数',
key: 'valid_counts',
width: 200
}
]
};
@ -104,7 +102,11 @@ export default {
resetSearch() {
for (let k in this.options) {
this.options[k] = '';
if (k === 'month') {
this.options[k] = this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM');
} else {
this.options[k] = '';
}
}
this.index();
@ -120,8 +122,7 @@ export default {
this.stats = {
total: sumBy(this.list, 'total'),
counts: sumBy(this.list, 'counts'),
renewed_counts: sumBy(this.list, 'renewed_counts'),
valid_counts: sumBy(this.list, 'valid_counts')
renewed_counts: sumBy(this.list, 'renewed_counts')
};
this.$nextTick(() => {

View File

@ -20,7 +20,7 @@ export default {
company_name: '',
package_name: '',
type: [],
month: this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM')
month: this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM')
},
data: [],
list: [],
@ -185,7 +185,7 @@ export default {
},
params() {
if (!this.options.month) {
this.options.month = this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM');
this.options.month = this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM');
}
if (!this.options.type.length) {
@ -223,7 +223,7 @@ export default {
resetSearch() {
for (let k in this.options) {
if (k === 'month') {
this.options[k] = this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM');
this.options[k] = this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM');
} else if (k === 'type') {
this.options[k] = [];
} else {

View File

@ -20,8 +20,8 @@ export default {
package_name: '',
pay_channel: '',
time: [
this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('2', 'months').endOf('month').format('YYYY-MM-DD')
this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('1', 'months').endOf('month').format('YYYY-MM-DD')
]
},
data: [],
@ -187,8 +187,8 @@ export default {
for (let k in this.options) {
if (k === 'time') {
this.options[k] = [
this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('2', 'months').endOf('month').format('YYYY-MM-DD')
this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('1', 'months').endOf('month').format('YYYY-MM-DD')
];
} else {
this.options[k] = '';

View File

@ -13,7 +13,7 @@ export default {
company_name: '',
name: '',
carrier_operator: '',
month: this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM')
month: this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM')
},
list_data: null,
reals: [],
@ -294,7 +294,7 @@ export default {
params() {
if (!this.options.month) {
this.options.month = this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM');
this.options.month = this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM');
}
let params = {
@ -360,7 +360,7 @@ export default {
resetSearch() {
for (let k in this.options) {
if (k === 'month') {
this.options[k] = this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM');
this.options[k] = this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM');
} else {
this.options[k] = '';
}

View File

@ -12,26 +12,28 @@
<div class="search-wrap">
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable placeholder="运营商" v-model="params.carrier_operator">
<Option :value="0">联通</Option>
<Option :value="1">移动</Option>
<Option :value="2">电信</Option>
</Select>
<Input clearable placeholder="订单编号" v-model.trim="params.sn"></Input>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="支付方式" v-model="params.pay_channel">
<Option :value="0">联通</Option>
<Option :value="1">移动</Option>
<Option :value="2">电信</Option>
<Input clearable placeholder="流水号" v-model.trim="params.transaction_no"></Input>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="支付方式" v-model="params.pay_channel_name">
<Option :value="'银行转账'">银行转账</Option>
<Option :value="'微信支付'">微信支付</Option>
<Option :value="'支付宝'">支付宝</Option>
<Option :value="'余额支付'">余额支付</Option>
<Option :value="'天猫续费'">天猫续费</Option>
</Select>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="使用状态" v-model="params.used">
<Option :value="0">未使用</Option>
<Option :value="1">部分使</Option>
<Option :value="2">全部使</Option>
<Option :value="1">部分</Option>
<Option :value="2">不可</Option>
</Select>
</li>
@ -44,15 +46,6 @@
v-model.trim="params.time"
></DatePicker>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index()" ghost type="primary">立即搜索</Button>
</div>
<div class="handle-item">
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
</div>
</li>
</ul>
<ul class="handle-wraper">
@ -72,6 +65,14 @@
</Select>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="运营商" v-model="params.carrier_operator">
<Option :value="0">联通</Option>
<Option :value="1">移动</Option>
<Option :value="2">电信</Option>
</Select>
</li>
<li class="handle-item w-250">
<Select
icon="ios-search"
@ -91,6 +92,19 @@
<li class="handle-item w-250">
<Input placeholder="SIM" type="textarea" v-model="params.sim"/>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index()" ghost type="primary">立即搜索</Button>
</div>
<div class="handle-item">
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
</div>
<div class="handle-item">
<Button @click="exportOrders" type="warning">导出订单</Button>
</div>
</li>
</ul>
</div>
</div>
@ -114,9 +128,12 @@
<a @click="sort" class="umar-r10">
<b>已选 {{counts}} </b>
</a>
<a @click="clearSelect">
<a @click="clearSelect" class="umar-r10">
<b>清空</b>
</a>
<a @click="exportOrders">
<b>导出</b>
</a>
</Row>
<Row v-else>
<b class="umar-r10"> {{filterTotal}} / {{total}} </b>

View File

@ -1,182 +1,35 @@
<template>
<Drawer
:mask-closable="false"
@on-visible-change="visibleChange"
title="订单详情"
v-model="my_show"
width="500"
>
<div class="page-detail-wrap" v-if="data">
<Divider>订单信息</Divider>
<ul>
<li class="ui-list">
<div class="ui-list-title">订单编号:</div>
<div class="ui-list-content">{{data.sn}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">企业名称:</div>
<div class="ui-list-content">{{data.company_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">运营商:</div>
<div class="ui-list-content">{{data.carrier_operator_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">套餐名称:</div>
<div class="ui-list-content">{{data.package_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">套餐单价:</div>
<div class="ui-list-content">{{data.unit_price}} /服务周期</div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单卡量:</div>
<div class="ui-list-content">{{data.counts}} </div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单总计:</div>
<div class="ui-list-content">{{data.custom_price}} </div>
</li>
<li class="ui-list">
<div class="ui-list-title">支付方式:</div>
<div class="ui-list-content">{{data.pay_channel}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">支付流水号:</div>
<div class="ui-list-content">{{data.transaction_no}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">下单时间:</div>
<div class="ui-list-content">{{data.order_at}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单状态:</div>
<div class="ui-list-content">
<Button ghost size="small" type="primary">{{data.order_status_name}}</Button>
</div>
</li>
<li class="ui-list" v-if="data.order_status === 1">
<div class="ui-list-title">取消理由:</div>
<div class="ui-list-content">{{data.extends.cancel_remark}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">支付状态:</div>
<div class="ui-list-content">
<Button
ghost
size="small"
type="info"
v-if="data.transaction_status === 0"
>{{data.transaction_status_name}}</Button>
<Button
ghost
size="small"
type="success"
v-if="data.transaction_status === 1"
>{{data.transaction_status_name}}</Button>
<Button
ghost
size="small"
type="error"
v-if="data.transaction_status === 2"
>{{data.transaction_status_name}}</Button>
</div>
</li>
<li class="ui-list" v-if="data.transaction_status === 2">
<div class="ui-list-title">退款方式:</div>
<div class="ui-list-content">{{data.extends.refund_channel}}</div>
</li>
<li class="ui-list" v-if="data.transaction_status === 2">
<div class="ui-list-title">退款账号:</div>
<div class="ui-list-content">{{data.extends.refund_account}}</div>
</li>
<li class="ui-list" v-if="data.transaction_status === 2">
<div class="ui-list-title">退款备注:</div>
<div class="ui-list-content">{{data.extends.refund_remark}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单备注:</div>
<div class="ui-list-content">{{data.remark}}</div>
</li>
</ul>
<Divider>物流信息</Divider>
<ul>
<li class="ui-list">
<div class="ui-list-title">收货地址:</div>
<div class="ui-list-content">{{data.area ? data.area.join(' ') : ''}} {{data.address}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货人:</div>
<div class="ui-list-content">{{data.contacts}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">联系电话:</div>
<div class="ui-list-content">{{data.mobile}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">物流备注:</div>
<div class="ui-list-content">{{data.logistics_remark}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">物流公司:</div>
<div class="ui-list-content">{{data.logistics_company_name}}</div>
</li>
<li class="ui-list">
<div class="ui-list-title">物流单号:</div>
<div class="ui-list-content">{{data.logistics_no}}</div>
</li>
</ul>
<Divider>
<span class="umar-r10">出库卡清单</span>
<Button @click="exportExcel" icon="md-download">导出</Button>
</Divider>
<Row class="ta-r"></Row>
<Table
class="umar-t10"
:columns="columns"
:data="cards"
:loading="loading"
disabled-hover
stripe
border
size="small"
></Table>
<Row justify="center" class="umar-tb10 ta-c">
<Page
:current="Number(page.page)"
:page-size="Number(page.limit)"
:page-size-opts="[10, 20, 50, 100]"
:total="Number(page.total)"
@on-change="changePage"
@on-page-size-change="changeLimit"
show-total
size="small"
></Page>
</Row>
<Drawer :mask-closable="false" @on-visible-change="visibleChange" v-model="my_show" width="500">
<div slot="header">
<div class="ivu-drawer-header-inner uinn-tb5" style="height: 30px;">
<span class="umar-r10">清单</span>
<Button @click="exportExcel" icon="md-download" size="small">导出</Button>
</div>
</div>
<Table
class="umar-t10"
:columns="columns"
:data="cards"
:loading="loading"
disabled-hover
stripe
border
size="small"
></Table>
<Row justify="center" class="umar-tb10 ta-c">
<Page
:current="Number(page.page)"
:page-size="Number(page.limit)"
:page-size-opts="[10, 20, 50, 100]"
:total="Number(page.total)"
@on-change="changePage"
@on-page-size-change="changeLimit"
show-total
size="small"
></Page>
</Row>
</Drawer>
</template>

View File

@ -145,41 +145,42 @@
<DatePicker type="datetime" placeholder="请选择时间" v-model.trim="params.order_at"></DatePicker>
</div>
</li>
<div v-if="type === 0">
<li class="ui-list">
<div class="ui-list-title">收货人</div>
<div class="ui-list-content">
<Input :maxlength="32" v-model.trim="params.contacts"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货人</div>
<div class="ui-list-content">
<Input :maxlength="32" v-model.trim="params.contacts"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">联系电话</div>
<div class="ui-list-content">
<Input :maxlength="11" v-model.trim="params.mobile"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">联系电话</div>
<div class="ui-list-content">
<Input :maxlength="11" v-model.trim="params.mobile"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货区域</div>
<div class="ui-list-content">
<al-selector :data-type="'name'" :level="2" v-model="params.area"/>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货区域</div>
<div class="ui-list-content">
<al-selector :data-type="'name'" :level="2" v-model="params.area"/>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货地址</div>
<div class="ui-list-content">
<Input :maxlength="255" v-model.trim="params.address"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">收货地址</div>
<div class="ui-list-content">
<Input :maxlength="255" v-model.trim="params.address"></Input>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单备注</div>
<div class="ui-list-content">
<Input v-model.trim="params.remark" type="textarea" placeholder="..."/>
</div>
</li>
<li class="ui-list">
<div class="ui-list-title">订单备注</div>
<div class="ui-list-content">
<Input v-model.trim="params.remark" type="textarea" placeholder="..."/>
</div>
</li>
</div>
</ul>
</div>

View File

@ -43,33 +43,36 @@
</li>
<li class="handle-item w-250">
<AutoComplete
@on-search="handleCompleteCompanies"
icon="ios-search"
placeholder="企业名称"
v-model.trim="params.company_name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledCompanies"
>{{ item.name }}</Option>
</AutoComplete>
<Input clearable placeholder="流水号" v-model.trim="params.transaction_no"></Input>
</li>
<li class="handle-item w-120" v-if="type === 0">
<Select clearable placeholder="订单状态" v-model="params.order_status">
<Option :value="0">已下单</Option>
<Option :value="1">已取消</Option>
<Option :value="2">已排单</Option>
<Option :value="3">已出库</Option>
<Option :value="4">已发货</Option>
<Option :value="5">已签收</Option>
</Select>
</li>
<li class="handle-item w-120" v-if="type === 0">
<Select clearable placeholder="收款状态" v-model="params.transaction_status">
<Option :value="0">未收款</Option>
<Option :value="1">已收款</Option>
<Option :value="2">已退款</Option>
</Select>
</li>
<li class="handle-item w-250">
<AutoComplete
@on-search="handleCompletePackages(type)"
icon="ios-search"
placeholder="套餐名称"
v-model.trim="params.package_name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledPackages"
>{{ item.name }}</Option>
</AutoComplete>
<Select clearable placeholder="支付方式" v-model="params.pay_channel">
<Option :value="'bank'">银行转账</Option>
<Option :value="'wx'">微信支付</Option>
<Option :value="'alipay'">支付宝</Option>
<Option :value="'account'">余额支付</Option>
<Option :value="'tmall'">天猫续费</Option>
</Select>
</li>
<li class="handle-item w-250">
@ -85,22 +88,18 @@
<ul class="handle-wraper">
<li class="handle-item w-250">
<Select clearable placeholder="订单状态" v-model="params.order_status">
<Option :value="0">已下单</Option>
<Option :value="1">已取消</Option>
<Option :value="2">已排单</Option>
<Option :value="3">已出库</Option>
<Option :value="4">已发货</Option>
<Option :value="5">已签收</Option>
</Select>
</li>
<li class="handle-item w-250">
<Select clearable placeholder="收款状态" v-model="params.transaction_status">
<Option :value="0">未收款</Option>
<Option :value="1">已收款</Option>
<Option :value="2">已退款</Option>
</Select>
<AutoComplete
@on-search="handleCompleteCompanies"
icon="ios-search"
placeholder="企业名称"
v-model.trim="params.company_name"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledCompanies"
>{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
@ -111,6 +110,25 @@
</Select>
</li>
<li class="handle-item w-250">
<AutoComplete
@on-search="handleMyCompletePackages"
icon="ios-search"
placeholder="套餐名称"
v-model.trim="params.package_name"
placement="bottom"
>
<Option
:key="item.id"
:value="item.name"
v-for="item in completeHandledPackages"
>{{ item.name }}</Option>
</AutoComplete>
</li>
<li class="handle-item w-250">
<Input placeholder="SIM" type="textarea" v-model="params.sim"/>
</li>
<li class="f-r">
<div class="handle-item">
<Button @click="index(1)" ghost type="primary">立即搜索</Button>
@ -118,6 +136,9 @@
<div class="handle-item">
<Button @click="resetSearch" ghost type="warning">重置搜索</Button>
</div>
<div class="handle-item">
<Button @click="exportOrders" type="warning">导出订单</Button>
</div>
</li>
</ul>
</div>

View File

@ -48,12 +48,15 @@ export default {
page: 1
},
params: {
sn: '',
company_name: '',
package_name: '',
carrier_operator: '',
transaction_no: '',
pay_channel_name: '',
time: [
this.moment().subtract('2', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('2', 'months').endOf('month').format('YYYY-MM-DD')
this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('1', 'months').endOf('month').format('YYYY-MM-DD')
],
used: '',
sim: ''
@ -63,6 +66,35 @@ export default {
showOrders: [],
showCards: [],
orderColumns: [
{
type: 'expand',
width: 50,
render: (h, context) => {
let row = context.row;
let html = [];
let col = [];
col.push(h('Col', { props: { span: 8 }, class: [] }, '订单编号: ' + row.sn));
col.push(h('Col', { props: { span: 8 }, class: [] }, '支付流水号: ' + row.transaction_no));
html.push(h('Row', { class: [] }, col));
if (this.type === 0) {
col = [];
col.push(h('Col', { props: { span: 8 }, class: [] }, '收货人: ' + row.contacts));
col.push(h('Col', { props: { span: 8 }, class: [] }, '联系电话: ' + row.mobile));
html.push(h('Row', { class: [] }, col));
col = [];
col.push(h('Col', { props: { span: 16 }, class: [] }, '收货地址: ' + row.address));
html.push(h('Row', { class: [] }, col));
}
return h('div', { class: ['fz-13'] }, html);
}
},
{
width: 60,
align: "center",
@ -81,7 +113,7 @@ export default {
on: {
input: value => {
let order_id = this.showOrders.filter(el => {
return el.shipments !== el.counts;
return el.counts - el.shipments - el.refunds > 0;
}).map(item => {
return item.id;
});
@ -104,7 +136,7 @@ export default {
props: {
indeterminate: value && !!indeterminate,
value: value,
disabled: context.row.counts === context.row.shipments
disabled: context.row.counts - context.row.shipments - context.row.refunds <= 0
},
on: {
input: value => {
@ -115,12 +147,11 @@ export default {
}
},
{
title: "订单编号",
key: "sn",
width: 220,
title: "ID",
key: "id",
width: 80,
sortable: true
},
{
title: "企业名称",
key: "company_name",
@ -144,28 +175,33 @@ export default {
width: 90
},
{
title: "数量",
title: "可用量",
key: "",
width: 90,
render: (h, context) => {
return h('span', context.row.counts - context.row.shipments - context.row.refunds);
}
},
{
title: "总量",
key: "counts",
width: 100,
sortable: true
},
{
title: "已用数量",
key: "shipments",
width: 90
},
{
title: "订单金额",
key: "total_price",
width: 120,
sortable: true
},
{
title: "订单时间",
key: "order_at",
width: 150,
sortable: true
title: '下单时间',
key: 'order_at',
minWidth: 110,
sortable: true,
render: (h, context) => {
return h('span', this.moment(context.row.order_at).format('YYYY-MM-DD'));
}
},
{
title: "所需卡量",
@ -181,7 +217,7 @@ export default {
return h("InputNumber", {
props: {
max: context.row.counts - context.row.shipments,
max: context.row.counts - context.row.shipments - context.row.refunds,
min: 0,
value: value,
precision: 0
@ -229,7 +265,7 @@ export default {
return h("Checkbox", {
props: {
value: value,
disabled: !!context.row.virtual_order_id
disabled: Boolean(context.row.virtual_order_id || context.row.refunded_at)
},
on: {
input: value => {
@ -251,13 +287,27 @@ export default {
key: "",
width: 100,
render: (h, { row, column, index }) => {
let color = 'success';
let status_name = '未使用';
if (row.refunded_at && row.virtual_order_id) {
color = 'error';
status_name = '退货使用';
} else if (row.virtual_order_id) {
color = 'primary';
status_name = '已使用';
} else if (row.refunded_at) {
color = 'warning';
status_name = '已退货';
}
return h(
"Tag", {
props: {
color: row.virtual_order_id ? "error" : "primary"
color: color
}
},
row.virtual_order_id ? "已使用" : "未使用"
status_name
);
}
},
@ -312,9 +362,10 @@ export default {
if (this.params.sim !== '') {
params.sim = this.params.sim;
let cardParams = { sim: this.params.sim };
this.params.time = [];
this.cardLoading = true;
this.$store.dispatch("getCardsByParams", params).then((cards) => {
this.$store.dispatch("getCardsByParams", cardParams).then((cards) => {
this.showCards = cards;
this.cardLoading = false;
}).catch(() => {
@ -360,6 +411,24 @@ export default {
});
}
if (this.params.pay_channel_name !== '' && this.params.pay_channel_name !== undefined) {
filterOrders = filterOrders.filter(el => {
return el.pay_channel_name && el.pay_channel_name.indexOf(this.params.pay_channel_name) !== -1;
});
}
if (this.params.transaction_no !== '' && this.params.transaction_no !== undefined) {
filterOrders = filterOrders.filter(el => {
return el.transaction_no && el.transaction_no.indexOf(this.params.transaction_no) !== -1;
});
}
if (this.params.sn !== '' && this.params.sn !== undefined) {
filterOrders = filterOrders.filter(el => {
return el.sn && el.sn.indexOf(this.params.sn) !== -1;
});
}
if (this.params.carrier_operator !== '' && this.params.carrier_operator !== undefined) {
filterOrders = filterOrders.filter(el => {
return el.carrier_operator === this.params.carrier_operator;
@ -370,11 +439,11 @@ export default {
filterOrders = filterOrders.filter(el => {
switch (this.params.used) {
case 0:
return el.shipments === 0;
return el.shipments + el.refunds === 0 && el.counts;
case 1:
return el.shipments > 0 && el.shipments !== el.counts;
return el.shipments + el.refunds > 0 && el.shipments + el.refunds !== el.counts;
case 2:
return el.shipments > 0 && el.shipments === el.counts;
return el.shipments + el.refunds === el.counts;
default:
break;
}
@ -405,13 +474,81 @@ export default {
clearSelect() {
this.$store.dispatch('setSelected', []);
},
exportOrders() {
let columns = [
{ title: "订单编号", key: "sn" },
{ title: "企业名称", key: "company_name" },
{ title: "运营商", key: "carrier_operator_name" },
{ title: "套餐名称", key: "package_name" },
{ title: "套餐单价", key: "unit_price" },
{ title: "支付方式", key: "pay_channel_name" },
{ title: "支付流水号", key: "transaction_no" },
{ title: "订单时间", key: "order_at" },
{ title: "SIM", key: "sim" },
{ title: "数量", key: "counts" },
{ title: "使用状态", key: "virtual_order_id" },
{ title: "VD企业", key: "virtual_company_name" },
{ title: "VD套餐", key: "virtual_package_name" }
];
if (this.type === 0) {
columns.push({ title: "退货", key: "refunded" });
}
let data = [];
let orders = {};
for (let index = 0; index < this.orders.length; index++) {
const element = this.orders[index];
orders[element.id] = element;
}
for (let index = 0; index < this.cards.length; index++) {
const element = this.cards[index];
let order = orders[element.order_id];
let obj = {
sn: order.sn,
company_name: order.company_name,
carrier_operator_name: order.carrier_operator_name,
package_name: order.package_name,
unit_price: order.unit_price,
pay_channel_name: order.pay_channel_name,
transaction_no: order.transaction_no,
order_at: order.order_at,
sim: element.sim,
counts: element.counts,
virtual_order_id: element.virtual_order_id ? '已使用' : '未使用',
virtual_company_name: element.company_name,
virtual_package_name: element.package_name
};
if (this.type === 0) {
obj.refunded = element.refunded_at ? '是' : '否';
}
data.push(obj);
}
this.$refs.cardSelection.exportCsv({
filename: '订单导出' + this.moment().format('YYYYMMDDhhmmss'),
columns: columns,
data: data
});
},
cannel() {
this.clear();
this.close();
},
resetParams() {
for (let k in this.params) {
if (k !== 'time') {
if (k === 'time') {
this.params[k] = [
this.moment().subtract('1', 'months').startOf('month').format('YYYY-MM-DD'),
this.moment().subtract('1', 'months').endOf('month').format('YYYY-MM-DD')
];
} else {
this.params[k] = '';
}
}
@ -428,14 +565,6 @@ export default {
this.$store.dispatch('getCards', params).then((cards) => {
this.cardLoading = false;
this.showCards = cards;
// 跳转到选择的行
// this.$nextTick(() => {
// if (typeof order_id !== 'object') {
// let index = this.showCards.findIndex(el => { return el.order_id === order_id; });
// let toIndex = index - 5 > 0 ? index - 5 : 0;
// this.$refs.cardSelection.scrollToRow(toIndex);
// }
// });
resolve(cards);
}).catch((err) => {
this.cardLoading = false;
@ -568,7 +697,7 @@ export default {
});
mapped.sort((a, b) => {
if (a.shipments === a.counts) {
if (a.counts - a.shipments - a.refunds <= 0) {
return -1;
}
@ -618,7 +747,7 @@ export default {
},
selectAll() {
let order_id = this.filterOrders.filter(el => {
return el.shipments !== el.counts;
return el.counts - el.shipments - el.refunds >= 0;
}).map(item => {
return item.id;
});

View File

@ -8,7 +8,7 @@ export default {
},
data: {
type: Object,
default () {
default() {
return null;
}
}
@ -29,30 +29,28 @@ export default {
page: {
total: 0,
page: 1,
limit: 10
limit: 20
},
columns: [
{
title: "SIM卡号",
key: "column1",
key: "sim",
align: 'center'
},
{
title: "数量",
key: "counts1",
key: "counts",
width: 75,
align: 'center'
},
{
title: "SIM卡号",
key: "column2",
align: 'center'
},
{
title: "数量",
key: "counts2",
title: "退货",
key: "",
width: 75,
align: 'center'
align: 'center',
render: (h, context) => {
return h('span', context.row.refunded_at ? '√' : '');
}
}
],
cards: []
@ -72,20 +70,7 @@ export default {
if (res.code === 0) {
this.page.total = res.data.total;
let cards = res.data.data;
let array = [];
for (let index = 0; index < cards.length; index = index + 2) {
array.push({
column1: cards[index] ? cards[index]['sim'] : '',
counts1: cards[index] ? cards[index]['counts'] : '',
column2: cards[index + 1] ? cards[index + 1]['sim'] : '',
counts2: cards[index + 1] ? cards[index + 1]['counts'] : ''
});
}
this.cards = array;
this.cards = res.data.data;
}
});
},

View File

@ -12,11 +12,13 @@ export default {
params: {
type: 0,
sn: '',
transaction_no: '',
company_name: '',
package_name: '',
order_status: '',
carrier_operator: '',
trashed: '',
sim: '',
time: []
},
type: 0,
@ -50,56 +52,108 @@ export default {
},
table_titles: [
{
title: '订单编号',
key: 'sn',
width: 280
type: 'expand',
width: 50,
render: (h, params) => {
let row = params.row;
let html = [];
html.push(h('div', { class: ['umar-b5'] }, '---- 订单信息 ----'));
let col = [];
col.push(h('Col', { props: { span: 6 }, class: [] }, '订单编号: ' + row.sn));
col.push(h('Col', { props: { span: 6 }, class: [] }, '支付流水号: ' + row.transaction_no));
col.push(h('Col', { props: { span: 6 }, class: [] }, '订单备注: ' + row.remark));
if (row.order_status === 1) {
col.push(h('Col', { props: { span: 6 }, class: [] }, '订单取消理由: ' + row.extends.cancel_remark));
}
html.push(h('Row', { class: [] }, col));
if (row.transaction_status === 2) {
col = [];
col.push(h('Col', { props: { span: 6 }, class: [] }, '退款方式: ' + row.extends.refund_channel));
col.push(h('Col', { props: { span: 6 }, class: [] }, '退款账号: ' + row.extends.refund_account));
col.push(h('Col', { props: { span: 12 }, class: [] }, '退款备注: ' + row.extends.refund_remark));
html.push(h('Row', {}, col));
}
if (this.type === 0) {
html.push(h('div', { class: ['umar-t10', 'umar-b5'] }, '---- 物流信息 ----'));
col = [];
col.push(h('Col', { props: { span: 6 }, class: [] }, '收货人: ' + row.contacts));
col.push(h('Col', { props: { span: 6 }, class: [] }, '联系电话: ' + row.mobile));
col.push(h('Col', { props: { span: 12 }, class: [] }, '收货地址: ' + (row.area ? row.area.join(' ') : '') + ' ' + row.address));
html.push(h('Row', { class: [] }, col));
col = [];
col.push(h('Col', { props: { span: 6 }, class: [] }, '物流单号: ' + row.logistics_no));
col.push(h('Col', { props: { span: 6 }, class: [] }, '物流公司: ' + row.logistics_company_name));
col.push(h('Col', { props: { span: 12 }, class: [] }, '物流备注: ' + row.logistics_remark));
html.push(h('Row', { class: [] }, col));
}
return h('div', { class: ['fz-13'] }, html);
}
},
{
title: 'ID',
key: 'id',
minWidth: 80
},
{
title: '企业名称',
key: 'company_name',
width: 320
minWidth: 240,
tooltip: true
},
{
title: '运营商',
key: 'carrier_operator',
width: 90
key: 'carrier_operator_name',
minWidth: 90
},
{
title: '套餐名称',
key: 'package_name',
width: 120
minWidth: 120
},
{
title: '套餐单价',
key: 'unit_price',
width: 100
minWidth: 100
},
{
title: '订单数量',
title: '订单量',
key: '',
width: 100,
render: (h, {
row,
column,
index
}) => {
return h('span', Number(row.counts));
minWidth: 80,
render: (h, context) => {
return h('span', Number(context.row.counts));
}
},
{
title: '订单金额',
key: 'custom_price',
width: 120
minWidth: 120
},
{
title: '支付方式',
key: 'pay_channel_name',
minWidth: 120
},
{
title: '订单状态',
key: '',
width: 100,
render: (h, {
row,
column,
index
}) => {
minWidth: 100,
render: (h, context) => {
let row = context.row;
let status = ['error', 'default', 'warning', 'primary', 'success'];
return h('Button', {
@ -228,12 +282,9 @@ export default {
{
title: '收款状态',
key: '',
width: 100,
render: (h, {
row,
column,
index
}) => {
minWidth: 100,
render: (h, context) => {
let row = context.row;
let status = ['error', 'success', 'default'];
return h('Button', {
@ -371,18 +422,18 @@ export default {
{
title: '下单时间',
key: 'order_at',
width: 170
minWidth: 110,
render: (h, context) => {
return h('span', this.moment(context.row.order_at).format('YYYY-MM-DD'));
}
},
{
title: '操作',
key: 'action',
width: 190,
fixed: 'right',
render: (h, {
row,
column,
index
}) => {
minWidth: 300,
render: (h, context) => {
let row = context.row;
row.unit_price = Number(row.unit_price);
let html = [];
if (row.deleted_at) {
@ -399,7 +450,7 @@ export default {
type: 'dashed',
size: 'small',
disabled: false,
icon: 'md-eye'
icon: 'ios-list'
},
class: ['btn'],
on: {
@ -407,7 +458,24 @@ export default {
this.show(row);
}
}
}, '查看'));
}, '清单'));
}
if (this.haveJurisdiction('update')) {
html.push(h('Button', {
props: {
type: 'primary',
size: 'small',
disabled: false,
icon: 'md-create'
},
class: ['btn'],
on: {
click: (event) => {
this.openEdit(true, 2, row);
}
}
}, '编辑'));
}
if (this.haveJurisdiction('update')) {
@ -421,7 +489,8 @@ export default {
}, [h('Button', {
props: {
type: 'success',
size: 'small'
size: 'small',
icon: 'md-list-box'
},
class: ['btn'],
on: {
@ -438,7 +507,8 @@ export default {
props: {
type: 'warning',
size: 'small',
disabled: false
disabled: false,
icon: 'md-timer'
},
class: ['btn'],
on: {
@ -451,10 +521,13 @@ export default {
API.reset({
ids: row.id
}).then(res => {
this.isShowLoading(false);
if (res.code == 0) {
this.$Message.success('操作成功');
this.request();
}
}).catch(() => {
this.isShowLoading(false);
});
}
});
@ -468,7 +541,8 @@ export default {
props: {
type: 'error',
size: 'small',
disabled: false
disabled: false,
icon: 'md-trash'
},
class: ['btn'],
on: {
@ -504,21 +578,26 @@ export default {
created() {
this.index(1);
},
mounted() {
if (this.type === 0) {
this.table_titles.splice(7, 0, {
title: '退货量',
key: 'refunds',
width: 80
});
}
if (this.type !== 0) {
this.table_titles.splice(9, 2);
}
},
methods: {
// 查看订单明细
show(row) {
this.isShowLoading(true);
API.show(row.id).then(res => {
this.isShowLoading(false);
if (res.code === 0) {
this.detailObj = {
show: true,
data: res.data
};
}
}).catch(() => {
this.isShowLoading(false);
});
this.detailObj = {
show: true,
data: row
};
},
/**
* [index 列表]
@ -539,6 +618,25 @@ export default {
this.isShowLoading(false);
});
},
exportOrders() {
this.isShowLoading(true);
let params = this.parseParams(this.params);
params.type = Number(this.$route.params.type);
API.exportOrders(params).then(res => {
if (res.code === 0) {
this.downloadFile(res.data);
} else {
this.$Modal.success({
title: '提示',
content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。'
});
}
this.isShowLoading(false);
}).catch(() => {
this.isShowLoading(false);
});
},
/**
* [openEdit 打开编辑弹窗]
@ -673,6 +771,9 @@ export default {
this.cardsObj.show = false;
this.$store.dispatch('initOrder');
this.index(page);
},
handleMyCompletePackages(value) {
this.handleCompletePackages(this.type, value);
}
}
};

View File

@ -1,14 +1,31 @@
<?php
use App\Domains\Card\Services\CardService;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
require_once realpath(dirname(__FILE__) . '/TestCase.php');
$simArray = [
$handle = fopen(__DIR__ . '/interval.csv', 'w');
$custom_no = DB::connection('vd_old')->table('ckb_custom_handle_log')->select('custom_no')->distinct()->where('type', 11)->get()->pluck('custom_no')->toArray();
];
fputcsv($handle, ['客户编号', '间隔时间']);
$values = CardService::getMongoCardsInfo($simArray);
foreach (array_chunk($custom_no, 1000) as $values) {
$res = DB::connection('vd_old')->table('ckb_custom_handle_log')->whereIn('custom_no', $values)->whereIn('type', [10, 11])->orderBy('valid_start_time')->get()->groupBy('custom_no');
dd($values);
foreach ($res as $no => $group) {
echo $no . PHP_EOL;
for ($i=1; $i < count($group); $i++) {
$item1 = $group[$i - 1];
$item2 = $group[$i];
$interval = Carbon::createFromTimestamp(intval($item2->valid_start_time))->diffInMonths(Carbon::createFromTimestamp(intval($item1->valid_end_time)));
fputcsv($handle, [$no, $interval]);
}
}
}
fclose($handle);
dd(1);

426075
tests/interval.csv Normal file

File diff suppressed because it is too large Load Diff