该项目开始是要求我们使用JavaWeb(java+jsp+servlet+MySQL+jdbc+css+js+jQuery)实现,但我学过一丢丢的框架,就改用了SpringBoot+Vue实现。
注意!!!!!代码中的serverIp我设置的我服务器的IP,所以不在服务器上面应该是localhost!!!!!!!!
在线商城项目演示视频:https://www.bilibili.com/video/BV1kY41117DH/
目录
1. 产品介绍
2. 产品面向的用户群体
3. 产品的范围
4. 产品中的角色
5. 产品的功能需求
5.1 功能性需求分类
5.2功能层次结构图
6. 产品的非功能性需求
6.1 用户界面需求
6.2 软硬件环境需求
6.3 产品质量需求
数据库设计:
前台页面:
在线商城首页:
编辑
首页Home.vue源代码:
商品详情页:
编辑源代码Detail.Vue
我的购物车:
我的订单:
返回数据:
个人信息:
源代码Person.vue
联系商家:
后台页面:
核心代码:
1.UserController
2.TokenUtils
3.CartController
4.EchartsController
5.FileController
6.MybatisPlusConfig
7.Constants
1. 产品介绍
对于网上商城,其最大好处是要能给用户带来最大的便捷,这种便捷不仅体现在网络之外的物流、商品的折扣等,更要体现在进行网络操作时的易用性,能够模拟用户的购物行为,营造一种尽量真实、贴切的用户购物过程。所以,在设计网络商城时,最重要的就是完成“用户功能”。其次,对众多商品、订单、用户信息的网络管理,对于网站经营者的经营效率的意义,也是不言而喻的,这些则可以称为“管理功能”。
2. 产品面向的用户群体
本系统主要面向系统管理员和普通用户。
(1)系统管理员:订单管理、用户管理、商品管理等。
(2)普通用户:主要使用的业务模块包括系统登录、注册、购买商品、查询订单。
3. 产品的范围
本项目主要分为系统设置模块、用户管理模块、商品管理模块、购买商品管理模块、订单管理模块。
4. 产品中的角色
角色名称 | 职责描述 |
普通用户 | 注册,登录,添加购物车,商品付款 |
管理员 | 登录,注册,商品信息管理,用户信息管理 |
5. 产品的功能需求
5.1 功能性需求分类
功能类别 | 功能名称 | 描述 |
用户管理 | 个人信息管理 | 管理用户的个人信息 |
用户管理 | 管理已存在用户 | |
商品管理 | 增加商品 | 对商城的商品进行添加 |
删除商品 | 删除商城的在架商品 | |
订单管理 | 订单详情管理 | 对订单进行处理,对已付款的订单进行发货,对发货的商品进行收货 |
订单支付 | 支付当前未付款的订单 | |
查看商品 | 查看该笔订单的商品信息 | |
购物车管理 | 添加购物车 | 添加商品到购物车 |
删除购物车 | 删除当前购物车内的商品 | |
商品结算 | 结算购物车的商品 |
5.2功能层次结构图
6. 产品的非功能性需求
6.1 用户界面需求
需求名称 | 详细要求 |
整体风格 | 以蓝色为主色调 |
兼容性 | 能在主流浏览器(火狐、谷歌、IE8+、360浏览器)上运行 |
6.2 软硬件环境需求
需求名称 | 详细要求 |
开发语言 | Java或.NET |
运行环境 | Jdk1.6+或.NET Framework3.5以上 |
数据库 | Mysql5.0或者SqlServer2005以上 |
操作系统 | Windows Server2008 |
6.3 产品质量需求
主要质量属性 | 详细要求 |
正确性 | 无数据计算错误,无流程错误 |
健壮性 | 程序出错后,系统能正常捕获异常,不会导致程序终止运行 |
可靠性 | 系统支持7*24无间断运行,不会因系统功能的复杂运算而导致系统崩溃 |
性能、效率 | 数据请求在0.2S内返回 |
易用性 | 功能使用,操作简单,避免繁琐的逻辑设定 |
清晰性 | 功能结果及名称清晰,避免用户误解 |
安全性 | 用户必须成功登陆后,根据权限才可使用对应的功能 |
可扩展性 | 提供良好的系统接口,支持后续功能的开发扩展 |
兼容性 | 兼容主流浏览器(火狐、谷歌、IE8+、360浏览器) |
可移植性 | 能较好部署到其他版本的Windows操作系统上 |
数据库设计:
1.1 数据库系统:
SQL Server 2008 / My SQL,服务器为MySQL8.0版本
1.2 设计工具:
Enterprise Architect
1.3连接工具:
Navicat、服务器为1核(vCPU) 2 GiB
前台页面:
在线商城首页:
首页Home.vue源代码:
在线商城首页商城官网我的购物车我的订单<!--后台管理--><!----><!--后台管理--><!--用户管理--><!--商品管理--><!--购物车管理--><!--订单管理--><!---->登录/注册<!--退出商城--><!--{{ user.nickname }}-->{{ user.nickname }}个人信息退出<!---->搜索- 手机
- 电脑
- 家装
- 医药
- 女装
- 男装
- 美妆
- 食品
手机{{item.refer}}{{item.name}}¥{{item.price}}<!----><!----><!--{{item.refer}}--><!--{{item.name}}--><!--¥{{item.price}}--><!----><!----><!----><!--{{item.refer}}--><!--{{item.name}}--><!--¥{{item.price}}--><!----><!----><!----><!--{{item.refer}}--><!--{{item.name}}--><!--¥{{item.price}}--><!---->合作伙伴
商城 | 游戏 | 政企服务 | 集团隐私政策 | 公司儿童信息保护规则 | 商城隐私政策 | 商城用户协议 | 问题反馈 | Select Location 互联网ICP备案:沪ICP备13002172号-3 沪-非经营性-2016-0143 营业性演出许可证 沪市文演(经)00-2253 | export default {name: "Home",components: {},data() {return {tableData: [],total: 0,pageNum: 1,pageSize: 10,name: "",user: localStorage.getItem("user") " /> {this.tableData = res.data.recordsthis.total = res.data.total})},home() {this.$router.push("/")},handleSizeChange(pageSize) {console.log(pageSize)this.pageSize = pageSizethis.load()},handleCurrentChange(pageNum) {console.log(pageNum)this.pageNum = pageNumthis.load()},}}li:hover {color: orangered;}.goodsName {font-size: 14px;text-align: center;cursor: pointer}.price {font-size: 16px;font-weight: bold;color: orangered;cursor: pointer;}.refer {padding: 2px;cursor: pointer;/*margin-top: 5px;*/overflow: hidden;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;}.base {margin: 0px;padding: 0px;}img {width: 100%;height: 100%;}.phone {background-color: white;padding-bottom: 10px;font-size: 16px;text-align: center;width: 160px;height: 150px;transition: all 0.8s;margin: 60px 16px;}.box~.phone{margin-left: 30px;}.box0 img{width: 170px;height: 170px;}.phone:hover{transform: scale(1.08);}
商品详情页:
源代码Detail.Vue
{{ goods.name }}{{ goods.refer }}¥{{ goods.price }}库存{{ goods.num }}台<!----> 直接购买 加入购物车<!---->联系商家import {serverIp} from "../../../public/config";const baseUrl = `http://${serverIp}:9090`export default {name: "Detail",data() {return {id: this.$route.query.id,goods: {},form: {number: 1},user: localStorage.getItem("user") " /> {this.goods = res.data})},chat() {this.$router.push("/im")},addCart() {if (!this.user.username) {this.$message.warning("请登录后操作")return}this.form.goodsId = this.goods.id//商品IDthis.request.post("/cart", this.form).then(res => {if (res.code === '200') {this.$message.success("加入购物车成功")} else {this.$message.error(res.msg)}})},buy(goodsId) {fetch(baseUrl + '/api/buy?goodsId=' + goodsId, {headers: {'Content-Type': 'application/json;charset=utf-8'},method: 'POST'}).then(res => res.json()).then(res => {if (res) {this.$message.success("下单成功")this.load()} else {this.$message.error("下单失败")}})}}}.addCar {padding: 10px;/*margin-left: 10px;*/width: 140px;height: 45px;background-color: #DF3033;color: white;font-weight: bold;font-size: 18px}.goodsName {padding: 10px;font-size: 22px;font-weight: bold;}.refer {padding: 10px;font-size: 16px;font-weight: bold}.price {height: 60px;padding: 15px 10px 10px 20px;background-color: #FCE5E5;color: orangered;font-weight: bold;margin-left: 10px;margin-top: 10px;font-size: 24px;}
我的购物车:
可以勾选商品进行计算价格,然后进行商品结算去订单页面付款,也可以选择删除购物车里面的商品。
我的订单:
可以查看自己的订单的商品,设置5s时间自动关闭。支付使用支付宝沙箱支付,对于已经支付的订单无法二次支付,即使支付也会报错,对于已支付的订单也不能进行取消。
返回数据:
支付宝进行回调信息存储到数据库,后台可以接收信息。
进行支付之后,后台可以对订单进行发货,用户在我的订单里面也可以看见订单状态是否发货,从而进行收货操作。
个人信息:
源代码Person.vue
保 存定位返回主页export default {name: "Person",data() {return {form: {},user: localStorage.getItem("user") " /> {if (res.code === '200') {this.form = res.data}})},methods: {sign() {const address = localStorage.getItem("address")const username = this.user.usernamethis.request.post("/user", { user: username, address: address }).then(res => {if (res.code === '200') {this.$message.success("获取成功")} else {this.$message.error(res.msg)}})},save() {this.request.post("/user", this.form).then(res => {if (res.data) {this.$message.success("保存成功")} else {this.$message.error("保存失败")}})},return1() {this.$router.push("/")}}}
联系商家:
此功能可以参考我的另外一篇文章:(2条消息) SpringBoot实现简易聊天室_慕言要努力的博客-CSDN博客
后台页面:
核心代码:
1.UserController
import cn.hutool.core.util.StrUtil;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.example.demo.common.Constants;import com.example.demo.common.Result;import com.example.demo.controller.dto.UserDTO;import com.example.demo.entity.User;import com.example.demo.service.IUserService;import com.example.demo.utils.TokenUtils;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import java.util.List;@CrossOrigin@RestController@RequestMapping("/user")public class UserController {@Resourceprivate IUserService userService;@PostMapping("/login")public Result login(@RequestBody UserDTO userDTO) {String username = userDTO.getUsername();String password = userDTO.getPassword();if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {return Result.error(Constants.CODE_400,"参数错误");}UserDTO dto = userService.login(userDTO);return Result.success(dto);}@PostMapping("/register")public Result register(@RequestBody UserDTO userDTO) {String username = userDTO.getUsername();String password = userDTO.getPassword();if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {return Result.error(Constants.CODE_400,"参数错误");}return Result.success(userService.register(userDTO));}//新增或者更新@PostMappingpublic Result save(@RequestBody User user) {return Result.success(userService.saveOrUpdate(user));}//删除@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id) {return Result.success(userService.removeById(id));}@PostMapping("/del/batch")public Result deleteBatch(@RequestBody List ids) {//批量删除return Result.success(userService.removeByIds(ids));}////查询所有数据//@GetMapping//public Result findAll() {//return Result.success(userService.list());//}//@GetMapping("/{id}")public Result findOne(@PathVariable Integer id) {return Result.success(userService.getById(id));}@GetMapping("/username/{username}")public Result findOne(@PathVariable String username) {QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("username", username);return Result.success(userService.getOne(queryWrapper));}@GetMapping("/page")public Result findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String username) {QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.orderByDesc("id");if (!"".equals(username)) {queryWrapper.like("username", username);}return Result.success(userService.page(new Page(pageNum, pageSize), queryWrapper));}}
2.TokenUtils
import cn.hutool.core.date.DateUtil;import cn.hutool.core.util.StrUtil;import com.auth0.jwt.JWT;import com.auth0.jwt.algorithms.Algorithm;import com.example.demo.entity.User;import com.example.demo.service.IUserService;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.PostConstruct;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import java.util.Date;@Componentpublic class TokenUtils {private static IUserService staticUserService;@Resourceprivate IUserService userService;@PostConstructpublic void setUserService() {staticUserService = userService;}/** * 生成token * * @return */public static String genToken(String userId, String sign) {return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥}/** * 获取当前登录的用户信息 * * @return user对象 */public static User getCurrentUser() {try {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token = request.getHeader("token");if (StrUtil.isNotBlank(token)) {String userId = JWT.decode(token).getAudience().get(0);return staticUserService.getById(Integer.valueOf(userId));}} catch (Exception e) {return null;}return null;}}
3.CartController
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.example.demo.common.Result;import com.example.demo.entity.Cart;import com.example.demo.entity.User;import com.example.demo.mapper.CartMapper;import com.example.demo.service.ICartService;import com.example.demo.utils.TokenUtils;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import java.util.List;@CrossOrigin@RestController@RequestMapping("/cart")public class CartController {@Resourceprivate ICartService cartService;@Resourceprivate CartMapper cartMapper;//新增或者更新@PostMappingpublic Result save(@RequestBody Cart cart) {Integer userId = TokenUtils.getCurrentUser().getId();//相同商品进行处理Integer goodsId = cart.getGoodsId();QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("goods_id", goodsId);queryWrapper.eq("user_id", userId);Cart db = cartService.getOne(queryWrapper);if (db != null) {//db.setNumber(db.getNumber() + cart.getNumber());db.setNumber(db.getNumber() + cart.getNumber());cartService.updateById(db);return Result.success();}//新增或更新if (cart.getId() == null) {cart.setUserId(userId);}cartService.saveOrUpdate(cart);return Result.success();}@PostMapping("/number/{id}/{number}")public Result updateNum(@PathVariable Integer id, @PathVariable Integer number) {cartMapper.updateNum(number, id);return Result.success();}//删除@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id) {return Result.success(cartService.removeById(id));}@PostMapping("/del/batch")public Result deleteBatch(@RequestBody List ids) {//批量删除return Result.success(cartService.removeByIds(ids));}//查询所有数据@GetMappingpublic Result findAll() {return Result.success(cartService.list());}@GetMapping("/{id}")public Result findOne(@PathVariable Integer id) {return Result.success(cartService.getById(id));}//@GetMapping("/id/{id}")//public Result findOne(@PathVariable String userId) {//QueryWrapper queryWrapper = new QueryWrapper();//queryWrapper.eq("userId", userId);//return Result.success(cartService.getOne(queryWrapper));//}@GetMapping("/page")public Result findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String name) {User currentUser = TokenUtils.getCurrentUser();Integer userId = currentUser.getId();String role = currentUser.getRole();return Result.success(cartMapper.page(new Page(pageNum, pageSize), userId, role, name));}}
4.EchartsController
import cn.hutool.core.collection.CollUtil;import cn.hutool.core.date.DateUtil;import cn.hutool.core.date.Quarter;import com.example.demo.common.Result;import com.example.demo.entity.User;import com.example.demo.service.IUserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;@RestController@RequestMapping("/echarts")public class EchartsController {@Autowiredprivate IUserService userService;@GetMapping("/example")public Result get() {Map map = new HashMap();map.put("x", CollUtil.newArrayList("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"));map.put("y", CollUtil.newArrayList(150, 230, 224, 218, 135, 147, 260));return Result.success(map);}@GetMapping("/members")public Result members() {List list = userService.list();int q1 = 0;//第一季度int q2 = 0;//第二季度int q3 = 0;//第三季度int q4 = 0;//第四季度for (User user : list) {Date createTime = user.getCreateTime();Quarter quarter = DateUtil.quarterEnum(createTime);switch (quarter) {case Q1: q1 += 1; break;case Q2: q2 += 1; break;case Q3: q3 += 1; break;case Q4: q4 += 1; break;default: break;}}return Result.success(CollUtil.newArrayList(q1, q2, q3, q4));}}
5.FileController
import cn.hutool.core.io.FileUtil;import cn.hutool.core.util.IdUtil;import cn.hutool.core.util.StrUtil;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.example.demo.entity.Files;import com.example.demo.entity.Goods;import com.example.demo.mapper.FileMapper;import com.example.demo.mapper.GoodsMapper;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.IOException;import java.net.URLEncoder;import java.util.List;@RestController@RequestMapping("/file")public class FileController {@Value("${files.upload.path}")private String fileUploadPath;@Value("${server.ip}")private String serverIp;@Resourceprivate FileMapper fileMapper;@Resourceprivate GoodsMapper goodsMapper;@PostMapping("/upload")public String upload(@RequestParam MultipartFile file) throws IOException {String originalFilename = file.getOriginalFilename();String type = FileUtil.extName(originalFilename);long size = file.getSize();//定义一个文件的唯一标识码//String uuid = IdUtil.fastSimpleUUID();//String fileUUID = uuid + StrUtil.DOT + type;//File uploadFile = new File(fileUploadPath + fileUUID);//先存储到磁盘File parentFile = new File(fileUploadPath);//File parentFile = uploadFile.getParentFile();//判断目录是否存在,不存在就新建if (!parentFile.exists()) { parentFile.mkdirs();}String uuid = IdUtil.fastSimpleUUID();String fileUUID = uuid + StrUtil.DOT + type;File uploadFile = new File(fileUploadPath + fileUUID);file.transferTo(uploadFile);String url = "http://" + serverIp + ":9090/file/" + fileUUID;//存储到数据库Files saveFile = new Files();Goods goods = new Goods();saveFile.setName(originalFilename);saveFile.setType(type);saveFile.setSize(size/1024);saveFile.setUrl(url);goods.setUrl(url);fileMapper.insert(saveFile);goodsMapper.insert(goods);return url;//String md5 = SecureUtil.md5(file.getInputStream());//Files files = getFileByMd5(md5);////String url;//if (files != null) {//url = files.getUrl();//} else {//file.transferTo(uploadFile);//url = "http://localhost:9090/file/" + fileUUID;//}////存储到数据库//Files saveFile = new Files();//saveFile.setName(originalFilename);//saveFile.setType(type);//saveFile.setSize(size/1024);//saveFile.setUrl(url);//saveFile.setMd5(md5);//fileMapper.insert(saveFile);//return url;}@GetMapping("/{fileUUID}")public void download(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {// 根据文件的唯一标识码获取文件File uploadFile = new File(fileUploadPath + fileUUID);// 设置输出流的格式ServletOutputStream os = response.getOutputStream();response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));response.setContentType("application/octet-stream");// 读取文件的字节流os.write(FileUtil.readBytes(uploadFile));os.flush();os.close();}/** * 通过文件的md5查询文件 * @param md5 * @return */private Files getFileByMd5(String md5) {// 查询文件的md5是否存在QueryWrapper queryWrapper = new QueryWrapper();queryWrapper.eq("md5", md5);List filesList = fileMapper.selectList(queryWrapper);return filesList.size() == 0 " />6.MybatisPlusConfig import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@MapperScan("com.example.demo.mapper")public class MybatisPlusConfig {/** * 分页插件 */@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}
7.Constants
public interface Constants {String CODE_200 = "200"; //成功String CODE_400 = "400"; //参数不足String CODE_401 = "401"; //权限不足String CODE_500 = "500"; //系统错误String CODE_600 = "600"; //其他业务异常}
完结撒花,对于在线商城这个项目呢,肯定还会有很多的优化,这个项目我也有2个版本,就是服务器上面的和不是服务器上面的,但是基本上都差不多。基本功能呢也都能实现,只是跟商业的比起来肯定差点意思啦。各位小伙伴觉得笔者写的还不错,就多多点赞,需要源码的也可以私信我哦,看见信息第一时间回复大家。记得点赞关注!!!!