feat: 完善售后退货退款查询与导出

Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
This commit is contained in:
苏木 2026-10-29 16:25:35 +08:00 committed by sumu
parent 393784a5e3
commit e0b7a66d37
9 changed files with 192 additions and 39 deletions

View File

@ -6992,16 +6992,37 @@ class Pages extends Controller
'refundPageSlug' => 'after-sales-return-refund',
'refundPageLabel' => '售后退货退款预警',
'refundWarningMode' => true,
'refundShowExport' => false,
'refundShowExport' => true,
'refundExportRouteName' => 'business.other.after-sales-return-refund.export',
]);
}
public function exportNonJingxiRefunds(Request $request)
{
return $this->exportRefundRows(
$request,
'refunds',
'non-jingxi-refund-data',
'after_sales_refunds',
);
}
public function exportAfterSalesReturnRefunds(Request $request)
{
return $this->exportRefundRows(
$request,
'other',
'after-sales-return-refund',
'after_sales_refund_warning',
);
}
private function exportRefundRows(Request $request, string $section, string $page, string $filenamePrefix)
{
$filters = $this->nonJingxiRefundFilters($request);
$redirectParameters = array_filter([
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
'section' => $section,
'page' => $page,
...$filters,
], static fn (mixed $value): bool => $value !== '' && $value !== null);
@ -7026,6 +7047,7 @@ class Pages extends Controller
$filters,
$redirectParameters,
$request->query('columns'),
$filenamePrefix,
);
} catch (Throwable $exception) {
report($exception);
@ -7035,7 +7057,7 @@ class Pages extends Controller
}
}
/** @return array{from:string,to:string,order_no:string,shop_name:string,sku_id:string} */
/** @return array{from:string,to:string,order_no:string,waybill_no:string,shop_name:string,sku_id:string} */
private function nonJingxiRefundFilters(
Request $request,
?string $defaultFrom = null,
@ -7053,20 +7075,28 @@ class Pages extends Controller
'from' => max($from, '2026-01-01'),
'to' => $to,
'order_no' => mb_substr(trim((string) $request->query('order_no', '')), 0, 100),
'waybill_no' => mb_substr(trim((string) $request->query('waybill_no', '')), 0, 100),
'shop_name' => mb_substr(trim((string) $request->query('shop_name', '')), 0, 191),
'sku_id' => mb_substr(trim((string) $request->query('sku_id', '')), 0, 100),
];
}
private function streamNonJingxiRefundCsv(iterable $rows, array $filters, array $redirectParameters, mixed $requestedFields = null)
private function streamNonJingxiRefundCsv(
iterable $rows,
array $filters,
array $redirectParameters,
mixed $requestedFields = null,
string $filenamePrefix = 'after_sales_refunds',
)
{
$columns = [
['label' => '序号/主键ID', 'field' => 'Id', 'identifier' => true],
['label' => '退款日期', 'field' => 'refundDate'],
['label' => '退款日期', 'field' => 'refundDate', 'date' => true],
['label' => '业务来源', 'field' => 'dataSource'],
['label' => '商品明细', 'field' => 'detailStatus'],
['label' => '售后单ID', 'field' => 'as_id', 'identifier' => true],
['label' => '外部售后单号', 'field' => 'outer_as_id', 'identifier' => true],
['label' => '运单号', 'field' => 'waybillNo', 'identifier' => true],
['label' => '订单ID', 'field' => 'o_id', 'identifier' => true],
['label' => '', 'field' => ''],
['label' => '子订单ID', 'field' => 'so_id', 'identifier' => true],
@ -7091,11 +7121,11 @@ class Pages extends Controller
['label' => '退款商品成本', 'field' => 'refundCostGoods', 'number' => true],
['label' => '退款回款金额', 'field' => 'RefundMoneyIncome', 'number' => true],
['label' => '售后金额', 'field' => 'AfSalesMoney', 'number' => true],
['label' => '创建时间', 'field' => 'createTime'],
['label' => '创建时间', 'field' => 'createTime', 'date' => true],
['label' => '状态', 'field' => 'status'],
['label' => '店铺状态', 'field' => 'shop_status'],
['label' => '创建记录/创建时间', 'field' => 'created'],
['label' => '确认日期', 'field' => 'confirm_date'],
['label' => '创建记录/创建时间', 'field' => 'created', 'date' => true],
['label' => '确认日期', 'field' => 'confirm_date', 'date' => true],
];
$columns = $this->filterExportColumns($columns, $requestedFields);
@ -7125,11 +7155,13 @@ class Pages extends Controller
fputcsv($csv, array_map(
fn (array $column): mixed => $column['field'] === ''
? ''
: (($column['number'] ?? false)
? data_get($row, $column['field'])
: (($column['identifier'] ?? false)
? $this->csvIdentifier(data_get($row, $column['field']))
: $this->csvText(data_get($row, $column['field'])))),
: (($column['date'] ?? false)
? $this->refundDateText(data_get($row, $column['field']))
: (($column['number'] ?? false)
? data_get($row, $column['field'])
: (($column['identifier'] ?? false)
? $this->csvIdentifier(data_get($row, $column['field']))
: $this->csvText(data_get($row, $column['field']))))),
$columns,
));
if (++$written % 1000 === 0) {
@ -7143,13 +7175,26 @@ class Pages extends Controller
} finally {
fclose($csv);
}
}, 'after_sales_refunds_' . $filters['from'] . '_' . $filters['to'] . '.csv', [
}, $filenamePrefix . '_' . $filters['from'] . '_' . $filters['to'] . '.csv', [
'Content-Type' => 'text/csv; charset=UTF-8',
'Cache-Control' => 'private, no-store',
'X-Accel-Buffering' => 'no',
]);
}
private function refundDateText(mixed $value): ?string
{
if ($value === null || $value === '') {
return null;
}
try {
return Carbon::parse((string) $value)->format('Y-m-d H:i:s');
} catch (Throwable) {
return (string) $value;
}
}
protected function contentForSmallPaymentWorkOrders(): array
{
$request = request();

View File

@ -154,7 +154,7 @@ class NonJingxiRefundMirrorRepository
}
/**
* @param array{from:string,to:string,order_no:string,shop_name:string,sku_id:string} $filters
* @param array{from:string,to:string,order_no:string,waybill_no:string,shop_name:string,sku_id:string} $filters
* @return array{data:list<object>,total:int}
*/
public function paginate(array $filters, int $page, int $perPage): array
@ -177,7 +177,7 @@ class NonJingxiRefundMirrorRepository
* Export all rows matching the supplied filters in the same order and
* shape as the paginated page data, without loading the full result set.
*
* @param array{from:string,to:string,order_no:string,shop_name:string,sku_id:string} $filters
* @param array{from:string,to:string,order_no:string,waybill_no:string,shop_name:string,sku_id:string} $filters
* @return iterable<int,object>
*/
public function exportRows(array $filters, int $chunkSize = 1000): iterable
@ -204,7 +204,7 @@ class NonJingxiRefundMirrorRepository
private function selectColumns(): array
{
return [
'r.as_date as refundDate', 'r.as_id', 'r.outer_as_id',
'r.as_date as refundDate', 'r.as_id', 'r.outer_as_id', 'r.l_id as waybillNo',
'r.o_id', 'r.so_id', 'r.type', 'r.node', 'r.shop_type', 'r.shop_name',
'r.shop_site as platform', 'r.refund as totalRefundMoney',
'i.outer_oi_id', 'i.shop_sku_id', 'i.i_id', 'i.sku_id', 'i.qty',
@ -389,7 +389,7 @@ class NonJingxiRefundMirrorRepository
});
if ($supplement) {
$query->select([
'r.as_date as refundDate', 'r.as_id', 'r.outer_as_id',
'r.as_date as refundDate', 'r.as_id', 'r.outer_as_id', 'r.l_id as waybillNo',
'r.o_id', 'r.so_id', 'r.type', 'r.node', 'r.shop_type', 'r.shop_name',
'r.refund as totalRefundMoney', 'i.outer_oi_id', 'i.shop_sku_id', 'i.i_id', 'i.sku_id',
'i.qty', 'i.shop_amount as detailRefundMoney', 'i.type as refundType',
@ -434,7 +434,7 @@ class NonJingxiRefundMirrorRepository
return implode('|', [$row->_source_rank, $row->_head_id, $row->_item_id, $row->_source_id]);
}
/** @param array{from:string,to:string,order_no:string,shop_name:string,sku_id:string} $filters */
/** @param array{from:string,to:string,order_no:string,waybill_no:string,shop_name:string,sku_id:string} $filters */
private function applyFilters(Builder $query, array $filters): void
{
$query->where('r.as_date', '>=', $filters['from'] . ' 00:00:00')
@ -443,6 +443,10 @@ class NonJingxiRefundMirrorRepository
if ($filters['shop_name'] !== '') {
$query->whereRaw("r.shop_name LIKE ? ESCAPE '!'", [$this->contains($filters['shop_name'])]);
}
if ($filters['waybill_no'] !== '') {
$query->where('r.l_id', trim($filters['waybill_no']));
}
}
private function applySkuFilter(Builder $query, string $value): void

View File

@ -3,5 +3,6 @@
'refundPageSlug' => 'after-sales-return-refund',
'refundPageLabel' => '售后退货退款预警',
'refundWarningMode' => true,
'refundShowExport' => false,
'refundShowExport' => true,
'refundExportRouteName' => 'business.other.after-sales-return-refund.export',
])

