Skip to content

新增模块开发

本文以在租户后台新增一个「产品管理」模块为例,介绍从数据库到前端的完整开发流程。

1. 创建数据表

编写迁移文件:

bash
cd server
php think make:migration create_products_table

编写迁移内容:

php
public function up()
{
    $this->table('products')
        ->addColumn('tenant_id', 'integer', ['default' => 0, 'comment' => '租户ID'])
        ->addColumn('name', 'string', ['limit' => 100, 'comment' => '名称'])
        ->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2, 'default' => 0])
        ->addColumn('status', 'integer', ['tiny' => true, 'default' => 1])
        ->addColumn('sort', 'integer', ['default' => 0])
        ->addColumn('created_at', 'datetime', ['null' => true])
        ->addColumn('updated_at', 'datetime', ['null' => true])
        ->addColumn('deleted_at', 'datetime', ['null' => true])
        ->addIndex('tenant_id')
        ->create();
}

多租户注意

所有租户级业务表必须包含 tenant_id 字段,Repository 基类的 query() 方法会自动注入 where('tenant_id', TenantContext::id())

执行迁移:

bash
php think migrate:run

2. 生成 CRUD 代码

bash
php think make:crud products --module=product --model=Product

自动生成:

后端:

  • app/model/product/Product.php — 模型
  • app/repository/product/ProductRepository.php — 数据访问层
  • app/service/product/ProductService.php — 业务逻辑层
  • app/tenantapi/controller/v1/product/ProductController.php — 控制器
  • app/tenantapi/validate/v1/product/ProductValidate.php — 验证器
  • app/tenantapi/route/product.php — 路由

前端:

  • tenant/src/api/product.ts — API 接口
  • tenant/src/views/product/index.vue — 列表页
  • tenant/src/views/product/components/ProductForm.vue — 表单组件

3. 添加菜单权限

在管理后台「系统管理 → 菜单管理」中添加:

  1. 目录菜单 — 产品管理(type=1)
  2. 页面菜单 — 产品列表(type=2,组件:product/index,权限:product.view
  3. 按钮权限 — 新增/编辑/删除(type=3,权限:product.store / product.update / product.destroy

4. 自定义业务逻辑

Repository 层 — 查询条件

php
// app/repository/product/ProductRepository.php
public function getSearchList(array $params, int $page = 1, int $size = 20): array
{
    $query = $this->query(); // 自动带 tenant_id 过滤

    if (!empty($params['keyword'])) {
        $query->whereLike('name', "%{$params['keyword']}%");
    }

    if (isset($params['status']) && $params['status'] !== '') {
        $query->where('status', (int) $params['status']);
    }

    return $query->order('sort', 'asc')
        ->order('id', 'desc')
        ->paginate(['page' => $page, 'list_rows' => $size])
        ->toArray();
}

Service 层 — 业务编排

php
// app/service/product/ProductService.php
public function createProduct(array $data): array
{
    // 业务校验...
    $product = $this->productRepository->create($data);

    // 触发事件(可选)
    $this->trigger('product.created', ['product_id' => $product['id']]);

    return $product;
}

Controller 层 — 请求处理

php
// app/tenantapi/controller/v1/product/ProductController.php
#[Permission('product.store')]
public function store(): Json
{
    $data = $this->request->post();
    $this->validate($data, ProductValidate::class);
    $result = $this->productService->createProduct($data);
    return $this->success('创建成功', $result);
}

5. 前端开发

API 定义

ts
// tenant/src/api/product.ts
import { myRequest } from '@/utils/request'

export const productApi = {
    list: (params: any) => myRequest.get('/tenantapi/products', { params }),
    show: (id: number) => myRequest.get(`/tenantapi/products/${id}`),
    create: (data: any) => myRequest.post('/tenantapi/products', data),
    update: (id: number, data: any) => myRequest.put(`/tenantapi/products/${id}`, data),
    destroy: (id: number) => myRequest.delete(`/tenantapi/products/${id}`),
}

列表页 hooks

列表页推荐使用 useListPage hook 简化分页、搜索、删除逻辑:

ts
const { list, loading, pagination, searchForm, getList, handleSearch, handleDelete } =
    useListPage({
        fetchFn: (params) => productApi.list(params),
        deleteFn: (id) => productApi.destroy(id),
        defaultSearchForm: { keyword: '', status: '' },
    })

6. 验证

  1. 执行数据库迁移
  2. 在菜单管理中添加菜单
  3. 分配角色权限
  4. 访问页面测试 CRUD 功能

基于 Apache-2.0 协议开源