ARTICLE · 1358178
JAVA智能商城AI智能商城系统源码支持小程序+公众号+H5
|
商城里的商品越来越多,用户却不一定更容易买:想找一件合适的商品,要在搜索、分类和详情页之间来回切换;客服重复回答规格、用途和配送问题,运营也很难知道推荐为什么有效。
这套系统把导购与交易放进同一条链路:先理解购物需求,再推荐可售商品,最后接住支付和订单。AI负责辅助选择,商品价格、库存、优惠与支付结果仍以服务端数据为准。
下面按八个功能点讲清它怎么跑,再给七段关键代码,覆盖导购记录、推荐服务、下单事务、库存扣减、支付回调、多端请求与后台订单列表。
OVERVIEW / 功能总览
八个模块,覆盖「导购 + 交易 + 履约」
功能点按真实购物顺序展开:先问需求、逛分类、接推荐,再进入专区与店铺,确认订单并完成支付,最后在用户端与后台共同管理订单。
|
01AI智能导购
输入:用户输入预算、用途、偏好等自然语言需求,也可从热门问题开始。
状态:系统先提取购物条件,再查询上架且有库存的商品;模型只负责组织表达,不直接决定价格和库存。
输出:输出商品卡片、匹配理由与查看详情入口,没有合适商品时明确提示并引导调整条件。

【AI智能商城】实拍 01|AI导购对话、商品详情与店铺特色商品;截图数据为演示数据
02分类导航
输入:用户选择分类、输入关键词,或从商品详情返回上一级继续浏览。
状态:分类树与商品归属在后台维护,筛选条件随列表请求提交,下架商品不进入前台结果。
输出:输出对应商品列表与空结果提示;分类调整不影响历史订单中的商品名称和成交快照。
03智能推荐弹窗
输入:输入当前页面、最近浏览和用户主动偏好,不用敏感信息做无关推断。
状态:弹窗有展示频次与关闭状态,同一推荐不连续弹出;推荐服务不可用时保留普通商品列表。
输出:输出少量候选商品与简短理由,用户可查看、关闭或继续选购,不自动加购、不自动下单。

【AI智能商城】实拍 02|订单详情、AI购物助手与我的订单;截图数据为演示数据
04AI购物助手
输入:输入商品比较、规格选择、配送与订单进度等追问,并携带当前会话编号。
状态:会话只保留必要的上下文;涉及订单时先校验归属,答案区分商品事实与模型生成内容。
输出:输出可继续操作的回答和商品入口;查不到的信息不编造,转人工或提示前往订单详情。
05特色商品专区
输入:运营按主题选择商品、设置排序和展示时间,用户从首页进入专区。
状态:专区只保存运营关系,商品上下架、售价与库存仍由商品主数据统一控制。
输出:输出主题列表、商品详情与购买入口,售罄或下架后立即收起不可售操作。

