Files
2026-08-31 11:33:59 +08:00

172 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
namespace app\api\controller\system;
use app\api\controller\Base;
use app\api\CurrentMember;
use app\dao\system\FeedbackDao;
use core\exception\handler\UnauthorizedHttpException;
use core\tool\Json;
use madong\swagger\annotation\response\SimpleResponse;
use madong\swagger\attribute\AllowAnonymous;
use OpenApi\Attributes as OA;
use Webman\Http\Request;
use Webman\Http\Response;
#[OA\Tag(name: '问题反馈')]
final class FeedbackController extends Base
{
private FeedbackDao $dao;
public function __construct(FeedbackDao $dao, private readonly CurrentMember $currentMember)
{
$this->dao = $dao;
}
#[OA\Get(
path: '/feedback/mine',
summary: '获取当前用户的问题反馈列表',
tags: ['问题反馈'],
parameters: [
new OA\Parameter(name: 'page', description: '页码', in: 'query', schema: new OA\Schema(type: 'integer', default: 1)),
new OA\Parameter(name: 'limit', description: '每页数量', in: 'query', schema: new OA\Schema(type: 'integer', default: 10)),
],
responses: [new OA\Response(response: 200, description: '获取成功')]
)]
#[SimpleResponse(schema: [], example: [])]
#[AllowAnonymous(requireToken: true, requirePermission: false, description: '会员接口')]
public function mine(Request $request): Response
{
try {
$memberId = $this->currentMember->id();
if (!$memberId) {
throw new UnauthorizedHttpException('请先登录后查看反馈记录');
}
$page = max(1, (int)$request->get('page', 1));
$limit = min(50, max(1, (int)$request->get('limit', 10)));
$where = ['created_by' => $memberId];
$total = $this->dao->getCount($where);
$items = $this->dao->selectList($where, '*', $page, $limit, 'created_at desc')?->values()->all() ?? [];
return Json::success('ok', [
'items' => $items,
'total' => $total,
'page' => $page,
'limit' => $limit,
]);
} catch (\Throwable $e) {
return Json::fail($e->getMessage(), [], $e instanceof UnauthorizedHttpException ? 401 : 0);
}
}
#[OA\Get(
path: '/feedback/mine/{id}',
summary: '获取当前用户的问题反馈详情',
tags: ['问题反馈'],
parameters: [new OA\Parameter(name: 'id', description: '反馈 ID', in: 'path', required: true, schema: new OA\Schema(type: 'string'))],
responses: [new OA\Response(response: 200, description: '获取成功')]
)]
#[SimpleResponse(schema: [], example: [])]
#[AllowAnonymous(requireToken: true, requirePermission: false, description: '会员接口')]
public function mineShow(Request $request): Response
{
try {
$memberId = $this->currentMember->id();
if (!$memberId) {
throw new UnauthorizedHttpException('请先登录后查看反馈记录');
}
$feedback = $this->dao->get([
'id' => $request->route->param('id'),
'created_by' => $memberId,
]);
if (!$feedback) {
return Json::fail('反馈记录不存在或无权查看', [], 404);
}
$feedback->load('replyAdmin');
return Json::success('ok', $feedback->toArray());
} catch (\Throwable $e) {
return Json::fail($e->getMessage(), [], $e instanceof UnauthorizedHttpException ? 401 : 0);
}
}
#[OA\Post(
path: '/feedback',
summary: '提交问题反馈',
tags: ['问题反馈'],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ['type', 'content', 'source'],
properties: [
new OA\Property(property: 'type', description: '反馈类型:bug/suggestion/correction', type: 'string'),
new OA\Property(property: 'content', description: '问题描述,最多2000个字符', type: 'string'),
new OA\Property(property: 'contact', description: '联系方式,最多100个字符', type: 'string'),
new OA\Property(property: 'images', description: '截图地址列表,最多3张', type: 'array', items: new OA\Items(type: 'string')),
new OA\Property(property: 'source', description: '反馈来源:app/web', type: 'string'),
]
)
),
responses: [
new OA\Response(response: 200, description: '提交成功'),
new OA\Response(response: 400, description: '参数错误'),
]
)]
#[SimpleResponse(schema: [], example: [])]
#[AllowAnonymous(requireToken: false, requirePermission: false, description: '公共接口')]
public function store(Request $request): Response
{
try {
$data = $request->post();
$data['content'] = trim((string)($data['content'] ?? ''));
$data['contact'] = trim((string)($data['contact'] ?? ''));
$data['source'] = (string)($data['source'] ?? 'app');
$data['images'] = $data['images'] ?? [];
$validator = validator($data, [
'type' => 'required|in:bug,suggestion,correction',
'content' => 'required|max:2000',
'contact' => 'nullable|max:100',
'images' => 'array|max:3',
'source' => 'required|in:app,web',
], [
'type.required' => '请选择反馈类型',
'type.in' => '反馈类型不正确',
'content.required' => '请填写问题描述',
'content.max' => '问题描述不能超过2000个字符',
'contact.max' => '联系方式不能超过100个字符',
'images.array' => '截图附件格式不正确',
'images.max' => '截图附件最多上传3张',
'source.required' => '请选择反馈来源',
'source.in' => '反馈来源不正确',
]);
if ($validator->fails()) {
throw new \InvalidArgumentException($validator->errors()->first());
}
$images = array_values(array_filter($data['images'], static fn ($image) => is_string($image) && $image !== ''));
$type = $data['type'];
$content = $data['content'];
$contact = $data['contact'];
$source = $data['source'];
$memberId = $this->currentMember->id();
$model = $this->dao->save([
'type' => $type,
'content' => $content,
'contact' => $contact,
'images' => $images,
'source' => $source,
'created_by' => $memberId ?: null,
]);
return Json::success('提交成功', ['id' => $model->id]);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
}