CaiWuXiTong/app/Http/Controllers/Business/SupplierNegativeAlertDingTalkActions.php
苏木 0f65cb124b feat: 供应商负数预警支持导出和未回传筛选 (#1113)
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
2026-11-11 19:49:44 +08:00

181 lines
6.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Business;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\EcommerceFinance\Services\SupplierNegativeAlertDingTalkWorkflow;
use Modules\EcommerceFinance\View\SupplierNegativeAlertPageData;
use RuntimeException;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Throwable;
class SupplierNegativeAlertDingTalkActions extends Controller
{
public function __construct(
private readonly SupplierNegativeAlertPageData $pageData,
private readonly SupplierNegativeAlertDingTalkWorkflow $workflow,
) {
$this->middleware('auth');
$this->middleware('company.identify');
}
public function saveRobot(Request $request): RedirectResponse
{
$data = $request->validate([
'dingtalk_webhook' => ['nullable', 'string', 'max:2048'],
'dingtalk_secret' => ['nullable', 'string', 'max:256'],
'dingtalk_group_name' => ['required', 'string', 'max:191'],
]);
$this->workflow->saveConfiguration(
(string) ($data['dingtalk_webhook'] ?? ''),
(string) ($data['dingtalk_secret'] ?? ''),
(string) $data['dingtalk_group_name'],
);
return back()->with('success', '供应商负数预警群机器人已保存。');
}
public function send(Request $request): RedirectResponse
{
$data = $request->validate([
'purchaser' => ['nullable', 'string', 'max:191'],
'month' => ['nullable', 'string', 'regex:/^\d{4}-\d{2}$/'],
'supplier' => ['nullable', 'string', 'max:191'],
'checker' => ['nullable', 'string', 'max:191'],
'settlement_method' => ['nullable', 'string', 'max:191'],
'handling' => ['nullable', 'in:,pending'],
]);
$purchaser = trim((string) ($data['purchaser'] ?? ''));
$filters = [
'month' => (string) ($data['month'] ?? ''),
'supplier' => (string) ($data['supplier'] ?? ''),
'checker' => (string) ($data['checker'] ?? ''),
'settlement_method' => (string) ($data['settlement_method'] ?? ''),
'purchaser' => $purchaser,
'handling' => (string) ($data['handling'] ?? ''),
];
$filteredRows = $this->pageData->rowsForFilters([
...$filters,
'purchaser' => '',
]);
$rows = $purchaser === ''
? $filteredRows
: array_values(array_filter(
$filteredRows,
static fn (array $row): bool => trim((string) ($row['purchaser'] ?? '')) === $purchaser,
));
if ($rows === []) {
return back()->withErrors([
'dingtalk_send' => '当前筛选没有可发送给该采购的供应商。',
]);
}
$companyId = (int) (company_id() ?: 1);
$rows = $this->workflow->unhandledRows($rows, $companyId);
if ($rows === []) {
return back()->withErrors([
'dingtalk_send' => '当前筛选中的供应商均已回传处理结果,无需重复发送。',
]);
}
try {
if ($purchaser === '') {
$result = $this->workflow->sendGrouped(
$rows,
$filters,
$companyId,
user_id() ? (int) user_id() : null,
);
} else {
$result = $this->workflow->send(
$rows,
$purchaser,
$filters,
$companyId,
user_id() ? (int) user_id() : null,
);
}
} catch (Throwable $exception) {
report($exception);
return back()->withErrors(['dingtalk_send' => '发送失败:' . $exception->getMessage()]);
}
if ($purchaser === '') {
$message = "已一键发送至{$result['group_name']}:{$result['purchaser_count']} 位采购,"
. "{$result['supplier_count']} 家供应商、{$result['row_count']} 条预警。";
if ($result['unassigned_supplier_count'] > 0) {
$message .= " 另有 {$result['unassigned_supplier_count']} 家供应商未分配采购,未发送。";
}
return back()->with('success', $message);
}
return back()->with('success', "已发送至{$result['group_name']}:采购 {$purchaser},"
. "{$result['supplier_count']} 家供应商、{$result['row_count']} 条预警。");
}
public function export(Request $request): StreamedResponse
{
$data = $this->pageData->exportData($request->query());
$fields = array_values(array_filter(
(array) ($data['fields'] ?? []),
static fn (array $field): bool => ($field['name'] ?? '') !== 'company',
));
$fields[] = ['name' => 'handling_result_meta', 'label' => '回传信息', 'type' => 'text'];
$rows = (array) ($data['rows'] ?? []);
$filters = (array) ($data['filters'] ?? []);
$period = trim((string) ($filters['month'] ?? '')) ?: '全部月份';
$filename = '供应商负数预警_' . $period . '_' . now()->format('Ymd_His') . '.csv';
return response()->streamDownload(function () use ($fields, $rows): void {
$stream = fopen('php://output', 'w');
if ($stream === false) {
throw new RuntimeException('无法创建供应商负数预警导出文件。');
}
fwrite($stream, "\xEF\xBB\xBF");
fputcsv($stream, array_map(
static fn (array $field): string => (string) ($field['label'] ?? ''),
$fields,
));
foreach ($rows as $row) {
$values = [];
foreach ($fields as $field) {
$name = (string) ($field['name'] ?? '');
$value = $row[$name] ?? '';
if ($name === 'handling_result' && trim((string) $value) === '') {
$value = '采购未回传';
} elseif (is_array($value)) {
$value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
$values[] = $this->csvCell($value);
}
fputcsv($stream, $values);
}
fclose($stream);
}, $filename, [
'Content-Type' => 'text/csv; charset=UTF-8',
'Cache-Control' => 'private, no-store',
]);
}
private function csvCell(mixed $value): string
{
$text = trim((string) $value);
if ($text === '' || is_numeric($text)) {
return $text;
}
if (preg_match('/^\d{12,}$/', $text) === 1) {
return "'" . $text;
}
return preg_match('/^[=+@\t\r]/u', $text) === 1 ? "'" . $text : $text;
}
}