发布日期:2024年1月20日 | 作者:PHP架构师
一、企业级CMS架构设计
1.1 分层架构设计
app/
├── admin/ # 后台管理模块
│ ├── controller/ # 控制器层
│ ├── service/ # 业务逻辑层
│ └── validate/ # 数据验证层
├── api/ # API接口模块
│ ├── v1/ # 版本控制
│ └── middleware/ # API中间件
├── common/ # 公共模块
│ ├── library/ # 自定义类库
│ ├── model/ # 数据模型层
│ └── trait/ # 特性封装
└── home/ # 前端展示模块
1.2 数据库设计规范
// 用户表设计示例
class User extends Model
{
// 设置数据表名
protected $table = 'cms_users';
// 自动时间戳
protected $autoWriteTimestamp = true;
// 字段类型转换
protected $type = [
'status' => 'integer',
'login_time' => 'datetime',
];
// 可批量赋值的字段
protected $fillable = ['username', 'email', 'nickname'];
// 定义关联关系
public function articles()
{
return $this->hasMany(Article::class, 'user_id');
}
// 搜索器
public function searchUsernameAttr($query, $value)
{
return $value ? $query->where('username', 'like', "%{$value}%") : '';
}
}
二、核心业务模块实现
2.1 内容管理模块
namespace appadminservice;
use appcommonmodelArticle;
use thinkfacadeDb;
class ArticleService
{
/**
* 创建文章
*/
public function createArticle($data)
{
Db::startTrans();
try {
// 数据验证
$this->validateArticleData($data);
// 处理内容标签
$tags = $this->processTags($data['tags'] ?? '');
// 创建文章记录
$article = Article::create([
'title' => $data['title'],
'content' => $data['content'],
'category_id' => $data['category_id'],
'user_id' => $data['user_id'],
'status' => $data['status'] ?? 1,
'tags' => $tags
]);
// 更新分类文章计数
$this->updateCategoryCount($data['category_id']);
Db::commit();
return $article;
} catch (Exception $e) {
Db::rollback();
throw new Exception('文章创建失败: ' . $e->getMessage());
}
}
/**
* 获取文章列表(带分页和搜索)
*/
public function getArticleList($params, $pageSize = 15)
{
$query = Article::with(['category', 'user'])
->where('status', '>', 0);
// 关键词搜索
if (!empty($params['keyword'])) {
$query->whereLike('title|content', "%{$params['keyword']}%");
}
// 分类筛选
if (!empty($params['category_id'])) {
$query->where('category_id', $params['category_id']);
}
// 状态筛选
if (isset($params['status'])) {
$query->where('status', $params['status']);
}
return $query->order('create_time', 'desc')
->paginate([
'list_rows' => $pageSize,
'query' => $params
]);
}
}
2.2 权限管理系统
namespace appadminmiddleware;
use thinkfacadeSession;
use appcommonmodelAuthRule;
class AuthCheck
{
public function handle($request, Closure $next)
{
// 排除登录页面
if (!$this->needAuthCheck($request)) {
return $next($request);
}
// 检查用户登录状态
if (!$this->checkLogin()) {
return redirect('/admin/login');
}
// 权限验证
if (!$this->checkAuth($request)) {
return json(['code' => 403, 'msg' => '权限不足']);
}
return $next($request);
}
/**
* 权限验证逻辑
*/
private function checkAuth($request)
{
$user = Session::get('admin_user');
// 超级管理员拥有所有权限
if ($user['is_super'] == 1) {
return true;
}
$currentUrl = strtolower($request->controller() . '/' . $request->action());
$authRules = AuthRule::getUserRules($user['id']);
return in_array($currentUrl, $authRules);
}
}
// 权限规则模型
class AuthRule extends Model
{
/**
* 获取用户权限规则
*/
public static function getUserRules($userId)
{
$cacheKey = "user_auth_rules_{$userId}";
$rules = cache($cacheKey);
if (!$rules) {
$rules = self::where('id', 'IN', function($query) use ($userId) {
$query->table('cms_role_rule')
->where('role_id', 'IN', function($q) use ($userId) {
$q->table('cms_user_role')
->where('user_id', $userId)
->field('role_id');
})
->field('rule_id');
})->column('url');
cache($cacheKey, $rules, 3600);
}
return $rules;
}
}
三、高级特性应用
3.1 多语言支持实现
// config/lang.php
return [
'default_lang' => 'zh-cn',
'allow_lang_list' => ['zh-cn', 'en-us'],
'auto_detect' => true,
];
// 语言包定义 resources/lang/zh-cn/admin.php
return [
'article' => [
'title_required' => '文章标题不能为空',
'content_required' => '文章内容不能为空',
'create_success' => '文章创建成功',
'update_success' => '文章更新成功',
],
];
// 控制器中使用多语言
class ArticleController
{
public function create()
{
try {
// 业务逻辑...
return json([
'code' => 200,
'msg' => lang('admin.article.create_success')
]);
} catch (Exception $e) {
return json([
'code' => 500,
'msg' => $e->getMessage()
]);
}
}
}
3.2 事件系统应用
// 定义事件类
namespace appadminevent;
class ArticleCreated
{
public $article;
public function __construct($article)
{
$this->article = $article;
}
}
// 事件监听器
namespace appadminlistener;
use appadmineventArticleCreated;
use thinkfacadeLog;
class ArticleListener
{
public function onArticleCreated(ArticleCreated $event)
{
// 记录操作日志
Log::info("文章创建成功: {$event->article->title}");
// 发送通知
$this->sendNotification($event->article);
// 更新缓存
$this->updateCache($event->article);
}
private function sendNotification($article)
{
// 发送邮件或消息通知
}
}
// 事件注册 event.php
return [
'bind' => [
'ArticleCreated' => 'appadmineventArticleCreated',
],
'listen' => [
'ArticleCreated' => ['appadminlistenerArticleListener'],
],
];
// 触发事件
event('ArticleCreated', new ArticleCreated($article));
3.3 队列系统应用
// 队列任务类
namespace appadminjob;
use thinkqueueJob;
use appcommonmodelArticle;
class ArticlePublish
{
public function fire(Job $job, $data)
{
try {
// 处理文章发布逻辑
$article = Article::find($data['article_id']);
if ($article) {
$article->status = 1;
$article->publish_time = time();
$article->save();
// 任务执行成功
$job->delete();
return true;
}
} catch (Exception $e) {
// 任务执行失败,记录日志
thinkfacadeLog::error("文章发布队列任务失败: " . $e->getMessage());
// 重试3次后放弃
if ($job->attempts() > 3) {
$job->delete();
}
}
return false;
}
}
// 控制器中推送队列任务
class ArticleController
{
public function publish($id)
{
// 推送发布任务到队列
thinkfacadeQueue::push('appadminjobArticlePublish', [
'article_id' => $id
]);
return json(['code' => 200, 'msg' => '文章已加入发布队列']);
}
}
四、性能优化策略
4.1 数据库优化
// 查询优化示例
class ArticleService
{
/**
* 优化后的文章列表查询
*/
public function getOptimizedArticleList($params)
{
return Article::field('id,title,create_time,category_id,user_id')
->with([
'category' => function($query) {
$query->field('id,name');
},
'user' => function($query) {
$query->field('id,nickname');
}
])
->where('status', 1)
->where('create_time', '>', strtotime('-30 days'))
->cache('recent_articles', 300) // 缓存5分钟
->order('id', 'desc')
->select();
}
/**
* 使用索引提示
*/
public function getArticlesByCategory($categoryId)
{
return Db::table('cms_articles')
->forceIndex('idx_category_status') // 强制使用索引
->where('category_id', $categoryId)
->where('status', 1)
->select();
}
}
// 数据库配置优化
// config/database.php
return [
// 开启断线重连
'break_reconnect' => true,
// 查询缓存
'fields_cache' => true,
// 长连接
'params' => [
PDO::ATTR_PERSISTENT => true,
],
];
4.2 缓存策略设计
namespace appcommonservice;
use thinkfacadeCache;
class CacheService
{
const CACHE_PREFIX = 'cms:';
const TTL_ONE_HOUR = 3600;
const TTL_ONE_DAY = 86400;
/**
* 文章缓存管理
*/
public static function cacheArticle($article)
{
$key = self::CACHE_PREFIX . "article:{$article->id}";
Cache::set($key, $article, self::TTL_ONE_DAY);
}
/**
* 获取文章缓存
*/
public static function getArticle($id)
{
$key = self::CACHE_PREFIX . "article:{$id}";
$article = Cache::get($key);
if (!$article) {
$article = appcommonmodelArticle::find($id);
if ($article) {
self::cacheArticle($article);
}
}
return $article;
}
/**
* 清除相关缓存
*/
public static function clearArticleCache($articleId)
{
$keys = [
self::CACHE_PREFIX . "article:{$articleId}",
self::CACHE_PREFIX . "recent_articles",
self::CACHE_PREFIX . "hot_articles",
];
foreach ($keys as $key) {
Cache::delete($key);
}
}
}
// Redis缓存配置
// config/cache.php
return [
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'persistent' => true, // 持久连接
],
],
];
五、安全设计方案
5.1 输入验证与过滤
namespace appadminvalidate;
use thinkValidate;
class ArticleValidate extends Validate
{
protected $rule = [
'title' => 'require|max:100|chsAlphaNum',
'content' => 'require|min:10',
'category_id' => 'require|integer|gt:0',
'tags' => 'max:200',
];
protected $message = [
'title.require' => '文章标题不能为空',
'title.max' => '标题长度不能超过100个字符',
'title.chsAlphaNum' => '标题只能包含中文、字母和数字',
'content.require' => '文章内容不能为空',
'content.min' => '文章内容至少10个字符',
];
/**
* 自定义验证规则 - XSS过滤
*/
protected function xssFilter($value)
{
$dangerous = [
'/script/i', '/javascript/i', '/onclick/i',
'/onload/i', '/iframe/i', '/object/i'
];
foreach ($dangerous as $pattern) {
if (preg_match($pattern, $value)) {
return false;
}
}
return true;
}
}
// 控制器中使用验证
class ArticleController
{
public function create()
{
$data = request()->post();
$validate = new ArticleValidate();
if (!$validate->check($data)) {
return json(['code' => 400, 'msg' => $validate->getError()]);
}
// 进一步过滤HTML内容
$data['content'] = $this->cleanHtml($data['content']);
// 处理业务逻辑...
}
private function cleanHtml($html)
{
// 使用HTML Purifier或自定义过滤规则
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
return $purifier->purify($html);
}
}
5.2 API安全防护
namespace appapimiddleware;
use thinkfacadeRequest;
use thinkfacadeCache;
class ApiAuth
{
public function handle($request, Closure $next)
{
// 验证API签名
if (!$this->checkSign($request)) {
return json(['code' => 401, 'msg' => '签名验证失败']);
}
// 防止重放攻击
if (!$this->checkReplay($request)) {
return json(['code' => 401, 'msg' => '请求已过期']);
}
// 频率限制
if (!$this->checkRateLimit($request)) {
return json(['code' => 429, 'msg' => '请求过于频繁']);
}
return $next($request);
}
private function checkSign($request)
{
$sign = $request->header('Sign');
$timestamp = $request->header('Timestamp');
$nonce = $request->header('Nonce');
// 获取API密钥
$appKey = $request->header('App-Key');
$appSecret = $this->getAppSecret($appKey);
// 生成签名
$params = $request->param();
ksort($params);
$signString = $appSecret . $timestamp . $nonce . json_encode($params);
$expectedSign = md5($signString);
return $sign === $expectedSign;
}
private function checkReplay($request)
{
$timestamp = $request->header('Timestamp');
$nonce = $request->header('Nonce');
// 检查时间戳(5分钟内有效)
if (abs(time() - $timestamp) > 300) {
return false;
}
// 检查nonce是否已使用
$nonceKey = 'api_nonce:' . $nonce;
if (Cache::has($nonceKey)) {
return false;
}
Cache::set($nonceKey, 1, 300); // 缓存5分钟
return true;
}
private function checkRateLimit($request)
{
$clientIp = $request->ip();
$key = 'api_rate_limit:' . $clientIp;
$requests = Cache::get($key, 0);
if ($requests >= 100) { // 每分钟限制100次请求
return false;
}
Cache::inc($key, 60); // 1分钟过期
return true;
}
}