简介:这是一套面向计算机专业本科生的校园二手交易平台微信小程序毕业设计资源,专为毕设开发、课程设计及项目实战练习打造,解决学生缺乏完整可运行全栈案例的痛点。资源包含789个文件,涵盖287个JS逻辑脚本、80个Java后端接口、29个WXML页面结构、30个WXSS样式文件、34个JSON配置及1个SQL数据库脚本,前端基于原生小程序框架,后端采用Java实现,支持商品发布、搜索、收藏、订单管理等核心功能,结构清晰、模块解耦。压缩包仅8.48MB,轻量易部署,所有代码经严格调试,可直接导入微信开发者工具与后端环境运行。目前已有263人学习下载,配套项目说明文档详述技术栈、部署步骤与目录结构,特别适合零基础入门到毕设交付的全流程实践需求。
1. 项目概述与核心价值
最近几年,微信小程序在校园场景下的应用可以说是遍地开花,而“校园二手交易平台”绝对是其中最经典、也最实用的毕业设计选题之一。它不像一些纯理论的课题那么空洞,而是实实在在地解决了一个高频刚需——学生群体对闲置物品流转的巨大需求。从教材、数码产品到生活用品,几乎每个学生都有“买二手”和“卖二手”的经历。这个选题之所以经久不衰,是因为它完美融合了前端交互、后端逻辑、数据库设计以及产品运营思维,是一个检验计算机专业学生综合能力的绝佳试金石。
我当年带学生做这个项目,以及后来在行业内看到的各种变体,核心逻辑其实非常清晰: 利用微信小程序的便捷性,构建一个轻量、可信、高效的本地化C2C交易闭环 。它不仅仅是完成一个“增删改查”的作业,更需要你思考如何设计商品展示流程、如何构建用户信任体系(比如实名认证、聊天沟通)、如何处理订单状态流转等实际业务问题。对于毕业生来说,能把这样一个涵盖完整业务流程的系统做出来、讲清楚,你的毕业设计就已经成功了一大半。下面,我就结合一个典型的实现方案,为你深度拆解从设计思路到代码落地的全过程,并提供大量实操中才会遇到的“坑”和技巧。
2. 整体系统架构与设计思路拆解
2.1 为什么选择微信小程序作为载体?
首先得明白,为什么不用原生App或者H5网站,而偏偏是微信小程序?这背后有四个关键考量点,也是你答辩时老师可能会问到的。
用户获取成本极低 :学生无需下载安装,扫一扫或搜一下即可使用,这极大地降低了平台冷启动的难度。在校园场景下,通过班级群、海报二维码可以瞬间触达大量潜在用户。
开发效率与性能平衡 :小程序的开发框架(如微信原生框架、uni-app、Taro)对于前端开发者相对友好,一套代码可适配iOS和Android。虽然有一定限制,但其提供的原生组件(如地图、富文本、相机)性能表现不错,能满足二手交易的核心交互需求。
生态赋能 :微信提供了完整的用户身份体系(微信登录)、支付能力(微信支付)、消息订阅(交易状态通知)以及社交传播能力(分享给好友、朋友圈)。你不需要从零开始构建用户系统,直接复用微信的生态能力,这是巨大的优势。
符合使用场景 :二手交易并非高频持续使用,而是“即用即走”的典型场景。学生想卖东西时打开发布,想买东西时浏览一下,小程序的形式完美契合这种间歇性的使用习惯。
基于以上几点,选择小程序是技术方案与产品需求的最佳结合点。
2.2 核心业务流程与功能模块设计
一个完整的校园二手交易平台,其核心业务流程可以抽象为一条主线: 用户发布商品 -> 潜在买家浏览/搜索 -> 双方沟通 -> 达成交易 -> 订单履约与评价 。
围绕这条主线,我们需要设计以下核心功能模块:
- 用户模块 :微信一键登录、用户个人信息管理(昵称、头像、校内身份认证如学号/校园卡绑定)、我的发布、我的购买、我的收藏、聊天记录。
- 商品模块 :商品发布(标题、描述、多图上传、分类、价格、位置)、商品列表展示(瀑布流/列表)、商品搜索与筛选(按分类、价格区间、关键词、距离)、商品详情页。
- 交易沟通模块 :集成即时通讯功能,允许买家在商品页联系卖家。这里通常有两种实现方式:一是利用小程序提供的客服消息,二是自己基于WebSocket或第三方SDK搭建简单的聊天功能。对于毕业设计,前者更简单合规。
- 订单模块 :生成订单(包含商品信息、价格、双方信息)、订单状态管理(待付款、待发货、待收货、已完成、已取消)、在线支付集成(微信支付沙箱环境演示)。
- 社区与信任模块 :这是提升平台活力的关键。包括用户评价系统、举报反馈功能、热门商品推荐、基于位置的“附近二手”等。
2.3 技术栈选型背后的思考
技术选型没有绝对的对错,只有是否适合。对于一个毕业设计级别的项目,我们的目标是 清晰、稳健、易于演示和答辩 。
-
前端(小程序端) :
- 方案一:微信小程序原生开发 。这是最稳妥、兼容性最好的选择。使用WXML、WXSS、JavaScript和微信提供的API。优势是文档齐全,遇到问题社区资源丰富,能用到所有最新能力。对于初学者,我强烈推荐从这个开始。
- 方案二:uni-app或Taro等多端框架 。如果你的项目要求同时输出H5或App,可以考虑。但会引入一定的学习成本和框架特有的问题。对于纯小程序毕业设计,有点“杀鸡用牛刀”,增加了不必要的复杂度。
- 实操心得 :坚持使用原生开发。它能让你最深刻地理解小程序的工作原理,答辩时也能更自信地讲解技术细节。那些多端框架的“坑”,可能在答辩演示时给你带来意外。
-
后端与服务端 :
- 方案一:Node.js + Express/Koa 。JavaScript全栈,上下文切换成本低。适合对JS熟悉的学生。生态活跃,中间件丰富。
- 方案二:Java + Spring Boot 。企业级应用主流,结构严谨,答辩时显得“功底扎实”。但配置稍显繁琐。
- 方案三:Python + Django/Flask 。开发速度快,语法简洁。特别是在做数据分析或推荐算法原型时,Python有优势。
- 方案四:PHP + ThinkPHP/Laravel 。传统Web开发利器,部署简单。
- 我的建议 :选择你最熟悉的语言!毕业设计的核心是展示你对系统设计的理解和实现能力,而不是炫技。用你最擅长的工具,把业务逻辑写扎实。我这里以 Node.js + Koa 为例进行后续讲解,因为它轻量、异步特性适合IO密集的网络应用,且与JavaScript前端呼应。
-
数据库 :
- 关系型数据库(MySQL/MariaDB) :绝对是首选。交易平台的数据关系明确(用户-商品-订单),需要事务支持(如支付扣款)、复杂查询(多条件筛选)。MySQL成熟稳定,是学习数据库设计的最佳实践。
- 为什么不用MongoDB? 虽然文档型数据库灵活,但对于需要强一致性和复杂关联查询的交易系统,关系型数据库的优势更明显。学号、订单号这类严格唯一的数据,用MySQL的约束来保证更让人放心。
-
云服务与部署 :
- 对于毕业设计,我推荐使用 微信云开发 或国内主流的云服务器厂商(如阿里云、腾讯云)的学生优惠套餐。
- 微信云开发 :最大优点是省去了服务器运维和域名HTTPS配置的麻烦,内置了数据库、存储和云函数,与小程序无缝集成。非常适合快速原型验证和毕业设计演示。
- 传统云服务器(ECS) :需要自己配置Node.js/Python环境、安装MySQL、配置Nginx反向代理和SSL证书。过程更完整,能体现你的运维能力,但耗时较长。
- 折中方案 :后端API部署在云服务器,数据库使用云厂商提供的RDS服务,文件存储用OSS。这样既体验了完整流程,又降低了部分运维难度。
3. 数据库设计的核心细节与避坑指南
数据库设计是整个系统的基石,设计得好,后期开发顺风顺水;设计得差,改表改到怀疑人生。这里给出一个经过精简但核心字段完整的ER模型。
3.1 核心表结构设计
用户表 ( user ) 这是所有业务的基础。除了微信返回的 openid ,我们强烈建议添加校内身份标识。
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`openid` varchar(100) NOT NULL UNIQUE COMMENT '微信用户唯一标识',
`nickname` varchar(100) DEFAULT NULL COMMENT '微信昵称',
`avatar_url` varchar(500) DEFAULT NULL COMMENT '微信头像',
`student_id` varchar(20) DEFAULT NULL UNIQUE COMMENT '学号(用于校内认证)',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`credit_score` int(11) DEFAULT 100 COMMENT '信用分,初始100',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
注意 :
openid和student_id都加了UNIQUE约束,防止重复。credit_score是为未来构建信任体系预留的字段,你可以设计规则,如交易成功加分,被举报扣分。
商品表 ( product ) 这是最核心的业务表,字段设计要兼顾展示需求和查询效率。
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`seller_id` int(11) NOT NULL COMMENT '卖家ID,关联user.id',
`title` varchar(100) NOT NULL COMMENT '商品标题',
`description` text COMMENT '商品详细描述',
`category_id` int(11) NOT NULL COMMENT '分类ID',
`price` decimal(10,2) NOT NULL COMMENT '价格',
`original_price` decimal(10,2) DEFAULT NULL COMMENT '原价',
`status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '状态:1-出售中,2-已售出,3-已下架',
`view_count` int(11) DEFAULT 0 COMMENT '浏览量',
`like_count` int(11) DEFAULT 0 COMMENT '点赞/收藏数',
`location` varchar(200) DEFAULT NULL COMMENT '交易地点,如“宿舍楼A区”',
`latitude` decimal(10,7) DEFAULT NULL COMMENT '纬度,用于地图显示',
`longitude` decimal(10,7) DEFAULT NULL COMMENT '经度,用于地图显示',
`is_recommended` tinyint(1) DEFAULT 0 COMMENT '是否推荐',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_seller` (`seller_id`),
KEY `idx_category` (`category_id`),
KEY `idx_status_time` (`status`, `create_time`) -- 复合索引,用于首页查询
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
实操心得 :
- 价格字段务必用
decimal,避免浮点数计算精度问题。status字段使用tinyint,并用数字代表状态,比用字符串(如‘selling’)查询效率更高。- 建立了
idx_status_time复合索引。因为首页查询最常用的SQL就是SELECT * FROM product WHERE status = 1 ORDER BY create_time DESC LIMIT 20。这个索引能极大提升查询速度。location和latitude/longitude分开存储。前者用于文本展示,后者用于基于地理位置的距离计算或附近推荐。
商品图片表 ( product_image ) 为什么要把图片单独存一张表?因为一个商品对应多张图片,这是典型的一对多关系。拆分开更符合数据库设计范式,也便于管理。
CREATE TABLE `product_image` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL COMMENT '商品ID',
`image_url` varchar(500) NOT NULL COMMENT '图片URL(存储在OSS或云存储)',
`sort_order` int(11) DEFAULT 0 COMMENT '排序,0表示首图',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品图片表';
订单表 ( order ) 订单是交易的法律凭证,设计必须严谨。状态流转是核心。
CREATE TABLE `order` (
`id` varchar(32) NOT NULL COMMENT '订单号,使用自定义规则生成,如时间戳+随机数',
`buyer_id` int(11) NOT NULL COMMENT '买家ID',
`seller_id` int(11) NOT NULL COMMENT '卖家ID',
`product_id` int(11) NOT NULL COMMENT '商品ID',
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
`status` tinyint(4) NOT NULL DEFAULT 10 COMMENT '订单状态:10-待付款,20-已付款/待发货,30-已发货/待收货,40-交易成功,50-交易关闭',
`payment_time` datetime DEFAULT NULL COMMENT '付款时间',
`delivery_info` varchar(500) DEFAULT NULL COMMENT '发货/交付信息(如见面地点、快递单号)',
`close_reason` varchar(200) DEFAULT NULL COMMENT '交易关闭原因',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_buyer` (`buyer_id`),
KEY `idx_seller` (`seller_id`),
KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
重要提示 :订单号
id不要用自增主键!使用有业务含义的、唯一的、非连续的字符串(如20240520123456+随机码)。自增ID会暴露平台订单总量,且在某些场景下(如分库分表)可能不适用。
3.2 数据库设计中的常见“坑”与优化技巧
- 字符集与排序规则 :一定要用
utf8mb4,而不是utf8。utf8在MySQL中是一个“阉割版”,无法存储完整的4字节UTF-8字符(如一些emoji表情)。utf8mb4才是真正的全支持。 - 字段长度预估 :像
title、location这类字段,长度要给得合理且充裕,但也不要盲目给varchar(255)。根据业务预估,title给100-200,location给200通常足够。过长的字段会影响内存临时表的大小。 - 索引不是越多越好 :索引能加速查询,但会降低写入速度并占用空间。只为 高频查询条件 和 排序字段 建立索引。例如,商品表按分类和状态查询很频繁,那么
(category_id, status)就是一个好的复合索引候选。 - 事务的使用场景 :在“创建订单并扣减库存”或“支付回调更新订单状态并通知买卖双方”这类需要多个操作要么全部成功、要么全部失败的关键流程中,务必使用数据库事务。在Koa中,可以结合
koa-mysql-session或sequelize等ORM库的事务功能来实现。 - 软删除还是硬删除? 对于用户、商品、订单等重要数据,建议采用“软删除”。即增加一个
is_deleted字段(默认为0),删除时将其置为1。这样数据可追溯,避免误操作。查询时默认加上WHERE is_deleted = 0的条件。
4. 后端API接口设计与关键实现
后端API是小程序前端与数据库之间的桥梁,设计要遵循RESTful风格,清晰、规范。
4.1 核心API列表与设计规范
我们使用Koa框架,并搭配 koa-router 、 koa-bodyparser 、 jsonwebtoken (JWT)等中间件。
- 用户相关 :
-
POST /api/auth/login:微信登录,用code换openid和session_key,生成自定义登录态(如JWT Token)返回给小程序。 -
GET /api/user/profile:获取当前用户详细信息。 -
POST /api/user/profile:更新用户信息(如绑定学号)。
-
- 商品相关 :
-
GET /api/products:获取商品列表,支持分页、分类筛选、排序、关键词搜索。 -
GET /api/products/:id:获取商品详情。 -
POST /api/products:发布新商品(需要身份认证)。 -
PUT /api/products/:id:更新商品信息。 -
DELETE /api/products/:id:下架商品。
-
- 订单相关 :
-
POST /api/orders:创建订单。 -
GET /api/orders:获取我的订单列表(买家或卖家视角)。 -
GET /api/orders/:id:获取订单详情。 -
PUT /api/orders/:id/status:更新订单状态(如买家确认收货)。 -
POST /api/orders/:id/pay:模拟支付或调用微信支付统一下单接口。
-
4.2 以“发布商品”接口为例的深度实现
这个接口涵盖了文件上传、数据验证、数据库事务等多个要点。
1. 路由与控制器 (controller/product.js)
const Router = require('koa-router');
const router = new Router({ prefix: '/api/products' });
const { Product, ProductImage } = require('../models'); // 假设使用Sequelize ORM
const { auth } = require('../middleware/auth'); // JWT认证中间件
const upload = require('../middleware/upload'); // 文件上传中间件,如koa-multer
// 发布商品
router.post('/', auth, upload.array('images', 5), async (ctx) => { // 限制最多5张图
const userId = ctx.state.user.id; // 从JWT中取出用户ID
const { title, description, categoryId, price, originalPrice, location, latitude, longitude } = ctx.request.body;
const images = ctx.request.files; // 上传的图片文件数组
// 1. 参数校验 (使用Joi或类似库更规范)
if (!title || !categoryId || !price) {
ctx.status = 400;
ctx.body = { code: 40001, message: '标题、分类和价格不能为空' };
return;
}
if (images && images.length > 5) {
ctx.status = 400;
ctx.body = { code: 40002, message: '最多上传5张图片' };
return;
}
// 2. 开启数据库事务
const transaction = await sequelize.transaction();
try {
// 3. 创建商品记录
const product = await Product.create({
seller_id: userId,
title,
description,
category_id: categoryId,
price,
original_price: originalPrice,
location,
latitude,
longitude,
status: 1
}, { transaction });
// 4. 处理图片上传与记录
if (images && images.length > 0) {
const imagePromises = images.map((file, index) => {
// 假设有一个uploadToOSS函数将文件上传到云存储并返回URL
return uploadToOSS(file).then(url => {
return ProductImage.create({
product_id: product.id,
image_url: url,
sort_order: index
}, { transaction });
});
});
await Promise.all(imagePromises);
}
// 5. 提交事务
await transaction.commit();
ctx.status = 201;
ctx.body = {
code: 0,
message: '发布成功',
data: { productId: product.id }
};
} catch (error) {
// 6. 出错则回滚事务
await transaction.rollback();
console.error('发布商品失败:', error);
ctx.status = 500;
ctx.body = { code: 50001, message: '服务器内部错误,发布失败' };
}
});
module.exports = router;
2. 文件上传中间件 (middleware/upload.js)
const multer = require('@koa/multer');
const path = require('path');
const fs = require('fs');
// 确保上传目录存在
const uploadDir = 'public/uploads';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, uploadDir);
},
filename: function (req, file, cb) {
// 生成唯一文件名,防止覆盖
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
});
// 文件过滤器:只允许图片
const fileFilter = (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb(new Error('只允许上传图片文件(jpeg, jpg, png, gif)'));
}
};
const upload = multer({
storage: storage,
limits: { fileSize: 5 * 1024 * 1024 }, // 限制5MB
fileFilter: fileFilter
});
module.exports = upload;
注意事项 :上述代码将文件保存在服务器本地
public/uploads目录。在生产环境中, 强烈建议直接上传到云存储(如阿里云OSS、腾讯云COS) 。因为服务器本地存储有容量限制、备份困难,且无法直接通过CDN加速访问。流程应改为:小程序前端将图片临时上传到你的后端,后端再将文件流上传到OSS,获取到URL后存入数据库,最后删除本地临时文件。
4.3 全局错误处理与响应封装
一个健壮的后端必须有统一的错误处理和响应格式。
// middleware/errorHandler.js
module.exports = async (ctx, next) => {
try {
await next();
} catch (err) {
console.error('Server Error:', err);
ctx.status = err.status || 500;
ctx.body = {
code: err.code || 50000,
message: err.message || 'Internal Server Error',
// 在开发环境下可以返回堆栈信息,生产环境不要返回
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
};
}
};
// middleware/responseFormatter.js
module.exports = async (ctx, next) => {
await next();
// 如果ctx.body已经被设置(比如错误处理中间件设置的),则不再处理
if (ctx.body && ctx.body.code !== undefined) return;
// 成功响应格式化
if (ctx.status >= 200 && ctx.status < 300) {
ctx.body = {
code: 0,
message: 'success',
data: ctx.body
};
}
};
然后在主入口文件 app.js 中按顺序使用:
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const errorHandler = require('./middleware/errorHandler');
const responseFormatter = require('./middleware/responseFormatter');
const app = new Koa();
app.use(errorHandler);
app.use(bodyParser());
// ... 其他中间件,如cors, helmet等
app.use(responseFormatter); // 响应格式化放在路由之后
// ... 加载路由
app.listen(3000);
5. 微信小程序前端关键功能实现
5.1 用户登录与状态管理
小程序前端第一关就是登录。微信提供了 wx.login() 获取临时code,你需要将这个code发送到自己的后端,后端用 code 、 appid 和 secret 去微信服务器换 openid 和 session_key 。
登录流程封装 :
// utils/auth.js
const login = () => {
return new Promise((resolve, reject) => {
wx.login({
success: async (res) => {
if (res.code) {
try {
// 调用自己的后端登录接口
const loginRes = await wx.request({
url: 'https://your-domain.com/api/auth/login',
method: 'POST',
data: { code: res.code }
});
if (loginRes.data.code === 0) {
const { token, userInfo } = loginRes.data.data;
// 将token存储到本地缓存
wx.setStorageSync('token', token);
wx.setStorageSync('userInfo', userInfo);
resolve(userInfo);
} else {
reject(new Error(loginRes.data.message));
}
} catch (err) {
reject(err);
}
} else {
reject(new Error('登录失败:' + res.errMsg));
}
},
fail: reject
});
});
};
// 封装带认证的request
const request = (options) => {
const token = wx.getStorageSync('token');
const header = { ...options.header };
if (token) {
header['Authorization'] = `Bearer ${token}`; // JWT标准格式
}
return new Promise((resolve, reject) => {
wx.request({
...options,
header,
success: (res) => {
if (res.statusCode === 401) {
// token过期,清空缓存,跳转到登录页
wx.removeStorageSync('token');
wx.removeStorageSync('userInfo');
wx.showToast({ title: '登录已过期', icon: 'none' });
// 可以在这里触发全局重新登录
setTimeout(() => wx.reLaunch({ url: '/pages/login/login' }), 1500);
reject(new Error('Unauthorized'));
return;
}
resolve(res.data);
},
fail: reject
});
});
};
export { login, request };
5.2 商品列表页的优化实现
列表页是流量入口,性能体验至关重要。核心是 分页加载 和 图片懒加载 。
分页加载逻辑 :
// pages/index/index.js
Page({
data: {
productList: [],
page: 1,
pageSize: 10,
hasMore: true,
isLoading: false
},
onLoad() {
this.loadProducts();
},
// 滚动触底加载更多
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.loadProducts();
}
},
async loadProducts() {
if (this.data.isLoading) return;
this.setData({ isLoading: true });
wx.showLoading({ title: '加载中...' });
try {
const { page, pageSize } = this.data;
const res = await request({
url: '/api/products',
method: 'GET',
data: { page, pageSize, status: 1 } // 只查询出售中的商品
});
if (res.code === 0) {
const newList = res.data.list || [];
const total = res.data.total || 0;
const allList = this.data.productList.concat(newList);
this.setData({
productList: allList,
page: page + 1,
hasMore: allList.length < total,
isLoading: false
});
}
} catch (err) {
console.error('加载商品失败', err);
wx.showToast({ title: '加载失败', icon: 'error' });
this.setData({ isLoading: false });
} finally {
wx.hideLoading();
}
},
// 图片懒加载:使用小程序自带的 lazy-load 属性
// 在wxml中:<image src="{{item.cover}}" mode="aspectFill" lazy-load />
})
下拉刷新 :
// 在Page中增加
onPullDownRefresh() {
// 重置数据
this.setData({
productList: [],
page: 1,
hasMore: true
});
// 重新加载
this.loadProducts().finally(() => {
wx.stopPullDownRefresh(); // 停止下拉刷新动画
});
}
5.3 商品发布页的多图上传
这是前端交互的一个难点,需要处理图片选择、预览、删除和上传。
// pages/publish/publish.js
Page({
data: {
imageList: [], // 已选择的图片临时路径数组
uploading: false
},
// 选择图片
chooseImage() {
const maxCount = 5 - this.data.imageList.length;
if (maxCount <= 0) {
wx.showToast({ title: '最多上传5张图片', icon: 'none' });
return;
}
wx.chooseMedia({
count: maxCount,
mediaType: ['image'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFiles = res.tempFiles.map(file => file.tempFilePath);
this.setData({
imageList: this.data.imageList.concat(tempFiles)
});
}
});
},
// 删除图片
removeImage(e) {
const index = e.currentTarget.dataset.index;
const newList = [...this.data.imageList];
newList.splice(index, 1);
this.setData({ imageList: newList });
},
// 上传表单
async submitForm(e) {
const formData = e.detail.value; // 获取表单其他字段
if (this.data.imageList.length === 0) {
wx.showToast({ title: '请至少上传一张图片', icon: 'none' });
return;
}
this.setData({ uploading: true });
wx.showLoading({ title: '发布中...' });
// 1. 先上传图片到后端,获取URL
const imageUrls = [];
for (const tempFilePath of this.data.imageList) {
try {
const uploadRes = await this.uploadFile(tempFilePath);
if (uploadRes.code === 0) {
imageUrls.push(uploadRes.data.url);
} else {
throw new Error('图片上传失败');
}
} catch (err) {
wx.hideLoading();
wx.showToast({ title: '图片上传失败', icon: 'error' });
this.setData({ uploading: false });
return;
}
}
// 2. 再提交商品信息(包含图片URL数组)
try {
const submitData = { ...formData, images: imageUrls };
const res = await request({
url: '/api/products',
method: 'POST',
data: submitData
});
if (res.code === 0) {
wx.showToast({ title: '发布成功' });
setTimeout(() => wx.navigateBack(), 1500);
}
} catch (err) {
wx.showToast({ title: '发布失败', icon: 'error' });
} finally {
this.setData({ uploading: false });
wx.hideLoading();
}
},
// 封装文件上传
uploadFile(filePath) {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: 'https://your-domain.com/api/upload', // 你的文件上传接口
filePath: filePath,
name: 'file',
formData: { type: 'product' },
success: (res) => {
const data = JSON.parse(res.data);
resolve(data);
},
fail: reject
});
});
}
});
6. 部署、测试与毕业设计答辩要点
6.1 本地开发与真机调试
- 后端 :使用
nodemon监听文件变化自动重启。npm install -g nodemon,然后在项目目录下nodemon app.js。 - 数据库 :本地安装MySQL,使用
Navicat或MySQL Workbench等工具管理。务必导出数据库结构脚本(mysqldump -u root -p --no-data database_name > schema.sql),这是毕业设计文档的重要组成部分。 - 小程序前端 :在微信开发者工具中,将“不校验合法域名”勾选上,以便连接本地后端API(如
http://localhost:3000)。真机调试时,需要确保手机和电脑在同一局域网,且后端服务运行在本地IP上(如http://192.168.1.100:3000)。
6.2 服务器部署(以CentOS 7 + Nginx为例)
- 环境准备 :在云服务器上安装Node.js、PM2(进程管理)、MySQL、Nginx。
- 上传代码 :使用Git克隆或FTP上传你的后端代码到服务器,例如
/var/www/二手平台。 - 安装依赖 :进入项目目录,运行
npm install --production。 - 配置MySQL :创建生产环境数据库,导入你的
schema.sql。 切勿使用root用户直接连接 ,创建一个专用用户并赋予最小必要权限。 - 配置环境变量 :使用
dotenv或直接修改配置文件,设置数据库连接信息、微信小程序AppSecret等敏感信息。 - 使用PM2启动 :
pm2 start app.js --name second-hand-api。PM2可以保证进程崩溃后自动重启,并管理日志。 - 配置Nginx反向代理 :
server { listen 80; server_name your-domain.com; # 你的域名 location / { proxy_pass http://localhost:3000; # 转发到Node.js应用 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 静态文件服务(如果你有上传到本地的图片) location /uploads/ { alias /var/www/second-hand-api/public/uploads/; } } - 配置HTTPS :申请免费SSL证书(如Let‘s Encrypt),在Nginx中配置,将HTTP重定向到HTTPS。小程序要求后端API必须为HTTPS。
- 配置微信小程序后台 :在微信公众平台,将你的服务器域名(如
https://your-domain.com)添加到“request合法域名”列表中。
6.3 毕业设计答辩核心准备
答辩不仅仅是演示功能,更是展示你的思考、设计和解决问题的能力。
-
演示准备 :
- 准备两套环境 :一套是本地开发环境,用于展示代码和运行;另一套是部署在公网的演示环境,用于手机扫码真机体验。确保演示环境稳定。
- 设计演示脚本 :按照“用户注册/登录 -> 浏览商品 -> 发布商品 -> 沟通咨询 -> 下单支付 -> 确认收货评价”的完整用户路径来演示。重点展示你的核心业务逻辑和亮点功能(如搜索筛选、聊天、支付流程)。
- 准备测试数据 :提前录入丰富、真实的商品数据,避免演示时页面空空如也。
-
文档与陈述 :
- 系统架构图 :用清晰的框图展示前端、后端、数据库、云服务之间的关系。
- 数据库ER图 :展示核心表及其关系,解释关键字段的设计原因。
- 核心代码讲解 :准备1-2个最复杂或最具代表性的代码片段(如发布商品的事务处理、微信登录流程),解释你的实现思路和遇到的挑战。
- 遇到的问题与解决方案 :这是加分项。主动提及你在开发中遇到的技术难点(如并发问题、图片上传优化、支付回调处理),以及你是如何排查和解决的。这体现了你的工程能力。
- 系统优化与展望 :谈谈如果时间充裕,你会从哪些方面优化(如引入Redis缓存热门商品、使用Elasticsearch实现全文搜索、增加推荐算法等)。这展示了你的技术视野。
-
问答环节预判 :
- 为什么选择这个技术栈? (结合项目特点和个人技术储备回答)
- 如何保证交易安全? (从用户认证、通信HTTPS、支付流程、防SQL注入/XSS等方面回答)
- 如果用户量增大,系统瓶颈可能在哪里?如何扩展? (可能是数据库连接数、单机性能,可以谈读写分离、分库分表、微服务化等方向)
- 你的平台和闲鱼有什么区别?校园特色体现在哪里? (强调校内认证、地理位置近、信任度高、物品更贴合学生需求等)
记住,答辩老师最想看到的是你 独立思考、解决问题和将理论应用于实践的能力 。一个运行稳定、设计合理、代码清晰、讲解透彻的校园二手交易平台小程序,绝对能为你赢得一个优秀的毕业设计成绩。
298




被折叠的 条评论
为什么被折叠?



