1. 相亲网站系统架构解析
这套基于Java SpringBoot+Vue3+MyBatis的相亲网站系统采用了经典的前后端分离架构。前端使用Vue3构建用户界面,后端采用SpringBoot提供RESTful API服务,MyBatis作为ORM框架与MySQL数据库交互。这种架构组合在当前企业级应用中非常普遍,既能保证开发效率,又能满足高性能需求。
提示:选择SpringBoot 2.7.x + Vue3 3.2.x + MyBatis 3.5.x的组合,这是目前最稳定的版本搭配,避免了最新版本可能存在的兼容性问题。
系统主要包含以下核心模块:
- 用户认证模块(JWT实现)
- 个人信息管理模块
- 匹配推荐算法模块
- 即时通讯模块(WebSocket)
- 支付与会员模块
- 后台管理模块
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈深度解析
2.1 SpringBoot后端设计
后端采用分层架构设计:
code复制controller(表现层)
service(业务逻辑层)
mapper(数据访问层)
entity(实体层)
config(配置层)
util(工具层)
数据库连接池配置示例(application.yml):
yaml复制spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dating_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
2.2 Vue3前端架构
前端采用Vue3组合式API开发,项目结构如下:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── styles/ # 全局样式
└── views/ # 页面组件
典型页面数据请求示例:
javascript复制import { ref } from 'vue'
import { getUserProfile } from '@/api/user'
const profile = ref(null)
const loading = ref(false)
const fetchProfile = async () => {
loading.value = true
try {
const res = await getUserProfile()
profile.value = res.data
} finally {
loading.value = false
}
}
2.3 MyBatis优化实践
MyBatis配置建议:
xml复制<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
动态SQL示例:
xml复制<select id="findUsersByCondition" resultType="User">
SELECT * FROM users
<where>
<if test="gender != null">
AND gender = #{gender}
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="maxAge != null">
AND age <= #{maxAge}
</if>
</where>
ORDER BY create_time DESC
</select>
3. 核心功能实现
3.1 用户认证系统
JWT认证流程实现:
java复制@Component
public class JwtTokenProvider {
private String secret = "datingSecretKey";
private long validityInMilliseconds = 3600000; // 1h
public String createToken(String username, List<String> roles) {
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
// 其他验证方法...
}
3.2 智能匹配算法
基于用户标签的匹配算法核心逻辑:
java复制public List<User> recommendMatches(Long userId) {
User currentUser = userRepository.findById(userId).orElseThrow();
List<User> allUsers = userRepository.findAll();
return allUsers.stream()
.filter(u -> !u.getId().equals(userId))
.sorted((u1, u2) -> {
double score1 = calculateMatchScore(currentUser, u1);
double score2 = calculateMatchScore(currentUser, u2);
return Double.compare(score2, score1);
})
.limit(10)
.collect(Collectors.toList());
}
private double calculateMatchScore(User u1, User u2) {
// 计算年龄匹配度(20%权重)
double ageScore = 1 - Math.min(1, Math.abs(u1.getAge()-u2.getAge())/10.0) * 0.2;
// 计算兴趣匹配度(50%权重)
double interestScore = calculateInterestSimilarity(u1.getInterests(), u2.getInterests()) * 0.5;
// 计算地理位置匹配度(30%权重)
double locationScore = calculateLocationProximity(u1.getLocation(), u2.getLocation()) * 0.3;
return ageScore + interestScore + locationScore;
}
3.3 即时通讯系统
WebSocket配置示例:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOrigins("*")
.withSockJS();
}
}
前端连接示例:
javascript复制import { Stomp } from '@stomp/stompjs'
const stompClient = Stomp.over(() => new WebSocket('ws://localhost:8080/ws'))
stompClient.connect({}, () => {
stompClient.subscribe('/topic/messages', (message) => {
console.log('Received:', JSON.parse(message.body))
})
})
4. 数据库设计与优化
4.1 MySQL表结构设计
核心表结构示例:
sql复制CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
gender TINYINT COMMENT '1-男, 2-女',
age TINYINT,
avatar VARCHAR(255),
status TINYINT DEFAULT 1 COMMENT '0-禁用, 1-正常',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE user_profiles (
user_id BIGINT PRIMARY KEY,
nickname VARCHAR(50),
height INT COMMENT '单位cm',
education VARCHAR(50),
job VARCHAR(50),
income VARCHAR(20),
marriage_status TINYINT COMMENT '0-未婚, 1-离异, 2-丧偶',
about TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 查询性能优化
索引优化建议:
sql复制-- 为常用查询字段添加索引
ALTER TABLE users ADD INDEX idx_gender_age (gender, age);
ALTER TABLE user_profiles ADD INDEX idx_education_job (education, job);
-- 全文索引优化搜索
ALTER TABLE user_profiles ADD FULLTEXT INDEX ft_about (about);
分页查询优化:
xml复制<select id="selectUsersByPage" resultType="User">
SELECT * FROM users
WHERE status = 1
ORDER BY id DESC
LIMIT #{offset}, #{pageSize}
</select>
5. 系统部署与运维
5.1 生产环境部署
使用Docker Compose部署示例(docker-compose.yml):
yaml复制version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: dating_db
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/dating_db
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root123
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
mysql_data:
5.2 性能监控配置
SpringBoot Actuator配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
tags:
application: dating-backend
前端性能监控(使用Sentry):
javascript复制import * as Sentry from '@sentry/vue'
app.use(Sentry, {
dsn: 'your-dsn-here',
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router)
})
],
tracesSampleRate: 0.2
})
6. 常见问题与解决方案
6.1 跨域问题处理
SpringBoot跨域配置:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.maxAge(3600);
}
}
6.2 文件上传处理
SpringBoot文件上传配置:
java复制@RestController
@RequestMapping("/api/upload")
public class UploadController {
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
try {
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
String filename = UUID.randomUUID() + "." +
StringUtils.getFilenameExtension(file.getOriginalFilename());
Files.copy(file.getInputStream(), uploadPath.resolve(filename));
return ResponseEntity.ok(filename);
} catch (Exception e) {
return ResponseEntity.status(500).body("Upload failed");
}
}
}
Vue3前端上传组件:
vue复制<template>
<div>
<input type="file" @change="handleUpload" />
<button @click="submitUpload">上传</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const file = ref(null)
const handleUpload = (e) => {
file.value = e.target.files[0]
}
const submitUpload = async () => {
if (!file.value) return
const formData = new FormData()
formData.append('file', file.value)
try {
const res = await axios.post('/api/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
console.log('Upload success:', res.data)
} catch (err) {
console.error('Upload failed:', err)
}
}
</script>
6.3 事务管理问题
MyBatis事务配置示例:
java复制@Service
@Transactional
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private ProfileMapper profileMapper;
public void createUserWithProfile(User user, UserProfile profile) {
userMapper.insert(user);
profile.setUserId(user.getId());
profileMapper.insert(profile);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateLoginTime(Long userId) {
userMapper.updateLoginTime(userId, new Date());
}
}
注意:在Spring中,默认只对RuntimeException回滚,如果需要检查异常也触发回滚,需要使用@Transactional(rollbackFor = Exception.class)
