做母婴商城这种项目,和做普通电商完全不是一回事。不是说换个商品品类、把页面做粉嫩一点就叫母婴商城了,选品逻辑、用户决策周期、敏感信息保护、售后规则这些业务差异,都会直接反应在系统架构和代码实现上。我最近把一套基于SpringBoot2 + Vue3 + MyBatis-Plus + MySQL8.0的母婴商城系统从零到部署完整跑了一遍,把源码、数据库脚本和开发文档都整理齐了。这篇就当作一次完整的项目复盘,从技术选型到核心模块实现,再到实际部署踩过的坑,一次性说清楚。
这套东西适合谁?一种是学校或者培训机构里做毕业设计、课程项目的同学,需要一套能跑通、有文档、能讲清楚技术点的完整系统;另一种是想自己练手做全栈项目、但不想从零搭框架的开发者,拿这套代码当骨架,往里面填自己的业务逻辑。这两种场景我都帮你考虑到了,下面讲的内容也尽量兼顾原理和实操。
1. 项目整体设计与技术选型思路
1.1 为什么是这套组合:SpringBoot2 + Vue3 + MyBatis-Plus + MySQL8.0
先聊技术栈。很多人上来就问"现在不都SpringBoot3了吗,你怎么还在用2.x",这个问题我在做技术选型的时候也纠结过。最终确定SpringBoot2的原因有两个:第一,市面上大部分成熟的中间件、第三方SDK对SpringBoot2的兼容性最稳,比如很多支付SDK和短信服务商,官方文档仍然以SpringBoot2为主,2.7版本是2.x的最后一个大版本,生命周期足够长;第二,对学习者来说,SpringBoot2的教程、案例、问题解答在社区里的存量最大,遇到问题更容易找到参考。
Vue3这边没有太多犹豫,Composition API写起来比Options API舒服很多,配合Vite开发时的热更新速度快到起飞。而且Element Plus这个组件库在Vue3生态里已经非常成熟,后台管理界面基本不用自己费劲写样式。
MyBatis-Plus选择它是冲着"快"去的。单表CRUD不用写SQL,内置的分页插件、代码生成器、逻辑删除功能,直接省掉一大半的基础编码工作。很多人觉得MyBatis-Plus就是"简单版MyBatis",其实它的动态数据源、多租户插件、乐观锁插件在复杂业务里也是能扛事的。
MySQL8.0相比5.7,我觉得最值得用的特性是窗口函数和公用表表达式(CTE)。做商品评价排行、订单统计分析这类需求时,一条SQL就能搞定的东西,不用再写复杂的子查询。另外8.0默认的utf8mb4字符集和更好的JSON支持,对电商这种需要存扩展属性的场景很友好。
1.2 母婴商城的业务特殊性:不是普通电商的换皮
母婴商城看着是电商,实际一拆解就能发现它比普通服饰、数码商城多出好几层特殊逻辑。
第一是用户决策周期长。买一罐奶粉,用户可能会反复对比配方、看评价、问客服,所以商品详情的浏览轨迹记录、评价体系的权重设计、搜索的关键词匹配策略,都要做得比普通电商更细致。
第二是敏感信息多。母婴用户从怀孕建档到孩子出生,会涉及孕产信息、宝宝月龄、身高体重等个人敏感数据,再加上收货地址里的家庭住址,这些信息在数据库里绝对不能明文存储。
第三是商品逻辑复杂。母婴商品跨度极大,从几十块的奶瓶到几千块的婴儿车,从消耗品尿不湿到长周期耐用品,不同品类的库存策略、物流策略都不同。比如临期奶粉的促销逻辑、跨境商品的清关状态,这些都会影响订单状态的流转设计。
第四是售后规则特殊。食品类目拆封不退,婴幼儿服装又涉及到贴身用品,这些规则要在订单状态机里体现出来,不能像普通电商那样一个"退货退款"走天下。
这些特殊性决定了系统在设计阶段就不能照搬通用的电商脚手架,下面我会逐个模块说清楚是怎么处理的。
2. 数据库设计与核心表结构拆解
2.1 商品SPU/SKU模型与多规格处理
商品模块是整个系统的地基,我采用了经典的三层结构:分类表(category)、SPU表(商品主体)、SKU表(具体规格)。
分类表设计成支持无限极分类,用parent_id关联父级,配合一个level字段标记层级。这里有个小技巧,查询某个分类下的所有商品时,如果用递归查询,数据量一大性能就崩。我实际用的是"路径枚举"方案,在分类表里加一个path字段,比如"0_12_34",这样查某个分类及其所有子分类的商品时,用LIKE '0_12_34_%'就能一把梭查出来,比递归的效率高一个量级。
SPU表存商品的基础信息:标题、副标题、品牌、适用年龄段、图片列表(JSON格式存储)、详情富文本等。注意适用年龄段是母婴商品的独有字段,我用的是age_min和age_max两个整数,单位是月龄,前端展示的时候再格式化成"0-6个月""1-3岁"这种文案,这样做的好处是后端可以做范围查询,比如"给我筛选适合3-6个月宝宝的玩具"这种需求,一条SQL就解决了。
SKU表挂SPU下面,存储具体规格组合和价格库存。规格属性我单独设计了一张属性表(attribute)和属性值表(attribute_value),用通用的"SPU-属性-属性值"关联,而不是给每个类目单独建表。比如奶粉有"段数"属性,纸尿裤有"尺码"属性,都走同一套结构。这样新增类目时不用改表结构。
库存扣减这块,MySQL8.0下我用的是乐观锁方案,在SKU表加一个version字段,扣减库存时执行:
UPDATE sku SET stock = stock - #{num}, version = version + 1 WHERE sku_id = #{skuId} AND stock >= #{num} AND version = #{version}这里"受影响行数为0"就说明库存不足或者版本冲突,代码里抛异常回滚事务。高并发场景下这个方案比悲观锁select for update性能好,比纯Redis预扣减方案实现又简单得多。母婴商城这种量级的系统,乐观锁完全够用。
2.2 订单、购物车与库存的协同设计
订单模块我拆成了五张表:购物车表(cart)、订单主表(orders)、订单明细表(order_item)、订单状态日志表(order_log)、退款售后表(refund)。
购物车表字段比较直接:user_id、sku_id、quantity、checked(是否选中)、create_time。注意一定要加user_id和sku_id的联合唯一索引,防止同一用户重复添加同一商品产生脏数据,插入时用"ON DUPLICATE KEY UPDATE quantity = quantity + 1"的方式实现累加逻辑。
订单主表是核心,除了常规的订单号、用户ID、总金额、支付方式,我专门加了几个字段:
status:订单状态(0待支付、1已支付待发货、2已发货、3确认收货、4已完成、5已取消、6售后中)close_reason:关闭原因,这个字段在客服纠纷排查时能救命address_snapshot:收货地址快照(JSON格式),下单那一刻把地址原样存进来,后续用户修改默认地址不影响已下订单
订单明细表里我冗余了商品标题、主图URL、规格文案、下单时单价。电商界的老规矩:订单生成后,商品改价、改图、改标题,历史订单显示必须不变,所以不能去关联SKU表查询实时数据。
订单状态日志表很多人会忽略,但我强烈建议做。每单的每一次状态变更都插一条记录,包括操作人(用户/系统/管理员)、变更前状态、变更后状态、变更原因。后面出现"我明明没点取消怎么订单没了"这种客诉,一查日志全清楚了。
订单号生成我用了"时间戳 + 用户ID后四位 + 随机数"的组合方式,避免用数据库自增ID直接当订单号,一个是安全问题(暴露销量),另一个是不好做分表。
2.3 会员信息与敏感数据加密存储
母婴商城的用户数据里有太多敏感字段:手机号、生日(用于计算宝宝月龄)、宝宝昵称、收货地址。我的处理原则是"分级加密"。
手机号是登录凭证,必须能查询,但绝对不能明文存。我用的是AES对称加密,密钥放在配置中心;同时单独建了一张user_mobile_index表存手机号的哈希值(SHA-256加盐),登录时先通过哈希匹配到userId,再解密获取明文。这样可以兼顾查询效率和存储安全。
宝宝信息表(baby_info)关联user_id,存储昵称、性别、出生日期。这类数据不参与任何查询条件,只做展示,所以我直接用了AES加密存储,查询出来后在Service层统一解密。
收货地址表里,收货人姓名和详细地址也是AES加密,手机号也是。这里有个坑,加密后的密文长度会变长,比如11位手机号加密后变成44位左右的字符串,建表时字段类型我特意用的VARCHAR(128),预留了充足空间,否则后面改表结构很痛苦。
对于安全管理上,还有个容易忽视的点:接口层返回的JSON自动过滤敏感字段。我用了一个自定义注解@SensitiveField,标注在实体类的敏感属性上,配合Jackson的序列化拦截器,在响应输出前自动解密、自动脱敏。这样即使某个接口忘记处理,也不会把密文或者完整敏感信息打给前端。
3. 后端核心模块实现与接口设计
3.1 统一返回结构、全局异常与参数校验
后端接口设计我遵循了前后端彻底分离的规范,所有接口统一返回Result<T>结构:
{ "code": 200, // 业务状态码,200成功,其他为各种失败 "message": "success", "data": {} // 泛型数据 }状态码我自定义了一套业务码:200成功、400参数错误、401未登录、403无权限、404资源不存在、500系统异常、1001库存不足、1002订单状态不允许操作等。注意HTTP状态码和业务状态码是两回事,比如用户未登录时HTTP返回200但业务码是401,这样前端拦截器可以根据业务码做统一跳转。
全局异常处理用Spring的@RestControllerAdvice统一拦截。最核心的是把MethodArgumentNotValidException(参数校验异常)和BusinessException(业务异常)分开处理,前者返回具体是哪个字段不合法,后者返回给用户可读的业务提示。我在参数校验上用的Hibernate Validator的注解,字段上标@NotNull、@Size、@Pattern,写起来方便,校验逻辑也都集中在实体类里。
登录鉴权这块用的是JWT(JSON Web Token)。用户登录成功后生成token,token里只放userId和角色信息,过期时间设为2小时,同时维护一个refresh_token用来续期,有效期7天。这里有个实践心得:JWT的最大缺点是"不可撤销",用户修改密码后旧token依然有效。所以我在Redis里存了一份"token黑名单",用户登出、改密码时把对应token加入黑名单,过期时间设为token剩余有效期,这样既保留了JWT的无状态优势,又解决了它的安全隐患。
还有一个容易踩的坑:跨域配置。前后端分离部署时必然遇到CORS问题,我用了一个CorsFilter统一处理,allowedOriginPatterns配置成*,但allowCredentials设为true。注意allowedOrigins不能和allowCredentials同时使用通配符,这是个老坑,SpringBoot版本不同表现还不一样,建议直接在WebMvcConfigurer里配置。
3.2 商品搜索:MySQL全文索引与缓存设计
母婴商品搜索有一个特点:用户搜索词比较"口语化",比如"半岁宝宝吃什么奶粉"这种长尾长句。直接把整句拆词匹配效果很差。我的方案是两步走。
第一步,维护一份商品搜索分词表。商品上架时,除了标题和副标题,运营还可以配置一组"搜索关键词",比如"新生儿 奶粉 一段 0-6个月 不上火",这组词被空格分词后存入search_index表。这一步虽然人工成本高一点,但搜索精准度远超纯分词器自动拆词。
第二步,搜索查询时把用户的输入先做简单的预处理:去停用词("的""了""什么")、提取数字+单位组合("6个月""2段"),然后去匹配标题和搜索关键词字段。MySQL8.0自带的全文索引(FULLTEXT)在这个场景下够用,配合BOOLEAN MODE可以实现包含多个关键词的逻辑:
SELECT * FROM product WHERE MATCH(title, sub_title) AGAINST('+奶粉 +6个月' IN BOOLEAN MODE)注意全文索引有最小分词长度限制,默认innodb_ft_min_token_size是3,中文单字搜索会失效,我把这个参数调整成1或者在索引表里直接存好了分词后的词组。
缓存层我用Redis做了两级缓存。商品详情等热点数据用"手动缓存"策略,更新商品时同步更新缓存;首页推荐、分类导航这种聚合数据用"定时缓存",每10分钟刷新一次"实际上"访问最多的Top商品列表。这比把全部商品都塞缓存要实际得多,内存占用低,命中率也高。
3.3 会员成长体系与优惠券状态机
母婴商城和普通电商的关键差异之一就是用户生命周期运营。我做了两个模块:会员等级和优惠券系统。
会员等级表很简单:等级名称、成长值下限、折扣率、权益描述。用户每天签到、下单、完善宝宝信息都会加成长值。下单加成长值这里用的是延迟计算,订单确认收货后才真正累加,防止用户下单后退款刷成长值。
优惠券系统比想象中复杂。我把优惠券拆成三张表:优惠券模板表(coupon_template)、用户持有的优惠券表(user_coupon)、优惠券使用记录表(coupon_usage)。模板表定义券的基本信息:面额、使用门槛、有效期类型(固定时间段或者领取后N天有效)、适用品类。发放时机包括新用户注册送、满额返券、定向营销发券。
用户领取优惠券时,根据模板复制一张券到user_coupon表,带独立的有效期。下单时锁定可用券,支付成功后标记已使用。这里有个业务细节要处理好:优惠券的校验必须在后端完成,不能依赖前端传参。用户提交订单时只传user_coupon_id,后端去数据库查出这张券,核对归属、有效期、使用门槛、是否与商品品类匹配,全部通过才允许使用。
我还做了一个很实际的防刷设计:注册送券接口做了同一设备ID限领一次的限制,Redis里以设备指纹为key记录领券次数,避免羊毛党批量注册刷券。母婴用品单价高、利润薄,风控这块不敢马虎。
3.4 订单状态机与支付回调处理
订单状态的流转,我用状态机模型来管理,而不是散落的if-else。搞了一个OrderStatusMachine组件,定义状态转移表:
| 当前状态 | 触发事件 | 目标状态 | 前置校验 |
|---|---|---|---|
| 待支付 | 用户取消 | 已取消 | 无 |
| 待支付 | 支付成功回调 | 待发货 | 校验签名、金额一致 |
| 待发货 | 商家发货 | 已发货 | 补录物流单号 |
| 已发货 | 用户确认收货 | 已完成 | 无 |
| 已完成 | 用户发起售后 | 售后中 | 在售后时效内 |
每次状态转移都同步写order_log日志表。状态机的好处是后续加需求(比如"超时自动取消")时,只需要在状态表里加一行配置,不用动核心业务代码。
支付回调是整个系统里最容易出bug的地方,核心原则就三个字:幂等性。第三方支付平台的回调通知因为网络原因可能重复发送,服务端必须保证同一条回调处理一次和多次的结果完全一致。我的做法是:回调接口第一件事就查订单状态,如果已经是"待发货"了,直接返回成功,不再执行后续逻辑。另外还要校验回调的金额和订单金额是否一致,防止支付平台传错参数或者被人恶意伪造回调。支付的异步通知处理完后,再主动查询一次支付平台的订单状态做二次确认,双保险。
3.5 商品收藏与浏览历史的实现
母婴决策周期长,收藏和历史记录功能看似简单,但对用户体验很重要。用户可能一周前看的婴儿车,今天想起来再对比。
收藏功能用的Redis里的Set结构存储,key是user:favorite:{userId},value是商品ID集合。用户点击收藏时用SADD命令,取消收藏用SREM,查询是否收藏用SISMEMBER。商品收藏数量可以查询SCARD获取。Redis的集合操作天然去重,而且性能极好。但Redis数据容易丢,所以收藏状态在数据库中还有一份表(favorite)做持久化备份,Redis只做查询加速。
浏览历史我用的最近最少使用(LRU)思路,在Redis里存一个ZSet,key是user:history:{userId},member是商品ID,score是浏览时间戳。每次浏览商品时用ZADD更新分数,拉取历史时用ZREVRANGE按时间倒序取前20条。这种方式天然支持"去重 + 排序"两个需求,代码量很少。
但要注意,MySQL中的数据也需要定期同步。我的方案是:每天凌晨批量把Redis里的浏览记录写入MySQL的浏览历史表,保留最近30天的数据,Redis中保留最近7天的热数据。这样既保证读取快速,又不丢失用户数据。
4. 前端Vue3实现:从页面到状态管理
4.1 项目结构、路由守卫与登录态管理
前端用的Vue3 + Vite + Pinia + Element Plus + Vue Router。项目结构上,我按"功能模块"而不是"文件类型"来组织目录:
src/ api/ // 接口请求封装,按模块拆分(product.js, order.js, user.js) assets/ // 静态资源 components/ // 通用组件(商品卡片、轮播图、地区选择器等) router/ // 路由配置 stores/ // Pinia状态管理(userStore, cartStore) views/ // 页面组件,按用户端/管理端分目录 utils/ // 工具函数(request拦截器、格式化工具)用户在母婴商城的浏览路径是:首页 -> 分类 -> 商品列表 -> 商品详情 -> 购物车 -> 订单结算 -> 支付 -> 订单中心。这个路径上有一个核心体验点:所有涉及用户身份的页面(购物车、订单结算等)都需要登录态,但用户在浏览商品时不一定需要登录。所以路由守卫不能简单地"未登录全部拦截"桌球。
我的做法是登录态用Pinia的userStore保存用户信息和token,路由守卫里区分"需要登录的页面"和"游客可访问页面"。用户进入结算页时如果发现未登录,弹窗提示登录,登录成功后通过redirect参数跳回原来要去的页面,保证流程不断。
管理端和用户端是两套独立的Vue应用吗?我在这个项目里用的是同一个应用,通过路由前缀区分/admin和/,管理端的路由用单独的路由表懒加载,配合一个角色判断的全局前置守卫。这样部署时只有一套前端资源,维护成本小。
4.2 移动端适配、购物车交互与性能细节
母婴商城的用户大部分都是拿手机逛的,所以前端必须要做移动端适配。我用的方案是rem布局,在入口文件里根据屏幕宽度动态设置根元素的font-size,配合postcss-pxtorem插件,开发时直接写px,编译时自动转rem。
购物车的交互做得比较细致。选中状态用checkbox控制,所有涉及金额的数字我都会做精度处理。这里有个经典的JavaScript浮点数问题:0.1加0.2不等于0.3,在计算商品总价时如果直接用浮点数相加,最终金额可能差几分钱。我的做法是价格统一用"分"为单位存储在数据库,前端展示时再转成"元",计算过程全部用整数运算。后端返回的数据也直接给"分"单位,格式化展示用组件formatPrice过滤函数统一处理。
商品列表页的性能优化:图片懒加载用Element Plus自带的v-lazy指令,滚动加载更多用IntersectionObserver监听页面底部的占位元素触发分页请求。接口请求统一走了封装的request工具,里面做了token自动附加、错误码统一提示、401自动跳转登录。详情页的富文本图片我用了缩放处理,在富文本内容渲染后把大图等比缩放到屏幕宽度,同时给图片加了点击放大预览,这套交互能明显提升阅读体验。
4.3 商品筛选与搜索交互设计
母婴商城的筛选需求比普通网购更精细,用户经常要按年龄、价格、品牌、功能组合筛选。比如"我要找200-300元之间,适合1-2岁宝宝的玩具"是一个非常常见的需求。
前端筛选栏我设计成多维度的筛选项,每个维度可以展开收起,选中的条件显示在筛选栏顶部,支持一键清除。筛选参数通过URL的query参数传递,这样用户筛选完刷新页面也不会丢失条件,而且可以分享链接给其他妈妈参考。
这个交互实现的关键点:筛选条件变化时重新请求商品列表接口,后端用Page对象接收分页参数,配合一个Map类型的筛选条件参数,动态拼接MyBatis-Plus的QueryWrapper。需要注意,不同商品类目的筛选维度不一样,奶粉有"段数"筛选,纸尿裤有"尺码"筛选,所以在接口设计时筛选条件用了一个通用结构:categoryId+List<FilterItem>(每个FilterItem包含attributeId和attributeValueIds的数组),这样前端配置类目筛选维度时,后端接口完全不用改动。
5. 部署上线与排坑实录
5.1 从本地到服务器的部署要点
开发环境跑通之后,部署上线是另一道坎。这套系统的部署链路是:Nginx做前端静态资源服务和反向代理,SpringBoot应用打包成jar包用systemd服务托管,MySQL和Redis都用Docker部署在云服务器上。
jar包打包前有几个细节要注意。第一是配置文件要区分环境,我用application.yml(公共配置)、application-dev.yml(开发环境)、application-prod.yml(生产环境),打包时通过--spring.profiles.active=prod参数激活对应环境。第二是MySQL连接串里要加useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai这三个参数,否则中文乱码和时区问题会让你排查到怀疑人生。
Nginx配置上有一个关键点:前端路由用的history模式,刷新页面时Nginx需要配置try_files回退到index.html,否则用户刷新详情页直接404:
location / { try_files $uri $uri/ /index.html; }/api前缀的请求反向代理到后端服务,同时配置了gzip压缩和静态资源的浏览器缓存策略。前端打了包之后,静态文件的体积能优化就优化,路由懒加载、组件按需引入、打包时把第三方库单独拆chunk,这些都能明显减少首屏加载时间。
生产环境的MySQL我单独跑了一个初始化脚本,创建了专门的业务账号(不是root),只授予业务库的增删改查权限,所有远程登录都通过SSH隧道或者内网IP,MySQL端口不对公网开放。这个安全习惯做好了,能省掉很多被拖库的风险。
5.2 开发中遇到的典型问题与排查实录
开发过程中我踩了不少坑,挑几个有代表性的列出来,也是大家复现这个项目时最可能遇到的。
第一个是跨域问题。前后端分离开发时,前端跑在8080端口,后端跑在8081端口,所有请求都会被浏览器拦截。我一开始在Controller上加了@CrossOrigin注解,结果只能解决部分接口的跨域,后来统一改成了WebMvcConfigurer里的全局跨域配置,问题才彻底解决。这里有个坑要注意:如果你同时上了Spring Security等安全框架,跨域配置要做两次——一次在安全过滤链里,一次在MVC层,不然放行的请求会在安全层又被拦掉。
第二个是MyBatis-Plus的字段映射问题。数据库字段我用的是下划线命名(create_time),Java实体用的是驼峰命名(createTime),MyBatis-Plus默认开启了下划线转驼峰,但如果用了自定义SQL且查询结果返回的是Map而不是实体,返回值key永远保持数据库字段名,前端拿到的字段名就对不上。解决方式是写@Results注解明确映射关系,或者在SQL里手动起别名转成驼峰。
第三个是后端传时间字段的时区差8小时问题。Java的LocalDateTime序列化默认用的是UTC,而用户在中国(东八区),导致前端展示的时间总是比实际慢8小时。排查发现是Jackson序列化的时区设置问题,在application.yml里加一行配置解决:
spring: jackson: time-zone: Asia/Shanghai第四个是Redis缓存穿透。商品详情页如果传一个不存在的商品ID,请求会直接打到数据库,缓存里没有、数据库也没有"这个数据,恶意循环请求就能打挂数据库。我的解决方式是缓存空值,把不存在的商品ID也缓存进去,value存一个特殊标记,过期时间设成正常缓存的1/5(比如正常缓存30分钟,空值缓存6分钟),这样能挡住大部分穿透流量。同时加了一个简单的接口限流,商品详情接口对同一IP的访问频率做限制,300次/分钟,超过就拒绝。
第五个是文件上传问题。商品图片上传用的本地磁盘存储,开发环境没问题,但生产环境如果用多实例部署,文件会存在不同机器的磁盘上导致图片加载不稳定。我换成了阿里云OSS的SDK(市面上主流的云存储方案都行),上传接口返回URL直接存数据库。如果只是自己部署演示用,本地存储配一个绝对路径也够用,但要注意Nginx需要把/uploads路径映射到磁盘目录,否则图片同样加载不出来。
5.3 文档编写与二次开发建议
这套项目附带的文档我花了很大心思。很多开源项目源码质量不错,但文档拉胯,接手的人看代码就像在看天书。我的文档分成三部分:
- 部署文档:从环境准备(JDK版本、Maven配置、MySQL初始化)、数据库导入、Redis启动、前端打包到后端jar包启动,每一步都有截图和命令。
- 接口文档:我用的是Apifox(也可以导出OpenAPI格式),把全部接口的请求参数、返回结构、业务码含义、示例都整理清楚。
- 开发指南:讲清楚项目目录结构、代码分层规范、如何新增一个模块、公共组件的使用方式。
二开的时候,我建议从这几个地方下手:把支付模块从模拟支付换成真实的第三方支付(需要企业资质);接入短信验证码登录;增加限时秒杀功能(用Redis预扣库存 + 消息队列异步削峰);把单机部署升级成前后端分离部署,再加上容器化编排。
6. 写在最后:这套系统让我重新理解了电商开发
做这个母婴商城之前,我以为电商系统最难的是那些花哨的营销功能,做完才发现真正决定系统质量的是最基础的那些模块:库存扣减的一致性、订单状态的闭环、敏感信息的加密、接口的幂等性。母婴这个领域又额外加了一层约束——你对用户的数据安全、对商品的品质管控承担的责任更大。特别是当我把这套代码部署上线,用一个真实的手机号注册、下单、模拟支付、确认收货,走完整个流程之后,对"系统设计决定业务上限"这句话的理解比看十遍理论都深刻。
给正在做类似项目的同学一个建议:不要急着堆功能,先把主链路走通,从商品浏览到支付完成,所有环节的数据在数据库里能完美闭环,这个系统就算立住了。至于营销活动、积分商城、直播带货那些花活,等主链路稳定了再逐步加上去,完全来得及。项目代码整理得比较干净,数据库脚本和开发文档也齐全,需要的直接拿去用,有问题欢迎随时交流。