Files
MrleeAdmin/.windsurf/skills/backend-gen-api-controller/SKILL.md
T
2026-08-31 11:33:59 +08:00

13 KiB

name, description, globs
name description globs
backend-gen-api-controller Generate API controller for madong module前台API控制器. Supports both app (main project) and plugin targets. Creates controller extending Base class with OpenAPI attributes and standard REST endpoints for public/member access.
app/api/controller/**/*.php
plugin/**/app/api/controller/**/*.php

Generate API Controller (前台 API 控制器)

Create API controller for module前台用户访问).

File Location

App (主项目):

app/api/controller/{module}/{Model}Controller.php

Plugin (插件):

plugin/{plugin}/app/api/controller/{module}/{Model}Controller.php

Target Variables

变量 App Plugin
{prefix} `` (空) plugin\{Plugin}
{ns} app plugin\{Plugin}\app
{service_ns} app\service\api plugin\{Plugin}\app\service\api

API Controller Features

  • 继承 Base: 继承 app\api\controller\Base 基类
  • 无需权限验证: 不使用 PermissionMiddleware
  • 支持会员认证: 认证属性和中间件必须先以当前项目 app/api 的实际实现为准。当前项目已核对的 app/api 控制器中未发现 MemberAuthRequired 的实际使用,不应直接套用该属性。
  • 轻量级: 只返回必要的数据
  • 雪花ID: 主键使用雪花ID

API Controller Template

<?php

declare(strict_types=1);
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 */

namespace {ns}\api\controller\{module};

use app\api\controller\Base;
use app\api\attribute\MemberAuthRequired;
use {service_ns}\{module}\{Model}Service;
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;

/**
 * {Model}前台API控制器
 */
#[OA\Tag(name: '{Module}模块')]
final class {Model}Controller extends Base
{
    public function __construct(private {Model}Service $service)
    {
    }

