为了解决这一痛点,我们基于 Java + Spring Boot 构建了一套可落地的 “库存双向同步系统”。 系统以 5 分钟为同步周期,自动检测外卖平台(美团/饿了么)与线下收银系统(商米、思迅或 Excel 导入)的库存差异,实现 “线下→线上” 与 “线上→线下” 的双向数据一致性。
对于中小商户而言,库存差异是外卖与线下经营中的“隐形杀手”。 线下卖完、线上还在售——导致“缺货订单”; 线上订单多、线下库存没更新——结果“超卖赔单”。
为了解决这一痛点,我们基于 Java + Spring Boot 构建了一套可落地的 “库存双向同步系统”。 系统以 5 分钟为同步周期,自动检测外卖平台(美团/饿了么)与线下收银系统(商米、思迅或 Excel 导入)的库存差异,实现 “线下→线上” 与 “线上→线下” 的双向数据一致性。
系统整体架构(轻量三层结构)
项目目录结构
/usr/local/java/sync
├── src/main/java/com/icoderoad/inventory
│ ├── controller
│ ├── service
│ ├── mapper
│ └── entity
├── src/main/resources
│ ├── application.yml
│ └── mapper/*.xml
└── pom.xml
系统架构采用典型的三层模型:
┌─────────────────┐ ┌────────────────────────────────┐ ┌────────────────────────┐
│ 微信小程序 │◄────►│ Spring Boot 后端服务(MySQL、定时任务)│◄────►│ 外卖平台 / 收银系统 API │
│(配置入口) │ │ │ │ 美团 / 饿了么 / 商米等 │
└─────────────────┘ └────────────────────────────────┘ └────────────────────────┘
设计目标:
- 轻量化部署:无复杂中间件依赖
- 模块化职责清晰:映射管理、库存同步、日志追踪
- 可平滑扩展:支持 Excel 导入、扫码匹配、实时推送
数据库设计
商品映射表(product_mapping)
用于建立 “线下商品” 与 “线上商品” 的对应关系。
CREATE TABLE `product_mapping` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`shop_id` VARCHAR(50) NOT NULL COMMENT '店铺ID',
`offline_product_id` VARCHAR(50) NOT NULL COMMENT '线下商品ID',
`offline_product_name` VARCHAR(100) NOT NULL COMMENT '线下商品名称',
`platform_type` TINYINT NOT NULL COMMENT '平台类型:1=美团,2=饿了么',
`online_product_id` VARCHAR(50) NOT NULL COMMENT '线上商品ID',
`online_product_name` VARCHAR(100) NOT NULL COMMENT '线上商品名称',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_shop_offline_platform` (`shop_id`,`offline_product_id`,`platform_type`)
) COMMENT='商品映射表';
库存记录表(inventory_record)
记录线下与线上库存数据的同步结果。
CREATE TABLE `inventory_record` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`shop_id` VARCHAR(50) NOT NULL COMMENT '店铺ID',
`offline_product_id` VARCHAR(50) NOT NULL COMMENT '线下商品ID',
`current_stock` INT NOT NULL DEFAULT 0 COMMENT '可售库存',
`offline_actual_stock` INT NOT NULL DEFAULT 0 COMMENT '线下实际库存',
`online_occupied_stock` INT NOT NULL DEFAULT 0 COMMENT '线上未发货订单占用',
`last_sync_time` DATETIME DEFAULT NULL COMMENT '最后同步时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_shop_offline` (`shop_id`,`offline_product_id`)
) COMMENT='库存记录表';
同步日志表(sync_log)
记录同步任务执行情况。
CREATE TABLE `sync_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`shop_id` VARCHAR(50) NOT NULL,
`sync_type` TINYINT NOT NULL COMMENT '1=线上→线下, 2=线下→线上, 3=手动同步',
`sync_result` TINYINT NOT NULL COMMENT '0=失败, 1=成功',
`message` VARCHAR(500) DEFAULT NULL COMMENT '失败原因',
`sync_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_shop_time` (`shop_id`,`sync_time`)
) COMMENT='同步日志表';
核心业务模块实现
商品映射管理模块(ProductMappingController)
package com.icoderoad.inventory.controller;
@RestController
@RequestMapping("/api/product-mapping")
public class ProductMappingController {
@Autowired
private ProductMappingService mappingService;
// 添加映射关系
@PostMapping("/add")
public Result<Void> addMapping(@RequestBody ProductMappingDTO dto) {
mappingService.addMapping(dto);
return Result.success();
}
// 查询映射列表
@GetMapping("/list")
public Result<List<ProductMappingVO>> getMappingList(@RequestParam String shopId) {
return Result.success(mappingService.listByShopId(shopId));
}
}
库存同步引擎(核心逻辑)
(1)拉取线上订单
package com.icoderoad.inventory.service;
@Service
public class OnlineOrderService {
// 获取美团5分钟内新订单
public List<MeituanOrder> pullMeituanOrders(String shopId, LocalDateTime start, LocalDateTime end) {
String sign = generateSign(shopId, start, end);
String url = "https://open-meituan.com/api/v1/order/list?shop_id=" + shopId
+ "&start_time=" + start + "&end_time=" + end + "&sign=" + sign;
String response = HttpClientUtil.get(url);
return JSON.parseArray(response, MeituanOrder.class);
}
// 拉取饿了么订单(同理实现)
}
(2)读取线下库存(API 或 Excel)
@Service
public class OfflineStockService {
// 调用商米API获取库存
public List<OfflineStock> getShangmiStock(String shopId) {
String url = "https://shangmi-api.com/stock?shop_id=" + shopId;
String response = HttpClientUtil.get(url);
return JSON.parseArray(response, OfflineStock.class);
}
// 解析 Excel 文件
public List<OfflineStock> parseExcelStock(MultipartFile file) throws IOException {
List<OfflineStock> stocks = new ArrayList<>();
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
stocks.add(new OfflineStock(
row.getCell(0).getStringCellValue(),
(int) row.getCell(1).getNumericCellValue()
));
}
}
return stocks;
}
}
(3)双向同步核心逻辑
@Service
@Transactional
public class InventorySyncService {
@Autowired private OnlineOrderService onlineOrderService;
@Autowired private OfflineStockService offlineStockService;
@Autowired private InventoryRecordMapper inventoryMapper;
@Autowired private ProductMappingService mappingService;
// 线上→线下
public void syncOnlineToOffline(String shopId) {
LocalDateTime end = LocalDateTime.now();
LocalDateTime start = end.minusMinutes(5);
List<MeituanOrder> mtOrders = onlineOrderService.pullMeituanOrders(shopId, start, end);
List<ElemeOrder> eleOrders = onlineOrderService.pullElemeOrders(shopId, start, end);
Map<String, Integer> occupied = calculateOnlineOccupied(mtOrders, eleOrders, shopId);
occupied.forEach((pid, num) -> inventoryMapper.updateOnlineOccupied(shopId, pid, num));
}
// 线下→线上
public void syncOfflineToOnline(String shopId) {
List<OfflineStock> stocks = offlineStockService.getLatestOfflineStock(shopId);
for (OfflineStock s : stocks) {
InventoryRecord record = inventoryMapper.getByShopAndProduct(shopId, s.getProductId());
int available = record.getOfflineActualStock() - record.getOnlineOccupiedStock();
syncToPlatform(shopId, s.getProductId(), available);
inventoryMapper.updateCurrentStock(shopId, s.getProductId(), available);
}
}
private void syncToPlatform(String shopId, String offlinePid, int stock) {
List<ProductMapping> mappings = mappingService.getByOfflineProduct(shopId, offlinePid);
for (ProductMapping m : mappings) {
if (m.getPlatformType() == 1) meituanApi.updateStock(m.getOnlineProductId(), stock);
if (m.getPlatformType() == 2) elemeApi.updateStock(m.getOnlineProductId(), stock);
}
}
}
定时任务 + 手动触发模块
@Service
public class SyncTask {
@Autowired private InventorySyncService syncService;
@Autowired private SyncLogService syncLogService;
@Autowired private ShopService shopService;
// 每5分钟执行
@Scheduled(cron = "0 0/5 * * * ?")
public void autoSync() {
for (String shopId : shopService.getAllShopIds()) {
try {
syncService.syncOnlineToOffline(shopId);
syncService.syncOfflineToOnline(shopId);
syncLogService.saveLog(shopId, 3, 1, "定时同步成功");
} catch (Exception e) {
syncLogService.saveLog(shopId, 3, 0, "同步失败:" + e.getMessage());
}
}
}
// 手动同步(微信小程序入口)
public void manualSync(String shopId) {
try {
syncService.syncOnlineToOffline(shopId);
syncService.syncOfflineToOnline(shopId);
syncLogService.saveLog(shopId, 3, 1, "手动同步成功");
} catch (Exception e) {
syncLogService.saveLog(shopId, 3, 0, "手动同步失败:" + e.getMessage());
throw new BusinessException("同步失败,请稍后重试");
}
}
}
库存状态可视化后台界面
为了让运营人员能够直观了解库存同步情况,我们设计了一个基于 Thymeleaf + Bootstrap + Chart.js 的后台页面,实现如下功能:
- 表格展示当前库存差异;
- 动态条形图显示各商品库存状态;
- 每 5 分钟自动刷新一次数据。
Controller 层
package com.icoderoad.stock.controller;
import com.icoderoad.stock.repository.ProductStockRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequiredArgsConstructor
public class StockViewController {
private final ProductStockRepository repository;
/** 访问后台库存页面 */
@GetMapping("/admin/stock")
public String stockPage(Model model) {
model.addAttribute("stocks", repository.findAll());
return "stock-view";
}
/** 提供库存数据接口(Chart.js 拉取用) */
@GetMapping("/admin/stock/data")
@ResponseBody
public Object stockData() {
return repository.findAll();
}
}
Thymeleaf 页面:src/main/resources/templates/stock-view.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>库存状态可视化后台</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="bg-light">
<div class="container mt-5">
<h3 class="mb-4 text-center">📊 外卖平台库存同步监控后台</h3>
<!-- 表格展示 -->
<div class="card shadow-sm mb-4">
<div class="card-body">
<h5 class="card-title mb-3">库存详情</h5>
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>商品编码</th>
<th>商品名称</th>
<th>本地库存</th>
<th>平台库存</th>
<th>最后同步时间</th>
</tr>
</thead>
<tbody>
<tr th:each="s : ${stocks}">
<td th:text="${s.productCode}"></td>
<td th:text="${s.productName}"></td>
<td th:text="${s.localStock}"></td>
<td th:text="${s.remoteStock}"></td>
<td th:text="${s.updateTime}"></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Chart.js 图表 -->
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title mb-3">库存对比图</h5>
<canvas id="stockChart" height="100"></canvas>
</div>
</div>
</div>
<script>
const ctx = document.getElementById('stockChart');
async function loadChartData() {
const res = await fetch('/admin/stock/data');
const data = await res.json();
const labels = data.map(d => d.productName);
const localStocks = data.map(d => d.localStock);
const remoteStocks = data.map(d => d.remoteStock);
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [
{
label: '本地库存',
data: localStocks,
borderWidth: 1
},
{
label: '平台库存',
data: remoteStocks,
borderWidth: 1
}
]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true }
}
}
});
}
loadChartData();
// 每5分钟刷新一次数据
setInterval(loadChartData, 300000);
</script>
</body>
</html>
微信小程序端设计
|
页面 |
功能说明 |
|
商品映射页 |
维护线下与线上商品对应关系 |
|
库存状态页 |
展示实时库存与“立即同步”操作 |
|
同步日志页 |
显示最近 10 条同步结果与错误原因 |
基于 uni-app 实现,一套代码支持微信与支付宝小程序。
关键技术要点与避坑建议
- 平台 API 调用签名
- 美团与饿了么需申请
appKey、secret; - 请求参数需按时间戳、签名算法严格生成;
- 控制 API 调用频率,避免被限流。
- 库存计算准确性
- 仅统计“已支付未发货”订单;
- Excel 导入库存需记录时间戳,以最新数据为准。
- 异常与重试机制
- 使用
@Retryable实现 API 自动重试; - 库存为负时提示“库存不足”,并记录日志。
- 安全与配置隔离
- 使用
token认证小程序用户; - 敏感信息通过环境变量或 Spring Config 存储。
部署方案与成本控制
|
项目 |
方案 |
年成本 |
|
服务器 |
阿里云轻量应用服务器(2核4G) |
¥1000 |
|
数据库 |
MySQL 8.0(同机部署) |
¥0 |
|
小程序认证 |
微信官方认证 |
¥300 |
|
开发成本 |
个人开发 2–3 周 |
约 ¥5000 |
💡 初期总成本约 ¥1300 / 年,适合小微商户快速上线。
验证与商业化路径
冷启动验证 选择 3–5 家便利店免费试用,收集反馈。
迭代优化
- 扫码匹配商品条码
- 缩短同步间隔(3 分钟)
- 接入“订单推送 API”实现实时同步
盈利模式 按店铺收取月费 ¥50,30 家店即可覆盖全部成本。
结论:让库存同步真正“自动化、可信赖”
这套基于 Spring Boot + 定时任务 + 平台 API 对接 的方案,用极低的开发与运维成本,实现了稳定、精准、可扩展的库存双向同步。 它不仅节省了人工更新成本,更避免了库存误差导致的差评和损失。
先让 1 家店同步跑通,再放大规模——这是中小商户数字化转型最务实的路径。 用熟悉的 Java 技术栈,你就能轻松打造一个“自动校准库存的隐形员工”。
AI大模型学习福利
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

被折叠的 条评论
为什么被折叠?



