这个基于Java SpringBoot+Vue3+MyBatis的全栈项目,是一个典型的现代IT技术交流社区实现方案。我去年在团队内部知识管理系统开发中就采用了几乎相同的技术组合,实测这套架构在开发效率和运行性能上都有不错的表现。
平台核心功能模块包括用户认证、内容发布、互动评论、标签分类和技术资源库等。前后端完全分离的设计让前端Vue3组件可以独立开发和部署,后端SpringBoot提供稳定的RESTful API服务,MyBatis作为ORM层与MySQL数据库交互。这种架构选择既保证了系统的可维护性,又满足了高并发场景下的性能需求。
提示:选择Vue3而非Vue2主要考虑其Composition API带来的更好TypeScript支持,这对中大型前端项目尤为重要
后端采用经典的MVC分层架构,但有几个特别的设计决策值得注意:
包结构规划:
code复制com.itechshare
├── config # 安全配置和第三方集成
├── controller # 带@RestController的API端点
├── service # 业务逻辑层
│ ├── impl # 接口实现类
├── dao # MyBatis Mapper接口
├── entity # 数据库实体
├── dto # 数据传输对象
└── util # 工具类
关键依赖选择:
性能优化配置示例(application.yml):
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
jackson:
serialization:
write-dates-as-timestamps: false
前端项目采用Vite构建工具创建,比传统Webpack启动速度快10倍以上。目录结构体现功能模块化思想:
code复制src/
├── api/ # 封装所有后端接口调用
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── editor/ # Markdown编辑器组件
├── composables/ # Vue3组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── styles/ # 全局样式
└── views/ # 页面组件
├── article/ # 文章相关页面
典型组件代码示例(ArticleCard.vue):
vue复制<script setup>
defineProps({
id: Number,
title: String,
summary: String,
viewCount: Number
})
</script>
<template>
<div class="card">
<h3>{{ title }}</h3>
<p class="text-gray-600">{{ summary }}</p>
<div class="stats">
<EyeIcon /> {{ viewCount }}
</div>
</div>
</template>
平台主要包含6张核心表,关系模型如下:
sql复制CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) UNIQUE NOT NULL,
`password` VARCHAR(100) NOT NULL,
`avatar` VARCHAR(255),
`bio` TEXT,
`status` TINYINT DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `article` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`title` VARCHAR(100) NOT NULL,
`content` LONGTEXT NOT NULL,
`view_count` INT DEFAULT 0,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
);
索引策略:
CREATE INDEX idx_user_article ON article(user_id, create_time)分页查询优化(避免深分页):
java复制@Select("SELECT * FROM article WHERE id < #{lastId} ORDER BY id DESC LIMIT #{size}")
List<Article> selectArticlesBeforeId(@Param("lastId") Long lastId, @Param("size") int size);
MyBatis二级缓存配置:
xml复制<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
前端采用TOAST UI Editor,支持GFM标准:
javascript复制import Editor from '@toast-ui/editor'
import '@toast-ui/editor/dist/toastui-editor.css'
const editor = new Editor({
el: document.querySelector('#editor'),
height: '500px',
initialEditType: 'markdown',
previewStyle: 'vertical'
})
后端处理XSS防护:
java复制public String sanitizeMarkdown(String content) {
PolicyFactory policy = new HtmlPolicyBuilder()
.allowElements("h1","h2","h3","p","pre","code","blockquote")
.allowAttributes("class").onElements("code")
.toFactory();
return policy.sanitize(content);
}
采用阿里云OSS存储方案,前端实现断点续传:
java复制@PostMapping("/upload")
public Result<String> upload(@RequestParam MultipartFile file) {
String fileName = UUID.randomUUID() + "." + FileUtil.extName(file.getOriginalFilename());
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, file.getInputStream());
return Result.success(domain + "/" + fileName);
}
前端上传组件关键逻辑:
javascript复制const uploadFile = async (file) => {
const chunkSize = 5 * 1024 * 1024 // 5MB分片
const chunks = Math.ceil(file.size / chunkSize)
for (let i = 0; i < chunks; i++) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize)
await axios.post('/upload', formData, {
headers: { 'Content-Range': `bytes ${i*chunkSize}-${Math.min((i+1)*chunkSize, file.size)-1}/${file.size}` }
})
}
}
Docker Compose编排方案:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: itech_share
volumes:
- ./mysql/data:/var/lib/mysql
ports:
- "3306:3306"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
Nginx配置示例(前端静态资源+API代理):
nginx复制server {
listen 80;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
SpringBoot Actuator集成:
properties复制management.endpoints.web.exposure.include=health,metrics,info
management.metrics.tags.application=itech-share-backend
前端Sentry错误监控:
javascript复制import * as Sentry from '@sentry/vue'
Sentry.init({
dsn: 'your-dsn',
integrations: [new BrowserTracing()],
tracesSampleRate: 0.2
})
开发环境配置(SpringBoot):
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.maxAge(3600);
}
}
生产环境推荐Nginx配置:
nginx复制add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,Content-Type';
典型错误场景:
java复制// 查询文章
Article article = articleMapper.selectById(1);
// 访问作者信息(session已关闭)
System.out.println(article.getUser().getUsername());
解决方案:
xml复制<resultMap id="articleWithUser" type="ArticleDTO">
<association property="user" column="user_id"
select="com.itechshare.dao.UserMapper.selectById"/>
</resultMap>
JWT实现流程:
java复制String token = Jwts.builder()
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 3600_000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
javascript复制// 使用httpOnly的Cookie
document.cookie = `token=${token}; Path=/; Secure; SameSite=Strict`
java复制http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
基于Redis的限流方案:
java复制@RateLimiter(value = 10, key = "#ip") // 自定义注解
@PostMapping("/comment")
public Result addComment(@RequestBody CommentDTO dto,
@RequestHeader String ip) {
// 业务逻辑
}
注解实现核心逻辑:
java复制Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, time, timeUnit);
}
if (count > limit) {
throw new RateLimitException();
}
后端扩展:
前端优化:
DevOps改进:
知识付费模块:
在线协作功能:
智能推荐系统:
这套源码最值得借鉴的是其清晰的架构边界设计,我在实际开发中发现,保持前端Vuex(或Pinia)状态与后端DTO模型的独立演化非常重要。建议在复杂表单处理时,前端使用Vuelidate进行验证,而不是依赖后端返回的错误信息,这样可以显著提升用户体验。