    /**
     * 获取列表
     */
    #[OA\Get(
        path: '/{api_prefix}',
        summary: '获取{Model}列表',
        tags: ['{Module}模块'],
        responses: [
            new OA\Response(response: 200, description: '获取成功'),
        ]
    )]
    #[AllowAnonymous(requireToken: false, requirePermission: false)]
    #[SimpleResponse(schema: [], example: [])]
    public function index(Request $request): Response
    {
        try {
            $page    = (int) $request->input('page', 1);
            $limit   = (int) $request->input('limit', 20);
            $where   = $this->buildWhere($request);
            $order   = $request->input('order', 'id desc');

            $total = $this->service->count($where);
            $list  = $this->service->selectList($where, '*', $page, $limit, $order);

            return Json::success('ok', compact('list', 'total'));
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 获取详情
     */
    #[OA\Get(
        path: '/{api_prefix}/{id}',
        summary: '获取{Model}详情',
        tags: ['{Module}模块'],
        parameters: [
            new OA\Parameter(name: 'id', description: '{Model}ID', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
        ],
        responses: [
            new OA\Response(response: 200, description: '获取成功'),
            new OA\Response(response: 404, description: '不存在'),
        ]
    )]
    #[AllowAnonymous(requireToken: false, requirePermission: false)]
    #[SimpleResponse(schema: [], example: [])]
    public function show(Request $request, int $id): Response
    {
        try {
            $data = $this->service->get($id);
            if (empty($data)) {
                return Json::fail('{Model}不存在', [], 404);
            }
            return Json::success('ok', $data);
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 创建{Model}
     */
    #[OA\Post(
        path: '/{api_prefix}',
        summary: '创建{Model}',
        tags: ['{Module}模块'],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: [{required_fields}],
                properties: [
                    {form_fields}
                ]
            )
        ),
        responses: [
            new OA\Response(response: 200, description: '创建成功'),
            new OA\Response(response: 401, description: '未登录'),
        ]
    )]
    #[MemberAuthRequired]
    #[SimpleResponse(schema: [], example: [])]
    public function store(Request $request): Response
    {
        try {
            $data  = $request->post();
            $model = $this->service->save($data);

            $pk = $model->getPk();
            return Json::success('创建成功', [$pk => $model->getAttribute($pk)]);
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 更新{Model}
     */
    #[OA\Put(
        path: '/{api_prefix}/{id}',
        summary: '更新{Model}',
        tags: ['{Module}模块'],
        parameters: [
            new OA\Parameter(name: 'id', description: '{Model}ID', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
        ],
        responses: [
            new OA\Response(response: 200, description: '更新成功'),
            new OA\Response(response: 401, description: '未登录'),
        ]
    )]
    #[MemberAuthRequired]
    #[SimpleResponse(schema: [], example: [])]
    public function update(Request $request, int $id): Response
    {
        try {
            $data = $request->post();
            $this->service->update($id, $data);
            return Json::success('更新成功');
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 删除{Model}
     */
    #[OA\Delete(
        path: '/{api_prefix}/{id}',
        summary: '删除{Model}',
        tags: ['{Module}模块'],
        parameters: [
            new OA\Parameter(name: 'id', description: '{Model}ID', in: 'path', required: true, schema: new OA\Schema(type: 'integer')),
        ],
        responses: [
            new OA\Response(response: 200, description: '删除成功'),
            new OA\Response(response: 401, description: '未登录'),
        ]
    )]
    #[MemberAuthRequired]
    #[SimpleResponse(schema: [], example: [])]
    public function destroy(Request $request, int $id): Response
    {
        try {
            $this->service->delete($id);
            return Json::success('删除成功');
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 构建查询条件
     */
    protected function buildWhere(Request $request): array
    {
        $where = [];

        // 添加常用的查询条件
        $searchFields = [{search_fields}];

        foreach ($searchFields as $field) {
            $value = $request->input($field);
            if ($value !== null && $value !== '') {
                $where[$field] = $value;
            }
        }

        // 状态条件
        if ($request->input('status') !== null) {
            $where['status'] = (int) $request->input('status');
        }

        return $where;
    }
}

Complete Example (App)

<?php

declare(strict_types=1);
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 */

namespace app\api\controller\question;

use app\api\controller\Base;
use app\api\attribute\MemberAuthRequired;
use app\service\api\question\QuestionService;
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;

/**
 * 问题前台API控制器
 */
#[OA\Tag(name: '问答模块')]
final class QuestionController extends Base
{
    public function __construct(private QuestionService $service)
    {
    }

    #[OA\Get(path: '/ask/question', summary: '获取问题列表', tags: ['问答模块'])]
    #[AllowAnonymous(requireToken: false, requirePermission: false)]
    public function index(Request $request): Response
    {
        try {
            $page = (int) $request->input('page', 1);
            $limit = (int) $request->input('limit', 20);
            $where = $this->buildWhere($request);

            $total = $this->service->count($where);
            $list = $this->service->selectList($where, '*', $page, $limit, 'id desc');

            return Json::success('ok', compact('list', 'total'));
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    #[OA\Get(path: '/ask/question/{id}', summary: '获取问题详情', tags: ['问答模块'])]
    #[AllowAnonymous(requireToken: false, requirePermission: false)]
    public function show(Request $request, int $id): Response
    {
        try {
            $data = $this->service->get($id);
            if (empty($data)) {
                return Json::fail('问题不存在', [], 404);
            }
            return Json::success('ok', $data);
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    #[OA\Post(path: '/ask/question', summary: '发布问题', tags: ['问答模块'])]
    #[MemberAuthRequired]
    public function store(Request $request): Response
    {
        try {
            $data = $request->post();
            $model = $this->service->save($data);
            return Json::success('发布成功', ['id' => $model->id]);
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    protected function buildWhere(Request $request): array
    {
        $where = [];
        if ($request->input('category_id')) {
            $where['category_id'] = (int) $request->input('category_id');
        }
        $where['status'] = 1;
        return $where;
    }
}

Complete Example (Plugin)

<?php

declare(strict_types=1);
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 */

namespace plugin\official\app\api\controller\question;

use app\api\controller\Base;
use app\api\attribute\MemberAuthRequired;
use plugin\official\app\service\api\question\QuestionService;
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;

/**
 * 问题前台API控制器
 */
#[OA\Tag(name: '问答模块')]
final class QuestionController extends Base
{
    public function __construct(private QuestionService $service)
    {
    }

    #[OA\Get(path: '/ask/question', summary: '获取问题列表', tags: ['问答模块'])]
    #[AllowAnonymous(requireToken: false, requirePermission: false)]
    public function index(Request $request): Response
    {
        try {
            $page = (int) $request->input('page', 1);
            $limit = (int) $request->input('limit', 20);
            $where = $this->buildWhere($request);

            $total = $this->service->count($where);
            $list = $this->service->selectList($where, '*', $page, $limit, 'id desc');

            return Json::success('ok', compact('list', 'total'));
        } catch (\Exception $e) {
            return Json::fail($e->getMessage());
        }
    }

    // ... 其他方法同上
}

API Path Convention

Method Path Auth Description
GET /{api_prefix} 公开 列表(分页)
GET /{api_prefix}/{id} 公开 详情
POST /{api_prefix} 会员 创建
PUT /{api_prefix}/{id} 会员 更新
DELETE /{api_prefix}/{id} 会员 删除

Authentication Options

公开接口

#[AllowAnonymous(requireToken: false, requirePermission: false)]

会员专用接口

请先检查当前项目 app/api 的真实认证中间件、控制器属性和 token 解析方式,再配置会员认证;不要假设项目一定存在 MemberAuthRequired。

Auto-generation Checklist

When generating API controller:

  • Set correct namespace based on target
  • Import required classes according to the actual project implementation (Base, Json, and verified authentication attributes/middleware)
  • Extend Base controller
  • Inject Service in constructor
  • Add OpenAPI attributes for each action
  • Use appropriate auth attribute (AllowAnonymous or MemberAuthRequired)
  • Implement standard CRUD methods
  • Use proper API path convention
  • Add buildWhere() for query building