免费资源下载
原创技术教程 | 更新时间:2023年10月
一、项目架构设计理念
在现代Web开发中,API服务已成为前后端分离架构的核心。ThinkPHP 6.0以其全新的架构设计,为构建高性能API服务提供了强大支持。本教程将通过一个完整的电商平台API案例,展示如何利用ThinkPHP 6.0的多应用模式、中间件机制和依赖注入特性,构建可扩展的企业级应用。
1.1 多应用模式的优势
ThinkPHP 6.0的多应用模式允许我们在单一项目中创建多个独立的应用模块,每个应用拥有自己的配置、控制器和路由。这种设计特别适合大型项目,例如:
project/
├── app/
│ ├── admin/ # 后台管理应用
│ ├── api/ # 移动端API应用
│ ├── web/ # PC端Web应用
│ └── common/ # 公共模块
├── config/
└── route/
二、环境配置与初始化
2.1 安装与配置
使用Composer创建项目并启用多应用模式:
composer create-project topthink/think tp6-api-project
cd tp6-api-project
php think multi-app:create api
php think multi-app:create admin
2.2 数据库配置优化
在config/database.php中配置数据库连接池:
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'tp6_api',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'break_reconnect' => true, // 断线重连
'trigger_sql' => true, // SQL监听
'pool' => [ // 连接池配置
'min_connections' => 5,
'max_connections' => 20,
'connect_timeout' => 10,
]
]
]
];
三、API服务核心实现
3.1 JWT认证中间件
创建app/api/middleware/JwtAuth.php:
<?php
declare(strict_types=1);
namespace appapimiddleware;
use FirebaseJWTJWT;
use FirebaseJWTKey;
use thinkfacadeConfig;
use thinkResponse;
class JwtAuth
{
public function handle($request, Closure $next)
{
// 排除不需要认证的路由
$excludeRoutes = ['user/login', 'user/register'];
if (in_array($request->pathinfo(), $excludeRoutes)) {
return $next($request);
}
$token = $request->header('Authorization');
if (!$token) {
return json(['code' => 401, 'msg' => 'Token缺失']);
}
try {
$config = Config::get('jwt');
$decoded = JWT::decode(
str_replace('Bearer ', '', $token),
new Key($config['key'], $config['alg'])
);
// 将用户信息存入请求对象
$request->user = $decoded->data;
// 验证Token是否在黑名单
if ($this->isBlacklisted($token)) {
return json(['code' => 401, 'msg' => 'Token已失效']);
}
return $next($request);
} catch (Exception $e) {
return json(['code' => 401, 'msg' => 'Token验证失败']);
}
}
private function isBlacklisted($token): bool
{
// 使用Redis检查Token黑名单
$redis = thinkfacadeCache::store('redis');
return $redis->has('jwt:blacklist:' . md5($token));
}
}
3.2 统一响应格式
创建基础控制器app/api/controller/BaseController.php:
<?php
namespace appapicontroller;
use thinkApp;
use thinkfacadeRequest;
abstract class BaseController
{
protected $app;
protected $request;
public function __construct(App $app)
{
$this->app = $app;
$this->request = $app->request;
}
protected function success($data = null, string $msg = '操作成功', int $code = 200)
{
$result = [
'code' => $code,
'msg' => $msg,
'data' => $data,
'timestamp' => time(),
'request_id' => $this->generateRequestId()
];
return json($result);
}
protected function error(string $msg = '操作失败', int $code = 400, $data = null)
{
return $this->success($data, $msg, $code);
}
private function generateRequestId(): string
{
return md5($this->request->ip() . microtime(true) . uniqid());
}
}
四、高性能商品模块实现
4.1 领域模型设计
创建商品领域模型app/common/model/Product.php:
<?php
namespace appcommonmodel;
use thinkModel;
use thinkmodelconcernSoftDelete;
class Product extends Model
{
use SoftDelete;
protected $table = 'products';
protected $pk = 'id';
// 自动时间戳
protected $autoWriteTimestamp = true;
// 字段类型转换
protected $type = [
'price' => 'float',
'stock' => 'integer',
'is_on_sale' => 'boolean',
'specs' => 'json',
'images' => 'json'
];
// 搜索器:商品名称
public function searchNameAttr($query, $value)
{
$query->whereLike('name', '%' . $value . '%');
}
// 搜索器:价格范围
public function searchPriceRangeAttr($query, $value)
{
if (isset($value['min'])) {
$query->where('price', '>=', $value['min']);
}
if (isset($value['max'])) {
$query->where('price', 'belongsTo(Category::class);
}
// 关联:商品SKU
public function skus()
{
return $this->hasMany(ProductSku::class);
}
}
4.2 服务层封装
创建商品服务类app/common/service/ProductService.php:
<?php
namespace appcommonservice;
use appcommonmodelProduct;
use thinkfacadeCache;
use thinkfacadeDb;
use thinkPaginator;
class ProductService
{
protected $cachePrefix = 'product:';
protected $cacheTtl = 3600; // 1小时
/**
* 获取商品列表(带缓存)
*/
public function getList(array $params, int $page = 1, int $size = 15): Paginator
{
$cacheKey = $this->cachePrefix . 'list:' . md5(json_encode($params) . $page . $size);
return Cache::remember($cacheKey, function() use ($params, $page, $size) {
$query = Product::with(['category', 'skus'])
->where('is_on_sale', true)
->order('sort', 'desc')
->order('id', 'desc');
// 动态搜索条件
foreach ($params as $field => $value) {
if (method_exists(Product::class, 'scope' . ucfirst($field))) {
$query->$field($value);
} elseif (!empty($value)) {
$query->where($field, $value);
}
}
return $query->paginate([
'page' => $page,
'list_rows' => $size,
'var_page' => 'page'
]);
}, $this->cacheTtl);
}
/**
* 获取商品详情(带缓存和防击穿)
*/
public function getDetail(int $id): ?array
{
$cacheKey = $this->cachePrefix . 'detail:' . $id;
$mutexKey = $cacheKey . ':mutex';
// 尝试从缓存获取
$data = Cache::get($cacheKey);
if ($data !== null) {
return $data;
}
// 使用互斥锁防止缓存击穿
if (!Cache::store('redis')->set($mutexKey, 1, ['nx', 'ex' => 10])) {
usleep(100000); // 等待100ms
return $this->getDetail($id);
}
try {
// 从数据库获取
$product = Product::with(['category', 'skus'])
->where('id', $id)
->where('is_on_sale', true)
->find();
if (!$product) {
Cache::set($cacheKey, [], 300); // 空数据缓存5分钟
return [];
}
$data = $product->toArray();
// 关联数据
$data['related_products'] = $this->getRelatedProducts($id, $product['category_id']);
// 写入缓存
Cache::set($cacheKey, $data, $this->cacheTtl);
return $data;
} finally {
Cache::store('redis')->delete($mutexKey);
}
}
/**
* 获取相关商品
*/
private function getRelatedProducts(int $excludeId, int $categoryId, int $limit = 6): array
{
return Product::where('category_id', $categoryId)
->where('id', '', $excludeId)
->where('is_on_sale', true)
->orderRaw('rand()')
->limit($limit)
->select()
->toArray();
}
/**
* 减少库存(使用事务和乐观锁)
*/
public function decreaseStock(int $productId, int $skuId, int $quantity): bool
{
return Db::transaction(function() use ($productId, $skuId, $quantity) {
// 更新商品总库存
$product = Product::where('id', $productId)
->where('stock', '>=', $quantity)
->lock(true)
->find();
if (!$product) {
throw new Exception('商品库存不足');
}
$product->stock -= $quantity;
$product->save();
// 更新SKU库存
if ($skuId > 0) {
$sku = ProductSku::where('id', $skuId)
->where('stock', '>=', $quantity)
->lock(true)
->find();
if (!$sku) {
throw new Exception('SKU库存不足');
}
$sku->stock -= $quantity;
$sku->save();
}
// 清除缓存
$this->clearCache($productId);
return true;
});
}
/**
* 清除商品缓存
*/
public function clearCache(int $productId): void
{
$keys = [
$this->cachePrefix . 'detail:' . $productId,
$this->cachePrefix . 'list:*' // 模糊删除列表缓存
];
foreach ($keys as $key) {
if (strpos($key, '*') !== false) {
$this->clearPatternCache($key);
} else {
Cache::delete($key);
}
}
}
/**
* 清除模式匹配的缓存
*/
private function clearPatternCache(string $pattern): void
{
$redis = Cache::store('redis')->handler();
$keys = $redis->keys($pattern);
if (!empty($keys)) {
$redis->del($keys);
}
}
}
五、路由与API版本控制
5.1 路由配置文件
route/app.php:
<?php
use thinkfacadeRoute;
// API版本分组
Route::group('api/:version', function() {
// 用户模块
Route::group('user', function() {
Route::post('login', 'user/login');
Route::post('register', 'user/register');
Route::get('profile', 'user/profile')->middleware('JwtAuth');
Route::put('update', 'user/update')->middleware('JwtAuth');
});
// 商品模块
Route::group('products', function() {
Route::get('/', 'product/index');
Route::get('/:id', 'product/read')
->pattern(['id' => 'd+']);
Route::get('/search', 'product/search');
Route::post('/:id/comments', 'product/addComment')
->middleware('JwtAuth');
});
// 订单模块
Route::group('orders', function() {
Route::post('/', 'order/create')->middleware('JwtAuth');
Route::get('/', 'order/index')->middleware('JwtAuth');
Route::get('/:id', 'order/read')
->pattern(['id' => 'd+'])
->middleware('JwtAuth');
Route::post('/:id/cancel', 'order/cancel')->middleware('JwtAuth');
});
})->prefix('api/')
->pattern(['version' => 'v[1-9]d*']) // 版本号匹配 v1, v2等
->allowCrossDomain();
5.2 版本控制控制器
app/api/controller/v1/Product.php:
<?php
namespace appapicontrollerv1;
use appapicontrollerBaseController;
use appcommonserviceProductService;
use thinkfacadeRequest;
class Product extends BaseController
{
protected $productService;
public function __construct(ProductService $productService)
{
parent::__construct(app());
$this->productService = $productService;
}
public function index()
{
$params = Request::only(['category_id', 'name', 'price_range', 'sort']);
$page = Request::param('page', 1);
$size = Request::param('size', 15);
$result = $this->productService->getList($params, $page, $size);
return $this->success([
'list' => $result->items(),
'pagination' => [
'total' => $result->total(),
'page' => $result->currentPage(),
'size' => $result->listRows(),
'pages' => $result->lastPage()
]
]);
}
public function read($id)
{
$data = $this->productService->getDetail($id);
if (empty($data)) {
return $this->error('商品不存在', 404);
}
return $this->success($data);
}
public function search()
{
$keyword = Request::param('keyword', '');
$sort = Request::param('sort', 'default');
if (empty($keyword)) {
return $this->error('搜索关键词不能为空');
}
// 使用Elasticsearch或数据库全文索引
$products = appcommonmodelProduct::withSearch(['name'], [
'name' => $keyword
])->paginate();
return $this->success([
'list' => $products->items(),
'total' => $products->total()
]);
}
}
六、性能优化与监控
6.1 数据库查询优化
// 使用查询构造器的最佳实践
$products = Db::table('products')
->alias('p')
->join('category c', 'p.category_id = c.id')
->field('p.id,p.name,p.price,c.name as category_name')
->where('p.is_on_sale', 1)
->where('p.stock', '>', 0)
->whereTime('p.create_time', '>=', '-30 days')
->order('p.sales', 'desc')
->limit(100)
->cache('hot_products', 300) // 缓存5分钟
->select();
// 使用索引提示
$products = Db::table('products')
->forceIndex('idx_category_sales')
->where('category_id', $categoryId)
->order('sales', 'desc')
->select();
6.2 缓存策略设计
// 多级缓存配置
return [
'default' => 'redis',
'stores' => [
'file' => [
'type' => 'File',
'path' => '../runtime/cache/',
],
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'persistent' => true, // 持久连接
'prefix' => 'tp6:api:',
'tag_prefix' => 'tag:',
],
'multi' => [
'type' => 'multi',
'stores' => ['redis', 'file'], // 多级缓存
]
]
];
6.3 日志与监控
// 自定义日志通道
return [
'default' => 'stack',
'channels' => [
'stack' => [
'type' => 'stack',
'channels' => ['daily', 'slack'],
],
'api' => [
'type' => 'daily',
'path' => '../runtime/log/api/',
'level' => 'info',
'max_files' => 30,
'json' => true, // JSON格式日志
],
'slow_query' => [
'type' => 'file',
'path' => '../runtime/log/slow_query/',
'level' => 'sql',
'max_files' => 90,
'threshold' => 1000, // 慢查询阈值1秒
]
]
];
// 记录API访问日志
class ApiLogMiddleware
{
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
$logData = [
'method' => $request->method(),
'url' => $request->url(),
'ip' => $request->ip(),
'params' => $request->param(),
'duration' => round((microtime(true) - $startTime) * 1000, 2),
'status' => $response->getCode(),
'user_id' => $request->user->id ?? 0,
'user_agent' => $request->header('user-agent'),
];
Log::channel('api')->info('API访问日志', $logData);
// 慢请求监控
if ($logData['duration'] > 1000) {
Log::channel('slow_query')->warning('慢请求警告', $logData);
}
return $response;
}
}
七、部署与安全
7.1 Nginx配置优化
server {
listen 80;
server_name api.example.com;
root /var/www/tp6-api-project/public;
index index.php;
# 静态文件缓存
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# PHP处理
location ~ .php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 超时设置
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
# 缓冲区优化
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
}
# API限流
location ~ ^/api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
# CORS配置
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
try_files $uri $uri/ /index.php?$query_string;
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
7.2 安全防护措施
// 中间件:XSS防护
class XssProtection
{
public function handle($request, Closure $next)
{
$input = $request->param();
array_walk_recursive($input, function(&$value) {
if (is_string($value)) {
$value = htmlspecialchars($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
});
$request->withParam($input);
return $next($request);
}
}
// 中间件:SQL注入防护
class SqlInjectionProtection
{
protected $dangerousPatterns = [
'/union.*select/i',
'/insert.*into/i',
'/delete.*from/i',
'/update.*set/i',
'/drop.*table/i',
'/--|/*|*//',
'/sleep(/i',
'/benchmark(/i'
];
public function handle($request, Closure $next)
{
$params = array_merge(
$request->param(),
$request->route(),
$request->header()
);
foreach ($params as $key => $value) {
if (is_string($value)) {
foreach ($this->dangerousPatterns as $pattern) {
if (preg_match($pattern, $value)) {
Log::warning('SQL注入尝试', [
'ip' => $request->ip(),
'key' => $key,
'value' => substr($value, 0, 100)
]);
return json([
'code' => 403,
'msg' => '非法请求参数'
]);
}
}
}
}
return $next($request);
}
}
八、测试与文档
8.1 单元测试示例
<?php
namespace testsapi;
use PHPUnitFrameworkTestCase;
use thinkApp;
use thinkfacadeDb;
class ProductTest extends TestCase
{
protected $app;
protected $http;
protected function setUp(): void
{
$this->app = new App();
$this->app->initialize();
// 测试数据库配置
Db::setConfig([
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'tp6_api_test',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
]
]
]);
}
public function testProductList()
{
// 模拟HTTP请求
$response = $this->app->http
->name('product')
->get('/api/v1/products');
$this->assertEquals(200, $response->getCode());
$this->assertArrayHasKey('data', $response->getData());
$this->assertArrayHasKey('pagination', $response->getData());
}
public function testProductDetail()
{
// 创建测试数据
$productId = Db::name('products')
->insertGetId([
'name' => '测试商品',
'price' => 99.99,
'stock' => 100,
'is_on_sale' => 1
]);
$response = $this->app->http
->name('product')
->get('/api/v1/products/' . $productId);
$this->assertEquals(200, $response->getCode());
$this->assertEquals('测试商品', $response->getData()['data']['name']);
// 清理测试数据
Db::name('products')->delete($productId);
}
public function testProductSearch()
{
$response = $this->app->http
->name('product')
->get('/api/v1/products/search', [
'keyword' => '手机'
]);
$this->assertEquals(200, $response->getCode());
}
}
8.2 API文档生成
/**
* @title 商品模块
* @desc 商品相关接口
* @group 商品
*/
class Product extends BaseController
{
/**
* @title 获取商品列表
* @desc 分页获取商品列表,支持多种筛选条件
* @url /api/v1/products
* @method GET
* @param int page 页码 可选 1
* @param int size 每页数量 可选 15
* @param int category_id 分类ID 可选
* @param string name 商品名称 可选
* @param array price_range 价格范围 可选 {"min":0,"max":1000}
* @return array list 商品列表
* @return object pagination 分页信息
* @return int pagination.total 总记录数
* @return int pagination.page 当前页码
* @return int pagination.size 每页数量
* @return int pagination.pages 总页数
*/
public function index()
{
// 实现代码...
}
/**
* @title 获取商品详情
* @desc 根据ID获取商品详细信息
* @url /api/v1/products/:id
* @method GET
* @param int id 商品ID 必填
* @routeParam id integer required 商品ID
* @return object product 商品信息
* @return int product.id 商品ID
* @return string product.name 商品名称
* @return float product.price 商品价格
* @return int product.stock 库存数量
* @return array product.images 商品图片
* @return array product.specs 商品规格
* @return array related_products 相关商品
*/
public function read($id)
{
// 实现代码...
}
}
九、总结与最佳实践
9.1 架构设计要点
- 分层架构:严格遵循控制器-服务-模型的分层原则,保持代码清晰
- 依赖注入:充分利用ThinkPHP 6.0的依赖注入容器,提高可测试性
- 多应用模式:合理划分应用边界,实现代码隔离和独立部署
- 缓存策略:采用多级缓存,合理设置缓存时间和失效机制
- 异常处理:统一异常处理机制,提供友好的错误信息
9.2 性能优化建议
- 使用OPcache加速PHP代码执行
- 合理配置数据库连接池参数
- 对频繁访问的数据进行缓存
- 使用异步队列处理耗时任务
- 启用HTTP/2和Gzip压缩
- 定期清理无用日志和缓存文件
9.3 安全注意事项
- 对所有用户输入进行验证和过滤
- 使用参数化查询防止SQL注入
- 实施API限流和防刷机制
- 定期更新依赖包的安全补丁
- 使用HTTPS加密数据传输
- 记录安全日志并设置告警

