files as $file) { @unlink($file); } parent::tearDown(); } public function testItImportsExactIdentifiersAndIsIdempotent(): void { $file = $this->workbook(); $this->artisan('business:import-taobao-alipay-bills', ['file' => $file, '--company' => 1]) ->expectsOutputToContain('新增 1 条,更新 0 条') ->assertSuccessful(); $bill = AlipayBill::query()->where('company_id', 1)->firstOrFail(); $this->assertSame('2000000001141269665', $bill->record_code); $this->assertSame('2026071323001146201449637707', $bill->business_serial_no); $this->assertSame('2026-07-15 23:59:12', $bill->occurred_at->format('Y-m-d H:i:s')); $this->assertSame('100.250000', $bill->income); $this->assertSame('attachment-import', $bill->created_from); $this->assertSame('2026-07-16 06:31:02', $bill->created_at->format('Y-m-d H:i:s')); $this->assertSame('2026-07-16 06:35:03', $bill->updated_at->format('Y-m-d H:i:s')); $this->artisan('business:import-taobao-alipay-bills', ['file' => $file, '--company' => 1]) ->expectsOutputToContain('新增 0 条,更新 1 条') ->assertSuccessful(); $this->assertSame(1, AlipayBill::query()->where('company_id', 1)->count()); } public function testItRejectsAnUnexpectedHeader(): void { $file = $this->workbook('错误表头'); $this->artisan('business:import-taobao-alipay-bills', ['file' => $file, '--company' => 1]) ->expectsOutputToContain('表头与淘系支付宝账户模板不一致') ->assertFailed(); $this->assertSame(0, AlipayBill::query()->count()); } public function testItRejectsAnUnexpectedSheetName(): void { $file = $this->workbook(null, '其他工作表'); $this->artisan('business:import-taobao-alipay-bills', ['file' => $file, '--company' => 1]) ->expectsOutputToContain('未找到工作表“淘系支付宝账户”') ->assertFailed(); } public function testReplaceValidatesFirstAndAtomicallyRemovesOldRows(): void { $old = AlipayBill::query()->create([ 'company_id' => 1, 'record_code' => 'OLD-ROW', 'store_name' => '旧店铺', 'bill_item' => '旧账单', 'business_type' => '', 'alipay_account' => '', 'counterparty_account' => '', 'account_order_no' => '', 'business_serial_no' => '', 'goods_name' => '', 'income' => 0, 'expense' => 0, 'balance' => 0, 'platform' => 'taobao', 'created_from' => 'old-data', ]); $this->artisan('business:import-taobao-alipay-bills', [ 'file' => $this->workbook('错误表头'), '--company' => 1, '--replace' => true, ])->assertFailed(); $this->assertDatabaseHas('ec_alipay_bills', ['id' => $old->id, 'record_code' => 'OLD-ROW']); $this->artisan('business:import-taobao-alipay-bills', [ 'file' => $this->workbook(), '--company' => 1, '--replace' => true, ])->expectsOutputToContain('替换旧数据 1 条')->assertSuccessful(); $this->assertSame(1, AlipayBill::query()->where('company_id', 1)->count()); $this->assertDatabaseMissing('ec_alipay_bills', ['record_code' => 'OLD-ROW']); $this->assertDatabaseHas('ec_alipay_bills', ['record_code' => '2000000001141269665']); } private function workbook(?string $firstHeader = null, string $sheetName = '淘系支付宝账户'): string { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle($sheetName); $headers = \App\Business\TaobaoAlipayBillSpreadsheetImporter::HEADERS; $headers[0] = $firstHeader ?? $headers[0]; $sheet->fromArray($headers, null, 'A1'); $sheet->fromArray([ '2000000001141269665', '淘宝_测试店铺', '', '交易收款', '2088480341979438', '1570808064092446200', '2026071323001146201449637707', 'T200P3312548005778060954', '', '', 'buyer@example.test', '2026-07-15 23:59:12', '100.25', '0.00', '456.78', '交易付款', '', '12345678901234567890', '淘宝_测试店铺', '测试业务', '支付宝', 'BIZ12345678901234567890', '2026/7/16 6:31:02', '2026/7/16 6:35:03', '测试人', ], null, 'A2'); $file = tempnam(sys_get_temp_dir(), 'alipay-import-') . '.xlsx'; (new Xlsx($spreadsheet))->save($file); $spreadsheet->disconnectWorksheets(); $this->files[] = $file; return $file; } }