Design and Implementation of a Personal Financial Management System Using SpringBoot, Vue, and uniapp
Technical Overview
The system is built using the SpringBoot framework for the backend, which includes embedded servers like Tomcat, Jetty, and Undertow. This allows for quick deployment without additional configuration. SpringBoot's auto-configuration feature simplifies setup by automatically configuring dependencies based on project needs. It also provides features such as Spring Data, Spring Security, and Spring Cloud, enhancing development efficiency.
For the frontend, Vue.js is used, leveraging its virtual DOM technology to enable efficient updates when data changes. Vue offers responsive data binding, componant-based architecture, and an intuitive API, making it easy to build scalable and maintainable applications.
The persistence layer uses MyBatis-Plus, an enhanced version of MyBatis that streamlines database operations. It supports various databases and includes tools like code generation, pagination, dynamic queries, and optimistic locking, reducing manual SQL writing and improving productivity.
Testing
System testing focuses on identifying issues through functional tests, ensuring all features meet requirements. Testing involves creating test cases that cover input validation, boundary checks, and role-based access control. The goal is to validate functionality, ensure usability, and confirm that the system meets user expectations.
Login Functionality Test Cases:
User Management Test Cases:
Code Example:
@IgnoreAuth
@PostMapping("/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
UsersEntity user = userService.selectOne(new EntityWrapper<UsersEntity>().eq("username", username));
if(user == null || !user.getPassword().equals(password)) {
return R.error("Invalid username or password");
}
String token = tokenService.generateToken(user.getId(), username, "users", user.getRole());
return R.ok().put("token", token);
}
@Override
public String generateToken(Long userId, String username, String tableName, String role) {
TokenEntity tokenEntity = this.selectOne(new EntityWrapper<TokenEntity>().eq("userId", userId).eq("role", role));
String token = CommonUtil.getRandomString(32);
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.HOUR_OF_DAY, 1);
if(tokenEntity != null) {
tokenEntity.setToken(token);
tokenEntity.setExpiryTime(cal.getTime());
this.updateById(tokenEntity);
} else {
this.insert(new TokenEntity(userId, username, tableName, role, token, cal.getTime()));
}
return token;
}
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
public static final String AUTH_TOKEN_HEADER = "Authorization";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Authorization, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpStatus.OK.value());
return false;
}
IgnoreAuth annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else {
return true;
}
String token = request.getHeader(AUTH_TOKEN_HEADER);
if (annotation != null) {
return true;
}
TokenEntity tokenEntity = null;
if (StringUtils.isNotBlank(token)) {
tokenEntity = tokenService.getTokenEntity(token);
}
if (tokenEntity != null) {
request.getSession().setAttribute("userId", tokenEntity.getUserId());
request.getSession().setAttribute("role", tokenEntity.getRole());
request.getSession().setAttribute("tableName", tokenEntity.getTableName());
request.getSession().setAttribute("username", tokenEntity.getUsername());
return true;
}
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try {
writer = response.getWriter();
writer.print(JSONObject.toJSONString(R.error(401, "Please log in first")));
} finally {
if (writer != null) {
writer.close();
}
}
return false;
}
}
Database Structure:
-- ----------------------------
-- Table structure for token
-- ----------------------------
DROP TABLE IF EXISTS `token`;
CREATE TABLE `token` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
`userId` bigint(20) NOT NULL COMMENT 'User ID',
`username` varchar(100) NOT NULL COMMENT 'Username',
`tableName` varchar(100) DEFAULT NULL COMMENT 'Table name',
`role` varchar(100) DEFAULT NULL COMMENT 'Role',
`token` varchar(200) NOT NULL COMMENT 'Token value',
`addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Add time',
`expiryTime` 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='Token table';
-- ----------------------------
-- Records of token
-- ----------------------------
INSERT INTO `token` VALUES ('9', '23', 'cd01', 'student', 'student', 'al6svx5qkei1wljry5o1npswhdpqcpcg', '2023-02-23 21:46:45', '2023-03-15 14:01:36');
INSERT INTO `token` VALUES ('10', '11', 'xh01', 'student', 'student', 'fahmrd9bkhqy04sq0fzrl4h9m86cu6kx', '2023-02-27 18:33:52', '2023-03-17 18:27:42');
INSERT INTO `token` VALUES ('11', '17', 'ch01', 'student', 'student', 'u5km44scxvzuv5yumdah2lhva0gp4393', '2023-02-27 18:46:19', '2023-02-27 19:48:58');
INSERT INTO `token` VALUES ('12', '1', 'admin', 'user', 'admin', 'h1pqzsb9bldh93m92j9m2sljy9bt1wdh', '2023-02-27 19:37:01', '2023-03-17 18:23:02');
INSERT INTO `token` VALUES ('13', '21', 'xiaohao', 'club_leader', 'club_leader', 'zdm7j8h1wnfe27pkxyiuzvxxy27ykl2a', '2023-02-27 19:38:07', '2023-03-17 18:25:20');
INSERT INTO `token` VALUES ('14', '27', 'djy01', 'student', 'student', 'g3teq4335pe21nwuwj2sqkrpqoabqomm', '2023-03-15 12:56:17', '2023-03-15 14:00:16');
INSERT INTO `token` VALUES ('15', '29', 'dajiyue', 'club_leader', 'club_leader', '0vb1x9xn7riewlp5ddma5ro7lp4u8m9j', '2023-03-15 12:58:08', '2023-03-15 14:03:48');