CMS内容管理系统的设计与实现:架构设计
一、整体架构方案
(一)架构方案选择(根据项目规模)
1. 中小型项目推荐方案(团队<10人)
- 技术要点:
- 使用 模块化单体架构(非微服务)
- 通过 package划分 隔离管理/门户API
- 利用 Profile 实现环境隔离
- 无需Spring Cloud,保持简单
2. 大型项目方案(团队>20人)
- 技术要点:
- 采用 Spring Cloud Alibaba
- 通过 Nacos 实现服务发现
- 使用 Sentinel 做流量控制
- 需要独立 DBA团队 支持
3.部署方案对比
方案 | 优点 | 缺点 | 适用阶段 |
---|---|---|---|
单体部署 | 运维简单,成本低 | 扩展性受限 | 初期(<5万UV) |
模块分离部署 | 部分服务可独立伸缩 | 需要基础监控能力 | 中期(5-50万UV) |
全微服务 | 弹性扩展,故障隔离 | 运维复杂,成本高 | 后期(>50万UV) |
(二)演进路线实施建议
# 初期:单体模块化
src/
└── main└── java└── com└── company└── cms├── content├── comment└── statistics# 中期:子模块拆分
cms-system/
├── cms-content
├── cms-comment
└── cms-statistics# 后期:微服务化
cms-admin-service/
cms-portal-service/
cms-comment-service/
-
初期采用模块化单体:
- 使用 Maven多模块 而非微服务
- 通过 @Profile(“admin”) 隔离配置
- 使用 Flyway 管理多环境数据库
-
渐进式演进路径:
-
监控准备:
- 即使单体架构也要配置 Prometheus+Granfana
- 日志系统使用 ELK Stack
- 关键业务指标埋点
可根据实际用户量增长逐步升级架构,避免早期过度设计。
二、模块化分层架构设计
(一)总体划分架构
1.后端目录划分
采用 「业务垂直切分 + 客户端横向隔离 + 技术纵向分层」 的三维结构,具体实现如下:
src/main/java/com/company/cms
├── content # 内容业务模块
│ ├── admin # 管理端实现
│ │ ├── controller
│ │ ├── service
│ │ └── mapper
│ ├── portal # 门户端实现
│ │ ├── controller
│ │ ├── service
│ │ └── mapper
│ └── domain # 公共领域对象
│ ├── entity
│ ├── dto
│ └── vo
├── comment # 评论业务模块
│ ├── admin
│ ├── portal
│ └── domain
├── statistics # 统计业务模块
│ ├── admin
│ ├── portal
│ └── domain
└── common # CMS公共模块├── config # 专有配置├── interceptor # 拦截器└── utils # 工具类
2. 业务垂直切分
-
隔离性:各业务模块可独立开发部署
-
可维护性:修改影响范围明确
-
复用性:公共domain对象跨客户端复用
3. 客户端横向隔离
// 内容模块下的权限差异示例
// 管理端Controller
@PreAuthorize("hasRole('CONTENT_ADMIN')")
@PostMapping("/admin/content")
public AjaxResult createContent(...) {// 需要审核流程
}// 门户端Controller
@RateLimit(100) // 限流注解
@GetMapping("/portal/content")
public ContentVO getContent(...) {// 缓存优化实现
}
4. 技术纵向分层
# 典型业务模块内部结构
content/
├── admin
│ ├── controller # API入口
│ ├── service # 业务逻辑
│ └── mapper # 数据持久
├── portal
│ ├── controller
│ ├── service
│ └── mapper
└── domain # 核心领域模型├── entity # JPA实体├── dto # 传输对象└── vo # 视图对象
(二)前后端代码统筹
1.代码仓库管理策略
若是属于少量人开发项目,则推荐单一代码仓库 + 多模块结构的方案进行开发。
my-cms-project/
├── backend/ # Spring Boot后端
│ ├── src/
│ └── pom.xml
├── admin-frontend/ # 管理端Vue3
│ ├── src/
│ └── package.json
└── portal-frontend/ # 门户端Nuxt3├── pages/└── package.json
若是多人开发项目,应该分为3个代码仓库,由不同的技术人员开发提交对应的代码。
2.配置示例(.gitignore):
# 通用忽略
*.log
*.iml
.idea/# 后端忽略
backend/target/# 前端忽略
admin-frontend/node_modules/
admin-frontend/dist/
portal-frontend/node_modules/
portal-frontend/.nuxt/
三、后端实施建议
(一)领域对象
1.领域对象复用
// 公共实体定义
@Entity
@Table(name = "cms_content")
public class Content {@Id@GeneratedValueprivate Long id;// 公共字段private String title;private String content;// 管理端特有字段(审计用)@Column(name = "audit_status")@AdminOnly // 自定义注解private AuditStatus auditStatus;// 门户端特有字段(展示用)@Column(name = "view_count")@PortalOnlyprivate Integer viewCount;
}
(二)API控制器
1.API路径设计规范
管理端和门户端的api接口需要隔离,用/api/admin和/api/portal区分好,还是用/admin/api和/portal/api区分好?
推荐:/api/admin
和 /api/portal
双前缀模式
管理端: /api/admin/v1/content
门户端: /api/portal/v1/content
优势分析:
- 路由清晰:/api 明确标识API入口,/admin|portal区分客户端
- 网关友好:Nginx可统一配置/api路由规则
- 安全隔离:方便在网关层设置不同安全策略
- 版本控制:v1支持平滑升级到v2
2.控制器隔离
// 管理端文章控制器
@RestController
@RequestMapping("/api/admin/v1/articles")
public class AdminArticleController {@PreAuthorize("hasRole('ADMIN')")@PostMappingpublic AjaxResult createArticle(@Valid @RequestBody ArticleCreateDTO dto) {// 管理端专用创建逻辑}
}// 门户端文章控制器
@RestController
@RequestMapping("/api/portal/v1/articles")
public class PortalArticleController {@GetMapping("/{id}")public ArticleVO getArticle(@PathVariable Long id) {// 门户端专用查询逻辑}
}
3.API文档管理
// 使用Swagger分组
@Bean
public Docket adminApi() {return new Docket(DocumentationType.OAS_30).groupName("管理端API").select().apis(RequestHandlerSelectors.basePackage("com.cms.controller.admin"))
}
(三)服务层
1.服务层复用策略
// 通用服务接口
public interface ArticleBaseService {Article getById(Long id);
}// 管理端服务实现
@Service
public class AdminArticleService implements ArticleBaseService {@Overridepublic Article getById(Long id) {// 管理端专用逻辑(包含审计字段)}public void auditArticle(Long id) {// 管理端特有方法}
}// 门户端服务实现
@Service
public class PortalArticleService implements ArticleBaseService {@Overridepublic Article getById(Long id) {// 门户端专用逻辑(缓存优化)}
}
2. 服务层差异处理
// 管理端服务实现
@Service
public class AdminContentService {@AdminAuditRequired // 自定义审核注解public void publishContent(Long id) {// 包含审核流程}
}// 门户端服务实现
@Service
public class PortalContentService {@Cacheable("content") // 缓存优化public ContentVO getContent(Long id) {// 访问统计逻辑}
}
(四)安全配置
1.安全配置分离
// 管理端安全配置类
@Configuration
@EnableWebSecurity
@Order(1) // 高优先级
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasRole("ADMIN").and().formLogin().disable();}
}// 门户端安全配置类
@Configuration
@Order(2) // 低优先级
public class PortalSecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.antMatcher("/portal/**").authorizeRequests().antMatchers(HttpMethod.GET).permitAll().anyRequest().authenticated().and().oauth2ResourceServer().jwt();}
}
# application-admin.yml(管理端配置)
security:oauth2:resource:id: admin-apiuser-info-uri: http://auth-server/oauth2/user# application-portal.yml(门户端配置)
security:oauth2:resource:id: portal-apiuser-info-uri: http://auth-server/oauth2/openid
# 权限配置示例(admin/portal分离)
security:admin:roles: [CONTENT_ADMIN, COMMENT_ADMIN]access: DENY_AFTER_17:00-08:00 # 管理端时间限制portal:rate-limit: 1000/分钟open-apis: [/api/portal/content/*]
(五)异常处理
1.异常处理方案
@RestControllerAdvice(basePackages = {"com.company.cms.content.admin","com.company.cms.content.portal"})
public class ContentExceptionHandler {@ExceptionHandler(ContentNotFoundException.class)public ResponseEntity<ErrorResult> handleNotFound(ContentNotFoundException ex) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorResult("CONTENT_NOT_FOUND", ex.getMessage()));}
}
2. 错误码规范示例
模块 | 错误范围 | 示例 |
---|---|---|
内容模块 | 1000-1999 | 1001=内容不存在 |
评论模块 | 2000-2999 | 2003=评论包含敏感词 |
统计模块 | 3000-3999 | 3005=数据导出失败 |
(六)配置管理
1.配置管理策略
多环境配置示例
config/
├── application.yml # 公共配置
├── application-dev.yml # 开发环境
├── application-prod.yml # 生产环境
├── content-admin.yml # 内容管理端特有配置
└── content-portal.yml # 内容门户端特有配置
# application.yml(公共配置)
spring:datasource:url: jdbc:mysql://localhost:3306/cmsusername: rootpassword: 123456---
# application-admin.yml(管理端特有配置)
management:endpoints:web:exposure:include: health,info,metricsendpoint:health:show-details: always---
# application-portal.yml(门户端特有配置)
spring:redis:host: portal-redisport: 6379
动态配置示例
@Configuration
@ConfigurationProperties(prefix = "cms.content")
public class ContentConfig {// 管理端特有配置private AdminConfig admin;// 门户端特有配置private PortalConfig portal;@Datapublic static class AdminConfig {private int auditTimeout = 24; // 审核超时时间(小时)}@Datapublic static class PortalConfig {private int cacheTTL = 3600; // 缓存时间(秒)}
}
3. 数据库隔离策略
-- 管理表(admin_开头)
CREATE TABLE admin_operation_log (id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,action VARCHAR(50) NOT NULL
);-- 门户表(portal_开头)
CREATE TABLE portal_comment (id BIGINT PRIMARY KEY,article_id BIGINT NOT NULL,content TEXT
);
四、前端技术确认
端类型 | 技术栈 | 适用场景 | 示例代码 |
---|---|---|---|
管理后台 | Vue3 + Ruoyi-UI | 需要快速开发CRUD界面 | 使用若依的src/views/system 结构 |
门户网站 | Nuxt3 + SSR | 需要SEO优化和首屏性能 | pages/article/[id].vue 动态路由 |
(一)前端API调用对比
管理端调用示例(Vue3):
// admin-frontend/src/api/content.js
import request from '@/utils/request'export function getContentList(params) {return request({url: '/api/admin/v1/content',method: 'get',params})
}
门户端调用示例(Nuxt3):
// portal-frontend/composables/useContent.js
export default () => {const getContent = async (id) => {return $fetch(`/api/portal/v1/content/${id}`, {method: 'GET'})}return { getContent }
}
(二)开发环境代理配置
管理端Vite配置(admin-frontend/vite.config.js):
export default defineConfig({server: {proxy: {'/api/admin': {target: 'http://localhost:8080', // Spring Boot地址changeOrigin: true,rewrite: path => path.replace(/^\/api\/admin/, '')}}}
})
门户端Nuxt配置(portal-frontend/nuxt.config.js):
export default defineNuxtConfig({devServer: {proxy: {'/api/portal': 'http://localhost:8080'}}
})
前端环境变量:
# admin-frontend/.env.development
VITE_API_BASE=/api/admin# portal-frontend/.env.production
NUXT_PUBLIC_API_BASE=/api/portal
五、其它
1.Nginx统一配置:
server {listen 80;# 管理前端location /admin {alias /var/www/admin-frontend/dist;try_files $uri $uri/ /admin/index.html;}# 门户前端location / {alias /var/www/portal-frontend/dist;try_files $uri $uri/ /index.html;}# API路由location /api {proxy_pass http://backend-server:8080;proxy_set_header Host $host;# 管理API超时设置location ~ ^/api/admin {proxy_read_timeout 300s;}# 门户API缓存配置location ~ ^/api/portal.*\.(js|css|png)$ {expires 30d;}}
}