ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

电子元器件商城系统开发实战:SpringBoot+微信小程序全流程解析

电子元器件商城系统开发实战:SpringBoot+微信小程序全流程解析 做电子元器件商城的系统开发说实话跟做普通商品商城完全是两码事。元器件品类多、规格参数复杂封装、耐压、容差、温度等级而且B端采购和C端零售的浏览习惯差异极大。这篇文章就是把我用SpringBoot搭后端、微信小程序做前端的完整过程梳理一遍从表结构设计到接口联调再到那些坑和绕过坑的办法一次性说清楚。1. 系统整体设计与技术选型思路1.1 项目背景与核心需求解析电子元器件商城跟卖衣服、卖数码产品的商城有几个核心差异第一元器件的SKU展开维度特别多一个型号的电容可能有十几个容值、封装组合第二用户往往是带着明确的型号或替代料号来搜索而不是像逛淘宝那样漫无目的第三价格体系复杂阶梯价对B端客户是刚需而微信小程序天然适合做轻量级的浏览和下单入口。从这个需求出发项目的核心路径就很清晰了需要一个能承载元器件复杂属性结构的后端服务还需要一个在微信生态里触手可及的前端入口。选SpringBoot的原因很实际——生态成熟、招人容易、部署省心Java体系里做电商类项目SpringBoot MyBatis-Plus MySQL这套组合可以说是标准答案。微信小程序这边则是因为它零安装、用完即走对采购工程师和硬件创业者来说手机里存个微信就够了不需要专门装App。整个系统我拆成了三个端微信小程序用户端、后台管理端Web、以及服务端API。用户端解决找料、选型、下单问题后台管理端解决商品、库存、订单、价格管理问题API层作为中间桥梁处理所有业务逻辑和数据交互。1.2 为什么选单体架构而不是微服务先明确一点这个商城项目的数据量级和团队规模决定了它不该一上来就上微服务。很多教程一谈起系统设计就扯微服务、消息队列、分布式事务但实际做一个元器件商城单体架构在开发效率、调试成本、部署复杂度上都有明显优势。我用单体架构的原因有三个业务边界尚不需要物理拆分。用户、商品、库存、订单、支付这五个模块在业务上是强关联的订单创建时扣库存、支付回调时改状态这些事务性操作在单体里用本地事务就能保证一致性拆成微服务反而要引入分布式事务方案。开发调试链路短。小程序端调后端接口本地起一个SpringBoot应用断点一打整条链路都能看穿。微服务化之后排查一个问题可能要跨好几个服务翻日志。部署成本低。一个jar包扔到服务器上就能跑配合Nginx就可以对外提供服务。对团队初期来说这个性价比是极高的。但我在设计时也留了拆分余地模块之间严格用Service接口隔离缓存、搜索、文件存储都做了独立封装将来业务量真的上来了可以按模块抽取独立服务而不需要推倒重来。这就是单体优先模块化编码的路线。1.3 技术栈全景与版本选型这里把配置列出来直接照抄可用。需要说明的是版本选型我踩过一个坑SpringBoot 2.x和3.x的差距不只是版本号底层Java版本要求、依赖命名空间、部分API行为都有变化。为了保证稳定最终采用的是社区资料最多、排错最容易的版本组合。组件版本说明JDK1.8 / 11二选一不要直接用JDK 17跑SpringBoot 2.x会有兼容问题SpringBoot2.7.x2.x系列的最后维护版本稳定、资料多MyBatis-Plus3.5.x增强版MyBatis内置分页、代码生成器MySQL5.7 / 8.08.0性能更好注意驱动和连接串差异Redis6.x缓存用户会话、商品热点数据Maven3.6项目构建管理微信小程序基础库2.x兼容大部分API这里有一个技术选型上的心得SpringBoot版本不要盲目追新。网上搜索时经常看到springboot版本太高这样的说法这其实不是新版本的错而是很多第三方starter没有跟上新版本的适配进度。比如某些支付SDK、短信SDK在SpringBoot 3.x下会遇到javax到jakarta命名空间的迁移问题处理起来相当头疼。对于商业项目选成熟版本比选最新版本要稳妥得多。2. 数据库设计与核心表结构2.1 元器件领域建模的特殊性元器件商城的表结构设计和普通电商最大的不同在商品属性建模上。普通电商的商品属性相对稳定颜色、尺码、款式用固定的SPU-SKU结构就能覆盖。但元器件的属性是多变的电阻有阻值、精度、功率、封装电容有容值、耐压、介质材料、温漂系数芯片有封装形式、工作温度范围、引脚数。如果针对每种品类建不同的属性表表结构会冗余到失控如果全部塞进一个通用属性字段查询和筛选又没法做。我采用的方案是SPUStandard Product Unit标准产品单元 SKUStock Keeping Unit库存保有单位 扩展属性JSON三层结构。SPU表存商品的公共信息品类、品牌、型号名称SKU表存具体的规格组合比如10kΩ ±1% 0603是一个SKU10kΩ ±5% 0603是另一个SKU扩展属性和参数信息存到一个JSON字段里用MySQL的json类型来存储和查询。这个方案的优点在于SPU和SKU满足了下单、库存、价格这些核心流程的刚性需求JSON字段则灵活承载了元器件的非结构化参数。实际使用中我用一个grep风格的参数搜索逻辑去查JSON字段用MySQL的JSON_CONTAINS表达式配合前缀匹配来实现按参数找料这种场景效果很好。2.2 核心表结构说明下面把最核心的几张表梳理一下spu_info商品表字段名类型说明idbigint主键category_idbigint品类IDbrandvarchar品牌item_modelvarchar完整型号titlevarchar商品标题descriptiontext简述spec_jsonjson关键规格参数JSONstatustinyint上架状态sku_info规格表字段名类型说明idbigint主键spu_idbigint关联SPUsku_specvarchar具体规格组合描述pricedecimal售价stockint库存step_pricejson阶梯价配置min_orderint起订量user_info用户表核心字段包括openid微信OpenID唯一标识、nickname、phone、default_address_id。order_info订单表核心字段包括order_no、user_id、total_amount、status待支付/已支付/已发货/已完成/已取消、pay_type、pay_time、address_snapshot下单时地址快照。order_item订单明细表核心字段包括order_id、sku_id、spu_id、single_price、quantity、total_price、spec_desc。cart_item购物车表核心字段包括user_id、sku_id、quantity、selected。search_log搜索记录表记录用户搜索关键词和频次用于后续做热门搜索推荐。这里有一个细节订单里的地址必须做快照。用户下单后可能修改了收货地址如果订单只记录地址ID到时候发货发到新地址就出问题了。把地址内容冗余到订单表是电商开发的常规操作也是血泪教训换来的经验。2.3 MyBatis-Plus使用要点很多开发者第一次用MyBatis-Plus都会踩到这个坑实体类的驼峰字段怎么转下划线分页插件为什么没生效总结几个核心要点全局配置下划线转驼峰。在application.yml里配置map-underscore-to-camel-case: true这样可以不用在每个实体字段上一一写TableField注解。分页插件必须显式配置。MyBatis-Plus的分页插件不是一个自动生效的配置需要手动添加一个PaginationInnerInterceptor否则selectPage方法拿到的分页数据永远只有前10条且total为0。这是一个极易踩的坑。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }逻辑删除用注解。不需要自己写update set deleted1 where id?实体类字段上加TableLogicMyBatis-Plus自动在查询、更新时过滤删除数据。自动填充时间字段。创建时间和更新时间用TableField(fill FieldFill.INSERT)配合MetaObjectHandler实现自动填充避免每个实体类都手动set时间。Component public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }这些配置一次性做好后面写业务代码时能省下大量重复劳动。3. 后端接口设计与业务功能落地3.1 微信登录认证链路小程序登录这套逻辑很多第一次接微信生态的后端开发会搞不清。微信小程序的登录不是一个输入账号密码的过程它的流程是小程序端调用wx.login()获取一个临时凭证code。小程序把code发给后端。后端拿codeappidappsecret去请求微信的jscode2session接口。微信返回openid用户唯一标识和session_key会话密钥。后端用openid去查用户表不存在则创建新用户最后生成自己的token返回给小程序。这里有一个容易踩的坑如果返回给小程序的是openid本身会带来安全隐患。正确做法是后端生成一个随机的token或使用JWT把openid和用户ID封装在token中小程序每次请求只携带token不暴露openid。我用的是JWT方案public String generateToken(UserInfo user) { return Jwts.builder() .setSubject(String.valueOf(user.getId())) .claim(openid, user.getOpenid()) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 3600 * 1000)) .signWith(signKey, SignatureAlgorithm.HS256) .compact(); }同时在后端写一个拦截器HandlerInterceptor对所有需要登录态的接口做令牌校验校验失败返回401。通过Autowired注入一个UserContext工具类用ThreadLocal存放当前登录用户信息接口里随时可以取。这里还要注意一个微信接口的细节jscode2session接口是有频次限制的单次code只能换取一次openid用完之后失效。因此在拼接请求时要设置合理的缓存策略不要每次都去微信接口刷新会话。3.2 商品搜索模块的实现元器件商城最核心的功能是找料。这个场景跟搜索引擎类似用户可能输入STM32F103C8T6也可能输入10k 0603 1%还可能输入钽电容 47uf 16v。不同输入形态对应了不同的搜索策略。我的实现方案是三分支精准型号搜索从spu_info表的item_model字段做等值匹配。如果用户搜了具体的型号直接返回该型号下的所有SKU精确匹配优先展示。参数搜索把用户输入的字符串按空格拆分成多个关键词分别匹配spec_json里的字段值。比如用户输入10k 0603 1%会拆成[10k, 0603, 1%]用JSON_CONTAINS匹配规格JSON。需要注意这类搜索MySQL的JSON索引效率并不高实际项目里如果数据量大可以用ES来做全文检索但数据量在5万以内、单库单表时MySQL的方案完全可以接受。模糊搜索用LIKE %关键词%匹配型号、品牌、描述字段作为兜底方案。搜索结果的排序规则也很重要。我给每个SPU加了一个热度权重字段search_countsale_count用户点击商品详情时权重自动增加搜索结果按权重从高到低排序。这样高人气商品会得到更好的展示位置而不是每次都按创建时间排序。public PageResultSpuInfo searchProducts(String keyword, int page, int size) { LambdaQueryWrapperSpuInfo wrapper new LambdaQueryWrapper(); // 1. 精确匹配型号优先 wrapper.eq(SpuInfo::getItemModel, keyword) .orderByDesc(SpuInfo::getSaleCount) .last(limit 10); ListSpuInfo exactList spuInfoMapper.selectList(wrapper); if (exactList.size() 0) { return PageResult.of(exactList, true); } // 2. 参数拆分匹配 String[] keywords keyword.split(\\s); // 拼接模糊查询条件... }这里有一个细节值得说明精确匹配的结果如果存在就不再过参数搜索和模糊搜索这样能最大程度保证搜索结果与用户意图的匹配度。3.3 购物车与订单流程设计购物车的实现相对简单一张cart_item表以用户ID为维度保存SKU ID和数量。需要注意的是并发控制——用户在A设备上加购、B设备上下单如果不加控制购物车数据可能覆盖丢失。我的做法是加购接口做UPDATE而非INSERT先查询是否存在同SKU记录存在就数量累加、不存在就新增核心是保证同一用户同一SKU只有一条购物车记录。订单流程是整个系统的重头。我按状态机的方式管理订单生命周期待支付用户提交订单后从购物车勾选商品创建订单。已支付用户完成支付后。已发货商家在后台管理端发货后。已完成用户确认收货后。已取消用户主动取消或超时未支付时。这里有几个关键决策下单时锁定库存。用户在提交订单那一刻就扣减库存而不是支付成功后才扣减。原因很简单如果支付后才扣库存会出现支付成功却无货可发的情况。扣减后如果用户超时未支付订单取消时要回补库存。库存扣减用乐观锁。update sku_info set stock stock - #{quantity} where id #{skuId} and stock #{quantity}通过SQL层面的条件更新来保证并发扣减的安全性避免出现超卖问题。订单号生成规则。用时间戳 用户ID后四位 随机数的组合保证全局唯一且可读性好。不要用UUID做订单号既难读又没法看出下单时间。订单提交使用事务。创建订单、扣减库存、清空购物车中的对应商品这三步必须在同一个Transactional事务方法内执行任何一个环节失败都要整体回滚。Transactional(rollbackFor Exception.class) public SubmitOrderResult submitOrder(ListLong cartItemIds, Long addressId) { // 1. 校验购物车数据 // 2. 创建订单主记录 // 3. 批量创建订单明细 // 4. 扣减对应SKU库存乐观锁条件更新 // 5. 删除已下单的购物车记录 // 6. 返回订单号 }3.4 阶梯价与购物车计算逻辑电子元器件的阶梯价是商城的特色功能。同样是100个0603电阻买100个是一个价买1000个是另一个价买10000个又是另一个价。这个逻辑我放在了sku_info表的step_price字段中[ {min: 1, price: 0.35}, {min: 100, price: 0.18}, {min: 1000, price: 0.09} ]计算接口在获取SKU单价或计算购物车总价时会根据用户选择的购买数量匹配对应的阶梯价格区间。这里有几个边界case需要注意数量在10到99之间时应该匹配min1这一档还是min100这一档我的规则是按向上取整匹配即数量落在哪个档位的区间内min且下一档min就匹配哪一档。阶梯价和会员折扣叠加时先算阶梯价再算折扣不能反过来。购物车展示的小计必须用阶梯价算否则用户下单时看到的价格和购物车展示不一致会被投诉。3.5 后台管理端核心功能后台管理端我用了Spring Boot Vue技术栈不是小程序。这里不用展开太多只讲几个核心模块商品管理支持批量导入元器件数据。我写了一个Excel导入模板用户按模板填好型号、品牌、品类、规格参数、阶梯价一键导入生成SPU和SKU记录。导入逻辑里做了一个关键校验同一型号、同一规格组合的SKU在库中已存在时不做重复创建而是提示用户更新价格或库存。订单管理按订单状态分组展示待处理订单、发货订单等。发货操作需要填写物流单号和物流公司系统对接了快递100的查询接口后端在每次订单状态变更时主动推送消息到小程序端。库存预警当SKU的库存低于预设的最低阈值时后台首页出现预警提示并支持一键生成采购补货清单。这个功能虽然简单但在实际运营中帮了大忙。4. 微信小程序端设计与实践4.1 小程序框架与页面结构规划小程序端整体用的是原生开发框架没有引入uni-app或Taro这类跨端方案。原因很简单项目只需要覆盖微信端原生框架的性能和API适配性最好跳过了跨端框架带来的中间层开销。如果未来要上支付宝小程序或抖音小程序再考虑跨端方案。页面结构上小程序分为四个Tab页和一个核心子页面集合首页推荐内容、热门元器件、品类入口。分类元器件分类树按品类划分比如电阻/电容/电感/二极管/三极管/IC/连接器等右侧列表展示该品类下的商品。购物车展示购物车商品支持数量修改、勾选计算总价、删除。我的用户信息、订单入口、地址管理、帮助中心。子页面包括搜索页、商品列表页、商品详情页、提交订单页、订单列表页、订单详情页、地址编辑页。4.2 微信登录与授权处理的细节小程序端的登录有几种实现方式。常见的做法是页面加载时触发wx.login()获取code然后调后端登录接口换取token存到Storage中。但这里有一个反复遇到的问题用户首次进入小程序时如果没有点击同意授权按钮wx.login()是可以正常调用的静默登录但wx.getUserProfile()获取用户头像昵称会失败。这是微信平台的规则限制。我的处理方案是分两步静默登录小程序App.js的onLaunch中调用wx.login()获取code后静默调后端接口完成用户识别。此时用户已经是一个合法的匿名用户可以浏览商品、加购但不能下单下单需要手机号。手机号授权下单前要求用户授权手机号通过button open-typegetPhoneNumber后端拿到手机号后更新用户信息。这个设计的好处是不会在用户刚进来就弹一堆授权框把用户吓跑。手机号授权有一个大坑wx.getPhoneNumber这个接口返回的手机号数据需要后端用session_key解密或使用官方云开发能力的加密解密接口。在原生框架下我使用的是后端解密方案——将encryptedData和iv传给后端后端用session_key调用AES算法解密得到手机号这个代码如果自己不熟悉加密流程照着微信官方文档抄也容易抄错解密模式一定要单测多跑几种不同手机号的case。4.3 商品列表与触底加载小程序端的商品列表要处理好加载更多的问题。小程序不像Web有方便的操作方式触底加载是唯一的常规加载方式很多人在这里会踩到分页重复的问题。我总结的可靠套路是页面data中维护pageNum、pageSize、hasMore三个变量。每次数据返回时用concat拼接到列表数组而不是覆盖。触底时利用onReachBottom生命周期判断hasMore为true才请求下一页。// pages/product/list.js data: { productList: [], pageNum: 1, pageSize: 10, hasMore: true, loading: false }, onReachBottom() { if (this.data.hasMore !this.data.loading) { this.loadProducts(); } }, async loadProducts() { this.setData({ loading: true }); const params { pageNum: this.data.pageNum, pageSize: this.data.pageSize, keyword: this.data.keyword }; const res await request.get(/api/product/search, params); const list res.data.list || []; this.setData({ productList: this.data.productList.concat(list), pageNum: this.data.pageNum 1, hasMore: list.length this.data.pageSize }); this.setData({ loading: false }); }这个方案的要点在于hasMore的判断逻辑当返回的列表长度等于pageSize时认为可能还有下一页小于pageSize则说明没有更多了。后端接口返回时同时把total和hasMore字段一起返回会更清晰。有些细节做得不好会直接影响用户体验加载状态需要避免重复请求网络慢时需要在页面底部加一个加载中的状态提示没有更多数据时不要在底部一直空白发出没有更多了的提示比较合适。4.4 小程序端购物车与下单流程的实现购物车在小程序端是一个很关键且状态较多的页面。用户在这个页面做的操作包括加/减数量、勾选/取消勾选、删除商品、全选/反选、刷新价格。为避免频繁调后端接口我的设计方案是购物车本地状态管理购物车数据一次加载进来本地维护数量和勾选状态。操作后批量同步用户点击结算时把勾选商品的ID和最新数量一次性提交给后端。后端校正下单前后端检查本地提交的数量和库存是否匹配返回最新的价格信息和可用库存供前端展示确认。这样做的好处是用户加减数量时页面响应迅速不会每点一次减号就发一个请求同时结算时保留了后端校验的严谨性。提交订单的页面需要展示收货地址可点击切换、商品清单、价格明细商品金额、运费、优惠、起订量校验低于最小起订量需提示。这一整套UI交互在小程序里不算复杂但状态同步容易出错。比如用户改了数量价格明细没有刷新或者用户选择了地址A切到地址B后地址快照没有同步更新——这些都是在联调中反复修过的问题建议尽早建立清晰的数据流。5. 高频问题与排查技巧实录5.1 接口请求失败域名配置与本地联调的坑小程序有一个硬性要求request请求的域名必须是HTTPS且在微信公众平台后台配置过合法域名。在开发阶段可以在开发者工具中勾选不校验合法域名来跳过这个限制但如果真机预览或体验版发布配置错一个域名整个请求直接失败且报错信息不太直观。排查办法是看小程序的Network面板如果请求状态是blocked或failed优先检查域名配置和HTTP协议。另外本地联调时经常遇到一个问题后端接口写的是http://localhost:8080小程序端真机预览时请求发到手机本机导致连接不上。原因是手机上的localhost是手机自己而不是开发机。正确做法是后端启动时绑定0.0.0.0默认就是小程序端请求地址写开发机的局域网IP例如http://192.168.1.100:8080。如果手机和开发机不在同一Wi-Fi就用Charles或Fiddler做代理映射这也是网上charles使用教程场景里最常见的需求。还有一点HTTPS证书问题。上线后生产环境必须用HTTPS我建议直接配置Nginx反向代理证书用免费的Lets Encrypt或者云厂商的免费证书即可。SpringBoot本身不需要配置SSL在Nginx层把HTTPS请求转发到内网SpringBoot的HTTP端口配置省心也方便后续扩展。5.2 购物车数据错乱与Session兼容问题购物车数据错乱多半是前端和后端数据同步出现了时间差。曾经遇到过一个场景用户在小程序端把数量从100改成80前端立即显示了总价但提交后后端拿到的还是100因为前端异步请求没有等到后端返回值就刷新了总价最终下单金额和页面显示不一致。解决方案是总价计算统一以后端返回的阶梯价结果为准前端的预计总价只是一个展示效果。提交订单接口返回订单信息时前端要重新渲染一遍最终金额。另外在SpringBoot后端使用HttpSession保存登录状态时要注意小程序端的请求头默认不携带Cookie除非显式设置所以基于Session的登录态方案在小程序端是行不通的。这也是为什么我改用TokenJWT方案每次请求在Authorization头携带Token后端解析校验。如果用了Spring Security要特别注意CSRF防护默认开启的问题Token方案下应该关闭CSRF否则每次请求都会429或403这是我初版服务上线时踩到的第一个安全相关的坑。5.3 微信支付接入的注意点微信支付是商城系统绕不开的环节。部署支付功能时我踩了几个坑值得写下来小程序支付需要开通微信支付商户号且商户号必须是小程序类型的应用关联。这里要先把小程序和商户号关联起来否则调起支付时会报商户号与AppID不匹配的错误。支付回调地址必须是公网HTTPS地址且要在支付平台后台正确配置否则用户支付成功后回调无法送达订单状态无法更新。支付金额单位是分。后端接收金额时要转换前端传入35.80元传到微信支付API的参数变成3580分。如果不注意这个转换支付金额会相差100倍这个问题造成了严重的资损风险我单独写单测来保证金额处理逻辑正确。回调验签。微信支付回调会带签名信息后台必须用商户APIv3密钥验签后再更新订单状态防止伪造回调。支付部分代码量不小核心逻辑是统一下单接口拿到prepay_id生成支付参数返回给小程序小程序收到参数调用wx.requestPayment用户支付完成后微信服务器异步回调后台接口后台根据回调结果更新订单状态。5.4 微信小程序从开发到体验版的发布流程很多开发到后期会卡在发布环节小程序不像普通网站改完就生效需要经过开发版→体验版→审核→正式版的流程。我自己的做法是先用开发者工具在源代码目录下执行详情 → 上传版本上传成功后在微信公众平台的小程序管理后台看到开发版本然后点击选为体验版。体验版需要绑定体验成员在成员管理中添加体验成员用微信扫码后即可在真机上运行这个版本适合做内测收集反馈。正式发布需要提交审核审核通过后点击发布小程序才会对所有用户生效。这里有一个经验如果有版本bug需要修复可以先通过版本回退回到上一个正式版而不是发布一个存在问题的半成品。发布和回退的权限要留给稳定可靠的人来操作避免误操作引发线上故障。5.5 网络排查利器Charles的使用场景在微信小程序开发和调试中Charles抓包几乎是必备工具。这里简单说一下场景小程序端请求发出去之后真的能到后端吗后端返回的数据到底是什么手机真机上哪些接口报错但开发者工具不报错所有这些问题Charles都能直观展示。Charles的核心配置开启SSL代理Proxy → SSL Proxying Settings中勾选并添加域名*或者目标后端域名。手机端配置代理手机Wi-Fi设置HTTP代理为开发机IP和Charles的默认端口8888。下载信任证书访问chls.pro/ssl下载证书并安装信任否则HTTPS流量抓不到。需要特别注意的是小程序基座部分流量可能不走系统代理这时候需要在微信开发者工具中设置代理为Charles。我在实际业务中遇到过这个几率挺高的坑排查了很久才发现是代理设置问题。5.6 常见问题速查表问题可能原因解决方案小程序请求报errno: 600001请求域名未配置或未使用HTTPS在微信公众平台配置合法域名wx.login()返回code但后端换取session失败appid/appsecret不匹配核对小程序AppID与密钥订单创建成功但库存没变化事务没有生效检查Transactional注解是否配了rollbackFor且自调用不走代理分页接口返回total恒为0MyBatis-Plus分页插件未配置添加PaginationInnerInterceptor小程序真机预览请求失败但工具正常请求地址写了localhost改成开发机局域网IP支付金额差100倍元与分单位混淆统一用分存储金额用户下单后库存超卖扣库存操作没有条件约束使用stock #{quantity}条件更新商品数量为0还能被下单缺少起订量校验下单前校验min_order6. 项目代码结构与其他扩展方向6.1 后端代码组织方式代码结构上我没有用网上很多教程中那种controller/service/mapper三层就完事的分包方式而是按功能模块分包这样模块之间边界更清晰将来做服务拆分也更方便。com.example.ymall ├── common // 通用类返回结果封装、异常处理、常量 ├── config // 配置类MyBatis-Plus、Redis、拦截器注册 ├── controller // 接口层按业务域拆分 │ ├── UserController │ ├── ProductController │ ├── CartController │ ├── OrderController │ └── PayController ├── service // 服务层业务逻辑 │ ├── UserService │ ├── ProductSearchService │ ├── CartService │ ├── OrderService │ └── PayService ├── mapper // 数据访问层MyBatis-Plus Mapper ├── entity // 数据库实体 ├── dto // 数据传输对象请求参数、响应VO └── utils // 工具类JWT、Excel导入、价格计算这个结构的特点是DTO和Entity分开。Entity直接映射数据库表结构DTO用于接口参数接收和结果返回避免数据库表结构调整时误伤接口协议。返回结果统一封装成ResultT结构code、message、data前端通过code判断是否成功。异常处理用RestControllerAdvice统一捕获业务异常抛出BusinessException由全局异常处理器转成友好提示返回不直接向用户展示500错误信息。6.2 未来扩展方向系统现在能跑起来但距离一个完整的商用系统还有不少扩展空间接入ElasticSearch商品超过10万条后MySQL的LIKE和JSON搜索无法保证性能ES的倒排索引对型号、参数类搜索支持更友好。引入消息队列订单创建、支付回调、短信通知这些非核心链路可以异步化避免用户请求被慢操作拖垮。价格和库存的实时同步如果未来对接了ERP或现有库存系统需要增加定时同步任务保证商城和库存系统数据一致。供应链流程打通元器件商城长远看核心竞争在供应链。系统可以考虑扩展到询报价、样品申请、BOM匹配、替代料推荐等功能。开发这个商城的过程本质上是一次典型的从业务理解到系统实现的完整闭环。个人在实际开发中的最大体会是不要一开始就追求系统完美先把主链路跑通再在每一个环节用真实的业务反馈来驱动系统演进。开发过程中踩坑不可避免但把每一次踩坑都沉淀为经验和文档会让后续维持和迭代变得顺畅很多。整个系统的价值不在技术本身而恰好在于这些细节——从设计数据库表到调整一个支付回调从给搜索添加权重到修复一个分页Bug每一步积累起来才构成一个让用户觉得好用的商城。希望这篇记录能帮到正在做类似项目的朋友少走几步弯路那这些经历就更有意义了。
返回列表