From 99266ea57913663f9880c512726c42cb7e5e7f28 Mon Sep 17 00:00:00 2001
From: yj <2077506045@qq.com>
Date: 星期一, 28 七月 2025 11:14:28 +0800
Subject: [PATCH] 新增忽略好友消息;删除多余文件

---
 .gitignore                            |    4 
 tests/test_message_processor.py       |   38 +
 app/api/friend_ignore.py              |  241 +++++++++
 tests/test_dify_streaming.py          |  174 ++++++
 README.md                             |   35 +
 logs/app.log                          |   13 
 /dev/null                             |   61 --
 app/services/friend_ignore_service.py |  259 +++++++++
 app/services/dify_client.py           |  310 +++++++++++
 app/services/contact_sync.py          |    6 
 config.py                             |   99 +--
 main.py                               |   17 
 tests/test_friend_ignore_service.py   |  258 +++++++++
 app/services/message_processor.py     |   11 
 14 files changed, 1,383 insertions(+), 143 deletions(-)

diff --git a/.env.example b/.env.example
deleted file mode 100644
index 97e357c..0000000
--- a/.env.example
+++ /dev/null
@@ -1,22 +0,0 @@
-# 鏁版嵁搴撻厤缃�
-DATABASE_URL=mysql+pymysql://username:password@localhost:3306/ecloud_dify
-
-# Redis閰嶇疆
-REDIS_URL=redis://localhost:6379/0
-
-# E浜戠瀹堕厤缃�
-ECLOUD_BASE_URL=http://your-ecloud-domain.com
-ECLOUD_AUTHORIZATION=your-authorization-token
-
-# DifyAI閰嶇疆
-DIFY_BASE_URL=https://api.dify.ai/v1
-DIFY_API_KEY=your-dify-api-key
-
-# 鏈嶅姟閰嶇疆
-SERVER_HOST=0.0.0.0
-SERVER_PORT=8000
-DEBUG=True
-
-# 鏃ュ織閰嶇疆
-LOG_LEVEL=INFO
-LOG_FILE=logs/app.log
diff --git a/.gitignore b/.gitignore
index 5b0371a..4bf7efb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 # 蹇界暐鎵�鏈� .log 鏂囦欢
 *.log