【AI智能商城】实拍 03|店铺主页、确认订单与我的收藏;截图数据为演示数据
06多支付渠道
输入:用户在确认订单页选择可用的微信支付或支付宝,并提交本次支付请求。
状态:服务端按订单金额创建预支付单;支付中、已支付、已关闭分开记录,重复回调只处理一次。
输出:输出明确的支付结果和后续入口,到账以支付平台验签后的异步通知为准。
07店铺主页
输入:输入店铺标识,用户可查看店铺介绍、特色商品与分类,也可继续搜索。
状态:营业状态、商品状态与店铺展示信息分开管理,停用店铺不出现在公共入口。
输出:输出统一的店铺首页与商品集合,订单仍保留成交时的店铺和商品快照。
08订单管理
输入:用户按状态查看订单,后台按订单号、店铺、时间和履约状态筛选。
状态:待付款、待发货、待收货、已完成与售后分开流转;每次操作记录操作者与时间。
输出:输出订单详情、物流或售后入口;用户只能看自己的订单,后台按角色与店铺范围授权。
ARCHITECTURE / 三端分工
一套交易数据,三端各管一段
|
一句话分工:AI提供选购建议,前端收集意图并展示结果;商品、金额、库存和订单状态始终由服务端确认,支付结果必须经过平台验签。
CODE / 数据库与核心代码
七段关键代码:从导购记录到订单履约
按「表结构 → 导购服务 → 下单事务 → 库存扣减 → 支付回调 → 多端请求 → 后台表格」的顺序给出示例。
以下均为示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。
01三张表记录导购与交易
导购需要可追溯,订单需要可核对;会话、推荐记录和订单不能混在一张表里。
CREATE TABLE ai_chat_session ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_no VARCHAR(32) NOT NULL UNIQUE, user_id BIGINT NOT NULL, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL, KEY idx_user_time (user_id, created_at));CREATE TABLE ai_recommend_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, session_no VARCHAR(32) NOT NULL, product_id BIGINT NOT NULL, product_name VARCHAR(128) NOT NULL, reason VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, KEY idx_session (session_no));CREATE TABLE mall_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, request_no VARCHAR(40) NOT NULL UNIQUE, user_id BIGINT NOT NULL, shop_id BIGINT NOT NULL, pay_amount DECIMAL(10,2) NOT NULL, pay_channel VARCHAR(20), status VARCHAR(20) NOT NULL, version INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL, KEY idx_user_status (user_id, status)); |
会话按用户隔离,推荐记录保存商品快照;订单号与请求号建立唯一约束。
02推荐结果必须回到商品库
模型可以组织需求,但商品是否可售、价格是多少,只能查询商品库。
public List<RecommendItem> recommend( Long userId, GuideCommand cmd) { GuideNeed need = aiClient.extractNeed( cmd.getText()); LambdaQueryWrapper<Product> qw = Wrappers.lambdaQuery(); qw.eq(Product::getStatus, 1) .gt(Product::getStock, 0) .le(need.getBudget() != null, Product::getPrice, need.getBudget()); if (need.getCategoryId() != null) { qw.eq(Product::getCategoryId, need.getCategoryId()); } List<Product> products = productMapper.selectList(qw); if (products.isEmpty()) { return Collections.emptyList(); } return products.stream().limit(3) .map(p -> RecommendItem.of( p.getId(), p.getName(), p.getPrice(), reasonBuilder.fromFacts(p, need))) .collect(Collectors.toList());} |
候选为空就返回空列表;推荐理由来自已查到的商品属性,不补写不存在的卖点。
03下单在一个事务里完成
订单、明细与库存需要保持一致,任一步失败都不能留下半截交易。
@Transactional(rollbackFor = Exception.class)public String create(Long userId, CreateOrderCmd cmd) { Order old = orderMapper.selectOne( Wrappers.lambdaQuery(Order.class) .eq(Order::getRequestNo, cmd.getRequestNo())); if (old != null) return old.getOrderNo(); Product p = productMapper.selectById( cmd.getProductId()); Assert.isTrue(p != null && p.getStatus() == 1, "商品不可售"); BigDecimal amount = priceService.quote( p, cmd.getQuantity(), userId); int changed = productMapper.deductStock( p.getId(), cmd.getQuantity()); if (changed != 1) { throw new BizException("库存不足"); } Order order = Order.create(userId, p, amount, cmd.getRequestNo()); orderMapper.insert(order); orderItemMapper.insert( OrderItem.snapshot(order, p, cmd)); return order.getOrderNo();} |
金额从服务端商品与优惠信息计算,异常统一回滚,前端不参与定价。
04库存用条件更新扣减
先查库存再更新会被并发请求穿透,条件更新能把判断和扣减合成一步。
@Update("UPDATE mall_product " + "SET stock = stock - #{qty}, " + "version = version + 1 " + "WHERE id = #{id} " + "AND stock >= #{qty} AND status = 1")int deductStock(@Param("id") Long id, @Param("qty") Integer qty);@Transactional(rollbackFor = Exception.class)public void closeExpired(String orderNo) { Order order = orderMapper .selectByOrderNo(orderNo); if (!"WAIT_PAY".equals(order.getStatus())) { return; } int changed = orderMapper.closeIfWaitPay( orderNo, order.getVersion()); if (changed == 1) { List<OrderItem> items = itemMapper .selectByOrderId(order.getId()); for (OrderItem item : items) { Long pid = item.getProductId(); Integer qty = item.getQty(); productMapper.restoreStock(pid, qty); } }} |
影响行数为零即失败;支付超时关闭订单时再按记录回补库存。
05支付回调先验签再幂等
支付结果不能由页面回跳决定,回调也必须防止平台重发造成重复入账。
@Transactional(rollbackFor = Exception.class)public void onPayNotify(PayNotify notify) { boolean valid = payGateway.verify( notify.getRawBody(), notify.getHeaders()); if (!valid) { throw new BizException("支付验签失败"); } Order order = orderMapper.selectOne( Wrappers.lambdaQuery(Order.class) .eq(Order::getOrderNo, notify.getOrderNo())); Assert.notNull(order, "订单不存在"); if (order.getPayAmount() .compareTo(notify.getAmount()) != 0) { throw new BizException("支付金额不一致"); } int changed = orderMapper.markPaid( order.getId(), notify.getTradeNo()); if (changed == 0) { log.info("重复回调忽略: {}", notify.getOrderNo()); return; } payLogMapper.insert(PayLog.success( order.getId(), notify.getTradeNo()));} |
验签、核对商户订单与金额后才更新;条件更新保证只有待支付订单能流转。
06多端共用一套请求封装
小程序、公众号、H5 若各写一套鉴权与错误处理,后续很容易行为不一致。
const newRequestNo = () => { const time = Date.now().toString(36) const rand = Math.random() .toString(36).slice(2, 10) return `${time}-${rand}`}export function request(options) { const token = uni.getStorageSync('token') return new Promise((resolve, reject) => { uni.request({ url: options.url, method: options.method || 'GET', data: options.data || {}, header: { Authorization: token ? `Bearer ${token}` : '', 'X-Request-No': options.requestNo || newRequestNo() }, success: (res) => { if (res.statusCode === 401) { uni.removeStorageSync('token') uni.reLaunch({ url: '/pages/login/index' }) reject(new Error('请先登录')) return } if (res.data.code !== 0) { reject(new Error(res.data.message)) return } resolve(res.data.data) }, fail: reject }) })} |
统一携带令牌和请求号,401 清登录态;AI请求与支付请求使用同一错误出口。
07订单列表按权限取数
后台列表不仅要能筛选,还要把店铺数据范围和订单状态流转一起管住。
<template> <div> <el-form :inline="true" :model="query"> <el-input v-model="query.orderNo" placeholder="订单号" clearable /> <el-select v-model="query.status" placeholder="状态" clearable> <el-option label="待付款" value="WAIT_PAY" /> <el-option label="待发货" value="WAIT_SHIP" /> <el-option label="待收货" value="SHIPPED" /> <el-option label="已完成" value="FINISHED" /> </el-select> <el-button type="primary" @click="load">查询</el-button> </el-form> <el-table :data="rows" v-loading="loading"> <el-table-column prop="orderNo" label="订单号" /> <el-table-column prop="shopName" label="店铺" /> <el-table-column prop="payAmount" label="实付金额" /> <el-table-column prop="statusName" label="状态" /> <el-table-column label="操作"> <template #default="scope"> <el-button type="text" v-if="scope.row.canShip" @click="ship(scope.row)"> 发货 </el-button> </template> </el-table-column> </el-table> <el-pagination :total="total" layout="total, prev, pager, next" :page-size="query.size" :current-page="query.page" @current-change="load" /> </div></template><script setup>import { reactive, ref, onMounted } from 'vue'import { getOrders, shipOrder } from '@/api/order'const query = reactive({ page: 1, size: 20 })const rows = ref([])const total = ref(0)const loading = ref(false)async function load(page = 1) { query.page = page loading.value = true try { const data = await getOrders(query) rows.value = data.records total.value = data.total } finally { loading.value = false }}async function ship(row) { await shipOrder(row.orderNo) await load(query.page)}onMounted(() => load())</script> |
查询条件在服务端组装,发货按钮只在待发货状态出现,并保留操作日志。
COMPLIANCE / 合规与上线
AI商城上线,这几条边界要写清
AI不替代交易事实。推荐和回答属于辅助信息,商品价格、库存、优惠与订单状态以服务端查询结果为准。
生成内容要可识别。明确AI助手身份,不伪装真人,不编造商品资质、效果或物流信息,必要时提供人工入口。
支付与隐私按最小范围处理。支付回调验签并核对金额;会话、地址与订单按用户隔离,日志不记录完整敏感信息。
上线前完成实际渠道配置。模型服务、内容审核、支付商户、退款与物流接口均需使用正式凭证并联调,示例代码不包含这些外部配置。
上线前建议逐项打勾
|
FIT / 适合谁
适合谁用
适合商品规格较多、用户需要比较与选购建议的零售团队,可把常见咨询沉淀为导购入口。
不太适合只希望改几次文案就自动带来成交的团队不太适合;AI导购仍需要商品资料、库存与运营规则共同维护。
二次开发服务端按SpringBoot分层,后台使用Vue与ElementUI,用户端使用uni-app,适合在此结构上继续接模型、支付与履约能力。
FAQ / 常见问题
常被问到的五个问题
Q:AI导购会直接替用户下单吗?A:不会。它只提供候选与解释,加购、确认订单和支付都由用户主动操作。
Q:AI回答的价格和库存可信吗?A:商品事实来自实时商品库,进入下单与支付时还会再次校验;模型不能自行定价或改库存。
Q:模型服务不可用时商城还能用吗?A:可以保留分类、搜索和普通商品列表,导购入口提示暂不可用,不阻断正常交易。
Q:多支付渠道如何避免重复入账?A:统一由服务端创建支付单,回调验签并核对金额,再通过状态条件更新保证重复通知只生效一次。
Q:源码包含哪些部分?A:包含SpringBoot + MyBatisPlus + MySQL服务端、Vue + ElementUI后台,以及可发布到小程序、公众号和H5的uni-app用户端;外部模型与支付能力需配置正式账号。
LAST / 写在最后
AI商城真正有价值的地方,不是多一个聊天框,而是让用户从说出需求到找到商品、完成订单,少几次无效跳转。
把推荐约束、商品事实、支付状态和订单权限放在同一套后端规则里,智能导购才能成为可维护的交易入口。
需要看完整功能清单或演示流程,可以在评论区留言说明你的经营品类。
文中截图均为演示数据,界面与文案随版本可能调整;具体功能以实际交付版本为准。














































































