Building a Fuling District Specialty Agricultural Products Trading System with Spring Boot, Vue.js, and UniApp
Introduction
This article details the development of a trading system for specialty agricultural products in the Fuling District, utilizing a modern technology stack including Spring Boot for the backend, Vue.js for the web frontend, and UniApp for cross-platform mobile applications.
Technology Stack
Backend Framework: Spring Boot
Spring Boot is a development framework based on the Spring Framework, offering numerous advantages. It includes built-in servers like Tomcat, Jetty, and Undertow, eliminating the need for additional installation and configuration. A key feature is its powerful auto-configuration capability, which automatically configures the application based on project dependencies, greatly simplifying development. Spring Boot also provides a rich set of out-of-the-box features and plugins, such as Spring Data, Spring Security, and Spring Cloud, enabling developers to build applications more quickly and easily integrate other technologies. Additionally, it offers flexible configuration management, rapid development and deployment, strong community support, monitoring and diagnostic tools, and reliable testing support. These benefits make Spring Boot a popular framework for building high-quality applications with good configurability, scalability, and maintainability.
Below is a sample core code snippet:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class ApplicationEntryPoint {
public static void main(String[] args) {
SpringApplication.run(ApplicationEntryPoint.class, args);
}
@GetMapping("/greeting")
public String getGreeting() {
return "Welcome to the System!";
}
}
This code defines a Spring Boot application entry class ApplicationEntryPoint, marked with @SpringBootApplication as a Spring Boot application and @RestController as a RESTful controller. The getGreeting method is mapped to the "/greeting" path using @GetMapping, returning a simple string response. After starting the application with SpringApplication.run, Spring Boot automatically configures and launches the embedded server, allowing access via "http://localhost:8080/greeting".
Frontend Framework: Vue.js
Vue.js is a popular JavaScript framework known for its advantages, including virtual DOM technology. The virtual DOM is an in-memory data structure that facilitates efficient DOM operations. Vue.js employs modern techniques like reactive data binding, virtual DOM, and componentization, providing a flexible, efficient, and maintainable development model. When data changes, Vue.js automatically updates the UI, allowing developers to focus on data processing without manual UI updates.
Here is an example demonstrating Vue.js core functionality:
<!DOCTYPE html>
<html>
<head>
<title>Vue.js Demo</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>{{ message }}</h2>
<button @click="updateMessage">Update Message</button>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello, Vue.js!'
},
methods: {
updateMessage: function() {
this.message = 'Vue.js is powerful!';
}
}
});
</script>
</body>
</html>
In this example, a Vue instance is created and bound to an element with id "app". The data property defines a variable message initialized to "Hello, Vue.js!". The page displays message using double curly braces ({{ message }}). The methods property defines an updateMessage method that changes message when the button is clicked. Due to Vue.js's reactive data binding, the displayed content updates automatically when message changes.
Persistence Layer Framework: MyBatis
MyBatis is an open-source persistence layer framework that simplifies database operations. Its core concept is separating SQL statements from Java code, using XML or annotations to describe database operations, thereby decoupling the data access layer and enhancing flexibility. Key advantages include:
- Simplified database operations: MyBatis provides powerful SQL mapping, allowing Java objects to be mapped to database tables without manual SQL writing.
- Flexible SQL control: It supports dynamic SQL, enabling generation of SQL statements based on different conditions and logic.
- Cache support: MyBatis offers first-level and second-level caching to reduce database access and improve performance.
- High extensibility: Its plugin mechanism allows easy customization to meet various business needs.
System Testing
To ensure the highest quality standards, comprehensive testing was conducted on the system from multiple perspectives to identify and address issues, ensuring integrity and reliability.
Purpose of System Testing
System testing is a critical phase in the development lifecycle, serving as the final checkpoint for quality and reliability. Its main goal is to prevent user issues and enhance the user experience by simulating various scenarios to detect and resolve defects. Testing validates that the system meets requirements and identifies discrepancies, focusing on user-centric scenarios to ensure expected outcomes match actual results.
Functional Testing
Functional testing involves testing system modules using methods like clicking, inputting boundary values, and validating required and optional fields through black-box testing. Test cases are written and executed to draw conclusions.
For example, login functionality was tested by verifying account passwords against database data. If inputs are incorrect, the system prompts errors. Role permissions are also validated, with errors for unauthorized roles. Below is a sample test case table for login:
| Input Data | Expected Result | Actual Result | Result Analysis |
|---|---|---|---|
| Username: admin, Passsword: 123456, Captcha: correct | Login successful | Login successful | Consistent |
| Username: admin, Password: 111111, Captcha: correct | Password error | Password error, please re-enter | Consistent |
| Username: admin, Password: 123456, Captcha: incorrect | Captcha error | Captcha error | Consistent |
| Username: empty, Password: 123456, Captcha: correct | Username required | Please enter username | Consistent |
| Username: admin, Password: empty, Captcha: correct | Password error | Password error, please re-enter | Consistent |
User management functions, including add, edit, delete, and search, were also tested with similar case tables.
Testing Conclusion
Black-box testing was primari used, simulating user interactions to validate system workflows. Testing is essential for system refinement and usability. The process confirmed that the system's functional modules meet design specifications, with logic correctly implemented for user-friendly operation. All scenarios aligned with user requirements, and final results indicate the system satisfeis functional and performance design criteria.
Code Reference
Below is a Java code snippet for login functionality, including token generation and authorization interception:
// Annotation to ignore authentication
@IgnoreAuth
@PostMapping(value = "/authenticate")
public Response loginUser(String username, String password, String captcha, HttpServletRequest request) {
// Retrieve user information
UserEntity user = userService.fetchUser(new EntityWrapper<UserEntity>().eq("username", username));
// Validate user existence and password
if(user == null || !user.getPassword().equals(password)) {
return Response.error("Incorrect username or password");
}
// Generate token
String token = tokenService.createToken(user.getId(), username, "users", user.getRole());
return Response.ok().addData("token", token);
}
// Token generation method
@Override
public String createToken(Long userId, String username, String tableName, String role) {
// Check for existing token
TokenEntity tokenRecord = this.fetchToken(new EntityWrapper<TokenEntity>().eq("userId", userId).eq("role", role));
// Generate random token string
String token = Utility.generateRandomString(32);
// Set token expiration to 1 hour from now
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.HOUR_OF_DAY, 1);
if(tokenRecord != null) {
// Update existing token
tokenRecord.setToken(token);
tokenRecord.setExpirationTime(calendar.getTime());
this.updateById(tokenRecord);
} else {
// Insert new token record
this.insert(new TokenEntity(userId, username, tableName, role, token, calendar.getTime()));
}
return token;
}
/**
* Authorization interceptor for token validation
*/
@Component
public class AuthInterceptor implements HandlerInterceptor {
// Key for token in request header
public static final String TOKEN_HEADER_KEY = "Authorization";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Enable CORS
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"));
// Handle preflight OPTIONS requests
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
// Retrieve @IgnoreAuth annotation
IgnoreAuth annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else {
return true;
}
// Extract token from header
String token = request.getHeader(TOKEN_HEADER_KEY);
// Bypass methods without authentication requirement
if(annotation != null) {
return true;
}
// Fetch token entity based on token
TokenEntity tokenEntity = null;
if(StringUtils.isNotBlank(token)) {
tokenEntity = tokenService.getTokenByValue(token);
}
if(tokenEntity != null) {
// Store user info in session
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;
}
// Authentication failed, return 401 error
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try {
writer = response.getWriter();
writer.print(JSONObject.toJSONString(Response.error(401, "Please log in first")));
} finally {
if(writer != null){
writer.close();
}
}
return false;
}
}
This code implements basic login functionality with token-based authorization, ensuring only users with valid tokens can access restricted resources.
Database Reference
An example SQL statement for designing a product table:
-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`name` varchar(100) NOT NULL COMMENT 'Product Name',
`price` decimal(10, 2) NOT NULL COMMENT 'Product Price',
`description` varchar(200) DEFAULT NULL COMMENT 'Product Description',
`stock` int(11) NOT NULL COMMENT 'Product Stock',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Product Table';
This product table includes fields for id, name, price, description, stock, create_time, and update_time.
Sample data insertion:
INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Apple iPhone 13', 999.99, 'A high-performance smartphone', 100);
INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Samsung Galaxy S21', 899.99, 'A flagship Android device', 150);
INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Sony PlayStation 5', 499.99, 'Next-generation gaming console', 50);