feat: 添加阿里云短信服务支持并优化验证码逻辑

- 引入 alibabacloud/dysmsapi-20170525 依赖,集成阿里云短信服务
- 将短信验证码从6位改为4位,优化用户体验
- 添加聚水潭商品按时间范围同步功能,支持分页拉取
- 增强Cron任务异常处理,添加错误日志记录
- 修复系统附件服务中图片路径处理逻辑
- 更新阿里云SDK相关组件至最新版本
- 添加think-trace调试扩展,便于问题排查
- 优化跨域中间件,使用str_contains替代strpos
This commit is contained in:
2026-04-30 15:19:40 +08:00
parent 0334fef6c1
commit c7f85ffbda
565 changed files with 59742 additions and 3391 deletions

View File

@@ -0,0 +1,465 @@
# 商城商品推送到聚水潭分析
## 一、答案
### ❌ 当前状态:**推送功能已禁用**
在 [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) | 业务封装 | ✅ 已实现 |