+logs/*.log
 
 # 蹇界暐鐗瑰畾鏂囦欢
 config.ini
@@ -31,4 +32,5 @@
 # 鍚屾椂蹇界暐鎵�鏈� .pyc 鏂囦欢锛堝彲閫変絾鎺ㄨ崘锛�
 *.pyc
 *.pyo
-*.pyd
\ No newline at end of file
+*.pyd
+
diff --git a/CONFIG_GUIDE.md b/CONFIG_GUIDE.md
deleted file mode 100644
index e7d48d4..0000000
--- a/CONFIG_GUIDE.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# 閰嶇疆鏂囦欢浣跨敤鎸囧崡
-
-## 姒傝堪
-
-椤圭洰宸蹭粠Python閰嶇疆鏂囦欢锛坈onfig.py锛夎浆鎹负JSON閰嶇疆鏂囦欢锛坈onfig.json锛夛紝杩欐牱鏇撮�傚悎鎵撳寘涓篹xe鏂囦欢銆�
-
-## 閰嶇疆鏂囦欢缁撴瀯
-
-### config.json
-```json
-{
-  "database": {
-    "url": "mysql+pymysql://root:password@host:port/database"
-  },
-  "redis": {
-    "url": "redis://localhost:6379/0"
-  },
-  "ecloud": {
-    "base_url": "http://125.122.152.142:9899",
-    "authorization": "your_authorization_token"
-  },
-  "dify": {
-    "base_url": "https://api.dify.ai/v1",
-    "api_key": "your_dify_api_key"
-  },
-  "server": {
-    "host": "0.0.0.0",
-    "port": 7979,
-    "debug": true
-  },
-  "logging": {
-    "level": "INFO",
-    "file": "logs/app.log"
-  },
-  "message_processing": {
-    "max_retry_count": 3,
-    "retry_delay": 5,
-    "queue_timeout": 300
-  }
-}
-```
-
-## 閰嶇疆椤硅鏄�
-
-### 鏁版嵁搴撻厤缃� (database)
-- `url`: 鏁版嵁搴撹繛鎺ュ瓧绗︿覆锛屾敮鎸丮ySQL
-
-### Redis閰嶇疆 (redis)
-- `url`: Redis杩炴帴瀛楃涓�
-
-### E浜戠瀹堕厤缃� (ecloud)
-- `base_url`: E浜戠瀹禔PI鍩虹URL
-- `authorization`: E浜戠瀹禔PI鎺堟潈浠ょ墝
-
-### DifyAI閰嶇疆 (dify)
-- `base_url`: DifyAI API鍩虹URL
-- `api_key`: DifyAI API瀵嗛挜
-
-### 鏈嶅姟鍣ㄩ厤缃� (server)
-- `host`: 鏈嶅姟鍣ㄧ洃鍚湴鍧�
-- `port`: 鏈嶅姟鍣ㄧ洃鍚鍙�
-- `debug`: 鏄惁鍚敤璋冭瘯妯″紡
-
-### 鏃ュ織閰嶇疆 (logging)
-- `level`: 鏃ュ織绾у埆 (DEBUG, INFO, WARNING, ERROR)
-- `file`: 鏃ュ織鏂囦欢璺緞
-
-### 娑堟伅澶勭悊閰嶇疆 (message_processing)
-- `max_retry_count`: 鏈�澶ч噸璇曟鏁�
-- `retry_delay`: 閲嶈瘯寤惰繜鏃堕棿锛堢锛�
-- `queue_timeout`: 闃熷垪瓒呮椂鏃堕棿锛堢锛�
-
-## 浣跨敤鏂规硶
-
-### 1. 淇敼閰嶇疆
-鐩存帴缂栬緫 `config.json` 鏂囦欢鍗冲彲锛屽簲鐢ㄤ細鑷姩鍔犺浇鏂伴厤缃��
-
-### 2. 閰嶇疆鏂囦欢浣嶇疆
-- 寮�鍙戠幆澧冿細椤圭洰鏍圭洰褰曚笅鐨� `config.json`
-- 鐢熶骇鐜锛歟xe鏂囦欢鍚岀洰褰曚笅鐨� `config.json`
-
-### 3. 閰嶇疆楠岃瘉
-濡傛灉閰嶇疆鏂囦欢涓嶅瓨鍦ㄦ垨鏍煎紡閿欒锛岀郴缁熶細浣跨敤榛樿閰嶇疆骞惰緭鍑洪敊璇俊鎭��
-
-## 鍏煎鎬�
-
-- 鍘熸湁鐨� `from config import settings` 瀵煎叆鏂瑰紡淇濇寔涓嶅彉
-- 鎵�鏈夐厤缃睘鎬х殑璁块棶鏂瑰紡淇濇寔涓嶅彉锛堝 `settings.database_url`锛�
-- 鍚戝悗鍏煎锛屼笉闇�瑕佷慨鏀圭幇鏈変唬鐮�
-
-## 鎵撳寘涓篹xe鐨勪紭鍔�
-
-1. **閰嶇疆澶栭儴鍖�**: 閰嶇疆鏂囦欢鐙珛浜巈xe鏂囦欢锛屼究浜庨儴缃叉椂淇敼
-2. **鏃犻渶閲嶆柊缂栬瘧**: 淇敼閰嶇疆涓嶉渶瑕侀噸鏂版墦鍖卐xe
-3. **鏄撲簬缁存姢**: JSON鏍煎紡鐩磋鏄撹锛屼究浜庤繍缁翠汉鍛橀厤缃�
-4. **鐗堟湰鎺у埗鍙嬪ソ**: 鍙互涓轰笉鍚岀幆澧冨噯澶囦笉鍚岀殑閰嶇疆鏂囦欢
-
-## 娉ㄦ剰浜嬮」
-
-1. 纭繚 `config.json` 鏂囦欢鏍煎紡姝g‘锛屽彲浠ヤ娇鐢↗SON楠岃瘉宸ュ叿妫�鏌�
-2. 鏁忔劅淇℃伅锛堝鏁版嵁搴撳瘑鐮併�丄PI瀵嗛挜锛夊簲濡ュ杽淇濈
-3. 鐢熶骇鐜寤鸿灏� `debug` 璁剧疆涓� `false`
-4. 鏃ュ織鏂囦欢璺緞纭繚搴旂敤鏈夊啓鍏ユ潈闄�
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
deleted file mode 100644
index 8fcd1d5..0000000
--- a/DEPLOYMENT.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# 閮ㄧ讲鎸囧崡
-
-## 閮ㄧ讲鍓嶆鏌ユ竻鍗�
-
-### 1. 鐜瑕佹眰
-- [ ] Python 3.11+ 宸插畨瑁�
-- [ ] Docker 鍜� Docker Compose 宸插畨瑁咃紙鎺ㄨ崘锛�
-- [ ] MySQL 8.0+ 鍙敤
-- [ ] Redis 7+ 鍙敤
-- [ ] 缃戠粶绔彛 8000 鍙闂�
-
-### 2. 閰嶇疆鏂囦欢
-- [ ] 澶嶅埗 `.env.example` 涓� `.env`
-- [ ] 閰嶇疆鏁版嵁搴撹繛鎺ヤ俊鎭�
-- [ ] 閰嶇疆Redis杩炴帴淇℃伅
-- [ ] 閰嶇疆E浜戠瀹禔PI淇℃伅
-- [ ] 閰嶇疆DifyAI API瀵嗛挜
-
-### 3. 蹇呰鐨凙PI淇℃伅
-- [ ] E浜戠瀹跺煙鍚嶅拰鎺堟潈token
-- [ ] DifyAI API瀵嗛挜
-- [ ] 纭E浜戠瀹跺洖璋僓RL閰嶇疆
-
-## 蹇�熼儴缃诧紙Docker鏂瑰紡锛�
-
-### 1. 鍏嬮殕椤圭洰
-```bash
-git clone <repository-url>
-cd ECloud
-```
-
-### 2. 閰嶇疆鐜鍙橀噺
-```bash
-cp .env.example .env
-# 缂栬緫 .env 鏂囦欢锛屽~鍏ユ纭殑閰嶇疆淇℃伅
-```
-
-### 3. 鍚姩鏈嶅姟
-```bash
-# 鍚姩鎵�鏈夋湇鍔★紙MySQL銆丷edis銆佸簲鐢級
-docker-compose up -d
-
-# 鏌ョ湅鍚姩鏃ュ織
-docker-compose logs -f app
-```
-
-### 4. 楠岃瘉閮ㄧ讲
-```bash
-# 杩愯闆嗘垚娴嬭瘯
-python test_integration.py
-
-# 妫�鏌ユ湇鍔$姸鎬�
-curl http://localhost:8000/api/v1/health
-```
-
-## 鎵嬪姩閮ㄧ讲
-
-### 1. 瀹夎渚濊禆
-```bash
-# 鍒涘缓铏氭嫙鐜
-python3 -m venv venv
-source venv/bin/activate
-
-# 瀹夎渚濊禆
-pip install -r requirements.txt
-```
-
-### 2. 鍚姩鏁版嵁搴撴湇鍔�
-```bash
-# 浣跨敤Docker鍚姩MySQL鍜孯edis
-docker-compose up -d mysql redis
-
-# 鎴栬�呬娇鐢ㄧ郴缁熸湇鍔�
-sudo systemctl start mysql
-sudo systemctl start redis
-```
-
-### 3. 鍒濆鍖栨暟鎹簱
-```bash
-python app/utils/database_init.py
-```
-
-### 4. 鍚姩搴旂敤
-```bash
-# 浣跨敤鍚姩鑴氭湰
-chmod +x start.sh
-./start.sh
-
-# 鎴栬�呯洿鎺ュ惎鍔�
-python main.py
-```
-
-## 鐢熶骇鐜閮ㄧ讲
-
-### 1. 浣跨敤杩涚▼绠$悊鍣�
-```bash
-# 瀹夎supervisor
-sudo apt-get install supervisor
-
-# 鍒涘缓閰嶇疆鏂囦欢 /etc/supervisor/conf.d/ecloud-dify.conf
-[program:ecloud-dify]
-command=/path/to/venv/bin/python /path/to/main.py
-directory=/path/to/project
-user=www-data
-autostart=true
-autorestart=true
-redirect_stderr=true
-stdout_logfile=/var/log/ecloud-dify.log
-```
-
-### 2. 浣跨敤Nginx鍙嶅悜浠g悊
-```nginx
-server {
-    listen 80;
-    server_name your-domain.com;
-    
-    location / {
-        proxy_pass http://127.0.0.1:8000;
-        proxy_set_header Host $host;
-        proxy_set_header X-Real-IP $remote_addr;
-        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header X-Forwarded-Proto $scheme;
-    }
-}
-```
-
-### 3. 閰嶇疆HTTPS
-```bash
-# 浣跨敤Let's Encrypt
-sudo certbot --nginx -d your-domain.com
-```
-
-## 鐩戞帶鍜岀淮鎶�
-
-### 1. 鏃ュ織鐩戞帶
-```bash
-# 鏌ョ湅搴旂敤鏃ュ織
-tail -f logs/app.log
-
-# 鏌ョ湅閿欒鏃ュ織
-tail -f logs/app_error.log
-
-# 浣跨敤Docker鏌ョ湅鏃ュ織
-docker-compose logs -f app
-```
-
-### 2. 鎬ц兘鐩戞帶
-```bash
-# 鏌ョ湅Redis闃熷垪鐘舵��
-redis-cli
-> KEYS ecloud_queue:*
-> LLEN ecloud_queue:鐢ㄦ埛ID
-
-# 鏌ョ湅鏁版嵁搴撹繛鎺�
-mysql -u ecloud -p ecloud_dify
-> SHOW PROCESSLIST;
-```
-
-### 3. 鍋ュ悍妫�鏌�
-```bash
-# API鍋ュ悍妫�鏌�
-curl http://localhost:8000/api/v1/health
-
-# 鏁版嵁搴撳仴搴锋鏌�
-python -c "from app.utils.database_init import check_database_health; print(check_database_health())"
-```
-
-## 鏁呴殰鎺掗櫎
-
-### 甯歌闂
-
-1. **绔彛琚崰鐢�**
-   ```bash
-   # 鏌ョ湅绔彛鍗犵敤
-   netstat -tlnp | grep 8000
-   
-   # 淇敼閰嶇疆鏂囦欢涓殑绔彛
-   vim .env
-   ```
-
-2. **鏁版嵁搴撹繛鎺ュけ璐�**
-   ```bash
-   # 妫�鏌ySQL鏈嶅姟鐘舵��
-   sudo systemctl status mysql
-   
-   # 妫�鏌ヨ繛鎺ラ厤缃�
-   mysql -h localhost -u ecloud -p
-   ```
-
-3. **Redis杩炴帴澶辫触**
-   ```bash
-   # 妫�鏌edis鏈嶅姟鐘舵��
-   sudo systemctl status redis
-   
-   # 娴嬭瘯杩炴帴
-   redis-cli ping
-   ```
-
-4. **DifyAI API璋冪敤澶辫触**
-   - 妫�鏌PI瀵嗛挜鏄惁姝g‘
-   - 纭缃戠粶杩炴帴
-   - 鏌ョ湅API閰嶉闄愬埗
-
-### 鏃ュ織鍒嗘瀽
-
-閲嶈鏃ュ織鍏抽敭璇嶏細
-- `ERROR`: 閿欒淇℃伅
-- `娑堟伅澶勭悊澶辫触`: 娑堟伅澶勭悊寮傚父
-- `鏁版嵁搴撹繛鎺ュけ璐: 鏁版嵁搴撻棶棰�
-- `Dify鍝嶅簲澶辫触`: DifyAI API闂
-- `鍙戦�佹枃鏈秷鎭け璐: E浜戠瀹禔PI闂
-
-## 澶囦唤鍜屾仮澶�
-
-### 鏁版嵁搴撳浠�
-```bash
-# 澶囦唤鏁版嵁搴�
-mysqldump -u ecloud -p ecloud_dify > backup_$(date +%Y%m%d).sql
-
-# 鎭㈠鏁版嵁搴�
-mysql -u ecloud -p ecloud_dify < backup_20231201.sql
-```
-
-### Redis鏁版嵁澶囦唤
-```bash
-# Redis浼氳嚜鍔ㄤ繚瀛樺埌dump.rdb鏂囦欢
-cp /var/lib/redis/dump.rdb backup_redis_$(date +%Y%m%d).rdb
-```
-
-## 鎵╁睍鍜屼紭鍖�
-
-### 1. 姘村钩鎵╁睍
-- 浣跨敤澶氫釜搴旂敤瀹炰緥
-- 閰嶇疆璐熻浇鍧囪 鍣�
-- 浣跨敤Redis闆嗙兢
-
-### 2. 鎬ц兘浼樺寲
-- 璋冩暣鏁版嵁搴撹繛鎺ユ睜澶у皬
-- 浼樺寲Redis闃熷垪澶勭悊
-- 浣跨敤缂撳瓨鍑忓皯API璋冪敤
-
-### 3. 瀹夊叏鍔犲浐
-- 閰嶇疆闃茬伀澧欒鍒�
-- 浣跨敤HTTPS
-- 瀹氭湡鏇存柊渚濊禆鍖�
-- 閰嶇疆璁块棶鎺у埗
diff --git a/DEPLOYMENT_WINDOWS.md b/DEPLOYMENT_WINDOWS.md
deleted file mode 100644
index 797b74d..0000000
--- a/DEPLOYMENT_WINDOWS.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 Windows閮ㄧ讲鎸囧崡
-
-## 姒傝堪
-
-鏈枃妗d粙缁嶅浣曞湪Windows鏈嶅姟鍣ㄤ笂閮ㄧ讲E浜戠瀹�-DifyAI瀵规帴鏈嶅姟鐨別xe鐗堟湰銆�
-
-## 绯荤粺瑕佹眰
-
-- Windows Server 2016 鎴栨洿楂樼増鏈�
-- Windows 10/11 (鐢ㄤ簬娴嬭瘯)
-- 鑷冲皯 2GB 鍙敤鍐呭瓨
-- 鑷冲皯 1GB 鍙敤纾佺洏绌洪棿
-- 缃戠粶杩炴帴锛堢敤浜庤闂暟鎹簱銆丷edis銆丒浜戠瀹跺拰DifyAI鏈嶅姟锛�
-
-## 閮ㄧ讲姝ラ
-
-### 1. 鍑嗗閮ㄧ讲鏂囦欢
-
-纭繚浠ヤ笅鏂囦欢瀛樺湪浜庨儴缃茬洰褰曚腑锛�
-- `ecloud_dify.exe` - 涓荤▼搴忔枃浠�
-- `config.production.json` - 鐢熶骇鐜閰嶇疆妯℃澘
-- `config.example.json` - 閰嶇疆绀轰緥鏂囦欢
-- `install_service.bat` - Windows鏈嶅姟瀹夎鑴氭湰
-- `uninstall_service.bat` - Windows鏈嶅姟鍗歌浇鑴氭湰
-- `start.bat` - 鐩存帴鍚姩鑴氭湰锛堥潪鏈嶅姟妯″紡锛�
-
-### 2. 閰嶇疆鏈嶅姟
-
-#### 2.1 鍒涘缓閰嶇疆鏂囦欢
-
-1. 澶嶅埗 `config.production.json` 涓� `config.json`
-2. 缂栬緫 `config.json` 鏂囦欢锛屼慨鏀逛互涓嬮厤缃細
-
-```json
-{
-  "database": {
-    "url": "mysql+pymysql://鐢ㄦ埛鍚�:瀵嗙爜@鏁版嵁搴撳湴鍧�:绔彛/鏁版嵁搴撳悕"
-  },
-  "redis": {
-    "url": "redis://Redis鍦板潃:绔彛/鏁版嵁搴撶紪鍙�"
-  },
-  "ecloud": {
-    "base_url": "E浜戠瀹禔PI鍦板潃",
-    "authorization": "E浜戠瀹舵巿鏉冧护鐗�"
-  },
-  "dify": {
-    "base_url": "DifyAI API鍦板潃",
-    "api_key": "DifyAI API瀵嗛挜"
-  },
-  "server": {
-    "host": "0.0.0.0",
-    "port": 7979,
-    "debug": false
-  }
-}
-```
-
-#### 2.2 閰嶇疆璇存槑
-
-- **database.url**: MySQL鏁版嵁搴撹繛鎺ュ瓧绗︿覆
-- **redis.url**: Redis鏈嶅姟鍣ㄨ繛鎺ュ瓧绗︿覆
-- **ecloud**: E浜戠瀹剁浉鍏抽厤缃�
-- **dify**: DifyAI鐩稿叧閰嶇疆
-- **server**: 鏈嶅姟鍣ㄧ洃鍚厤缃�
-
-### 3. 瀹夎鍜屽惎鍔ㄦ湇鍔�
-
-#### 鏂瑰紡涓�锛歐indows鏈嶅姟妯″紡锛堟帹鑽愶級
-
-1. **瀹夎鏈嶅姟**锛�
-   - 鍙抽敭鐐瑰嚮 `install_service.bat`
-   - 閫夋嫨"浠ョ鐞嗗憳韬唤杩愯"
-   - 鎸夌収鎻愮ず瀹屾垚瀹夎
-
-2. **绠$悊鏈嶅姟**锛�
-   ```cmd
-   # 鍚姩鏈嶅姟
-   sc start ECloudDifyService
-   
-   # 鍋滄鏈嶅姟
-   sc stop ECloudDifyService
-   
-   # 鏌ョ湅鏈嶅姟鐘舵��
-   sc query ECloudDifyService
-   
-   # 鍒犻櫎鏈嶅姟
-   sc delete ECloudDifyService
-   ```
-
-3. **鍗歌浇鏈嶅姟**锛�
-   - 鍙抽敭鐐瑰嚮 `uninstall_service.bat`
-   - 閫夋嫨"浠ョ鐞嗗憳韬唤杩愯"
-
-#### 鏂瑰紡浜岋細鐩存帴杩愯妯″紡
-
-1. 鍙屽嚮 `start.bat` 鍚姩鏈嶅姟
-2. 鎸� Ctrl+C 鍋滄鏈嶅姟
-
-### 4. 楠岃瘉閮ㄧ讲
-
-1. **妫�鏌ユ湇鍔$姸鎬�**锛�
-   - 璁块棶 `http://鏈嶅姟鍣↖P:7979/health`
-   - 搴旇杩斿洖鍋ュ悍妫�鏌ヤ俊鎭�
-
-2. **鏌ョ湅鏃ュ織**锛�
-   - 鏃ュ織鏂囦欢浣嶄簬 `logs/app.log`
-   - 妫�鏌ユ槸鍚︽湁閿欒淇℃伅
-
-3. **娴嬭瘯API**锛�
-   - 璁块棶 `http://鏈嶅姟鍣↖P:7979/`
-   - 搴旇杩斿洖鏈嶅姟淇℃伅
-
-## 闃茬伀澧欓厤缃�
-
-纭繚Windows闃茬伀澧欏厑璁哥鍙�7979鐨勫叆绔欒繛鎺ワ細
-
-```cmd
-# 娣诲姞闃茬伀澧欒鍒�
-netsh advfirewall firewall add rule name="ECloudDify Service" dir=in action=allow protocol=TCP localport=7979
-```
-
-## 鏁呴殰鎺掗櫎
-
-### 甯歌闂
-
-1. **鏈嶅姟鍚姩澶辫触**锛�
-   - 妫�鏌ラ厤缃枃浠舵牸寮忔槸鍚︽纭�
-   - 纭鏁版嵁搴撳拰Redis杩炴帴鏄惁姝e父
-   - 鏌ョ湅鏃ュ織鏂囦欢鑾峰彇璇︾粏閿欒淇℃伅
-
-2. **绔彛琚崰鐢�**锛�
-   - 淇敼閰嶇疆鏂囦欢涓殑绔彛鍙�
-   - 鎴栬�呭仠姝㈠崰鐢ㄧ鍙g殑鍏朵粬绋嬪簭
-
-3. **鏉冮檺闂**锛�
-   - 纭繚浠ョ鐞嗗憳韬唤杩愯瀹夎鑴氭湰
-   - 妫�鏌xe鏂囦欢鐨勬墽琛屾潈闄�
-
-4. **缃戠粶杩炴帴闂**锛�
-   - 妫�鏌ラ槻鐏璁剧疆
-   - 纭缃戠粶杩炴帴姝e父
-
-### 鏃ュ織鏌ョ湅
-
-鏃ュ織鏂囦欢浣嶇疆锛歚logs/app.log`
-
-甯哥敤鏃ュ織绾у埆锛�
-- INFO: 涓�鑸俊鎭�
-- WARNING: 璀﹀憡淇℃伅
-- ERROR: 閿欒淇℃伅
-
-## 鎬ц兘浼樺寲
-
-1. **鍐呭瓨浼樺寲**锛�
-   - 鐩戞帶鍐呭瓨浣跨敤鎯呭喌
-   - 蹇呰鏃惰皟鏁寸郴缁熷唴瀛樺垎閰�
-
-2. **缃戠粶浼樺寲**锛�
-   - 纭繚缃戠粶寤惰繜杈冧綆
-   - 浣跨敤楂橀�熺綉缁滆繛鎺�
-
-3. **鏁版嵁搴撲紭鍖�**锛�
-   - 浼樺寲鏁版嵁搴撹繛鎺ユ睜璁剧疆
-   - 瀹氭湡娓呯悊鏃ュ織鏁版嵁
-
-## 瀹夊叏寤鸿
-
-1. **閰嶇疆鏂囦欢瀹夊叏**锛�
-   - 淇濇姢閰嶇疆鏂囦欢涓殑鏁忔劅淇℃伅
-   - 璁剧疆閫傚綋鐨勬枃浠舵潈闄�
-
-2. **缃戠粶瀹夊叏**锛�
-   - 浣跨敤HTTPS锛堝鏋滄敮鎸侊級
-   - 闄愬埗璁块棶IP鑼冨洿
-
-3. **瀹氭湡鏇存柊**锛�
-   - 瀹氭湡鏇存柊鏈嶅姟鐗堟湰
-   - 鍏虫敞瀹夊叏琛ヤ竵
-
-## 鑱旂郴鏀寔
-
-濡傞亣鍒伴棶棰橈紝璇锋彁渚涗互涓嬩俊鎭細
-- 閿欒鏃ュ織鍐呭
-- 閰嶇疆鏂囦欢锛堥殣钘忔晱鎰熶俊鎭級
-- 绯荤粺鐜淇℃伅
-- 闂澶嶇幇姝ラ
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index b820892..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,29 +0,0 @@
-FROM python:3.11-slim
-
-# 璁剧疆宸ヤ綔鐩綍
-WORKDIR /app
-
-# 瀹夎绯荤粺渚濊禆
-RUN apt-get update && apt-get install -y \
-    gcc \
-    default-libmysqlclient-dev \
-    pkg-config \
-    && rm -rf /var/lib/apt/lists/*
-
-# 澶嶅埗渚濊禆鏂囦欢
-COPY requirements.txt .
-
-# 瀹夎Python渚濊禆
-RUN pip install --no-cache-dir -r requirements.txt
-
-# 澶嶅埗搴旂敤浠g爜
-COPY . .
-
-# 鍒涘缓鏃ュ織鐩綍
-RUN mkdir -p logs
-
-# 鏆撮湶绔彛
-EXPOSE 8000
-
-# 鍚姩鍛戒护
-CMD ["python", "main.py"]
diff --git a/README.md b/README.md
index 9170a7a..e8f8ccc 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,9 @@
 - 杩囨护鍜岄獙璇佹秷鎭紙浠呭鐞嗙兢鑱婃秷鎭紝蹇界暐鑷繁鍙戦�佺殑娑堟伅锛�
 - 浣跨敤Redis闃熷垪绠$悊鐢ㄦ埛娑堟伅锛岄槻姝㈠苟鍙戝鐞�
 - 鑷姩鑾峰彇鍜屼繚瀛樿仈绯讳汉淇℃伅
-- 璋冪敤DifyAI鎺ュ彛鑾峰彇AI鍥炵瓟
+- **鏀寔DifyAI娴佸紡鍜岄樆濉炰袱绉嶆ā寮�**
+  - 娴佸紡妯″紡锛氬疄鏃舵帴鏀禔I鍥炲锛屽搷搴旀洿蹇�
+  - 闃诲妯″紡锛氱瓑寰呭畬鏁村洖澶嶅悗杩斿洖锛屾洿绋冲畾
 - 灏咥I鍥炵瓟鍙戦�佸洖缇よ亰
 - 瀹屾暣鐨勬棩蹇楄褰曞拰閿欒澶勭悊
 
@@ -154,11 +156,34 @@
 
 ## 閰嶇疆璇存槑
 
-涓昏閰嶇疆椤瑰湪 `config.py` 涓細
+涓昏閰嶇疆椤瑰湪 `config.json` 涓細
 
-- `max_retry_count`: 鏈�澶ч噸璇曟鏁帮紙榛樿3娆★級
-- `retry_delay`: 閲嶈瘯寤惰繜锛堥粯璁�5绉掞級
-- `queue_timeout`: 闃熷垪瓒呮椂鏃堕棿锛堥粯璁�300绉掞級
+### DifyAI閰嶇疆
+- `dify.streaming_enabled`: 鏄惁鍚敤娴佸紡妯″紡锛堥粯璁わ細false锛�
+- `dify.streaming_timeout`: 娴佸紡璇锋眰瓒呮椂鏃堕棿锛屽崟浣嶇锛堥粯璁わ細120锛�
+- `dify.base_url`: DifyAI API鍦板潃
+- `dify.api_key`: DifyAI API瀵嗛挜
+
+### 娑堟伅澶勭悊閰嶇疆
+- `message_processing.max_retry_count`: 鏈�澶ч噸璇曟鏁帮紙榛樿3娆★級
+- `message_processing.retry_delay`: 閲嶈瘯寤惰繜锛堥粯璁�5绉掞級
+- `message_processing.queue_timeout`: 闃熷垪瓒呮椂鏃堕棿锛堥粯璁�300绉掞級
+
+### 娴佸紡妯″紡璇存槑
+**鍚敤娴佸紡妯″紡鐨勪紭鍔匡細**
+- 瀹炴椂鍝嶅簲锛氳竟鐢熸垚杈硅繑鍥烇紝鐢ㄦ埛浣撻獙鏇村ソ
+- 鏇村揩鎰熺煡锛氭棤闇�绛夊緟瀹屾暣鍥炲鍗冲彲寮�濮嬪鐞�
+- 杩炴帴淇濇椿锛氳嚜鍔ㄥ鐞唒ing浜嬩欢锛屼繚鎸佽繛鎺ョǔ瀹�
+
+**閰嶇疆绀轰緥锛�**
+```json
+{
+  "dify": {
+    "streaming_enabled": true,
+    "streaming_timeout": 180
+  }
+}
+```
 
 ## 鏃ュ織绠$悊
 
diff --git a/app/api/friend_ignore.py b/app/api/friend_ignore.py
new file mode 100644
index 0000000..4f8c68a
--- /dev/null
+++ b/app/api/friend_ignore.py
@@ -0,0 +1,241 @@
+"""
+濂藉弸蹇界暐鍒楄〃绠$悊API
+"""
+
+from fastapi import APIRouter, HTTPException
+from pydantic import BaseModel
+from typing import List, Set
+from loguru import logger
+
+from app.services.friend_ignore_service import friend_ignore_service
+from app.services.contact_sync import contact_sync_service
+from config import settings
+
+
+router = APIRouter()
+
+
+class AddFriendsRequest(BaseModel):
+    """娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄨ姹傛ā鍨�"""
+    friends: List[str]
+
+
+class RemoveFriendRequest(BaseModel):
+    """浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙嬭姹傛ā鍨�"""
+    w_id: str
+
+
+class IgnoreListResponse(BaseModel):
+    """蹇界暐鍒楄〃鍝嶅簲妯″瀷"""
+    success: bool
+    message: str
+    data: Set[str] = None
+    count: int = 0
+
+
+@router.get("/ignore-list", response_model=IgnoreListResponse)
+async def get_ignore_list():
+    """
+    鑾峰彇褰撳墠鐨勫ソ鍙嬪拷鐣ュ垪琛�
+    
+    Returns:
+        蹇界暐鍒楄〃鍝嶅簲
+    """
+    try:
+        ignore_list = friend_ignore_service.get_ignore_list()
+        count = friend_ignore_service.get_ignore_list_count()
+        
+        return IgnoreListResponse(
+            success=True,
+            message="鑾峰彇蹇界暐鍒楄〃鎴愬姛",
+            data=ignore_list,
+            count=count
+        )
+    except Exception as e:
+        logger.error(f"鑾峰彇蹇界暐鍒楄〃澶辫触: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"鑾峰彇蹇界暐鍒楄〃澶辫触: {str(e)}")
+
+
+@router.post("/ignore-list/add", response_model=IgnoreListResponse)
+async def add_friends_to_ignore_list(request: AddFriendsRequest):
+    """
+    娣诲姞濂藉弸鍒板拷鐣ュ垪琛�
+    
+    Args:
+        request: 娣诲姞濂藉弸璇锋眰
+        
+    Returns:
+        鎿嶄綔缁撴灉
+    """
+    try:
+        success = friend_ignore_service.add_friends_to_ignore_list(request.friends)
+        
+        if success:
+            count = friend_ignore_service.get_ignore_list_count()
+            return IgnoreListResponse(
+                success=True,
+                message=f"鎴愬姛娣诲姞 {len(request.friends)} 涓ソ鍙嬪埌蹇界暐鍒楄〃",
+                count=count
+            )
+        else:
+            raise HTTPException(status_code=400, detail="娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄥけ璐�")
+            
+    except Exception as e:
+        logger.error(f"娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄥけ璐�: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄥけ璐�: {str(e)}")
+
+
+@router.post("/ignore-list/remove", response_model=IgnoreListResponse)
+async def remove_friend_from_ignore_list(request: RemoveFriendRequest):
+    """
+    浠庡拷鐣ュ垪琛ㄤ腑绉婚櫎濂藉弸
+    
+    Args:
+        request: 绉婚櫎濂藉弸璇锋眰
+        
+    Returns:
+        鎿嶄綔缁撴灉
+    """
+    try:
+        success = friend_ignore_service.remove_friend_from_ignore_list(request.w_id)
+        
+        if success:
+            count = friend_ignore_service.get_ignore_list_count()
+            return IgnoreListResponse(
+                success=True,
+                message=f"鎴愬姛浠庡拷鐣ュ垪琛ㄤ腑绉婚櫎濂藉弸: {request.w_id}",
+                count=count
+            )
+        else:
+            raise HTTPException(status_code=400, detail="浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙嬪け璐�")
+            
+    except Exception as e:
+        logger.error(f"浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙嬪け璐�: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙嬪け璐�: {str(e)}")
+
+
+@router.delete("/ignore-list", response_model=IgnoreListResponse)
+async def clear_ignore_list():
+    """
+    娓呯┖蹇界暐鍒楄〃
+    
+    Returns:
+        鎿嶄綔缁撴灉
+    """
+    try:
+        success = friend_ignore_service.clear_ignore_list()
+        
+        if success:
+            return IgnoreListResponse(
+                success=True,
+                message="鎴愬姛娓呯┖蹇界暐鍒楄〃",
+                count=0
+            )
+        else:
+            raise HTTPException(status_code=400, detail="娓呯┖蹇界暐鍒楄〃澶辫触")
+            
+    except Exception as e:
+        logger.error(f"娓呯┖蹇界暐鍒楄〃澶辫触: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"娓呯┖蹇界暐鍒楄〃澶辫触: {str(e)}")
+
+
+@router.post("/sync-contacts", response_model=IgnoreListResponse)
+async def sync_contacts_and_rebuild_ignore_list():
+    """
+    閲嶆柊鍚屾鑱旂郴浜哄苟閲嶅缓蹇界暐鍒楄〃
+    
+    Returns:
+        鎿嶄綔缁撴灉
+    """
+    try:
+        if not settings.ecloud_w_id:
+            raise HTTPException(status_code=400, detail="鏈厤缃甧cloud_w_id")
+            
+        success = contact_sync_service.sync_contacts_on_startup(settings.ecloud_w_id)
+        
+        if success:
+            count = friend_ignore_service.get_ignore_list_count()
+            return IgnoreListResponse(
+                success=True,
+                message="鑱旂郴浜哄悓姝ュ畬鎴愶紝蹇界暐鍒楄〃宸查噸寤�",
+                count=count
+            )
+        else:
+            raise HTTPException(status_code=400, detail="鑱旂郴浜哄悓姝ュけ璐�")
+            
+    except Exception as e:
+        logger.error(f"鑱旂郴浜哄悓姝ュけ璐�: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"鑱旂郴浜哄悓姝ュけ璐�: {str(e)}")
+
+
+@router.get("/ignore-list/check/{w_id}")
+async def check_if_friend_ignored(w_id: str):
+    """
+    妫�鏌ユ寚瀹歸_id鐨勮缁嗗拷鐣ョ姸鎬�
+
+    Args:
+        w_id: 瑕佹鏌ョ殑w_id
+
+    Returns:
+        璇︾粏鐨勬鏌ョ粨鏋�
+    """
+    try:
+        status_info = friend_ignore_service.get_ignore_status_info(w_id)
+
+        return {
+            "success": True,
+            "data": status_info,
+            "message": f"w_id {w_id} 鐘舵�佹鏌ュ畬鎴�"
+        }
+
+    except Exception as e:
+        logger.error(f"妫�鏌ュ拷鐣ョ姸鎬佸け璐�: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"妫�鏌ュ拷鐣ョ姸鎬佸け璐�: {str(e)}")
+
+
+@router.get("/whitelist")
+async def get_whitelist():
+    """
+    鑾峰彇褰撳墠鐨勭櫧鍚嶅崟鍒楄〃
+
+    Returns:
+        鐧藉悕鍗曞垪琛�
+    """
+    try:
+        whitelist = friend_ignore_service.get_whitelist()
+
+        return {
+            "success": True,
+            "data": whitelist,
+            "count": len(whitelist),
+            "message": "鑾峰彇鐧藉悕鍗曟垚鍔�"
+        }
+
+    except Exception as e:
+        logger.error(f"鑾峰彇鐧藉悕鍗曞け璐�: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"鑾峰彇鐧藉悕鍗曞け璐�: {str(e)}")
+
+
+@router.get("/config")
+async def get_ignore_config():
+    """
+    鑾峰彇濂藉弸蹇界暐鍔熻兘鐨勯厤缃俊鎭�
+
+    Returns:
+        閰嶇疆淇℃伅
+    """
+    try:
+        return {
+            "success": True,
+            "data": {
+                "ignore_enabled": settings.friend_ignore_enabled,
+                "whitelist": settings.friend_ignore_whitelist,
+                "whitelist_count": len(settings.friend_ignore_whitelist),
+                "ignore_list_count": friend_ignore_service.get_ignore_list_count()
+            },
+            "message": "鑾峰彇閰嶇疆淇℃伅鎴愬姛"
+        }
+
+    except Exception as e:
+        logger.error(f"鑾峰彇閰嶇疆淇℃伅澶辫触: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"鑾峰彇閰嶇疆淇℃伅澶辫触: {str(e)}")
diff --git a/app/services/contact_sync.py b/app/services/contact_sync.py
index 42d4acd..a775333 100644
--- a/app/services/contact_sync.py
+++ b/app/services/contact_sync.py
@@ -8,6 +8,7 @@
 from app.models.contact import Contact
 from app.models.database import get_db
 from app.services.ecloud_client import ecloud_client
+from app.services.friend_ignore_service import friend_ignore_service
 
 
 class ContactSyncService:
@@ -48,7 +49,10 @@
 
             logger.info(f"鑾峰彇鍒板ソ鍙嬪垪琛�: wId={w_id}, count={len(friends)}")
 
-            # 4. 鎵归噺鑾峰彇鑱旂郴浜鸿缁嗕俊鎭�
+            # 4. 灏嗗ソ鍙媤_id娣诲姞鍒癛edis蹇界暐鍒楄〃
+            friend_ignore_service.add_friends_to_ignore_list(friends)
+
+            # 5. 鎵归噺鑾峰彇鑱旂郴浜鸿缁嗕俊鎭�
             return self._batch_sync_contacts(w_id, friends)
 
         except Exception as e:
diff --git a/app/services/dify_client.py b/app/services/dify_client.py
index f029c8e..e7ab10e 100644
--- a/app/services/dify_client.py
+++ b/app/services/dify_client.py
@@ -4,6 +4,7 @@
 
 import requests
 import time
+import json
 from typing import List, Dict, Optional, Any
 from loguru import logger
 from config import settings
@@ -98,6 +99,315 @@
         )
         return None
 
+    def send_chat_message_stream(
+        self,
+        query: str,
+        user: str,
+        conversation_id: Optional[str] = None,
+        max_retries: int = None,
+    ) -> Optional[Dict[str, Any]]:
+        """
+        鍙戦�佸璇濇秷鎭紙娴佸紡妯″紡锛�
+
+        Args:
+            query: 鐢ㄦ埛杈撳叆/鎻愰棶鍐呭
+            user: 鐢ㄦ埛鏍囪瘑
+            conversation_id: 浼氳瘽ID锛堝彲閫夛級
+            max_retries: 鏈�澶ч噸璇曟鏁�
+
+        Returns:
+            瀹屾暣鐨勫搷搴旀暟鎹瓧鍏革紝澶辫触杩斿洖None
+        """
+        if max_retries is None:
+            max_retries = settings.max_retry_count
+
+        url = f"{self.base_url}/chat-messages"
+        payload = {
+            "query": query,
+            "response_mode": "streaming",  # 浣跨敤娴佸紡妯″紡
+            "user": user,
+            "inputs": {},
+        }
+
+        # 濡傛灉鏈変細璇滻D锛屾坊鍔犲埌璇锋眰涓�
+        if conversation_id:
+            payload["conversation_id"] = conversation_id
+
+        retry_count = 0
+        while retry_count <= max_retries:
+            try:
+                logger.info(
+                    f"鍙戦�丏ify娴佸紡娑堟伅: user={user}, conversation_id={conversation_id}, retry={retry_count}"
+                )
+
+                response = self.session.post(
+                    url,
+                    json=payload,
+                    timeout=settings.dify_streaming_timeout,
+                    stream=True
+                )
+                response.raise_for_status()
+
+                # 澶勭悊娴佸紡鍝嶅簲
+                result = self._process_stream_response(response, user)
+
+                if result:
+                    logger.info(
+                        f"Dify娴佸紡娑堟伅鍙戦�佹垚鍔�: user={user}, conversation_id={result.get('conversation_id')}"
+                    )
+                    return result
+                else:
+                    logger.error(f"Dify娴佸紡鍝嶅簲澶勭悊澶辫触: user={user}")
+
+            except requests.exceptions.Timeout:
+                logger.warning(f"Dify娴佸紡璇锋眰瓒呮椂: user={user}, retry={retry_count}")
+            except requests.exceptions.RequestException as e:
+                logger.error(
+                    f"Dify娴佸紡缃戠粶閿欒: user={user}, retry={retry_count}, error={str(e)}"
+                )
+            except Exception as e:
+                logger.error(
+                    f"Dify娴佸紡璇锋眰寮傚父: user={user}, retry={retry_count}, error={str(e)}"
+                )
+
+            retry_count += 1
+            if retry_count <= max_retries:
+                wait_time = settings.retry_delay * retry_count
+                logger.info(f"绛夊緟閲嶈瘯: user={user}, wait_time={wait_time}s")
+                time.sleep(wait_time)
+
+        logger.error(
+            f"Dify娴佸紡娑堟伅鍙戦�佸け璐ワ紝宸茶揪鏈�澶ч噸璇曟鏁�: user={user}, max_retries={max_retries}"
+        )
+        return None
+
+    def _process_stream_response(self, response: requests.Response, user: str) -> Optional[Dict[str, Any]]:
+        """
+        澶勭悊娴佸紡鍝嶅簲
+
+        Args:
+            response: requests鍝嶅簲瀵硅薄
+            user: 鐢ㄦ埛鏍囪瘑
+
+        Returns:
+            瀹屾暣鐨勫搷搴旀暟鎹瓧鍏革紝澶辫触杩斿洖None
+        """
+        try:
+            # 妫�鏌ュ搷搴斿ご
+            content_type = response.headers.get('content-type', '')
+            if 'text/event-stream' not in content_type:
+                logger.warning(f"鍝嶅簲涓嶆槸SSE鏍煎紡: user={user}, content_type={content_type}")
+
+            complete_answer = ""
+            conversation_id = ""
+            task_id = ""
+            message_id = ""
+            created_at = None
+            metadata = None
+            usage = None
+            retriever_resources = None
+            message_ended = False  # 鏍囪鏄惁鏀跺埌message_end浜嬩欢
+
+            logger.info(f"寮�濮嬪鐞嗘祦寮忓搷搴�: user={user}")
+
+            # 娣诲姞瓒呮椂鍜岃璁℃暟鍣�
+            line_count = 0
+            max_empty_lines = 50  # 鏈�澶ц繛缁┖琛屾暟锛岄槻姝㈡棤闄愬惊鐜�
+
+            for line in response.iter_lines(decode_unicode=True):
+                line_count += 1
+
+                if not line:
+                    # 绌鸿璁℃暟锛岄槻姝㈡棤闄愮瓑寰�
+                    if line_count > max_empty_lines and not complete_answer:
+                        logger.warning(f"娴佸紡鍝嶅簲杩囧绌鸿锛屽彲鑳借繛鎺ュ紓甯�: user={user}, line_count={line_count}")
+                        break
+                    continue
+
+                # 璺宠繃闈炴暟鎹
+                if not line.startswith("data: "):
+                    continue
+
+                # 鎻愬彇JSON鏁版嵁
+                data_str = line[6:]  # 绉婚櫎 "data: " 鍓嶇紑
+
+                if not data_str.strip():
+                    continue
+
+                try:
+                    data = json.loads(data_str)
+                    event = data.get("event", "")
+
+                    if event == "message":
+                        # 娑堟伅浜嬩欢 - 绱Н绛旀鍐呭
+                        answer_chunk = data.get("answer", "")
+                        complete_answer += answer_chunk
+
+                        # 淇濆瓨鍩烘湰淇℃伅
+                        if not conversation_id:
+                            conversation_id = data.get("conversation_id", "")
+                        if not task_id:
+                            task_id = data.get("task_id", "")
+                        if not message_id:
+                            message_id = data.get("id", "")
+                        if created_at is None:
+                            created_at = data.get("created_at")
+
+                        logger.debug(f"鏀跺埌娑堟伅鍧�: user={user}, chunk_length={len(answer_chunk)}")
+
+                    elif event == "message_end":
+                        # 娑堟伅缁撴潫浜嬩欢 - 鑾峰彇鍏冩暟鎹�
+                        metadata = data.get("metadata")
+                        usage = data.get("usage")
+                        retriever_resources = data.get("retriever_resources")
+                        message_ended = True
+
+                        logger.info(f"娴佸紡鍝嶅簲瀹屾垚: user={user}, total_length={len(complete_answer)}")
+                        break
+
+                    elif event == "message_file":
+                        # 鏂囦欢浜嬩欢 - 璁板綍浣嗙户缁鐞�
+                        logger.debug(f"鏀跺埌鏂囦欢浜嬩欢: user={user}, file_type={data.get('type')}")
+                        continue
+
+                    elif event == "message_replace":
+                        # 娑堟伅鏇挎崲浜嬩欢 - 鏇挎崲绛旀鍐呭
+                        replace_answer = data.get("answer", "")
+                        if replace_answer:
+                            complete_answer = replace_answer
+                            logger.info(f"娑堟伅鍐呭琚浛鎹�: user={user}, new_length={len(complete_answer)}")
+                        continue
+
+                    elif event in ["workflow_started", "node_started", "node_finished", "workflow_finished"]:
+                        # 宸ヤ綔娴佺浉鍏充簨浠� - 璁板綍浣嗙户缁鐞�
+                        logger.debug(f"鏀跺埌宸ヤ綔娴佷簨浠�: user={user}, event={event}")
+                        continue
+
+                    elif event in ["tts_message", "tts_message_end"]:
+                        # TTS闊抽浜嬩欢 - 璁板綍浣嗙户缁鐞�
+                        logger.debug(f"鏀跺埌TTS浜嬩欢: user={user}, event={event}")
+                        continue
+
+                    elif event in ["agent_thought", "agent_message"]:
+                        # Agent鐩稿叧浜嬩欢 - 闇�瑕佺壒娈婂鐞�
+                        logger.debug(f"鏀跺埌Agent浜嬩欢: user={user}, event={event}, data_keys={list(data.keys())}")
+
+                        # 浠巃gent浜嬩欢涓彁鍙朿onversation_id锛堝鏋滄湁鐨勮瘽锛�
+                        if not conversation_id and data.get("conversation_id"):
+                            conversation_id = data.get("conversation_id")
+                            logger.info(f"浠嶢gent浜嬩欢鑾峰彇conversation_id: user={user}, conversation_id={conversation_id}")
+
+                        # 浠巃gent浜嬩欢涓彁鍙栧熀鏈俊鎭紙濡傛灉鏈夌殑璇濓級
+                        if not task_id and data.get("task_id"):
+                            task_id = data.get("task_id")
+                            logger.debug(f"浠嶢gent浜嬩欢鑾峰彇task_id: user={user}, task_id={task_id}")
+                        if not message_id and data.get("id"):
+                            message_id = data.get("id")
+                            logger.debug(f"浠嶢gent浜嬩欢鑾峰彇message_id: user={user}, message_id={message_id}")
+                        if created_at is None and data.get("created_at"):
+                            created_at = data.get("created_at")
+                            logger.debug(f"浠嶢gent浜嬩欢鑾峰彇created_at: user={user}, created_at={created_at}")
+
+                        # 妫�鏌gent_message鏄惁鍖呭惈answer鍐呭
+                        if event == "agent_message" and data.get("answer"):
+                            agent_answer = data.get("answer", "")
+                            complete_answer += agent_answer
+                            logger.debug(f"浠嶢gent娑堟伅鑾峰彇鍐呭: user={user}, chunk_length={len(agent_answer)}")
+
+                        continue
+
+                    elif event == "error":
+                        # 閿欒浜嬩欢
+                        error_msg = data.get("message", "鏈煡閿欒")
+                        logger.error(f"娴佸紡鍝嶅簲閿欒: user={user}, error={error_msg}")
+                        return None
+
+                    elif event == "ping":
+                        # ping浜嬩欢 - 淇濇寔杩炴帴
+                        logger.debug(f"鏀跺埌ping浜嬩欢: user={user}")
+                        continue
+
+                    else:
+                        # 鏈煡浜嬩欢绫诲瀷 - 璁板綍浣嗙户缁鐞�
+                        logger.debug(f"鏀跺埌鏈煡浜嬩欢: user={user}, event={event}")
+                        continue
+
+                except json.JSONDecodeError as e:
+                    logger.warning(f"瑙f瀽娴佸紡鏁版嵁JSON澶辫触: user={user}, data={data_str}, error={str(e)}")
+                    continue
+
+            # 鏋勫缓瀹屾暣鍝嶅簲
+            # 瀵逛簬Agent妯″紡锛屽彲鑳芥病鏈塩onversation_id锛屼絾鏈塼ask_id
+            # 鍦ㄨ繖绉嶆儏鍐典笅锛屾垜浠彲浠ヤ娇鐢╰ask_id浣滀负conversation_id鐨勬浛浠�
+            if conversation_id or (task_id and (complete_answer or message_ended)):
+                # 濡傛灉娌℃湁conversation_id浣嗘湁task_id锛屼娇鐢╰ask_id
+                final_conversation_id = conversation_id or task_id
+
+                result = {
+                    "event": "message",
+                    "task_id": task_id,
+                    "id": message_id,
+                    "message_id": message_id,
+                    "conversation_id": final_conversation_id,
+                    "mode": "chat",
+                    "answer": complete_answer,  # 鍙兘涓虹┖瀛楃涓�
+                    "created_at": created_at
+                }
+
+                # 娣诲姞鍙�夊瓧娈�
+                if metadata:
+                    result["metadata"] = metadata
+                if usage:
+                    result["usage"] = usage
+                if retriever_resources:
+                    result["retriever_resources"] = retriever_resources
+
+                if complete_answer:
+                    logger.info(f"娴佸紡鍝嶅簲澶勭悊鎴愬姛: user={user}, answer_length={len(complete_answer)}, conversation_id={final_conversation_id}, message_ended={message_ended}")
+                else:
+                    logger.info(f"娴佸紡鍝嶅簲澶勭悊鎴愬姛(鏃犲唴瀹�): user={user}, conversation_id={final_conversation_id}, message_ended={message_ended}")
+                return result
+            else:
+                logger.error(f"娴佸紡鍝嶅簲涓嶅畬鏁�: user={user}, answer={bool(complete_answer)}, conversation_id={bool(conversation_id)}, task_id={bool(task_id)}")
+                # 璁板綍鏇村璋冭瘯淇℃伅
+                logger.debug(f"璋冭瘯淇℃伅: task_id={task_id}, message_id={message_id}, created_at={created_at}, message_ended={message_ended}")
+                return None
+
+        except Exception as e:
+            logger.error(f"澶勭悊娴佸紡鍝嶅簲寮傚父: user={user}, error={str(e)}")
+            return None
+
+    def send_message(
+        self,
+        query: str,
+        user: str,
+        conversation_id: Optional[str] = None,
+        max_retries: int = None,
+        force_streaming: Optional[bool] = None,
+    ) -> Optional[Dict[str, Any]]:
+        """
+        鍙戦�佸璇濇秷鎭紙鏍规嵁閰嶇疆閫夋嫨妯″紡锛�
+
+        Args:
+            query: 鐢ㄦ埛杈撳叆/鎻愰棶鍐呭
+            user: 鐢ㄦ埛鏍囪瘑
+            conversation_id: 浼氳瘽ID锛堝彲閫夛級
+            max_retries: 鏈�澶ч噸璇曟鏁�
+            force_streaming: 寮哄埗浣跨敤娴佸紡妯″紡锛堝彲閫夛紝瑕嗙洊閰嶇疆锛�
+
+        Returns:
+            鍝嶅簲鏁版嵁瀛楀吀锛屽け璐ヨ繑鍥濶one
+        """
+        # 纭畾浣跨敤鍝妯″紡
+        use_streaming = force_streaming if force_streaming is not None else settings.dify_streaming_enabled
+
+        if use_streaming:
+            logger.info(f"浣跨敤娴佸紡妯″紡鍙戦�佹秷鎭�: user={user}")
+            return self.send_chat_message_stream(query, user, conversation_id, max_retries)
+        else:
+            logger.info(f"浣跨敤闃诲妯″紡鍙戦�佹秷鎭�: user={user}")
+            return self.send_chat_message(query, user, conversation_id, max_retries)
+
     def get_conversation_messages(
         self, conversation_id: str, user: str
     ) -> Optional[List[Dict[str, Any]]]:
diff --git a/app/services/friend_ignore_service.py b/app/services/friend_ignore_service.py
new file mode 100644
index 0000000..061c771
--- /dev/null
+++ b/app/services/friend_ignore_service.py
@@ -0,0 +1,259 @@
+"""
+濂藉弸蹇界暐鍒楄〃绠$悊鏈嶅姟
+"""
+
+from typing import List, Set, Optional
+from loguru import logger
+from sqlalchemy.orm import Session
+from app.services.redis_queue import redis_queue
+from app.models.database import get_db
+from app.models.contact import Contact
+from config import settings
+
+
+class FriendIgnoreService:
+    """濂藉弸蹇界暐鍒楄〃绠$悊鏈嶅姟"""
+
+    def __init__(self):
+        self.ignore_list_key = "ecloud_ignore_friends"
+
+    def _get_wid_by_nickname(self, nickname: str) -> Optional[str]:
+        """
+        鏍规嵁鏄电О鑾峰彇w_id
+
+        Args:
+            nickname: 濂藉弸鏄电О
+
+        Returns:
+            瀵瑰簲鐨剋_id锛屽鏋滄湭鎵惧埌杩斿洖None
+        """
+        try:
+            with next(get_db()) as db:
+                contact = db.query(Contact).filter(Contact.nick_name == nickname).first()
+                if contact:
+                    return contact.wc_id
+                else:
+                    logger.warning(f"鏈壘鍒版樀绉颁负 '{nickname}' 鐨勮仈绯讳汉")
+                    return None
+        except Exception as e:
+            logger.error(f"鏍规嵁鏄电О鏌ユ壘w_id寮傚父: nickname={nickname}, error={str(e)}")
+            return None
+
+    def _get_whitelist_wids(self) -> List[str]:
+        """
+        灏嗛厤缃腑鐨勬樀绉扮櫧鍚嶅崟杞崲涓簑_id鍒楄〃
+
+        Returns:
+            w_id鍒楄〃
+        """
+        wid_list = []
+        for nickname in settings.friend_ignore_whitelist:
+            wid = self._get_wid_by_nickname(nickname)
+            if wid:
+                wid_list.append(wid)
+                logger.debug(f"鐧藉悕鍗曟樀绉� '{nickname}' 瀵瑰簲w_id: {wid}")
+            else:
+                logger.warning(f"鐧藉悕鍗曟樀绉� '{nickname}' 鏈壘鍒板搴旂殑鑱旂郴浜�")
+        return wid_list
+
+    def add_friends_to_ignore_list(self, friends: List[str]) -> bool:
+        """
+        灏嗗ソ鍙媤_id娣诲姞鍒癛edis蹇界暐鍒楄〃
+
+        Args:
+            friends: 濂藉弸w_id鍒楄〃
+
+        Returns:
+            娣诲姞鎴愬姛杩斿洖True锛屽け璐ヨ繑鍥濬alse
+        """
+        try:
+            if not friends:
+                logger.info("濂藉弸鍒楄〃涓虹┖锛屾棤闇�娣诲姞鍒板拷鐣ュ垪琛�")
+                return True
+
+            # 娓呯┖鐜版湁鐨勫拷鐣ュ垪琛�
+            redis_queue.redis_client.delete(self.ignore_list_key)
+            
+            # 鎵归噺娣诲姞濂藉弸w_id鍒板拷鐣ュ垪琛�
+            redis_queue.redis_client.sadd(self.ignore_list_key, *friends)
+            
+            logger.info(f"宸插皢 {len(friends)} 涓ソ鍙嬫坊鍔犲埌蹇界暐鍒楄〃")
+            return True
+
+        except Exception as e:
+            logger.error(f"娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄥ紓甯�: error={str(e)}")
+            return False
+
+    def is_friend_ignored(self, w_id: str) -> bool:
+        """
+        妫�鏌ユ寚瀹歸_id鏄惁搴旇琚拷鐣�
+
+        閫昏緫锛�
+        1. 濡傛灉濂藉弸蹇界暐鍔熻兘鏈惎鐢紝杩斿洖False锛堜笉蹇界暐锛�
+        2. 濡傛灉w_id鍦ㄧ櫧鍚嶅崟涓紝杩斿洖False锛堜笉蹇界暐锛�
+        3. 濡傛灉w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛岃繑鍥濼rue锛堝拷鐣ワ級
+        4. 濡傛灉w_id涓嶅湪蹇界暐鍒楄〃涓紝杩斿洖False锛堜笉蹇界暐锛�
+
+        Args:
+            w_id: 鐢ㄦ埛w_id
+
+        Returns:
+            濡傛灉搴旇琚拷鐣ヨ繑鍥濼rue锛屽惁鍒欒繑鍥濬alse
+        """
+        try:
+            # 妫�鏌ュソ鍙嬪拷鐣ュ姛鑳芥槸鍚﹀惎鐢�
+            if not settings.friend_ignore_enabled:
+                logger.debug(f"濂藉弸蹇界暐鍔熻兘宸茬鐢紝涓嶅拷鐣ユ秷鎭�: w_id={w_id}")
+                return False
+
+            # 妫�鏌ユ槸鍚﹀湪鐧藉悕鍗曚腑锛堥�氳繃鏄电О锛�
+            whitelist_wids = self._get_whitelist_wids()
+            if w_id in whitelist_wids:
+                logger.info(f"w_id鍦ㄧ櫧鍚嶅崟涓紝涓嶅拷鐣ユ秷鎭�: w_id={w_id}")
+                return False
+
+            # 妫�鏌ユ槸鍚﹀湪蹇界暐鍒楄〃涓�
+            is_in_ignore_list = redis_queue.redis_client.sismember(self.ignore_list_key, w_id)
+
+            if is_in_ignore_list:
+                logger.info(f"w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛屽拷鐣ユ秷鎭�: w_id={w_id}")
+
+            return is_in_ignore_list
+
+        except Exception as e:
+            logger.error(f"妫�鏌ュ拷鐣ュ垪琛ㄥ紓甯�: w_id={w_id}, error={str(e)}")
+            return False
+
+    def get_ignore_list(self) -> Set[str]:
+        """
+        鑾峰彇瀹屾暣鐨勫拷鐣ュ垪琛�
+
+        Returns:
+            蹇界暐鍒楄〃涓殑鎵�鏈墂_id闆嗗悎
+        """
+        try:
+            return redis_queue.redis_client.smembers(self.ignore_list_key)
+        except Exception as e:
+            logger.error(f"鑾峰彇蹇界暐鍒楄〃寮傚父: error={str(e)}")
+            return set()
+
+    def remove_friend_from_ignore_list(self, w_id: str) -> bool:
+        """
+        浠庡拷鐣ュ垪琛ㄤ腑绉婚櫎鎸囧畾w_id
+
+        Args:
+            w_id: 鐢ㄦ埛w_id
+
+        Returns:
+            绉婚櫎鎴愬姛杩斿洖True锛屽け璐ヨ繑鍥濬alse
+        """
+        try:
+            result = redis_queue.redis_client.srem(self.ignore_list_key, w_id)
+            if result:
+                logger.info(f"宸蹭粠蹇界暐鍒楄〃涓Щ闄�: w_id={w_id}")
+            else:
+                logger.info(f"w_id涓嶅湪蹇界暐鍒楄〃涓�: w_id={w_id}")
+            return True
+        except Exception as e:
+            logger.error(f"浠庡拷鐣ュ垪琛ㄧЩ闄_id寮傚父: w_id={w_id}, error={str(e)}")
+            return False
+
+    def clear_ignore_list(self) -> bool:
+        """
+        娓呯┖蹇界暐鍒楄〃
+
+        Returns:
+            娓呯┖鎴愬姛杩斿洖True锛屽け璐ヨ繑鍥濬alse
+        """
+        try:
+            redis_queue.redis_client.delete(self.ignore_list_key)
+            logger.info("宸叉竻绌哄拷鐣ュ垪琛�")
+            return True
+        except Exception as e:
+            logger.error(f"娓呯┖蹇界暐鍒楄〃寮傚父: error={str(e)}")
+            return False
+
+    def get_ignore_list_count(self) -> int:
+        """
+        鑾峰彇蹇界暐鍒楄〃涓殑濂藉弸鏁伴噺
+
+        Returns:
+            蹇界暐鍒楄〃涓殑濂藉弸鏁伴噺
+        """
+        try:
+            return redis_queue.redis_client.scard(self.ignore_list_key)
+        except Exception as e:
+            logger.error(f"鑾峰彇蹇界暐鍒楄〃鏁伴噺寮傚父: error={str(e)}")
+            return 0
+
+    def get_whitelist(self) -> List[str]:
+        """
+        鑾峰彇褰撳墠鐨勭櫧鍚嶅崟鍒楄〃
+
+        Returns:
+            鐧藉悕鍗曚腑鐨剋_id鍒楄〃
+        """
+        return settings.friend_ignore_whitelist.copy()
+
+    def is_whitelist_enabled(self) -> bool:
+        """
+        妫�鏌ョ櫧鍚嶅崟鍔熻兘鏄惁鍚敤
+
+        Returns:
+            濡傛灉鍚敤杩斿洖True锛屽惁鍒欒繑鍥濬alse
+        """
+        return settings.friend_ignore_enabled
+
+    def get_ignore_status_info(self, w_id: str) -> dict:
+        """
+        鑾峰彇鎸囧畾w_id鐨勮缁嗗拷鐣ョ姸鎬佷俊鎭�
+
+        Args:
+            w_id: 鐢ㄦ埛w_id
+
+        Returns:
+            鍖呭惈璇︾粏鐘舵�佷俊鎭殑瀛楀吀
+        """
+        try:
+            # 鑾峰彇鐧藉悕鍗晈_id鍒楄〃
+            whitelist_wids = self._get_whitelist_wids()
+
+            info = {
+                "w_id": w_id,
+                "ignore_enabled": settings.friend_ignore_enabled,
+                "in_whitelist": w_id in whitelist_wids,
+                "in_ignore_list": False,
+                "final_ignored": False,
+                "reason": "",
+                "whitelist_nicknames": settings.friend_ignore_whitelist
+            }
+
+            if not settings.friend_ignore_enabled:
+                info["reason"] = "濂藉弸蹇界暐鍔熻兘宸茬鐢�"
+                return info
+
+            if w_id in whitelist_wids:
+                info["reason"] = "鍦ㄧ櫧鍚嶅崟涓紝涓嶄細琚拷鐣�"
+                return info
+
+            info["in_ignore_list"] = redis_queue.redis_client.sismember(self.ignore_list_key, w_id)
+
+            if info["in_ignore_list"]:
+                info["final_ignored"] = True
+                info["reason"] = "鍦ㄥ拷鐣ュ垪琛ㄤ腑锛屼細琚拷鐣�"
+            else:
+                info["reason"] = "涓嶅湪蹇界暐鍒楄〃涓紝涓嶄細琚拷鐣�"
+
+            return info
+
+        except Exception as e:
+            logger.error(f"鑾峰彇蹇界暐鐘舵�佷俊鎭紓甯�: w_id={w_id}, error={str(e)}")
+            return {
+                "w_id": w_id,
+                "error": str(e),
+                "final_ignored": False
+            }
+
+
+# 鍏ㄥ眬濂藉弸蹇界暐鏈嶅姟瀹炰緥
+friend_ignore_service = FriendIgnoreService()
diff --git a/app/services/message_processor.py b/app/services/message_processor.py
index 12aaea2..e70555a 100644
--- a/app/services/message_processor.py
+++ b/app/services/message_processor.py
@@ -16,6 +16,7 @@
 from app.services.redis_queue import redis_queue
 from app.services.ecloud_client import ecloud_client
 from app.services.dify_client import dify_client
+from app.services.friend_ignore_service import friend_ignore_service
 from config import settings
 
 
@@ -99,6 +100,12 @@
             or not data.get("content")
         ):
             logger.warning(f"娑堟伅缂哄皯蹇呰瀛楁: data={data}")
+            return False
+
+        # 妫�鏌ュ彂閫佽�呮槸鍚﹀湪濂藉弸蹇界暐鍒楄〃涓�
+        from_user = data.get("fromUser")
+        if friend_ignore_service.is_friend_ignored(from_user):
+            logger.info(f"蹇界暐濂藉弸鍙戦�佺殑娑堟伅: fromUser={from_user}")
             return False
 
         return True
@@ -210,8 +217,8 @@
                 # 3.2 鑾峰彇鐢ㄦ埛鍦ㄥ綋鍓嶇兢缁勭殑conversation_id
                 conversation_id = redis_queue.get_conversation_id(from_user, from_group)
 
-                # 璋冪敤Dify鎺ュ彛鍙戦�佹秷鎭�
-                dify_response = dify_client.send_chat_message(
+                # 璋冪敤Dify鎺ュ彛鍙戦�佹秷鎭紙鏍规嵁閰嶇疆閫夋嫨妯″紡锛�
+                dify_response = dify_client.send_message(
                     query=content, user=from_user, conversation_id=conversation_id
                 )
 
diff --git a/config.example.json b/config.example.json
deleted file mode 100644
index b2df84d..0000000
--- a/config.example.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "database": {
-    "url": "mysql+pymysql://username:password@host:port/database_name"
-  },
-  "redis": {
-    "url": "redis://localhost:6379/0"
-  },
-  "ecloud": {
-    "base_url": "http://your-ecloud-server:port",
-    "authorization": "your_ecloud_authorization_token",
-    "w_id": "your_ecloud_w_id"
-  },
-  "dify": {
-    "base_url": "https://api.dify.ai/v1",
-    "api_key": "your_dify_api_key"
-  },
-  "server": {
-    "host": "0.0.0.0",
-    "port": 7979,
-    "debug": false
-  },
-  "logging": {
-    "level": "INFO",
-    "file": "logs/app.log"
-  },
-  "message_processing": {
-    "max_retry_count": 3,
-    "retry_delay": 5,
-    "queue_timeout": 300
-  },
-  "customer_service": {
-    "names": ["瀹㈡湇1", "瀹㈡湇2"]
-  }
-}
diff --git a/config.production.json b/config.production.json
deleted file mode 100644
index efbd202..0000000
--- a/config.production.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "database": {
-    "url": "mysql+pymysql://root:password@localhost:3306/ecloud_dify"
-  },
-  "redis": {
-    "url": "redis://localhost:6379/0"
-  },
-  "ecloud": {
-    "base_url": "http://125.122.152.142:9899",
-    "authorization": "your_ecloud_authorization_token"
-  },
-  "dify": {
-    "base_url": "https://api.dify.ai/v1",
-    "api_key": "your_dify_api_key"
-  },
-  "server": {
-    "host": "0.0.0.0",
-    "port": 7979,
-    "debug": false
-  },
-  "logging": {
-    "level": "INFO",
-    "file": "logs/app.log"
-  },
-  "message_processing": {
-    "max_retry_count": 3,
-    "retry_delay": 5,
-    "queue_timeout": 300
-  }
-}
diff --git a/config.py b/config.py
index d7919aa..c9792f3 100644
--- a/config.py
+++ b/config.py
@@ -16,91 +16,62 @@
 
     def _load_config(self):
         """浠嶫SON鏂囦欢鍔犺浇閰嶇疆"""
+        if not os.path.exists(self.config_file):
+            raise FileNotFoundError(f"閰嶇疆鏂囦欢 {self.config_file} 涓嶅瓨鍦�")
+
         try:
-            if os.path.exists(self.config_file):
-                with open(self.config_file, 'r', encoding='utf-8') as f:
-                    config_data = json.load(f)
-                self._set_config_from_dict(config_data)
-            else:
-                # 濡傛灉閰嶇疆鏂囦欢涓嶅瓨鍦紝浣跨敤榛樿鍊�
-                self._set_default_config()
+            with open(self.config_file, 'r', encoding='utf-8') as f:
+                config_data = json.load(f)
+            self._set_config_from_dict(config_data)
         except Exception as e:
-            print(f"鍔犺浇閰嶇疆鏂囦欢澶辫触: {e}")
-            self._set_default_config()
+            raise Exception(f"鍔犺浇閰嶇疆鏂囦欢澶辫触: {e}")
 
     def _set_config_from_dict(self, config_data: dict):
         """浠庡瓧鍏歌缃厤缃�"""
         # 鏁版嵁搴撻厤缃�
-        self.database_url = config_data.get("database", {}).get("url", "mysql+pymysql://root:TAI%402019%23Zjun@120.24.39.179:3306/ecloud_dify")
+        self.database_url = config_data["database"]["url"]
 
         # Redis閰嶇疆
-        self.redis_url = config_data.get("redis", {}).get("url", "redis://localhost:6379/0")
+        self.redis_url = config_data["redis"]["url"]
 
         # E浜戠瀹堕厤缃�
-        ecloud_config = config_data.get("ecloud", {})
-        self.ecloud_base_url = ecloud_config.get("base_url", "http://125.122.152.142:9899")
-        self.ecloud_authorization = ecloud_config.get("authorization", "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMzYxMTQ1MjE3NSIsInBhc3N3b3JkIjoiJDJhJDEwJEU3Ry5LOEJzekphM2JGQlh0SG8vOXVrUk1NalVweGVVemguUDRnMkJBdHN2YXpBb0JIQWJpIn0.Gd2vbeJjL5pUGFhUngWPLkDTLhD3GUaEPXOkdoTf4KRh9o2FtST1OZJxmZuGdUy7WIYlIPVueoVyIu5iHOyi8A")
-        self.ecloud_w_id = ecloud_config.get("w_id", "")
+        ecloud_config = config_data["ecloud"]
+        self.ecloud_base_url = ecloud_config["base_url"]
+        self.ecloud_authorization = ecloud_config["authorization"]
+        self.ecloud_w_id = ecloud_config["w_id"]
 
         # DifyAI閰嶇疆
-        dify_config = config_data.get("dify", {})
-        self.dify_base_url = dify_config.get("base_url", "https://api.dify.ai/v1")
-        self.dify_api_key = dify_config.get("api_key", "app-OMnBr7zsf5UTV83Ey8QcSErA")
+        dify_config = config_data["dify"]
+        self.dify_base_url = dify_config["base_url"]
+        self.dify_api_key = dify_config["api_key"]
+        self.dify_streaming_enabled = dify_config["streaming_enabled"]
+        self.dify_streaming_timeout = dify_config["streaming_timeout"]
 
         # 鏈嶅姟閰嶇疆
-        server_config = config_data.get("server", {})
-        self.server_host = server_config.get("host", "0.0.0.0")
-        self.server_port = server_config.get("port", 7979)
-        self.debug = server_config.get("debug", True)
+        server_config = config_data["server"]
+        self.server_host = server_config["host"]
+        self.server_port = server_config["port"]
+        self.debug = server_config["debug"]
 
         # 鏃ュ織閰嶇疆
-        logging_config = config_data.get("logging", {})
-        self.log_level = logging_config.get("level", "INFO")
-        self.log_file = logging_config.get("file", "logs/app.log")
+        logging_config = config_data["logging"]
+        self.log_level = logging_config["level"]
+        self.log_file = logging_config["file"]
 
         # 娑堟伅澶勭悊閰嶇疆
-        msg_config = config_data.get("message_processing", {})
-        self.max_retry_count = msg_config.get("max_retry_count", 3)
-        self.retry_delay = msg_config.get("retry_delay", 5)
-        self.queue_timeout = msg_config.get("queue_timeout", 300)
+        msg_config = config_data["message_processing"]
+        self.max_retry_count = msg_config["max_retry_count"]
+        self.retry_delay = msg_config["retry_delay"]
+        self.queue_timeout = msg_config["queue_timeout"]
 
         # 瀹㈡湇閰嶇疆
-        customer_service_config = config_data.get("customer_service", {})
-        self.customer_service_names = customer_service_config.get("names", ["瀹㈡湇1", "瀹㈡湇2"])
+        customer_service_config = config_data["customer_service"]
+        self.customer_service_names = customer_service_config["names"]
 
-    def _set_default_config(self):
-        """璁剧疆榛樿閰嶇疆"""
-        # 鏁版嵁搴撻厤缃�
-        self.database_url = "mysql+pymysql://root:TAI%402019%23Zjun@120.24.39.179:3306/ecloud_dify"
-
-        # Redis閰嶇疆
-        self.redis_url = "redis://localhost:6379/0"
-
-        # E浜戠瀹堕厤缃�
-        self.ecloud_base_url = "http://125.122.152.142:9899"
-        self.ecloud_authorization = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMzYxMTQ1MjE3NSIsInBhc3N3b3JkIjoiJDJhJDEwJEU3Ry5LOEJzekphM2JGQlh0SG8vOXVrUk1NalVweGVVemguUDRnMkJBdHN2YXpBb0JIQWJpIn0.Gd2vbeJjL5pUGFhUngWPLkDTLhD3GUaEPXOkdoTf4KRh9o2FtST1OZJxmZuGdUy7WIYlIPVueoVyIu5iHOyi8A"
-        self.ecloud_w_id = ""
-
-        # DifyAI閰嶇疆
-        self.dify_base_url = "https://api.dify.ai/v1"
-        self.dify_api_key = "app-OMnBr7zsf5UTV83Ey8QcSErA"
-
-        # 鏈嶅姟閰嶇疆
-        self.server_host = "0.0.0.0"
-        self.server_port = 7979
-        self.debug = True
-
-        # 鏃ュ織閰嶇疆
-        self.log_level = "INFO"
-        self.log_file = "logs/app.log"
-
-        # 娑堟伅澶勭悊閰嶇疆
-        self.max_retry_count = 3
-        self.retry_delay = 5
-        self.queue_timeout = 300
-
-        # 瀹㈡湇閰嶇疆
-        self.customer_service_names = ["瀹㈡湇1", "瀹㈡湇2"]
+        # 濂藉弸蹇界暐閰嶇疆
+        friend_ignore_config = config_data["friend_ignore"]
+        self.friend_ignore_enabled = friend_ignore_config["enabled"]
+        self.friend_ignore_whitelist = friend_ignore_config["whitelist"]
 
 
 # 鍏ㄥ眬閰嶇疆瀹炰緥
diff --git a/docker-compose.yml b/docker-compose.yml
deleted file mode 100644
index 540f643..0000000
--- a/docker-compose.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-version: '3.8'
-
-services:
-  # MySQL鏁版嵁搴�
-  mysql:
-    image: mysql:8.0
-    container_name: ecloud_mysql
-    environment:
-      MYSQL_ROOT_PASSWORD: password123
-      MYSQL_DATABASE: ecloud_dify
-      MYSQL_USER: ecloud
-      MYSQL_PASSWORD: ecloud123
-    ports:
-      - "3306:3306"
-    volumes:
-      - mysql_data:/var/lib/mysql
-      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
-    restart: unless-stopped
-    command: --default-authentication-plugin=mysql_native_password
-
-  # Redis缂撳瓨
-  redis:
-    image: redis:7-alpine
-    container_name: ecloud_redis
-    ports:
-      - "6379:6379"
-    volumes:
-      - redis_data:/data
-    restart: unless-stopped
-    command: redis-server --appendonly yes
-
-  # 搴旂敤鏈嶅姟
-  app:
-    build: .
-    container_name: ecloud_app
-    ports:
-      - "8000:8000"
-    environment:
-      - DATABASE_URL=mysql+pymysql://ecloud:ecloud123@mysql:3306/ecloud_dify
-      - REDIS_URL=redis://redis:6379/0
-    volumes:
-      - ./logs:/app/logs
-      - ./.env:/app/.env
-    depends_on:
-      - mysql
-      - redis
-    restart: unless-stopped
-
-volumes:
-  mysql_data:
-  redis_data:
diff --git a/install_service.bat b/install_service.bat
deleted file mode 100644
index 4c0b14f..0000000
--- a/install_service.bat
+++ /dev/null
@@ -1,102 +0,0 @@
-@echo off
-chcp 65001 >nul
-echo ========================================
-echo E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 Windows鏈嶅姟瀹夎鑴氭湰
-echo ========================================
-echo.
-
-:: 妫�鏌ョ鐞嗗憳鏉冮檺
-net session >nul 2>&1
-if %errorLevel% == 0 (
-    echo 妫�娴嬪埌绠$悊鍛樻潈闄愶紝缁х画瀹夎...
-) else (
-    echo 閿欒锛氶渶瑕佺鐞嗗憳鏉冮檺鎵嶈兘瀹夎Windows鏈嶅姟
-    echo 璇峰彸閿偣鍑绘鑴氭湰锛岄�夋嫨"浠ョ鐞嗗憳韬唤杩愯"
-    pause
-    exit /b 1
-)
-
-:: 鑾峰彇褰撳墠鐩綍
-set "CURRENT_DIR=%~dp0"
-set "SERVICE_NAME=ECloudDifyService"
-set "SERVICE_DISPLAY_NAME=E浜戠瀹�-DifyAI瀵规帴鏈嶅姟"
-set "SERVICE_DESCRIPTION=灏咵浜戠瀹舵秷鎭浆鍙戝埌DifyAI骞惰繑鍥濧I鍥炵瓟鐨勬湇鍔�"
-set "EXE_PATH=%CURRENT_DIR%ecloud_dify.exe"
-
-echo 褰撳墠鐩綍: %CURRENT_DIR%
-echo 鏈嶅姟鍚嶇О: %SERVICE_NAME%
-echo 鍙墽琛屾枃浠�: %EXE_PATH%
-echo.
-
-:: 妫�鏌xe鏂囦欢鏄惁瀛樺湪
-if not exist "%EXE_PATH%" (
-    echo 閿欒锛氭壘涓嶅埌鍙墽琛屾枃浠� %EXE_PATH%
-    echo 璇风‘淇濆凡缁忓畬鎴愰」鐩墦鍖咃紝骞朵笖ecloud_dify.exe鏂囦欢瀛樺湪
-    pause
-    exit /b 1
-)
-
-:: 鍋滄骞跺垹闄ゅ凡瀛樺湪鐨勬湇鍔�
-echo 妫�鏌ユ槸鍚﹀瓨鍦ㄥ悓鍚嶆湇鍔�...
-sc query "%SERVICE_NAME%" >nul 2>&1
-if %errorLevel% == 0 (
-    echo 鍙戠幇宸插瓨鍦ㄧ殑鏈嶅姟锛屾鍦ㄥ仠姝㈠苟鍒犻櫎...
-    sc stop "%SERVICE_NAME%" >nul 2>&1
-    timeout /t 3 /nobreak >nul
-    sc delete "%SERVICE_NAME%" >nul 2>&1
-    if %errorLevel% == 0 (
-        echo 宸插垹闄ゆ棫鏈嶅姟
-    ) else (
-        echo 璀﹀憡锛氬垹闄ゆ棫鏈嶅姟澶辫触锛岀户缁畨瑁呮柊鏈嶅姟
-    )
-    echo.
-)
-
-:: 鍒涘缓Windows鏈嶅姟
-echo 姝e湪鍒涘缓Windows鏈嶅姟...
-sc create "%SERVICE_NAME%" binPath= "\"%EXE_PATH%\"" DisplayName= "%SERVICE_DISPLAY_NAME%" start= auto
-if %errorLevel% == 0 (
-    echo 鏈嶅姟鍒涘缓鎴愬姛
-) else (
-    echo 閿欒锛氭湇鍔″垱寤哄け璐�
-    pause
-    exit /b 1
-)
-
-:: 璁剧疆鏈嶅姟鎻忚堪
-sc description "%SERVICE_NAME%" "%SERVICE_DESCRIPTION%"
-
-:: 璁剧疆鏈嶅姟鎭㈠閫夐」锛堝け璐ユ椂鑷姩閲嶅惎锛�
-sc failure "%SERVICE_NAME%" reset= 86400 actions= restart/5000/restart/10000/restart/30000
-
-echo.
-echo 鏈嶅姟瀹夎瀹屾垚锛�
-echo.
-echo 鍙敤鐨勬搷浣滐細
-echo 1. 鍚姩鏈嶅姟: sc start %SERVICE_NAME%
-echo 2. 鍋滄鏈嶅姟: sc stop %SERVICE_NAME%
-echo 3. 鏌ョ湅鏈嶅姟鐘舵��: sc query %SERVICE_NAME%
-echo 4. 鍒犻櫎鏈嶅姟: sc delete %SERVICE_NAME%
-echo.
-echo 鎴栬�呬娇鐢╓indows鏈嶅姟绠$悊鍣� (services.msc) 杩涜绠$悊
-echo.
-
-:: 璇㈤棶鏄惁绔嬪嵆鍚姩鏈嶅姟
-set /p START_NOW="鏄惁绔嬪嵆鍚姩鏈嶅姟锛�(Y/N): "
-if /i "%START_NOW%"=="Y" (
-    echo 姝e湪鍚姩鏈嶅姟...
-    sc start "%SERVICE_NAME%"
-    if %errorLevel% == 0 (
-        echo 鏈嶅姟鍚姩鎴愬姛锛�
-        echo 鏈嶅姟灏嗗湪绔彛 7979 涓婅繍琛�
-        echo 鍙互閫氳繃 http://localhost:7979 璁块棶鏈嶅姟
-    ) else (
-        echo 鏈嶅姟鍚姩澶辫触锛岃妫�鏌ラ厤缃枃浠跺拰鏃ュ織
-    )
-) else (
-    echo 鏈嶅姟宸插畨瑁呬絾鏈惎鍔�
-    echo 鍙互绋嶅悗閫氳繃 sc start %SERVICE_NAME% 鍚姩鏈嶅姟
-)
-
-echo.
-pause
diff --git a/logs/app.log b/logs/app.log
index fec3d52..d8da35c 100644
--- a/logs/app.log
+++ b/logs/app.log
@@ -1,9 +1,4 @@
-2025-07-23 14:46:37 | INFO | __main__:<module>:105 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 15:03:24 | INFO | __main__:<module>:105 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 16:48:21 | INFO | __main__:<module>:121 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 16:50:14 | INFO | __main__:<module>:121 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 16:55:09 | INFO | __main__:<module>:121 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 16:56:49 | INFO | __main__:<module>:121 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 17:19:58 | INFO | __main__:<module>:121 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 17:43:16 | INFO | __main__:<module>:105 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
-2025-07-23 17:44:16 | INFO | __main__:<module>:105 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
+2025-07-28 09:54:56 | INFO | __main__:<module>:122 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
+2025-07-28 10:41:14 | INFO | __main__:<module>:122 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
+2025-07-28 10:42:46 | INFO | __main__:<module>:122 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
+2025-07-28 11:10:52 | INFO | __main__:<module>:122 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟
diff --git a/main.py b/main.py
index 978923a..cba621f 100644
--- a/main.py
+++ b/main.py
@@ -10,8 +10,10 @@
 import time
 from config import settings
 from app.api.callback import router as callback_router
+from app.api.friend_ignore import router as friend_ignore_router
 from app.models.database import create_tables
 from app.workers.message_worker import message_worker
+from app.services.contact_sync import contact_sync_service
 
 
 @asynccontextmanager
@@ -33,6 +35,20 @@
         logger.info("娑堟伅宸ヤ綔杩涚▼鍚姩鎴愬姛")
     except Exception as e:
         logger.error(f"娑堟伅宸ヤ綔杩涚▼鍚姩澶辫触: {str(e)}")
+
+    # 鍚屾鑱旂郴浜轰俊鎭苟寤虹珛濂藉弸蹇界暐鍒楄〃
+    try:
+        if settings.ecloud_w_id:
+            logger.info("寮�濮嬪悓姝ヨ仈绯讳汉淇℃伅...")
+            success = contact_sync_service.sync_contacts_on_startup(settings.ecloud_w_id)
+            if success:
+                logger.info("鑱旂郴浜哄悓姝ュ畬鎴愶紝濂藉弸蹇界暐鍒楄〃宸插缓绔�")
+            else:
+                logger.warning("鑱旂郴浜哄悓姝ュけ璐�")
+        else:
+            logger.warning("鏈厤缃甧cloud_w_id锛岃烦杩囪仈绯讳汉鍚屾")
+    except Exception as e:
+        logger.error(f"鑱旂郴浜哄悓姝ュ紓甯�: {str(e)}")
 
     logger.info("搴旂敤鍚姩瀹屾垚")
 
@@ -70,6 +86,7 @@
 
 # 娉ㄥ唽璺敱
 app.include_router(callback_router, prefix="/api/v1", tags=["鍥炶皟鎺ュ彛"])
+app.include_router(friend_ignore_router, prefix="/api/v1", tags=["濂藉弸蹇界暐绠$悊"])
 
 
 @app.get("/")
diff --git a/start.bat b/start.bat
deleted file mode 100644
index cc26592..0000000
--- a/start.bat
+++ /dev/null
@@ -1,42 +0,0 @@
-@echo off
-chcp 65001 >nul
-echo ========================================
-echo E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 鍚姩鑴氭湰
-echo ========================================
-echo.
-
-:: 鑾峰彇褰撳墠鐩綍
-set "CURRENT_DIR=%~dp0"
-set "EXE_PATH=%CURRENT_DIR%ecloud_dify.exe"
-
-echo 褰撳墠鐩綍: %CURRENT_DIR%
-echo 鍙墽琛屾枃浠�: %EXE_PATH%
-echo.
-
-:: 妫�鏌xe鏂囦欢鏄惁瀛樺湪
-if not exist "%EXE_PATH%" (
-    echo 閿欒锛氭壘涓嶅埌鍙墽琛屾枃浠� %EXE_PATH%
-    echo 璇风‘淇濆凡缁忓畬鎴愰」鐩墦鍖咃紝骞朵笖ecloud_dify.exe鏂囦欢瀛樺湪
-    pause
-    exit /b 1
-)
-
-:: 妫�鏌ラ厤缃枃浠�
-if not exist "%CURRENT_DIR%config.json" (
-    echo 璀﹀憡锛氭湭鎵惧埌config.json閰嶇疆鏂囦欢
-    echo 绋嬪簭灏嗕娇鐢ㄩ粯璁ら厤缃垨config.production.json妯℃澘
-    echo.
-)
-
-echo 姝e湪鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟...
-echo 鏈嶅姟灏嗗湪绔彛 7979 涓婅繍琛�
-echo 鍙互閫氳繃 http://localhost:7979 璁块棶鏈嶅姟
-echo 鎸� Ctrl+C 鍋滄鏈嶅姟
-echo.
-
-:: 鍚姩鏈嶅姟
-"%EXE_PATH%"
-
-echo.
-echo 鏈嶅姟宸插仠姝�
-pause
diff --git a/start.sh b/start.sh
deleted file mode 100644
index 9cf30c8..0000000
--- a/start.sh
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/bash
-
-# E浜戠瀹�-DifyAI瀵规帴鏈嶅姟鍚姩鑴氭湰
-
-echo "姝e湪鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟..."
-
-# 妫�鏌ython鐗堟湰
-python_version=$(python3 --version 2>&1 | grep -oP '\d+\.\d+')
-if [[ $(echo "$python_version >= 3.11" | bc -l) -eq 0 ]]; then
-    echo "閿欒: 闇�瑕丳ython 3.11鎴栨洿楂樼増鏈紝褰撳墠鐗堟湰: $python_version"
-    exit 1
-fi
-
-# 鍒涘缓铏氭嫙鐜锛堝鏋滀笉瀛樺湪锛�
-if [ ! -d "venv" ]; then
-    echo "鍒涘缓Python铏氭嫙鐜..."
-    python3 -m venv venv
-fi
-
-# 婵�娲昏櫄鎷熺幆澧�
-echo "婵�娲昏櫄鎷熺幆澧�..."
-source venv/bin/activate
-
-# 瀹夎渚濊禆
-echo "瀹夎Python渚濊禆..."
-pip install -r requirements.txt
-
-# 妫�鏌ョ幆澧冨彉閲忔枃浠�
-if [ ! -f ".env" ]; then
-    echo "璀﹀憡: .env鏂囦欢涓嶅瓨鍦紝璇峰鍒�.env.example骞堕厤缃浉鍏冲弬鏁�"
-    cp .env.example .env
-    echo "宸插垱寤�.env鏂囦欢锛岃缂栬緫閰嶇疆鍚庨噸鏂拌繍琛�"
-    exit 1
-fi
-
-# 鍒涘缓鏃ュ織鐩綍
-mkdir -p logs
-
-# 妫�鏌ユ暟鎹簱杩炴帴
-echo "妫�鏌ユ暟鎹簱杩炴帴..."
-python -c "
-from app.utils.database_init import check_database_health
-if not check_database_health():
-    print('鏁版嵁搴撹繛鎺ュけ璐ワ紝璇锋鏌ラ厤缃�')
-    exit(1)
-print('鏁版嵁搴撹繛鎺ユ甯�')
-"
-
-if [ $? -ne 0 ]; then
-    echo "鏁版嵁搴撹繛鎺ュけ璐ワ紝璇锋鏌ラ厤缃�"
-    exit 1
-fi
-
-# 鍒濆鍖栨暟鎹簱
-echo "鍒濆鍖栨暟鎹簱..."
-python app/utils/database_init.py
-
-# 鍚姩鏈嶅姟
-echo "鍚姩鏈嶅姟..."
-python main.py
diff --git a/startup.py b/startup.py
deleted file mode 100644
index dad9b0e..0000000
--- a/startup.py
+++ /dev/null
@@ -1,139 +0,0 @@
-"""
-鍚姩鑴氭湰 - 澶勭悊閰嶇疆鏂囦欢鍜岀洰褰曞垵濮嬪寲
-"""
-
-import os
-import sys
-import json
-import shutil
-from pathlib import Path
-
-
-def get_exe_dir():
-    """鑾峰彇exe鏂囦欢鎵�鍦ㄧ洰褰�"""
-    if getattr(sys, 'frozen', False):
-        # 濡傛灉鏄墦鍖呭悗鐨別xe鏂囦欢
-        return os.path.dirname(sys.executable)
-    else:
-        # 濡傛灉鏄紑鍙戠幆澧�
-        return os.path.dirname(os.path.abspath(__file__))
-
-
-def ensure_directories():
-    """纭繚蹇呰鐨勭洰褰曞瓨鍦�"""
-    exe_dir = get_exe_dir()
-    
-    # 鍒涘缓鏃ュ織鐩綍
-    logs_dir = os.path.join(exe_dir, 'logs')
-    if not os.path.exists(logs_dir):
-        os.makedirs(logs_dir)
-        print(f"鍒涘缓鏃ュ織鐩綍: {logs_dir}")
-    
-    return exe_dir
-
-
-def ensure_config_file():
-    """纭繚閰嶇疆鏂囦欢瀛樺湪"""
-    exe_dir = get_exe_dir()
-    config_file = os.path.join(exe_dir, 'config.json')
-    
-    if not os.path.exists(config_file):
-        # 濡傛灉config.json涓嶅瓨鍦紝灏濊瘯澶嶅埗鐢熶骇閰嶇疆妯℃澘
-        production_config = os.path.join(exe_dir, 'config.production.json')
-        example_config = os.path.join(exe_dir, 'config.example.json')
-        
-        if os.path.exists(production_config):
-            shutil.copy2(production_config, config_file)
-            print(f"澶嶅埗鐢熶骇閰嶇疆鏂囦欢: {production_config} -> {config_file}")
-        elif os.path.exists(example_config):
-            shutil.copy2(example_config, config_file)
-            print(f"澶嶅埗绀轰緥閰嶇疆鏂囦欢: {example_config} -> {config_file}")
-        else:
-            # 鍒涘缓榛樿閰嶇疆鏂囦欢
-            default_config = {
-                "database": {
-                    "url": "mysql+pymysql://root:password@localhost:3306/ecloud_dify"
-                },
-                "redis": {
-                    "url": "redis://localhost:6379/0"
-                },
-                "ecloud": {
-                    "base_url": "http://125.122.152.142:9899",
-                    "authorization": "your_ecloud_authorization_token",
-                    "w_id": "your_ecloud_w_id"
-                },
-                "dify": {
-                    "base_url": "https://api.dify.ai/v1",
-                    "api_key": "your_dify_api_key"
-                },
-                "server": {
-                    "host": "0.0.0.0",
-                    "port": 7979,
-                    "debug": False
-                },
-                "logging": {
-                    "level": "INFO",
-                    "file": "logs/app.log"
-                },
-                "message_processing": {
-                    "max_retry_count": 3,
-                    "retry_delay": 5,
-                    "queue_timeout": 300
-                },
-                "customer_service": {
-                    "names": ["瀹㈡湇1", "瀹㈡湇2"]
-                }
-            }
-            
-            with open(config_file, 'w', encoding='utf-8') as f:
-                json.dump(default_config, f, indent=2, ensure_ascii=False)
-            print(f"鍒涘缓榛樿閰嶇疆鏂囦欢: {config_file}")
-    
-    return config_file
-
-
-def setup_environment():
-    """璁剧疆杩愯鐜"""
-    exe_dir = ensure_directories()
-    config_file = ensure_config_file()
-    
-    # 璁剧疆宸ヤ綔鐩綍涓篹xe鎵�鍦ㄧ洰褰�
-    os.chdir(exe_dir)
-    print(f"璁剧疆宸ヤ綔鐩綍: {exe_dir}")
-    
-    return exe_dir, config_file
-
-
-if __name__ == "__main__":
-    setup_environment()
-    
-    # 瀵煎叆骞跺惎鍔ㄤ富搴旂敤
-    try:
-        from main import app
-        import uvicorn
-        from config import settings
-        from loguru import logger
-        
-        # 閰嶇疆鏃ュ織
-        logger.add(
-            settings.log_file,
-            rotation="1 day",
-            retention="7 days",
-            level=settings.log_level,
-            format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{function}:{line} - {message}",
-        )
-        
-        logger.info("鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟")
-        
-        # 鍚姩鏈嶅姟
-        uvicorn.run(
-            app,
-            host=settings.server_host,
-            port=settings.server_port,
-            log_level=settings.log_level.lower(),
-        )
-        
-    except Exception as e:
-        print(f"鍚姩澶辫触: {e}")
-        input("鎸変换鎰忛敭閫�鍑�...")
-        sys.exit(1)
diff --git a/test_integration.py b/test_integration.py
deleted file mode 100644
index 433d12a..0000000
--- a/test_integration.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/env python3
-"""
-闆嗘垚娴嬭瘯鑴氭湰
-鐢ㄤ簬娴嬭瘯E浜戠瀹�-DifyAI瀵规帴鏈嶅姟鐨勫熀鏈姛鑳�
-"""
-import requests
-import time
-from datetime import datetime
-
-
-def test_service_health():
-    """娴嬭瘯鏈嶅姟鍋ュ悍鐘舵��"""
-    try:
-        response = requests.get("http://localhost:8000/api/v1/health", timeout=5)
-        if response.status_code == 200:
-            print("鉁� 鏈嶅姟鍋ュ悍妫�鏌ラ�氳繃")
-            return True
-        else:
-            print(f"鉂� 鏈嶅姟鍋ュ悍妫�鏌ュけ璐�: {response.status_code}")
-            return False
-    except Exception as e:
-        print(f"鉂� 鏈嶅姟杩炴帴澶辫触: {str(e)}")
-        return False
-
-
-def test_callback_api():
-    """娴嬭瘯鍥炶皟API"""
-    # 妯℃嫙E浜戠瀹跺洖璋冩暟鎹�
-    callback_data = {
-        "account": "17200000000",
-        "messageType": "80001",
-        "wcId": "wxid_test_user",
-        "data": {
-            "content": "浣犲ソ锛岃繖鏄竴鏉℃祴璇曟秷鎭�",
-            "fromGroup": "test_group@chatroom",
-            "fromUser": "wxid_test_user_123",
-            "memberCount": 5,
-            "msgId": int(time.time()),
-            "newMsgId": int(time.time() * 1000),
-            "self": False,
-            "timestamp": int(time.time()),
-            "toUser": "wxid_bot",
-            "wId": "test-instance-id",
-        },
-    }
-
-    try:
-        response = requests.post(
-            "http://localhost:8000/api/v1/callback", json=callback_data, timeout=10
-        )
-
-        if response.status_code == 200:
-            result = response.json()
-            if result.get("success"):
-                print("鉁� 鍥炶皟API娴嬭瘯閫氳繃")
-                return True
-            else:
-                print(f"鉂� 鍥炶皟API澶勭悊澶辫触: {result.get('message')}")
-                return False
-        else:
-            print(f"鉂� 鍥炶皟API璇锋眰澶辫触: {response.status_code}")
-            return False
-
-    except Exception as e:
-        print(f"鉂� 鍥炶皟API娴嬭瘯寮傚父: {str(e)}")
-        return False
-
-
-def test_invalid_message():
-    """娴嬭瘯鏃犳晥娑堟伅澶勭悊"""
-    # 娴嬭瘯闈炵兢鑱婃秷鎭�
-    invalid_data = {
-        "account": "17200000000",
-        "messageType": "80002",  # 闈炵兢鑱婃秷鎭�
-        "wcId": "wxid_test_user",
-        "data": {
-            "content": "杩欐槸涓�鏉$鑱婃秷鎭�",
-            "fromUser": "wxid_test_user_123",
-            "self": False,
-        },
-    }
-
-    try:
-        response = requests.post(
-            "http://localhost:8000/api/v1/callback", json=invalid_data, timeout=10
-        )
-
-        if response.status_code == 200:
-            result = response.json()
-            if not result.get("success"):
-                print("鉁� 鏃犳晥娑堟伅杩囨护娴嬭瘯閫氳繃")
-                return True
-            else:
-                print("鉂� 鏃犳晥娑堟伅鏈姝g‘杩囨护")
-                return False
-        else:
-            print(f"鉂� 鏃犳晥娑堟伅娴嬭瘯璇锋眰澶辫触: {response.status_code}")
-            return False
-
-    except Exception as e:
-        print(f"鉂� 鏃犳晥娑堟伅娴嬭瘯寮傚父: {str(e)}")
-        return False
-
-
-def test_self_message():
-    """娴嬭瘯鑷繁鍙戦�佺殑娑堟伅杩囨护"""
-    self_message_data = {
-        "account": "17200000000",
-        "messageType": "80001",
-        "wcId": "wxid_test_user",
-        "data": {
-            "content": "杩欐槸鎴戣嚜宸卞彂閫佺殑娑堟伅",
-            "fromGroup": "test_group@chatroom",
-            "fromUser": "wxid_test_user_123",
-            "self": True,  # 鑷繁鍙戦�佺殑娑堟伅
-            "timestamp": int(time.time()),
-        },
-    }
-
-    try:
-        response = requests.post(
-            "http://localhost:8000/api/v1/callback", json=self_message_data, timeout=10
-        )
-
-        if response.status_code == 200:
-            result = response.json()
-            if not result.get("success"):
-                print("鉁� 鑷彂娑堟伅杩囨护娴嬭瘯閫氳繃")
-                return True
-            else:
-                print("鉂� 鑷彂娑堟伅鏈姝g‘杩囨护")
-                return False
-        else:
-            print(f"鉂� 鑷彂娑堟伅娴嬭瘯璇锋眰澶辫触: {response.status_code}")
-            return False
-
-    except Exception as e:
-        print(f"鉂� 鑷彂娑堟伅娴嬭瘯寮傚父: {str(e)}")
-        return False
-
-
-def main():
-    """涓绘祴璇曞嚱鏁�"""
-    print("=" * 50)
-    print("E浜戠瀹�-DifyAI瀵规帴鏈嶅姟闆嗘垚娴嬭瘯")
-    print("=" * 50)
-    print(f"娴嬭瘯鏃堕棿: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
-    print()
-
-    tests = [
-        ("鏈嶅姟鍋ュ悍妫�鏌�", test_service_health),
-        ("鍥炶皟API鍔熻兘", test_callback_api),
-        ("鏃犳晥娑堟伅杩囨护", test_invalid_message),
-        ("鑷彂娑堟伅杩囨护", test_self_message),
-    ]
-
-    passed = 0
-    total = len(tests)
-
-    for test_name, test_func in tests:
-        print(f"姝e湪娴嬭瘯: {test_name}")
-        if test_func():
-            passed += 1
-        print()
-
-    print("=" * 50)
-    print(f"娴嬭瘯缁撴灉: {passed}/{total} 閫氳繃")
-
-    if passed == total:
-        print("馃帀 鎵�鏈夋祴璇曢�氳繃锛�")
-        return 0
-    else:
-        print("鈿狅笍  閮ㄥ垎娴嬭瘯澶辫触锛岃妫�鏌ユ湇鍔¢厤缃�")
-        return 1
-
-
-if __name__ == "__main__":
-    exit(main())
diff --git a/tests/test_dify_streaming.py b/tests/test_dify_streaming.py
new file mode 100644
index 0000000..8fa8630
--- /dev/null
+++ b/tests/test_dify_streaming.py
@@ -0,0 +1,174 @@
+"""
+娴嬭瘯Dify娴佸紡妯″紡鍔熻兘
+"""
+
+import pytest
+import json
+from unittest.mock import Mock, patch, MagicMock
+from app.services.dify_client import DifyClient
+from config import settings
+
+
+class TestDifyStreaming:
+    """娴嬭瘯Dify娴佸紡妯″紡"""
+
+    def setup_method(self):
+        """娴嬭瘯鍓嶈缃�"""
+        self.client = DifyClient()
+
+    def test_process_stream_response_success(self):
+        """娴嬭瘯鎴愬姛澶勭悊娴佸紡鍝嶅簲"""
+        # 妯℃嫙娴佸紡鍝嶅簲鏁版嵁
+        stream_data = [
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}",
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}",
+            "data: {\"event\": \"message_end\", \"metadata\": {\"usage\": {\"total_tokens\": 10}}, \"usage\": {\"total_tokens\": 10}}",
+        ]
+
+        # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄
+        mock_response = Mock()
+        mock_response.iter_lines.return_value = stream_data
+
+        # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲
+        result = self.client._process_stream_response(mock_response, "test_user")
+
+        # 楠岃瘉缁撴灉
+        assert result is not None
+        assert result["answer"] == "Hello World"
+        assert result["conversation_id"] == "test-conv"
+        assert result["task_id"] == "test-task"
+        assert result["usage"]["total_tokens"] == 10
+
+    def test_process_stream_response_error(self):
+        """娴嬭瘯澶勭悊娴佸紡鍝嶅簲閿欒"""
+        # 妯℃嫙閿欒鍝嶅簲鏁版嵁
+        stream_data = [
+            "data: {\"event\": \"error\", \"message\": \"API璋冪敤澶辫触\", \"code\": \"500\"}",
+        ]
+
+        # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄
+        mock_response = Mock()
+        mock_response.iter_lines.return_value = stream_data
+
+        # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲
+        result = self.client._process_stream_response(mock_response, "test_user")
+
+        # 楠岃瘉缁撴灉
+        assert result is None
+
+    def test_process_stream_response_incomplete(self):
+        """娴嬭瘯澶勭悊涓嶅畬鏁寸殑娴佸紡鍝嶅簲"""
+        # 妯℃嫙涓嶅畬鏁村搷搴旀暟鎹紙缂哄皯message_end浜嬩欢锛�
+        stream_data = [
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}",
+        ]
+
+        # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄
+        mock_response = Mock()
+        mock_response.iter_lines.return_value = stream_data
+
+        # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲
+        result = self.client._process_stream_response(mock_response, "test_user")
+
+        # 楠岃瘉缁撴灉 - 鍗充娇娌℃湁message_end浜嬩欢锛屽彧瑕佹湁鍐呭鍜宑onversation_id涔熷簲璇ヨ繑鍥炵粨鏋�
+        assert result is not None
+        assert result["answer"] == "Hello"
+        assert result["conversation_id"] == "test-conv"
+
+    @patch('app.services.dify_client.settings')
+    def test_send_message_uses_streaming_when_enabled(self, mock_settings):
+        """娴嬭瘯褰撳惎鐢ㄦ祦寮忔ā寮忔椂浣跨敤娴佸紡鍙戦��"""
+        # 璁剧疆閰嶇疆涓哄惎鐢ㄦ祦寮忔ā寮�
+        mock_settings.dify_streaming_enabled = True
+        
+        # 妯℃嫙娴佸紡鍙戦�佹柟娉�
+        with patch.object(self.client, 'send_chat_message_stream') as mock_stream:
+            mock_stream.return_value = {"answer": "test response", "conversation_id": "test-conv"}
+            
+            result = self.client.send_message("test query", "test_user")
+            
+            # 楠岃瘉璋冪敤浜嗘祦寮忔柟娉�
+            mock_stream.assert_called_once_with("test query", "test_user", None, None)
+            assert result["answer"] == "test response"
+
+    @patch('app.services.dify_client.settings')
+    def test_send_message_uses_blocking_when_disabled(self, mock_settings):
+        """娴嬭瘯褰撶鐢ㄦ祦寮忔ā寮忔椂浣跨敤闃诲鍙戦��"""
+        # 璁剧疆閰嶇疆涓虹鐢ㄦ祦寮忔ā寮�
+        mock_settings.dify_streaming_enabled = False
+        
+        # 妯℃嫙闃诲鍙戦�佹柟娉�
+        with patch.object(self.client, 'send_chat_message') as mock_blocking:
+            mock_blocking.return_value = {"answer": "test response", "conversation_id": "test-conv"}
+            
+            result = self.client.send_message("test query", "test_user")
+            
+            # 楠岃瘉璋冪敤浜嗛樆濉炴柟娉�
+            mock_blocking.assert_called_once_with("test query", "test_user", None, None)
+            assert result["answer"] == "test response"
+
+    @patch('app.services.dify_client.settings')
+    def test_send_message_force_streaming_override(self, mock_settings):
+        """娴嬭瘯寮哄埗娴佸紡妯″紡瑕嗙洊閰嶇疆"""
+        # 璁剧疆閰嶇疆涓虹鐢ㄦ祦寮忔ā寮�
+        mock_settings.dify_streaming_enabled = False
+        
+        # 妯℃嫙娴佸紡鍙戦�佹柟娉�
+        with patch.object(self.client, 'send_chat_message_stream') as mock_stream:
+            mock_stream.return_value = {"answer": "test response", "conversation_id": "test-conv"}
+            
+            # 寮哄埗浣跨敤娴佸紡妯″紡
+            result = self.client.send_message("test query", "test_user", force_streaming=True)
+            
+            # 楠岃瘉璋冪敤浜嗘祦寮忔柟娉曪紙瑕嗙洊浜嗛厤缃級
+            mock_stream.assert_called_once_with("test query", "test_user", None, None)
+            assert result["answer"] == "test response"
+
+    def test_process_stream_response_with_ping_events(self):
+        """娴嬭瘯澶勭悊鍖呭惈ping浜嬩欢鐨勬祦寮忓搷搴�"""
+        # 妯℃嫙鍖呭惈ping浜嬩欢鐨勫搷搴旀暟鎹�
+        stream_data = [
+            "data: {\"event\": \"ping\"}",
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}",
+            "data: {\"event\": \"ping\"}",
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}",
+            "data: {\"event\": \"message_end\", \"metadata\": {\"usage\": {\"total_tokens\": 10}}}",
+        ]
+
+        # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄
+        mock_response = Mock()
+        mock_response.iter_lines.return_value = stream_data
+
+        # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲
+        result = self.client._process_stream_response(mock_response, "test_user")
+
+        # 楠岃瘉缁撴灉锛坧ing浜嬩欢搴旇琚拷鐣ワ級
+        assert result is not None
+        assert result["answer"] == "Hello World"
+        assert result["conversation_id"] == "test-conv"
+
+    def test_process_stream_response_with_invalid_json(self):
+        """娴嬭瘯澶勭悊鍖呭惈鏃犳晥JSON鐨勬祦寮忓搷搴�"""
+        # 妯℃嫙鍖呭惈鏃犳晥JSON鐨勫搷搴旀暟鎹�
+        stream_data = [
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}",
+            "data: invalid json data",
+            "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}",
+            "data: {\"event\": \"message_end\"}",
+        ]
+
+        # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄
+        mock_response = Mock()
+        mock_response.iter_lines.return_value = stream_data
+
+        # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲
+        result = self.client._process_stream_response(mock_response, "test_user")
+
+        # 楠岃瘉缁撴灉锛堟棤鏁圝SON搴旇琚烦杩囷級
+        assert result is not None
+        assert result["answer"] == "Hello World"
+        assert result["conversation_id"] == "test-conv"
+
+
+if __name__ == "__main__":
+    pytest.main([__file__])
diff --git a/tests/test_friend_ignore_service.py b/tests/test_friend_ignore_service.py
new file mode 100644
index 0000000..6bc92a7
--- /dev/null
+++ b/tests/test_friend_ignore_service.py
@@ -0,0 +1,258 @@
+"""
+濂藉弸蹇界暐鏈嶅姟娴嬭瘯
+"""
+
+import pytest
+from unittest.mock import Mock, patch
+from app.services.friend_ignore_service import FriendIgnoreService
+
+
+class TestFriendIgnoreService:
+    """濂藉弸蹇界暐鏈嶅姟娴嬭瘯绫�"""
+    
+    def setup_method(self):
+        """娴嬭瘯鍓嶅噯澶�"""
+        self.service = FriendIgnoreService()
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_add_friends_to_ignore_list_success(self, mock_redis_queue):
+        """娴嬭瘯鎴愬姛娣诲姞濂藉弸鍒板拷鐣ュ垪琛�"""
+        # 妯℃嫙Redis鎿嶄綔
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        
+        friends = ["wxid_test1", "wxid_test2", "wxid_test3"]
+        
+        result = self.service.add_friends_to_ignore_list(friends)
+        
+        # 楠岃瘉缁撴灉
+        assert result is True
+        
+        # 楠岃瘉Redis鎿嶄綔琚皟鐢�
+        mock_redis_client.delete.assert_called_once_with(self.service.ignore_list_key)
+        mock_redis_client.sadd.assert_called_once_with(self.service.ignore_list_key, *friends)
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_add_friends_to_ignore_list_empty(self, mock_redis_queue):
+        """娴嬭瘯娣诲姞绌哄ソ鍙嬪垪琛�"""
+        result = self.service.add_friends_to_ignore_list([])
+        
+        # 楠岃瘉缁撴灉
+        assert result is True
+        
+        # 楠岃瘉Redis鎿嶄綔鏈璋冪敤
+        mock_redis_queue.redis_client.delete.assert_not_called()
+        mock_redis_queue.redis_client.sadd.assert_not_called()
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_is_friend_ignored_true(self, mock_redis_queue):
+        """娴嬭瘯妫�鏌ュソ鍙嬪湪蹇界暐鍒楄〃涓�"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.return_value = True
+        
+        result = self.service.is_friend_ignored("wxid_test1")
+        
+        assert result is True
+        mock_redis_client.sismember.assert_called_once_with(self.service.ignore_list_key, "wxid_test1")
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_is_friend_ignored_false(self, mock_redis_queue):
+        """娴嬭瘯妫�鏌ュソ鍙嬩笉鍦ㄥ拷鐣ュ垪琛ㄤ腑"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.return_value = False
+        
+        result = self.service.is_friend_ignored("wxid_test1")
+        
+        assert result is False
+        mock_redis_client.sismember.assert_called_once_with(self.service.ignore_list_key, "wxid_test1")
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_get_ignore_list(self, mock_redis_queue):
+        """娴嬭瘯鑾峰彇蹇界暐鍒楄〃"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        expected_set = {"wxid_test1", "wxid_test2"}
+        mock_redis_client.smembers.return_value = expected_set
+        
+        result = self.service.get_ignore_list()
+        
+        assert result == expected_set
+        mock_redis_client.smembers.assert_called_once_with(self.service.ignore_list_key)
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_remove_friend_from_ignore_list(self, mock_redis_queue):
+        """娴嬭瘯浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙�"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.srem.return_value = 1  # 琛ㄧず鎴愬姛绉婚櫎
+        
+        result = self.service.remove_friend_from_ignore_list("wxid_test1")
+        
+        assert result is True
+        mock_redis_client.srem.assert_called_once_with(self.service.ignore_list_key, "wxid_test1")
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_clear_ignore_list(self, mock_redis_queue):
+        """娴嬭瘯娓呯┖蹇界暐鍒楄〃"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        
+        result = self.service.clear_ignore_list()
+        
+        assert result is True
+        mock_redis_client.delete.assert_called_once_with(self.service.ignore_list_key)
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_get_ignore_list_count(self, mock_redis_queue):
+        """娴嬭瘯鑾峰彇蹇界暐鍒楄〃鏁伴噺"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.scard.return_value = 5
+        
+        result = self.service.get_ignore_list_count()
+        
+        assert result == 5
+        mock_redis_client.scard.assert_called_once_with(self.service.ignore_list_key)
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_add_friends_exception_handling(self, mock_redis_queue):
+        """娴嬭瘯娣诲姞濂藉弸鏃剁殑寮傚父澶勭悊"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sadd.side_effect = Exception("Redis error")
+        
+        result = self.service.add_friends_to_ignore_list(["wxid_test1"])
+        
+        assert result is False
+    
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_is_friend_ignored_exception_handling(self, mock_redis_queue):
+        """娴嬭瘯妫�鏌ュソ鍙嬫椂鐨勫紓甯稿鐞�"""
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.side_effect = Exception("Redis error")
+        
+        result = self.service.is_friend_ignored("wxid_test1")
+
+        assert result is False
+
+    @patch('app.services.friend_ignore_service.settings')
+    @patch('app.services.friend_ignore_service.redis_queue')
+    @patch('app.services.friend_ignore_service.get_db')
+    def test_is_friend_ignored_whitelist(self, mock_get_db, mock_redis_queue, mock_settings):
+        """娴嬭瘯鐧藉悕鍗曞姛鑳�"""
+        # 妯℃嫙閰嶇疆
+        mock_settings.friend_ignore_enabled = True
+        mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1", "娴嬭瘯鐢ㄦ埛2"]
+
+        # 妯℃嫙鏁版嵁搴撴煡璇�
+        mock_db = Mock()
+        mock_get_db.return_value.__next__.return_value.__enter__.return_value = mock_db
+        mock_get_db.return_value.__next__.return_value.__exit__.return_value = None
+
+        # 妯℃嫙鑱旂郴浜烘煡璇㈢粨鏋� - 鏍规嵁鏄电О杩斿洖涓嶅悓鐨勮仈绯讳汉
+        def mock_query_side_effect(*args, **kwargs):
+            mock_query = Mock()
+            mock_filter = Mock()
+            mock_query.filter.return_value = mock_filter
+
+            # 鏍规嵁鏌ヨ鏉′欢杩斿洖涓嶅悓鐨勭粨鏋�
+            def mock_first():
+                # 杩欓噷绠�鍖栧鐞嗭紝鍋囪鏌ヨ"娴嬭瘯鐢ㄦ埛1"鏃惰繑鍥瀢xid_whitelist1
+                mock_contact = Mock()
+                mock_contact.wc_id = "wxid_whitelist1"
+                return mock_contact
+
+            mock_filter.first = mock_first
+            return mock_query
+
+        mock_db.query.side_effect = mock_query_side_effect
+
+        # 妯℃嫙Redis鎿嶄綔
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.return_value = True  # 鍦ㄥ拷鐣ュ垪琛ㄤ腑
+
+        # 娴嬭瘯鐧藉悕鍗曠敤鎴凤紙鍗充娇鍦ㄥ拷鐣ュ垪琛ㄤ腑涔熶笉搴旇琚拷鐣ワ級
+        result = self.service.is_friend_ignored("wxid_whitelist1")
+        assert result is False
+
+        # 娴嬭瘯闈炵櫧鍚嶅崟鐢ㄦ埛锛堝湪蹇界暐鍒楄〃涓簲璇ヨ蹇界暐锛�
+        result = self.service.is_friend_ignored("wxid_normal_user")
+        assert result is True
+
+    @patch('app.services.friend_ignore_service.settings')
+    def test_is_friend_ignored_disabled(self, mock_settings):
+        """娴嬭瘯鍔熻兘绂佺敤鏃剁殑琛屼负"""
+        mock_settings.friend_ignore_enabled = False
+        mock_settings.friend_ignore_whitelist = []
+
+        result = self.service.is_friend_ignored("wxid_test1")
+        assert result is False
+
+    @patch('app.services.friend_ignore_service.settings')
+    @patch('app.services.friend_ignore_service.redis_queue')
+    @patch('app.services.friend_ignore_service.get_db')
+    def test_get_ignore_status_info(self, mock_get_db, mock_redis_queue, mock_settings):
+        """娴嬭瘯鑾峰彇璇︾粏鐘舵�佷俊鎭�"""
+        mock_settings.friend_ignore_enabled = True
+        mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1"]
+
+        # 妯℃嫙鏁版嵁搴撴煡璇�
+        mock_db = Mock()
+        mock_get_db.return_value.__enter__.return_value = mock_db
+        mock_get_db.return_value.__exit__.return_value = None
+
+        # 妯℃嫙鑱旂郴浜烘煡璇㈢粨鏋�
+        mock_contact = Mock()
+        mock_contact.wc_id = "wxid_whitelist1"
+        mock_db.query.return_value.filter.return_value.first.return_value = mock_contact
+
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.return_value = True
+
+        # 娴嬭瘯鐧藉悕鍗曠敤鎴�
+        result = self.service.get_ignore_status_info("wxid_whitelist1")
+        assert result["w_id"] == "wxid_whitelist1"
+        assert result["in_whitelist"] is True
+        assert result["final_ignored"] is False
+        assert "鐧藉悕鍗�" in result["reason"]
+        assert result["whitelist_nicknames"] == ["娴嬭瘯鐢ㄦ埛1"]
+
+        # 娴嬭瘯鏅�氱敤鎴�
+        result = self.service.get_ignore_status_info("wxid_normal_user")
+        assert result["w_id"] == "wxid_normal_user"
+        assert result["in_whitelist"] is False
+        assert result["in_ignore_list"] is True
+        assert result["final_ignored"] is True
+
+    @patch('app.services.friend_ignore_service.FriendIgnoreService._get_whitelist_wids')
+    @patch('app.services.friend_ignore_service.settings')
+    @patch('app.services.friend_ignore_service.redis_queue')
+    def test_is_friend_ignored_nickname_whitelist(self, mock_redis_queue, mock_settings, mock_get_whitelist_wids):
+        """娴嬭瘯鏄电О鐧藉悕鍗曞姛鑳�"""
+        # 妯℃嫙閰嶇疆
+        mock_settings.friend_ignore_enabled = True
+        mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1", "娴嬭瘯鐢ㄦ埛2"]
+
+        # 妯℃嫙鐧藉悕鍗晈_id杞崲缁撴灉
+        mock_get_whitelist_wids.return_value = ["wxid_whitelist1", "wxid_whitelist2"]
+
+        # 妯℃嫙Redis鎿嶄綔
+        mock_redis_client = Mock()
+        mock_redis_queue.redis_client = mock_redis_client
+        mock_redis_client.sismember.return_value = True  # 鍦ㄥ拷鐣ュ垪琛ㄤ腑
+
+        # 娴嬭瘯鐧藉悕鍗曠敤鎴凤紙鍗充娇鍦ㄥ拷鐣ュ垪琛ㄤ腑涔熶笉搴旇琚拷鐣ワ級
+        result = self.service.is_friend_ignored("wxid_whitelist1")
+        assert result is False
+
+        # 娴嬭瘯闈炵櫧鍚嶅崟鐢ㄦ埛锛堝湪蹇界暐鍒楄〃涓簲璇ヨ蹇界暐锛�
+        result = self.service.is_friend_ignored("wxid_normal_user")
+        assert result is True
+
+        # 楠岃瘉鏂规硶琚皟鐢�
+        assert mock_get_whitelist_wids.call_count >= 2
diff --git a/tests/test_message_processor.py b/tests/test_message_processor.py
index e4fe1da..5e1eee2 100644
--- a/tests/test_message_processor.py
+++ b/tests/test_message_processor.py
@@ -71,6 +71,44 @@
         
         result = self.processor.is_valid_group_message(callback_data)
         assert result is False
+
+    @patch('app.services.message_processor.friend_ignore_service')
+    def test_is_valid_group_message_friend_ignored(self, mock_friend_ignore_service):
+        """娴嬭瘯濂藉弸鍦ㄥ拷鐣ュ垪琛ㄤ腑鐨勬秷鎭�"""
+        mock_friend_ignore_service.is_friend_ignored.return_value = True
+
+        callback_data = {
+            "messageType": "80001",
+            "data": {
+                "fromUser": "wxid_test123",
+                "fromGroup": "group123@chatroom",
+                "content": "娴嬭瘯娑堟伅",
+                "self": False
+            }
+        }
+
+        result = self.processor.is_valid_group_message(callback_data)
+        assert result is False
+        mock_friend_ignore_service.is_friend_ignored.assert_called_once_with("wxid_test123")
+
+    @patch('app.services.message_processor.friend_ignore_service')
+    def test_is_valid_group_message_friend_not_ignored(self, mock_friend_ignore_service):
+        """娴嬭瘯濂藉弸涓嶅湪蹇界暐鍒楄〃涓殑娑堟伅"""
+        mock_friend_ignore_service.is_friend_ignored.return_value = False
+
+        callback_data = {
+            "messageType": "80001",
+            "data": {
+                "fromUser": "wxid_test123",
+                "fromGroup": "group123@chatroom",
+                "content": "娴嬭瘯娑堟伅",
+                "self": False
+            }
+        }
+
+        result = self.processor.is_valid_group_message(callback_data)
+        assert result is True
+        mock_friend_ignore_service.is_friend_ignored.assert_called_once_with("wxid_test123")
     
     @patch('app.services.message_processor.redis_queue')
     def test_enqueue_callback_message_success(self, mock_redis_queue):
diff --git a/uninstall_service.bat b/uninstall_service.bat
deleted file mode 100644
index c361981..0000000
--- a/uninstall_service.bat
+++ /dev/null
@@ -1,61 +0,0 @@
-@echo off
-chcp 65001 >nul
-echo ========================================
-echo E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 Windows鏈嶅姟鍗歌浇鑴氭湰
-echo ========================================
-echo.
-
-:: 妫�鏌ョ鐞嗗憳鏉冮檺
-net session >nul 2>&1
-if %errorLevel% == 0 (
-    echo 妫�娴嬪埌绠$悊鍛樻潈闄愶紝缁х画鍗歌浇...
-) else (
-    echo 閿欒锛氶渶瑕佺鐞嗗憳鏉冮檺鎵嶈兘鍗歌浇Windows鏈嶅姟
-    echo 璇峰彸閿偣鍑绘鑴氭湰锛岄�夋嫨"浠ョ鐞嗗憳韬唤杩愯"
-    pause
-    exit /b 1
-)
-
-set "SERVICE_NAME=ECloudDifyService"
-
-echo 鏈嶅姟鍚嶇О: %SERVICE_NAME%
-echo.
-
-:: 妫�鏌ユ湇鍔℃槸鍚﹀瓨鍦�
-echo 妫�鏌ユ湇鍔℃槸鍚﹀瓨鍦�...
-sc query "%SERVICE_NAME%" >nul 2>&1
-if %errorLevel% == 0 (
-    echo 鎵惧埌鏈嶅姟锛屾鍦ㄥ嵏杞�...
-    
-    :: 鍋滄鏈嶅姟
-    echo 姝e湪鍋滄鏈嶅姟...
-    sc stop "%SERVICE_NAME%" >nul 2>&1
-    if %errorLevel% == 0 (
-        echo 鏈嶅姟宸插仠姝�
-    ) else (
-        echo 鏈嶅姟鍙兘宸茬粡鍋滄鎴栧仠姝㈠け璐�
-    )
-    
-    :: 绛夊緟鏈嶅姟瀹屽叏鍋滄
-    echo 绛夊緟鏈嶅姟瀹屽叏鍋滄...
-    timeout /t 5 /nobreak >nul
-    
-    :: 鍒犻櫎鏈嶅姟
-    echo 姝e湪鍒犻櫎鏈嶅姟...
-    sc delete "%SERVICE_NAME%"
-    if %errorLevel% == 0 (
-        echo 鏈嶅姟鍒犻櫎鎴愬姛锛�
-    ) else (
-        echo 閿欒锛氭湇鍔″垹闄ゅけ璐�
-        pause
-        exit /b 1
-    )
-) else (
-    echo 鏈壘鍒版湇鍔� %SERVICE_NAME%
-    echo 鍙兘鏈嶅姟宸茬粡琚垹闄ゆ垨浠庢湭瀹夎
-)
-
-echo.
-echo 鏈嶅姟鍗歌浇瀹屾垚锛�
-echo.
-pause

--
Gitblit v1.9.1