Development Environment
Backend Language: Java 1.8 Backend Framework: Spring Boot Build Tool: Maven 3.3.9+ Application Server: Tomcat 9 Database: MySQL 8.0 Database Management Tool: DBeaver Frontend Framework: Vue 3 IDE Options: IntelliJ IDEA / VS Code / Eclipse Browser: Chrome / Firefox / Edge
Admin Panel URL:
http://localhost:8080/{project-slug}/admin/dashboard/index.htmlUser Portal URL:http://localhost:8080/{project-slug}/user/home/index.htmlDefault Admin Credentials: Usernameadmin, Passwordsecure123
Key Development Technologies
Java SE Overview
Java is a statically typed, object-oriented programming language designed for cross-platform execution via the Java Virtual Machine (JVM). Its core principles—encapsulation, inheritance, and polymorphism—promote modular, reusable, and maintainable codebases. Built-in garbage collection automatically manages memory allocation and deallocation, reducing common runtime errors like memory leaks. Java’s extensive standard library provides pre-built components for networking, I/O, and collections, accelerating development workflows. It supports single inheritance for classes and multiple inheritance for interfaces, enabling flexible design patterns.
Spring Boot Capabilities
Spring Boot simplifies enterprise Java application development by eliminating boilerplate configuration through auto-configuration and opinionated starter dependencies. It builds on the Spring ecosystem, preserving features like dependency injection and aspect-oriented programming while streamlining project setup. Spring Boot includes a embedded Tomcat server, allowing applications to run as standalone JAR files without external deployment. Starter dependencies bundle compatible libraries for common use cases (e.g., web, data access, security), resolving version conflicts automatically and reducing setup time.
MySQL Database Features
MySQL is an open-source relational database management system (RDBMS) owned by Oracle Corporation. It uses Structured Query Language (SQL) for data manipulation and definition, with a simple syntax that minimizes development effort. MySQL supports ACID (Atomicity, Consistency, Isolation, Durability) transactions, ensuring data integrity for critical operations. It offers high performance, scalability, and robust security features like user authentication, role-based access control, and data encryption. MySQL is widely used in web applications due to its compatibility with most server-side technologies and low resource overhead.
Core Implementation Code
File Handling Controller
package com.platform.controller;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.annotation.PublicEndpoint;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.entity.SystemConfigEntity;
import com.exception.CustomValidationException;
import com.service.SystemConfigService;
import com.response.ApiResponse;
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.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
@RestController
@RequestMapping("/media")
@SuppressWarnings({"unchecked", "rawtypes"})
public class MediaController {
@Autowired
private SystemConfigService systemConfigService;
private static final String UPLOAD_DIR = "static/media/";
private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
@PublicEndpoint
@PostMapping("/upload")
public ApiResponse uploadMedia(@RequestParam("file") MultipartFile file, @RequestParam(required = false) String configKey) throws Exception {
if (file.isEmpty()) {
throw new CustomValidationException("Uploaded file cannot be empty");
}
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
String uniqueFilename = LocalDateTime.now().format(TIMESTAMP_FORMATTER) + "_" + UUID.randomUUID().toString().substring(0, 8) + "." + fileExtension;
Path uploadPath;
try {
ClassPathResource resource = new ClassPathResource(UPLOAD_DIR);
uploadPath = resource.getFile().toPath();
} catch (IOException e) {
uploadPath = Paths.get(UPLOAD_DIR);
}
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
Path targetFile = uploadPath.resolve(uniqueFilename);
file.transferTo(targetFile.toFile());
if (StringUtils.isNotBlank(configKey)) {
SystemConfigEntity config = systemConfigService.getOne(new QueryWrapper<SystemConfigEntity>().eq("config_key", configKey));
if (config == null) {
config = new SystemConfigEntity();
config.setConfigKey(configKey);
}
config.setConfigValue(uniqueFilename);
systemConfigService.saveOrUpdate(config);
}
return ApiResponse.success().put("filename", uniqueFilename);
}
@PublicEndpoint
@GetMapping("/download")
public ResponseEntity<byte[]> downloadMedia(@RequestParam String filename) {
try {
Path uploadPath;
try {
ClassPathResource resource = new ClassPathResource(UPLOAD_DIR);
uploadPath = resource.getFile().toPath();
} catch (IOException e) {
uploadPath = Paths.get(UPLOAD_DIR);
}
Path targetFile = uploadPath.resolve(filename);
if (Files.exists(targetFile)) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", filename);
return new ResponseEntity<>(Files.readAllBytes(targetFile), headers, HttpStatus.OK);
}
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Adoption Discussion Board Controller
package com.platform.controller;
import com.annotation.PublicEndpoint;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.entity.DiscussionBoardEntity;
import com.response.ApiResponse;
import com.service.DiscussionBoardService;
import com.util.PageQueryUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/discussion")
public class DiscussionBoardController {
@Autowired
private DiscussionBoardService discussionBoardService;
@GetMapping("/admin/page")
public ApiResponse getAdminDiscussionPage(@RequestParam(required = false) Integer pageNum,
@RequestParam(required = false) Integer pageSize,
DiscussionBoardEntity discussion,
HttpSession session) {
Page<DiscussionBoardEntity> page = PageQueryUtil.initPage(pageNum, pageSize);
QueryWrapper<DiscussionBoardEntity> queryWrapper = new QueryWrapper<>();
String role = (String) session.getAttribute("userRole");
if (!"ADMIN".equals(role)) {
Long userId = (Long) session.getAttribute("userId");
queryWrapper.eq("user_id", userId);
}
PageQueryUtil.applyConditions(queryWrapper, discussion);
IPage<DiscussionBoardEntity> resultPage = discussionBoardService.page(page, queryWrapper);
return ApiResponse.success().put("pageData", resultPage);
}
@PublicEndpoint
@GetMapping("/user/list")
public ApiResponse getUserDiscussionList(@RequestParam(required = false) Integer pageNum,
@RequestParam(required = false) Integer pageSize,
DiscussionBoardEntity discussion) {
Page<DiscussionBoardEntity> page = PageQueryUtil.initPage(pageNum, pageSize);
QueryWrapper<DiscussionBoardEntity> queryWrapper = new QueryWrapper<>();
PageQueryUtil.applyConditions(queryWrapper, discussion);
IPage<DiscussionBoardEntity> resultPage = discussionBoardService.page(page, queryWrapper);
return ApiResponse.success().put("listData", resultPage);
}
@PublicEndpoint
@GetMapping("/detail/{id}")
public ApiResponse getDiscussionDetail(@PathVariable Long id) {
DiscussionBoardEntity discussion = discussionBoardService.getById(id);
List<DiscussionBoardEntity> replies = discussionBoardService.list(new QueryWrapper<DiscussionBoardEntity>().eq("parent_id", id));
discussion.setReplies(replies);
return ApiResponse.success().put("discussionDetail", discussion);
}
@PostMapping("/create")
@Transactional(rollbackFor = Exception.class)
public ApiResponse createDiscussion(@RequestBody DiscussionBoardEntity discussion, HttpSession session) {
discussion.setId(System.currentTimeMillis() + (long)(Math.random() * 10000));
discussion.setUserId((Long) session.getAttribute("userId"));
discussionBoardService.save(discussion);
return ApiResponse.success();
}
@PutMapping("/update")
@Transactional(rollbackFor = Exception.class)
public ApiResponse updateDiscussion(@RequestBody DiscussionBoardEntity discussion) {
discussionBoardService.updateById(discussion);
return ApiResponse.success();
}
@DeleteMapping("/delete")
@Transactional(rollbackFor = Exception.class)
public ApiResponse deleteDiscussions(@RequestBody Long[] ids) {
discussionBoardService.removeByIds(Arrays.asList(ids));
return ApiResponse.success();
}
}
System Validation
System validation includes both white-box and black-box testing. White-box testing checks internal code structure, logic flow, and path coverage, ensuring all critical branches are executed. Black-box testing verifies functional requirements without accessing internal code, covering scenarios like user registration, pet search, adoption application submission, and admin management operations. Testing follows key principles: traceability to user requirements, pre-planning before coding, focusing on high-risk modules, incremental testing from unit to integration, and designing test cases for maximum coverage. The goal is to identify defects early, improve system reliability, and ensure alignment with user expectations.