mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-01 12:07:36 +08:00
完善项目结构
This commit is contained in:
parent
c8150ae464
commit
9ec01047aa
16
.env.example
16
.env.example
@ -70,6 +70,22 @@ TOKEN_RETRY_INTERVAL=300
|
||||
# 消息过期时间 (毫秒)
|
||||
MESSAGE_EXPIRE_TIME=300000
|
||||
|
||||
# ==================== AI回复配置 ====================
|
||||
# AI回复功能总开关 (true/false)
|
||||
AI_REPLY_ENABLED=false
|
||||
|
||||
# 默认AI模型
|
||||
DEFAULT_AI_MODEL=qwen-plus
|
||||
|
||||
# 默认AI API地址
|
||||
DEFAULT_AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
|
||||
# AI请求超时时间 (秒)
|
||||
AI_REQUEST_TIMEOUT=30
|
||||
|
||||
# AI最大生成token数
|
||||
AI_MAX_TOKENS=100
|
||||
|
||||
# ================================
|
||||
# 自动回复配置
|
||||
# ================================
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -27,4 +27,5 @@ __pypackages__/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
venv.bak/
|
||||
*.db
|
214
AI_REPLY_GUIDE.md
Normal file
214
AI_REPLY_GUIDE.md
Normal file
@ -0,0 +1,214 @@
|
||||
# 🤖 AI回复功能使用指南
|
||||
|
||||
## 📋 功能概述
|
||||
|
||||
AI回复功能集成了先进的人工智能技术,为每个闲鱼账号提供智能化的客服回复能力。系统支持意图识别、智能议价、技术咨询等多种场景,让您的客服更加专业和高效。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
### 🎯 智能意图识别
|
||||
- **价格咨询**: 自动识别议价、优惠、降价等价格相关询问
|
||||
- **技术咨询**: 识别产品参数、使用方法、故障等技术问题
|
||||
- **通用咨询**: 处理商品介绍、物流、售后等常见问题
|
||||
|
||||
### 💰 智能议价系统
|
||||
- **阶梯式降价**: 根据议价轮数智能调整优惠幅度
|
||||
- **价值强调**: 突出商品优势,提高成交率
|
||||
- **议价限制**: 可设置最大优惠百分比和金额
|
||||
|
||||
### 🧠 多Agent专家系统
|
||||
- **分类Agent**: 负责意图识别和路由
|
||||
- **价格Agent**: 专业的议价专家
|
||||
- **技术Agent**: 产品技术支持专家
|
||||
- **默认Agent**: 通用客服助手
|
||||
|
||||
### 📝 上下文感知
|
||||
- **对话历史**: 记住完整的对话上下文
|
||||
- **商品信息**: 结合具体商品信息回复
|
||||
- **个性化**: 根据用户行为调整回复策略
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 启用AI回复
|
||||
|
||||
1. **进入账号管理页面**
|
||||
2. **找到目标账号**,点击 🤖 **配置AI回复** 按钮
|
||||
3. **开启AI回复开关**
|
||||
4. **配置API密钥**
|
||||
|
||||
### 2. 配置API密钥
|
||||
|
||||
#### 通义千问 (推荐)
|
||||
```
|
||||
模型: qwen-plus
|
||||
API地址: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
API密钥: 在阿里云DashScope控制台获取
|
||||
```
|
||||
|
||||
#### OpenAI GPT
|
||||
```
|
||||
模型: gpt-3.5-turbo 或 gpt-4
|
||||
API地址: https://api.openai.com/v1
|
||||
API密钥: 在OpenAI控制台获取
|
||||
```
|
||||
|
||||
### 3. 议价策略配置
|
||||
|
||||
- **最大优惠百分比**: 建议设置5-15%
|
||||
- **最大优惠金额**: 根据商品价值设置
|
||||
- **最大议价轮数**: 建议3-5轮
|
||||
|
||||
## ⚙️ 详细配置
|
||||
|
||||
### 基本设置
|
||||
|
||||
| 配置项 | 说明 | 推荐值 |
|
||||
|--------|------|--------|
|
||||
| AI模型 | 选择使用的AI模型 | qwen-plus |
|
||||
| API地址 | AI服务的API地址 | 默认值 |
|
||||
| API密钥 | 访问AI服务的密钥 | 必填 |
|
||||
|
||||
### 议价设置
|
||||
|
||||
| 配置项 | 说明 | 推荐值 |
|
||||
|--------|------|--------|
|
||||
| 最大优惠百分比 | 最大可优惠的百分比 | 10% |
|
||||
| 最大优惠金额 | 最大可优惠的金额 | 100元 |
|
||||
| 最大议价轮数 | 最多允许议价的次数 | 3轮 |
|
||||
|
||||
### 提示词自定义
|
||||
|
||||
可以自定义四种类型的提示词:
|
||||
|
||||
```json
|
||||
{
|
||||
"classify": "意图分类提示词",
|
||||
"price": "议价专家提示词",
|
||||
"tech": "技术专家提示词",
|
||||
"default": "通用客服提示词"
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 1. 消息接收
|
||||
系统接收到用户消息后,首先检查该账号是否启用AI回复。
|
||||
|
||||
### 2. 意图识别
|
||||
AI分析消息内容,识别用户意图:
|
||||
- 价格相关 → 路由到价格专家
|
||||
- 技术相关 → 路由到技术专家
|
||||
- 其他咨询 → 路由到通用客服
|
||||
|
||||
### 3. 上下文构建
|
||||
- 获取对话历史
|
||||
- 加载商品信息
|
||||
- 统计议价次数
|
||||
|
||||
### 4. 智能回复
|
||||
根据意图类型和上下文信息,生成个性化回复。
|
||||
|
||||
### 5. 回复发送
|
||||
在回复前添加 `[AI回复]` 标识,便于区分。
|
||||
|
||||
## 📊 优先级说明
|
||||
|
||||
当账号启用AI回复后,系统回复优先级为:
|
||||
|
||||
1. **🔥 API回复** (最高优先级)
|
||||
2. **🤖 AI回复** (AI启用时)
|
||||
3. **📝 关键词匹配** (AI禁用时)
|
||||
4. **💬 默认回复** (最低优先级)
|
||||
|
||||
> ⚠️ **重要**: 启用AI回复后,关键词匹配和默认回复将自动失效!
|
||||
|
||||
## 🧪 功能测试
|
||||
|
||||
### 测试步骤
|
||||
1. 在AI回复配置页面,找到 **功能测试** 区域
|
||||
2. 输入测试消息,如:"这个商品能便宜点吗?"
|
||||
3. 设置商品价格
|
||||
4. 点击 **测试AI回复** 按钮
|
||||
5. 查看生成的回复效果
|
||||
|
||||
### 测试场景
|
||||
- **议价测试**: "能便宜点吗?"、"最低多少钱?"
|
||||
- **技术测试**: "这个怎么用?"、"有什么功能?"
|
||||
- **通用测试**: "包邮吗?"、"什么时候发货?"
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### 1. API密钥管理
|
||||
- 定期更换API密钥
|
||||
- 监控API使用量和费用
|
||||
- 设置合理的调用频率限制
|
||||
|
||||
### 2. 议价策略
|
||||
- 根据商品类型调整议价幅度
|
||||
- 高价值商品可设置更高的优惠金额
|
||||
- 低价商品建议限制议价轮数
|
||||
|
||||
### 3. 提示词优化
|
||||
- 根据实际业务场景自定义提示词
|
||||
- 定期分析回复效果,优化提示词
|
||||
- 保持回复风格的一致性
|
||||
|
||||
### 4. 监控和优化
|
||||
- 定期查看AI回复的效果
|
||||
- 分析用户反馈,调整配置
|
||||
- 监控API调用成本
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 安全性
|
||||
- API密钥加密存储,但请定期更换
|
||||
- 建议使用子账号API密钥,限制权限
|
||||
- 监控异常调用,防止滥用
|
||||
|
||||
### 成本控制
|
||||
- AI回复会产生API调用费用
|
||||
- 建议设置合理的调用限制
|
||||
- 监控每日/每月的使用量
|
||||
|
||||
### 合规性
|
||||
- AI生成的内容可能需要人工审核
|
||||
- 确保回复内容符合平台规则
|
||||
- 避免过度承诺或误导用户
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: AI回复不生效?**
|
||||
A: 检查以下项目:
|
||||
- 账号是否启用AI回复
|
||||
- API密钥是否正确
|
||||
- 网络连接是否正常
|
||||
- API服务是否可用
|
||||
|
||||
**Q: 回复质量不佳?**
|
||||
A: 尝试以下优化:
|
||||
- 调整AI模型选择
|
||||
- 自定义提示词
|
||||
- 优化商品信息描述
|
||||
- 增加测试和调优
|
||||
|
||||
**Q: API调用失败?**
|
||||
A: 检查以下方面:
|
||||
- API密钥是否有效
|
||||
- API地址是否正确
|
||||
- 账户余额是否充足
|
||||
- 请求频率是否超限
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如果您在使用过程中遇到问题,可以:
|
||||
|
||||
1. 查看系统日志获取详细错误信息
|
||||
2. 使用测试功能验证配置是否正确
|
||||
3. 检查API服务商的状态页面
|
||||
4. 联系技术支持获取帮助
|
||||
|
||||
---
|
||||
|
||||
🎉 **恭喜!** 您已经掌握了AI回复功能的使用方法。开始享受智能化的客服体验吧!
|
146
CHANGELOG.md
Normal file
146
CHANGELOG.md
Normal file
@ -0,0 +1,146 @@
|
||||
# 📋 更新日志
|
||||
|
||||
本文档记录了闲鱼自动回复管理系统的所有重要更新和变更。
|
||||
|
||||
## [v2.0.0] - 2024-07-24
|
||||
|
||||
### 🎉 重大更新
|
||||
- **AI智能回复系统**:集成多种AI模型,支持智能对话和议价
|
||||
- **商品管理功能**:自动收集和管理商品信息
|
||||
- **实时日志系统**:完整的日志收集、查看和分析功能
|
||||
- **Docker容器化**:完整的Docker部署支持
|
||||
|
||||
### ✨ 新增功能
|
||||
- **多AI模型支持**:支持通义千问、GPT等主流AI模型
|
||||
- **智能意图识别**:自动识别价格咨询、技术问题、通用咨询
|
||||
- **智能议价系统**:阶梯式降价策略,可设置最大优惠幅度
|
||||
- **商品信息自动收集**:消息触发时自动提取并保存商品信息
|
||||
- **批量商品获取**:一键获取账号下所有商品信息
|
||||
- **实时日志查看**:Web界面实时查看系统运行日志
|
||||
- **日志过滤和搜索**:支持按级别、来源、关键词过滤日志
|
||||
- **系统健康监控**:完整的系统健康检查和监控
|
||||
- **数据备份恢复**:支持完整的数据备份和恢复功能
|
||||
|
||||
### 🔧 功能改进
|
||||
- **用户界面优化**:现代化的Web界面设计
|
||||
- **性能优化**:异步处理,提高系统并发能力
|
||||
- **安全增强**:JWT认证,数据加密存储
|
||||
- **错误处理**:完善的错误处理和重试机制
|
||||
- **配置管理**:灵活的配置文件管理系统
|
||||
|
||||
### 🐛 问题修复
|
||||
- 修复WebSocket连接不稳定的问题
|
||||
- 修复Cookie失效时的自动刷新机制
|
||||
- 修复数据库并发访问的问题
|
||||
- 修复文件上传的安全问题
|
||||
- 修复日志文件过大的问题
|
||||
|
||||
### 📚 文档更新
|
||||
- 完整的README.md文档
|
||||
- 详细的功能使用说明
|
||||
- Docker部署指南
|
||||
- API接口文档
|
||||
- 故障排除指南
|
||||
|
||||
## [v1.5.0] - 2024-06-15
|
||||
|
||||
### ✨ 新增功能
|
||||
- **自动发货增强**:支持API接口类型卡券
|
||||
- **批量数据管理**:支持批量导入和管理卡券
|
||||
- **发货规则优化**:更智能的商品匹配算法
|
||||
- **消息通知格式化**:美化消息通知格式
|
||||
|
||||
### 🔧 功能改进
|
||||
- 优化自动发货匹配逻辑
|
||||
- 改进卡券管理界面
|
||||
- 增强错误处理机制
|
||||
- 提升系统稳定性
|
||||
|
||||
### 🐛 问题修复
|
||||
- 修复发货规则匹配不准确的问题
|
||||
- 修复批量数据消耗过快的问题
|
||||
- 修复API接口调用失败的问题
|
||||
|
||||
## [v1.4.0] - 2024-05-20
|
||||
|
||||
### ✨ 新增功能
|
||||
- **多账号管理**:支持同时管理多个闲鱼账号
|
||||
- **关键词回复**:每个账号独立的关键词回复设置
|
||||
- **用户认证系统**:安全的登录认证机制
|
||||
- **数据持久化**:SQLite数据库存储
|
||||
|
||||
### 🔧 功能改进
|
||||
- 重构代码架构,提高可维护性
|
||||
- 优化数据库设计
|
||||
- 改进用户界面体验
|
||||
- 增强系统安全性
|
||||
|
||||
## [v1.3.0] - 2024-04-25
|
||||
|
||||
### ✨ 新增功能
|
||||
- **自动发货功能**:支持自动发送卡券和虚拟商品
|
||||
- **发货规则配置**:灵活的发货规则设置
|
||||
- **卡券管理**:支持多种卡券类型管理
|
||||
|
||||
### 🔧 功能改进
|
||||
- 优化消息处理逻辑
|
||||
- 改进WebSocket连接稳定性
|
||||
- 增强日志记录功能
|
||||
|
||||
## [v1.2.0] - 2024-03-30
|
||||
|
||||
### ✨ 新增功能
|
||||
- **Web管理界面**:现代化的Web管理界面
|
||||
- **实时状态监控**:实时查看系统运行状态
|
||||
- **配置文件管理**:可视化配置文件编辑
|
||||
|
||||
### 🔧 功能改进
|
||||
- 优化自动回复逻辑
|
||||
- 改进错误处理机制
|
||||
- 增强系统稳定性
|
||||
|
||||
## [v1.1.0] - 2024-02-28
|
||||
|
||||
### ✨ 新增功能
|
||||
- **关键词匹配**:支持关键词自动回复
|
||||
- **变量替换**:支持消息中的变量替换
|
||||
- **日志系统**:完整的日志记录功能
|
||||
|
||||
### 🔧 功能改进
|
||||
- 优化WebSocket连接处理
|
||||
- 改进消息解析逻辑
|
||||
- 增强系统容错能力
|
||||
|
||||
## [v1.0.0] - 2024-01-15
|
||||
|
||||
### 🎉 首次发布
|
||||
- **基础自动回复**:支持闲鱼消息自动回复
|
||||
- **WebSocket连接**:稳定的WebSocket连接机制
|
||||
- **Token管理**:自动Token刷新和管理
|
||||
- **消息处理**:完整的消息接收和处理流程
|
||||
|
||||
---
|
||||
|
||||
## 📝 版本说明
|
||||
|
||||
### 版本号规则
|
||||
- **主版本号**:重大功能更新或架构变更
|
||||
- **次版本号**:新功能添加或重要改进
|
||||
- **修订版本号**:问题修复和小幅改进
|
||||
|
||||
### 更新类型说明
|
||||
- 🎉 **重大更新**:重要的新功能或架构变更
|
||||
- ✨ **新增功能**:新增的功能特性
|
||||
- 🔧 **功能改进**:现有功能的优化和改进
|
||||
- 🐛 **问题修复**:Bug修复和问题解决
|
||||
- 📚 **文档更新**:文档和说明的更新
|
||||
- 🔒 **安全更新**:安全相关的更新和修复
|
||||
|
||||
### 兼容性说明
|
||||
- **向后兼容**:新版本保持与旧版本的兼容性
|
||||
- **配置迁移**:提供配置文件自动迁移功能
|
||||
- **数据迁移**:提供数据库自动升级功能
|
||||
|
||||
---
|
||||
|
||||
**注意**:建议在升级前备份重要数据,详细的升级指南请参考相关文档。
|
220
CONTRIBUTING.md
Normal file
220
CONTRIBUTING.md
Normal file
@ -0,0 +1,220 @@
|
||||
# 🤝 贡献指南
|
||||
|
||||
感谢您对闲鱼自动回复管理系统的关注!我们欢迎任何形式的贡献,包括但不限于代码、文档、问题反馈和功能建议。
|
||||
|
||||
## 📋 贡献方式
|
||||
|
||||
### 🐛 报告问题
|
||||
如果您发现了bug或有改进建议,请:
|
||||
1. 检查 [Issues](https://github.com/your-repo/xianyu-auto-reply/issues) 确认问题未被报告
|
||||
2. 创建新的Issue,详细描述问题
|
||||
3. 提供复现步骤和环境信息
|
||||
4. 如果可能,提供错误日志和截图
|
||||
|
||||
### 💡 功能建议
|
||||
如果您有新功能的想法:
|
||||
1. 在Issues中创建功能请求
|
||||
2. 详细描述功能需求和使用场景
|
||||
3. 说明功能的预期效果
|
||||
4. 讨论实现方案的可行性
|
||||
|
||||
### 🔧 代码贡献
|
||||
我们欢迎代码贡献,请遵循以下流程:
|
||||
|
||||
#### 开发环境搭建
|
||||
1. **Fork项目**到您的GitHub账号
|
||||
2. **克隆项目**到本地:
|
||||
```bash
|
||||
git clone https://github.com/your-username/xianyu-auto-reply.git
|
||||
cd xianyu-auto-reply
|
||||
```
|
||||
3. **创建虚拟环境**:
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Linux/Mac
|
||||
# 或
|
||||
venv\Scripts\activate # Windows
|
||||
```
|
||||
4. **安装依赖**:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
5. **运行测试**确保环境正常:
|
||||
```bash
|
||||
python Start.py
|
||||
```
|
||||
|
||||
#### 开发流程
|
||||
1. **创建分支**:
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
2. **编写代码**,遵循项目的代码规范
|
||||
3. **编写测试**,确保新功能有相应的测试用例
|
||||
4. **运行测试**,确保所有测试通过
|
||||
5. **提交代码**:
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: 添加新功能描述"
|
||||
```
|
||||
6. **推送分支**:
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
7. **创建Pull Request**
|
||||
|
||||
## 📝 代码规范
|
||||
|
||||
### Python代码规范
|
||||
- 遵循 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 代码风格
|
||||
- 使用有意义的变量和函数名
|
||||
- 添加必要的注释和文档字符串
|
||||
- 保持函数简洁,单一职责原则
|
||||
|
||||
### 提交信息规范
|
||||
使用 [Conventional Commits](https://www.conventionalcommits.org/) 规范:
|
||||
|
||||
```
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
#### 提交类型
|
||||
- `feat`: 新功能
|
||||
- `fix`: 问题修复
|
||||
- `docs`: 文档更新
|
||||
- `style`: 代码格式调整
|
||||
- `refactor`: 代码重构
|
||||
- `test`: 测试相关
|
||||
- `chore`: 构建过程或辅助工具的变动
|
||||
|
||||
#### 示例
|
||||
```
|
||||
feat(ai): 添加智能议价功能
|
||||
|
||||
- 实现阶梯式降价策略
|
||||
- 支持最大优惠限制
|
||||
- 添加议价轮数统计
|
||||
|
||||
Closes #123
|
||||
```
|
||||
|
||||
### 文档规范
|
||||
- 使用Markdown格式
|
||||
- 保持文档结构清晰
|
||||
- 添加必要的代码示例
|
||||
- 及时更新相关文档
|
||||
|
||||
## 🧪 测试指南
|
||||
|
||||
### 运行测试
|
||||
```bash
|
||||
# 运行所有测试
|
||||
python -m pytest
|
||||
|
||||
# 运行特定测试文件
|
||||
python -m pytest tests/test_ai_reply.py
|
||||
|
||||
# 运行带覆盖率的测试
|
||||
python -m pytest --cov=.
|
||||
```
|
||||
|
||||
### 编写测试
|
||||
- 为新功能编写单元测试
|
||||
- 确保测试覆盖率不低于80%
|
||||
- 使用有意义的测试名称
|
||||
- 测试边界条件和异常情况
|
||||
|
||||
### 测试示例
|
||||
```python
|
||||
def test_ai_reply_with_valid_input():
|
||||
"""测试AI回复功能的正常输入"""
|
||||
# 准备测试数据
|
||||
message = "这个商品能便宜点吗?"
|
||||
|
||||
# 执行测试
|
||||
result = ai_reply_engine.process_message(message)
|
||||
|
||||
# 验证结果
|
||||
assert result is not None
|
||||
assert "优惠" in result
|
||||
```
|
||||
|
||||
## 📚 文档贡献
|
||||
|
||||
### 文档类型
|
||||
- **用户文档**:使用说明、配置指南
|
||||
- **开发文档**:API文档、架构说明
|
||||
- **部署文档**:安装部署指南
|
||||
|
||||
### 文档更新
|
||||
- 新功能需要更新相关文档
|
||||
- 修复文档中的错误和过时信息
|
||||
- 改进文档的可读性和准确性
|
||||
|
||||
## 🔍 代码审查
|
||||
|
||||
### 审查标准
|
||||
- **功能正确性**:代码是否实现了预期功能
|
||||
- **代码质量**:是否遵循代码规范
|
||||
- **性能考虑**:是否有性能问题
|
||||
- **安全性**:是否存在安全隐患
|
||||
- **测试覆盖**:是否有足够的测试
|
||||
|
||||
### 审查流程
|
||||
1. 提交Pull Request
|
||||
2. 自动化测试运行
|
||||
3. 代码审查和讨论
|
||||
4. 修改和完善
|
||||
5. 合并到主分支
|
||||
|
||||
## 🎯 贡献建议
|
||||
|
||||
### 适合新手的任务
|
||||
- 修复文档中的错误
|
||||
- 改进错误信息和提示
|
||||
- 添加单元测试
|
||||
- 优化用户界面
|
||||
|
||||
### 高级贡献
|
||||
- 新功能开发
|
||||
- 性能优化
|
||||
- 架构改进
|
||||
- 安全增强
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
### 获取帮助
|
||||
- **GitHub Issues**:报告问题和讨论
|
||||
- **GitHub Discussions**:一般性讨论和问答
|
||||
- **Email**:紧急问题联系
|
||||
|
||||
### 社区参与
|
||||
- 参与Issue讨论
|
||||
- 帮助其他用户解决问题
|
||||
- 分享使用经验和技巧
|
||||
- 推广项目
|
||||
|
||||
## 🏆 贡献者认可
|
||||
|
||||
### 贡献者列表
|
||||
我们会在项目中维护贡献者列表,感谢每一位贡献者的付出。
|
||||
|
||||
### 贡献统计
|
||||
- 代码贡献
|
||||
- 文档贡献
|
||||
- 问题报告
|
||||
- 功能建议
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
通过贡献代码,您同意您的贡献将在 [MIT License](LICENSE) 下发布。
|
||||
|
||||
---
|
||||
|
||||
**再次感谢您的贡献!** 🙏
|
||||
|
||||
每一个贡献都让这个项目变得更好,无论大小,我们都非常感激。让我们一起构建一个更好的闲鱼自动回复管理系统!
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 肥极喵
|
||||
Copyright (c) 2024 Xianyu Auto Reply System
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
551
README.md
551
README.md
@ -1,173 +1,444 @@
|
||||
# 🐟 XianYuAutoDeliveryX - 闲鱼虚拟商品商自动发货&聊天对接大模型
|
||||
# 🚀 闲鱼自动回复管理系统
|
||||
|
||||
[](https://www.python.org/)
|
||||
[](LICENSE)
|
||||
<div align="center">
|
||||
|
||||
**✨ 基于闲鱼API的自动发货系统,支持虚拟商品商品聊天窗口自动发货、消息自动回复等功能。**
|
||||
**⚠️ 注意:本项目仅供学习交流使用,请勿用于商业用途。**
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 🌟 核心特性
|
||||
**一个功能强大的闲鱼自动回复管理系统,支持多账号管理、AI智能回复、自动发货等功能**
|
||||
|
||||
- 🔐 **用户认证系统** - 安全的登录认证,保护管理界面
|
||||
- 👥 **多账号管理** - 支持同时管理多个闲鱼账号
|
||||
- 🎯 **智能关键词回复** - 每个账号独立的关键词回复设置
|
||||
- 💾 **数据持久化** - SQLite数据库存储账号和关键词数据
|
||||
- 🌐 **美观Web界面** - 响应式设计,操作简单直观
|
||||
- 📡 **API接口** - 完整的RESTful API支持
|
||||
- 🔄 **实时消息处理** - 基于WebSocket的实时消息监控
|
||||
- 📊 **订单状态监控** - 实时跟踪订单状态变化
|
||||
- 📝 **完善的日志系统** - 详细的操作日志记录
|
||||
[功能特性](#-功能特性) • [快速开始](#-快速开始) • [部署方式](#-部署方式) • [使用文档](#-使用文档) • [API文档](#-api文档) • [贡献指南](#-贡献指南)
|
||||
|
||||
## 🛠️ 快速开始
|
||||
</div>
|
||||
|
||||
### ⛳ 运行环境
|
||||
- Python 3.7+
|
||||
## 📋 项目概述
|
||||
|
||||
### 🎯 安装依赖
|
||||
闲鱼自动回复管理系统是一个基于 Python + FastAPI 开发的自动化客服系统,专为闲鱼平台设计。系统通过 WebSocket 连接闲鱼服务器,实时接收和处理消息,提供智能化的自动回复服务。
|
||||
|
||||
### 🎯 核心优势
|
||||
|
||||
- **🤖 AI智能回复**:集成多种AI模型,支持意图识别和智能议价
|
||||
- **🔄 多账号管理**:同时管理多个闲鱼账号,独立配置和监控
|
||||
- **📦 商品管理**:自动收集和管理商品信息,支持批量操作
|
||||
- **🚚 自动发货**:智能匹配发货规则,自动发送卡券信息
|
||||
- **📊 实时监控**:完整的日志系统和状态监控
|
||||
- **🐳 容器化部署**:支持Docker一键部署,简化运维
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
### 🤖 AI智能回复
|
||||
- **多模型支持**:支持通义千问、GPT等主流AI模型
|
||||
- **意图识别**:自动识别价格咨询、技术问题、通用咨询
|
||||
- **智能议价**:阶梯式降价策略,可设置最大优惠幅度
|
||||
- **上下文感知**:记住完整对话历史,提供连贯回复
|
||||
- **自定义提示词**:支持针对不同场景自定义AI提示词
|
||||
|
||||
### 👥 多账号管理
|
||||
- **账号隔离**:每个账号独立配置和管理
|
||||
- **批量操作**:支持批量添加、删除、配置账号
|
||||
- **状态监控**:实时监控账号连接状态和消息处理情况
|
||||
- **权限控制**:基于JWT的安全认证系统
|
||||
|
||||
### 📦 商品管理
|
||||
- **自动收集**:消息触发时自动收集商品信息
|
||||
- **详情获取**:通过API获取完整商品详情
|
||||
- **批量管理**:支持查看、编辑、删除商品信息
|
||||
- **智能匹配**:基于商品信息进行关键词匹配
|
||||
|
||||
### 🚚 自动发货
|
||||
- **规则配置**:灵活的发货规则配置系统
|
||||
- **卡券管理**:支持多种卡券类型和批量导入
|
||||
- **智能匹配**:根据商品信息自动匹配发货规则
|
||||
- **发货记录**:完整的发货历史记录和统计
|
||||
|
||||
### 📊 监控与日志
|
||||
- **实时日志**:多级别日志记录和实时查看
|
||||
- **性能监控**:系统资源使用情况监控
|
||||
- **消息统计**:消息处理统计和分析
|
||||
- **健康检查**:完整的系统健康检查机制
|
||||
|
||||
## 🛠️ 技术栈
|
||||
|
||||
### 后端技术
|
||||
- **Python 3.11+**:主要开发语言
|
||||
- **FastAPI**:现代化的Web框架
|
||||
- **SQLite**:轻量级数据库
|
||||
- **WebSocket**:实时通信
|
||||
- **AsyncIO**:异步编程
|
||||
|
||||
### 前端技术
|
||||
- **HTML5 + CSS3**:现代化界面设计
|
||||
- **JavaScript (ES6+)**:交互逻辑
|
||||
- **Bootstrap**:响应式布局
|
||||
- **Chart.js**:数据可视化
|
||||
|
||||
### 部署技术
|
||||
- **Docker**:容器化部署
|
||||
- **Docker Compose**:多容器编排
|
||||
- **Nginx**:反向代理和负载均衡
|
||||
- **SSL/TLS**:安全传输
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 方式一:Docker 部署(推荐)
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
git clone https://github.com/your-repo/xianyu-auto-reply.git
|
||||
cd xianyu-auto-reply
|
||||
```
|
||||
|
||||
2. **一键部署**
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
3. **访问系统**
|
||||
- 打开浏览器访问:http://localhost:8080
|
||||
- 默认账号:admin / admin123
|
||||
|
||||
### 方式二:本地部署
|
||||
|
||||
1. **安装依赖**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **启动系统**
|
||||
```bash
|
||||
python Start.py
|
||||
```
|
||||
|
||||
3. **访问系统**
|
||||
- 打开浏览器访问:http://localhost:8080
|
||||
|
||||
## 📚 使用文档
|
||||
|
||||
### 基础使用
|
||||
1. **[使用说明](./使用说明.md)** - 系统基础使用指南
|
||||
2. **[Docker部署说明](./Docker部署说明.md)** - Docker部署详细说明
|
||||
|
||||
### 功能文档
|
||||
1. **[AI回复功能指南](./AI_REPLY_GUIDE.md)** - AI回复功能详细说明
|
||||
2. **[商品管理功能说明](./商品管理功能说明.md)** - 商品管理功能使用
|
||||
3. **[自动发货功能说明](./自动发货功能说明.md)** - 自动发货配置和使用
|
||||
4. **[日志管理功能说明](./日志管理功能说明.md)** - 日志查看和管理
|
||||
5. **[获取所有商品功能说明](./获取所有商品功能说明.md)** - 商品批量获取功能
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### 环境变量配置
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
# 基础配置
|
||||
TZ=Asia/Shanghai
|
||||
WEB_PORT=8080
|
||||
|
||||
# 管理员账号
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=admin123
|
||||
|
||||
# AI回复配置
|
||||
AI_REPLY_ENABLED=true
|
||||
DEFAULT_AI_MODEL=qwen-plus
|
||||
DEFAULT_AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
```
|
||||
|
||||
### 🎨 配置说明
|
||||
1. 在 `global_config.yml` 中配置基本参数
|
||||
2. 系统支持多账号管理,可通过Web界面添加多个闲鱼账号Cookie
|
||||
### 全局配置文件
|
||||
主要配置文件为 `global_config.yml`,包含:
|
||||
- API端点配置
|
||||
- WebSocket连接配置
|
||||
- 自动回复配置
|
||||
- 日志配置
|
||||
- 商品详情获取配置
|
||||
|
||||
### 🚀 运行项目
|
||||
## 📡 API文档
|
||||
|
||||
### 认证接口
|
||||
- `POST /login` - 用户登录
|
||||
- `POST /logout` - 用户登出
|
||||
|
||||
### 账号管理
|
||||
- `GET /cookies` - 获取所有账号
|
||||
- `POST /cookies` - 添加新账号
|
||||
- `DELETE /cookies/{cookie_id}` - 删除账号
|
||||
|
||||
### AI回复管理
|
||||
- `GET /ai-reply/{cookie_id}` - 获取AI配置
|
||||
- `POST /ai-reply/{cookie_id}` - 保存AI配置
|
||||
- `POST /ai-reply/{cookie_id}/test` - 测试AI回复
|
||||
|
||||
### 商品管理
|
||||
- `GET /items` - 获取所有商品
|
||||
- `GET /items/cookie/{cookie_id}` - 获取指定账号商品
|
||||
- `PUT /items/{cookie_id}/{item_id}` - 更新商品详情
|
||||
|
||||
### 系统监控
|
||||
- `GET /health` - 健康检查
|
||||
- `GET /logs` - 获取系统日志
|
||||
- `GET /logs/stats` - 日志统计信息
|
||||
|
||||
完整API文档访问:http://localhost:8080/docs
|
||||
|
||||
## 🏗️ 项目结构
|
||||
|
||||
```
|
||||
xianyu-auto-reply/
|
||||
├── Start.py # 项目启动入口
|
||||
├── XianyuAutoAsync.py # 闲鱼WebSocket客户端
|
||||
├── reply_server.py # FastAPI Web服务器
|
||||
├── config.py # 配置管理
|
||||
├── cookie_manager.py # Cookie账号管理
|
||||
├── db_manager.py # 数据库管理
|
||||
├── ai_reply_engine.py # AI回复引擎
|
||||
├── file_log_collector.py # 文件日志收集器
|
||||
├── bargain_demo.py # 议价功能演示
|
||||
├── global_config.yml # 全局配置文件
|
||||
├── requirements.txt # Python依赖
|
||||
├── Dockerfile # Docker镜像构建
|
||||
├── docker-compose.yml # Docker编排配置
|
||||
├── deploy.sh # 部署脚本
|
||||
├── static/ # 前端静态文件
|
||||
│ ├── index.html # 主页面
|
||||
│ ├── login.html # 登录页面
|
||||
│ └── xianyu_js_version_2.js # 前端逻辑
|
||||
├── utils/ # 工具模块
|
||||
│ ├── xianyu_utils.py # 闲鱼工具函数
|
||||
│ ├── message_utils.py # 消息处理工具
|
||||
│ └── ws_utils.py # WebSocket工具
|
||||
├── nginx/ # Nginx配置
|
||||
├── docs/ # 文档目录
|
||||
└── backups/ # 备份目录
|
||||
```
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
我们欢迎任何形式的贡献!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详细的贡献指南。
|
||||
|
||||
### 快速开始贡献
|
||||
1. **Fork** 项目到你的GitHub账号
|
||||
2. **克隆**项目到本地开发环境
|
||||
3. **创建分支**进行功能开发或问题修复
|
||||
4. **提交代码**并创建Pull Request
|
||||
|
||||
### 贡献类型
|
||||
- 🐛 **问题报告**:发现bug或提出改进建议
|
||||
- 💡 **功能建议**:提出新功能想法
|
||||
- 🔧 **代码贡献**:修复问题或开发新功能
|
||||
- 📚 **文档改进**:完善文档和说明
|
||||
- 🧪 **测试用例**:添加或改进测试
|
||||
|
||||
详细信息请参考:[贡献指南](CONTRIBUTING.md)
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 MIT 许可证,详情请查看 [LICENSE](LICENSE) 文件。
|
||||
|
||||
## 📋 更新日志
|
||||
|
||||
查看 [CHANGELOG.md](CHANGELOG.md) 了解详细的版本更新历史。
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
感谢所有为这个项目做出贡献的开发者和用户!
|
||||
|
||||
### 特别感谢
|
||||
- 所有提交代码的贡献者
|
||||
- 提供问题反馈的用户
|
||||
- 完善文档的志愿者
|
||||
- 推广项目的支持者
|
||||
|
||||
### 技术支持
|
||||
- [FastAPI](https://fastapi.tiangolo.com/) - 现代化的Web框架
|
||||
- [SQLite](https://www.sqlite.org/) - 轻量级数据库
|
||||
- [Docker](https://www.docker.com/) - 容器化技术
|
||||
- [Loguru](https://github.com/Delgan/loguru) - 优秀的日志库
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**如果这个项目对你有帮助,请给个 ⭐ Star 支持一下!**
|
||||
|
||||
[📋 报告问题](https://github.com/your-repo/xianyu-auto-reply/issues) • [💡 功能建议](https://github.com/your-repo/xianyu-auto-reply/issues) • [🤝 参与贡献](https://github.com/your-repo/xianyu-auto-reply/pulls) • [📖 查看文档](https://github.com/your-repo/xianyu-auto-reply/wiki)
|
||||
|
||||
**让我们一起构建更好的闲鱼自动化工具!** 🚀
|
||||
|
||||
</div>
|
||||
|
||||
## 🔧 核心模块说明
|
||||
|
||||
### Start.py - 项目启动入口
|
||||
- 初始化文件日志收集器
|
||||
- 创建和管理 CookieManager
|
||||
- 启动 FastAPI Web服务器
|
||||
- 加载配置文件和环境变量中的账号
|
||||
|
||||
### XianyuAutoAsync.py - 闲鱼WebSocket客户端
|
||||
- 维持与闲鱼服务器的WebSocket连接
|
||||
- 处理消息接收和发送
|
||||
- 自动刷新token和维持心跳
|
||||
- 商品信息获取和处理
|
||||
- 自动回复逻辑处理
|
||||
|
||||
### reply_server.py - FastAPI Web服务器
|
||||
- 提供Web管理界面
|
||||
- RESTful API接口
|
||||
- 用户认证和权限控制
|
||||
- 文件上传和下载
|
||||
- 健康检查和监控
|
||||
|
||||
### ai_reply_engine.py - AI回复引擎
|
||||
- 多AI模型支持(通义千问、GPT等)
|
||||
- 意图识别和分类
|
||||
- 智能议价逻辑
|
||||
- 对话历史管理
|
||||
- 自定义提示词处理
|
||||
|
||||
### db_manager.py - 数据库管理
|
||||
- SQLite数据库操作
|
||||
- 数据表结构管理
|
||||
- 数据备份和恢复
|
||||
- 事务处理和连接池
|
||||
|
||||
## 🎮 使用场景
|
||||
|
||||
### 个人卖家
|
||||
- **自动客服**:24小时自动回复买家咨询
|
||||
- **智能议价**:自动处理价格谈判,提高成交率
|
||||
- **快速发货**:自动发送卡券和虚拟商品
|
||||
|
||||
### 商家店铺
|
||||
- **多账号管理**:统一管理多个闲鱼账号
|
||||
- **批量操作**:批量设置关键词和回复规则
|
||||
- **数据分析**:查看消息统计和销售数据
|
||||
|
||||
### 代运营服务
|
||||
- **客户隔离**:为不同客户提供独立的账号管理
|
||||
- **定制化配置**:根据客户需求定制回复策略
|
||||
- **监控报告**:提供详细的运营数据报告
|
||||
|
||||
## 🔒 安全特性
|
||||
|
||||
### 数据安全
|
||||
- **本地存储**:所有数据存储在本地,不上传到第三方
|
||||
- **加密传输**:支持HTTPS和WSS加密传输
|
||||
- **权限控制**:基于JWT的用户认证系统
|
||||
|
||||
### 隐私保护
|
||||
- **Cookie加密**:敏感信息加密存储
|
||||
- **日志脱敏**:自动过滤敏感信息
|
||||
- **访问控制**:IP白名单和访问频率限制
|
||||
|
||||
## 📈 性能优化
|
||||
|
||||
### 系统性能
|
||||
- **异步处理**:全异步架构,高并发处理能力
|
||||
- **连接池**:数据库连接池,提高数据库操作效率
|
||||
- **缓存机制**:智能缓存,减少重复请求
|
||||
|
||||
### 资源优化
|
||||
- **内存管理**:自动清理过期数据和缓存
|
||||
- **日志轮转**:自动清理过期日志文件
|
||||
- **资源限制**:Docker资源限制,防止资源滥用
|
||||
|
||||
## 🚨 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: 系统启动失败?**
|
||||
A: 检查以下项目:
|
||||
- Python版本是否为3.11+
|
||||
- 依赖包是否正确安装
|
||||
- 端口8080是否被占用
|
||||
- 配置文件是否存在
|
||||
|
||||
**Q: WebSocket连接失败?**
|
||||
A: 检查以下方面:
|
||||
- 网络连接是否正常
|
||||
- Cookie是否有效
|
||||
- 防火墙设置
|
||||
- 代理配置
|
||||
|
||||
**Q: AI回复不工作?**
|
||||
A: 检查以下配置:
|
||||
- AI API密钥是否正确
|
||||
- API地址是否可访问
|
||||
- 账户余额是否充足
|
||||
- 网络连接是否稳定
|
||||
|
||||
### 日志查看
|
||||
```bash
|
||||
python Start.py
|
||||
# 查看实时日志
|
||||
docker-compose logs -f
|
||||
|
||||
# 查看特定服务日志
|
||||
docker-compose logs xianyu-app
|
||||
|
||||
# 查看系统日志文件
|
||||
tail -f logs/xianyu_$(date +%Y-%m-%d).log
|
||||
```
|
||||
|
||||
### 🔐 登录系统
|
||||
1. 启动后访问 `http://localhost:8080`
|
||||
2. 默认登录账号:
|
||||
- 用户名:`admin`
|
||||
- 密码:`admin123`
|
||||
3. 登录后可进入管理界面进行操作
|
||||
## 🤝 贡献指南
|
||||
|
||||
## 📁 项目结构
|
||||
```
|
||||
├── Start.py # 项目启动入口
|
||||
├── XianyuAutoAsync.py # 核心业务逻辑
|
||||
├── config.py # 配置管理
|
||||
├── cookie_manager.py # Cookie管理器
|
||||
├── db_manager.py # 数据库管理
|
||||
├── reply_server.py # FastAPI服务器
|
||||
├── utils/ # 工具函数目录
|
||||
│ ├── xianyu_utils.py # 闲鱼相关工具
|
||||
│ ├── message_utils.py # 消息处理工具
|
||||
│ └── ws_utils.py # WebSocket工具
|
||||
├── static/ # 静态资源
|
||||
│ ├── index.html # 管理界面
|
||||
│ └── login.html # 登录页面
|
||||
├── logs/ # 日志文件
|
||||
├── global_config.yml # 全局配置文件
|
||||
├── xianyu_data.db # SQLite数据库
|
||||
└── requirements.txt # Python依赖
|
||||
```
|
||||
我们欢迎任何形式的贡献!请查看 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详细的贡献指南。
|
||||
|
||||
## 🎯 主要功能
|
||||
### 快速开始贡献
|
||||
1. **Fork** 项目到你的GitHub账号
|
||||
2. **克隆**项目到本地开发环境
|
||||
3. **创建分支**进行功能开发或问题修复
|
||||
4. **提交代码**并创建Pull Request
|
||||
|
||||
### 1. 用户认证系统
|
||||
- 安全的登录认证机制
|
||||
- Session token管理
|
||||
- 自动登录状态检查
|
||||
- 登出功能
|
||||
### 贡献类型
|
||||
- 🐛 **问题报告**:发现bug或提出改进建议
|
||||
- 💡 **功能建议**:提出新功能想法
|
||||
- 🔧 **代码贡献**:修复问题或开发新功能
|
||||
- 📚 **文档改进**:完善文档和说明
|
||||
- 🧪 **测试用例**:添加或改进测试
|
||||
|
||||
### 2. 多账号管理
|
||||
- 支持添加多个闲鱼账号
|
||||
- 每个账号独立管理
|
||||
- Cookie安全存储
|
||||
- 账号状态监控
|
||||
### 开发规范
|
||||
- 遵循 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 代码风格
|
||||
- 使用 [Conventional Commits](https://www.conventionalcommits.org/) 提交规范
|
||||
- 为新功能添加相应的测试用例
|
||||
- 更新相关文档和说明
|
||||
|
||||
### 3. 智能关键词回复
|
||||
- 每个账号独立的关键词设置
|
||||
- 支持变量替换:`{send_user_name}`, `{send_user_id}`, `{send_message}`
|
||||
- 实时关键词匹配
|
||||
- 默认回复机制
|
||||
详细信息请参考:[贡献指南](CONTRIBUTING.md)
|
||||
|
||||
### 4. Web管理界面
|
||||
- 响应式设计,支持移动端
|
||||
- 直观的操作界面
|
||||
- 实时数据更新
|
||||
- 操作反馈提示
|
||||
## 📄 许可证
|
||||
|
||||
## 🔌 API 接口说明
|
||||
本项目采用 MIT 许可证,详情请查看 [LICENSE](LICENSE) 文件。
|
||||
|
||||
### 智能回复接口
|
||||
`POST http://localhost:8080/xianyu/reply`
|
||||
## <20> 更新日志
|
||||
|
||||
#### 接口说明
|
||||
你需要实现这个接口,本项目会调用这个接口获取自动回复的内容并发送给客户
|
||||
不实现这个接口也没关系,系统会默认回复,你也可以配置默认回复的内容
|
||||
用于处理闲鱼消息的自动回复,支持对接大语言模型进行智能回复。
|
||||
查看 [CHANGELOG.md](CHANGELOG.md) 了解详细的版本更新历史。
|
||||
|
||||
**通过这个接口可以检测到用户是否已付款,然后回复虚拟资料内容即可**
|
||||
#### 请求参数
|
||||
```json
|
||||
{
|
||||
"msg_time": "消息时间",
|
||||
"user_url": "用户主页URL",
|
||||
"send_user_id": "发送者ID",
|
||||
"send_user_name": "发送者昵称",
|
||||
"item_id": "商品ID",
|
||||
"send_message": "发送的消息内容",
|
||||
"chat_id": "会话ID"
|
||||
}
|
||||
```
|
||||
## <20>🙏 致谢
|
||||
|
||||
#### 响应格式
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"send_msg": "回复的消息内容"
|
||||
}
|
||||
}
|
||||
```
|
||||
感谢所有为这个项目做出贡献的开发者和用户!
|
||||
|
||||
#### 配置示例
|
||||
```yaml
|
||||
AUTO_REPLY:
|
||||
api:
|
||||
enabled: true # 是否启用API回复
|
||||
timeout: 10 # 超时时间(秒)
|
||||
url: http://localhost:8080/xianyu/reply
|
||||
```
|
||||
### 特别感谢
|
||||
- 所有提交代码的贡献者
|
||||
- 提供问题反馈的用户
|
||||
- 完善文档的志愿者
|
||||
- 推广项目的支持者
|
||||
|
||||
#### 使用场景
|
||||
- 当收到买家消息时,系统会自动调用此接口
|
||||
- 支持接入 ChatGPT、文心一言等大语言模型
|
||||
- 支持自定义回复规则和模板
|
||||
- 支持消息变量替换(如 `{send_user_name}`)
|
||||
### 技术支持
|
||||
- [FastAPI](https://fastapi.tiangolo.com/) - 现代化的Web框架
|
||||
- [SQLite](https://www.sqlite.org/) - 轻量级数据库
|
||||
- [Docker](https://www.docker.com/) - 容器化技术
|
||||
- [Loguru](https://github.com/Delgan/loguru) - 优秀的日志库
|
||||
|
||||
#### 注意事项
|
||||
- 接口需要返回正确的状态码(200)和消息内容
|
||||
- 建议实现错误重试机制
|
||||
- 注意处理超时情况(默认10秒)
|
||||
- 可以根据需要扩展更多的参数和功能
|
||||
---
|
||||
|
||||
## 🗝️ 注意事项
|
||||
- 请确保闲鱼账号已登录并获取有效的 Cookie
|
||||
- 建议在正式环境使用前先在测试环境验证
|
||||
- 定期检查日志文件,及时处理异常情况
|
||||
- 使用大模型时注意 API 调用频率和成本控制
|
||||
<div align="center">
|
||||
|
||||
## 📝 效果
|
||||
**如果这个项目对你有帮助,请给个 ⭐ Star 支持一下!**
|
||||
|
||||
[📋 报告问题](https://github.com/your-repo/xianyu-auto-reply/issues) • [💡 功能建议](https://github.com/your-repo/xianyu-auto-reply/issues) • [🤝 参与贡献](https://github.com/your-repo/xianyu-auto-reply/pulls) • [📖 查看文档](https://github.com/your-repo/xianyu-auto-reply/wiki)
|
||||
|
||||

|
||||
**让我们一起构建更好的闲鱼自动化工具!** 🚀
|
||||
|
||||

|
||||
|
||||
## 🧸特别鸣谢
|
||||
|
||||
本项目参考了以下开源项目: https://github.com/cv-cat/XianYuApis
|
||||
|
||||
感谢[@CVcat](https://github.com/cv-cat)的技术支持
|
||||
|
||||
## 📞 联系方式
|
||||
如有问题或建议,欢迎提交 Issue 或 Pull Request。
|
||||
|
||||
## 技术交流
|
||||
|
||||

|
||||
</div>
|
||||
|
@ -1,92 +0,0 @@
|
||||
# 🎨 界面优化总结
|
||||
|
||||
## ✨ 主要改进
|
||||
|
||||
### 1. 🎯 Cookie显示优化
|
||||
- **❌ 修改前**: Cookie值被隐藏为星号 (`****`)
|
||||
- **✅ 修改后**: 显示完整的Cookie内容,便于查看和调试
|
||||
|
||||
### 2. 🎨 视觉设计升级
|
||||
- **现代化配色方案**: 使用更现代的紫色主题 (`#4f46e5`)
|
||||
- **渐变背景**: 美丽的渐变背景和卡片效果
|
||||
- **毛玻璃效果**: 卡片使用 `backdrop-filter: blur()` 实现毛玻璃效果
|
||||
- **阴影和动画**: 悬停时的阴影和位移动画效果
|
||||
|
||||
### 3. 🔧 功能增强
|
||||
- **一键复制Cookie**: 点击Cookie值或复制按钮即可复制到剪贴板
|
||||
- **改进的按钮组**: 更紧凑的按钮布局,包含复制功能
|
||||
- **更好的空状态**: 当没有账号时显示更友好的提示
|
||||
|
||||
### 4. 📱 响应式设计
|
||||
- **移动端优化**: 在小屏幕上按钮垂直排列
|
||||
- **自适应布局**: 表格和卡片在不同屏幕尺寸下的自适应
|
||||
|
||||
## 🛠️ 技术改进
|
||||
|
||||
### 新增API接口
|
||||
```javascript
|
||||
GET /cookies/details
|
||||
```
|
||||
返回包含Cookie ID和完整值的详细信息,而不仅仅是ID列表。
|
||||
|
||||
### CSS样式优化
|
||||
- **CSS变量**: 统一的颜色管理
|
||||
- **现代字体**: 使用 Inter 字体提升可读性
|
||||
- **代码字体**: Cookie值使用等宽字体 (JetBrains Mono)
|
||||
- **流畅动画**: 所有交互都有平滑的过渡效果
|
||||
|
||||
### JavaScript功能增强
|
||||
- **复制功能**: 支持现代浏览器的 Clipboard API
|
||||
- **降级方案**: 对于不支持的浏览器提供传统复制方法
|
||||
- **用户反馈**: 复制成功/失败的Toast提示
|
||||
|
||||
## 🎯 用户体验提升
|
||||
|
||||
### 1. Cookie管理
|
||||
- **完整显示**: 不再隐藏Cookie内容,便于调试
|
||||
- **一键复制**: 快速复制Cookie值到剪贴板
|
||||
- **格式化显示**: 使用等宽字体和适当的行高
|
||||
|
||||
### 2. 视觉反馈
|
||||
- **悬停效果**: 所有可交互元素都有悬停反馈
|
||||
- **状态指示**: 清晰的按钮状态和颜色区分
|
||||
- **加载动画**: 优雅的加载状态显示
|
||||
|
||||
### 3. 操作便利性
|
||||
- **按钮分组**: 相关操作按钮紧凑排列
|
||||
- **图标提示**: 每个按钮都有清晰的图标和提示
|
||||
- **确认对话框**: 危险操作有确认提示
|
||||
|
||||
## 📊 界面对比
|
||||
|
||||
| 功能 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| Cookie显示 | 隐藏为星号 | 完整显示 |
|
||||
| 复制功能 | 无 | 一键复制 |
|
||||
| 视觉效果 | 基础Bootstrap | 现代化渐变设计 |
|
||||
| 响应式 | 基本支持 | 完全优化 |
|
||||
| 用户反馈 | 基础提示 | 丰富的Toast反馈 |
|
||||
|
||||
## 🚀 使用说明
|
||||
|
||||
### 访问界面
|
||||
1. 启动系统: `python Start.py`
|
||||
2. 打开浏览器: `http://localhost:8080`
|
||||
3. 登录: `admin` / `admin123`
|
||||
|
||||
### 主要功能
|
||||
- **添加账号**: 在顶部表单中输入账号ID和Cookie值
|
||||
- **查看Cookie**: 完整的Cookie值显示在表格中
|
||||
- **复制Cookie**: 点击Cookie值或复制按钮
|
||||
- **管理关键词**: 点击关键词按钮设置自动回复
|
||||
- **删除账号**: 点击删除按钮(有确认提示)
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
这次界面优化大幅提升了用户体验:
|
||||
- ✅ **Cookie不再隐藏**,便于查看和调试
|
||||
- ✅ **现代化设计**,视觉效果更佳
|
||||
- ✅ **功能增强**,操作更便利
|
||||
- ✅ **响应式优化**,支持各种设备
|
||||
|
||||
界面现在更加美观、实用和用户友好!🎨✨
|
@ -650,7 +650,7 @@ class XianyuLive:
|
||||
if result:
|
||||
return result
|
||||
|
||||
logger.warning("所有方法都未能提取到商品ID")
|
||||
logger.debug("所有方法都未能提取到商品ID")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
@ -713,6 +713,105 @@ class XianyuLive:
|
||||
logger.error(f"获取默认回复失败: {self._safe_str(e)}")
|
||||
return None
|
||||
|
||||
async def get_keyword_reply(self, send_user_name: str, send_user_id: str, send_message: str) -> str:
|
||||
"""获取关键词匹配回复"""
|
||||
try:
|
||||
from db_manager import db_manager
|
||||
|
||||
# 获取当前账号的关键词列表
|
||||
keywords = db_manager.get_keywords(self.cookie_id)
|
||||
|
||||
if not keywords:
|
||||
logger.debug(f"账号 {self.cookie_id} 没有配置关键词")
|
||||
return None
|
||||
|
||||
# 遍历关键词,查找匹配
|
||||
for keyword, reply in keywords:
|
||||
if keyword.lower() in send_message.lower():
|
||||
# 进行变量替换
|
||||
try:
|
||||
formatted_reply = reply.format(
|
||||
send_user_name=send_user_name,
|
||||
send_user_id=send_user_id,
|
||||
send_message=send_message
|
||||
)
|
||||
logger.info(f"关键词匹配成功: '{keyword}' -> {formatted_reply}")
|
||||
return f"[关键词回复] {formatted_reply}"
|
||||
except Exception as format_error:
|
||||
logger.error(f"关键词回复变量替换失败: {self._safe_str(format_error)}")
|
||||
# 如果变量替换失败,返回原始内容
|
||||
return f"[关键词回复] {reply}"
|
||||
|
||||
logger.debug(f"未找到匹配的关键词: {send_message}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取关键词回复失败: {self._safe_str(e)}")
|
||||
return None
|
||||
|
||||
async def get_ai_reply(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str, chat_id: str):
|
||||
"""获取AI回复"""
|
||||
try:
|
||||
from ai_reply_engine import ai_reply_engine
|
||||
|
||||
# 检查是否启用AI回复
|
||||
if not ai_reply_engine.is_ai_enabled(self.cookie_id):
|
||||
logger.debug(f"账号 {self.cookie_id} 未启用AI回复")
|
||||
return None
|
||||
|
||||
# 从数据库获取商品信息
|
||||
from db_manager import db_manager
|
||||
item_info_raw = db_manager.get_item_info(self.cookie_id, item_id)
|
||||
|
||||
if not item_info_raw:
|
||||
logger.debug(f"数据库中无商品信息: {item_id}")
|
||||
# 使用默认商品信息
|
||||
item_info = {
|
||||
'title': '商品信息获取失败',
|
||||
'price': 0,
|
||||
'desc': '暂无商品描述'
|
||||
}
|
||||
else:
|
||||
# 解析数据库中的商品信息
|
||||
item_info = {
|
||||
'title': item_info_raw.get('item_title', '未知商品'),
|
||||
'price': self._parse_price(item_info_raw.get('item_price', '0')),
|
||||
'desc': item_info_raw.get('item_description', '暂无商品描述')
|
||||
}
|
||||
|
||||
# 生成AI回复
|
||||
reply = ai_reply_engine.generate_reply(
|
||||
message=send_message,
|
||||
item_info=item_info,
|
||||
chat_id=chat_id,
|
||||
cookie_id=self.cookie_id,
|
||||
user_id=send_user_id,
|
||||
item_id=item_id
|
||||
)
|
||||
|
||||
if reply:
|
||||
logger.info(f"AI回复生成成功: {reply}")
|
||||
return f"[AI回复] {reply}"
|
||||
else:
|
||||
logger.debug(f"AI回复生成失败")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取AI回复失败: {self._safe_str(e)}")
|
||||
return None
|
||||
|
||||
def _parse_price(self, price_str: str) -> float:
|
||||
"""解析价格字符串为数字"""
|
||||
try:
|
||||
if not price_str:
|
||||
return 0.0
|
||||
# 移除非数字字符,保留小数点
|
||||
import re
|
||||
price_clean = re.sub(r'[^\d.]', '', str(price_str))
|
||||
return float(price_clean) if price_clean else 0.0
|
||||
except:
|
||||
return 0.0
|
||||
|
||||
async def send_notification(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str = None):
|
||||
"""发送消息通知"""
|
||||
try:
|
||||
@ -911,11 +1010,11 @@ class XianyuLive:
|
||||
logger.warning(f"数据库中商品标题和详情都为空,且无法从API获取: {item_id}")
|
||||
search_text = item_title or item_id
|
||||
else:
|
||||
logger.warning(f"数据库中未找到商品信息: {item_id}")
|
||||
logger.debug(f"数据库中未找到商品信息: {item_id}")
|
||||
search_text = item_title or item_id
|
||||
|
||||
except Exception as db_e:
|
||||
logger.warning(f"从数据库获取商品信息失败: {self._safe_str(db_e)}")
|
||||
logger.debug(f"从数据库获取商品信息失败: {self._safe_str(db_e)}")
|
||||
search_text = item_title or item_id
|
||||
|
||||
if not search_text:
|
||||
@ -1499,7 +1598,7 @@ class XianyuLive:
|
||||
else:
|
||||
user_id = "unknown_user"
|
||||
except Exception as e:
|
||||
logger.warning(f"提取用户ID失败: {self._safe_str(e)}")
|
||||
logger.debug(f"提取用户ID失败: {self._safe_str(e)}")
|
||||
user_id = "unknown_user"
|
||||
|
||||
# 安全地获取商品ID
|
||||
@ -1542,7 +1641,7 @@ class XianyuLive:
|
||||
|
||||
if not item_id:
|
||||
item_id = f"auto_{user_id}_{int(time.time())}"
|
||||
logger.warning(f"无法提取商品ID,使用默认值: {item_id}")
|
||||
logger.debug(f"无法提取商品ID,使用默认值: {item_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"提取商品ID时发生错误: {self._safe_str(e)}")
|
||||
@ -1719,10 +1818,20 @@ class XianyuLive:
|
||||
# 记录回复来源
|
||||
reply_source = 'API' # 默认假设是API回复
|
||||
|
||||
# 如果API回复失败或未启用API,尝试使用默认回复
|
||||
# 如果API回复失败或未启用API,尝试使用AI回复
|
||||
if not reply:
|
||||
reply = await self.get_default_reply(send_user_name, send_user_id, send_message)
|
||||
reply_source = '默认' # 标记为默认回复
|
||||
reply = await self.get_ai_reply(send_user_name, send_user_id, send_message, item_id, chat_id)
|
||||
if reply:
|
||||
reply_source = 'AI' # 标记为AI回复
|
||||
else:
|
||||
# 如果AI回复也失败,尝试关键词匹配
|
||||
reply = await self.get_keyword_reply(send_user_name, send_user_id, send_message)
|
||||
if reply:
|
||||
reply_source = '关键词' # 标记为关键词回复
|
||||
else:
|
||||
# 最后尝试使用默认回复
|
||||
reply = await self.get_default_reply(send_user_name, send_user_id, send_message)
|
||||
reply_source = '默认' # 标记为默认回复
|
||||
|
||||
# 保存商品信息到数据库(记录通知时传递的item_id)
|
||||
if item_id:
|
||||
|
273
ai_reply_engine.py
Normal file
273
ai_reply_engine.py
Normal file
@ -0,0 +1,273 @@
|
||||
"""
|
||||
AI回复引擎模块
|
||||
集成XianyuAutoAgent的AI回复功能到现有项目中
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import sqlite3
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
from openai import OpenAI
|
||||
from db_manager import db_manager
|
||||
|
||||
|
||||
class AIReplyEngine:
|
||||
"""AI回复引擎"""
|
||||
|
||||
def __init__(self):
|
||||
self.clients = {} # 存储不同账号的OpenAI客户端
|
||||
self.agents = {} # 存储不同账号的Agent实例
|
||||
self._init_default_prompts()
|
||||
|
||||
def _init_default_prompts(self):
|
||||
"""初始化默认提示词"""
|
||||
self.default_prompts = {
|
||||
'classify': '''你是一个意图分类专家,需要判断用户消息的意图类型。
|
||||
请根据用户消息内容,返回以下意图之一:
|
||||
- price: 价格相关(议价、优惠、降价等)
|
||||
- tech: 技术相关(产品参数、使用方法、故障等)
|
||||
- default: 其他一般咨询
|
||||
|
||||
只返回意图类型,不要其他内容。''',
|
||||
|
||||
'price': '''你是一位经验丰富的销售专家,擅长议价。
|
||||
语言要求:简短直接,每句≤10字,总字数≤40字。
|
||||
议价策略:
|
||||
1. 根据议价次数递减优惠:第1次小幅优惠,第2次中等优惠,第3次最大优惠
|
||||
2. 接近最大议价轮数时要坚持底线,强调商品价值
|
||||
3. 优惠不能超过设定的最大百分比和金额
|
||||
4. 语气要友好但坚定,突出商品优势
|
||||
注意:结合商品信息、对话历史和议价设置,给出合适的回复。''',
|
||||
|
||||
'tech': '''你是一位技术专家,专业解答产品相关问题。
|
||||
语言要求:简短专业,每句≤10字,总字数≤40字。
|
||||
回答重点:产品功能、使用方法、注意事项。
|
||||
注意:基于商品信息回答,避免过度承诺。''',
|
||||
|
||||
'default': '''你是一位资深电商卖家,提供优质客服。
|
||||
语言要求:简短友好,每句≤10字,总字数≤40字。
|
||||
回答重点:商品介绍、物流、售后等常见问题。
|
||||
注意:结合商品信息,给出实用建议。'''
|
||||
}
|
||||
|
||||
def get_client(self, cookie_id: str) -> Optional[OpenAI]:
|
||||
"""获取指定账号的OpenAI客户端"""
|
||||
if cookie_id not in self.clients:
|
||||
settings = db_manager.get_ai_reply_settings(cookie_id)
|
||||
if not settings['ai_enabled'] or not settings['api_key']:
|
||||
return None
|
||||
|
||||
try:
|
||||
self.clients[cookie_id] = OpenAI(
|
||||
api_key=settings['api_key'],
|
||||
base_url=settings['base_url']
|
||||
)
|
||||
logger.info(f"为账号 {cookie_id} 创建OpenAI客户端")
|
||||
except Exception as e:
|
||||
logger.error(f"创建OpenAI客户端失败 {cookie_id}: {e}")
|
||||
return None
|
||||
|
||||
return self.clients[cookie_id]
|
||||
|
||||
def is_ai_enabled(self, cookie_id: str) -> bool:
|
||||
"""检查指定账号是否启用AI回复"""
|
||||
settings = db_manager.get_ai_reply_settings(cookie_id)
|
||||
return settings['ai_enabled']
|
||||
|
||||
def detect_intent(self, message: str, cookie_id: str) -> str:
|
||||
"""检测用户消息意图"""
|
||||
client = self.get_client(cookie_id)
|
||||
if not client:
|
||||
return 'default'
|
||||
|
||||
try:
|
||||
settings = db_manager.get_ai_reply_settings(cookie_id)
|
||||
custom_prompts = json.loads(settings['custom_prompts']) if settings['custom_prompts'] else {}
|
||||
classify_prompt = custom_prompts.get('classify', self.default_prompts['classify'])
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model=settings['model_name'],
|
||||
messages=[
|
||||
{"role": "system", "content": classify_prompt},
|
||||
{"role": "user", "content": message}
|
||||
],
|
||||
max_tokens=10,
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
intent = response.choices[0].message.content.strip().lower()
|
||||
if intent in ['price', 'tech', 'default']:
|
||||
return intent
|
||||
else:
|
||||
return 'default'
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"意图检测失败 {cookie_id}: {e}")
|
||||
return 'default'
|
||||
|
||||
def generate_reply(self, message: str, item_info: dict, chat_id: str,
|
||||
cookie_id: str, user_id: str, item_id: str) -> Optional[str]:
|
||||
"""生成AI回复"""
|
||||
if not self.is_ai_enabled(cookie_id):
|
||||
return None
|
||||
|
||||
client = self.get_client(cookie_id)
|
||||
if not client:
|
||||
return None
|
||||
|
||||
try:
|
||||
# 1. 获取AI回复设置
|
||||
settings = db_manager.get_ai_reply_settings(cookie_id)
|
||||
|
||||
# 2. 检测意图
|
||||
intent = self.detect_intent(message, cookie_id)
|
||||
logger.info(f"检测到意图: {intent} (账号: {cookie_id})")
|
||||
|
||||
# 3. 获取对话历史
|
||||
context = self.get_conversation_context(chat_id, cookie_id)
|
||||
|
||||
# 4. 获取议价次数
|
||||
bargain_count = self.get_bargain_count(chat_id, cookie_id)
|
||||
|
||||
# 5. 检查议价轮数限制
|
||||
if intent == "price":
|
||||
max_bargain_rounds = settings.get('max_bargain_rounds', 3)
|
||||
if bargain_count >= max_bargain_rounds:
|
||||
logger.info(f"议价次数已达上限 ({bargain_count}/{max_bargain_rounds}),拒绝继续议价")
|
||||
# 返回拒绝议价的回复
|
||||
refuse_reply = f"抱歉,这个价格已经是最优惠的了,不能再便宜了哦!"
|
||||
# 保存对话记录
|
||||
self.save_conversation(chat_id, cookie_id, user_id, item_id, "user", message, intent)
|
||||
self.save_conversation(chat_id, cookie_id, user_id, item_id, "assistant", refuse_reply, intent)
|
||||
return refuse_reply
|
||||
|
||||
# 6. 构建提示词
|
||||
custom_prompts = json.loads(settings['custom_prompts']) if settings['custom_prompts'] else {}
|
||||
system_prompt = custom_prompts.get(intent, self.default_prompts[intent])
|
||||
|
||||
# 7. 构建商品信息
|
||||
item_desc = f"商品标题: {item_info.get('title', '未知')}\n"
|
||||
item_desc += f"商品价格: {item_info.get('price', '未知')}元\n"
|
||||
item_desc += f"商品描述: {item_info.get('desc', '无')}"
|
||||
|
||||
# 8. 构建对话历史
|
||||
context_str = "\n".join([f"{msg['role']}: {msg['content']}" for msg in context[-10:]]) # 最近10条
|
||||
|
||||
# 9. 构建用户消息
|
||||
max_bargain_rounds = settings.get('max_bargain_rounds', 3)
|
||||
max_discount_percent = settings.get('max_discount_percent', 10)
|
||||
max_discount_amount = settings.get('max_discount_amount', 100)
|
||||
|
||||
user_prompt = f"""商品信息:
|
||||
{item_desc}
|
||||
|
||||
对话历史:
|
||||
{context_str}
|
||||
|
||||
议价设置:
|
||||
- 当前议价次数:{bargain_count}
|
||||
- 最大议价轮数:{max_bargain_rounds}
|
||||
- 最大优惠百分比:{max_discount_percent}%
|
||||
- 最大优惠金额:{max_discount_amount}元
|
||||
|
||||
用户消息:{message}
|
||||
|
||||
请根据以上信息生成回复:"""
|
||||
|
||||
# 10. 调用AI生成回复
|
||||
response = client.chat.completions.create(
|
||||
model=settings['model_name'],
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
max_tokens=100,
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
reply = response.choices[0].message.content.strip()
|
||||
|
||||
# 11. 保存对话记录
|
||||
self.save_conversation(chat_id, cookie_id, user_id, item_id, "user", message, intent)
|
||||
self.save_conversation(chat_id, cookie_id, user_id, item_id, "assistant", reply, intent)
|
||||
|
||||
# 12. 更新议价次数
|
||||
if intent == "price":
|
||||
self.increment_bargain_count(chat_id, cookie_id)
|
||||
|
||||
logger.info(f"AI回复生成成功 (账号: {cookie_id}): {reply}")
|
||||
return reply
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"AI回复生成失败 {cookie_id}: {e}")
|
||||
return None
|
||||
|
||||
def get_conversation_context(self, chat_id: str, cookie_id: str, limit: int = 20) -> List[Dict]:
|
||||
"""获取对话上下文"""
|
||||
try:
|
||||
with db_manager.lock:
|
||||
cursor = db_manager.conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT role, content FROM ai_conversations
|
||||
WHERE chat_id = ? AND cookie_id = ?
|
||||
ORDER BY created_at DESC LIMIT ?
|
||||
''', (chat_id, cookie_id, limit))
|
||||
|
||||
results = cursor.fetchall()
|
||||
# 反转顺序,使其按时间正序
|
||||
context = [{"role": row[0], "content": row[1]} for row in reversed(results)]
|
||||
return context
|
||||
except Exception as e:
|
||||
logger.error(f"获取对话上下文失败: {e}")
|
||||
return []
|
||||
|
||||
def save_conversation(self, chat_id: str, cookie_id: str, user_id: str,
|
||||
item_id: str, role: str, content: str, intent: str = None):
|
||||
"""保存对话记录"""
|
||||
try:
|
||||
with db_manager.lock:
|
||||
cursor = db_manager.conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO ai_conversations
|
||||
(cookie_id, chat_id, user_id, item_id, role, content, intent)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
''', (cookie_id, chat_id, user_id, item_id, role, content, intent))
|
||||
db_manager.conn.commit()
|
||||
except Exception as e:
|
||||
logger.error(f"保存对话记录失败: {e}")
|
||||
|
||||
def get_bargain_count(self, chat_id: str, cookie_id: str) -> int:
|
||||
"""获取议价次数"""
|
||||
try:
|
||||
with db_manager.lock:
|
||||
cursor = db_manager.conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT COUNT(*) FROM ai_conversations
|
||||
WHERE chat_id = ? AND cookie_id = ? AND intent = 'price' AND role = 'user'
|
||||
''', (chat_id, cookie_id))
|
||||
|
||||
result = cursor.fetchone()
|
||||
return result[0] if result else 0
|
||||
except Exception as e:
|
||||
logger.error(f"获取议价次数失败: {e}")
|
||||
return 0
|
||||
|
||||
def increment_bargain_count(self, chat_id: str, cookie_id: str):
|
||||
"""增加议价次数(通过保存记录自动增加)"""
|
||||
# 议价次数通过查询price意图的用户消息数量来计算,无需单独操作
|
||||
pass
|
||||
|
||||
def clear_client_cache(self, cookie_id: str = None):
|
||||
"""清理客户端缓存"""
|
||||
if cookie_id:
|
||||
self.clients.pop(cookie_id, None)
|
||||
logger.info(f"清理账号 {cookie_id} 的客户端缓存")
|
||||
else:
|
||||
self.clients.clear()
|
||||
logger.info("清理所有客户端缓存")
|
||||
|
||||
|
||||
# 全局AI回复引擎实例
|
||||
ai_reply_engine = AIReplyEngine()
|
180
bargain_demo.py
Normal file
180
bargain_demo.py
Normal file
@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
议价功能演示脚本
|
||||
展示AI回复的议价轮数限制功能
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加项目根目录到Python路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from ai_reply_engine import ai_reply_engine
|
||||
from db_manager import db_manager
|
||||
|
||||
def simulate_bargain_conversation():
|
||||
"""模拟议价对话流程"""
|
||||
print("🎭 模拟议价对话流程")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试参数
|
||||
cookie_id = "demo_account_001"
|
||||
chat_id = "demo_chat_001"
|
||||
user_id = "customer_001"
|
||||
item_id = "item_12345"
|
||||
user_name = "小明"
|
||||
|
||||
# 商品信息
|
||||
item_info = {
|
||||
'title': 'iPhone 14 Pro 256GB 深空黑色',
|
||||
'price': 8999,
|
||||
'desc': '全新未拆封,国行正品,支持全国联保'
|
||||
}
|
||||
|
||||
# 设置AI回复配置(模拟)
|
||||
ai_settings = {
|
||||
'ai_enabled': True,
|
||||
'model_name': 'qwen-plus',
|
||||
'api_key': 'demo-key',
|
||||
'base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
'max_discount_percent': 10, # 最大优惠10%
|
||||
'max_discount_amount': 500, # 最大优惠500元
|
||||
'max_bargain_rounds': 3, # 最大议价3轮
|
||||
'custom_prompts': ''
|
||||
}
|
||||
|
||||
# 保存配置
|
||||
db_manager.save_ai_reply_settings(cookie_id, ai_settings)
|
||||
|
||||
# 清理之前的对话
|
||||
try:
|
||||
with db_manager.lock:
|
||||
cursor = db_manager.conn.cursor()
|
||||
cursor.execute('DELETE FROM ai_conversations WHERE cookie_id = ? AND chat_id = ?',
|
||||
(cookie_id, chat_id))
|
||||
db_manager.conn.commit()
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f"📱 商品信息:")
|
||||
print(f" 标题: {item_info['title']}")
|
||||
print(f" 价格: ¥{item_info['price']}")
|
||||
print(f" 描述: {item_info['desc']}")
|
||||
|
||||
print(f"\n⚙️ 议价设置:")
|
||||
print(f" 最大议价轮数: {ai_settings['max_bargain_rounds']}")
|
||||
print(f" 最大优惠百分比: {ai_settings['max_discount_percent']}%")
|
||||
print(f" 最大优惠金额: ¥{ai_settings['max_discount_amount']}")
|
||||
|
||||
# 模拟议价对话
|
||||
bargain_messages = [
|
||||
"你好,这个iPhone能便宜点吗?",
|
||||
"8500能卖吗?",
|
||||
"8800行不行?最后一次了",
|
||||
"8700,真的不能再少了"
|
||||
]
|
||||
|
||||
print(f"\n💬 议价对话模拟:")
|
||||
print("-" * 30)
|
||||
|
||||
for i, message in enumerate(bargain_messages, 1):
|
||||
print(f"\n第{i}轮议价:")
|
||||
print(f"👤 {user_name}: {message}")
|
||||
|
||||
# 检查当前议价次数
|
||||
current_count = ai_reply_engine.get_bargain_count(chat_id, cookie_id)
|
||||
max_rounds = ai_settings['max_bargain_rounds']
|
||||
|
||||
print(f"📊 当前议价次数: {current_count}/{max_rounds}")
|
||||
|
||||
# 模拟意图检测为price
|
||||
intent = "price"
|
||||
|
||||
# 检查是否超出限制
|
||||
if current_count >= max_rounds:
|
||||
print(f"🚫 已达到最大议价轮数限制!")
|
||||
refuse_reply = "抱歉,这个价格已经是最优惠的了,不能再便宜了哦!"
|
||||
print(f"🤖 AI回复: {refuse_reply}")
|
||||
|
||||
# 保存对话记录
|
||||
ai_reply_engine.save_conversation(chat_id, cookie_id, user_id, item_id, "user", message, intent)
|
||||
ai_reply_engine.save_conversation(chat_id, cookie_id, user_id, item_id, "assistant", refuse_reply, intent)
|
||||
|
||||
print(f"✋ 议价结束,系统拒绝继续议价")
|
||||
break
|
||||
else:
|
||||
# 模拟AI回复(因为没有真实API密钥,这里手动模拟)
|
||||
if i == 1:
|
||||
ai_reply = "您好!这个价格已经很优惠了,最多可以优惠200元,8799元怎么样?"
|
||||
elif i == 2:
|
||||
ai_reply = "8500太低了,我们再让一点,8699元,这已经是很大的优惠了!"
|
||||
elif i == 3:
|
||||
ai_reply = "好的,看您很有诚意,8799元成交,这真的是最低价了!"
|
||||
else:
|
||||
ai_reply = "抱歉,这个价格已经是最优惠的了!"
|
||||
|
||||
print(f"🤖 AI回复: {ai_reply}")
|
||||
|
||||
# 保存对话记录
|
||||
ai_reply_engine.save_conversation(chat_id, cookie_id, user_id, item_id, "user", message, intent)
|
||||
ai_reply_engine.save_conversation(chat_id, cookie_id, user_id, item_id, "assistant", ai_reply, intent)
|
||||
|
||||
# 显示最终统计
|
||||
print(f"\n📈 最终统计:")
|
||||
final_count = ai_reply_engine.get_bargain_count(chat_id, cookie_id)
|
||||
print(f" 总议价轮数: {final_count}")
|
||||
print(f" 最大允许轮数: {max_rounds}")
|
||||
print(f" 是否达到限制: {'是' if final_count >= max_rounds else '否'}")
|
||||
|
||||
def show_bargain_features():
|
||||
"""展示议价功能特性"""
|
||||
print(f"\n🎯 议价功能特性说明:")
|
||||
print("=" * 50)
|
||||
|
||||
features = [
|
||||
"✅ 智能议价轮数统计:自动统计用户的议价次数",
|
||||
"✅ 灵活轮数限制:可配置最大议价轮数(1-10轮)",
|
||||
"✅ 优惠金额控制:设置最大优惠百分比和金额",
|
||||
"✅ 友好拒绝回复:超出限制时礼貌拒绝继续议价",
|
||||
"✅ 上下文感知:AI了解完整的议价历史",
|
||||
"✅ 个性化策略:根据议价轮数调整回复策略",
|
||||
"✅ 数据持久化:议价记录永久保存",
|
||||
"✅ 实时生效:配置修改后立即生效"
|
||||
]
|
||||
|
||||
for feature in features:
|
||||
print(f" {feature}")
|
||||
|
||||
print(f"\n💡 使用建议:")
|
||||
suggestions = [
|
||||
"设置合理的最大议价轮数(建议3-5轮)",
|
||||
"配合最大优惠百分比和金额使用",
|
||||
"在AI提示词中强调议价策略",
|
||||
"定期分析议价数据,优化策略",
|
||||
"根据商品类型调整议价参数"
|
||||
]
|
||||
|
||||
for suggestion in suggestions:
|
||||
print(f" • {suggestion}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 AI回复议价功能演示")
|
||||
|
||||
# 模拟议价对话
|
||||
simulate_bargain_conversation()
|
||||
|
||||
# 展示功能特性
|
||||
show_bargain_features()
|
||||
|
||||
print(f"\n🎉 演示完成!")
|
||||
print(f"\n📋 下一步:")
|
||||
print(f" 1. 在Web界面配置真实的AI API密钥")
|
||||
print(f" 2. 为需要的账号启用AI回复功能")
|
||||
print(f" 3. 设置合适的议价参数")
|
||||
print(f" 4. 测试实际的议价效果")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
166
db_manager.py
166
db_manager.py
@ -81,6 +81,52 @@ class DBManager:
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建AI回复配置表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ai_reply_settings (
|
||||
cookie_id TEXT PRIMARY KEY,
|
||||
ai_enabled BOOLEAN DEFAULT FALSE,
|
||||
model_name TEXT DEFAULT 'qwen-plus',
|
||||
api_key TEXT,
|
||||
base_url TEXT DEFAULT 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
max_discount_percent INTEGER DEFAULT 10,
|
||||
max_discount_amount INTEGER DEFAULT 100,
|
||||
max_bargain_rounds INTEGER DEFAULT 3,
|
||||
custom_prompts TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (cookie_id) REFERENCES cookies(id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建AI对话历史表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ai_conversations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
cookie_id TEXT NOT NULL,
|
||||
chat_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
item_id TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
intent TEXT,
|
||||
bargain_count INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (cookie_id) REFERENCES cookies (id) ON DELETE CASCADE
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建AI商品信息缓存表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ai_item_cache (
|
||||
item_id TEXT PRIMARY KEY,
|
||||
data TEXT NOT NULL,
|
||||
price REAL,
|
||||
description TEXT,
|
||||
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建卡券表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS cards (
|
||||
@ -394,6 +440,117 @@ class DBManager:
|
||||
logger.error(f"获取所有Cookie状态失败: {e}")
|
||||
return {}
|
||||
|
||||
# -------------------- AI回复设置操作 --------------------
|
||||
def save_ai_reply_settings(self, cookie_id: str, settings: dict) -> bool:
|
||||
"""保存AI回复设置"""
|
||||
with self.lock:
|
||||
try:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT OR REPLACE INTO ai_reply_settings
|
||||
(cookie_id, ai_enabled, model_name, api_key, base_url,
|
||||
max_discount_percent, max_discount_amount, max_bargain_rounds,
|
||||
custom_prompts, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
''', (
|
||||
cookie_id,
|
||||
settings.get('ai_enabled', False),
|
||||
settings.get('model_name', 'qwen-plus'),
|
||||
settings.get('api_key', ''),
|
||||
settings.get('base_url', 'https://dashscope.aliyuncs.com/compatible-mode/v1'),
|
||||
settings.get('max_discount_percent', 10),
|
||||
settings.get('max_discount_amount', 100),
|
||||
settings.get('max_bargain_rounds', 3),
|
||||
settings.get('custom_prompts', '')
|
||||
))
|
||||
self.conn.commit()
|
||||
logger.debug(f"AI回复设置保存成功: {cookie_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"保存AI回复设置失败: {e}")
|
||||
self.conn.rollback()
|
||||
return False
|
||||
|
||||
def get_ai_reply_settings(self, cookie_id: str) -> dict:
|
||||
"""获取AI回复设置"""
|
||||
with self.lock:
|
||||
try:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT ai_enabled, model_name, api_key, base_url,
|
||||
max_discount_percent, max_discount_amount, max_bargain_rounds,
|
||||
custom_prompts
|
||||
FROM ai_reply_settings WHERE cookie_id = ?
|
||||
''', (cookie_id,))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
return {
|
||||
'ai_enabled': bool(result[0]),
|
||||
'model_name': result[1],
|
||||
'api_key': result[2],
|
||||
'base_url': result[3],
|
||||
'max_discount_percent': result[4],
|
||||
'max_discount_amount': result[5],
|
||||
'max_bargain_rounds': result[6],
|
||||
'custom_prompts': result[7]
|
||||
}
|
||||
else:
|
||||
# 返回默认设置
|
||||
return {
|
||||
'ai_enabled': False,
|
||||
'model_name': 'qwen-plus',
|
||||
'api_key': '',
|
||||
'base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
'max_discount_percent': 10,
|
||||
'max_discount_amount': 100,
|
||||
'max_bargain_rounds': 3,
|
||||
'custom_prompts': ''
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"获取AI回复设置失败: {e}")
|
||||
return {
|
||||
'ai_enabled': False,
|
||||
'model_name': 'qwen-plus',
|
||||
'api_key': '',
|
||||
'base_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
'max_discount_percent': 10,
|
||||
'max_discount_amount': 100,
|
||||
'max_bargain_rounds': 3,
|
||||
'custom_prompts': ''
|
||||
}
|
||||
|
||||
def get_all_ai_reply_settings(self) -> Dict[str, dict]:
|
||||
"""获取所有账号的AI回复设置"""
|
||||
with self.lock:
|
||||
try:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT cookie_id, ai_enabled, model_name, api_key, base_url,
|
||||
max_discount_percent, max_discount_amount, max_bargain_rounds,
|
||||
custom_prompts
|
||||
FROM ai_reply_settings
|
||||
''')
|
||||
|
||||
result = {}
|
||||
for row in cursor.fetchall():
|
||||
cookie_id = row[0]
|
||||
result[cookie_id] = {
|
||||
'ai_enabled': bool(row[1]),
|
||||
'model_name': row[2],
|
||||
'api_key': row[3],
|
||||
'base_url': row[4],
|
||||
'max_discount_percent': row[5],
|
||||
'max_discount_amount': row[6],
|
||||
'max_bargain_rounds': row[7],
|
||||
'custom_prompts': row[8]
|
||||
}
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"获取所有AI回复设置失败: {e}")
|
||||
return {}
|
||||
|
||||
# -------------------- 默认回复操作 --------------------
|
||||
def save_default_reply(self, cookie_id: str, enabled: bool, reply_content: str = None):
|
||||
"""保存默认回复设置"""
|
||||
@ -693,7 +850,8 @@ class DBManager:
|
||||
tables = [
|
||||
'cookies', 'keywords', 'cookie_status', 'cards',
|
||||
'delivery_rules', 'default_replies', 'notification_channels',
|
||||
'message_notifications', 'system_settings', 'item_info'
|
||||
'message_notifications', 'system_settings', 'item_info',
|
||||
'ai_reply_settings', 'ai_conversations', 'ai_item_cache'
|
||||
]
|
||||
|
||||
for table in tables:
|
||||
@ -729,7 +887,8 @@ class DBManager:
|
||||
# 注意:按照外键依赖关系的逆序删除
|
||||
tables = [
|
||||
'message_notifications', 'notification_channels', 'default_replies',
|
||||
'delivery_rules', 'cards', 'item_info', 'cookie_status', 'keywords', 'cookies'
|
||||
'delivery_rules', 'cards', 'item_info', 'cookie_status', 'keywords',
|
||||
'ai_conversations', 'ai_reply_settings', 'ai_item_cache', 'cookies'
|
||||
]
|
||||
|
||||
for table in tables:
|
||||
@ -743,7 +902,8 @@ class DBManager:
|
||||
for table_name, table_data in data.items():
|
||||
if table_name not in ['cookies', 'keywords', 'cookie_status', 'cards',
|
||||
'delivery_rules', 'default_replies', 'notification_channels',
|
||||
'message_notifications', 'system_settings', 'item_info']:
|
||||
'message_notifications', 'system_settings', 'item_info',
|
||||
'ai_reply_settings', 'ai_conversations', 'ai_item_cache']:
|
||||
continue
|
||||
|
||||
columns = table_data['columns']
|
||||
|
298
deploy.bat
298
deploy.bat
@ -1,298 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: 闲鱼自动回复系统 Docker 部署脚本 (Windows版本)
|
||||
:: 作者: Xianyu Auto Reply System
|
||||
:: 版本: 1.0.0
|
||||
|
||||
title 闲鱼自动回复系统 Docker 部署
|
||||
|
||||
:: 颜色定义
|
||||
set "RED=[91m"
|
||||
set "GREEN=[92m"
|
||||
set "YELLOW=[93m"
|
||||
set "BLUE=[94m"
|
||||
set "NC=[0m"
|
||||
|
||||
:: 打印带颜色的消息
|
||||
:print_info
|
||||
echo %BLUE%[INFO]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_success
|
||||
echo %GREEN%[SUCCESS]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_warning
|
||||
echo %YELLOW%[WARNING]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_error
|
||||
echo %RED%[ERROR]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:: 检查Docker是否安装
|
||||
:check_docker
|
||||
call :print_info "检查 Docker 环境..."
|
||||
|
||||
docker --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
call :print_error "Docker 未安装,请先安装 Docker Desktop"
|
||||
echo.
|
||||
echo 下载地址: https://www.docker.com/products/docker-desktop
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
docker-compose --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
call :print_error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "Docker 环境检查通过"
|
||||
goto :eof
|
||||
|
||||
:: 创建必要的目录
|
||||
:create_directories
|
||||
call :print_info "创建必要的目录..."
|
||||
|
||||
if not exist "data" mkdir data
|
||||
if not exist "logs" mkdir logs
|
||||
if not exist "backups" mkdir backups
|
||||
if not exist "nginx" mkdir nginx
|
||||
if not exist "nginx\ssl" mkdir nginx\ssl
|
||||
|
||||
REM 检查目录是否创建成功
|
||||
if not exist "data" (
|
||||
call :print_error "data目录创建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not exist "logs" (
|
||||
call :print_error "logs目录创建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "目录创建完成"
|
||||
goto :eof
|
||||
|
||||
:: 生成默认配置文件
|
||||
:generate_config
|
||||
REM 生成.env文件
|
||||
if not exist ".env" (
|
||||
if exist ".env.example" (
|
||||
call :print_info "从模板生成 .env 文件..."
|
||||
copy ".env.example" ".env" >nul
|
||||
call :print_success ".env 文件已生成"
|
||||
) else (
|
||||
call :print_warning ".env.example 文件不存在,跳过 .env 文件生成"
|
||||
)
|
||||
) else (
|
||||
call :print_info ".env 文件已存在,跳过生成"
|
||||
)
|
||||
|
||||
REM 生成global_config.yml文件
|
||||
if exist "global_config.yml" (
|
||||
call :print_info "配置文件已存在,跳过生成"
|
||||
goto :eof
|
||||
)
|
||||
|
||||
call :print_info "生成默认配置文件..."
|
||||
|
||||
(
|
||||
echo # 闲鱼自动回复系统配置文件
|
||||
echo API_ENDPOINTS:
|
||||
echo login_check: https://passport.goofish.com/newlogin/hasLogin.do
|
||||
echo message_headinfo: https://h5api.m.goofish.com/h5/mtop.idle.trade.pc.message.headinfo/1.0/
|
||||
echo token: https://h5api.m.goofish.com/h5/mtop.taobao.idlemessage.pc.login.token/1.0/
|
||||
echo.
|
||||
echo APP_CONFIG:
|
||||
echo api_version: '1.0'
|
||||
echo app_key: 444e9908a51d1cb236a27862abc769c9
|
||||
echo app_version: '1.0'
|
||||
echo platform: web
|
||||
echo.
|
||||
echo AUTO_REPLY:
|
||||
echo enabled: true
|
||||
echo default_message: '亲爱的"{send_user_name}" 老板你好!所有宝贝都可以拍,秒发货的哈~不满意的话可以直接申请退款哈~'
|
||||
echo max_retry: 3
|
||||
echo retry_interval: 5
|
||||
echo api:
|
||||
echo enabled: false
|
||||
echo host: 0.0.0.0 # 绑定所有网络接口,支持IP访问
|
||||
echo port: 8080 # Web服务端口
|
||||
echo url: http://0.0.0.0:8080/xianyu/reply
|
||||
echo timeout: 10
|
||||
echo.
|
||||
echo COOKIES:
|
||||
echo last_update_time: ''
|
||||
echo value: ''
|
||||
echo.
|
||||
echo DEFAULT_HEADERS:
|
||||
echo accept: application/json
|
||||
echo accept-language: zh-CN,zh;q=0.9
|
||||
echo cache-control: no-cache
|
||||
echo origin: https://www.goofish.com
|
||||
echo pragma: no-cache
|
||||
echo referer: https://www.goofish.com/
|
||||
echo user-agent: Mozilla/5.0 ^(Windows NT 10.0; Win64; x64^) AppleWebKit/537.36 ^(KHTML, like Gecko^) Chrome/119.0.0.0 Safari/537.36
|
||||
echo.
|
||||
echo WEBSOCKET_URL: wss://wss-goofish.dingtalk.com/
|
||||
echo HEARTBEAT_INTERVAL: 15
|
||||
echo HEARTBEAT_TIMEOUT: 5
|
||||
echo TOKEN_REFRESH_INTERVAL: 3600
|
||||
echo TOKEN_RETRY_INTERVAL: 300
|
||||
echo MESSAGE_EXPIRE_TIME: 300000
|
||||
echo.
|
||||
echo LOG_CONFIG:
|
||||
echo level: INFO
|
||||
echo format: '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level} | {name}:{function}:{line} - {message}'
|
||||
echo rotation: '1 day'
|
||||
echo retention: '7 days'
|
||||
) > global_config.yml
|
||||
|
||||
call :print_success "默认配置文件已生成"
|
||||
goto :eof
|
||||
|
||||
:: 构建Docker镜像
|
||||
:build_image
|
||||
call :print_info "构建 Docker 镜像..."
|
||||
|
||||
docker build -t xianyu-auto-reply:latest .
|
||||
if errorlevel 1 (
|
||||
call :print_error "Docker 镜像构建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "Docker 镜像构建完成"
|
||||
goto :eof
|
||||
|
||||
:: 启动服务
|
||||
:start_services
|
||||
call :print_info "启动服务..."
|
||||
|
||||
if "%~1"=="--with-nginx" (
|
||||
call :print_info "启动服务(包含 Nginx)..."
|
||||
docker-compose --profile with-nginx up -d
|
||||
) else (
|
||||
call :print_info "启动服务(不包含 Nginx)..."
|
||||
docker-compose up -d
|
||||
)
|
||||
|
||||
if errorlevel 1 (
|
||||
call :print_error "服务启动失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "服务启动完成"
|
||||
goto :eof
|
||||
|
||||
:: 显示服务状态
|
||||
:show_status
|
||||
call :print_info "服务状态:"
|
||||
docker-compose ps
|
||||
|
||||
echo.
|
||||
call :print_info "服务日志(最近10行):"
|
||||
docker-compose logs --tail=10
|
||||
goto :eof
|
||||
|
||||
:: 显示访问信息
|
||||
:show_access_info
|
||||
call :print_success "部署完成!"
|
||||
echo.
|
||||
call :print_info "访问信息:"
|
||||
echo Web界面: http://localhost:8080
|
||||
echo 默认账号: admin
|
||||
echo 默认密码: admin123
|
||||
echo.
|
||||
call :print_info "常用命令:"
|
||||
echo 查看日志: docker-compose logs -f
|
||||
echo 重启服务: docker-compose restart
|
||||
echo 停止服务: docker-compose down
|
||||
echo 更新服务: deploy.bat update
|
||||
echo.
|
||||
call :print_info "数据目录:"
|
||||
echo 数据库: .\data\xianyu_data.db
|
||||
echo 日志: .\logs\
|
||||
echo 配置: .\global_config.yml
|
||||
echo.
|
||||
goto :eof
|
||||
|
||||
:: 更新服务
|
||||
:update_services
|
||||
call :print_info "更新服务..."
|
||||
|
||||
docker-compose down
|
||||
call :build_image
|
||||
call :start_services %~1
|
||||
|
||||
call :print_success "服务更新完成"
|
||||
goto :eof
|
||||
|
||||
:: 清理资源
|
||||
:cleanup
|
||||
call :print_warning "清理 Docker 资源..."
|
||||
|
||||
docker-compose down --volumes --remove-orphans
|
||||
docker rmi xianyu-auto-reply:latest 2>nul
|
||||
|
||||
call :print_success "清理完成"
|
||||
goto :eof
|
||||
|
||||
:: 显示帮助
|
||||
:show_help
|
||||
echo 使用方法:
|
||||
echo %~nx0 # 首次部署
|
||||
echo %~nx0 with-nginx # 部署并启动 Nginx
|
||||
echo %~nx0 update # 更新服务
|
||||
echo %~nx0 update with-nginx # 更新服务并启动 Nginx
|
||||
echo %~nx0 status # 查看服务状态
|
||||
echo %~nx0 cleanup # 清理所有资源
|
||||
echo %~nx0 help # 显示帮助
|
||||
goto :eof
|
||||
|
||||
:: 主函数
|
||||
:main
|
||||
echo ========================================
|
||||
echo 闲鱼自动回复系统 Docker 部署脚本
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
if "%~1"=="update" (
|
||||
call :print_info "更新模式"
|
||||
call :check_docker
|
||||
call :update_services %~2
|
||||
call :show_status
|
||||
call :show_access_info
|
||||
) else if "%~1"=="cleanup" (
|
||||
call :print_warning "清理模式"
|
||||
call :cleanup
|
||||
) else if "%~1"=="status" (
|
||||
call :show_status
|
||||
) else if "%~1"=="help" (
|
||||
call :show_help
|
||||
) else (
|
||||
call :print_info "首次部署模式"
|
||||
call :check_docker
|
||||
call :create_directories
|
||||
call :generate_config
|
||||
call :build_image
|
||||
call :start_services %~1
|
||||
call :show_status
|
||||
call :show_access_info
|
||||
)
|
||||
|
||||
echo.
|
||||
pause
|
||||
goto :eof
|
||||
|
||||
:: 执行主函数
|
||||
call :main %*
|
@ -36,6 +36,12 @@ services:
|
||||
- AUTO_DELIVERY_TIMEOUT=${AUTO_DELIVERY_TIMEOUT:-30}
|
||||
- API_CARD_TIMEOUT=${API_CARD_TIMEOUT:-10}
|
||||
- BATCH_DATA_LOCK_TIMEOUT=${BATCH_DATA_LOCK_TIMEOUT:-5}
|
||||
# AI回复相关配置
|
||||
- AI_REPLY_ENABLED=${AI_REPLY_ENABLED:-false}
|
||||
- DEFAULT_AI_MODEL=${DEFAULT_AI_MODEL:-qwen-plus}
|
||||
- DEFAULT_AI_BASE_URL=${DEFAULT_AI_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
|
||||
- AI_REQUEST_TIMEOUT=${AI_REQUEST_TIMEOUT:-30}
|
||||
- AI_MAX_TOKENS=${AI_MAX_TOKENS:-100}
|
||||
- WEBSOCKET_URL=${WEBSOCKET_URL:-wss://wss-goofish.dingtalk.com/}
|
||||
- HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-15}
|
||||
- HEARTBEAT_TIMEOUT=${HEARTBEAT_TIMEOUT:-5}
|
||||
|
@ -1,301 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: 闲鱼自动回复系统 Docker 部署脚本 (Windows版本)
|
||||
:: 支持快速部署和管理
|
||||
|
||||
set PROJECT_NAME=xianyu-auto-reply
|
||||
set COMPOSE_FILE=docker-compose.yml
|
||||
set ENV_FILE=.env
|
||||
|
||||
:: 颜色定义 (Windows 10+ 支持ANSI颜色)
|
||||
set "RED=[31m"
|
||||
set "GREEN=[32m"
|
||||
set "YELLOW=[33m"
|
||||
set "BLUE=[34m"
|
||||
set "NC=[0m"
|
||||
|
||||
:: 打印带颜色的消息
|
||||
:print_info
|
||||
echo %BLUE%ℹ️ %~1%NC%
|
||||
goto :eof
|
||||
|
||||
:print_success
|
||||
echo %GREEN%✅ %~1%NC%
|
||||
goto :eof
|
||||
|
||||
:print_warning
|
||||
echo %YELLOW%⚠️ %~1%NC%
|
||||
goto :eof
|
||||
|
||||
:print_error
|
||||
echo %RED%❌ %~1%NC%
|
||||
goto :eof
|
||||
|
||||
:: 检查依赖
|
||||
:check_dependencies
|
||||
call :print_info "检查系统依赖..."
|
||||
|
||||
docker --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
call :print_error "Docker 未安装,请先安装 Docker Desktop"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
docker-compose --version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
call :print_error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "系统依赖检查通过"
|
||||
goto :eof
|
||||
|
||||
:: 初始化配置
|
||||
:init_config
|
||||
call :print_info "初始化配置文件..."
|
||||
|
||||
if not exist "%ENV_FILE%" (
|
||||
if exist ".env.example" (
|
||||
copy ".env.example" "%ENV_FILE%" >nul
|
||||
call :print_success "已创建 %ENV_FILE% 配置文件"
|
||||
) else (
|
||||
call :print_error ".env.example 文件不存在"
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
call :print_warning "%ENV_FILE% 已存在,跳过创建"
|
||||
)
|
||||
|
||||
:: 创建必要的目录
|
||||
if not exist "data" mkdir data
|
||||
if not exist "logs" mkdir logs
|
||||
if not exist "backups" mkdir backups
|
||||
call :print_success "已创建必要的目录"
|
||||
goto :eof
|
||||
|
||||
:: 构建镜像
|
||||
:build_image
|
||||
call :print_info "构建 Docker 镜像..."
|
||||
docker-compose build --no-cache
|
||||
if errorlevel 1 (
|
||||
call :print_error "镜像构建失败"
|
||||
exit /b 1
|
||||
)
|
||||
call :print_success "镜像构建完成"
|
||||
goto :eof
|
||||
|
||||
:: 启动服务
|
||||
:start_services
|
||||
set "profile="
|
||||
if "%~1"=="with-nginx" (
|
||||
set "profile=--profile with-nginx"
|
||||
call :print_info "启动服务(包含 Nginx)..."
|
||||
) else (
|
||||
call :print_info "启动基础服务..."
|
||||
)
|
||||
|
||||
docker-compose %profile% up -d
|
||||
if errorlevel 1 (
|
||||
call :print_error "服务启动失败"
|
||||
exit /b 1
|
||||
)
|
||||
call :print_success "服务启动完成"
|
||||
|
||||
:: 等待服务就绪
|
||||
call :print_info "等待服务就绪..."
|
||||
timeout /t 10 /nobreak >nul
|
||||
|
||||
:: 检查服务状态
|
||||
docker-compose ps | findstr "Up" >nul
|
||||
if errorlevel 1 (
|
||||
call :print_error "服务启动失败"
|
||||
docker-compose logs
|
||||
exit /b 1
|
||||
) else (
|
||||
call :print_success "服务运行正常"
|
||||
call :show_access_info "%~1"
|
||||
)
|
||||
goto :eof
|
||||
|
||||
:: 停止服务
|
||||
:stop_services
|
||||
call :print_info "停止服务..."
|
||||
docker-compose down
|
||||
call :print_success "服务已停止"
|
||||
goto :eof
|
||||
|
||||
:: 重启服务
|
||||
:restart_services
|
||||
call :print_info "重启服务..."
|
||||
docker-compose restart
|
||||
call :print_success "服务已重启"
|
||||
goto :eof
|
||||
|
||||
:: 查看日志
|
||||
:show_logs
|
||||
if "%~1"=="" (
|
||||
docker-compose logs -f
|
||||
) else (
|
||||
docker-compose logs -f "%~1"
|
||||
)
|
||||
goto :eof
|
||||
|
||||
:: 查看状态
|
||||
:show_status
|
||||
call :print_info "服务状态:"
|
||||
docker-compose ps
|
||||
|
||||
call :print_info "资源使用:"
|
||||
for /f "tokens=*" %%i in ('docker-compose ps -q') do (
|
||||
docker stats --no-stream %%i
|
||||
)
|
||||
goto :eof
|
||||
|
||||
:: 显示访问信息
|
||||
:show_access_info
|
||||
echo.
|
||||
call :print_success "🎉 部署完成!"
|
||||
echo.
|
||||
|
||||
if "%~1"=="with-nginx" (
|
||||
echo 📱 访问地址:
|
||||
echo HTTP: http://localhost
|
||||
echo HTTPS: https://localhost ^(如果配置了SSL^)
|
||||
) else (
|
||||
echo 📱 访问地址:
|
||||
echo HTTP: http://localhost:8080
|
||||
)
|
||||
|
||||
echo.
|
||||
echo 🔐 默认登录信息:
|
||||
echo 用户名: admin
|
||||
echo 密码: admin123
|
||||
echo.
|
||||
echo 📊 管理命令:
|
||||
echo 查看状态: %~nx0 status
|
||||
echo 查看日志: %~nx0 logs
|
||||
echo 重启服务: %~nx0 restart
|
||||
echo 停止服务: %~nx0 stop
|
||||
echo.
|
||||
goto :eof
|
||||
|
||||
:: 健康检查
|
||||
:health_check
|
||||
call :print_info "执行健康检查..."
|
||||
|
||||
set "url=http://localhost:8080/health"
|
||||
set "max_attempts=30"
|
||||
set "attempt=1"
|
||||
|
||||
:health_loop
|
||||
curl -f -s "%url%" >nul 2>&1
|
||||
if not errorlevel 1 (
|
||||
call :print_success "健康检查通过"
|
||||
goto :eof
|
||||
)
|
||||
|
||||
call :print_info "等待服务就绪... (!attempt!/%max_attempts%)"
|
||||
timeout /t 2 /nobreak >nul
|
||||
set /a attempt+=1
|
||||
|
||||
if !attempt! leq %max_attempts% goto health_loop
|
||||
|
||||
call :print_error "健康检查失败"
|
||||
exit /b 1
|
||||
|
||||
:: 备份数据
|
||||
:backup_data
|
||||
call :print_info "备份数据..."
|
||||
|
||||
for /f "tokens=2 delims==" %%i in ('wmic OS Get localdatetime /value') do set datetime=%%i
|
||||
set backup_dir=backups\%datetime:~0,8%_%datetime:~8,6%
|
||||
mkdir "%backup_dir%" 2>nul
|
||||
|
||||
:: 备份数据库
|
||||
if exist "data\xianyu_data.db" (
|
||||
copy "data\xianyu_data.db" "%backup_dir%\" >nul
|
||||
call :print_success "数据库备份完成"
|
||||
)
|
||||
|
||||
:: 备份配置
|
||||
copy "%ENV_FILE%" "%backup_dir%\" >nul
|
||||
copy "global_config.yml" "%backup_dir%\" >nul 2>&1
|
||||
|
||||
call :print_success "数据备份完成: %backup_dir%"
|
||||
goto :eof
|
||||
|
||||
:: 显示帮助信息
|
||||
:show_help
|
||||
echo 闲鱼自动回复系统 Docker 部署脚本 ^(Windows版本^)
|
||||
echo.
|
||||
echo 用法: %~nx0 [命令] [选项]
|
||||
echo.
|
||||
echo 命令:
|
||||
echo init 初始化配置文件
|
||||
echo build 构建 Docker 镜像
|
||||
echo start [with-nginx] 启动服务^(可选包含 Nginx^)
|
||||
echo stop 停止服务
|
||||
echo restart 重启服务
|
||||
echo status 查看服务状态
|
||||
echo logs [service] 查看日志
|
||||
echo health 健康检查
|
||||
echo backup 备份数据
|
||||
echo help 显示帮助信息
|
||||
echo.
|
||||
echo 示例:
|
||||
echo %~nx0 init # 初始化配置
|
||||
echo %~nx0 start # 启动基础服务
|
||||
echo %~nx0 start with-nginx # 启动包含 Nginx 的服务
|
||||
echo %~nx0 logs xianyu-app # 查看应用日志
|
||||
echo.
|
||||
goto :eof
|
||||
|
||||
:: 主函数
|
||||
:main
|
||||
if "%~1"=="init" (
|
||||
call :check_dependencies
|
||||
call :init_config
|
||||
) else if "%~1"=="build" (
|
||||
call :check_dependencies
|
||||
call :build_image
|
||||
) else if "%~1"=="start" (
|
||||
call :check_dependencies
|
||||
call :init_config
|
||||
call :build_image
|
||||
call :start_services "%~2"
|
||||
) else if "%~1"=="stop" (
|
||||
call :stop_services
|
||||
) else if "%~1"=="restart" (
|
||||
call :restart_services
|
||||
) else if "%~1"=="status" (
|
||||
call :show_status
|
||||
) else if "%~1"=="logs" (
|
||||
call :show_logs "%~2"
|
||||
) else if "%~1"=="health" (
|
||||
call :health_check
|
||||
) else if "%~1"=="backup" (
|
||||
call :backup_data
|
||||
) else if "%~1"=="help" (
|
||||
call :show_help
|
||||
) else if "%~1"=="-h" (
|
||||
call :show_help
|
||||
) else if "%~1"=="--help" (
|
||||
call :show_help
|
||||
) else if "%~1"=="" (
|
||||
call :print_info "快速部署模式"
|
||||
call :check_dependencies
|
||||
call :init_config
|
||||
call :build_image
|
||||
call :start_services
|
||||
) else (
|
||||
call :print_error "未知命令: %~1"
|
||||
call :show_help
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
goto :eof
|
||||
|
||||
:: 执行主函数
|
||||
call :main %*
|
350
docker-deploy.sh
350
docker-deploy.sh
@ -1,350 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 闲鱼自动回复系统 Docker 部署脚本
|
||||
# 支持快速部署和管理
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 项目配置
|
||||
PROJECT_NAME="xianyu-auto-reply"
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
ENV_FILE=".env"
|
||||
|
||||
# 打印带颜色的消息
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}❌ $1${NC}"
|
||||
}
|
||||
|
||||
# 检查依赖
|
||||
check_dependencies() {
|
||||
print_info "检查系统依赖..."
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_error "Docker 未安装,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
print_error "Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "系统依赖检查通过"
|
||||
}
|
||||
|
||||
# 初始化配置
|
||||
init_config() {
|
||||
print_info "初始化配置文件..."
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example "$ENV_FILE"
|
||||
print_success "已创建 $ENV_FILE 配置文件"
|
||||
else
|
||||
print_error ".env.example 文件不存在"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_warning "$ENV_FILE 已存在,跳过创建"
|
||||
fi
|
||||
|
||||
# 创建必要的目录
|
||||
mkdir -p data logs backups
|
||||
print_success "已创建必要的目录"
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_image() {
|
||||
print_info "构建 Docker 镜像..."
|
||||
docker-compose build --no-cache
|
||||
print_success "镜像构建完成"
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
local profile=""
|
||||
if [ "$1" = "with-nginx" ]; then
|
||||
profile="--profile with-nginx"
|
||||
print_info "启动服务(包含 Nginx)..."
|
||||
else
|
||||
print_info "启动基础服务..."
|
||||
fi
|
||||
|
||||
docker-compose $profile up -d
|
||||
print_success "服务启动完成"
|
||||
|
||||
# 等待服务就绪
|
||||
print_info "等待服务就绪..."
|
||||
sleep 10
|
||||
|
||||
# 检查服务状态
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
print_success "服务运行正常"
|
||||
show_access_info "$1"
|
||||
else
|
||||
print_error "服务启动失败"
|
||||
docker-compose logs
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
stop_services() {
|
||||
print_info "停止服务..."
|
||||
docker-compose down
|
||||
print_success "服务已停止"
|
||||
}
|
||||
|
||||
# 重启服务
|
||||
restart_services() {
|
||||
print_info "重启服务..."
|
||||
docker-compose restart
|
||||
print_success "服务已重启"
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
show_logs() {
|
||||
local service="$1"
|
||||
if [ -z "$service" ]; then
|
||||
docker-compose logs -f
|
||||
else
|
||||
docker-compose logs -f "$service"
|
||||
fi
|
||||
}
|
||||
|
||||
# 查看状态
|
||||
show_status() {
|
||||
print_info "服务状态:"
|
||||
docker-compose ps
|
||||
|
||||
print_info "资源使用:"
|
||||
docker stats --no-stream $(docker-compose ps -q)
|
||||
}
|
||||
|
||||
# 显示访问信息
|
||||
show_access_info() {
|
||||
local with_nginx="$1"
|
||||
|
||||
echo ""
|
||||
print_success "🎉 部署完成!"
|
||||
echo ""
|
||||
|
||||
if [ "$with_nginx" = "with-nginx" ]; then
|
||||
echo "📱 访问地址:"
|
||||
echo " HTTP: http://localhost"
|
||||
echo " HTTPS: https://localhost (如果配置了SSL)"
|
||||
else
|
||||
echo "📱 访问地址:"
|
||||
echo " HTTP: http://localhost:8080"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔐 默认登录信息:"
|
||||
echo " 用户名: admin"
|
||||
echo " 密码: admin123"
|
||||
echo ""
|
||||
echo "📊 管理命令:"
|
||||
echo " 查看状态: $0 status"
|
||||
echo " 查看日志: $0 logs"
|
||||
echo " 重启服务: $0 restart"
|
||||
echo " 停止服务: $0 stop"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
health_check() {
|
||||
print_info "执行健康检查..."
|
||||
|
||||
local url="http://localhost:8080/health"
|
||||
local max_attempts=30
|
||||
local attempt=1
|
||||
|
||||
while [ $attempt -le $max_attempts ]; do
|
||||
if curl -f -s "$url" > /dev/null 2>&1; then
|
||||
print_success "健康检查通过"
|
||||
return 0
|
||||
fi
|
||||
|
||||
print_info "等待服务就绪... ($attempt/$max_attempts)"
|
||||
sleep 2
|
||||
((attempt++))
|
||||
done
|
||||
|
||||
print_error "健康检查失败"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 备份数据
|
||||
backup_data() {
|
||||
print_info "备份数据..."
|
||||
|
||||
local backup_dir="backups/$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# 备份数据库
|
||||
if [ -f "data/xianyu_data.db" ]; then
|
||||
cp data/xianyu_data.db "$backup_dir/"
|
||||
print_success "数据库备份完成"
|
||||
fi
|
||||
|
||||
# 备份配置
|
||||
cp "$ENV_FILE" "$backup_dir/"
|
||||
cp global_config.yml "$backup_dir/" 2>/dev/null || true
|
||||
|
||||
print_success "数据备份完成: $backup_dir"
|
||||
}
|
||||
|
||||
# 更新部署
|
||||
update_deployment() {
|
||||
print_info "更新部署..."
|
||||
|
||||
# 备份数据
|
||||
backup_data
|
||||
|
||||
# 停止服务
|
||||
stop_services
|
||||
|
||||
# 拉取最新代码(如果是git仓库)
|
||||
if [ -d ".git" ]; then
|
||||
print_info "拉取最新代码..."
|
||||
git pull
|
||||
fi
|
||||
|
||||
# 重新构建
|
||||
build_image
|
||||
|
||||
# 启动服务
|
||||
start_services
|
||||
|
||||
print_success "更新完成"
|
||||
}
|
||||
|
||||
# 清理环境
|
||||
cleanup() {
|
||||
print_warning "这将删除所有容器、镜像和数据,确定要继续吗?(y/N)"
|
||||
read -r response
|
||||
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
print_info "清理环境..."
|
||||
|
||||
# 停止并删除容器
|
||||
docker-compose down -v --rmi all
|
||||
|
||||
# 删除数据目录
|
||||
rm -rf data logs backups
|
||||
|
||||
print_success "环境清理完成"
|
||||
else
|
||||
print_info "取消清理操作"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "闲鱼自动回复系统 Docker 部署脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " init 初始化配置文件"
|
||||
echo " build 构建 Docker 镜像"
|
||||
echo " start [with-nginx] 启动服务(可选包含 Nginx)"
|
||||
echo " stop 停止服务"
|
||||
echo " restart 重启服务"
|
||||
echo " status 查看服务状态"
|
||||
echo " logs [service] 查看日志"
|
||||
echo " health 健康检查"
|
||||
echo " backup 备份数据"
|
||||
echo " update 更新部署"
|
||||
echo " cleanup 清理环境"
|
||||
echo " help 显示帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 init # 初始化配置"
|
||||
echo " $0 start # 启动基础服务"
|
||||
echo " $0 start with-nginx # 启动包含 Nginx 的服务"
|
||||
echo " $0 logs xianyu-app # 查看应用日志"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "$1" in
|
||||
"init")
|
||||
check_dependencies
|
||||
init_config
|
||||
;;
|
||||
"build")
|
||||
check_dependencies
|
||||
build_image
|
||||
;;
|
||||
"start")
|
||||
check_dependencies
|
||||
init_config
|
||||
build_image
|
||||
start_services "$2"
|
||||
;;
|
||||
"stop")
|
||||
stop_services
|
||||
;;
|
||||
"restart")
|
||||
restart_services
|
||||
;;
|
||||
"status")
|
||||
show_status
|
||||
;;
|
||||
"logs")
|
||||
show_logs "$2"
|
||||
;;
|
||||
"health")
|
||||
health_check
|
||||
;;
|
||||
"backup")
|
||||
backup_data
|
||||
;;
|
||||
"update")
|
||||
check_dependencies
|
||||
update_deployment
|
||||
;;
|
||||
"cleanup")
|
||||
cleanup
|
||||
;;
|
||||
"help"|"--help"|"-h")
|
||||
show_help
|
||||
;;
|
||||
"")
|
||||
print_info "快速部署模式"
|
||||
check_dependencies
|
||||
init_config
|
||||
build_image
|
||||
start_services
|
||||
;;
|
||||
*)
|
||||
print_error "未知命令: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
@ -1,156 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: 修复数据库权限问题的脚本 (Windows版本)
|
||||
:: 解决Docker容器中数据库无法创建的问题
|
||||
|
||||
title 数据库权限修复脚本
|
||||
|
||||
:: 颜色定义
|
||||
set "RED=[91m"
|
||||
set "GREEN=[92m"
|
||||
set "YELLOW=[93m"
|
||||
set "BLUE=[94m"
|
||||
set "NC=[0m"
|
||||
|
||||
:: 打印带颜色的消息
|
||||
:print_info
|
||||
echo %BLUE%[INFO]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_success
|
||||
echo %GREEN%[SUCCESS]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_warning
|
||||
echo %YELLOW%[WARNING]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_error
|
||||
echo %RED%[ERROR]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
echo ========================================
|
||||
echo 数据库权限修复脚本
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
:: 1. 停止现有容器
|
||||
call :print_info "停止现有容器..."
|
||||
docker-compose down >nul 2>&1
|
||||
|
||||
:: 2. 检查并创建目录
|
||||
call :print_info "检查并创建必要目录..."
|
||||
|
||||
for %%d in (data logs backups) do (
|
||||
if not exist "%%d" (
|
||||
call :print_info "创建目录: %%d"
|
||||
mkdir "%%d"
|
||||
)
|
||||
|
||||
if not exist "%%d" (
|
||||
call :print_error "目录 %%d 创建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :print_success "目录 %%d 权限正常"
|
||||
)
|
||||
|
||||
:: 3. 检查现有数据库文件
|
||||
if exist "data\xianyu_data.db" (
|
||||
call :print_info "检查现有数据库文件..."
|
||||
call :print_success "数据库文件存在"
|
||||
) else (
|
||||
call :print_info "数据库文件不存在,将在启动时创建"
|
||||
)
|
||||
|
||||
:: 4. 测试数据库创建
|
||||
call :print_info "测试数据库创建..."
|
||||
python -c "
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
db_path = 'data/test_db.sqlite'
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute('CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY)')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print('✅ 数据库创建测试成功')
|
||||
if os.path.exists(db_path):
|
||||
os.remove(db_path)
|
||||
except Exception as e:
|
||||
print(f'❌ 数据库创建测试失败: {e}')
|
||||
exit(1)
|
||||
"
|
||||
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_error "数据库创建测试失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 5. 重新构建并启动
|
||||
call :print_info "重新构建并启动服务..."
|
||||
docker-compose build --no-cache
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_error "Docker镜像构建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
docker-compose up -d
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_error "服务启动失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 6. 等待服务启动
|
||||
call :print_info "等待服务启动..."
|
||||
timeout /t 15 /nobreak >nul
|
||||
|
||||
:: 7. 检查服务状态
|
||||
call :print_info "检查服务状态..."
|
||||
docker-compose ps | findstr "Up" >nul
|
||||
if !errorlevel! equ 0 (
|
||||
call :print_success "服务启动成功"
|
||||
|
||||
:: 检查日志
|
||||
call :print_info "检查启动日志..."
|
||||
docker-compose logs --tail=20 xianyu-app
|
||||
|
||||
:: 测试健康检查
|
||||
call :print_info "测试健康检查..."
|
||||
timeout /t 5 /nobreak >nul
|
||||
curl -f http://localhost:8080/health >nul 2>&1
|
||||
if !errorlevel! equ 0 (
|
||||
call :print_success "健康检查通过"
|
||||
) else (
|
||||
call :print_warning "健康检查失败,但服务可能仍在启动中"
|
||||
)
|
||||
) else (
|
||||
call :print_error "服务启动失败"
|
||||
call :print_info "查看错误日志:"
|
||||
docker-compose logs xianyu-app
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
call :print_success "数据库权限修复完成!"
|
||||
echo.
|
||||
call :print_info "服务信息:"
|
||||
echo Web界面: http://localhost:8080
|
||||
echo 健康检查: http://localhost:8080/health
|
||||
echo 默认账号: admin / admin123
|
||||
echo.
|
||||
call :print_info "常用命令:"
|
||||
echo 查看日志: docker-compose logs -f
|
||||
echo 重启服务: docker-compose restart
|
||||
echo 停止服务: docker-compose down
|
||||
echo.
|
||||
|
||||
pause
|
@ -1,167 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 修复数据库权限问题的脚本
|
||||
# 解决Docker容器中数据库无法创建的问题
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "========================================"
|
||||
echo " 数据库权限修复脚本"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 1. 停止现有容器
|
||||
print_info "停止现有容器..."
|
||||
docker-compose down 2>/dev/null || true
|
||||
|
||||
# 2. 检查并创建目录
|
||||
print_info "检查并创建必要目录..."
|
||||
for dir in data logs backups; do
|
||||
if [ ! -d "$dir" ]; then
|
||||
print_info "创建目录: $dir"
|
||||
mkdir -p "$dir"
|
||||
fi
|
||||
|
||||
# 设置权限
|
||||
chmod 755 "$dir"
|
||||
|
||||
# 检查权限
|
||||
if [ ! -w "$dir" ]; then
|
||||
print_error "目录 $dir 没有写权限"
|
||||
|
||||
# 尝试修复权限
|
||||
print_info "尝试修复权限..."
|
||||
sudo chmod 755 "$dir" 2>/dev/null || {
|
||||
print_error "无法修复权限,请手动执行: sudo chmod 755 $dir"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
print_success "目录 $dir 权限正常"
|
||||
done
|
||||
|
||||
# 3. 检查现有数据库文件
|
||||
if [ -f "data/xianyu_data.db" ]; then
|
||||
print_info "检查现有数据库文件权限..."
|
||||
if [ ! -w "data/xianyu_data.db" ]; then
|
||||
print_warning "数据库文件没有写权限,尝试修复..."
|
||||
chmod 644 "data/xianyu_data.db"
|
||||
print_success "数据库文件权限已修复"
|
||||
else
|
||||
print_success "数据库文件权限正常"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4. 检查Docker用户映射
|
||||
print_info "检查Docker用户映射..."
|
||||
CURRENT_UID=$(id -u)
|
||||
CURRENT_GID=$(id -g)
|
||||
|
||||
print_info "当前用户 UID:GID = $CURRENT_UID:$CURRENT_GID"
|
||||
|
||||
# 5. 创建测试数据库
|
||||
print_info "测试数据库创建..."
|
||||
python3 -c "
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
db_path = 'data/test_db.sqlite'
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.execute('CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY)')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print('✅ 数据库创建测试成功')
|
||||
os.remove(db_path)
|
||||
except Exception as e:
|
||||
print(f'❌ 数据库创建测试失败: {e}')
|
||||
exit(1)
|
||||
" || {
|
||||
print_error "数据库创建测试失败"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 6. 更新docker-compose.yml用户映射
|
||||
print_info "检查docker-compose.yml用户映射..."
|
||||
if ! grep -q "user:" docker-compose.yml; then
|
||||
print_info "添加用户映射到docker-compose.yml..."
|
||||
|
||||
# 备份原文件
|
||||
cp docker-compose.yml docker-compose.yml.backup
|
||||
|
||||
# 在xianyu-app服务中添加user配置
|
||||
sed -i '/container_name: xianyu-auto-reply/a\ user: "'$CURRENT_UID':'$CURRENT_GID'"' docker-compose.yml
|
||||
|
||||
print_success "用户映射已添加"
|
||||
else
|
||||
print_info "用户映射已存在"
|
||||
fi
|
||||
|
||||
# 7. 重新构建并启动
|
||||
print_info "重新构建并启动服务..."
|
||||
docker-compose build --no-cache
|
||||
docker-compose up -d
|
||||
|
||||
# 8. 等待服务启动
|
||||
print_info "等待服务启动..."
|
||||
sleep 10
|
||||
|
||||
# 9. 检查服务状态
|
||||
print_info "检查服务状态..."
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
print_success "服务启动成功"
|
||||
|
||||
# 检查日志
|
||||
print_info "检查启动日志..."
|
||||
docker-compose logs --tail=20 xianyu-app
|
||||
|
||||
# 测试健康检查
|
||||
print_info "测试健康检查..."
|
||||
sleep 5
|
||||
if curl -f http://localhost:8080/health >/dev/null 2>&1; then
|
||||
print_success "健康检查通过"
|
||||
else
|
||||
print_warning "健康检查失败,但服务可能仍在启动中"
|
||||
fi
|
||||
else
|
||||
print_error "服务启动失败"
|
||||
print_info "查看错误日志:"
|
||||
docker-compose logs xianyu-app
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "数据库权限修复完成!"
|
||||
echo ""
|
||||
print_info "服务信息:"
|
||||
echo " Web界面: http://localhost:8080"
|
||||
echo " 健康检查: http://localhost:8080/health"
|
||||
echo " 默认账号: admin / admin123"
|
||||
echo ""
|
||||
print_info "常用命令:"
|
||||
echo " 查看日志: docker-compose logs -f"
|
||||
echo " 重启服务: docker-compose restart"
|
||||
echo " 停止服务: docker-compose down"
|
@ -1,144 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: 修复Docker部署警告的快速脚本 (Windows版本)
|
||||
:: 解决version过时和.env文件缺失问题
|
||||
|
||||
title Docker部署警告修复脚本
|
||||
|
||||
:: 颜色定义
|
||||
set "RED=[91m"
|
||||
set "GREEN=[92m"
|
||||
set "YELLOW=[93m"
|
||||
set "BLUE=[94m"
|
||||
set "NC=[0m"
|
||||
|
||||
:: 打印带颜色的消息
|
||||
:print_info
|
||||
echo %BLUE%[INFO]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_success
|
||||
echo %GREEN%[SUCCESS]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_warning
|
||||
echo %YELLOW%[WARNING]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_error
|
||||
echo %RED%[ERROR]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
echo ========================================
|
||||
echo Docker部署警告修复脚本
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
:: 1. 检查并创建.env文件
|
||||
call :print_info "检查 .env 文件..."
|
||||
if not exist ".env" (
|
||||
if exist ".env.example" (
|
||||
call :print_info "从 .env.example 创建 .env 文件..."
|
||||
copy ".env.example" ".env" >nul
|
||||
call :print_success ".env 文件已创建"
|
||||
) else (
|
||||
call :print_warning ".env.example 文件不存在"
|
||||
call :print_info "创建基本的 .env 文件..."
|
||||
|
||||
(
|
||||
echo # 闲鱼自动回复系统 Docker 环境变量配置文件
|
||||
echo.
|
||||
echo # 基础配置
|
||||
echo TZ=Asia/Shanghai
|
||||
echo PYTHONUNBUFFERED=1
|
||||
echo LOG_LEVEL=INFO
|
||||
echo.
|
||||
echo # 数据库配置
|
||||
echo DB_PATH=/app/data/xianyu_data.db
|
||||
echo.
|
||||
echo # 服务配置
|
||||
echo WEB_PORT=8080
|
||||
echo.
|
||||
echo # 安全配置
|
||||
echo ADMIN_USERNAME=admin
|
||||
echo ADMIN_PASSWORD=admin123
|
||||
echo JWT_SECRET_KEY=xianyu-auto-reply-secret-key-2024
|
||||
echo.
|
||||
echo # 资源限制
|
||||
echo MEMORY_LIMIT=512
|
||||
echo CPU_LIMIT=0.5
|
||||
echo MEMORY_RESERVATION=256
|
||||
echo CPU_RESERVATION=0.25
|
||||
echo.
|
||||
echo # 自动回复配置
|
||||
echo AUTO_REPLY_ENABLED=true
|
||||
echo WEBSOCKET_URL=wss://wss-goofish.dingtalk.com/
|
||||
echo HEARTBEAT_INTERVAL=15
|
||||
echo TOKEN_REFRESH_INTERVAL=3600
|
||||
) > .env
|
||||
|
||||
call :print_success "基本 .env 文件已创建"
|
||||
)
|
||||
) else (
|
||||
call :print_success ".env 文件已存在"
|
||||
)
|
||||
|
||||
:: 2. 检查docker-compose.yml版本问题
|
||||
call :print_info "检查 docker-compose.yml 配置..."
|
||||
findstr /B "version:" docker-compose.yml >nul 2>&1
|
||||
if !errorlevel! equ 0 (
|
||||
call :print_warning "发现过时的 version 字段"
|
||||
call :print_info "移除 version 字段..."
|
||||
|
||||
REM 备份原文件
|
||||
copy docker-compose.yml docker-compose.yml.backup >nul
|
||||
|
||||
REM 创建临时文件,移除version行
|
||||
(
|
||||
for /f "tokens=*" %%a in (docker-compose.yml) do (
|
||||
echo %%a | findstr /B "version:" >nul
|
||||
if !errorlevel! neq 0 (
|
||||
echo %%a
|
||||
)
|
||||
)
|
||||
) > docker-compose.yml.tmp
|
||||
|
||||
REM 替换原文件
|
||||
move docker-compose.yml.tmp docker-compose.yml >nul
|
||||
|
||||
call :print_success "已移除过时的 version 字段"
|
||||
call :print_info "原文件已备份为 docker-compose.yml.backup"
|
||||
) else (
|
||||
call :print_success "docker-compose.yml 配置正确"
|
||||
)
|
||||
|
||||
:: 3. 验证修复结果
|
||||
call :print_info "验证修复结果..."
|
||||
|
||||
echo.
|
||||
call :print_info "测试 Docker Compose 配置..."
|
||||
docker-compose config >nul 2>&1
|
||||
if !errorlevel! equ 0 (
|
||||
call :print_success "Docker Compose 配置验证通过"
|
||||
) else (
|
||||
call :print_error "Docker Compose 配置验证失败"
|
||||
echo 请检查 docker-compose.yml 文件
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
call :print_success "所有警告已修复!"
|
||||
echo.
|
||||
call :print_info "现在可以正常使用以下命令:"
|
||||
echo docker-compose up -d # 启动服务
|
||||
echo docker-compose ps # 查看状态
|
||||
echo docker-compose logs -f # 查看日志
|
||||
echo.
|
||||
call :print_info "如果需要恢复原配置:"
|
||||
echo move docker-compose.yml.backup docker-compose.yml
|
||||
echo.
|
||||
|
||||
pause
|
@ -1,145 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 修复Docker部署警告的快速脚本
|
||||
# 解决version过时和.env文件缺失问题
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "========================================"
|
||||
echo " Docker部署警告修复脚本"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 1. 检查并创建.env文件
|
||||
print_info "检查 .env 文件..."
|
||||
if [ ! -f ".env" ]; then
|
||||
if [ -f ".env.example" ]; then
|
||||
print_info "从 .env.example 创建 .env 文件..."
|
||||
cp .env.example .env
|
||||
print_success ".env 文件已创建"
|
||||
else
|
||||
print_warning ".env.example 文件不存在"
|
||||
print_info "创建基本的 .env 文件..."
|
||||
cat > .env << 'EOF'
|
||||
# 闲鱼自动回复系统 Docker 环境变量配置文件
|
||||
|
||||
# 基础配置
|
||||
TZ=Asia/Shanghai
|
||||
PYTHONUNBUFFERED=1
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# 数据库配置
|
||||
DB_PATH=/app/data/xianyu_data.db
|
||||
|
||||
# 服务配置
|
||||
WEB_PORT=8080
|
||||
|
||||
# 安全配置
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=admin123
|
||||
JWT_SECRET_KEY=xianyu-auto-reply-secret-key-2024
|
||||
|
||||
# 资源限制
|
||||
MEMORY_LIMIT=512
|
||||
CPU_LIMIT=0.5
|
||||
MEMORY_RESERVATION=256
|
||||
CPU_RESERVATION=0.25
|
||||
|
||||
# 自动回复配置
|
||||
AUTO_REPLY_ENABLED=true
|
||||
WEBSOCKET_URL=wss://wss-goofish.dingtalk.com/
|
||||
HEARTBEAT_INTERVAL=15
|
||||
TOKEN_REFRESH_INTERVAL=3600
|
||||
EOF
|
||||
print_success "基本 .env 文件已创建"
|
||||
fi
|
||||
else
|
||||
print_success ".env 文件已存在"
|
||||
fi
|
||||
|
||||
# 2. 检查docker-compose.yml版本问题
|
||||
print_info "检查 docker-compose.yml 配置..."
|
||||
if grep -q "^version:" docker-compose.yml 2>/dev/null; then
|
||||
print_warning "发现过时的 version 字段"
|
||||
print_info "移除 version 字段..."
|
||||
|
||||
# 备份原文件
|
||||
cp docker-compose.yml docker-compose.yml.backup
|
||||
|
||||
# 移除version行
|
||||
sed -i '/^version:/d' docker-compose.yml
|
||||
sed -i '/^$/N;/^\n$/d' docker-compose.yml # 移除空行
|
||||
|
||||
print_success "已移除过时的 version 字段"
|
||||
print_info "原文件已备份为 docker-compose.yml.backup"
|
||||
else
|
||||
print_success "docker-compose.yml 配置正确"
|
||||
fi
|
||||
|
||||
# 3. 检查env_file配置
|
||||
print_info "检查 env_file 配置..."
|
||||
if grep -A1 "env_file:" docker-compose.yml | grep -q "required: false"; then
|
||||
print_success "env_file 配置正确"
|
||||
else
|
||||
print_info "更新 env_file 配置为可选..."
|
||||
|
||||
# 备份文件(如果还没备份)
|
||||
if [ ! -f "docker-compose.yml.backup" ]; then
|
||||
cp docker-compose.yml docker-compose.yml.backup
|
||||
fi
|
||||
|
||||
# 更新env_file配置
|
||||
sed -i '/env_file:/,+1c\
|
||||
env_file:\
|
||||
- path: .env\
|
||||
required: false' docker-compose.yml
|
||||
|
||||
print_success "env_file 配置已更新"
|
||||
fi
|
||||
|
||||
# 4. 验证修复结果
|
||||
print_info "验证修复结果..."
|
||||
|
||||
echo ""
|
||||
print_info "测试 Docker Compose 配置..."
|
||||
if docker-compose config >/dev/null 2>&1; then
|
||||
print_success "Docker Compose 配置验证通过"
|
||||
else
|
||||
print_error "Docker Compose 配置验证失败"
|
||||
echo "请检查 docker-compose.yml 文件"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "所有警告已修复!"
|
||||
echo ""
|
||||
print_info "现在可以正常使用以下命令:"
|
||||
echo " docker-compose up -d # 启动服务"
|
||||
echo " docker-compose ps # 查看状态"
|
||||
echo " docker-compose logs -f # 查看日志"
|
||||
echo ""
|
||||
print_info "如果需要恢复原配置:"
|
||||
echo " mv docker-compose.yml.backup docker-compose.yml"
|
@ -1,121 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 快速修复WebSocket兼容性问题
|
||||
# 解决 "extra_headers" 参数不支持的问题
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "🔧 WebSocket兼容性问题修复"
|
||||
echo "================================"
|
||||
|
||||
# 1. 检查当前websockets版本
|
||||
print_info "检查当前websockets版本..."
|
||||
if command -v python3 &> /dev/null; then
|
||||
PYTHON_CMD="python3"
|
||||
elif command -v python &> /dev/null; then
|
||||
PYTHON_CMD="python"
|
||||
else
|
||||
print_error "未找到Python解释器"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_VERSION=$($PYTHON_CMD -c "import websockets; print(websockets.__version__)" 2>/dev/null || echo "未安装")
|
||||
print_info "当前websockets版本: $CURRENT_VERSION"
|
||||
|
||||
# 2. 测试WebSocket兼容性
|
||||
print_info "测试WebSocket兼容性..."
|
||||
$PYTHON_CMD test-websocket-compatibility.py
|
||||
|
||||
# 3. 停止现有服务
|
||||
print_info "停止现有Docker服务..."
|
||||
docker-compose down 2>/dev/null || true
|
||||
|
||||
# 4. 更新websockets版本
|
||||
print_info "更新websockets版本到兼容版本..."
|
||||
if [ -f "requirements.txt" ]; then
|
||||
# 备份原文件
|
||||
cp requirements.txt requirements.txt.backup
|
||||
|
||||
# 更新websockets版本
|
||||
sed -i 's/websockets>=.*/websockets>=10.0,<13.0 # 兼容性版本范围/' requirements.txt
|
||||
|
||||
print_success "requirements.txt已更新"
|
||||
else
|
||||
print_warning "requirements.txt文件不存在"
|
||||
fi
|
||||
|
||||
# 5. 重新构建Docker镜像
|
||||
print_info "重新构建Docker镜像..."
|
||||
docker-compose build --no-cache
|
||||
|
||||
# 6. 启动服务
|
||||
print_info "启动服务..."
|
||||
docker-compose up -d
|
||||
|
||||
# 7. 等待服务启动
|
||||
print_info "等待服务启动..."
|
||||
sleep 15
|
||||
|
||||
# 8. 检查服务状态
|
||||
print_info "检查服务状态..."
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
print_success "✅ 服务启动成功!"
|
||||
|
||||
# 检查WebSocket错误
|
||||
print_info "检查WebSocket连接状态..."
|
||||
sleep 5
|
||||
|
||||
# 查看最近的日志
|
||||
echo ""
|
||||
print_info "最近的服务日志:"
|
||||
docker-compose logs --tail=20 xianyu-app | grep -E "(WebSocket|extra_headers|ERROR)" || echo "未发现WebSocket相关错误"
|
||||
|
||||
# 测试健康检查
|
||||
if curl -f http://localhost:8080/health >/dev/null 2>&1; then
|
||||
print_success "健康检查通过"
|
||||
else
|
||||
print_warning "健康检查失败,服务可能仍在启动中"
|
||||
fi
|
||||
|
||||
else
|
||||
print_error "❌ 服务启动失败"
|
||||
print_info "查看错误日志:"
|
||||
docker-compose logs --tail=30 xianyu-app
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "🎉 WebSocket兼容性问题修复完成!"
|
||||
echo ""
|
||||
print_info "服务信息:"
|
||||
echo " Web界面: http://localhost:8080"
|
||||
echo " 健康检查: http://localhost:8080/health"
|
||||
echo " 默认账号: admin / admin123"
|
||||
echo ""
|
||||
print_info "如果仍有WebSocket问题,请:"
|
||||
echo " 1. 查看日志: docker-compose logs -f xianyu-app"
|
||||
echo " 2. 运行测试: python test-websocket-compatibility.py"
|
||||
echo " 3. 检查网络连接和防火墙设置"
|
@ -9,11 +9,11 @@ APP_CONFIG:
|
||||
platform: web
|
||||
AUTO_REPLY:
|
||||
api:
|
||||
enabled: true
|
||||
enabled: true # 禁用API回复,使用AI回复或关键词回复
|
||||
host: 0.0.0.0 # 绑定所有网络接口,支持IP访问
|
||||
port: 8080 # Web服务端口
|
||||
timeout: 10
|
||||
url: http://0.0.0.0:8080/xianyu/reply
|
||||
url: http://localhost:8080/xianyu/reply # 修复URL地址
|
||||
default_message: 亲爱的"{send_user_name}" 老板你好!所有宝贝都可以拍,秒发货的哈~不满意的话可以直接申请退款哈~
|
||||
enabled: true
|
||||
max_retry: 3
|
||||
|
@ -1,116 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: 快速修复Docker权限问题 (Windows版本)
|
||||
|
||||
title 快速修复Docker权限问题
|
||||
|
||||
:: 颜色定义
|
||||
set "RED=[91m"
|
||||
set "GREEN=[92m"
|
||||
set "YELLOW=[93m"
|
||||
set "BLUE=[94m"
|
||||
set "NC=[0m"
|
||||
|
||||
:print_info
|
||||
echo %BLUE%[INFO]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_success
|
||||
echo %GREEN%[SUCCESS]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:print_error
|
||||
echo %RED%[ERROR]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
echo 🚀 快速修复Docker权限问题
|
||||
echo ================================
|
||||
echo.
|
||||
|
||||
:: 1. 停止容器
|
||||
call :print_info "停止现有容器..."
|
||||
docker-compose down >nul 2>&1
|
||||
|
||||
:: 2. 确保目录存在
|
||||
call :print_info "创建必要目录..."
|
||||
if not exist "data" mkdir data
|
||||
if not exist "logs" mkdir logs
|
||||
if not exist "backups" mkdir backups
|
||||
|
||||
:: 3. 检查并修复docker-compose.yml
|
||||
call :print_info "检查docker-compose.yml配置..."
|
||||
findstr /C:"user.*0:0" docker-compose.yml >nul 2>&1
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_info "添加root用户配置..."
|
||||
|
||||
REM 备份原文件
|
||||
copy docker-compose.yml docker-compose.yml.backup >nul
|
||||
|
||||
REM 创建临时文件添加user配置
|
||||
(
|
||||
for /f "tokens=*" %%a in (docker-compose.yml) do (
|
||||
echo %%a
|
||||
echo %%a | findstr /C:"container_name: xianyu-auto-reply" >nul
|
||||
if !errorlevel! equ 0 (
|
||||
echo user: "0:0"
|
||||
)
|
||||
)
|
||||
) > docker-compose.yml.tmp
|
||||
|
||||
REM 替换原文件
|
||||
move docker-compose.yml.tmp docker-compose.yml >nul
|
||||
|
||||
call :print_success "已配置使用root用户运行"
|
||||
)
|
||||
|
||||
:: 4. 重新构建镜像
|
||||
call :print_info "重新构建Docker镜像..."
|
||||
docker-compose build --no-cache
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_error "Docker镜像构建失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 5. 启动服务
|
||||
call :print_info "启动服务..."
|
||||
docker-compose up -d
|
||||
if !errorlevel! neq 0 (
|
||||
call :print_error "服务启动失败"
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 6. 等待启动
|
||||
call :print_info "等待服务启动..."
|
||||
timeout /t 15 /nobreak >nul
|
||||
|
||||
:: 7. 检查状态
|
||||
call :print_info "检查服务状态..."
|
||||
docker-compose ps | findstr "Up" >nul
|
||||
if !errorlevel! equ 0 (
|
||||
call :print_success "✅ 服务启动成功!"
|
||||
|
||||
echo.
|
||||
call :print_info "最近的日志:"
|
||||
docker-compose logs --tail=10 xianyu-app
|
||||
|
||||
echo.
|
||||
call :print_success "🎉 权限问题已修复!"
|
||||
echo.
|
||||
echo 访问信息:
|
||||
echo Web界面: http://localhost:8080
|
||||
echo 健康检查: http://localhost:8080/health
|
||||
echo 默认账号: admin / admin123
|
||||
|
||||
) else (
|
||||
call :print_error "❌ 服务启动失败"
|
||||
echo.
|
||||
call :print_info "错误日志:"
|
||||
docker-compose logs xianyu-app
|
||||
)
|
||||
|
||||
echo.
|
||||
pause
|
@ -1,88 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 快速修复Docker权限问题
|
||||
# 这个脚本会立即解决权限问题并重启服务
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
echo "🚀 快速修复Docker权限问题"
|
||||
echo "================================"
|
||||
|
||||
# 1. 停止容器
|
||||
print_info "停止现有容器..."
|
||||
docker-compose down
|
||||
|
||||
# 2. 确保目录存在并设置权限
|
||||
print_info "设置目录权限..."
|
||||
mkdir -p data logs backups
|
||||
chmod 777 data logs backups
|
||||
|
||||
# 3. 检查并修复docker-compose.yml
|
||||
print_info "检查docker-compose.yml配置..."
|
||||
if ! grep -q "user.*0:0" docker-compose.yml; then
|
||||
print_info "添加root用户配置..."
|
||||
|
||||
# 备份原文件
|
||||
cp docker-compose.yml docker-compose.yml.backup
|
||||
|
||||
# 在container_name后添加user配置
|
||||
sed -i '/container_name: xianyu-auto-reply/a\ user: "0:0"' docker-compose.yml
|
||||
|
||||
print_success "已配置使用root用户运行"
|
||||
fi
|
||||
|
||||
# 4. 重新构建镜像
|
||||
print_info "重新构建Docker镜像..."
|
||||
docker-compose build --no-cache
|
||||
|
||||
# 5. 启动服务
|
||||
print_info "启动服务..."
|
||||
docker-compose up -d
|
||||
|
||||
# 6. 等待启动
|
||||
print_info "等待服务启动..."
|
||||
sleep 15
|
||||
|
||||
# 7. 检查状态
|
||||
print_info "检查服务状态..."
|
||||
if docker-compose ps | grep -q "Up"; then
|
||||
print_success "✅ 服务启动成功!"
|
||||
|
||||
# 显示日志
|
||||
echo ""
|
||||
print_info "最近的日志:"
|
||||
docker-compose logs --tail=10 xianyu-app
|
||||
|
||||
echo ""
|
||||
print_success "🎉 权限问题已修复!"
|
||||
echo ""
|
||||
echo "访问信息:"
|
||||
echo " Web界面: http://localhost:8080"
|
||||
echo " 健康检查: http://localhost:8080/health"
|
||||
echo " 默认账号: admin / admin123"
|
||||
|
||||
else
|
||||
print_error "❌ 服务启动失败"
|
||||
echo ""
|
||||
print_info "错误日志:"
|
||||
docker-compose logs xianyu-app
|
||||
fi
|
116
reply_server.py
116
reply_server.py
@ -16,6 +16,7 @@ import uvicorn
|
||||
import cookie_manager
|
||||
from db_manager import db_manager
|
||||
from file_log_collector import setup_file_logging, get_file_log_collector
|
||||
from ai_reply_engine import ai_reply_engine
|
||||
|
||||
# 关键字文件路径
|
||||
KEYWORDS_FILE = Path(__file__).parent / "回复关键字.txt"
|
||||
@ -1034,6 +1035,17 @@ class BatchDeleteRequest(BaseModel):
|
||||
items: List[dict] # [{"cookie_id": "xxx", "item_id": "yyy"}, ...]
|
||||
|
||||
|
||||
class AIReplySettings(BaseModel):
|
||||
ai_enabled: bool
|
||||
model_name: str = "qwen-plus"
|
||||
api_key: str = ""
|
||||
base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
max_discount_percent: int = 10
|
||||
max_discount_amount: int = 100
|
||||
max_bargain_rounds: int = 3
|
||||
custom_prompts: str = ""
|
||||
|
||||
|
||||
@app.delete("/items/batch")
|
||||
def batch_delete_items(
|
||||
request: BatchDeleteRequest,
|
||||
@ -1058,6 +1070,110 @@ def batch_delete_items(
|
||||
raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")
|
||||
|
||||
|
||||
# ==================== AI回复管理API ====================
|
||||
|
||||
@app.get("/ai-reply-settings/{cookie_id}")
|
||||
def get_ai_reply_settings(cookie_id: str, _: None = Depends(require_auth)):
|
||||
"""获取指定账号的AI回复设置"""
|
||||
try:
|
||||
settings = db_manager.get_ai_reply_settings(cookie_id)
|
||||
return settings
|
||||
except Exception as e:
|
||||
logger.error(f"获取AI回复设置异常: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")
|
||||
|
||||
|
||||
@app.put("/ai-reply-settings/{cookie_id}")
|
||||
def update_ai_reply_settings(cookie_id: str, settings: AIReplySettings, _: None = Depends(require_auth)):
|
||||
"""更新指定账号的AI回复设置"""
|
||||
try:
|
||||
# 检查账号是否存在
|
||||
if cookie_manager.manager is None:
|
||||
raise HTTPException(status_code=500, detail='CookieManager 未就绪')
|
||||
|
||||
if cookie_id not in cookie_manager.manager.cookies:
|
||||
raise HTTPException(status_code=404, detail='账号不存在')
|
||||
|
||||
# 保存设置
|
||||
settings_dict = settings.dict()
|
||||
success = db_manager.save_ai_reply_settings(cookie_id, settings_dict)
|
||||
|
||||
if success:
|
||||
# 清理客户端缓存,强制重新创建
|
||||
ai_reply_engine.clear_client_cache(cookie_id)
|
||||
|
||||
# 如果启用了AI回复,记录日志
|
||||
if settings.ai_enabled:
|
||||
logger.info(f"账号 {cookie_id} 启用AI回复")
|
||||
else:
|
||||
logger.info(f"账号 {cookie_id} 禁用AI回复")
|
||||
|
||||
return {"message": "AI回复设置更新成功"}
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="更新失败")
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"更新AI回复设置异常: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")
|
||||
|
||||
|
||||
@app.get("/ai-reply-settings")
|
||||
def get_all_ai_reply_settings(_: None = Depends(require_auth)):
|
||||
"""获取所有账号的AI回复设置"""
|
||||
try:
|
||||
settings = db_manager.get_all_ai_reply_settings()
|
||||
return settings
|
||||
except Exception as e:
|
||||
logger.error(f"获取所有AI回复设置异常: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")
|
||||
|
||||
|
||||
@app.post("/ai-reply-test/{cookie_id}")
|
||||
def test_ai_reply(cookie_id: str, test_data: dict, _: None = Depends(require_auth)):
|
||||
"""测试AI回复功能"""
|
||||
try:
|
||||
# 检查账号是否存在
|
||||
if cookie_manager.manager is None:
|
||||
raise HTTPException(status_code=500, detail='CookieManager 未就绪')
|
||||
|
||||
if cookie_id not in cookie_manager.manager.cookies:
|
||||
raise HTTPException(status_code=404, detail='账号不存在')
|
||||
|
||||
# 检查是否启用AI回复
|
||||
if not ai_reply_engine.is_ai_enabled(cookie_id):
|
||||
raise HTTPException(status_code=400, detail='该账号未启用AI回复')
|
||||
|
||||
# 构造测试数据
|
||||
test_message = test_data.get('message', '你好')
|
||||
test_item_info = {
|
||||
'title': test_data.get('item_title', '测试商品'),
|
||||
'price': test_data.get('item_price', 100),
|
||||
'desc': test_data.get('item_desc', '这是一个测试商品')
|
||||
}
|
||||
|
||||
# 生成测试回复
|
||||
reply = ai_reply_engine.generate_reply(
|
||||
message=test_message,
|
||||
item_info=test_item_info,
|
||||
chat_id=f"test_{int(time.time())}",
|
||||
cookie_id=cookie_id,
|
||||
user_id="test_user",
|
||||
item_id="test_item"
|
||||
)
|
||||
|
||||
if reply:
|
||||
return {"message": "测试成功", "reply": reply}
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="AI回复生成失败")
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"测试AI回复异常: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"服务器错误: {str(e)}")
|
||||
|
||||
|
||||
# ==================== 日志管理API ====================
|
||||
|
||||
@app.get("/logs")
|
||||
|
@ -26,4 +26,8 @@ psutil>=5.9.0
|
||||
requests>=2.31.0
|
||||
|
||||
# 文件上传支持
|
||||
python-multipart>=0.0.6
|
||||
python-multipart>=0.0.6
|
||||
|
||||
# AI回复相关
|
||||
openai>=1.65.5
|
||||
python-dotenv>=1.0.1
|
@ -631,11 +631,14 @@
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
min-width: 2rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.status-badge.enabled {
|
||||
@ -1262,11 +1265,12 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">账号ID</th>
|
||||
<th style="width: 25%">Cookie值</th>
|
||||
<th style="width: 10%">关键词</th>
|
||||
<th style="width: 10%">状态</th>
|
||||
<th style="width: 12%">默认回复</th>
|
||||
<th style="width: 33%">操作</th>
|
||||
<th style="width: 20%">Cookie值</th>
|
||||
<th style="width: 8%">关键词</th>
|
||||
<th style="width: 8%">状态</th>
|
||||
<th style="width: 10%">默认回复</th>
|
||||
<th style="width: 10%">AI回复</th>
|
||||
<th style="width: 34%">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
@ -3236,7 +3240,7 @@
|
||||
if (cookieDetails.length === 0) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-muted empty-state">
|
||||
<td colspan="7" class="text-center py-4 text-muted empty-state">
|
||||
<i class="bi bi-inbox fs-1 d-block mb-3"></i>
|
||||
<h5>暂无账号</h5>
|
||||
<p class="mb-0">请添加新的闲鱼账号开始使用</p>
|
||||
@ -3271,16 +3275,28 @@
|
||||
defaultReply = await defaultReplyResponse.json();
|
||||
}
|
||||
|
||||
// 获取AI回复设置
|
||||
const aiReplyResponse = await fetch(`${apiBase}/ai-reply-settings/${cookie.id}`, {
|
||||
headers: { 'Authorization': `Bearer ${authToken}` }
|
||||
});
|
||||
|
||||
let aiReply = { ai_enabled: false, model_name: 'qwen-plus' };
|
||||
if (aiReplyResponse.ok) {
|
||||
aiReply = await aiReplyResponse.json();
|
||||
}
|
||||
|
||||
return {
|
||||
...cookie,
|
||||
keywordCount: keywordCount,
|
||||
defaultReply: defaultReply
|
||||
defaultReply: defaultReply,
|
||||
aiReply: aiReply
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
...cookie,
|
||||
keywordCount: 0,
|
||||
defaultReply: { enabled: false, reply_content: '' }
|
||||
defaultReply: { enabled: false, reply_content: '' },
|
||||
aiReply: { ai_enabled: false, model_name: 'qwen-plus' }
|
||||
};
|
||||
}
|
||||
})
|
||||
@ -3299,6 +3315,11 @@
|
||||
'<span class="badge bg-success">启用</span>' :
|
||||
'<span class="badge bg-secondary">禁用</span>';
|
||||
|
||||
// AI回复状态标签
|
||||
const aiReplyBadge = cookie.aiReply.ai_enabled ?
|
||||
'<span class="badge bg-primary">AI启用</span>' :
|
||||
'<span class="badge bg-secondary">AI禁用</span>';
|
||||
|
||||
tr.innerHTML = `
|
||||
<td class="align-middle">
|
||||
<div class="cookie-id">
|
||||
@ -3321,15 +3342,17 @@
|
||||
<input type="checkbox" ${isEnabled ? 'checked' : ''} onchange="toggleAccountStatus('${cookie.id}', this.checked)">
|
||||
<span class="status-slider"></span>
|
||||
</label>
|
||||
<span class="status-badge ${isEnabled ? 'enabled' : 'disabled'}">
|
||||
<span class="status-badge ${isEnabled ? 'enabled' : 'disabled'}" title="${isEnabled ? '账号已启用' : '账号已禁用'}">
|
||||
<i class="bi bi-${isEnabled ? 'check-circle-fill' : 'x-circle-fill'}"></i>
|
||||
${isEnabled ? '启用' : '禁用'}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
${defaultReplyBadge}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
${aiReplyBadge}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<div class="btn-group" role="group">
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="editCookieInline('${cookie.id}', '${cookie.value}')" title="修改Cookie" ${!isEnabled ? 'disabled' : ''}>
|
||||
@ -3338,6 +3361,9 @@
|
||||
<button class="btn btn-sm btn-outline-success" onclick="goToAutoReply('${cookie.id}')" title="${isEnabled ? '设置自动回复' : '配置关键词 (账号已禁用)'}">
|
||||
<i class="bi bi-arrow-right-circle"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-warning" onclick="configAIReply('${cookie.id}')" title="配置AI回复" ${!isEnabled ? 'disabled' : ''}>
|
||||
<i class="bi bi-robot"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info" onclick="copyCookie('${cookie.id}', '${cookie.value}')" title="复制Cookie">
|
||||
<i class="bi bi-clipboard"></i>
|
||||
</button>
|
||||
@ -3618,9 +3644,9 @@
|
||||
|
||||
// 更新状态徽章
|
||||
statusBadge.className = `status-badge ${enabled ? 'enabled' : 'disabled'}`;
|
||||
statusBadge.title = enabled ? '账号已启用' : '账号已禁用';
|
||||
statusBadge.innerHTML = `
|
||||
<i class="bi bi-${enabled ? 'check-circle-fill' : 'x-circle-fill'}"></i>
|
||||
${enabled ? '启用' : '禁用'}
|
||||
`;
|
||||
|
||||
// 更新按钮状态(只禁用编辑Cookie按钮,其他按钮保持可用)
|
||||
@ -4009,6 +4035,177 @@
|
||||
showToast('测试功能开发中...', 'info');
|
||||
}
|
||||
|
||||
// ==================== AI回复配置相关函数 ====================
|
||||
|
||||
// 配置AI回复
|
||||
async function configAIReply(accountId) {
|
||||
try {
|
||||
// 获取当前AI回复设置
|
||||
const settings = await fetchJSON(`${apiBase}/ai-reply-settings/${accountId}`);
|
||||
|
||||
// 填充表单
|
||||
document.getElementById('aiConfigAccountId').value = accountId;
|
||||
document.getElementById('aiConfigAccountIdDisplay').value = accountId;
|
||||
document.getElementById('aiReplyEnabled').checked = settings.ai_enabled;
|
||||
document.getElementById('aiModelName').value = settings.model_name;
|
||||
document.getElementById('aiBaseUrl').value = settings.base_url;
|
||||
document.getElementById('aiApiKey').value = settings.api_key;
|
||||
document.getElementById('maxDiscountPercent').value = settings.max_discount_percent;
|
||||
document.getElementById('maxDiscountAmount').value = settings.max_discount_amount;
|
||||
document.getElementById('maxBargainRounds').value = settings.max_bargain_rounds;
|
||||
document.getElementById('customPrompts').value = settings.custom_prompts;
|
||||
|
||||
// 切换设置显示状态
|
||||
toggleAIReplySettings();
|
||||
|
||||
// 显示模态框
|
||||
const modal = new bootstrap.Modal(document.getElementById('aiReplyConfigModal'));
|
||||
modal.show();
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取AI回复设置失败:', error);
|
||||
showToast('获取AI回复设置失败', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// 切换AI回复设置显示
|
||||
function toggleAIReplySettings() {
|
||||
const enabled = document.getElementById('aiReplyEnabled').checked;
|
||||
const settingsDiv = document.getElementById('aiReplySettings');
|
||||
const bargainSettings = document.getElementById('bargainSettings');
|
||||
const promptSettings = document.getElementById('promptSettings');
|
||||
const testArea = document.getElementById('testArea');
|
||||
|
||||
if (enabled) {
|
||||
settingsDiv.style.display = 'block';
|
||||
bargainSettings.style.display = 'block';
|
||||
promptSettings.style.display = 'block';
|
||||
testArea.style.display = 'block';
|
||||
} else {
|
||||
settingsDiv.style.display = 'none';
|
||||
bargainSettings.style.display = 'none';
|
||||
promptSettings.style.display = 'none';
|
||||
testArea.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 保存AI回复配置
|
||||
async function saveAIReplyConfig() {
|
||||
try {
|
||||
const accountId = document.getElementById('aiConfigAccountId').value;
|
||||
const enabled = document.getElementById('aiReplyEnabled').checked;
|
||||
|
||||
// 如果启用AI回复,验证必填字段
|
||||
if (enabled) {
|
||||
const apiKey = document.getElementById('aiApiKey').value.trim();
|
||||
if (!apiKey) {
|
||||
showToast('请输入API密钥', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证自定义提示词格式
|
||||
const customPrompts = document.getElementById('customPrompts').value.trim();
|
||||
if (customPrompts) {
|
||||
try {
|
||||
JSON.parse(customPrompts);
|
||||
} catch (e) {
|
||||
showToast('自定义提示词格式错误,请检查JSON格式', 'warning');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建设置对象
|
||||
const settings = {
|
||||
ai_enabled: enabled,
|
||||
model_name: document.getElementById('aiModelName').value,
|
||||
api_key: document.getElementById('aiApiKey').value,
|
||||
base_url: document.getElementById('aiBaseUrl').value,
|
||||
max_discount_percent: parseInt(document.getElementById('maxDiscountPercent').value),
|
||||
max_discount_amount: parseInt(document.getElementById('maxDiscountAmount').value),
|
||||
max_bargain_rounds: parseInt(document.getElementById('maxBargainRounds').value),
|
||||
custom_prompts: document.getElementById('customPrompts').value
|
||||
};
|
||||
|
||||
// 保存设置
|
||||
const response = await fetch(`${apiBase}/ai-reply-settings/${accountId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
},
|
||||
body: JSON.stringify(settings)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showToast('AI回复配置保存成功', 'success');
|
||||
bootstrap.Modal.getInstance(document.getElementById('aiReplyConfigModal')).hide();
|
||||
loadCookies(); // 刷新账号列表以更新AI回复状态显示
|
||||
} else {
|
||||
const error = await response.text();
|
||||
showToast(`保存失败: ${error}`, 'danger');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('保存AI回复配置失败:', error);
|
||||
showToast('保存AI回复配置失败', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// 测试AI回复
|
||||
async function testAIReply() {
|
||||
try {
|
||||
const accountId = document.getElementById('aiConfigAccountId').value;
|
||||
const testMessage = document.getElementById('testMessage').value.trim();
|
||||
const testItemPrice = document.getElementById('testItemPrice').value;
|
||||
|
||||
if (!testMessage) {
|
||||
showToast('请输入测试消息', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建测试数据
|
||||
const testData = {
|
||||
message: testMessage,
|
||||
item_title: '测试商品',
|
||||
item_price: parseFloat(testItemPrice) || 100,
|
||||
item_desc: '这是一个用于测试AI回复功能的商品'
|
||||
};
|
||||
|
||||
// 显示加载状态
|
||||
const testResult = document.getElementById('testResult');
|
||||
const testReplyContent = document.getElementById('testReplyContent');
|
||||
testResult.style.display = 'block';
|
||||
testReplyContent.innerHTML = '<i class="bi bi-hourglass-split"></i> 正在生成AI回复...';
|
||||
|
||||
// 调用测试API
|
||||
const response = await fetch(`${apiBase}/ai-reply-test/${accountId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${authToken}`
|
||||
},
|
||||
body: JSON.stringify(testData)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
testReplyContent.innerHTML = result.reply;
|
||||
showToast('AI回复测试成功', 'success');
|
||||
} else {
|
||||
const error = await response.text();
|
||||
testReplyContent.innerHTML = `<span class="text-danger">测试失败: ${error}</span>`;
|
||||
showToast(`测试失败: ${error}`, 'danger');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试AI回复失败:', error);
|
||||
const testReplyContent = document.getElementById('testReplyContent');
|
||||
testReplyContent.innerHTML = `<span class="text-danger">测试失败: ${error.message}</span>`;
|
||||
showToast('测试AI回复失败', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 监听默认回复启用状态变化
|
||||
@ -6364,5 +6561,163 @@
|
||||
|
||||
</script>
|
||||
|
||||
<!-- AI回复配置模态框 -->
|
||||
<div class="modal fade" id="aiReplyConfigModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="bi bi-robot me-2"></i>AI回复配置
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="aiReplyConfigForm">
|
||||
<input type="hidden" id="aiConfigAccountId">
|
||||
|
||||
<!-- 基本设置 -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-gear me-2"></i>基本设置</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">账号ID</label>
|
||||
<input type="text" class="form-control" id="aiConfigAccountIdDisplay" readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mt-4">
|
||||
<input class="form-check-input" type="checkbox" id="aiReplyEnabled" onchange="toggleAIReplySettings()">
|
||||
<label class="form-check-label" for="aiReplyEnabled">
|
||||
<strong>启用AI回复</strong>
|
||||
</label>
|
||||
<small class="form-text text-muted d-block">启用后将自动禁用关键词匹配和默认回复</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="aiReplySettings" style="display: none;">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="aiModelName" class="form-label">AI模型</label>
|
||||
<select class="form-select" id="aiModelName">
|
||||
<option value="qwen-plus">通义千问Plus</option>
|
||||
<option value="qwen-turbo">通义千问Turbo</option>
|
||||
<option value="qwen-max">通义千问Max</option>
|
||||
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
|
||||
<option value="gpt-4">GPT-4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="aiBaseUrl" class="form-label">API地址</label>
|
||||
<input type="url" class="form-control" id="aiBaseUrl"
|
||||
value="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
placeholder="API Base URL">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="aiApiKey" class="form-label">API密钥 <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control" id="aiApiKey"
|
||||
placeholder="请输入API密钥" required>
|
||||
<small class="form-text text-muted">
|
||||
通义千问请使用DashScope API Key,GPT请使用OpenAI API Key
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 议价设置 -->
|
||||
<div class="card mb-3" id="bargainSettings" style="display: none;">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-currency-dollar me-2"></i>议价设置</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label for="maxDiscountPercent" class="form-label">最大优惠百分比</label>
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="maxDiscountPercent"
|
||||
min="0" max="50" value="10">
|
||||
<span class="input-group-text">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="maxDiscountAmount" class="form-label">最大优惠金额</label>
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="maxDiscountAmount"
|
||||
min="0" value="100">
|
||||
<span class="input-group-text">元</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="maxBargainRounds" class="form-label">最大议价轮数</label>
|
||||
<input type="number" class="form-control" id="maxBargainRounds"
|
||||
min="1" max="10" value="3">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词设置 -->
|
||||
<div class="card mb-3" id="promptSettings" style="display: none;">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-chat-quote me-2"></i>提示词设置</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">自定义提示词 (JSON格式)</label>
|
||||
<textarea class="form-control" id="customPrompts" rows="8"
|
||||
placeholder='{"classify": "分类提示词", "price": "议价提示词", "tech": "技术提示词", "default": "默认提示词"}'></textarea>
|
||||
<small class="form-text text-muted">
|
||||
留空使用系统默认提示词。格式:{"classify": "...", "price": "...", "tech": "...", "default": "..."}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 测试区域 -->
|
||||
<div class="card" id="testArea" style="display: none;">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0"><i class="bi bi-play-circle me-2"></i>功能测试</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="testMessage" class="form-label">测试消息</label>
|
||||
<input type="text" class="form-control" id="testMessage"
|
||||
value="你好,这个商品能便宜点吗?" placeholder="输入测试消息">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="testItemPrice" class="form-label">商品价格</label>
|
||||
<input type="number" class="form-control" id="testItemPrice"
|
||||
value="100" placeholder="商品价格">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary" onclick="testAIReply()">
|
||||
<i class="bi bi-play me-1"></i>测试AI回复
|
||||
</button>
|
||||
<div id="testResult" class="mt-3" style="display: none;">
|
||||
<div class="alert alert-info">
|
||||
<strong>AI回复:</strong>
|
||||
<div id="testReplyContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveAIReplyConfig()">
|
||||
<i class="bi bi-check-lg me-1"></i>保存配置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
27
test_fix.py
27
test_fix.py
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试修复
|
||||
"""
|
||||
|
||||
def test_imports():
|
||||
print("🧪 测试导入修复")
|
||||
|
||||
try:
|
||||
from file_log_collector import setup_file_logging, get_file_log_collector
|
||||
print("✅ file_log_collector 导入成功")
|
||||
|
||||
# 测试初始化
|
||||
collector = setup_file_logging()
|
||||
print("✅ 文件日志收集器初始化成功")
|
||||
|
||||
# 生成测试日志
|
||||
from loguru import logger
|
||||
logger.info("测试日志修复")
|
||||
|
||||
print("✅ 所有导入和初始化都正常")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 导入失败: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_imports()
|
128
使用说明.md
128
使用说明.md
@ -152,27 +152,133 @@ python Start.py
|
||||
- 备份 `global_config.yml` 配置文件
|
||||
- 备份自定义的关键词文件
|
||||
|
||||
## 🔧 高级功能
|
||||
|
||||
### AI回复配置
|
||||
1. 在账号列表中点击"🤖 配置AI回复"
|
||||
2. 开启AI回复功能
|
||||
3. 配置API密钥和模型参数
|
||||
4. 设置议价策略和优惠限制
|
||||
5. 自定义提示词(可选)
|
||||
|
||||
### 自动发货设置
|
||||
1. 进入"自动发货"页面
|
||||
2. 添加发货规则和关键词匹配
|
||||
3. 上传卡券文件或手动添加卡券
|
||||
4. 配置发货模板和通知方式
|
||||
|
||||
### 商品管理
|
||||
1. 进入"商品管理"页面
|
||||
2. 查看自动收集的商品信息
|
||||
3. 编辑商品详情和分类
|
||||
4. 批量获取账号下所有商品
|
||||
|
||||
### 日志监控
|
||||
1. 进入"日志管理"页面
|
||||
2. 实时查看系统运行日志
|
||||
3. 按级别和来源筛选日志
|
||||
4. 查看系统统计信息
|
||||
|
||||
## 📊 数据管理
|
||||
|
||||
### 数据备份
|
||||
1. 进入"系统设置"页面
|
||||
2. 点击"导出备份"按钮
|
||||
3. 下载备份文件到本地
|
||||
4. 定期备份重要数据
|
||||
|
||||
### 数据恢复
|
||||
1. 进入"系统设置"页面
|
||||
2. 点击"导入备份"按钮
|
||||
3. 选择备份文件上传
|
||||
4. 确认恢复操作
|
||||
|
||||
### 数据清理
|
||||
- 定期清理过期日志文件
|
||||
- 删除无效的商品信息
|
||||
- 清理过期的对话记录
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
### 测试系统
|
||||
运行测试脚本检查系统状态:
|
||||
```bash
|
||||
python test_system.py
|
||||
### 系统健康检查
|
||||
访问健康检查端点:
|
||||
```
|
||||
http://localhost:8080/health
|
||||
```
|
||||
|
||||
### 重新创建配置
|
||||
如果配置文件损坏,运行:
|
||||
### 查看系统状态
|
||||
```bash
|
||||
python create_config.py
|
||||
# 查看容器状态
|
||||
docker-compose ps
|
||||
|
||||
# 查看系统资源使用
|
||||
docker stats
|
||||
|
||||
# 查看实时日志
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
## 🎯 使用建议
|
||||
### 常见问题解决
|
||||
|
||||
**问题1:Cookie失效**
|
||||
- 重新获取Cookie并更新
|
||||
- 检查账号是否被限制
|
||||
- 确认Cookie格式正确
|
||||
|
||||
**问题2:消息接收异常**
|
||||
- 检查网络连接
|
||||
- 重启WebSocket连接
|
||||
- 查看错误日志
|
||||
|
||||
**问题3:AI回复失败**
|
||||
- 检查API密钥是否正确
|
||||
- 确认API服务可用
|
||||
- 检查账户余额
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
### 安全建议
|
||||
1. **定期更换密码**:修改默认管理员密码
|
||||
2. **限制访问**:仅允许信任的IP访问
|
||||
3. **备份数据**:定期备份重要配置和数据
|
||||
4. **监控日志**:定期查看系统日志
|
||||
|
||||
### 性能优化
|
||||
1. **合理设置**:根据实际需求配置参数
|
||||
2. **定期清理**:清理过期数据和日志
|
||||
3. **监控资源**:关注系统资源使用情况
|
||||
4. **优化配置**:根据使用情况调整配置
|
||||
|
||||
### 使用技巧
|
||||
1. **Cookie获取**:使用浏览器开发者工具获取完整Cookie
|
||||
2. **关键词设置**:设置常用的咨询关键词和回复
|
||||
3. **定期检查**:定期查看日志确保系统正常运行
|
||||
4. **备份数据**:重要数据请及时备份
|
||||
3. **AI配置**:根据商品类型调整AI提示词
|
||||
4. **发货规则**:设置精确的商品匹配规则
|
||||
|
||||
## 🔄 系统更新
|
||||
|
||||
### 更新步骤
|
||||
1. 备份当前数据
|
||||
2. 停止系统服务
|
||||
3. 拉取最新代码
|
||||
4. 重新构建镜像
|
||||
5. 启动更新后的服务
|
||||
|
||||
### Docker更新
|
||||
```bash
|
||||
# 停止服务
|
||||
docker-compose down
|
||||
|
||||
# 拉取最新代码
|
||||
git pull
|
||||
|
||||
# 重新构建
|
||||
./deploy.sh --update
|
||||
|
||||
# 检查状态
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**注意**:本系统仅供学习交流使用,请遵守相关法律法规和平台规则。
|
||||
**注意**:本系统仅供学习交流使用,请遵守相关法律法规和平台规则。使用前请仔细阅读相关文档,确保正确配置和使用。
|
||||
|
58
商品管理功能说明.md
58
商品管理功能说明.md
@ -194,6 +194,62 @@ Authorization: Bearer {token}
|
||||
- 监控自动发货匹配情况
|
||||
- 及时处理异常情况
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
**问题1:商品信息收集失败**
|
||||
- 检查网络连接是否正常
|
||||
- 确认Cookie是否有效
|
||||
- 验证商品ID格式是否正确
|
||||
- 查看详细错误日志
|
||||
|
||||
**问题2:商品详情获取失败**
|
||||
- 检查API服务是否可用
|
||||
- 确认商品是否存在
|
||||
- 验证请求参数是否正确
|
||||
- 检查API配置
|
||||
|
||||
**问题3:数据库操作失败**
|
||||
- 检查数据库文件权限
|
||||
- 确认磁盘空间是否充足
|
||||
- 验证数据格式是否正确
|
||||
- 查看数据库错误日志
|
||||
|
||||
### 性能优化建议
|
||||
1. **定期清理**:清理无效的商品信息
|
||||
2. **索引优化**:为常用查询字段建立索引
|
||||
3. **批量操作**:使用批量操作提高效率
|
||||
4. **缓存机制**:缓存常用的商品信息
|
||||
|
||||
## 📊 数据统计
|
||||
|
||||
### 商品统计信息
|
||||
- **总商品数量**:系统中所有商品的总数
|
||||
- **有效商品数**:包含完整信息的商品数量
|
||||
- **账号分布**:各账号的商品数量分布
|
||||
- **分类统计**:不同分类的商品数量
|
||||
|
||||
### 使用统计
|
||||
- **收集成功率**:商品信息收集的成功率
|
||||
- **API调用次数**:商品详情API的调用统计
|
||||
- **匹配成功率**:自动发货匹配的成功率
|
||||
- **处理速度**:商品信息处理的平均速度
|
||||
|
||||
## 🚀 未来规划
|
||||
|
||||
### 即将推出的功能
|
||||
1. **商品分析**:基于商品数据的深度分析
|
||||
2. **价格监控**:监控商品价格变化趋势
|
||||
3. **库存管理**:集成库存管理功能
|
||||
4. **销量统计**:统计商品销售数据
|
||||
|
||||
### 长期发展方向
|
||||
1. **智能推荐**:基于商品数据的智能推荐
|
||||
2. **自动定价**:根据市场数据自动调整价格
|
||||
3. **竞品分析**:分析同类商品的竞争情况
|
||||
4. **数据挖掘**:深度挖掘商品数据价值
|
||||
|
||||
---
|
||||
|
||||
🎉 **商品管理功能让您的闲鱼自动发货更加智能和准确!**
|
||||
🎉 **商品管理功能为您的闲鱼自动发货提供了强大的数据支持,让自动化运营更加智能和精准!通过持续优化和功能扩展,将为您带来更好的使用体验。**
|
||||
|
92
日志管理功能说明.md
92
日志管理功能说明.md
@ -280,17 +280,91 @@
|
||||
## 🎯 使用建议
|
||||
|
||||
### 适用场景
|
||||
- ✅ **开发调试**:实时查看程序运行状态
|
||||
- ✅ **问题排查**:快速定位错误和异常
|
||||
- ✅ **性能监控**:监控系统运行情况
|
||||
- ✅ **用户支持**:协助用户解决问题
|
||||
- ✅ **开发调试**:实时查看程序运行状态和调试信息
|
||||
- ✅ **问题排查**:快速定位错误和异常,分析问题原因
|
||||
- ✅ **性能监控**:监控系统运行情况和性能指标
|
||||
- ✅ **用户支持**:协助用户解决问题,提供技术支持
|
||||
- ✅ **运维监控**:生产环境的实时监控和告警
|
||||
- ✅ **安全审计**:监控系统安全事件和异常行为
|
||||
|
||||
### 最佳实践
|
||||
1. **开启自动刷新**:实时监控系统状态
|
||||
2. **使用过滤器**:快速找到关注的日志
|
||||
3. **查看统计信息**:了解系统整体状况
|
||||
4. **定期清空**:避免内存占用过多
|
||||
1. **开启自动刷新**:实时监控系统状态,及时发现问题
|
||||
2. **合理使用过滤器**:根据需要过滤特定级别或来源的日志
|
||||
3. **定期查看统计信息**:了解系统整体运行状况和趋势
|
||||
4. **适时清空内存日志**:避免内存占用过多,保持系统性能
|
||||
5. **结合文件日志**:重要日志同时查看文件日志进行备份
|
||||
6. **设置告警规则**:对ERROR级别日志设置告警通知
|
||||
|
||||
### 日志级别使用指南
|
||||
- **DEBUG**:详细的调试信息,开发阶段使用
|
||||
- **INFO**:一般信息,记录程序正常运行状态
|
||||
- **WARNING**:警告信息,需要注意但不影响运行
|
||||
- **ERROR**:错误信息,需要立即处理的问题
|
||||
- **CRITICAL**:严重错误,可能导致程序崩溃
|
||||
|
||||
## 🔧 高级功能
|
||||
|
||||
### 日志导出
|
||||
1. **实时导出**:将当前显示的日志导出为文件
|
||||
2. **批量导出**:导出指定时间范围的日志
|
||||
3. **格式选择**:支持TXT、JSON、CSV等格式
|
||||
4. **自动压缩**:大文件自动压缩处理
|
||||
|
||||
### 日志分析
|
||||
1. **趋势分析**:分析日志数量和级别的时间趋势
|
||||
2. **异常检测**:自动检测异常日志模式
|
||||
3. **性能分析**:分析系统性能相关日志
|
||||
4. **报表生成**:生成日志分析报表
|
||||
|
||||
### 告警配置
|
||||
1. **级别告警**:ERROR级别日志自动告警
|
||||
2. **频率告警**:异常日志频率过高时告警
|
||||
3. **关键词告警**:包含特定关键词的日志告警
|
||||
4. **通知方式**:支持邮件、短信、webhook等通知
|
||||
|
||||
## 🚨 故障排除
|
||||
|
||||
### 常见问题
|
||||
**Q: 日志不更新?**
|
||||
A: 检查以下项目:
|
||||
- 自动刷新是否开启
|
||||
- 网络连接是否正常
|
||||
- 服务器是否正常运行
|
||||
- 浏览器是否支持WebSocket
|
||||
|
||||
**Q: 日志显示不完整?**
|
||||
A: 可能原因:
|
||||
- 内存缓冲区已满,旧日志被清理
|
||||
- 过滤器设置过于严格
|
||||
- 日志级别设置不当
|
||||
|
||||
**Q: 性能影响?**
|
||||
A: 优化建议:
|
||||
- 适当调整刷新频率
|
||||
- 使用过滤器减少显示数量
|
||||
- 定期清空内存日志
|
||||
- 关闭不必要的DEBUG日志
|
||||
|
||||
### 性能优化
|
||||
1. **合理设置缓冲区大小**:根据系统内存调整
|
||||
2. **优化刷新频率**:平衡实时性和性能
|
||||
3. **使用过滤器**:减少不必要的日志传输
|
||||
4. **定期清理**:避免内存泄漏
|
||||
|
||||
## 📊 监控指标
|
||||
|
||||
### 系统指标
|
||||
- **日志生成速率**:每秒生成的日志数量
|
||||
- **内存使用量**:日志缓冲区内存占用
|
||||
- **处理延迟**:日志从生成到显示的延迟
|
||||
- **错误率**:ERROR级别日志的比例
|
||||
|
||||
### 业务指标
|
||||
- **消息处理量**:处理的消息数量统计
|
||||
- **API调用次数**:各API接口的调用统计
|
||||
- **用户活动**:用户操作和访问统计
|
||||
- **系统健康度**:基于日志的系统健康评分
|
||||
|
||||
---
|
||||
|
||||
🎉 **实时日志管理功能已完成,提供了真正的实时日志查看、过滤和分析能力!**
|
||||
🎉 **实时日志管理功能提供了完整的日志查看、分析和监控能力,是系统运维和问题排查的重要工具!**
|
||||
|
92
自动发货功能说明.md
92
自动发货功能说明.md
@ -254,12 +254,92 @@ python test-item-info-delivery.py
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
1. **规则设计**:关键字要具体明确,避免过于宽泛
|
||||
2. **库存管理**:定期检查批量数据库存,及时补充
|
||||
3. **监控告警**:设置发货失败告警,及时处理异常
|
||||
4. **测试验证**:新规则上线前充分测试
|
||||
5. **日志分析**:定期分析发货日志,优化匹配规则
|
||||
### 规则设计原则
|
||||
1. **关键字精确性**:关键字要具体明确,避免过于宽泛
|
||||
2. **优先级设置**:重要商品设置更长的关键字,提高匹配优先级
|
||||
3. **分类管理**:按商品类型分组管理发货规则
|
||||
4. **定期更新**:根据商品变化及时更新匹配规则
|
||||
|
||||
### 库存管理策略
|
||||
1. **实时监控**:定期检查批量数据库存状态
|
||||
2. **预警机制**:设置库存低于阈值时的告警
|
||||
3. **自动补充**:配置自动补充机制或定期手动补充
|
||||
4. **分批管理**:将卡券分批次管理,避免一次性消耗完
|
||||
|
||||
### 质量控制措施
|
||||
1. **测试验证**:新规则上线前在测试环境充分测试
|
||||
2. **灰度发布**:新规则先在部分商品上试运行
|
||||
3. **监控告警**:设置发货失败率告警,及时处理异常
|
||||
4. **人工审核**:重要商品可设置人工审核环节
|
||||
|
||||
### 数据分析优化
|
||||
1. **日志分析**:定期分析发货日志,识别问题模式
|
||||
2. **成功率统计**:监控各规则的匹配成功率
|
||||
3. **用户反馈**:收集买家反馈,优化发货内容
|
||||
4. **性能监控**:监控发货响应时间,优化处理流程
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
**问题1:发货规则不匹配**
|
||||
- 检查关键字是否正确
|
||||
- 确认商品信息是否完整
|
||||
- 验证匹配逻辑是否合理
|
||||
- 查看详细的匹配日志
|
||||
|
||||
**问题2:API接口调用失败**
|
||||
- 检查API地址是否正确
|
||||
- 验证请求参数和格式
|
||||
- 确认网络连接状态
|
||||
- 查看API服务状态
|
||||
|
||||
**问题3:批量数据消耗过快**
|
||||
- 检查匹配规则是否过于宽泛
|
||||
- 确认是否有重复发货
|
||||
- 调整匹配策略
|
||||
- 增加库存补充频率
|
||||
|
||||
**问题4:发货消息发送失败**
|
||||
- 检查账号连接状态
|
||||
- 验证消息格式是否正确
|
||||
- 确认网络连接稳定
|
||||
- 查看WebSocket连接日志
|
||||
|
||||
### 调试技巧
|
||||
1. **开启详细日志**:在配置中开启DEBUG级别日志
|
||||
2. **单步测试**:使用测试功能验证单个规则
|
||||
3. **模拟环境**:在测试环境模拟真实场景
|
||||
4. **监控面板**:使用系统监控面板查看实时状态
|
||||
|
||||
## 📈 性能优化
|
||||
|
||||
### 系统性能优化
|
||||
1. **缓存机制**:缓存商品信息,减少API调用
|
||||
2. **异步处理**:使用异步处理提高并发能力
|
||||
3. **连接池**:优化数据库连接池配置
|
||||
4. **资源限制**:合理设置系统资源限制
|
||||
|
||||
### 业务流程优化
|
||||
1. **规则优化**:优化匹配算法,提高匹配效率
|
||||
2. **批量处理**:支持批量发货操作
|
||||
3. **智能调度**:根据系统负载智能调度任务
|
||||
4. **预处理**:预处理商品信息,提高匹配速度
|
||||
|
||||
## 🔐 安全考虑
|
||||
|
||||
### 数据安全
|
||||
1. **敏感信息加密**:卡券内容加密存储
|
||||
2. **访问控制**:严格的权限控制机制
|
||||
3. **审计日志**:完整的操作审计日志
|
||||
4. **备份恢复**:定期备份重要数据
|
||||
|
||||
### 业务安全
|
||||
1. **防重复发货**:严格的重复发货检测
|
||||
2. **异常监控**:实时监控异常发货行为
|
||||
3. **人工干预**:支持紧急情况下的人工干预
|
||||
4. **风险控制**:设置发货频率和数量限制
|
||||
|
||||
---
|
||||
|
||||
🎉 **自动发货功能让您的闲鱼店铺实现真正的自动化运营!**
|
||||
🎉 **自动发货功能让您的闲鱼店铺实现真正的自动化运营!通过合理配置和优化,可以大大提高运营效率和用户体验。**
|
||||
|
120
获取所有商品功能说明.md
120
获取所有商品功能说明.md
@ -181,22 +181,120 @@ async def get_item_list_info(self, retry_count=0):
|
||||
- 错误信息记录
|
||||
- 便于问题排查
|
||||
|
||||
## 🚀 扩展可能
|
||||
## 🚀 高级功能
|
||||
|
||||
### 1. 批量操作
|
||||
- 支持多个账号批量获取
|
||||
- 导出商品信息到文件
|
||||
- 商品信息对比分析
|
||||
- **多账号批量获取**:一次性获取所有账号的商品信息
|
||||
- **定时自动获取**:设置定时任务自动更新商品信息
|
||||
- **增量更新**:只获取新增或变更的商品信息
|
||||
- **并发处理**:支持多账号并发获取,提高效率
|
||||
|
||||
### 2. 数据处理
|
||||
- 商品信息入库存储
|
||||
- 商品状态监控
|
||||
- 价格变化追踪
|
||||
### 2. 数据处理与分析
|
||||
- **商品信息入库**:自动将获取的商品信息存储到数据库
|
||||
- **商品状态监控**:监控商品上下架状态变化
|
||||
- **价格变化追踪**:跟踪商品价格变化趋势
|
||||
- **销量统计**:统计商品浏览量和销售数据
|
||||
- **数据导出**:支持导出为Excel、CSV等格式
|
||||
|
||||
### 3. 界面优化
|
||||
- 商品信息表格显示
|
||||
- 商品图片预览
|
||||
- 筛选和搜索功能
|
||||
- **商品信息表格**:以表格形式展示商品详细信息
|
||||
- **商品图片预览**:显示商品主图和详情图
|
||||
- **高级筛选**:按价格、分类、状态等条件筛选
|
||||
- **搜索功能**:支持商品标题、描述的全文搜索
|
||||
- **排序功能**:按时间、价格、浏览量等排序
|
||||
|
||||
### 4. 智能分析
|
||||
- **商品分类统计**:自动分析商品分类分布
|
||||
- **价格区间分析**:分析不同价格区间的商品数量
|
||||
- **热门商品识别**:基于浏览量识别热门商品
|
||||
- **库存预警**:监控商品库存状态,及时预警
|
||||
|
||||
## 📊 数据统计
|
||||
|
||||
### 获取统计信息
|
||||
- **总商品数量**:账号下所有商品的总数
|
||||
- **在售商品数**:当前在售状态的商品数量
|
||||
- **已售出商品数**:已售出的商品数量
|
||||
- **平均价格**:所有商品的平均售价
|
||||
- **价格分布**:不同价格区间的商品分布
|
||||
|
||||
### 性能指标
|
||||
- **获取速度**:每秒获取的商品数量
|
||||
- **成功率**:获取成功的商品比例
|
||||
- **错误率**:获取失败的商品比例
|
||||
- **响应时间**:API响应时间统计
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 常见问题及解决方案
|
||||
|
||||
**问题1:获取失败**
|
||||
- 检查账号Cookie是否有效
|
||||
- 确认网络连接是否正常
|
||||
- 验证账号是否被限制
|
||||
- 查看详细错误日志
|
||||
|
||||
**问题2:获取速度慢**
|
||||
- 检查网络连接质量
|
||||
- 确认服务器负载情况
|
||||
- 优化获取策略
|
||||
- 考虑分批次获取
|
||||
|
||||
**问题3:数据不完整**
|
||||
- 检查API返回数据格式
|
||||
- 确认商品状态是否正常
|
||||
- 验证解析逻辑是否正确
|
||||
- 查看控制台错误信息
|
||||
|
||||
**问题4:Token频繁失效**
|
||||
- 检查Cookie有效期
|
||||
- 确认账号登录状态
|
||||
- 优化Token刷新策略
|
||||
- 考虑降低获取频率
|
||||
|
||||
### 调试技巧
|
||||
1. **开启详细日志**:查看完整的获取过程
|
||||
2. **单步测试**:先测试单个商品获取
|
||||
3. **网络监控**:监控网络请求和响应
|
||||
4. **数据验证**:验证获取数据的完整性
|
||||
|
||||
## 💡 使用建议
|
||||
|
||||
### 最佳实践
|
||||
1. **合理频率**:避免过于频繁的获取操作
|
||||
2. **错峰使用**:在网络较好的时段进行获取
|
||||
3. **数据备份**:定期备份重要的商品数据
|
||||
4. **监控告警**:设置获取失败的告警机制
|
||||
|
||||
### 性能优化
|
||||
1. **缓存机制**:缓存已获取的商品信息
|
||||
2. **增量更新**:只获取变更的商品信息
|
||||
3. **并发控制**:合理控制并发获取数量
|
||||
4. **资源管理**:及时释放不需要的资源
|
||||
|
||||
### 安全考虑
|
||||
1. **权限控制**:限制获取功能的使用权限
|
||||
2. **频率限制**:设置合理的获取频率限制
|
||||
3. **数据保护**:保护获取的商品数据安全
|
||||
4. **审计日志**:记录所有获取操作的审计日志
|
||||
|
||||
## 🔮 未来规划
|
||||
|
||||
### 即将推出的功能
|
||||
1. **商品同步**:支持与其他平台的商品信息同步
|
||||
2. **智能推荐**:基于商品数据的智能推荐算法
|
||||
3. **自动定价**:根据市场数据自动调整商品价格
|
||||
4. **竞品分析**:分析同类商品的价格和销量
|
||||
|
||||
### 长期发展方向
|
||||
1. **大数据分析**:基于海量商品数据的深度分析
|
||||
2. **机器学习**:使用AI技术优化商品运营策略
|
||||
3. **API开放**:提供开放API供第三方系统集成
|
||||
4. **移动端支持**:开发移动端应用,随时随地管理商品
|
||||
|
||||
---
|
||||
|
||||
🎉 **获取所有商品功能为商品管理提供了强大的数据获取能力,是商品分析和运营的重要工具!通过持续优化和功能扩展,将为用户提供更加完善的商品管理解决方案。**
|
||||
|
||||
---
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user