1. 项目背景与核心价值
黔醉酒业白酒销售系统是一个典型的电商平台毕业设计项目,它融合了当下企业级开发中最主流的技术栈(SpringBoot+Vue+MySQL)和推荐算法实践(协同过滤)。这个选题的价值在于:
- 技术栈的完整性:覆盖了Java后端、前端框架和数据库三大核心模块
- 算法与业务的结合:将协同过滤这种经典推荐算法落地到具体业务场景
- 毕业设计的黄金标准:既有技术深度又具备完整项目流程
我在实际开发中发现,这类系统最考验的不是单一技术点的实现,而是如何让各模块有机协同。比如Vue前端如何优雅地消费SpringBoot的REST API,协同过滤算法如何与MySQL数据存储配合等。
2. 技术架构设计解析
2.1 整体架构设计
采用前后端分离架构:
code复制前端:Vue 2.x + Element UI
后端:SpringBoot 2.7 + MyBatis Plus
数据库:MySQL 8.0
算法层:基于用户的协同过滤(UserCF)
这种选型的优势在于:
- Vue的组件化开发能快速构建管理后台界面
- SpringBoot的自动配置简化了后端开发复杂度
- MyBatis Plus的Active Record模式提升数据库操作效率
2.2 数据库设计要点
白酒销售系统的核心表包括:
sql复制CREATE TABLE `product` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '酒品名称',
`category` varchar(50) NOT NULL COMMENT '香型分类',
`price` decimal(10,2) NOT NULL,
`alcohol_degree` decimal(3,1) COMMENT '酒精度数',
`sales` int DEFAULT 0 COMMENT '销量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `user_behavior` (
`user_id` int NOT NULL,
`product_id` int NOT NULL,
`behavior_type` tinyint NOT NULL COMMENT '1-浏览 2-收藏 3-购买',
`create_time` datetime NOT NULL,
KEY `idx_user_product` (`user_id`,`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
特别注意:用户行为表是协同过滤算法的数据基础,需要记录完整的用户操作流水
3. 协同过滤算法实现
3.1 算法核心逻辑
基于用户的协同过滤(UserCF)实现步骤:
- 构建用户-物品矩阵:
java复制// 从数据库加载用户行为数据
List<UserBehavior> behaviors = userBehaviorMapper.selectList(
new QueryWrapper<UserBehavior>()
.select("user_id", "product_id", "behavior_type")
);
// 构建评分矩阵(行为加权)
Map<Integer, Map<Integer, Double>> userItemMatrix = new HashMap<>();
behaviors.forEach(behavior -> {
double score = behavior.getBehaviorType() * 0.3; // 浏览0.3,收藏0.6,购买1.0
userItemMatrix.computeIfAbsent(behavior.getUserId(), k -> new HashMap<>())
.merge(behavior.getProductId(), score, Double::sum);
});
- 计算用户相似度(余弦相似度):
java复制public double cosineSimilarity(Map<Integer, Double> user1, Map<Integer, Double> user2) {
double dotProduct = 0.0;
double norm1 = 0.0;
double norm2 = 0.0;
// 计算交集物品
Set<Integer> commonItems = new HashSet<>(user1.keySet());
commonItems.retainAll(user2.keySet());
for (Integer itemId : commonItems) {
dotProduct += user1.get(itemId) * user2.get(itemId);
}
for (double value : user1.values()) {
norm1 += Math.pow(value, 2);
}
for (double value : user2.values()) {
norm2 += Math.pow(value, 2);
}
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
3.2 性能优化实践
原始算法在用户量较大时会出现性能问题,我们通过以下方式优化:
- 相似度预计算:每天凌晨定时任务计算用户相似度矩阵
- 最近邻缓存:为每个用户缓存TOP 20相似用户
- 推荐结果预生成:结合缓存生成推荐结果存入Redis
java复制@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
public void precomputeRecommendations() {
List<Integer> allUserIds = userMapper.selectAllIds();
allUserIds.parallelStream().forEach(userId -> {
Map<Integer, Double> neighbors = findTopKSimilarUsers(userId, 20);
List<Integer> recommendations = generateRecommendations(userId, neighbors);
redisTemplate.opsForValue().set("rec:"+userId,
JSON.toJSONString(recommendations));
});
}
4. 前后端关键交互实现
4.1 商品列表页接口
SpringBoot后端接口示例:
java复制@GetMapping("/products")
public Result<List<ProductVO>> getProductList(
@RequestParam(required = false) String category,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
Page<Product> pageInfo = new Page<>(page, size);
LambdaQueryWrapper<Product> query = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(category)) {
query.eq(Product::getCategory, category);
}
query.orderByDesc(Product::getSales);
IPage<Product> productPage = productService.page(pageInfo, query);
List<ProductVO> voList = productPage.getRecords().stream()
.map(this::convertToVO)
.collect(Collectors.toList());
return Result.success(voList)
.setTotal(productPage.getTotal());
}
Vue前端调用示例:
javascript复制export default {
data() {
return {
products: [],
loading: false,
pagination: {
page: 1,
pageSize: 10,
total: 0
}
}
},
methods: {
async loadProducts() {
this.loading = true;
try {
const res = await this.$http.get('/products', {
params: {
page: this.pagination.page,
size: this.pagination.pageSize
}
});
this.products = res.data;
this.pagination.total = res.total;
} finally {
this.loading = false;
}
}
}
}
4.2 推荐接口的特殊处理
推荐结果需要结合实时行为和预计算数据:
java复制@GetMapping("/recommend/{userId}")
public Result<List<ProductVO>> getRecommendations(
@PathVariable Integer userId,
@RequestParam(defaultValue = "10") int size) {
// 1. 尝试从Redis获取预计算结果
String cacheKey = "rec:" + userId;
String cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
List<Integer> productIds = JSON.parseArray(cached, Integer.class);
List<Product> products = productService.listByIds(productIds);
return Result.success(products.stream()
.map(this::convertToVO)
.limit(size)
.collect(Collectors.toList()));
}
// 2. 实时计算兜底
List<Integer> recommendations = cfService.realTimeRecommend(userId, size);
return Result.success(recommendations.stream()
.map(id -> convertToVO(productService.getById(id)))
.collect(Collectors.toList()));
}
5. 部署与上线注意事项
5.1 多环境配置
SpringBoot的application.yml配置示例:
yaml复制spring:
profiles:
active: @profileActive@ # maven打包时替换
---
# 开发环境
spring:
profiles: dev
datasource:
url: jdbc:mysql://localhost:3306/qiandui_dev
username: dev_user
password: dev123
---
# 生产环境
spring:
profiles: prod
datasource:
url: jdbc:mysql://prod-db:3306/qiandui_prod
username: ${DB_USER}
password: ${DB_PASSWORD}
redis:
host: redis-master
5.2 前端部署优化
Vue项目打包时需要:
- 配置生产环境API地址
js复制// .env.production
VUE_APP_API_BASE_URL=https://api.qiandui.com
- 启用Gzip压缩(nginx配置示例):
nginx复制server {
listen 80;
server_name qiandui.com;
gzip on;
gzip_types text/plain application/xml application/javascript;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://springboot:8080;
}
}
6. 论文写作要点建议
毕业设计论文应包含以下核心章节:
-
系统需求分析
- 白酒行业电商平台的业务特点
- 用户角色与用例分析
-
关键技术选型
- SpringBoot vs 传统SSM框架的对比
- 协同过滤算法的多种实现方案比较
-
推荐算法详细设计
- 相似度计算方法的数学推导
- 算法评估指标(准确率、召回率)
-
系统测试方案
- JMeter压力测试报告
- 推荐效果A/B测试设计
论文写作技巧:多用图表展示系统架构和算法流程,测试数据要包含量化指标对比
7. 项目扩展方向
完成基础功能后,可以考虑以下增强点:
- 多算法融合:结合内容推荐(基于酒品特征)和协同过滤
- 实时推荐:使用Flink处理用户实时行为流
- 销量预测:基于时间序列分析预测热门商品
- 微信小程序端:拓展移动端销售渠道
我在开发过程中发现,加入简单的销量预测功能能显著提升系统完整度。可以通过MySQL窗口函数实现基础版本:
sql复制-- 计算各商品周销量增长率
SELECT
product_id,
AVG(sales) OVER (
PARTITION BY product_id
ORDER BY week_date
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
) as moving_avg
FROM product_weekly_sales;
