Building a Community Elderly Care Service System with SpringBoot and Vue
Technology Stack Overview
Backend Framework: SpringBoot
SpringBoot simplifies application development by embedding servers like Tomcat, Jetty, and Undertow directly into the framework. This eliminates the need for external server installation and complex configuration. The auto-configuration mechanism automatically configures application components based on project dependencies, significantly reducing manual setup work. Additionally, SpringBoot integrates seamlessly with other Spring ecosystem projects such as Spring Data, Spring Security, and Spring Cloud, enabling rapid application development with minimal boilerplate code.
Frontend Framework: Vue.js
Vue.js leverages virtual DOM technology to optimize UI rendering performance. The framework implements reactive data binding, meaning any changes to the underlying data automatically reflect in the user interface. This declarative approach allows developers to focus on data manipulation rather than manual DOM updates. Vue's component-based architecture promotes code reusability and maintainability, making it an excellent choice for single-page applications.
Persistence Layer: MyBatis-Plus
MyBatis-Plus extends the MyBatis framework with enhanced features for database operations. It supports multiple database systems including MySQL, Oracle, SQL Server, and PostgreSQL. The framework provides a rich set of APIs and annotations that simplify ORM operations, reducing the need for handwritten SQL queries. MyBatis-Plus also includes a code generator that can automatically create entity classes, mapper interfaces, and XML mapping files, accelerating development speed. Additional features include pagination queries, dynamic query construction, optimistic locking, and performance analysis tools.
Authentication Implementation
Login Controller
The authentication module handles user login requests and token generation:
@PostMapping("/auth/login")
public ApiResponse authenticateUser(String loginName, String secretKey, String verifyCode, HttpServletRequest httpRequest) {
UserAccount account = accountService.queryOne(new QueryWrapper<UserAccount>().eq("login_name", loginName));
if (account == null || !account.getSecretKey().equals(secretKey)) {
return ApiResponse.fail("Invalid credentials");
}
String authToken = authTokenService.createAuthToken(account.getId(), loginName, "user_accounts", account.getRoleType());
return ApiResponse.success().put("authToken", authToken);
}Token Generation Service
@Override
public String createAuthToken(Long accountId, String loginName, String entityTable, String roleType) {
AuthTokenRecord record = this.fetchOne(new QueryWrapper<AuthTokenRecord>().eq("account_id", accountId).eq("role_type", roleType));
String generatedToken = SecurityUtils.generateRandomKey(32);
Calendar expiryCalendar = Calendar.getInstance();
expiryCalendar.setTime(new Date());
expiryCalendar.add(Calendar.HOUR_OF_DAY, 1);
if (record != null) {
record.setAuthToken(generatedToken);
record.setExpiryTime(expiryCalendar.getTime());
this.modifyById(record);
} else {
this.store(new AuthTokenRecord(accountId, loginName, entityTable, roleType, generatedToken, expiryCalendar.getTime()));
}
return generatedToken;
}Authorization Interceptor
The interceptor validates tokens for protected endpoints:
@Component
public class TokenValidationInterceptor implements HandlerInterceptor {
public static final String AUTH_HEADER = "AuthToken";
@Autowired
private AuthTokenService authTokenService;
@Override
public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Object handler) throws Exception {
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,AuthToken,Origin,imgType,Content-Type,cache-control,postman-token,Cookie,Accept,authorization");
httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpResponse.setStatus(HttpStatus.OK.value());
return false;
}
SkipAuth skipAnnotation = null;
if (handler instanceof HandlerMethod) {
skipAnnotation = ((HandlerMethod) handler).getMethodAnnotation(SkipAuth.class);
} else {
return true;
}
String authToken = httpRequest.getHeader(AUTH_HEADER);
if (skipAnnotation != null) {
return true;
}
AuthTokenRecord tokenRecord = null;
if (StringUtils.isNotBlank(authToken)) {
tokenRecord = authTokenService.fetchTokenRecord(authToken);
}
if (tokenRecord != null) {
httpRequest.getSession().setAttribute("userId", tokenRecord.getAccountId());
httpRequest.getSession().setAttribute("role", tokenRecord.getRoleType());
httpRequest.getSession().setAttribute("tableName", tokenRecord.getEntityTable());
httpRequest.getSession().setAttribute("username", tokenRecord.getLoginName());
return true;
}
PrintWriter responseWriter = null;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
try {
responseWriter = httpResponse.getWriter();
responseWriter.print(JSONObject.toJSONString(ApiResponse.fail(401, "Authentication required")));
} finally {
if (responseWriter != null) {
responseWriter.close();
}
}
return false;
}
}Database Schema
The token table stores authentication session information:
DROP TABLE IF EXISTS `auth_token`;
CREATE TABLE `auth_token` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`account_id` bigint(20) NOT NULL COMMENT 'Account ID',
`login_name` varchar(100) NOT NULL COMMENT 'Login Name',
`entity_table` varchar(100) DEFAULT NULL COMMENT 'Entity Table',
`role_type` varchar(100) DEFAULT NULL COMMENT 'Role Type',
`auth_token` varchar(200) NOT NULL COMMENT 'Authentication Token',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
`expiry_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Expiry Time',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Authentication Token Table';System Testing
Testing Objectives
System testing validates that the application meets all functional requirements specified in the requirements document. The primary goal is to identify defects and inconsistencies from multiple perspectives. Testing ensures system quality, reliability, and user experience by simulating various usage scenarios and edge cases.
Login Functionality Testing
Login testing verifies authentication mechanisms through boundary value analysis and input validation:
| Test Input | Expected Result | Actual Result | Status |
|---|---|---|---|
| Username: admin, Password: admin123, Captcha: correct | Login successful | Login successful | Pass |
| Username: admin, Password: wrongpass, Captcha: correct | Password error | Password error message displayed | Pass |
| Username: admin, Password: admin123, Captcha: incorrect | Captcha error | Captcha error message displayed | Pass |
| Username: empty, Password: admin123, Captcha: correct | Username required | Username required message displayed | Pass |
| Username: admin, Password: empty, Captcha: correct | Password required | Password required message displayed | Pass |
User Management Testing
User management testing covers CRUD operations and validation rules:
| Test Input | Expected Result | Actual Result | Status |
|---|---|---|---|
| Enter valid user details | User added successfully, displayed in list | User appears in list | Pass |
| Modify user information | Changes saved successfully | Updated information displayed | Pass |
| Delete selected user | Confirmation prompt, user removed after confirmation | User removed from system | Pass |
| Submit without username | Validation error for required field | Username required message shown | Pass |
| Enter duplicate username | Duplicate entry rejected | Duplicate username error shown | Pass |