摘要
电子商务与移动支付的普及,线上购书已成为高校师生及社会公众获取图书的重要方式。传统线下书店在图书检索、库存查询、订单跟踪等方面存在信息分散、效率较低等问题。本文设计并实现了一套基于 B/S 架构的网上书店系统,采用前后端分离模式,面向用户、商家与管理员三类角色,覆盖图书分类浏览、全站图书查询、购物车管理、下单结算、订单处理、库存维护、评价审核等核心业务。
系统后端基于 Spring Boot 3 构建 RESTful 服务,持久层采用 MyBatis-Plus 访问 MySQL 数据库,通过 JWT 实现无状态身份认证与基于角色的访问控制;前端采用 Vue 3 单页应用,用户端与商家/管理员后台采用双布局设计,配合 Element Plus 与 ECharts 实现业务页面与统计图表。数据库共设计 9 张业务表,字段命名统一为 snake_case;业务层采用外键 ID 与 Service 层手动填充关联信息,在下单结算时使用事务校验库存并扣减,保障数据一致性。
经功能测试,系统各模块运行稳定,满足网上书店日常运营的信息化需求,对同类电商类 Web 应用开发具有一定的参考价值。
技术栈: Spring Boot 3 + MyBatis-Plus + MySQL + Vue 3 + Element Plus + ECharts
数据库表:9张
🍅文末获取联系🍅
🍅文末获取联系🍅
作者介绍:专注计算机课设、毕设辅导,个人开发,坚持原创,非工作室,源码全网唯一。
✅技术主流:SpringBoot + Vue 前后端分离,MySQL,Echarts数据统计,可本地运行
✅配套资料:源码 + 数据库 + 实验报告/论文 + 答辩 PPT+部署演示+远程调试+问题解答
技术范围:SpringBoot、Vue、数据可视化、小程序、HLMT、Nodejs、uni-app、MySQL数据库、ElementUi等设计与开发。
适用范围:软件工程、软件技术、数据库课程设计、计算机科学与技术、数据库系统原理、JavaWeb开发、JavaEE、Java、Web应用开发、动态网页设计的课程设计、课设、大作业、课程实验、期末作业
实验报告
实验报告可供大家参考使用
功能展示
用户
管理员+商家
数据库及架构
系统数据库设计为:
Controller及Service层核心代码写法:
package com.springboot.controller; import com.springboot.auth.RequireRole; import com.springboot.dto.*; import com.springboot.entity.Book; import com.springboot.entity.UserRole; import com.springboot.service.BookService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/books") @RequiredArgsConstructor public class BookController { private final BookService bookService; @GetMapping("/browse") @RequireRole(UserRole.USER) public ApiResponse<PageResult<Book>> browse( @RequestParam Long merchant_id, @RequestParam(required = false) String category, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return ApiResponse.ok(bookService.listByMerchant(merchant_id, category, page, size)); } @GetMapping("/search") @RequireRole(UserRole.USER) public ApiResponse<PageResult<Book>> search( @RequestParam(required = false) String keyword, @RequestParam(required = false) String category, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return ApiResponse.ok(bookService.searchForUser(keyword, category, page, size)); } @GetMapping @RequireRole({UserRole.ADMIN, UserRole.MERCHANT}) public ApiResponse<PageResult<Book>> list( @RequestParam(required = false) String keyword, @RequestParam(required = false) String category, @RequestParam(required = false) String status, @RequestParam(required = false) Long merchant_id, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return ApiResponse.ok(bookService.list(keyword, category, status, merchant_id, page, size)); } @GetMapping("/{id}") @RequireRole({UserRole.ADMIN, UserRole.MERCHANT, UserRole.USER}) public ApiResponse<Book> detail(@PathVariable Long id) { return ApiResponse.ok(bookService.detail(id)); } @PostMapping @RequireRole({UserRole.ADMIN, UserRole.MERCHANT}) public ApiResponse<Book> create(@Valid @RequestBody BookDTO dto) { return ApiResponse.ok("新增成功", bookService.create(dto)); } @PutMapping("/{id}") @RequireRole({UserRole.ADMIN, UserRole.MERCHANT}) public ApiResponse<Book> update(@PathVariable Long id, @Valid @RequestBody BookDTO dto) { return ApiResponse.ok("更新成功", bookService.update(id, dto)); } @DeleteMapping("/batch") @RequireRole({UserRole.ADMIN, UserRole.MERCHANT}) public ApiResponse<Void> batchDelete(@Valid @RequestBody IdsDTO dto) { bookService.batchDelete(dto.getIds()); return ApiResponse.ok("删除成功", null); } } package com.springboot.service; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.springboot.auth.AuthContext; import com.springboot.dto.BookDTO; import com.springboot.dto.PageResult; import com.springboot.entity.Book; import com.springboot.entity.Merchant; import com.springboot.mapper.BookMapper; import com.springboot.mapper.MerchantMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.*; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class BookService { private final BookMapper bookMapper; private final MerchantMapper merchantMapper; public PageResult<Book> list(String keyword, String category, String status, Long merchant_id, int page, int size) { if (AuthContext.isMerchant()) { merchant_id = AuthContext.getUserId(); } var wrapper = Wrappers.<Book>lambdaQuery() .eq(merchant_id != null, Book::getMerchant_id, merchant_id) .eq(StringUtils.hasText(status), Book::getStatus, status) .eq(StringUtils.hasText(category), Book::getCategory, category) .and(StringUtils.hasText(keyword), w -> w .like(Book::getName, keyword) .or().like(Book::getDescription, keyword)) .orderByDesc(Book::getId); Page<Book> result = bookMapper.selectPage(new Page<>(page, size), wrapper); enrichBooks(result.getRecords()); return PageResult.of(result); } public PageResult<Book> searchForUser(String keyword, String category, int page, int size) { List<Long> enabledMerchantIds = merchantMapper.selectList( Wrappers.<Merchant>lambdaQuery().eq(Merchant::getEnabled, 1).select(Merchant::getId)) .stream().map(Merchant::getId).toList(); if (enabledMerchantIds.isEmpty()) { return PageResult.of(new Page<>(page, size)); } var wrapper = Wrappers.<Book>lambdaQuery() .in(Book::getMerchant_id, enabledMerchantIds) .eq(Book::getStatus, "ON_SALE") .gt(Book::getStock, 0) .eq(StringUtils.hasText(category), Book::getCategory, category) .and(StringUtils.hasText(keyword), w -> w .like(Book::getName, keyword) .or().like(Book::getDescription, keyword)) .orderByDesc(Book::getId); Page<Book> result = bookMapper.selectPage(new Page<>(page, size), wrapper); enrichBooks(result.getRecords()); return PageResult.of(result); } public PageResult<Book> listByMerchant(Long merchant_id, String category, int page, int size) { Merchant m = merchantMapper.selectById(merchant_id); if (m == null || m.getEnabled() != 1) throw new RuntimeException("书店不存在或已停用"); var wrapper = Wrappers.<Book>lambdaQuery() .eq(Book::getMerchant_id, merchant_id) .eq(Book::getStatus, "ON_SALE") .gt(Book::getStock, 0) .eq(StringUtils.hasText(category), Book::getCategory, category) .orderByDesc(Book::getId); Page<Book> result = bookMapper.selectPage(new Page<>(page, size), wrapper); enrichBooks(result.getRecords()); return PageResult.of(result); } public Book detail(Long id) { Book book = bookMapper.selectById(id); if (book == null) throw new RuntimeException("图书不存在"); enrichBooks(List.of(book)); return book; } @Transactional public Book create(BookDTO dto) { Long merchantId = AuthContext.isMerchant() ? AuthContext.getUserId() : dto.getMerchant_id(); if (merchantId == null) throw new RuntimeException("商家ID不能为空"); Book book = new Book(); applyDto(book, dto, merchantId); book.setStatus(StringUtils.hasText(dto.getStatus()) ? dto.getStatus() : "ON_SALE"); bookMapper.insert(book); enrichBooks(List.of(book)); return book; } @Transactional public Book update(Long id, BookDTO dto) { Book book = requireOwned(id); applyDto(book, dto, book.getMerchant_id()); if (StringUtils.hasText(dto.getStatus())) book.setStatus(dto.getStatus()); bookMapper.updateById(book); enrichBooks(List.of(book)); return book; } @Transactional public void batchDelete(List<Long> ids) { if (ids == null || ids.isEmpty()) throw new RuntimeException("请选择要删除的数据"); for (Long id : ids) { requireOwned(id); bookMapper.deleteById(id); } } private Book requireOwned(Long id) { Book book = bookMapper.selectById(id); if (book == null) throw new RuntimeException("图书不存在"); if (AuthContext.isMerchant() && !Objects.equals(book.getMerchant_id(), AuthContext.getUserId())) { throw new RuntimeException("无权操作该图书"); } return book; } private void applyDto(Book book, BookDTO dto, Long merchantId) { book.setMerchant_id(merchantId); book.setName(dto.getName()); book.setCategory(dto.getCategory()); book.setPrice(dto.getPrice()); book.setStock(dto.getStock() != null ? dto.getStock() : 0); book.setImage_url(dto.getImage_url()); book.setDescription(dto.getDescription()); } private void enrichBooks(List<Book> books) { if (books.isEmpty()) return; Set<Long> merchantIds = books.stream().map(Book::getMerchant_id).filter(Objects::nonNull).collect(Collectors.toSet()); Map<Long, Merchant> map = merchantIds.isEmpty() ? Map.of() : merchantMapper.selectBatchIds(merchantIds).stream().collect(Collectors.toMap(Merchant::getId, m -> m, (a, b) -> a)); for (Book b : books) { Merchant m = map.get(b.getMerchant_id()); if (m != null) { b.setShop_name(m.getShop_name()); b.setMerchant_name(m.getReal_name()); } } } }擅长:功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文降重、长期答辩答疑辅导、腾讯会议一对一专业讲解辅导答辩、模拟答辩演练、和理解代码逻辑思路等。
获取联系
项目功能完整,可在本地运行,并可远程调试,确保运行顺利!
👇🏻👇🏻获取联系方式👇🏻👇🏻
课程设计获取
https://blog.csdn.net/qq_59059632/article/details/163685632?spm=1001.2014.3001.5501