View File

@ -6,6 +6,7 @@
['label' => '商品明细', 'field' => 'detailStatus'],
['label' => '售后单ID', 'field' => 'as_id'],
['label' => '外部售后单号', 'field' => 'outer_as_id'],
['label' => '运单号', 'field' => 'waybillNo'],
['label' => '订单ID', 'field' => 'o_id'],
['label' => '', 'field' => ''],
['label' => '子订单ID', 'field' => 'so_id'],
@ -53,10 +54,24 @@
$refundPageLabel = $refundPageLabel ?? '售后(退货退款)';
$refundWarningMode = $refundWarningMode ?? false;
$refundShowExport = $refundShowExport ?? true;
$refundExportRouteName = $refundExportRouteName ?? 'business.refunds.non-jingxi-refund-data.export';
$refundPageUrl = route('business.pages.show', [
'section' => $refundPageSection,
'page' => $refundPageSlug,
]);
$refundExportUrl = route($refundExportRouteName);
$refundDateFields = ['refundDate', 'createTime', 'created', 'confirm_date'];
$formatRefundDate = static function (mixed $value): mixed {
if ($value === null || $value === '') {
return null;
}
try {
return \Illuminate\Support\Carbon::parse((string) $value)->format('Y-m-d H:i:s');
} catch (\Throwable) {
return $value;
}
};
@endphp
<div
@ -168,6 +183,10 @@
<label for="non-jingxi-refund-order">订单号</label>
<input id="non-jingxi-refund-order" type="search" name="order_no" value="{{ $filters['order_no'] }}" placeholder="完整订单或售后单号(精确查询)">
</div>
<div class="upstream-data-field">
<label for="non-jingxi-refund-waybill">运单号</label>
<input id="non-jingxi-refund-waybill" type="search" name="waybill_no" value="{{ $filters['waybill_no'] }}" placeholder="完整运单号(精确查询)">
</div>
<div class="upstream-data-field">
<label for="non-jingxi-refund-shop">店铺名称</label>
<input id="non-jingxi-refund-shop" type="search" name="shop_name" value="{{ $filters['shop_name'] }}" placeholder="店铺名称">
@ -189,7 +208,7 @@
<a
class="upstream-data-button finance-table-fallback-export {{ $rows->total() === 0 || $refundError || $refundPending || $refundIndexPending ? 'is-disabled' : '' }}"
data-finance-fallback-export
href="{{ $rows->total() === 0 || $refundError || $refundPending || $refundIndexPending ? '#' : route('business.refunds.non-jingxi-refund-data.export', $filters) }}"
href="{{ $rows->total() === 0 || $refundError || $refundPending || $refundIndexPending ? '#' : route($refundExportRouteName, $filters) }}"
@if ($rows->total() === 0 || $refundError || $refundPending || $refundIndexPending) aria-disabled="true" @endif
>
<span class="material-icons-outlined" aria-hidden="true">download</span>
@ -205,7 +224,7 @@
class="upstream-data-table refund-data-table refund-data-table--wide"
aria-label="{{ $refundPageLabel }}"
@if ($refundShowExport)
data-finance-export-base="{{ route('business.refunds.non-jingxi-refund-data.export') }}"
data-finance-export-base="{{ $refundExportUrl }}"
data-finance-export-query="{{ json_encode($filters, JSON_UNESCAPED_UNICODE) }}"
data-finance-export-disabled="{{ $rows->total() === 0 || $refundError || $refundPending || $refundIndexPending ? 'true' : 'false' }}"
@endif
@ -229,6 +248,9 @@
@php
$field = $column['field'];
$value = $field === '' ? null : data_get($row, $field);
if (in_array($field, $refundDateFields, true)) {
$value = $formatRefundDate($value);
}
$isNumber = (bool) ($column['number'] ?? false);
@endphp
<td @class(['is-number' => $isNumber])>

