Building a Hospital Resource Management System with Spring Boot, Vue, and Uniapp
Spring Boot serves as the back end framework, integrating embedded servers like Tomcat, Jetty, and Undertow for streamlined deployment. Its auto-configuration capability automatically sets up dependencies, reducing manual configuration efforts. The framework offers out-of-the-box features and plugins, such as Spring Data and Spring Security, enabling rapid development of high-quality applications.
Vue.js is utilized for the frontend, leveraging virtual DOM technology to enhance DOM manipulation efficiency. It employs reactive data binding and component-based architecture, allowing developers to focus on data handling rather than manual UI updates, resulting in a flexible and maintainable development model.
MyBatis-Plus is chosen as the persistence layer framework, built on MyBatis to simplify database operations. It supports multiple databases, including MySQL and PostgreSQL, and provides rich APIs and annotations for ORM tasks, minimizing SQL writing. The framework includes a code generator for automatic creation of entity classes and mapper files, along with features like pagination and dynamic queries to boost development efficiency.
System testing is conducted from multiple perspectives to identify and rectify issues, ensuring the system meets requirements. The primary goal is to prevent user problems and enhance the user experience by simulating various scenarios to detect and resolve defects.
Functional testing involves black-box methods, such as clicking, input validation, and boundary testing, to verify system modules. Test cases are written and executed to derive conclusions.
Login functionality testing includes validating user credentials and role permissions. For example, when a user attempts to log in, the system checks if the username and password match stored data, with error prompts for incorrect inputs. Role-based access is also verified, ensuring administrators can access restricted areas.
| Input Data | Expected Result | Actual Result | Analysis |
|---|---|---|---|
| Username: admin, Password: 123456, Captcha: correct | Login successful | Login successful | Matches expected |
| Username: admin, Password: 111111, Captcha: correct | Password error | Password error, prompt to re-enter | Matches expected |
| Username: admin, Password: 123456, Captcha: incorrect | Captcha error | Captcha error | Matches expected |
| Username: empty, Password: 123456, Captcha: correct | Username required | Prompt to enter username | Matches expected |
| Username: admin, Password: empty, Captcha: correct | Password error | Password error, prompt to re-enter | Matches expected |
User management testing covers adding, editing, deleting, and searching users. When adding a user, the system validates required fields and checks for duplicate usernames. Editing and deletion operations are confirmed through system prompts.
| Input Data | Expected Result | Actual Result | Analysis |
|---|---|---|---|
| Fill in user details | Add successful, appears in list | User appears in list | Matches expected |
| Modify user information | Edit successful, information updated | Information updated | Matches expected |
| Select delete user | System asks for confirmation, user removed after confirm | User not found after deletion | Matches expected |
| Add user without username | Prompt: username cannot be empty | Prompt: username cannot be empty | Matches expected |
| Enter existing username | Add failed, duplicate username prompt | Add failed, duplicate username prompt | Matches expected |
Testing concludes that the system meets functional and performance requirements through black-box methods, ensuring correctness and usability. All scenarios align with user needs, with issues addressed from a user perspective.
@IgnoreAuth
@PostMapping("/authenticate")
public ResponseResult login(String userIdentifier, String passphrase, String verificationCode, HttpServletRequest req) {
UserAccount account = accountService.fetchOne(new QueryWrapper<UserAccount>().eq("user_identifier", userIdentifier));
if (account == null || !account.getPassphrase().equals(passphrase)) {
return ResponseResult.failure("Invalid credentials");
}
String authToken = tokenProvider.createToken(account.getId(), userIdentifier, "user_accounts", account.getRole());
return ResponseResult.success().withData("authToken", authToken);
}
@Override
public String createToken(Long accountId, String userIdentifier, String tableName, String role) {
TokenRecord tokenRecord = this.fetchOne(new QueryWrapper<TokenRecord>().eq("account_id", accountId).eq("role", role));
String newToken = RandomUtil.generateString(32);
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.HOUR_OF_DAY, 1);
if (tokenRecord != null) {
tokenRecord.setToken(newToken);
tokenRecord.setExpirationTime(calendar.getTime());
this.updateById(tokenRecord);
} else {
this.insert(new TokenRecord(accountId, userIdentifier, tableName, role, newToken, calendar.getTime()));
}
return newToken;
}
@Component
public class AuthInterceptor implements HandlerInterceptor {
private static final String AUTH_TOKEN_HEADER = "Auth-Token";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
resp.setHeader("Access-Control-Max-Age", "3600");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Allow-Headers", "x-requested-with, Token, Origin, Content-Type, Cookie, Accept, authorization");
resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
if (req.getMethod().equals(HttpMethod.OPTIONS.name())) {
resp.setStatus(HttpStatus.OK.value());
return false;
}
IgnoreAuth annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else {
return true;
}
if (annotation != null) {
return true;
}
String token = req.getHeader(AUTH_TOKEN_HEADER);
TokenRecord tokenRecord = null;
if (StringUtils.isNotBlank(token)) {
tokenRecord = tokenService.getTokenRecord(token);
}
if (tokenRecord != null) {
req.getSession().setAttribute("accountId", tokenRecord.getAccountId());
req.getSession().setAttribute("role", tokenRecord.getRole());
req.getSession().setAttribute("tableName", tokenRecord.getTableName());
req.getSession().setAttribute("userIdentifier", tokenRecord.getUserIdentifier());
return true;
}
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json; charset=utf-8");
try (PrintWriter writer = resp.getWriter()) {
writer.print(JSONObject.toJSONString(ResponseResult.failure(401, "Authentication required")));
}
return false;
}
}
-- Table structure for token
DROP TABLE IF EXISTS `auth_token`;
CREATE TABLE `auth_token` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
`account_id` BIGINT NOT NULL COMMENT 'Account ID',
`user_identifier` VARCHAR(100) NOT NULL COMMENT 'Username',
`table_name` VARCHAR(100) DEFAULT NULL COMMENT 'Table name',
`role` VARCHAR(100) DEFAULT NULL COMMENT 'Role',
`token` VARCHAR(200) NOT NULL COMMENT 'Token',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
`expires_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Expiration time',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Token table';
-- Sample records
INSERT INTO `auth_token` VALUES (1, 1, 'admin', 'user_accounts', 'Administrator', 'sampletoken123', '2023-02-27 19:37:01', '2023-03-17 18:23:02');