CaiWuXiTong/modules/EcommerceFinance/Services/AccessoryReshipCourierStore.php

130 lines
6.5 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class AccessoryReshipCourierStore
{
public const TABLE = 'business_accessory_reship_couriers';
public const EVENTS = 'business_accessory_reship_config_events';
public function query(int $companyId)
{
abort_unless($companyId > 0, 422, '缺少当前公司。');
return DB::table(self::TABLE)->where('company_id', $companyId);
}
public function activeRates(int $companyId): array
{
$result = [];
foreach ($this->query($companyId)->whereNull('deleted_at')->get() as $row) {
$result[trim($row->courier_name)] = (array) $row;
}
return $result;
}
public function filtered(int $companyId, array $filters)
{
$query = $this->query($companyId);
$status = $filters['deleted_status'] ?? 'active';
if ($status === 'deleted') {
$query->whereNotNull('deleted_at');
} elseif ($status !== 'all') {
$query->whereNull('deleted_at');
}
foreach (['courier_name', 'creator'] as $field) {
if (($filters[$field] ?? '') !== '') {
$query->where($field, 'like', '%' . addcslashes($filters[$field], '\\%_') . '%');
}
}
if (($filters['created_from'] ?? '') !== '') {
$query->whereRaw('COALESCE(source_created_at, created_at) >= ?', [$filters['created_from'] . ' 00:00:00']);
}
if (($filters['created_to'] ?? '') !== '') {
$query->whereRaw('COALESCE(source_created_at, created_at) < ?', [Carbon::parse($filters['created_to'])->addDay()->format('Y-m-d') . ' 00:00:00']);
}
return $query->orderByRaw('COALESCE(source_created_at, created_at) DESC')->orderByDesc('id');
}
public function save(int $companyId, array $data, string $actor, ?int $id = null): int
{
return DB::transaction(function () use ($companyId, $data, $actor, $id): int {
$before = $id === null ? null : $this->query($companyId)->where('id', $id)->whereNull('deleted_at')->lockForUpdate()->first();
abort_if($id !== null && $before === null, 404);
$this->assertIdentity($companyId, $data, $id);
$time = $this->time();
$values = ['courier_code' => trim($data['courier_code']), 'courier_name' => trim($data['courier_name']), 'rate_basis_points' => (int) $data['rate_basis_points'], 'updated_at' => $time];
if ($id === null) {
$id = DB::table(self::TABLE)->insertGetId($values + ['company_id' => $companyId, 'creator' => $actor, 'source_created_at' => null, 'created_at' => $time]);
} else {
$this->query($companyId)->where('id', $id)->update($values);
}
$this->event($companyId, $id, $actor, $before ? 'update' : 'create', $before ? '修改快递扣款配置' : '新增快递扣款配置', ['before' => $before, 'after' => $values]);
return $id;
});
}
public function changeDeleted(int $companyId, int $id, bool $deleted, string $actor): void
{
DB::transaction(function () use ($companyId, $id, $deleted, $actor): void {
$row = $this->query($companyId)->where('id', $id)->lockForUpdate()->first();
abort_if($row === null, 404);
if (($row->deleted_at !== null) === $deleted) {
return;
}
$this->query($companyId)->where('id', $id)->update(['deleted_at' => $deleted ? $this->time() : null, 'updated_at' => $this->time()]);
$this->event($companyId, $id, $actor, $deleted ? 'delete' : 'restore', ($deleted ? '删除' : '恢复') . '快递扣款配置', ['courier_code' => $row->courier_code, 'courier_name' => $row->courier_name]);
});
}
/** 整批事务:重复导入相同配置跳过,冲突不覆盖、删除状态不自动恢复。 */
public function import(int $companyId, array $rows, string $actor, string $sourceHash): array
{
return DB::transaction(function () use ($companyId, $rows, $actor, $sourceHash): array {
$added = 0;
$skipped = 0;
foreach ($rows as $row) {
$existing = $this->query($companyId)->where('courier_code', $row['courier_code'])->lockForUpdate()->first();
if ($existing !== null) {
if ($existing->courier_name !== $row['courier_name'] || (int) $existing->rate_basis_points !== $row['rate_basis_points'] || $existing->deleted_at !== null) {
throw ValidationException::withMessages(['file' => '快递代码 ' . $row['courier_code'] . ' 已存在不同配置或已删除,整批未导入,请核对后重试。']);
}
$skipped++;
continue;
}
$this->assertIdentity($companyId, $row);
DB::table(self::TABLE)->insert($row + ['company_id' => $companyId, 'created_at' => $this->time(), 'updated_at' => $this->time()]);
$added++;
}
$this->event($companyId, null, $actor, 'import', "导入快递配置:新增{$added}条,重复跳过{$skipped}条", ['added' => $added, 'skipped' => $skipped, 'source_sha256' => $sourceHash]);
return compact('added', 'skipped');
});
}
private function assertIdentity(int $companyId, array $data, ?int $exceptId = null): void
{
$query = $this->query($companyId)->where(function ($query) use ($data): void {
$query->where('courier_code', trim($data['courier_code']))->orWhere('courier_name', trim($data['courier_name']));
});
if ($exceptId !== null) {
$query->where('id', '<>', $exceptId);
}
if ($query->exists()) {
throw ValidationException::withMessages(['courier_name' => '快递代码或名称已存在(含已删除配置),请编辑或恢复原配置。']);
}
}
private function event(int $companyId, ?int $id, string $actor, string $action, string $description, array $details): void
{
DB::table(self::EVENTS)->insert(['company_id' => $companyId, 'config_id' => $id, 'actor' => $actor, 'action' => $action, 'description' => $description, 'details' => json_encode($details, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), 'created_at' => $this->time()]);
}
private function time(): string
{
return Carbon::now(config('app.platform_timezone', 'Asia/Shanghai'))->format('Y-m-d H:i:s');
}
}