View File

@ -475,6 +475,10 @@ Route::get('workspace/refunds/non-jingxi-refund-data/export', 'Business\Pages@ex
->middleware('business.page:refunds,non-jingxi-refund-data')
->name('business.refunds.non-jingxi-refund-data.export');
Route::get('workspace/other/after-sales-return-refund/export', 'Business\Pages@exportAfterSalesReturnRefunds')
->middleware('business.page:other,after-sales-return-refund')
->name('business.other.after-sales-return-refund.export');
Route::get('workspace/financial-operations/small-payment-work-orders/export', 'Business\Pages@exportSmallPaymentWorkOrders')
->middleware('business.page:financial-operations,small-payment-work-orders')
->name('business.financial-operations.small-payment-work-orders.export');

View File

@ -39,15 +39,17 @@ class NonJingxiRefundPageTest extends FeatureTestCase
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'waybill_no' => 'WB-2002',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
] && $page === 2 && $limit === 25
)->andReturn([
'data' => [(object) [
'Id' => 102,
'refundDate' => '2026-08-20 10:00:00',
'refundDate' => '2026-08-20 10:00:00.123456',
'as_id' => 2002,
'outer_as_id' => 'AS-2002',
'waybillNo' => 'WB-2002',
'o_id' => 3002,
'so_id' => 'ORDER-1',
'type' => '退款',
@ -86,7 +88,7 @@ class NonJingxiRefundPageTest extends FeatureTestCase
'section' => 'refunds',
'page' => 'non-jingxi-refund-data',
]) . '?' . http_build_query([
'from' => '2026-08-01', 'to' => '2026-08-21', 'order_no' => 'ORDER-1',
'from' => '2026-08-01', 'to' => '2026-08-21', 'order_no' => 'ORDER-1', 'waybill_no' => 'WB-2002',
'shop_name' => '测试店', 'sku_id' => 'SKU-1', 'page' => 2,
]);
@ -97,6 +99,9 @@ class NonJingxiRefundPageTest extends FeatureTestCase
->assertSee('data-sync-state="syncing"', false)
->assertSeeText('业务镜像同步中,当前显示最近成功数据')
->assertSeeText('AS-2002')
->assertSeeText('WB-2002')
->assertSeeText('2026-08-20 10:00:00')
->assertDontSee('2026-08-20 10:00:00.123456', false)
->assertSeeText('业务来源')
->assertSeeText('商品明细')
->assertSeeText('SKU-1')
@ -108,11 +113,13 @@ class NonJingxiRefundPageTest extends FeatureTestCase
->assertSee('data-finance-export-base=', false)
->assertSee('data-export-field="refundDate"', false)
->assertSee('value="2026-08-01"', false)
->assertSee('value="ORDER-1"', false);
->assertSee('value="ORDER-1"', false)
->assertSee('name="waybill_no"', false)
->assertSee('value="WB-2002"', false);
$this->assertSame(1, preg_match('/data-finance-export-query="([^"]+)"/', $response->getContent(), $exportQuery));
$this->assertSame([
'from' => '2026-08-01', 'to' => '2026-08-21', 'order_no' => 'ORDER-1',
'from' => '2026-08-01', 'to' => '2026-08-21', 'order_no' => 'ORDER-1', 'waybill_no' => 'WB-2002',
'shop_name' => '测试店', 'sku_id' => 'SKU-1',
], json_decode(html_entity_decode($exportQuery[1], ENT_QUOTES | ENT_HTML5, 'UTF-8'), true, 512, JSON_THROW_ON_ERROR));
}
@ -128,13 +135,15 @@ class NonJingxiRefundPageTest extends FeatureTestCase
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'waybill_no' => 'WB-2002',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
])->andReturn([(object) [
'Id' => 102,
'refundDate' => '2026-08-20 10:00:00',
'refundDate' => '2026-08-20 10:00:00.123456',
'as_id' => 2002,
'outer_as_id' => 'AS-2002',
'waybillNo' => 'WB-2002',
'o_id' => 3002,
'so_id' => 'ORDER-1',
'type' => '退款',
@ -170,6 +179,7 @@ class NonJingxiRefundPageTest extends FeatureTestCase
'from' => '2026-08-01',
'to' => '2026-08-21',
'order_no' => 'ORDER-1',
'waybill_no' => 'WB-2002',
'shop_name' => '测试店',
'sku_id' => 'SKU-1',
]));
@ -179,6 +189,9 @@ class NonJingxiRefundPageTest extends FeatureTestCase
$this->assertStringStartsWith("\xEF\xBB\xBF", $content);
$this->assertStringContainsString('序号/主键ID', $content);
$this->assertStringContainsString('AS-2002', $content);
$this->assertStringContainsString('WB-2002', $content);
$this->assertStringContainsString('2026-08-20 10:00:00', $content);
$this->assertStringNotContainsString('.123456', $content);
$this->assertStringContainsString('99.5', $content);
}
@ -204,7 +217,8 @@ class NonJingxiRefundPageTest extends FeatureTestCase
$content = $response->streamedContent();
$header = strtok($content, "\r\n");
$this->assertSame("\xEF\xBB\xBF店铺名称,退款日期", $header);
$this->assertStringContainsString('测试店,2026-08-20', $content);
$lines = preg_split('/\r?\n/', trim($content));
$this->assertSame(['测试店', '2026-08-20 00:00:00'], str_getcsv($lines[1]));
$this->assertStringNotContainsString('SKU编码', $content);
}

