Files
assistant-storefront/CHATWOOT_INITIALIZATION.md
Liang XJ 4bd11c0ecc
Some checks failed
Lock Threads / action (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
feat: add search_image and product_list content types with image upload
## 新增功能

### 1. 新增消息类型
- 添加 search_image (17) 和 product_list (18) content_type
- 支持图片搜索和商品列表消息展示

### 2. 图片上传功能
- 添加 ImageUploadButton 组件,支持图片上传到 Mall API
- 上传后发送 search_image 类型消息,图片 URL 存储在 content_attributes
- 支持图片文件验证(类型、大小)

### 3. Webhook 推送优化
- 修改 webhook_listener.rb,允许 search_image 类型即使 content 为空也推送 webhook
- 解决 search_image 消息不触发 webhook 的问题

### 4. 前端组件
- 新增 SearchImage.vue 组件(widget 和 dashboard)
- 新增 ProductList.vue、Logistics.vue、OrderDetail.vue、OrderList.vue 组件
- 更新 Message.vue 路由逻辑支持新的 content_type
- 更新 UserMessage.vue 支持 search_image 消息显示

### 5. API 层修改
- widget/messages_controller.rb: 允许 content_attributes 参数
- widget/base_controller.rb: 使用前端传入的 content_attributes
- widget/conversation API: 支持 contentAttributes 参数传递
- conversation actions 和 helpers: 完整的 content_attributes 数据流

### 6. Widget 测试页面优化
- 重写 /widget_tests 页面,支持游客/用户模式切换
- 登录流程:使用 reset() + setUser(),不刷新页面
- 退出流程:使用 reset() + 刷新页面
- 添加详细的日志输出和状态显示

## 技术细节

### Message Model
```ruby
enum content_type: {
  # ...existing types...
  search_image: 17,
  product_list: 18
}
```

### Webhook Listener
```ruby
# Allow search_image webhook even if content is blank
return if message.content.blank? && message.content_type != 'search_image'
```

### Widget Upload Flow
```
用户选择图片
  → 上传到 Mall API
  → 获取图片 URL
  → 发送消息: { content: '', content_type: 'search_image', content_attributes: { url: '...' } }
```

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-27 19:03:46 +08:00

250 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Chatwoot Widget 初始化流程说明
## 🔍 当前的初始化流程
### 1. 页面首次加载(游客模式)
**服务器端 (Ruby ERB):**
```erb
<%
token_present = cookies[:token].present? # false
test_user_id = nil # 没有用户 ID
test_user_hash = nil
%>
```
**前端 JavaScript:**
```javascript
// 服务器端渲染后
const TEST_USER = {
userId: '', // 空字符串
userHash: '', // 空字符串
...
};
const token = getCookie('token'); // null
if (token) {
// 不执行
}
const widgetConfig = {
websiteToken: 'xxx',
baseUrl: '',
locale: 'zh_CN',
useBrowserLanguage: false
// 没有 userIdentifier
};
window.chatwootSDK.run(widgetConfig);
// ✅ Widget 初始化成功(游客模式)
```
### 2. 点击登录(不刷新)
```javascript
function simulateLogin() {
setCookie('token', TEST_TOKEN); // 设置 cookie
isLoggedIn = true;
currentUser = TEST_USER;
// 调用 setUser 设置用户信息
window.$chatwoot.setUser(currentUser.userId, {
identifier_hash: currentUser.userHash,
email: currentUser.email,
name: currentUser.name,
...
});
// ✅ 用户信息已设置
// ⚠️ 但 widget 仍然是游客模式初始化的!
}
```
### 3. 刷新页面(登录后)
**服务器端 (Ruby ERB):**
```erb
<%
token_present = cookies[:token].present? # ✅ true
test_user_id = '211845' # ✅ 有用户 ID
test_user_hash = OpenSSL::HMAC.hexdigest(...)
%>
```
**前端 JavaScript:**
```javascript
// 服务器端渲染后
const TEST_USER = {
userId: '211845', # 正确的用户 ID
userHash: 'xxx...', # 正确的 hash
...
};
const token = getCookie('token'); // ✅ 存在
if (token) {
const widgetConfig = {
websiteToken: 'xxx',
baseUrl: '',
locale: 'zh_CN',
useBrowserLanguage: false,
userIdentifier: '211845' # 设置用户标识
};
window.chatwootSDK.run(widgetConfig);
// ❌ 问题window.$chatwoot 已经存在!
// ❌ SDK 内部会 return不会重新初始化
}
```
## ⚠️ 核心问题
**Chatwoot SDK 只能初始化一次!**
```javascript
// app/javascript/entrypoints/sdk.js:21-24
runSDK({ baseUrl, websiteToken }) {
if (window.$chatwoot) {
return; // ❌ 已经初始化过,直接返回
}
// ... 初始化代码
}
```
## 💡 解决方案
### 方案 1登录时强制刷新推荐
```javascript
function simulateLogin() {
setCookie('token', TEST_TOKEN);
isLoggedIn = true;
currentUser = TEST_USER;
// 立即刷新页面,让 SDK 正确初始化
addLog('💡 即将刷新页面以应用登录状态...');
setTimeout(() => {
location.reload();
}, 500);
}
```
**优点:**
- 简单可靠
- SDK 会正确初始化
- userIdentifier 在初始化时就设置
**缺点:**
- 需要刷新页面
- 用户体验略差
### 方案 2在登录前先 reset
```javascript
function simulateLogin() {
// 先清除旧的 SDK 实例
if (window.$chatwoot && window.$chatwoot.reset) {
window.$chatwoot.reset();
}
// 等待 reset 完成
setTimeout(() => {
setCookie('token', TEST_TOKEN);
isLoggedIn = true;
currentUser = TEST_USER;
// 重新初始化
const widgetConfig = {
websiteToken: '<%= @web_widget.website_token %>',
baseUrl: '',
locale: 'zh_CN',
useBrowserLanguage: false,
userIdentifier: currentUser.userId
};
window.chatwootSDK.run(widgetConfig);
// ...
}, 100);
}
```
**问题:**
- SDK 已经初始化检查在 `run()` 函数内部
- 无法绕过这个检查
### 方案 3使用 iframe 的方式(不推荐)
每次都创建新的 iframe但这样会失去状态同步。
## ✅ 推荐的实现
**登录时自动刷新页面:**
```javascript
function simulateLogin() {
addLog('🔐 开始模拟用户登录...');
// 设置 token cookie
setCookie('token', TEST_TOKEN);
// 验证设置成功
const tokenCheck = getCookie('token');
if (tokenCheck) {
addLog('✅ Token 已设置');
addLog('⏳ 即将刷新页面以完成登录...');
// 显示倒计时
let countdown = 2;
const countdownInterval = setInterval(() => {
countdown--;
if (countdown <= 0) {
clearInterval(countdownInterval);
location.reload();
}
}, 1000);
} else {
addLog('❌ Token 设置失败');
}
}
```
## 📊 完整流程
### 游客 → 登录:
```
1. 游客模式访问
2. 点击登录
3. 设置 token
4. 自动刷新2秒倒计时
5. Ruby 检测到 token设置 user_id = '211845'
6. SDK 初始化(带 userIdentifier
7. ✅ 登录成功
```
### 登录 → 退出:
```
1. 点击退出
2. 删除 token
3. 调用 reset() 清除所有数据
4. ✅ 立即切换到游客模式
```
### 退出 → 登录:
```
1. 游客模式
2. 点击登录
3. 设置 token
4. 自动刷新
5. ✅ 恢复登录状态(新会话)
```
## 🎯 总结
**Chatwoot SDK 的设计限制:**
- SDK 只能初始化一次
- 初始化后无法动态更改 userIdentifier
- 必须刷新页面才能重新初始化
**最佳实践:**
- **登录/退出时都刷新页面**
- 服务器端检查 token 来决定初始状态
- 确保每次刷新都能正确初始化