Developing an Online Matchmaking Service Using Spring Boot and Vue
The system offers a complete solution for managing matchmaking-related activities, including wedding company listings, booking and favoriting, marriage case studies with bookmarking, user profiles, matchmaking interactions such as messages and favorites, and administrative tools. It is implemented with Java, Spring Boot, and MySQL, combined with a Vue-based front end.
Technology Stack
- Backend Language: Java
- Framework: Spring Boot 2.x
- JDK: 1.8
- Database: MySQL 5.7
- ORM: MyBatis-Plus
- Build Tool: Maven 3.3+
- Frontend: Vue.js
- Server: Embedded Tomcat
Default admin credentials: admin / admin
Key Technologies
Java provides a robust, object-oriented environment with strong typing and automatic memory management. Encapsulation, inheritance, and polymorphism enable modular and reusable code.
Spring Boot minimizes configuration overhead by auto-configuring components and embedding the server. It integrates a wide ecosystem of libraries while avoiding dependency conflicts.
MySQL is a high-performance relational database offering data integrity, fast queries, and flexible access control. It is well suited for web applications requiring shared, low-redundancy data storage.
Core Implementation Snippets
File Upload and Download Handler
package com.platform.controller;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.annotation.IgnoreAuth;
import com.entity.SystemConfig;
import com.service.SystemConfigService;
import com.utils.ResponseResult;
@RestController
@RequestMapping("/storage")
public class FileStorageController {
@Autowired
private SystemConfigService configService;
private static final String UPLOAD_DIR = "upload";
@IgnoreAuth
@PostMapping("/upload")
public ResponseResult handleUpload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "type", required = false) String type) throws IOException {
if (file.isEmpty()) {
throw new RuntimeException("Selected file is empty");
}
String originalName = file.getOriginalFilename();
String extension = originalName.substring(originalName.lastIndexOf('.') + 1);
Path basePath = Paths.get("static");
if (!Files.exists(basePath)) {
basePath = Paths.get("");
}
Path uploadPath = basePath.resolve(UPLOAD_DIR);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
String storedName = Instant.now().toEpochMilli() + "." + extension;
Path target = uploadPath.resolve(storedName);
file.transferTo(target.toFile());
if ("1".equals(type)) {
SystemConfig config = configService.getOne(
new com.baomidou.mybatisplus.mapper.EntityWrapper<SystemConfig>().eq("name", "faceFile"));
if (config == null) {
config = new SystemConfig();
config.setName("faceFile");
config.setValue(storedName);
} else {
config.setValue(storedName);
}
configService.saveOrUpdate(config);
}
return ResponseResult.success().put("file", storedName);
}
@IgnoreAuth
@GetMapping("/download")
public ResponseEntity<Resource> handleDownload(@RequestParam("fileName") String fileName) throws IOException {
Path uploadPath = Paths.get("static", UPLOAD_DIR);
Path filePath = uploadPath.resolve(fileName);
if (!Files.exists(filePath)) {
return ResponseEntity.notFound().build();
}
byte[] data = Files.readAllBytes(filePath);
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(resource);
}
}
Discussion Forum REST API
package com.platform.controller;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.annotation.IgnoreAuth;
import com.entity.ForumTopic;
import com.entity.view.ForumTopicView;
import com.service.ForumTopicService;
import com.utils.PageResult;
import com.utils.ResponseResult;
import com.utils.ToolUtil;
@RestController
@RequestMapping("/forum")
public class ForumTopicController {
@Autowired
private ForumTopicService topicService;
@GetMapping("/page")
public ResponseResult page(@RequestParam Map<String, Object> params,
ForumTopic topic,
HttpServletRequest request) {
HttpSession session = request.getSession();
if (!"admin".equals(session.getAttribute("role"))) {
topic.setUserId((Long) session.getAttribute("userId"));
}
EntityWrapper<ForumTopic> wrapper = new EntityWrapper<>();
PageResult page = topicService.queryPage(params, ToolUtil.buildSort(ToolUtil.buildCondition(wrapper, topic, params), params));
return ResponseResult.success().put("data", page);
}
@IgnoreAuth
@GetMapping("/public")
public ResponseResult publicList(@RequestParam Map<String, Object> params,
ForumTopic topic) {
EntityWrapper<ForumTopic> wrapper = new EntityWrapper<>();
PageResult page = topicService.queryPage(params, ToolUtil.buildSort(ToolUtil.buildCondition(wrapper, topic, params), params));
return ResponseResult.success().put("data", page);
}
@GetMapping("/{id}")
public ResponseResult detail(@PathVariable Long id) {
ForumTopic topic = topicService.selectById(id);
if (topic != null) {
buildThreadHierarchy(topic);
}
return ResponseResult.success().put("data", topic);
}
private void buildThreadHierarchy(ForumTopic parent) {
List<ForumTopic> children = topicService.selectList(
new EntityWrapper<ForumTopic>().eq("parent_id", parent.getId()));
if (children == null || children.isEmpty()) return;
parent.setChildren(children);
for (ForumTopic child : children) {
buildThreadHierarchy(child);
}
}
@PostMapping
@Transactional
public ResponseResult create(@RequestBody ForumTopic topic,
HttpServletRequest request) {
topic.setId(generateId());
topic.setUserId((Long) request.getSession().getAttribute("userId"));
topicService.insert(topic);
return ResponseResult.success();
}
@PutMapping
public ResponseResult update(@RequestBody ForumTopic topic) {
topicService.updateById(topic);
return ResponseResult.success();
}
@DeleteMapping
public ResponseResult remove(@RequestBody Long[] ids) {
topicService.deleteBatchIds(Arrays.asList(ids));
return ResponseResult.success();
}
private Long generateId() {
return System.currentTimeMillis() + (long) (Math.random() * 1000);
}
}
Testing Approach
Testing was conducted both on local servers and through structured verification methods. White-box testing examined internal logic and path coverage, while black-box testing validated functional requirements from an end-user perspective. The primary goal was to uncover defects before deployment.
Tests followed principles such as tracing back to user needs, beginning with smaller components before integration, and focusing on high-risk modules (Pareto principle). Test cases were designed to cover major functional areas, including forum interactions, file handling, and role-based access.