middleware('auth'); $this->middleware('company.identify'); } public function export(Request $request): StreamedResponse|RedirectResponse { if (! $this->repository->ready() || ! $this->repository->initialized()) { return $this->redirectToPage($request) ->with('error', '商品价格镜像尚未完成首次同步,暂时不能导出。'); } $filters = $this->filters($request); $companyId = (int) (company_id() ?: 1); return response()->streamDownload(function () use ($filters, $companyId): void { $csv = fopen('php://output', 'w'); if ($csv === false) { throw new RuntimeException('无法创建商品价格导出流。'); } fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF)); fputcsv($csv, [ '记录标识', '业务编号', '商品编码', 'SPU', '商品名称', '类目', '生产状态', '最低售价', '计算限价', '采购价', '运费', '京喜限价', '流程状态', '审批结果', '流程标题', '流程完成时间', '人工维护', '维护时间', ]); foreach ($this->repository->exportRows($filters, $companyId) as $row) { fputcsv($csv, [ $this->csvText($row->upstream_key ?? ''), $this->csvText($row->business_id ?? ''), $this->csvText($row->barcode ?? ''), $this->csvText($row->spu ?? ''), $this->csvText($row->product_name ?? ''), $this->csvText($row->category ?? ''), $this->csvText($row->production_status ?? ''), $row->minimum_price ?? null, $row->price_limit ?? null, $row->purchase_price ?? null, $row->shipping_fee ?? null, $row->jingxi_limit_price ?? null, $this->csvText($row->approval_status ?? ''), $this->csvText($row->approval_result ?? ''), $this->csvText($row->workflow_title ?? ''), $this->csvText($row->completed_at ?? ''), (int) ($row->is_overridden ?? 0) === 1 ? '是' : '否', $this->csvText($row->override_updated_at ?? ''), ]); } fclose($csv); }, '商品价格_' . now()->format('Ymd_His') . '.csv', [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store', ]); } public function import(Request $request): RedirectResponse { $request->validate([ 'import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:10240'], ]); $result = $this->importer->import( $request->file('import'), (int) (company_id() ?: 1), user_id() ? (int) user_id() : null, ); return $this->redirectToPage($request)->with( 'success', "导入完成:新增维护 {$result['created']} 条,更新维护 {$result['updated']} 条。", ); } public function template(): StreamedResponse { return $this->template->download('商品价格', $this->importer->templateFields()); } public function update(Request $request, string $upstreamKey): RedirectResponse { $values = $request->validate( [ 'barcode' => ['required', 'string', 'max:191'], 'spu' => ['nullable', 'string', 'max:191'], 'product_name' => ['nullable', 'string', 'max:5000'], 'category' => ['nullable', 'string', 'max:255'], 'production_status' => ['nullable', 'string', 'max:120'], 'minimum_price' => ['nullable', 'numeric', 'decimal:0,4', 'min:0', 'max:9999999999999999.9999'], 'price_limit' => ['nullable', 'numeric', 'decimal:0,4', 'min:0', 'max:9999999999999999.9999'], 'purchase_price' => ['nullable', 'numeric', 'decimal:0,4', 'min:0', 'max:9999999999999999.9999'], 'shipping_fee' => ['nullable', 'numeric', 'decimal:0,4', 'min:0', 'max:9999999999999999.9999'], 'jingxi_limit_price' => ['nullable', 'numeric', 'decimal:0,4', 'min:0', 'max:9999999999999999.9999'], ], [ 'minimum_price.decimal' => '最低售价最多支持 4 位小数。', 'price_limit.decimal' => '计算限价最多支持 4 位小数。', 'purchase_price.decimal' => '采购价最多支持 4 位小数。', 'shipping_fee.decimal' => '运费最多支持 4 位小数。', 'jingxi_limit_price.decimal' => '京喜限价最多支持 4 位小数。', ], ); try { $this->repository->applyOverrides( [$upstreamKey => $values], (int) (company_id() ?: 1), user_id() ? (int) user_id() : null, 'manual', ); } catch (RuntimeException $exception) { if (str_starts_with($exception->getMessage(), 'product_price_record_not_found:')) { abort(404, '商品价格记录不存在。'); } if ($exception->getMessage() === 'product_price_maintenance_schema_missing') { throw ValidationException::withMessages([ 'barcode' => '商品价格维护功能尚未完成初始化,请稍后重试。', ]); } throw $exception; } return $this->redirectToPage($request)->with('success', '商品价格已保存。'); } /** @return array{barcode:string,completed_from:string,completed_to:string} */ private function filters(Request $request): array { return [ 'barcode' => mb_substr(trim((string) $request->query('barcode', '')), 0, 191), 'completed_from' => $this->date((string) $request->query('completed_from', '')), 'completed_to' => $this->date((string) $request->query('completed_to', '')), ]; } private function redirectToPage(Request $request): RedirectResponse { $filters = $this->filters($request); $query = array_filter([ ...$filters, 'page' => max((int) $request->query('page', 1), 1), ], static fn (mixed $value): bool => $value !== ''); $url = route('business.pages.show', [ 'company_id' => company_id() ?: 1, 'section' => 'supplier-payables', 'page' => 'product-prices', ]); return redirect()->to($url . ($query === [] ? '' : '?' . http_build_query($query))); } private function date(string $value): string { $value = trim($value); if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) !== 1) { return ''; } [$year, $month, $day] = array_map('intval', explode('-', $value)); return checkdate($month, $day, $year) ? $value : ''; } private function csvText(mixed $value): string { $value = (string) $value; return preg_match('/^[=+\-@\t\r]/u', $value) === 1 ? "\t" . $value : $value; } }