- 引入 alibabacloud/dysmsapi-20170525 依赖,集成阿里云短信服务 - 将短信验证码从6位改为4位,优化用户体验 - 添加聚水潭商品按时间范围同步功能,支持分页拉取 - 增强Cron任务异常处理,添加错误日志记录 - 修复系统附件服务中图片路径处理逻辑 - 更新阿里云SDK相关组件至最新版本 - 添加think-trace调试扩展,便于问题排查 - 优化跨域中间件,使用str_contains替代strpos
466 lines
13 KiB
Markdown
466 lines
13 KiB
Markdown
# 商城商品推送到聚水潭分析
|
||
|
||
## 一、答案
|
||
|
||
### ❌ 当前状态:**推送功能已禁用**
|
||
|
||
在 [CreateSuccess.php#L80-99](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php#L80-99) 中,推送到ERP的代码**被注释掉了**:
|
||
|
||
```php
|
||
//ERP功能开启
|
||
// if (sys_config('erp_open') && $data['product_type'] == 0) {
|
||
// //上传商品至ERP平台
|
||
// ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
// ...
|
||
// }
|
||
```
|
||
|
||
---
|
||
|
||
## 二、推送功能架构
|
||
|
||
### 2.1 推送流程图
|
||
|
||
```
|
||
商城商品创建成功
|
||
↓
|
||
触发 CreateSuccess 事件监听器
|
||
↓
|
||
检查 ERP 功能是否开启 (erp_open = 1)
|
||
↓
|
||
├─ 未开启 → 不推送
|
||
│
|
||
└─ 开启 → ProductSyncErp::dispatchDo('upProductToErp', [$id])
|
||
↓
|
||
┌──────────────┐
|
||
│ 商品数据转换 │
|
||
│ │
|
||
│ 商品主数据 │
|
||
│ SPU编码 │
|
||
│ 商品名称 │
|
||
│ SKU规格 │
|
||
│ 价格信息 │
|
||
└──────┬───────┘
|
||
↓
|
||
调用聚水潭API
|
||
/open/jushuitan/itemsku/upload
|
||
↓
|
||
推送商品到聚水潭平台
|
||
```
|
||
|
||
### 2.2 核心文件
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| [CreateSuccess.php](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php#L80-99) | 商品创建事件监听器 |
|
||
| [ProductSyncErp.php](file:///home/jone/workspace/CRMEB_PRO/app/jobs/product/ProductSyncErp.php#L31-61) | 推送任务(upProductToErp方法)|
|
||
| [Product.php](file:///home/jone/workspace/CRMEB_PRO/crmeb/services/erp/storage/jushuitan/Product.php#L36-54) | 聚水潭API调用封装 |
|
||
|
||
---
|
||
|
||
## 三、推送逻辑详解
|
||
|
||
### 3.1 商品创建监听器
|
||
|
||
**文件**: [CreateSuccess.php#L79-99](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php#L79-99)
|
||
|
||
```php
|
||
//ERP功能开启
|
||
if (sys_config('erp_open') && $data['product_type'] == 0) {
|
||
// 1. 上传商品至ERP平台
|
||
ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
|
||
// 2. 上传商品至ERP默认门店
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [
|
||
$id,
|
||
['erp_shop_id' => sys_config('jst_default_shopid')]
|
||
]);
|
||
|
||
// 3. 给所有绑定了ERP店铺的门店增加该商品
|
||
$systemStoreServices = app()->make(SystemStoreServices::class);
|
||
$erpStoreList = $systemStoreServices->getStoreList([
|
||
['erp_shop_id', '>', 0]
|
||
]);
|
||
foreach ($erpStoreList['list'] as $item) {
|
||
if ($item['erp_shop_id'] < 1) continue;
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [$id, $item]);
|
||
}
|
||
|
||
// 4. 如果是新商品,同步库存
|
||
if ($is_new) {
|
||
ProductSyncErp::dispatchDo('stockFromErp', [[$id]]);
|
||
}
|
||
}
|
||
```
|
||
|
||
**触发条件**:
|
||
- ✅ `erp_open` 配置已开启
|
||
- ✅ 商品类型为 `product_type == 0`(普通商品,非虚拟商品等)
|
||
- ✅ 商品创建成功事件被触发
|
||
|
||
### 3.2 数据转换逻辑
|
||
|
||
**文件**: [ProductSyncErp.php#L31-61](file:///home/jone/workspace/CRMEB_PRO/app/jobs/product/ProductSyncErp.php#L31-61)
|
||
|
||
```php
|
||
public function upProductToErp($id)
|
||
{
|
||
try {
|
||
// 1. 获取商品信息
|
||
$productServices = app()->make(StoreProductServices::class);
|
||
$productInfo = $productServices->getInfo($id)['productInfo'];
|
||
|
||
// 2. 准备数据
|
||
$data = [];
|
||
$attrs = $productInfo['attrs'] ?? [$productInfo['attr']];
|
||
|
||
foreach ($attrs as $item) {
|
||
// 处理图片URL
|
||
if ($item['pic'] && strstr($item['pic'], 'http') === false) {
|
||
$siteUrl = sys_config('site_url');
|
||
$item['pic'] = $siteUrl . $item['pic'];
|
||
}
|
||
|
||
// 3. 构建推送数据
|
||
$data[] = [
|
||
'i_id' => $productInfo['code'], // SPU编码
|
||
'sku_id' => $item['code'], // SKU编码
|
||
'name' => $productInfo['store_name'], // 商品名称
|
||
'properties_value' => str_replace(',', ' ', $item['values']), // 规格属性
|
||
's_price' => $item['price'], // 售价
|
||
'pic' => $item['pic'], // 图片URL
|
||
'c_price' => $item['cost'], // 成本价
|
||
'market_price' => $item['ot_price'], // 划线价
|
||
];
|
||
}
|
||
|
||
// 4. 调用聚水潭API
|
||
(new erpServices())->serviceDriver('product')->updateProduct($data);
|
||
|
||
} catch (\Exception $e) {
|
||
response_log_write([
|
||
'message' => '上传商品到ERP失败, 原因: ' . $e->getMessage(),
|
||
'file' => $e->getFile(),
|
||
'line' => $e->getLine()
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 API调用
|
||
|
||
**文件**: [Product.php#L36-54](file:///home/jone/workspace/CRMEB_PRO/crmeb/services/erp/storage/jushuitan/Product.php#L36-54)
|
||
|
||
```php
|
||
public function updateProduct($data)
|
||
{
|
||
$url = $this->accessToken->getApiUrl("/open/jushuitan/itemsku/upload");
|
||
|
||
// 业务参数
|
||
$biz['items'] = $data;
|
||
|
||
// 拼装请求参数
|
||
$params = $this->getParams($biz);
|
||
|
||
// 请求平台接口
|
||
$request = $this->postRequest($url, $params);
|
||
|
||
if ($request['code'] == 0) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
**调用API**: `POST /open/jushuitan/itemsku/upload`
|
||
|
||
---
|
||
|
||
## 四、数据映射关系
|
||
|
||
### 4.1 推送数据格式
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"i_id": "4651127369", // SPU编码(来自CRMEB的code字段)
|
||
"sku_id": "1N55001102", // SKU编码(来自CRMEB的attrs.code字段)
|
||
"name": "贝肽斯夏季儿童防晒帽", // 商品名称
|
||
"properties_value": "卡其布黄;M码:头围50-54cm", // 规格属性
|
||
"s_price": 35.9, // 售价
|
||
"pic": "https://img.yzcdn.cn/...", // 图片URL
|
||
"c_price": 0, // 成本价
|
||
"market_price": 0 // 划线价
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 4.2 字段对应关系
|
||
|
||
| 聚水潭字段 | CRMEB字段 | 说明 |
|
||
|-----------|-----------|------|
|
||
| `i_id` | `productInfo.code` | SPU编码(商品唯一标识)|
|
||
| `sku_id` | `attrs[].code` | SKU编码 |
|
||
| `name` | `productInfo.store_name` | 商品名称 |
|
||
| `properties_value` | `attrs[].values` | 规格属性(逗号替换为空格)|
|
||
| `s_price` | `attrs[].price` | 售价 |
|
||
| `pic` | `attrs[].pic` | SKU图片 |
|
||
| `c_price` | `attrs[].cost` | 成本价 |
|
||
| `market_price` | `attrs[].ot_price` | 划线价 |
|
||
|
||
---
|
||
|
||
## 五、推送触发场景
|
||
|
||
### 5.1 自动推送(已禁用)
|
||
|
||
**触发时机**: 商品创建成功
|
||
|
||
```php
|
||
// 在 CreateSuccess.php 中被注释
|
||
if (sys_config('erp_open') && $data['product_type'] == 0) {
|
||
ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
}
|
||
```
|
||
|
||
### 5.2 手动推送
|
||
|
||
**触发方式**: 后台手动操作
|
||
|
||
可以通过后台页面触发,调用接口:
|
||
```bash
|
||
POST /adminapi/product/sync_jst_product
|
||
{
|
||
"spu_str": "商品SPU编码"
|
||
}
|
||
```
|
||
|
||
### 5.3 门店商品推送
|
||
|
||
除了主商品库,还会推送到绑定了ERP店铺的门店:
|
||
|
||
```php
|
||
// 上传到ERP默认门店
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [
|
||
$id,
|
||
['erp_shop_id' => sys_config('jst_default_shopid')]
|
||
]);
|
||
|
||
// 上传到所有绑定ERP店铺的门店
|
||
foreach ($erpStoreList['list'] as $item) {
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [$id, $item]);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、当前状态分析
|
||
|
||
### 6.1 为什么推送功能被禁用?
|
||
|
||
查看代码注释,ERP推送功能是被有意注释掉的,可能原因:
|
||
|
||
1. **测试阶段** - 功能尚未完善,避免生产环境误操作
|
||
2. **数据同步策略** - 可能改为只从聚水潭拉取,不推送到聚水潭
|
||
3. **业务调整** - 业务方向改变,暂不需要此功能
|
||
4. **数据一致性** - 避免双向同步导致的数据冲突
|
||
|
||
### 6.2 如何启用推送功能?
|
||
|
||
**步骤1**: 取消注释推送代码
|
||
|
||
编辑 [CreateSuccess.php#L80-99](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php#L80-99),取消注释:
|
||
|
||
```php
|
||
//ERP功能开启
|
||
if (sys_config('erp_open') && $data['product_type'] == 0) {
|
||
//上传商品至ERP平台
|
||
ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
|
||
//上传商品至ERP默认门店
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [
|
||
$id,
|
||
['erp_shop_id' => sys_config('jst_default_shopid')]
|
||
]);
|
||
|
||
//给所有绑定了ERP店铺的门店增加该商品
|
||
$systemStoreServices = app()->make(SystemStoreServices::class);
|
||
$erpStoreList = $systemStoreServices->getStoreList([['erp_shop_id', '>', 0]]);
|
||
foreach ($erpStoreList['list'] as $item) {
|
||
if ($item['erp_shop_id'] < 1) continue;
|
||
ProductSyncErp::dispatchDo('upBranchProductToErp', [$id, $item]);
|
||
}
|
||
|
||
if ($is_new) {
|
||
ProductSyncErp::dispatchDo('stockFromErp', [[$id]]);
|
||
}
|
||
}
|
||
```
|
||
|
||
**步骤2**: 确保ERP配置正确
|
||
|
||
在后台配置:
|
||
- ✅ 开启ERP功能
|
||
- ✅ 配置聚水潭AppKey和Secret
|
||
- ✅ 配置默认门店ID
|
||
|
||
**步骤3**: 测试推送功能
|
||
|
||
```bash
|
||
# 创建测试商品
|
||
POST /adminapi/product/save
|
||
{
|
||
"store_name": "测试商品",
|
||
"price": 99.00
|
||
}
|
||
|
||
# 检查日志
|
||
tail -f runtime/log/xxx.log | grep "upProductToErp"
|
||
```
|
||
|
||
---
|
||
|
||
## 七、推送 vs 拉取
|
||
|
||
### 7.1 对比表
|
||
|
||
| 特性 | 拉取(已有)| 推送(已禁用)|
|
||
|------|-----------|-------------|
|
||
| **触发方式** | 手动/定时任务 | 商品创建时自动 |
|
||
| **数据流向** | 聚水潭 → CRMEB | CRMEB → 聚水潭 |
|
||
| **代码状态** | ✅ 正常 | ❌ 被注释 |
|
||
| **使用场景** | 从ERP同步商品 | 上传新品到ERP |
|
||
| **功能完整性** | ✅ 完善 | ⚠️ 需启用 |
|
||
|
||
### 7.2 推荐策略
|
||
|
||
#### 方案A: 仅拉取(当前)
|
||
|
||
**适用场景**:
|
||
- 聚水潭是主数据源
|
||
- CRMEB作为销售前端
|
||
- 商品信息在聚水潭维护
|
||
|
||
**优点**:
|
||
- ✅ 数据单一来源,避免冲突
|
||
- ✅ 商品信息统一管理
|
||
- ✅ 降低系统复杂度
|
||
|
||
#### 方案B: 仅推送
|
||
|
||
**适用场景**:
|
||
- CRMEB是主数据源
|
||
- 聚水潭仅用于库存/订单管理
|
||
- 商品信息在CRMEB维护
|
||
|
||
**优点**:
|
||
- ✅ 快速响应商品变化
|
||
- ✅ 统一商品发布渠道
|
||
- ✅ 便于营销活动管理
|
||
|
||
#### 方案C: 双向同步
|
||
|
||
**适用场景**:
|
||
- 两边都需要管理商品
|
||
- 有专业团队分别维护
|
||
- 需要数据一致性保障机制
|
||
|
||
**挑战**:
|
||
- ⚠️ 数据冲突解决策略
|
||
- ⚠️ 同步延迟处理
|
||
- ⚠️ 异常情况处理
|
||
|
||
---
|
||
|
||
## 八、启用推送的注意事项
|
||
|
||
### 8.1 数据一致性
|
||
|
||
```php
|
||
// 推送时,确保商品编码唯一
|
||
$productInfo['code'] 必须在聚水潭中是唯一的
|
||
|
||
// 如果推送失败,需要记录日志并重试
|
||
if (!(new erpServices())->serviceDriver('product')->updateProduct($data)) {
|
||
// 记录失败日志
|
||
Log::error('商品推送失败', ['product_id' => $id]);
|
||
// 可以加入重试队列
|
||
}
|
||
```
|
||
|
||
### 8.2 性能考虑
|
||
|
||
```php
|
||
// 当前是异步队列推送,无需担心性能
|
||
ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
|
||
// 但需要注意:
|
||
// 1. 批量创建商品时,可能产生大量队列任务
|
||
// 2. 建议限制推送频率
|
||
// 3. 添加推送限流机制
|
||
```
|
||
|
||
### 8.3 错误处理
|
||
|
||
```php
|
||
try {
|
||
(new erpServices())->serviceDriver('product')->updateProduct($data);
|
||
} catch (\Exception $e) {
|
||
// 1. 记录错误日志
|
||
Log::error('推送商品到聚水潭失败', [
|
||
'product_id' => $id,
|
||
'error' => $e->getMessage()
|
||
]);
|
||
|
||
// 2. 可以加入失败重试队列
|
||
ProductSyncErp::dispatchDo('upProductToErp', [$id]);
|
||
|
||
// 3. 通知管理员
|
||
// sendNotify('商品推送失败,需要人工处理');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 九、总结
|
||
|
||
### ❌ 当前状态
|
||
|
||
**商城创建的商品推送到聚水潭的功能已被禁用**
|
||
|
||
### ✅ 推送功能已实现
|
||
|
||
代码逻辑完整,包括:
|
||
- ✅ 商品数据转换
|
||
- ✅ SKU规格处理
|
||
- ✅ 聚水潭API调用
|
||
- ✅ 门店商品推送
|
||
- ✅ 库存同步
|
||
|
||
### 🔧 如何启用
|
||
|
||
1. 取消 [CreateSuccess.php](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php#L80-99) 中的注释
|
||
2. 配置ERP功能
|
||
3. 测试推送流程
|
||
|
||
### 💡 建议
|
||
|
||
根据业务需求选择合适的数据同步策略:
|
||
- **推荐仅拉取**:单数据源,避免冲突
|
||
- **慎用双向同步**:需要完善冲突处理机制
|
||
|
||
---
|
||
|
||
## 十、相关文件清单
|
||
|
||
| 文件路径 | 说明 | 状态 |
|
||
|---------|------|------|
|
||
| [CreateSuccess.php](file:///home/jone/workspace/CRMEB_PRO/app/listener/product/CreateSuccess.php) | 商品创建监听器 | ⚠️ 推送代码被注释 |
|
||
| [ProductSyncErp.php](file:///home/jone/workspace/CRMEB_PRO/app/jobs/product/ProductSyncErp.php) | 推送任务 | ✅ 已实现 |
|
||
| [Product.php](file:///home/jone/workspace/CRMEB_PRO/crmeb/services/erp/storage/jushuitan/Product.php) | API调用 | ✅ 已实现 |
|
||
| [Jushuitan.php](file:///home/jone/workspace/CRMEB_PRO/crmeb/services/erp/storage/Jushuitan.php) | 聚水潭SDK | ✅ 已实现 |
|
||
| [HaichaoJstServices.php](file:///home/jone/workspace/CRMEB_PRO/app/haichao/HaichaoJstServices.php) | 业务封装 | ✅ 已实现 |
|