View File

@ -1066,6 +1066,7 @@ class PagesTest extends FeatureTestCase
'from' => '2026-08-24',
'to' => '2026-08-24',
'order_no' => '',
'waybill_no' => '',
'shop_name' => '天猫旗舰店',
'sku_id' => '',
] && $page === 1 && $limit === 10
@ -1187,7 +1188,7 @@ class PagesTest extends FeatureTestCase
});
$this->assertSame([
'序号/主键ID', '退款日期', '业务来源', '商品明细', '售后单ID', '外部售后单号', '订单ID', '',
'序号/主键ID', '退款日期', '业务来源', '商品明细', '售后单ID', '外部售后单号', '运单号', '订单ID', '',
'子订单ID', '类型', '节点', '是否自营', '店铺名称', '平台', '责任部门',
'退款总金额', '原始邮编', '店铺商品ID', '店铺SKU编码', '商品ID', 'SKU编码',
'买家实付金额', '数量', '商品单位成本', '明细退款金额', '退款类型',
@ -1196,7 +1197,7 @@ class PagesTest extends FeatureTestCase
], $headers->get('labels'));
$this->assertSame([
'Id', 'refundDate', 'dataSource', 'detailStatus', 'as_id', 'outer_as_id', 'o_id', '', 'so_id', 'type',
'Id', 'refundDate', 'dataSource', 'detailStatus', 'as_id', 'outer_as_id', 'waybillNo', 'o_id', '', 'so_id', 'type',
'node', 'isZC', 'shop_name', 'platform', 'responsibleDepartment',
'totalRefundMoney', 'originPostcode', 'shop_i_id', 'shop_sku_id', 'i_id',
'sku_id', 'buyer_paid_amount', 'qty', 'unitCostGoods', 'detailRefundMoney',

View File

@ -69,10 +69,14 @@ class WarningPageTest extends FeatureTestCase
->assertSeeText('售后退货退款')
->assertSeeText('聚水潭售后数据已接入')
->assertSeeText('AS-WARNING-001')
->assertSeeText('WB-WARNING-001')
->assertSeeText('售后测试店')
->assertSeeText('退款总金额')
->assertSeeText('运单号')
->assertSee('data-refund-page="after-sales-warning"', false)
->assertSee('action="' . $afterSalesRefundUrl . '"', false)
->assertSee('name="waybill_no"', false)
->assertSee(route('business.other.after-sales-return-refund.export'), false)
->assertDontSee(route('business.refunds.non-jingxi-refund-data.export'), false)
->assertDontSeeText('售后退货退款预警规则待接入');
}
@ -108,6 +112,55 @@ class WarningPageTest extends FeatureTestCase
->assertSeeText('AS-WARNING-001');
}
public function test_warning_page_exports_filtered_rows_without_refund_page_permission(): void
{
$regular = $this->regularAccount();
$subject = 'user_' . $regular->id;
setting()->set('system.sidebar_permissions', [
'configured' => [$subject],
'subjects' => [$subject => ['other/warnings']],
]);
$filters = [
'from' => '2026-10-23',
'to' => '2026-10-29',
'order_no' => 'ORDER-WARNING-001',
'waybill_no' => 'WB-WARNING-001',
'shop_name' => '售后测试店',
'sku_id' => 'SKU-WARNING-001',
];
$repository = \Mockery::mock(NonJingxiRefundMirrorRepository::class);
$repository->shouldReceive('available', 'ready', 'queryReady')->andReturnTrue();
$repository->shouldReceive('exportRows')->once()->with($filters)->andReturn([(object) [
'Id' => 9001,
'refundDate' => '2026-10-28 10:00:00.123456',
'waybillNo' => 'WB-WARNING-001',
'outer_as_id' => 'AS-WARNING-001',
'shop_name' => '售后测试店',
'sku_id' => 'SKU-WARNING-001',
]]);
$this->app->instance(NonJingxiRefundMirrorRepository::class, $repository);
$response = $this->loginAs($regular)->get(route(
'business.other.after-sales-return-refund.export',
$filters + ['columns' => 'refundDate,waybillNo,outer_as_id,shop_name,sku_id'],
));
$response->assertOk()->assertDownload(
'after_sales_refund_warning_2026-10-23_2026-10-29.csv'
);
$content = $response->streamedContent();
$lines = preg_split('/\r?\n/', trim($content));
$this->assertSame(
['退款日期', '运单号', '外部售后单号', '店铺名称', 'SKU编码'],
str_getcsv(ltrim($lines[0], "\xEF\xBB\xBF")),
);
$this->assertSame(
['2026-10-28 10:00:00', "'WB-WARNING-001", "'AS-WARNING-001", '售后测试店', "'SKU-WARNING-001"],
str_getcsv($lines[1]),
);
}
private function regularAccount(): User
{
$name = 'fixture-warning-page-' . User::count();
@ -155,6 +208,7 @@ class WarningPageTest extends FeatureTestCase
'detailStatus' => '已关联',
'as_id' => 8001,
'outer_as_id' => 'AS-WARNING-001',
'waybillNo' => 'WB-WARNING-001',
'o_id' => 'ORDER-WARNING-001',
'so_id' => 'SUB-WARNING-001',
'type' => '退货退款',

View File

@ -69,11 +69,12 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
['Id' => 2, 'erpShopName' => '二号店', 'responsibleDepartment' => '部门乙'],
]);
$filters = ['from' => '2026-08-18', 'to' => '2026-08-21', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''];
$filters = ['from' => '2026-08-18', 'to' => '2026-08-21', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''];
$page2 = $this->repository->paginate($filters, 2, 1);
$this->assertSame(2, $page2['total']);
$this->assertSame(11, $page2['data'][0]->Id);
$this->assertSame('WB-1', $page2['data'][0]->waybillNo);
$this->assertSame('ZIP-1', $page2['data'][0]->originPostcode);
$this->assertSame('ITEM-1', $page2['data'][0]->shop_i_id);
$this->assertSame(88, $page2['data'][0]->buyer_paid_amount);
@ -92,8 +93,11 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
$filters['sku_id'] = '';
$filters['order_no'] = '8902';
$this->assertSame(1, $this->repository->paginate($filters, 1, 25)['total']);
$filters['order_no'] = '';
$filters['waybill_no'] = 'WB-1';
$this->assertSame(1, $this->repository->paginate($filters, 1, 25)['total']);
$filters['waybill_no'] = '';
$exportRows = iterator_to_array($this->repository->exportRows($filters, 1));
$this->assertCount(2, $exportRows);
$this->assertSame(12, $exportRows[0]->Id);
@ -137,7 +141,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
['Id' => 2, 'shop_id' => 'SHOP-1', 'shop_site' => '天猫'],
['Id' => 3, 'shop_id' => 'JINGXI', 'shop_site' => '京喜'],
]);
$filters = ['from' => '2026-06-01', 'to' => '2026-07-31', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''];
$filters = ['from' => '2026-06-01', 'to' => '2026-07-31', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''];
$result = $this->repository->paginate($filters, 1, 25);
$this->assertSame(3, $result['total']);
$this->assertSame(['202', '201', '101'], array_map(fn ($r) => (string) $r->as_id, $result['data']));
@ -176,7 +180,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
$differentShop['as_id'] = '203';
$differentShop['shop_id'] = 'SHOP-2';
$db->table('ops_jst_qmcust_refunds')->insert([$sameId, $sameExternal, $differentShop]);
$filters = ['from' => '2026-07-01', 'to' => '2026-07-31', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''];
$filters = ['from' => '2026-07-01', 'to' => '2026-07-31', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''];
$result = $this->repository->paginate($filters, 1, 25);
$this->assertSame(2, $result['total']);
$this->assertEqualsCanonicalizing(['101', '203'], array_map(fn ($r) => (string) $r->as_id, $result['data']));
@ -203,7 +207,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
['asi_id' => 501, 'as_id' => '202', 'sku_id' => 'MATCH'],
['asi_id' => 502, 'as_id' => '202', 'sku_id' => 'MATCH'],
]);
$filters = ['from' => '2026-07-01', 'to' => '2026-07-31', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''];
$filters = ['from' => '2026-07-01', 'to' => '2026-07-31', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''];
$expected = ['202:502', '202:501', '201:201', '102:14', '102:13', '102:12', '101:11'];
$identity = fn ($r) => $r->as_id . ':' . $r->Id;
$this->assertSame($expected, array_map($identity, iterator_to_array($this->repository->exportRows($filters, 1))));
@ -235,7 +239,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
foreach (array_chunk($headers, 100) as $chunk) {
DB::connection('business_mirror')->table('ops_jst_return_refund_info')->insert($chunk);
}
$filters = ['from' => '2026-07-01', 'to' => '2026-07-01', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''];
$filters = ['from' => '2026-07-01', 'to' => '2026-07-01', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''];
$rows = iterator_to_array($this->repository->exportRows($filters));
$this->assertSame(range(11001, 10001), array_map(static fn ($row): int => (int) $row->as_id, $rows));
@ -253,7 +257,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('business_mirror_dataset_not_ready');
$this->repository->paginate(
['from' => '2026-08-18', 'to' => '2026-08-21', 'order_no' => '', 'shop_name' => '', 'sku_id' => ''],
['from' => '2026-08-18', 'to' => '2026-08-21', 'order_no' => '', 'waybill_no' => '', 'shop_name' => '', 'sku_id' => ''],
1,
25,
);
@ -294,6 +298,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
$table->integer('Id'); $table->integer('as_id'); $table->dateTime('as_date'); $table->string('outer_as_id')->nullable();
$table->integer('o_id'); $table->string('so_id')->nullable(); $table->string('type')->nullable(); $table->string('node')->nullable();
$table->string('shop_type')->nullable(); $table->string('shop_name')->nullable(); $table->string('shop_site')->nullable();
$table->string('l_id')->nullable();
$table->decimal('refund', 18, 2)->nullable(); $table->decimal('receive_refund', 18, 2)->nullable();
$table->decimal('buyer_apply_refund', 18, 2)->nullable(); $table->dateTime('createTime')->nullable();
$table->string('status')->nullable(); $table->string('shop_status')->nullable(); $table->dateTime('created')->nullable();
@ -338,6 +343,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
$table->string('as_id'); $table->dateTime('as_date'); $table->string('outer_as_id')->nullable();
$table->integer('o_id')->nullable(); $table->string('so_id')->nullable(); $table->string('type')->nullable();
$table->string('node')->nullable(); $table->string('shop_type')->nullable(); $table->string('shop_name')->nullable();
$table->string('l_id')->nullable();
$table->string('shop_id')->nullable(); $table->string('refund')->nullable(); $table->string('buyer_apply_refund')->nullable();
$table->string('status')->nullable(); $table->string('shop_status')->nullable(); $table->dateTime('created')->nullable();
$table->dateTime('confirm_date')->nullable(); $table->dateTime('local_created')->nullable();
@ -356,7 +362,8 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
{
return ['as_id' => (string) $id, 'as_date' => $date, 'outer_as_id' => 'AS-Q-' . $id,
'o_id' => 9000 + $id, 'so_id' => 'SO-Q-' . $id, 'type' => '仅退款',
'shop_id' => $shop, 'shop_name' => '测试店', 'refund' => '12.34', 'status' => 'Confirmed'];
'shop_id' => $shop, 'shop_name' => '测试店', 'l_id' => 'WB-Q-' . $id,
'refund' => '12.34', 'status' => 'Confirmed'];
}
private function markReady(): void
@ -378,6 +385,7 @@ class NonJingxiRefundMirrorRepositoryTest extends TestCase
'Id' => $id, 'as_id' => $asId, 'as_date' => $date, 'outer_as_id' => 'AS-' . $asId,
'o_id' => 8900 + $id, 'so_id' => 'SO-' . $id, 'type' => '退款', 'node' => '完成',
'shop_type' => '自营', 'shop_name' => $shopName, 'shop_site' => $platform,
'l_id' => 'WB-' . $id,
'refund' => 100, 'receive_refund' => 80, 'buyer_apply_refund' => 100,
'createTime' => $date, 'status' => '完成', 'shop_status' => '完成',
'created' => $date, 'confirm_date' => $date,