Files
datahub/backend/app/Controller/Api/V1/SkuOriginController.php
2026-04-14 13:45:28 +08:00

451 lines
17 KiB
PHP
Raw Permalink 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
declare(strict_types=1);
namespace App\Controller\Api\V1;
use App\Controller\AbstractController;
use App\Middleware\AuthMiddleware;
use App\Middleware\PermissionMiddleware;
use App\Model\SkuOrigin;
use App\Service\OperationLogService;
use App\Service\SkuService;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\Middleware;
use Hyperf\HttpServer\Annotation\RequestMapping;
use OpenApi\Attributes as OA;
use Psr\Http\Message\ResponseInterface;
/**
* 客户内部 SKU 管理接口
*/
#[OA\Tag(name: 'SkuOrigins', description: '客户内部 SKU 管理')]
#[Controller(prefix: "/api/v1/sku-origins")]
#[Middleware(AuthMiddleware::class)]
#[Middleware(PermissionMiddleware::class)]
class SkuOriginController extends AbstractController
{
public function __construct(
protected readonly SkuService $skuService,
) {}
/**
* SKU 列表
*/
#[OA\Get(
path: '/sku-origins',
summary: '客户内部 SKU 列表',
description: '获取客户内部 SKU 列表,支持分页和筛选',
security: [['bearerAuth' => []]],
tags: ['SkuOrigins'],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'per_page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15, maximum: 100)),
new OA\Parameter(name: 'company_id', in: 'query', required: false, description: '公司 ID 精确筛选', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'sku', in: 'query', required: false, description: 'SKU 编码模糊搜索', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'name', in: 'query', required: false, description: '产品名称模糊搜索', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'barcode', in: 'query', required: false, description: '条形码模糊搜索', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: '获取成功',
content: new OA\JsonContent(properties: [
new OA\Property(property: 'code', type: 'integer', example: 0),
new OA\Property(property: 'message', type: 'string', example: '获取成功'),
new OA\Property(property: 'data', properties: [
new OA\Property(property: 'items', type: 'array', items: new OA\Items(ref: '#/components/schemas/SkuOrigin')),
new OA\Property(property: 'total', type: 'integer', example: 100),
new OA\Property(property: 'page', type: 'integer', example: 1),
new OA\Property(property: 'per_page', type: 'integer', example: 15),
], type: 'object'),
])
),
new OA\Response(response: 401, description: '未认证', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
new OA\Response(response: 403, description: '无权限', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[RequestMapping(path: "", methods: "GET")]
public function index(): array
{
$query = SkuOrigin::query()->select([
'id', 'company_id', 'sku', 'barcode', 'name', 'label', 'hs', 'created_at', 'updated_at',
]);
// DataScope 过滤
$this->applyDataScope($query);
// 筛选条件
$company_id = $this->request->input('company_id');
if ($company_id !== null && $company_id !== '') {
$query->where('company_id', (int) $company_id);
}
$sku = $this->request->input('sku');
if ($sku !== null && $sku !== '') {
$query->where('sku', 'ilike', "%{$sku}%");
}
$name = $this->request->input('name');
if ($name !== null && $name !== '') {
$query->where(function ($q) use ($name): void {
$q->where('name', 'ilike', "%{$name}%")
->orWhere('label', 'ilike', "%{$name}%");
});
}
$barcode = $this->request->input('barcode');
if ($barcode !== null && $barcode !== '') {
$query->where('barcode', 'ilike', "%{$barcode}%");
}
$query->orderBy('created_at', 'desc');
// 分页
$per_page = min(max((int) $this->request->input('per_page', 15), 1), 100);
$page = max((int) $this->request->input('page', 1), 1);
$total = $query->count();
$items = $query->offset(($page - 1) * $per_page)->limit($per_page)->get();
return [
'code' => 0,
'message' => '获取成功',
'data' => [
'items' => $items,
'total' => $total,
'page' => $page,
'per_page' => $per_page,
],
];
}
/**
* SKU 详情
*/
#[OA\Get(
path: '/sku-origins/{id}',
summary: '客户内部 SKU 详情',
security: [['bearerAuth' => []]],
tags: ['SkuOrigins'],
parameters: [
new OA\Parameter(name: 'id', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: '获取成功',
content: new OA\JsonContent(properties: [
new OA\Property(property: 'code', type: 'integer', example: 0),
new OA\Property(property: 'message', type: 'string', example: '获取成功'),
new OA\Property(property: 'data', ref: '#/components/schemas/SkuOrigin'),
])
),
new OA\Response(response: 404, description: '数据不存在', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[RequestMapping(path: "{id}", methods: "GET")]
public function show(int $id): ResponseInterface|array
{
$query = SkuOrigin::query();
$this->applyDataScope($query);
$record = $query->where('id', $id)->first();
if (!$record) {
return $this->response->json([
'code' => 404,
'message' => '数据不存在',
])->withStatus(404);
}
return [
'code' => 0,
'message' => '获取成功',
'data' => $record,
];
}
/**
* 创建 SKU
*/
#[OA\Post(
path: '/sku-origins',
summary: '创建客户内部 SKU',
security: [['bearerAuth' => []]],
tags: ['SkuOrigins'],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ['company_id', 'sku', 'barcode', 'name'],
properties: [
new OA\Property(property: 'company_id', type: 'integer', example: 3),
new OA\Property(property: 'sku', type: 'string', example: '0032'),
new OA\Property(property: 'barcode', type: 'string', example: '6901234567890'),
new OA\Property(property: 'name', type: 'string', example: 'Running Shoes Model A'),
new OA\Property(property: 'label', type: 'string', nullable: true, example: '跑步鞋 A 款'),
new OA\Property(property: 'hs', type: 'string', nullable: true, example: '6403990090'),
new OA\Property(property: 'ledger', type: 'string', nullable: true),
new OA\Property(property: 'ext', type: 'object', nullable: true),
]
)
),
responses: [
new OA\Response(
response: 201,
description: '创建成功',
content: new OA\JsonContent(properties: [
new OA\Property(property: 'code', type: 'integer', example: 0),
new OA\Property(property: 'message', type: 'string', example: '创建成功'),
new OA\Property(property: 'data', ref: '#/components/schemas/SkuOrigin'),
])
),
new OA\Response(response: 422, description: '参数校验失败', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[RequestMapping(path: "", methods: "POST")]
public function store(): ResponseInterface|array
{
$data = $this->request->all();
// 参数校验
$required_fields = ['company_id', 'sku', 'barcode', 'name'];
foreach ($required_fields as $field) {
if (!isset($data[$field]) || $data[$field] === '') {
return $this->response->json([
'code' => 422,
'message' => "缺少必填字段: {$field}",
])->withStatus(422);
}
}
// 唯一性校验 (company_id + sku)
$exists = SkuOrigin::query()
->where('company_id', (int) $data['company_id'])
->where('sku', $data['sku'])
->exists();
if ($exists) {
return $this->response->json([
'code' => 422,
'message' => "该公司下 SKU '{$data['sku']}' 已存在",
])->withStatus(422);
}
$record = SkuOrigin::query()->create([
'company_id' => (int) $data['company_id'],
'sku' => $data['sku'],
'hs' => $data['hs'] ?? null,
'barcode' => $data['barcode'],
'name' => $data['name'],
'label' => $data['label'] ?? null,
'ledger' => $data['ledger'] ?? null,
'ext' => $data['ext'] ?? null,
]);
// 记录操作日志
$user_id = OperationLogService::getCurrentUserId();
if ($user_id) {
OperationLogService::log(
$user_id,
'create',
'sku_origin',
$record->id,
"创建内部 SKU: {$record->sku}",
ip: $this->request->getHeaderLine('x-real-ip') ?: null,
);
}
return $this->response->json([
'code' => 0,
'message' => '创建成功',
'data' => $record,
])->withStatus(201);
}
/**
* 更新 SKU
*/
#[OA\Put(
path: '/sku-origins/{id}',
summary: '更新客户内部 SKU',
security: [['bearerAuth' => []]],
tags: ['SkuOrigins'],
parameters: [
new OA\Parameter(name: 'id', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(properties: [
new OA\Property(property: 'sku', type: 'string'),
new OA\Property(property: 'barcode', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'label', type: 'string', nullable: true),
new OA\Property(property: 'hs', type: 'string', nullable: true),
new OA\Property(property: 'ledger', type: 'string', nullable: true),
new OA\Property(property: 'ext', type: 'object', nullable: true),
])
),
responses: [
new OA\Response(
response: 200,
description: '更新成功',
content: new OA\JsonContent(properties: [
new OA\Property(property: 'code', type: 'integer', example: 0),
new OA\Property(property: 'message', type: 'string', example: '更新成功'),
new OA\Property(property: 'data', ref: '#/components/schemas/SkuOrigin'),
])
),
new OA\Response(response: 404, description: '数据不存在', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
new OA\Response(response: 422, description: '参数校验失败', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[RequestMapping(path: "{id}", methods: "PUT")]
public function update(int $id): ResponseInterface|array
{
$query = SkuOrigin::query();
$this->applyDataScope($query);
$record = $query->where('id', $id)->first();
if (!$record) {
return $this->response->json([
'code' => 404,
'message' => '数据不存在',
])->withStatus(404);
}
$data = $this->request->all();
$allowed_fields = ['sku', 'barcode', 'name', 'label', 'hs', 'ledger', 'ext'];
$update_data = array_intersect_key($data, array_flip($allowed_fields));
// 如果修改了 sku,检查唯一性
if (isset($update_data['sku']) && $update_data['sku'] !== $record->sku) {
$exists = SkuOrigin::query()
->where('company_id', $record->company_id)
->where('sku', $update_data['sku'])
->where('id', '!=', $id)
->exists();
if ($exists) {
return $this->response->json([
'code' => 422,
'message' => "该公司下 SKU '{$update_data['sku']}' 已存在",
])->withStatus(422);
}
}
$record->update($update_data);
$user_id = OperationLogService::getCurrentUserId();
if ($user_id) {
OperationLogService::log(
$user_id,
'update',
'sku_origin',
$record->id,
"更新内部 SKU: {$record->sku}",
detail: ['updated_fields' => array_keys($update_data)],
ip: $this->request->getHeaderLine('x-real-ip') ?: null,
);
}
return [
'code' => 0,
'message' => '更新成功',
'data' => $record->fresh(),
];
}
/**
* 删除 SKU
*/
#[OA\Delete(
path: '/sku-origins/{id}',
summary: '删除客户内部 SKU',
description: '删除前检查是否有映射引用,有引用时禁止删除',
security: [['bearerAuth' => []]],
tags: ['SkuOrigins'],
parameters: [
new OA\Parameter(name: 'id', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: '删除成功',
content: new OA\JsonContent(properties: [
new OA\Property(property: 'code', type: 'integer', example: 0),
new OA\Property(property: 'message', type: 'string', example: '删除成功'),
])
),
new OA\Response(response: 404, description: '数据不存在', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
new OA\Response(response: 409, description: '存在映射引用', content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[RequestMapping(path: "{id}", methods: "DELETE")]
public function destroy(int $id): ResponseInterface|array
{
$query = SkuOrigin::query();
$this->applyDataScope($query);
$record = $query->where('id', $id)->first();
if (!$record) {
return $this->response->json([
'code' => 404,
'message' => '数据不存在',
])->withStatus(404);
}
// 检查映射引用
if ($this->skuService->hasReferences($id)) {
return $this->response->json([
'code' => 409,
'message' => '该 SKU 存在映射引用,请先删除相关映射记录',
])->withStatus(409);
}
$sku_value = $record->sku;
$record->delete();
$user_id = OperationLogService::getCurrentUserId();
if ($user_id) {
OperationLogService::log(
$user_id,
'delete',
'sku_origin',
$id,
"删除内部 SKU: {$sku_value}",
ip: $this->request->getHeaderLine('x-real-ip') ?: null,
);
}
return [
'code' => 0,
'message' => '删除成功',
];
}
/**
* DataScope 过滤(基于 company_id
*/
private function applyDataScope(\Hyperf\Database\Model\Builder $query): void
{
$scope_type = $this->request->getAttribute('scope_type');
$scope_ids = $this->request->getAttribute('scope_ids', []);
if ($scope_type === 'store') {
$company_ids = \App\Model\Store::query()
->whereIn('id', $scope_ids)
->distinct()
->pluck('company_id')
->toArray();
$query->whereIn('company_id', $company_ids);
} elseif ($scope_type === 'platform') {
$company_ids = \App\Model\Store::query()
->whereIn('platform_id', $scope_ids)
->distinct()
->pluck('company_id')
->toArray();
$query->whereIn('company_id', $company_ids);
}
}
}