
先交代一下背景。最近在做一套Spring Boot网上漫画商城系统的完整源码整理顺手把整个项目的设计思路、开发过程和填坑记录都写了出来。这套系统不是那种纯摆样子的demo而是真能跑通用户浏览漫画-加入购物车-下单支付-后台管理发货全流程的实战项目技术栈以Spring Boot为主前端用Vue3配合Element Plus数据库用的MySQL缓存和登录态交给Redis。如果你正在做毕业设计、打算接私活或者想快速上手一套前后端分离的电商类系统这份拆解应该能帮你省下不少折腾的时间。下面我按项目的实际推进顺序来写从需求拆解到技术选型从数据库建模到核心代码实现最后说部署和常见的坑。内容偏实操没有太多理论空谈。1. 网上漫画商城到底要做什么——先想清楚再动工1.1 角色与业务范围界定不管做什么系统第一步不是写代码而是把用户画像和角色权限理清楚。漫画商城表面看只有买家和卖家两个角色实际上手你就知道至少要拆成三类。第一类是普通用户也就是买漫画的人。他们关心的是能不能快速找到想看的漫画或周边、看到的封面和简介是不是清楚、下单流程顺不顺、支付完成后能不能查到订单。这类用户的使用场景集中在门户首页、漫画详情页、购物车、结算页和个人中心。第二类是运营人员负责商品上下架、分类管理、轮播图配置、处理用户留言。他们每天面对的是后台的商品列表、订单列表、内容审核页面操作要够快表单要够清晰。第三类是超级管理员除了运营的权限外还要管理用户账号状态、角色权限、系统基础配置。在实现上这前三类角色应该走同一套登录认证只是登录后拿到的角色标识不同、菜单路由不同、接口权限不同。我的做法是给每个用户绑定一个角色编码JWT里带上角色信息后端接口用注解做权限拦截。这里特别提醒一句网上漫画商城如果涉及付费内容还要额外考虑果子这类虚拟货币的设计有的系统做的是充值然后再买章节。但大多数毕设和二次开发需求做到真实支付对接就够了不需要搞虚拟货币体系。如果需求方提了再考虑扩展。1.2 完整交易链路与状态流转漫画商城和普通服装电商在流程上没本质区别核心就一条线商品浏览 → 购物车管理 → 提交订单 → 支付处理 → 订单履约 → 售后与评价。这条链路里最容易做漏的是订单状态流转。我在开发时专门画了一张状态表来核对状态编码状态名称说明0待支付订单已提交等待用户完成支付1已支付/待发货支付回调已验证等待运营发货2已发货/待收货运营已发货可填写物流单号3已收货/已完成用户确认收货或超时自动完成4已取消用户主动取消或超时系统取消5退款/售后订单进入售后流程实际开发中状态字段不要用字符串直接用 tinyint代码里用枚举类做映射。也不要只更新状态一定要在订单表里附带一个状态变更时间字段比如 pay_time、ship_time、finish_time这样运营在后台看订单才能一眼判断卡在哪个环节。还有一个容易被忽略的需求漫画商城通常会有免费阅读和付费阅读的区分数字化商品不需要真实物流。如果是卖实体漫画书和周边就要走传统的物流发货体系。我当前这套源码默认按实体商品来做因为这样交易链路最完整也最适合演示前后端全栈能力。2. 技术选型与方案取舍——不跟风只选合适的2.1 为什么是Spring Boot 3 MyBatis Plus后端框架我选的是Spring Boot 3.x搭配MyBatis Plus持久层框架。这套组合在一线小型项目里出镜率极高原因就这么几个。Spring Boot 3自己带了一整套生态配置内嵌Tomcat起步依赖把大部分常用功能都封装好了写一个Web接口的成本比以前低得多。它内置的spring-boot-starter-validation、spring-boot-starter-data-redis、spring-boot-starter-security能直接覆盖这个项目需要的参数校验、缓存、安全认证。MyBatis Plus的优势在于单表CRUD完全不用手写SQLBaseMapper把 insert、selectById、updateById 这些通用方法全给你备好了分页查询也有内置的Page对象。我把漫画商品、分类、轮播图、收藏这些模块的DAO层代码量压到了极小核心业务才需要手写多表联查SQL。这里要注意MyBatis Plus不是让你完全不写SQL遇到订单和商品联查、统计报表这类复杂查询该写XML映射还是得写只是比例大幅下降。还有一点如果用Spring Boot 2.x也没问题但新项目建议直接上3.x因为Spring官方对2.x的维护已经进入尾声而且3.x默认使用Jakarta命名空间以后接微服务网关、Spring Cloud Alibaba这些生态也更顺。2.2 Redis在漫画商城里的两个关键作用本项目引入Redis主要干两件事缓存热点数据、维持登录态。缓存这块首页的漫画列表、轮播图、热门榜单这些数据如果每次都查MySQL压力全在数据库上。我把查询结果序列化成JSON字符串塞进Redis设置5到10分钟的过期时间访问量大的时候能显著降低数据库压力。具体实现上用Spring Cache的Cacheable注解就可以但要注意缓存key的设计要包含分页参数和分类ID否则不同页面会串数据。登录态这块我用Redis来存储用户的登录Token信息配合JWT一起用。JWT本身是无状态的但遇到修改密码、踢人下线这类需求时无状态反而难处理。所以我在Redis里维护一个用户登录会话表key是userIdvalue是当前有效的token。每次请求校验token时除了验签名还要比对Redis里的token是否一致不一致就视为登录失效。这算是一个很实用的防御思路只靠JWT做登出是做不到的。另外Redis还能在防重复提交上发挥作用。用户在订单提交接口疯狂点击时我以userId:order:{goodsId}作为key设置3秒过期利用setIfAbsent方法判断是否重复请求。这个方案在项目里实施了效果立竿见影。2.3 前端用Vue3还是服务端模板引擎早期很多Spring Boot项目喜欢在templates目录里写Thymeleaf模板前后端不分离开发快但后期维护难。现在的前端生态Vue3 Vite Element Plus已经成为主流选择。我这个漫画商城采用的就是前后端分离架构前端工程独立部署通过axios调用后端REST接口。选Vue3的原因很直接组件化开发适合商城这种模块多的场景。首页、商品详情、购物车、结算、订单列表、后台管理每个页面都是一个独立组件互不干扰。Composition API的组织方式也比Vue2的Options API更适合复杂业务逻辑复用。配合Pinia做状态管理把用户登录信息、购物车数量这些全局数据统一管理体验比之前用Vuex顺手。Element Plus提供了现成的表格、表单、分页、对话框组件后台管理页面90%的界面都是通过这些组件拼出来的。前端构建产物是纯静态文件可以交给Nginx直接托管部署起来非常省事。如果你是新手不要纠结要不要学TypeScript这个项目里用JavaScript就够了重点是先跑通业务。3. 数据库设计漫画商城的核心建模3.1 漫画商品与分类表设计商城类系统的核心表之一就是商品表漫画商品相比普通商品多了几个特殊字段作者、出版社、连载状态连载中/已完结、章节数量、是否支持在线预览。当然封面图、价格、库存、销量、上下架状态这些通用字段一个不能少。我设计的漫画信息表主要字段大概是这样CREATE TABLE comic_info ( id bigint NOT NULL AUTO_INCREMENT, comic_name varchar(200) NOT NULL COMMENT 漫画标题, category_id bigint DEFAULT NULL COMMENT 分类ID, cover_url varchar(500) DEFAULT NULL COMMENT 封面图URL, author varchar(100) DEFAULT NULL COMMENT 作者, publisher varchar(200) DEFAULT NULL COMMENT 出版社, serial_status tinyint DEFAULT 0 COMMENT 连载状态0连载中 1已完结, price decimal(10,2) DEFAULT 0.00 COMMENT 售价, original_price decimal(10,2) DEFAULT 0.00 COMMENT 原价, stock int DEFAULT 0 COMMENT 库存, sales_count int DEFAULT 0 COMMENT 销量, description text COMMENT 图文详情, status tinyint DEFAULT 0 COMMENT 上下架0上架 1下架, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT漫画信息表;分类表就简单得多自关联或者平铺一层都行。漫画商城一般不搞三级分类最多两级一级是漫画、周边、实体书二级才是热血、恋爱、悬疑。我在实践里直接做成单表parent_id结构后续要扩展也方便。很多新手容易把价格字段设计成float这是大忌。金额计算一定要用decimal(10,2)否则月底对账你会被各种浮点误差逼疯。商品表的status字段也要单独建索引因为前台查询全都要过滤status0。3.2 购物车与订单表建模购物车一般有两种设计用Redis临时存储或者在MySQL里建购物车表。这种做法看项目需求Redis方案适合未登录用户也能加购MySQL方案适合多端同步。电商后台管理演示项目最好用MySQL因为购物车数据要跟用户绑定还可能要查历史加购记录。cart_item表核心字段就四个user_id、comic_id、quantity、checked。checked字段标记这个购物车项在结算时是否选中这个设计在部分结算场景下很关键。订单表是整个系统里最重要的表。我把订单拆成订单主表和订单明细表主表记录订单号、用户ID、总金额、状态、收货人信息、支付时间、发货时间明细表记录每个商品快照。这里有个很重要的设计原则明细表里的商品名称、价格一定要存冗余快照不能关联实时查询商品表。因为商品可能改价、下架甚至删除用户订单历史里看到的必须是下单那一刻的信息。CREATE TABLE order_info ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(64) NOT NULL COMMENT 订单编号, user_id bigint NOT NULL COMMENT 下单用户ID, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, pay_type tinyint DEFAULT 1 COMMENT 支付方式1余额 2模拟支付, status tinyint DEFAULT 0 COMMENT 订单状态, receiver_name varchar(50) DEFAULT NULL, receiver_phone varchar(20) DEFAULT NULL, receiver_address varchar(300) DEFAULT NULL, pay_time datetime DEFAULT NULL, ship_time datetime DEFAULT NULL, finish_time datetime DEFAULT NULL, create_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_status (user_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表;订单号不要用自增ID要单独生成。我用的是时间戳 用户ID后四位 随机数的组合方式长度大概18位既能保证唯一又能从订单号里反查到基本信息。3.3 用户、角色与权限表设计用户表其实没什么花头账号、密码、昵称、头像、手机号、邮箱、状态、注册时间。密码必须加密存储老项目用MD5的已经被喷惨了这次我用的BCryptSpring Security自带的PasswordEncoder每次加密自动加盐同一密码出来的密文都不一样安全性有保障。角色和权限这块很多毕设项目直接给用户表加一个role字段就完事小系统可以接受但稍微正规一点还是建议拆表。我设计的结构是三张表sys_user、sys_role、sys_user_role。用户和角色多对多登录时查询用户的角色编码接口层用自定义注解PreAuthorize判断。漫画商城的管理员后台需要区分运营和管理员运营只能管理商品和订单管理员还能操作用户状态和角色分配。如果不拆表后面每加一种后台操作权限就要改代码拆了表以后就是往关联表里插数据的事。4. 核心功能实现细节——关键代码与踩坑记录4.1 登录鉴权与JWT Redis组合方案登录接口的流程看起来简单实际上有几个细节直接关系到系统的安全性。第一步是校验用户名密码第二步是签发Token第三步是把Token存入Redis第四步是返回用户基本信息给前端。我踩过最大的坑是JWT密钥写死在application.yml里。刚开始开发为了方便就配置了一个固定的字符串结果泄露之后任何人都能伪造Token。后面我改成了从环境变量读取部署时单独配置。虽然麻烦一点但生产环境安全性完全不一样。Token有效期也要讲究。我给Token设置的24小时过期Redis里的有效期同步设24小时。用户每次请求时拦截器做两件事校验JWT签名和有效期、比对Redis中的Token值。用户主动退出时直接删除Redis里的Key这一步极为关键不然退出登录的Token在有效期内还能用。登录拦截器实现上我继承了HandlerInterceptorAdapter在preHandle方法里从request的Header里取Authorization字段解析Token把用户ID放进ThreadLocal。同时定义一个注解LoginRequired需要鉴权的接口就加上不需要的门口接口则放行这样首页浏览不用强制登录下单和购物车操作就必须带Token。4.2 漫画搜索与分类筛选的SQL写法前台漫画列表的查询不只是简单的分页还要支持关键词搜索、分类筛选、价格区间、连载状态过滤、按销量或按新品排序。如果全部写死在SQL里前端每次组合条件都要拼一个不同的SQL难维护。我的方案是直接用MyBatis Plus的LambdaQueryWrapper动态拼接条件来感受一下LambdaQueryWrapperComicInfo wrapper Wrappers.lambdaQuery(); wrapper.eq(StringUtils.isNotBlank(categoryId), ComicInfo::getCategoryId, categoryId) .like(StringUtils.isNotBlank(keyword), ComicInfo::getComicName, keyword) .eq(serialStatus ! null, ComicInfo::getSerialStatus, serialStatus) .orderByDesc(QuerySort.NEW, ComicInfo::getCreateTime) .orderByDesc(QuerySort.SALES, ComicInfo::getSalesCount);这种写法最大的好处是条件参数为null时自动忽略前端传什么就过滤什么不用在Java代码里写一堆if判断。搜索关键字用like会导致索引失效但漫画商城的数据量通常不会太大性能瓶颈不明显。如果以后数据量真起来了再换成Elasticsearch也不迟前期不用过度设计。分类筛选这里有个额外需求运营有时需要一次看多个分类的商品。我支持categoryIds列表传入SQL用IN查询同样是通过wrapper.in实现。列表页的销量排序要做成已售数量倒序但如果只是展示随时可以用乐观锁字段维护一个冗余缓存字段定时刷新避免频繁count统计拖慢首页。4.3 购物车、下单与库存扣减购物车模块相对容易无非是加购、改数量、勾选、删除、查列表五个接口。但加购的时候要注意一个细节如果同一个商品已经再购物车里就不要新增一条记录而是把quantity加一否则购物车会变成一堆重复行前端渲染和管理都会崩溃。下单是校验逻辑最多的流程我在OrderServiceImpl里大致分这么几步从请求里解析要结算的购物车条目和收货地址。循环查出对应的商品信息检查是否上架、库存是否充足。计算总金额加上运费我没做运费模板直接固定0。生成订单号插入订单主表批量插入订单明细表。删除已购买的购物车条目。接口直接返回订单ID和支付参数。很多人会漏掉第四步和第五步之间的事务控制。如果删除购物车失败导致报错订单却已经插入那就会出现订单存在但购物车还在的僵尸数据。所以下单方法必须加Transactional任何一个环节抛异常就整体回滚。库存扣减这里我建议用预占库存而不是下单后直接扣减。什么意思下单时不直接执行comic_info的stock减一而是把扣减操作和支付回调挂钩。用户提交订单后库存不动只是锁定待支付等支付成功回调时再真正扣减库存。好处是用户一直不支付也不会把库存扣没坏处是超时未支付的订单要释放锁定库存实现略复杂。我这个项目为了演示简洁直接在提交订单时先校验库存再扣减简单直接但上线前要知道这个方案在超卖场景下是有风险的。4.4 支付对接与回调处理接真实支付需要商户资质很多毕设场景不具备这个条件所以我在项目里做了模拟支付通道。用户在订单详情页点击去支付跳到模拟支付页面点击确认支付后后端模拟支付平台回调更新订单状态。这个模拟回调的接口设计要遵循真实支付平台的结构给前端一个支付成功后的跳转地址实际上由前端携带订单号调用后端确认接口。真正的生产环境支付回调是从支付平台服务器发起的而不是前端能伪造的所以后端验证签名这步极其重要。模拟阶段没签名验证但接真实支付时一定要把验签放第一位。支付成功后的处理逻辑包括更新订单状态为已支付、扣减库存、累加销量、清掉Redis中的待支付缓存、给用户发放积分如果做了。这一串操作同样要保证事务性哪一步失败都不能导致用户已经付了钱但订单还挂着待支付。5. 源码结构解读与本地运行5.1 前后端工程项目划分拿到源码压缩包时第一件事不是急着启动而是先看清目录结构。一般来说压缩包解压后会看到两个主目录springboot开头的后端工程vue或web开头的管理端前端工程还有一份sql数据库脚本和说明文档。后端工程按标准Maven结构组织重点看这几个包com.example.mall ├── common # 通用工具、统一返回结果、异常处理 ├── config # 安全配置、Redis配置、MyBatisPlus配置 ├── controller # 接口层 ├── service # 业务逻辑层 ├── mapper # MyBatis Plus的Mapper接口 ├── entity # 数据库实体类 └── dto # 入参出参对象管理端前端用的是Vite构建页面目录大概有views/system、views/goods、views/order等。里面区分了系统管理、商品管理、订单管理、营销管理等模块。5.2 从零跑起来的具体步骤启动之前要准备的软件JDK 17以上、Maven 3.8以上、MySQL 8.0、Redis 6以上、Node.js 16以上。没有一样可以直接跳过后端和前端都跑不起来。第一步是创建数据库我建议直接在Navicat里执行sql脚本。注意先创建数据库再导入sql文件编码要选utf8mb4。导入完成后检查核心表数量对不对对比一下README里的说明至少应该有十几张业务表。第二步是修改后端配置文件application.yml。要把数据源地址、账号密码、Redis地址改成本地的再确认server.port没有冲突。第一次启动如果端口被占用改一下port参数就行。第三步是启动后端。在命令行进入后端目录执行mvn spring-boot:run或者用IDEA打开工程后直接运行主启动类。看到Spring Boot的启动日志出现Started xxxApplication就说明后端没问题。第四步是启动前端。进入前端目录执行npm install安装依赖这一步在网络不好的时候可能很慢如果卡住了换个npm镜像源再试。依赖装完后执行npm run dev控制台会打印出前端访问地址默认通常是localhost:5173。听我一句劝跑项目的时候打开前端的控制台F12Network面板里能看到所有接口请求状态。如果前端页面空白或者数据加载不出来基本是后端没启动或者接口跨域没配好。跨域问题我在后端写了一个CorsConfig配置类本地联调时允许所有来源部署时再收紧。6. 部署上线与常见问题排查6.1 服务器部署实践本地跑通不代表能上线。部署我推荐用最朴素也最稳的方式后端打包成jar前端构建成静态文件交给Nginx。后端打包执行mvn clean package在target目录生成一个可执行的jar包。服务器上只要装了JDK17直接执行java -jar mall-server.jar --spring.profiles.activeprod就能启动。生产环境的配置单独放在application-prod.yml里数据库密码、Redis密码用环境变量方式注入不要明文写在代码仓库里。前端打包执行npm run build产物在dist目录。把dist里的文件上传到服务器的某个目录比如/var/www/mall然后在Nginx配置里指过去server { listen 80; server_name your-domain.com; location / { root /var/www/mall; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }location /的try_files配置要注意前后端分离项目如果少了这一行刷新二级页面时会出现404这是新手部署踩得最多的坑。/api/开头的请求转发到后端服务这样前端代码里就不需要写完整的后端地址统一用相对路径/api。数据库生产环境不要用root账号连创建一个小权限账号只赋予业务库的增删改查权限。Redis也要设置密码并要求protected-mode否则会被扫描端口后植入恶意数据。6.2 高频Bug排查实测我在联调过程中收集了出镜率最高的一批问题列成速查表给后面接手源码的同学参考。现象可能原因解决办法后端能启动前端请求连不上跨域未配置或端口不一致检查后端CorsConfig确认前端proxy指向登录后接口报401Redis里的Token丢失或过期检查Redis连接是否正常重启后端会清空会话图片加载不出来上传目录和访问路径不一致配置静态资源映射确认图片URL拼接正确中文乱码数据库字符集不是utf8mb4建库时指定utf8mb4连接串加characterEncodingutf8下单成功但支付后状态未变回调接口签名或参数没对齐模拟支付场景下检查订单号传递是否一致分页数据重复SQL的排序字段不是唯一的排序追加id排序保证顺序稳定还有一个隐藏问题前后端时间字段格式不一致。Java后端返回的LocalDateTime默认带T字符前端显示会有点怪。我在Jackson配置里统一指定了yyyy-MM-dd HH:mm:ss格式前后端就对齐了这个问题不遇到还好遇到就很烦人。6.3 如果要做二次开发建议优先扩展哪块源码拿回来后不只是用来跑通演示的真正值钱的是按自己的需求去改。我建议三个方向的优先级。运营后台优先扩展营销模块。比如限时折扣、满减活动、优惠券发放这些功能在现阶段的电商系统里几乎必做。数据库层面只需要加一张活动表、一张优惠券表订单结算时增加优惠计算逻辑。数据可视化优先扩展后台统计首页。目前后台首页只是放了一些基础统计真正的运营需要看近30天销售额趋势图、热门漫画Top榜、转化漏斗。前端用ECharts就能画后端提供聚合SQL接口工作量不大但演示效果极强。线上阅读体验优先扩展阅读器功能。如果漫画站的核心业务是数字版阅读那就需要PDF或图片在线阅读器的集成这种功能在毕设答辩时非常加分因为比单纯的商城多出了内容管理维度。这套系统做完一遍之后我个人的体会是小到购物车数量同步大到订单状态机设计每个看似简单的功能背后都有值得深挖的边界情况。漫画商城这个选题之所以经典就是因为它把电商系统的主要复杂度都包含了又没有像完整电商平台那样复杂到让人无从下手。对于想系统认识Spring Boot全栈开发、想拿得出手一份完整作品的人来说花时间把这类项目吃透远比刷一堆零散教程有用。希望这篇记录能帮你少走几步弯路项目跑起来之后再根据自己的想法一步步改出自己的版本。