Commit d7337bcd by luoqi

feat:阶段提交

parent 5ff33ce4
# 使用 CircleCI 2.1 版本的配置语法
version: 2.1
# "Jobs" 定义了要执行的具体任务
jobs:
# 将这个任务命名为 "deploy"
deploy:
# 指定运行此任务的环境。我们选择一个预装了常用工具(如 git, ssh)的基础 Docker 镜像
docker:
- image: cimg/base:stable
# "steps" 定义了任务中要按顺序执行的具体步骤
steps:
# 第一步:添加 SSH 密钥,用于连接你的部署服务器
# CircleCI 会从你的项目设置中,加载你预先存好的那个 SSH 私钥
- add_ssh_keys:
fingerprints:
- "SHA256:VsEKAt0iuZUz4zVUWBSmFm1qS/CvL8goIsNDK8zN0VQ"
# 第二步:执行连接服务器并部署的命令
- run:
name: Connect and Deploy to Server
# 这里的 command 就是您提供的部署脚本
command: |
echo "准备连接到部署服务器..."
# 使用 ssh 命令连接你的服务器
# -p $SSH_PORT 指定了你修改后的端口
# -o StrictHostKeyChecking=no 避免了首次连接时需要手动确认主机的提示,这在自动化脚本中是必需的
# $SSH_USER 和 $SSH_HOST 是你需要在 CircleCI 网站上设置的环境变量
ssh -p 19822 -o StrictHostKeyChecking=no root@47.251.104.47 << 'EOF'
# --- 以下是在你的部署服务器上执行的命令 ---
echo "✅ 连接服务器成功,开始执行部署脚本..."
# 1. 进入你的项目工作目录
# 请确保这个目录在服务器上已经存在,并且已经从 GitLab 克隆过一次
echo "进入项目目录: customer-recall"
cd customer-recall
# 2. 从 master 分支拉取最新的代码
echo "正在从 GitLab (origin) 拉取最新代码..."
git pull origin master
# 3. 使用 Docker Compose 重新构建并启动服务
echo "正在使用 Docker Compose 部署..."
docker compose up -d --build
echo "🚀 部署流程执行完毕!"
# --- 远程服务器上的命令结束 ---
EOF
# "Workflows" 用来编排和组织 Jobs 的执行流程和触发条件
workflows:
# 将这个工作流命名为 "build-and-deploy"
build-and-deploy:
jobs:
- deploy:
# "filters" 是过滤器,用来定义触发此 Job 的条件
filters:
branches:
# "only" 表示只有当代码被推送到指定分支时,才执行这个 Job
only:
- master
name: Production Deployment with Database Backup
on:
push:
branches: [ master ]
env:
PROJECT_DIR: customer-recall
BACKUP_DIR: /backup/database
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}
- name: Deploy to Production
run: |
echo "Starting production deployment..."
echo "Deployment time: $(date)"
echo "Project: Patient Profile Follow-up System"
echo "Uploading deployment script..."
scp -P ${{ secrets.SSH_PORT }} deploy_scripts/deploy_with_backup.sh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/tmp/
echo "Connecting to production server..."
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "chmod +x /tmp/deploy_with_backup.sh"
echo "Executing deployment script..."
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "/tmp/deploy_with_backup.sh"
echo "Deployment completed!"
- name: Test Deployment
run: |
echo "Testing deployment..."
echo "Current time: $(date)"
echo "Project: Patient Profile Follow-up System"
echo "Test successful!"
- name: Check Production Status
run: |
echo "Checking production environment..."
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ env.PROJECT_DIR }} && docker compose ps"
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "ls -lh ${{ env.BACKUP_DIR }}/production_backup_*.sql 2>/dev/null || echo 'No backup files'"
echo "Production environment check completed!"
test:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Tests
run: |
echo "Running tests..."
echo "Test time: $(date)"
echo "Project: Patient Profile Follow-up System"
echo "All tests passed!"
\ No newline at end of file
name: Test Workflow
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Hello World
run: echo "Hello, world!"
\ No newline at end of file
stages:
- validate
- deploy
- migrate
- test
variables:
DOCKER_DRIVER: overlay2
# 验证阶段:检查迁移文件
validate_migrations:
stage: validate
image: alpine:latest
before_script:
- apk add --no-cache openssh-client python3 py3-pip
- pip3 install pymysql
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "🔍 验证迁移文件..."
- scp -P $SSH_PORT -r migrations/ $SSH_USER@$SSH_HOST:/tmp/
- scp -P $SSH_PORT database_migration_manager.py $SSH_USER@$SSH_HOST:/tmp/
- scp -P $SSH_PORT migrate.py $SSH_USER@$SSH_HOST:/tmp/
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd /tmp && python3 migrate.py validate"
only:
- master
# 部署阶段:更新代码和重启服务
deploy_to_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "🚀 开始部署到生产环境..."
- echo "📋 上传部署脚本"
- scp -P $SSH_PORT deploy_scripts/deploy_with_backup.sh $SSH_USER@$SSH_HOST:/tmp/
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "chmod +x /tmp/deploy_with_backup.sh"
- echo "🔄 执行部署脚本"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "/tmp/deploy_with_backup.sh"
- echo "📋 上传迁移文件"
- scp -P $SSH_PORT -r migrations/ $SSH_USER@$SSH_HOST:customer-recall/
- scp -P $SSH_PORT database_migration_manager.py $SSH_USER@$SSH_HOST:customer-recall/
- scp -P $SSH_PORT migrate.py $SSH_USER@$SSH_HOST:customer-recall/
- echo "✅ 部署完成,等待迁移阶段..."
only:
- master
# 迁移阶段:执行数据库迁移
migrate_database:
stage: migrate
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "🗄️ 开始数据库迁移..."
- echo "📊 检查迁移状态"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python migrate.py status"
- echo "🔍 试运行迁移(验证SQL语法)"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python migrate.py migrate --dry-run"
- echo "🚀 执行数据库迁移"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python migrate.py migrate"
- echo "📊 检查迁移结果"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python migrate.py status"
- echo "🔍 验证数据库结构"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -p\$DB_PASSWORD -e 'USE callback_system; SHOW TABLES;'"
- echo "📋 检查关键表的记录数"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -p\$DB_PASSWORD -e 'USE callback_system; SELECT \"users\" as table_name, COUNT(*) as count FROM users UNION SELECT \"patients\", COUNT(*) FROM patients UNION SELECT \"callback_records\", COUNT(*) FROM callback_records;'"
only:
- master
dependencies:
- deploy_to_production
# 测试阶段:验证部署结果
post_deploy_test:
stage: test
image: alpine:latest
before_script:
- apk add --no-cache openssh-client curl
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "🧪 开始部署后测试..."
- echo "🔍 检查容器状态"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose ps"
- echo "🌐 检查应用健康状态"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && curl -f http://localhost:5000/login || echo 'Application health check failed'"
- echo "🗄️ 检查数据库连接"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -p\$DB_PASSWORD -e 'SELECT 1;'"
- echo "📊 最终数据统计"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -p\$DB_PASSWORD -e 'USE callback_system; SELECT clinic_name, COUNT(*) as patient_count FROM patients GROUP BY clinic_name ORDER BY patient_count DESC;'"
- echo "✅ 部署测试完成"
only:
- master
dependencies:
- migrate_database
# 手动回滚任务(仅在需要时手动触发)
rollback_migration:
stage: migrate
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "🔄 手动回滚迁移..."
- echo "⚠️ 警告:这是一个危险操作!"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python migrate.py status"
- echo "请在GitLab CI界面手动指定要回滚的版本号"
- echo "使用方法:docker compose exec -T patient_callback_app python migrate.py rollback VERSION"
when: manual
only:
- master
# .gitlab-ci.yml
stages:
- deploy
- test
deploy_to_production:
stage: deploy
image: alpine:latest
# --- 在这里添加 tags 部分 ---
tags:
- jarvis
- jarvis # 指定使用带有 "jarvis" 标签 Runner
# ---------------------------
# before_script, script, only 这些部分保持不变
before_script:
- 'which ssh-agent || ( apk add --update --no-cache openssh-client )'
- 'eval $(ssh-agent -s)'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script:
- echo "Starting production deployment"
- echo "SSH connection setup completed"
- echo "Testing SSH connection"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "echo 'SSH connection successful'"
- echo "SSH connection test completed"
- echo "Checking current Docker containers"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "docker ps -a"
- echo "Checking project directory"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "ls -la customer-recall/ 2>/dev/null || echo 'Project directory not found'"
- echo "Uploading deployment script"
- scp -P $SSH_PORT deploy_scripts/deploy_with_backup.sh $SSH_USER@$SSH_HOST:/tmp/
- echo "Setting script permissions"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "chmod +x /tmp/deploy_with_backup.sh"
- echo "Executing deployment script"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "/tmp/deploy_with_backup.sh"
- echo "Checking deployment status"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose ps"
- echo "Basic database check start"
- echo "Check MySQL container logs"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose logs --tail=5 mysql"
- echo "Check MySQL container environment"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql env | grep -i mysql || echo 'Cannot get MySQL environment'"
- echo "Try database connection with correct password"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'SHOW DATABASES;' || echo 'Database connection failed'"
- echo "Check database tables"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'USE callback_system; SHOW TABLES;' || echo 'Cannot show tables'"
- echo "Check patients table count"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'USE callback_system; SELECT COUNT(*) as total_patients FROM patients;' || echo 'Cannot count patients'"
- echo "Check clinic distribution"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'USE callback_system; SELECT clinic_name, COUNT(*) as count FROM patients GROUP BY clinic_name ORDER BY count DESC;' || echo 'Cannot get clinic distribution'"
- echo "Basic database check end"
- echo "Clinic JSON files check start"
- echo "Check clinic patient json directory"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && ls -la 诊所患者json/ 2>/dev/null || echo 'Clinic patient json directory not found'"
- echo "Check specific clinic files"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && for file in 东亭门诊.json 大丰门诊.json 惠山门诊.json 新吴门诊.json 河埒门诊.json 红豆门诊.json 通善口腔医院.json 马山门诊.json 学前街门诊.json; do if [ -f \"诊所患者json/\$file\" ]; then echo \"✓ \$file exists\"; else echo \"✗ \$file missing\"; fi; done"
- echo "Check 学前街门诊.json specifically"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && if [ -f \"诊所患者json/学前街门诊.json\" ]; then echo \"学前街门诊.json exists\"; ls -la \"诊所患者json/学前街门诊.json\"; else echo \"学前街门诊.json missing - this is the problem!\"; fi"
- echo "Clinic JSON files check end"
- echo "Data import check start"
- echo "Check safe_import_patients.py script"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && ls -la safe_import_patients.py 2>/dev/null || echo 'safe_import_patients.py not found'"
- echo "Check Python environment in app container"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python --version 2>/dev/null || echo 'Cannot check Python version'"
- echo "Try to run safe_import_patients.py manually"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python safe_import_patients.py 2>&1 || echo 'safe_import_patients.py execution failed'"
- echo "Data import check end"
- echo "Data import execution start"
- echo "Execute data import script"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T patient_callback_app python safe_import_patients.py"
- echo "Check import results"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'USE callback_system; SELECT COUNT(*) as total_patients FROM patients;'"
- ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "cd customer-recall && docker compose exec -T mysql mysql -u root -pdev_password_123 -e 'USE callback_system; SELECT clinic_name, COUNT(*) as count FROM patients GROUP BY clinic_name ORDER BY count DESC;'"
- echo "Data import execution end"
- echo "Deployment completed successfully"
- |
ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "
echo '✅ 连接服务器成功,开始执行部署脚本...'
cd customer-recall
git pull origin master
docker compose up -d --build
echo '� 部署流程执行完毕!!!'
"
only:
- master
simple_test:
stage: test
image: alpine:latest
tags:
- jarvis
script:
- echo "Starting test"
- echo "Test completed"
only:
- master
\ No newline at end of file
# 数据库迁移管理系统使用指南
## 📋 概述
这是一个为 Flask + MySQL + Docker 环境设计的数据库迁移管理系统,提供安全、可控的数据库结构更新机制。
## 🏗️ 系统架构
### 核心组件
- **DatabaseMigrationManager**: 迁移管理器核心类
- **migrate.py**: 命令行工具
- **migrations/**: 迁移文件目录
- **CI/CD集成**: 自动化部署和迁移
### 安全特性
-**自动备份**: 每次迁移前自动创建数据库备份
-**事务性执行**: 迁移失败自动回滚
-**版本控制**: 完整的迁移历史记录
-**校验和验证**: 防止迁移文件被意外修改
-**试运行模式**: 执行前验证SQL语法
## 🚀 快速开始
### 1. 安装依赖
```bash
pip install pymysql
```
### 2. 配置数据库连接
确保 `database_config.py` 中的 `get_database_config()` 函数返回正确的数据库配置。
### 3. 初始化迁移系统
```bash
python migrate.py status
```
## 📝 创建迁移
### 创建新迁移文件
```bash
python migrate.py create "add_user_email" "为用户表添加邮箱字段"
```
这会创建一个新的迁移文件:`migrations/20250105_143022_add_user_email.sql`
### 编辑迁移文件
```sql
-- ==========================================
-- UP Migration (执行迁移)
-- ==========================================
ALTER TABLE users
ADD COLUMN email VARCHAR(255) NULL AFTER username;
ALTER TABLE users
ADD UNIQUE INDEX idx_users_email (email);
-- ==========================================
-- DOWN Migration (回滚迁移)
-- ==========================================
ALTER TABLE users
DROP INDEX idx_users_email;
ALTER TABLE users
DROP COLUMN email;
```
## 🔧 执行迁移
### 查看迁移状态
```bash
python migrate.py status
```
### 试运行迁移(推荐)
```bash
python migrate.py migrate --dry-run
```
### 执行迁移
```bash
python migrate.py migrate
```
### 执行到指定版本
```bash
python migrate.py migrate --target=20250105_143022
```
## 🔄 回滚迁移
### 回滚指定版本
```bash
python migrate.py rollback 20250105_143022
```
**注意**: 回滚操作需要迁移文件包含完整的 DOWN 部分。
## 🔍 验证和维护
### 验证迁移文件
```bash
python migrate.py validate
```
### 查看详细状态
```bash
python migrate.py status
```
## 🐳 Docker 环境使用
### 在容器中执行迁移
```bash
docker compose exec patient_callback_app python migrate.py status
docker compose exec patient_callback_app python migrate.py migrate --dry-run
docker compose exec patient_callback_app python migrate.py migrate
```
## 🚀 CI/CD 集成
### 使用新的 CI/CD 配置
1.`.gitlab-ci-new.yml` 重命名为 `.gitlab-ci.yml`
2. 确保环境变量配置正确:
- `SSH_PRIVATE_KEY`
- `SSH_KNOWN_HOSTS`
- `SSH_PORT`
- `SSH_USER`
- `SSH_HOST`
- `DB_PASSWORD`
### 部署流程
1. **验证阶段**: 验证迁移文件语法和完整性
2. **部署阶段**: 更新代码和重启容器
3. **迁移阶段**: 执行数据库迁移
4. **测试阶段**: 验证部署结果
## 📁 文件结构
```
customer-recall/
├── database_migration_manager.py # 迁移管理器
├── migrate.py # 命令行工具
├── migrations/ # 迁移文件目录
│ ├── 20250101_000000_initial_schema.sql
│ └── 20250102_120000_add_user_email.sql
├── deploy_scripts/
│ └── deploy_with_migration.sh # 集成迁移的部署脚本
└── .gitlab-ci-new.yml # 新的CI/CD配置
```
## 🛡️ 安全最佳实践
### 1. 迁移文件编写
- ✅ 总是提供 DOWN 迁移用于回滚
- ✅ 使用事务安全的操作
- ✅ 避免删除数据的操作
- ✅ 大表操作考虑分批执行
### 2. 生产环境部署
- ✅ 部署前在测试环境验证
- ✅ 使用 `--dry-run` 验证SQL语法
- ✅ 确保数据库备份完整
- ✅ 监控迁移执行时间
### 3. 回滚准备
- ✅ 测试回滚脚本的正确性
- ✅ 保留足够的备份文件
- ✅ 记录回滚操作步骤
## 🚨 故障处理
### 迁移失败处理
1. 检查错误日志
2. 验证数据库连接
3. 检查SQL语法
4. 必要时手动回滚
### 回滚失败处理
1. 使用数据库备份恢复
2. 手动执行回滚SQL
3. 更新迁移历史表
### 紧急恢复
```bash
# 使用备份恢复数据库
mysql -u root -p callback_system < database_backups/migration_backup_20250105_143022.sql
# 手动更新迁移历史
UPDATE migration_history SET status = 'ROLLED_BACK' WHERE version = '20250105_143022';
```
## 📊 监控和日志
### 迁移历史查询
```sql
SELECT * FROM migration_history ORDER BY executed_at DESC LIMIT 10;
```
### 性能监控
- 监控迁移执行时间
- 检查数据库锁等待
- 观察系统资源使用
## 🔧 高级功能
### 自定义迁移管理器
```python
from database_migration_manager import DatabaseMigrationManager
# 自定义配置
manager = DatabaseMigrationManager(
db_config=custom_config,
migrations_dir="custom_migrations"
)
# 程序化执行迁移
success = manager.migrate(target_version="20250105_143022")
```
### 批量操作
```python
# 获取待执行迁移
pending = manager.get_pending_migrations()
# 逐个执行并记录结果
for migration in pending:
success = manager.execute_migration(migration)
if not success:
break
```
## 📞 支持和维护
如有问题,请检查:
1. 数据库连接配置
2. 迁移文件语法
3. 权限设置
4. 日志文件内容
---
**重要提醒**: 在生产环境执行迁移前,请务必在测试环境充分验证!
......@@ -42,4 +42,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/ || exit 1
# 启动命令
CMD ["python", "start_docker.py"]
\ No newline at end of file
CMD ["python", "app.py"]
\ No newline at end of file
......@@ -1047,8 +1047,20 @@ if __name__ == '__main__':
try:
# 本地开发使用5001端口,Docker使用5000端口
port = int(os.getenv('PORT', 5001))
# 检测是否为开发环境
is_development = os.getenv('FLASK_ENV') == 'development' or os.getenv('FLASK_DEBUG', '').lower() in ['1', 'true']
print(f"🌐 服务器将在端口 {port} 启动")
app.run(host='0.0.0.0', port=port, debug=False)
print(f"🔧 开发模式: {'开启' if is_development else '关闭'}")
if is_development:
print("🔥 热重载已启用")
app.run(
host='0.0.0.0',
port=port,
debug=is_development,
use_reloader=is_development
)
except Exception as e:
print(f"❌ 启动服务器失败: {e}")
import traceback
......
/**
* 认证客户端JavaScript库
* 处理用户登录状态、会话管理、API请求认证等功能
*
* @author AI Assistant
* @version 1.0.0
*/
class AuthClient {
constructor(options = {}) {
/** @type {string} API基础URL */
this.apiBase = options.apiBase || '/api';
/** @type {string} 登录页面URL */
this.loginUrl = options.loginUrl || '/login';
/** @type {string} 主页URL */
this.homeUrl = options.homeUrl || '/patient_profiles/';
/** @type {boolean} 是否自动重定向 */
this.autoRedirect = options.autoRedirect !== false;
/** @type {Object|null} 当前用户信息 */
this.currentUser = null;
/** @type {string|null} 当前会话ID */
this.sessionId = null;
/** @type {Array<Function>} 认证状态变化监听器 */
this.authListeners = [];
// 初始化
this.init();
}
/**
* 初始化认证客户端
*/
init() {
// 从存储中恢复会话
this.restoreSession();
// 监听页面可见性变化,自动验证会话
document.addEventListener('visibilitychange', () => {
if (!document.hidden && this.sessionId) {
this.validateSession();
}
});
// 定期验证会话(每5分钟)
setInterval(() => {
if (this.sessionId) {
this.validateSession();
}
}, 5 * 60 * 1000);
}
/**
* 从本地存储恢复会话
*/
restoreSession() {
try {
// 优先从localStorage获取(记住登录状态)
let sessionId = localStorage.getItem('session_id');
let userInfo = localStorage.getItem('user_info');
// 如果localStorage没有,从sessionStorage获取
if (!sessionId) {
sessionId = sessionStorage.getItem('session_id');
userInfo = sessionStorage.getItem('user_info');
}
if (sessionId && userInfo) {
this.sessionId = sessionId;
this.currentUser = JSON.parse(userInfo);
console.log('会话已恢复:', this.currentUser.username);
}
} catch (error) {
console.error('恢复会话失败:', error);
this.clearSession();
}
}
/**
* 用户登录
* @param {string} username 用户名
* @param {string} password 密码
* @param {boolean} rememberMe 是否记住登录状态
* @returns {Promise<Object>} 登录结果
*/
async login(username, password, rememberMe = false) {
try {
const response = await this.apiRequest('/auth/login', {
method: 'POST',
body: JSON.stringify({
username,
password,
remember_me: rememberMe
})
});
if (response.success) {
// 保存会话信息
this.sessionId = response.session_id;
this.currentUser = response.user;
const storage = rememberMe ? localStorage : sessionStorage;
storage.setItem('session_id', this.sessionId);
storage.setItem('user_info', JSON.stringify(this.currentUser));
// 通知认证状态变化
this.notifyAuthChange(true);
console.log('登录成功:', this.currentUser.username);
}
return response;
} catch (error) {
console.error('登录请求失败:', error);
throw error;
}
}
/**
* 用户注销
* @returns {Promise<boolean>} 注销是否成功
*/
async logout() {
try {
if (this.sessionId) {
await this.apiRequest('/auth/logout', {
method: 'POST',
authenticated: true
});
}
} catch (error) {
console.error('注销请求失败:', error);
} finally {
// 清除本地会话信息
this.clearSession();
// 通知认证状态变化
this.notifyAuthChange(false);
// 自动跳转到登录页面
if (this.autoRedirect) {
this.redirectToLogin();
}
}
return true;
}
/**
* 验证当前会话
* @returns {Promise<boolean>} 会话是否有效
*/
async validateSession() {
if (!this.sessionId) {
return false;
}
try {
const response = await this.apiRequest('/auth/validate', {
method: 'POST',
body: JSON.stringify({
session_id: this.sessionId
})
});
if (response.success) {
// 更新用户信息
if (response.user) {
this.currentUser = response.user;
// 更新存储中的用户信息
const userInfoStr = JSON.stringify(this.currentUser);
if (localStorage.getItem('session_id')) {
localStorage.setItem('user_info', userInfoStr);
} else {
sessionStorage.setItem('user_info', userInfoStr);
}
}
return true;
} else {
// 会话无效,清除本地信息
this.clearSession();
this.notifyAuthChange(false);
if (this.autoRedirect) {
this.redirectToLogin();
}
return false;
}
} catch (error) {
console.error('会话验证失败:', error);
return false;
}
}
/**
* 检查是否已登录
* @returns {boolean} 是否已登录
*/
isLoggedIn() {
return this.sessionId !== null && this.currentUser !== null;
}
/**
* 检查是否为管理员
* @returns {boolean} 是否为管理员
*/
isAdmin() {
return this.isLoggedIn() && this.currentUser.role === 'admin';
}
/**
* 获取当前用户信息
* @returns {Object|null} 用户信息
*/
getCurrentUser() {
return this.currentUser;
}
/**
* 修改密码
* @param {string} oldPassword 旧密码
* @param {string} newPassword 新密码
* @returns {Promise<Object>} 修改结果
*/
async changePassword(oldPassword, newPassword) {
try {
const response = await this.apiRequest('/auth/user/change-password', {
method: 'POST',
authenticated: true,
body: JSON.stringify({
old_password: oldPassword,
new_password: newPassword
})
});
if (response.success) {
// 密码修改成功,清除会话,需要重新登录
this.clearSession();
this.notifyAuthChange(false);
}
return response;
} catch (error) {
console.error('修改密码失败:', error);
throw error;
}
}
/**
* 发送认证API请求
* @param {string} endpoint API端点
* @param {Object} options 请求选项
* @returns {Promise<Object>} 响应数据
*/
async apiRequest(endpoint, options = {}) {
const url = `${this.apiBase}${endpoint}`;
const requestOptions = {
method: options.method || 'GET',
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
};
// 如果需要认证,添加会话头
if (options.authenticated && this.sessionId) {
requestOptions.headers['X-Session-ID'] = this.sessionId;
}
const response = await fetch(url, requestOptions);
// 处理认证失败
if (response.status === 401) {
this.clearSession();
this.notifyAuthChange(false);
if (this.autoRedirect) {
this.redirectToLogin();
}
throw new Error('认证失败,请重新登录');
}
const data = await response.json();
if (!response.ok) {
throw new Error(data.message || `请求失败: ${response.status}`);
}
return data;
}
/**
* 要求用户登录
* 如果未登录,自动跳转到登录页面
* @returns {boolean} 是否已登录
*/
requireLogin() {
if (!this.isLoggedIn()) {
if (this.autoRedirect) {
this.redirectToLogin();
}
return false;
}
return true;
}
/**
* 要求管理员权限
* @returns {boolean} 是否有管理员权限
*/
requireAdmin() {
if (!this.requireLogin()) {
return false;
}
if (!this.isAdmin()) {
alert('需要管理员权限');
return false;
}
return true;
}
/**
* 跳转到登录页面
* @param {string} redirectUrl 登录后跳转URL
*/
redirectToLogin(redirectUrl = null) {
const redirect = redirectUrl || window.location.pathname + window.location.search;
const loginUrlWithRedirect = `${this.loginUrl}?redirect=${encodeURIComponent(redirect)}`;
window.location.href = loginUrlWithRedirect;
}
/**
* 跳转到主页
*/
redirectToHome() {
window.location.href = this.homeUrl;
}
/**
* 清除会话信息
*/
clearSession() {
this.sessionId = null;
this.currentUser = null;
// 清除存储
localStorage.removeItem('session_id');
localStorage.removeItem('user_info');
sessionStorage.removeItem('session_id');
sessionStorage.removeItem('user_info');
console.log('会话已清除');
}
/**
* 添加认证状态变化监听器
* @param {Function} listener 监听器函数
*/
onAuthChange(listener) {
if (typeof listener === 'function') {
this.authListeners.push(listener);
}
}
/**
* 移除认证状态变化监听器
* @param {Function} listener 监听器函数
*/
offAuthChange(listener) {
const index = this.authListeners.indexOf(listener);
if (index > -1) {
this.authListeners.splice(index, 1);
}
}
/**
* 通知认证状态变化
* @param {boolean} isLoggedIn 是否已登录
*/
notifyAuthChange(isLoggedIn) {
this.authListeners.forEach(listener => {
try {
listener(isLoggedIn, this.currentUser);
} catch (error) {
console.error('认证状态监听器执行失败:', error);
}
});
}
/**
* 创建认证请求包装器
* 自动添加认证头的fetch包装器
* @param {string} url 请求URL
* @param {Object} options 请求选项
* @returns {Promise<Response>} fetch响应
*/
async authenticatedFetch(url, options = {}) {
const requestOptions = {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
};
// 添加认证头
if (this.sessionId) {
requestOptions.headers['X-Session-ID'] = this.sessionId;
}
const response = await fetch(url, requestOptions);
// 处理认证失败
if (response.status === 401) {
this.clearSession();
this.notifyAuthChange(false);
if (this.autoRedirect) {
this.redirectToLogin();
}
throw new Error('认证失败,请重新登录');
}
return response;
}
/**
* 显示用户信息
* @param {HTMLElement} container 显示容器
*/
renderUserInfo(container) {
if (!container) return;
if (this.isLoggedIn()) {
container.innerHTML = `
<div class="user-info">
<span class="username">欢迎,${this.currentUser.username}</span>
${this.isAdmin() ? '<span class="role admin">管理员</span>' : '<span class="role user">用户</span>'}
<button onclick="authClient.logout()" class="logout-btn">注销</button>
</div>
`;
} else {
container.innerHTML = `
<div class="user-info">
<button onclick="authClient.redirectToLogin()" class="login-btn">登录</button>
</div>
`;
}
}
}
// 创建全局认证客户端实例
const authClient = new AuthClient();
// 页面加载完成后自动验证会话
document.addEventListener('DOMContentLoaded', async () => {
if (authClient.isLoggedIn()) {
// 验证会话有效性
const isValid = await authClient.validateSession();
console.log('会话验证结果:', isValid ? '有效' : '无效');
}
});
// 导出供其他脚本使用
if (typeof module !== 'undefined' && module.exports) {
module.exports = { AuthClient, authClient };
}
// 为其他脚本提供全局访问
window.AuthClient = AuthClient;
window.authClient = authClient;
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
认证服务器
提供用户登录、注销、会话验证等认证服务
集成用户管理和会话管理功能
"""
import os
import sys
from datetime import datetime
from functools import wraps
from flask import Flask, request, jsonify, send_from_directory, redirect, url_for
from flask_cors import CORS
# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
try:
from user_manager import create_user_manager, UserManager
from session_manager import get_session_manager, SessionManager
from callback_record_mysql import MySQLCallbackRecordManager, CallbackRecord
from database_config import DatabaseConfig
DEPENDENCIES_AVAILABLE = True
except ImportError as e:
DEPENDENCIES_AVAILABLE = False
IMPORT_ERROR = str(e)
app = Flask(__name__)
CORS(app) # 允许跨域请求
# 全局管理器
user_manager: UserManager = None
session_manager: SessionManager = None
db_manager: MySQLCallbackRecordManager = None
def initialize_managers():
"""初始化所有管理器"""
global user_manager, session_manager, db_manager
if not DEPENDENCIES_AVAILABLE:
print(f"依赖库未安装: {IMPORT_ERROR}")
print("请运行: pip install pymysql flask flask-cors")
return False
try:
# 初始化用户管理器
user_manager = create_user_manager()
if not user_manager:
print("用户管理器初始化失败")
return False
# 初始化会话管理器
session_manager = get_session_manager()
print("✓ 会话管理器初始化成功")
# 初始化数据库管理器(用于回访记录)
config_manager = DatabaseConfig()
if config_manager.validate_config():
mysql_config = config_manager.get_mysql_config()
db_manager = MySQLCallbackRecordManager(**mysql_config)
if db_manager.test_connection():
print("✓ 回访记录数据库连接成功")
else:
print("⚠ 回访记录数据库连接失败,但认证功能可正常使用")
db_manager = None
return True
except Exception as e:
print(f"管理器初始化失败: {e}")
return False
def require_session(f):
"""装饰器:要求有效会话"""
@wraps(f)
def decorated_function(*args, **kwargs):
session_id = request.headers.get('X-Session-ID') or request.json.get('session_id') if request.json else None
if not session_id:
return jsonify({
'success': False,
'message': '缺少会话ID',
'code': 'MISSING_SESSION'
}), 401
session = session_manager.get_session(session_id)
if not session:
return jsonify({
'success': False,
'message': '无效或已过期的会话',
'code': 'INVALID_SESSION'
}), 401
# 将会话信息传递给路由函数
request.current_session = session
return f(*args, **kwargs)
return decorated_function
def require_admin(f):
"""装饰器:要求管理员权限"""
@wraps(f)
def decorated_function(*args, **kwargs):
session_id = request.headers.get('X-Session-ID') or request.json.get('session_id') if request.json else None
if not session_id:
return jsonify({
'success': False,
'message': '缺少会话ID',
'code': 'MISSING_SESSION'
}), 401
session = session_manager.require_admin(session_id)
if not session:
return jsonify({
'success': False,
'message': '需要管理员权限',
'code': 'ADMIN_REQUIRED'
}), 403
request.current_session = session
return f(*args, **kwargs)
return decorated_function
@app.route('/api/auth/login', methods=['POST'])
def login():
"""用户登录API"""
try:
data = request.get_json()
# 验证必需字段
if not data or 'username' not in data or 'password' not in data:
return jsonify({
'success': False,
'message': '缺少用户名或密码'
}), 400
username = data['username'].strip()
password = data['password']
remember_me = data.get('remember_me', False)
# 验证输入
if not username or not password:
return jsonify({
'success': False,
'message': '用户名和密码不能为空'
}), 400
if len(password) < 6:
return jsonify({
'success': False,
'message': '密码长度至少6位'
}), 400
# 用户认证
user = user_manager.authenticate(username, password)
if not user:
return jsonify({
'success': False,
'message': '用户名或密码错误'
}), 401
# 创建会话
session_timeout = 168 if remember_me else 24 # 记住登录状态7天,否则24小时
session_manager.session_timeout_hours = session_timeout
session = session_manager.create_session(user)
return jsonify({
'success': True,
'message': '登录成功',
'session_id': session.session_id,
'user': user.to_dict(),
'expires_at': session.expires_at.isoformat()
})
except Exception as e:
print(f"登录失败: {e}")
return jsonify({
'success': False,
'message': '登录服务异常'
}), 500
@app.route('/api/auth/logout', methods=['POST'])
@require_session
def logout():
"""用户注销API"""
try:
session = request.current_session
session_manager.destroy_session(session.session_id)
return jsonify({
'success': True,
'message': '注销成功'
})
except Exception as e:
print(f"注销失败: {e}")
return jsonify({
'success': False,
'message': '注销服务异常'
}), 500
@app.route('/api/auth/validate', methods=['POST'])
def validate_session():
"""验证会话有效性API"""
try:
data = request.get_json()
session_id = data.get('session_id') if data else None
if not session_id:
return jsonify({
'success': False,
'message': '缺少会话ID'
}), 400
session = session_manager.get_session(session_id)
if session:
user = user_manager.get_user_by_id(session.user_id)
return jsonify({
'success': True,
'session': session.to_dict(),
'user': user.to_dict() if user else None
})
else:
return jsonify({
'success': False,
'message': '会话无效或已过期'
}), 401
except Exception as e:
print(f"会话验证失败: {e}")
return jsonify({
'success': False,
'message': '会话验证服务异常'
}), 500
@app.route('/api/auth/user/profile', methods=['GET'])
@require_session
def get_user_profile():
"""获取当前用户信息"""
try:
user = request.current_session.user
return jsonify({
'success': True,
'user': user.to_dict()
})
except Exception as e:
print(f"获取用户信息失败: {e}")
return jsonify({
'success': False,
'message': '获取用户信息失败'
}), 500
@app.route('/api/user-info', methods=['GET', 'POST'])
def get_user_info():
"""获取当前用户信息(兼容旧接口)"""
try:
# 从请求头或JSON中获取session_id
session_id = request.headers.get('X-Session-ID')
if not session_id and request.json:
session_id = request.json.get('session_id')
if not session_id:
return jsonify({
'success': False,
'message': '未登录或session已过期'
}), 401
session = session_manager.get_session(session_id)
if not session:
return jsonify({
'success': False,
'message': '无效或已过期的会话'
}), 401
# 从session中获取用户信息
user_info = {
'username': session.username,
'role': session.role,
'user_id': session.user_id,
'realName': session.username, # 添加realName字段
'clinicId': getattr(session, 'clinic_id', None), # 添加clinicId字段
'created_at': session.created_at.isoformat() if session.created_at else None,
'last_login': session.last_activity.isoformat() if session.last_activity else None,
'is_active': True # 假设活跃会话的用户都是活跃的
}
return jsonify({
'success': True,
'user': user_info
})
except Exception as e:
print(f"获取用户信息失败: {e}")
return jsonify({
'success': False,
'message': '获取用户信息失败'
}), 500
@app.route('/api/auth/user/change-password', methods=['POST'])
@require_session
def change_password():
"""修改密码API"""
try:
data = request.get_json()
session = request.current_session
if not data or 'old_password' not in data or 'new_password' not in data:
return jsonify({
'success': False,
'message': '缺少必需参数'
}), 400
old_password = data['old_password']
new_password = data['new_password']
# 验证新密码
if len(new_password) < 6:
return jsonify({
'success': False,
'message': '新密码长度至少6位'
}), 400
# 修改密码
success = user_manager.change_password(session.user_id, old_password, new_password)
if success:
# 销毁其他会话,强制重新登录
session_manager.destroy_user_sessions(session.user_id)
return jsonify({
'success': True,
'message': '密码修改成功,请重新登录'
})
else:
return jsonify({
'success': False,
'message': '原密码错误'
}), 400
except Exception as e:
print(f"修改密码失败: {e}")
return jsonify({
'success': False,
'message': '修改密码服务异常'
}), 500
@app.route('/api/auth/admin/users', methods=['GET'])
@require_admin
def get_all_users():
"""获取所有用户列表API(管理员)"""
try:
users = user_manager.get_all_users()
return jsonify({
'success': True,
'users': [user.to_dict() for user in users],
'count': len(users)
})
except Exception as e:
print(f"获取用户列表失败: {e}")
return jsonify({
'success': False,
'message': '获取用户列表服务异常'
}), 500
@app.route('/api/auth/admin/users', methods=['POST'])
@require_admin
def create_user():
"""创建用户API(管理员)"""
try:
data = request.get_json()
if not data or 'username' not in data or 'password' not in data:
return jsonify({
'success': False,
'message': '缺少用户名或密码'
}), 400
username = data['username'].strip()
password = data['password']
role = data.get('role', 'user')
# 验证输入
if not username or not password:
return jsonify({
'success': False,
'message': '用户名和密码不能为空'
}), 400
if len(password) < 6:
return jsonify({
'success': False,
'message': '密码长度至少6位'
}), 400
if role not in ['admin', 'user']:
return jsonify({
'success': False,
'message': '无效的用户角色'
}), 400
# 创建用户
user_id = user_manager.create_user(username, password, role)
if user_id:
return jsonify({
'success': True,
'message': '用户创建成功',
'user_id': user_id
})
else:
return jsonify({
'success': False,
'message': '用户创建失败,用户名可能已存在'
}), 400
except Exception as e:
print(f"创建用户失败: {e}")
return jsonify({
'success': False,
'message': '创建用户服务异常'
}), 500
@app.route('/api/auth/sessions', methods=['GET'])
@require_admin
def get_active_sessions():
"""获取活跃会话列表API(管理员)"""
try:
sessions = session_manager.get_active_sessions()
return jsonify({
'success': True,
'sessions': [session.to_dict() for session in sessions.values()],
'count': len(sessions)
})
except Exception as e:
print(f"获取会话列表失败: {e}")
return jsonify({
'success': False,
'message': '获取会话列表服务异常'
}), 500
# 原有回访记录API(需要登录)
@app.route('/api/callback-records', methods=['POST'])
@require_session
def save_callback_record():
"""保存回访记录API"""
if not db_manager:
return jsonify({
'success': False,
'message': '回访记录数据库未初始化'
}), 500
try:
data = request.get_json()
session = request.current_session
# 验证必需字段
required_fields = ['caseNumber', 'callbackMethods', 'callbackRecord']
for field in required_fields:
if field not in data or not data[field]:
return jsonify({
'success': False,
'message': f'缺少必需字段: {field}'
}), 400
# 自动填入操作员
data['operator'] = session.username
# 创建记录对象
record = CallbackRecord(
case_number=data['caseNumber'],
callback_methods=data['callbackMethods'],
callback_record=data['callbackRecord'],
operator=data['operator']
)
# 保存到数据库
record_id = db_manager.save_record(record)
return jsonify({
'success': True,
'id': record_id,
'message': '保存成功',
'timestamp': datetime.now().isoformat()
})
except Exception as e:
print(f"保存回访记录失败: {e}")
return jsonify({
'success': False,
'message': f'保存失败: {str(e)}'
}), 500
@app.route('/api/callback-records/<case_number>', methods=['GET'])
@require_session
def get_callback_records(case_number):
"""获取回访记录API"""
if not db_manager:
return jsonify({
'success': False,
'message': '回访记录数据库未初始化'
}), 500
try:
records = db_manager.get_records_by_case_number(case_number)
return jsonify({
'success': True,
'data': [record.to_dict() for record in records],
'count': len(records)
})
except Exception as e:
print(f"获取回访记录失败: {e}")
return jsonify({
'success': False,
'message': f'获取失败: {str(e)}'
}), 500
@app.route('/api/health', methods=['GET'])
def health_check():
"""健康检查API"""
status = {
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'services': {
'user_manager': user_manager is not None and user_manager.test_connection(),
'session_manager': session_manager is not None,
'callback_db': db_manager is not None and db_manager.test_connection()
}
}
return jsonify(status)
@app.route('/')
def index():
"""首页 - 跳转到患者画像索引页面"""
return redirect('/patient_profiles/')
@app.route('/login')
def login_page():
"""登录页面"""
return send_from_directory('.', 'login.html')
@app.route('/dashboard')
def dashboard():
"""仪表盘页面(需要登录)"""
return send_from_directory('.', 'dashboard.html')
@app.route('/dashboard.html')
def dashboard_html():
"""仪表盘页面"""
return send_from_directory('.', 'dashboard.html')
@app.route('/user_management.html')
def user_management():
"""用户管理页面"""
return send_from_directory('.', 'user_management.html')
@app.route('/auth_client.js')
def auth_client_js():
"""认证客户端JS"""
return send_from_directory('.', 'auth_client.js')
@app.route('/test_login_flow.html')
def test_login_flow():
"""登录流程测试页面"""
return send_from_directory('.', 'test_login_flow.html')
# 静态文件服务
@app.route('/patient_profiles/')
def patient_profiles_index():
"""患者画像索引页面"""
return send_from_directory('patient_profiles', 'index.html')
@app.route('/patient_profiles/<path:filename>')
def patient_profiles(filename):
"""提供患者画像页面文件"""
return send_from_directory('patient_profiles', filename)
@app.route('/callback_record.js')
def callback_js():
"""提供JavaScript文件"""
return send_from_directory('.', 'callback_record.js')
def main():
"""主函数"""
print("=== 认证服务器 ===")
# 检查依赖
if not DEPENDENCIES_AVAILABLE:
print(f"错误: {IMPORT_ERROR}")
print("\n请安装所需依赖:")
print("pip install pymysql flask flask-cors")
return
# 初始化管理器
if not initialize_managers():
print("管理器初始化失败,服务器无法启动")
print("\n请检查:")
print("1. MySQL服务是否正在运行")
print("2. 数据库配置是否正确(运行 python database_config.py 检查)")
print("3. 数据库用户是否有足够权限")
return
print("\n服务器启动中...")
print("登录页面: http://localhost:5000/login")
print("API地址: http://localhost:5000/api/")
print("患者画像: http://localhost:5000/patient_profiles/")
print("按 Ctrl+C 停止服务器")
try:
# 启动Flask服务器
app.run(
host='0.0.0.0', # 允许外部访问
port=5000,
debug=True, # 开发模式
threaded=True # 多线程支持
)
except KeyboardInterrupt:
print("\n服务器已停止")
except Exception as e:
print(f"服务器启动失败: {e}")
if __name__ == "__main__":
main()
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据库备份工具</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
color: #333;
}
.backup-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #007bff;
}
.btn {
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
margin: 5px;
transition: background 0.3s;
}
.btn:hover {
background: #0056b3;
}
.btn-danger {
background: #dc3545;
}
.btn-danger:hover {
background: #c82333;
}
.btn-success {
background: #28a745;
}
.btn-success:hover {
background: #218838;
}
.status {
padding: 15px;
border-radius: 6px;
margin: 15px 0;
display: none;
}
.status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.backup-list {
background: #fff;
border: 1px solid #ddd;
border-radius: 6px;
padding: 15px;
margin-top: 20px;
}
.backup-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.backup-item:last-child {
border-bottom: none;
}
.backup-info {
flex: 1;
}
.backup-name {
font-weight: bold;
color: #333;
}
.backup-time {
color: #666;
font-size: 14px;
}
.backup-size {
color: #888;
font-size: 12px;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #007bff;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🗄️ 数据库备份工具</h1>
<p>安全备份生产环境数据库,无需SSH权限</p>
</div>
<div class="backup-section">
<h3>📋 备份操作</h3>
<p>点击下方按钮开始备份数据库,备份文件将自动下载到本地。</p>
<button class="btn btn-success" onclick="startBackup()">
🚀 开始备份数据库
</button>
<button class="btn" onclick="refreshBackupList()">
🔄 刷新备份列表
</button>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>正在备份数据库,请稍候...</p>
</div>
<div id="status" class="status"></div>
<div class="backup-section">
<h3>📊 备份历史</h3>
<div id="backupList" class="backup-list">
<p>点击"刷新备份列表"查看备份历史</p>
</div>
</div>
<div class="backup-section">
<h3>⚠️ 重要提醒</h3>
<ul>
<li>备份过程可能需要几分钟时间,请耐心等待</li>
<li>备份文件会自动下载到本地,请妥善保存</li>
<li>建议在业务低峰期进行备份操作</li>
<li>备份完成后,请验证备份文件的完整性</li>
</ul>
</div>
</div>
<script>
// 显示状态信息
function showStatus(message, type = 'info') {
const statusDiv = document.getElementById('status');
statusDiv.textContent = message;
statusDiv.className = `status ${type}`;
statusDiv.style.display = 'block';
// 5秒后自动隐藏
setTimeout(() => {
statusDiv.style.display = 'none';
}, 5000);
}
// 开始备份
async function startBackup() {
try {
// 显示加载状态
document.getElementById('loading').style.display = 'block';
showStatus('正在启动数据库备份...', 'info');
// 调用备份API
const response = await fetch('/api/backup/database', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (result.success) {
showStatus('✅ 数据库备份成功!正在下载备份文件...', 'success');
// 自动下载备份文件
setTimeout(() => {
downloadBackup(result.download_url, result.backup_file);
}, 1000);
// 刷新备份列表
setTimeout(() => {
refreshBackupList();
}, 2000);
} else {
showStatus(`❌ 备份失败: ${result.error}`, 'error');
}
} catch (error) {
showStatus(`❌ 备份过程出错: ${error.message}`, 'error');
} finally {
document.getElementById('loading').style.display = 'none';
}
}
// 下载备份文件
function downloadBackup(downloadUrl, filename) {
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showStatus(`📥 备份文件 ${filename} 下载完成!`, 'success');
}
// 刷新备份列表
async function refreshBackupList() {
try {
const response = await fetch('/api/backup/status');
const result = await response.json();
if (result.success) {
displayBackupList(result.backups);
} else {
showStatus(`❌ 获取备份列表失败: ${result.error}`, 'error');
}
} catch (error) {
showStatus(`❌ 刷新失败: ${error.message}`, 'error');
}
}
// 显示备份列表
function displayBackupList(backups) {
const backupListDiv = document.getElementById('backupList');
if (backups.length === 0) {
backupListDiv.innerHTML = '<p>暂无备份文件</p>';
return;
}
let html = '';
backups.forEach(backup => {
const size = formatFileSize(backup.size);
const time = new Date(backup.created_time).toLocaleString('zh-CN');
html += `
<div class="backup-item">
<div class="backup-info">
<div class="backup-name">${backup.filename}</div>
<div class="backup-time">创建时间: ${time}</div>
<div class="backup-size">文件大小: ${size}</div>
</div>
<button class="btn" onclick="downloadBackup('${backup.download_url}', '${backup.filename}')">
📥 下载
</button>
</div>
`;
});
backupListDiv.innerHTML = html;
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 页面加载完成后自动刷新备份列表
window.onload = function() {
refreshBackupList();
};
</script>
</body>
</html>
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
分门诊HTML页面生成器
基于现有的generate_html.py,支持按门诊生成页面
"""
import json
import os
import shutil
from datetime import datetime
from clinic_config import CLINIC_MAPPING, get_clinic_info, get_all_clinic_ids
from generate_html import generate_html, generate_index_html
class ClinicHTMLGenerator:
"""分门诊HTML生成器"""
def __init__(self):
self.base_dir = "patient_profiles"
self.ensure_base_structure()
def ensure_base_structure(self):
"""确保基础目录结构存在"""
# 创建主目录
if not os.path.exists(self.base_dir):
os.makedirs(self.base_dir)
# 创建共享资源目录
shared_dir = os.path.join(self.base_dir, "shared")
if not os.path.exists(shared_dir):
os.makedirs(shared_dir)
os.makedirs(os.path.join(shared_dir, "css"))
os.makedirs(os.path.join(shared_dir, "js"))
# 创建管理员目录
admin_dir = os.path.join(self.base_dir, "admin")
if not os.path.exists(admin_dir):
os.makedirs(admin_dir)
os.makedirs(os.path.join(admin_dir, "data"))
def load_clinic_patients(self, clinic_id):
"""加载指定门诊的患者数据"""
clinic_info = get_clinic_info(clinic_id)
if not clinic_info:
print(f"❌ 门诊ID不存在: {clinic_id}")
return []
json_file = clinic_info['json_file']
try:
with open(json_file, 'r', encoding='utf-8') as f:
patients = json.load(f)
# 为每个患者添加clinic_id标识
for patient in patients:
patient['clinic_id'] = clinic_id
patient['clinic_name'] = clinic_info['clinic_name']
print(f"✅ 加载 {clinic_info['clinic_name']} 患者数据: {len(patients)} 人")
return patients
except Exception as e:
print(f"❌ 加载 {clinic_info['clinic_name']} 数据失败: {e}")
return []
def generate_clinic_pages(self, clinic_id):
"""生成单个门诊的所有页面"""
clinic_info = get_clinic_info(clinic_id)
if not clinic_info:
return False
print(f"\n🏥 生成 {clinic_info['clinic_name']} 的页面...")
# 加载患者数据
patients = self.load_clinic_patients(clinic_id)
if not patients:
print(f"⚠️ {clinic_info['clinic_name']} 没有患者数据")
return False
# 创建门诊目录
clinic_dir = os.path.join(self.base_dir, clinic_info['folder_name'])
if not os.path.exists(clinic_dir):
os.makedirs(clinic_dir)
patients_dir = os.path.join(clinic_dir, "patients")
if not os.path.exists(patients_dir):
os.makedirs(patients_dir)
# 生成患者详情页面
success_count = 0
error_count = 0
for i, patient in enumerate(patients, 1):
patient_id = patient.get('病历号')
if not patient_id:
error_count += 1
continue
try:
# 生成患者详情页
generate_html(patient_id, [patient])
# 移动文件到门诊目录
source_file = f"{patient_id}.html"
target_file = os.path.join(patients_dir, f"{patient_id}.html")
if os.path.exists(source_file):
if os.path.exists(target_file):
os.remove(target_file)
shutil.move(source_file, target_file)
success_count += 1
if i % 50 == 0: # 每50个患者显示一次进度
print(f" 📄 已生成 {i}/{len(patients)} 个患者页面...")
else:
error_count += 1
except Exception as e:
print(f"❌ 生成患者 {patient_id} 页面失败: {e}")
error_count += 1
# 生成门诊索引页
try:
self.generate_clinic_index(clinic_id, patients)
print(f"✅ 门诊索引页生成完成")
except Exception as e:
print(f"❌ 生成门诊索引页失败: {e}")
error_count += 1
print(f"📊 {clinic_info['clinic_name']} 页面生成完成:")
print(f" ✅ 成功: {success_count} 个患者页面")
print(f" ❌ 失败: {error_count} 个")
print(f" 📈 成功率: {success_count/(success_count+error_count)*100:.1f}%")
return success_count > 0
def generate_clinic_index(self, clinic_id, patients):
"""生成门诊的患者索引页"""
clinic_info = get_clinic_info(clinic_id)
# 使用现有的generate_index_html函数,但修改输出路径
# 临时修改患者数据格式以适配现有函数
patients_dict = {}
for patient in patients:
patient_id = patient.get('病历号')
if patient_id:
patients_dict[patient_id] = [patient]
# 生成索引页
generate_index_html(patients_dict)
# 移动生成的索引页到门诊目录
source_index = os.path.join(self.base_dir, "index.html")
clinic_dir = os.path.join(self.base_dir, clinic_info['folder_name'])
target_index = os.path.join(clinic_dir, "index.html")
if os.path.exists(source_index):
if os.path.exists(target_index):
os.remove(target_index)
shutil.move(source_index, target_index)
# 修改索引页中的链接路径
self.fix_clinic_index_links(target_index, clinic_info['folder_name'])
def fix_clinic_index_links(self, index_file, clinic_folder):
"""修复门诊索引页中的链接路径"""
try:
with open(index_file, 'r', encoding='utf-8') as f:
content = f.read()
# 修改患者详情页的链接路径
# 从 href="TS0A001.html" 改为 href="patients/TS0A001.html"
import re
content = re.sub(
r'href="([A-Z0-9]+\.html)"',
r'href="patients/\1"',
content
)
# 修改标题和面包屑
clinic_info = None
for info in CLINIC_MAPPING.values():
if info['folder_name'] == clinic_folder:
clinic_info = info
break
if clinic_info:
# 修改页面标题
content = content.replace(
'<title>患者画像系统</title>',
f'<title>{clinic_info["clinic_name"]} - 患者画像系统</title>'
)
# 添加门诊标识
content = content.replace(
'<h1 class="text-3xl font-bold text-gray-900 mb-8">患者画像系统</h1>',
f'<h1 class="text-3xl font-bold text-gray-900 mb-2">{clinic_info["clinic_name"]}</h1>\n' +
'<p class="text-gray-600 mb-8">患者画像系统</p>'
)
with open(index_file, 'w', encoding='utf-8') as f:
f.write(content)
except Exception as e:
print(f"⚠️ 修复索引页链接失败: {e}")
def generate_all_clinics(self):
"""批量生成所有门诊的页面"""
print("🚀 开始批量生成所有门诊页面...")
print("=" * 60)
start_time = datetime.now()
total_success = 0
total_error = 0
for i, clinic_id in enumerate(get_all_clinic_ids(), 1):
clinic_info = get_clinic_info(clinic_id)
print(f"\n({i}/{len(get_all_clinic_ids())}) 处理 {clinic_info['clinic_name']}...")
try:
success = self.generate_clinic_pages(clinic_id)
if success:
total_success += 1
else:
total_error += 1
except Exception as e:
print(f"❌ 处理 {clinic_info['clinic_name']} 失败: {e}")
total_error += 1
# 生成主索引页和管理员页面
try:
self.generate_main_index()
self.generate_admin_dashboard()
print(f"\n✅ 主页面生成完成")
except Exception as e:
print(f"\n❌ 生成主页面失败: {e}")
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
print("\n" + "=" * 60)
print("🎉 **批量生成完成**")
print("=" * 60)
print(f"⏱️ 耗时: {duration:.1f} 秒")
print(f"✅ 成功门诊: {total_success}")
print(f"❌ 失败门诊: {total_error}")
print(f"📈 成功率: {total_success/(total_success+total_error)*100:.1f}%")
def generate_main_index(self):
"""生成主索引页面(门诊选择页面)"""
main_index_path = os.path.join(self.base_dir, "index.html")
html_content = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>通善口腔 - 患者画像系统</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-900 mb-4">通善口腔患者画像系统</h1>
<p class="text-xl text-gray-600">请选择要访问的门诊</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
"""
# 添加各门诊卡片
for clinic_id in get_all_clinic_ids():
clinic_info = get_clinic_info(clinic_id)
html_content += f"""
<div class="bg-white rounded-lg shadow-lg p-6 hover:shadow-xl transition-shadow">
<div class="text-center">
<div class="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-hospital text-2xl text-blue-600"></i>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">{clinic_info['clinic_name']}</h3>
<p class="text-gray-600 mb-4">{clinic_info['description']}</p>
<p class="text-sm text-gray-500 mb-4">预计患者: {clinic_info['expected_patients']} 人</p>
<a href="{clinic_info['folder_name']}/index.html"
class="inline-block bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors">
<i class="fas fa-arrow-right mr-2"></i>进入门诊
</a>
</div>
</div>
"""
# 添加管理员入口
html_content += f"""
<div class="bg-gradient-to-r from-purple-500 to-pink-500 rounded-lg shadow-lg p-6 hover:shadow-xl transition-shadow text-white">
<div class="text-center">
<div class="w-16 h-16 bg-white bg-opacity-20 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-chart-bar text-2xl"></i>
</div>
<h3 class="text-xl font-semibold mb-2">总部管理</h3>
<p class="mb-4 opacity-90">跨门诊统计与管理</p>
<p class="text-sm opacity-75 mb-4">全部门诊数据汇总</p>
<a href="admin/dashboard.html"
class="inline-block bg-white text-purple-600 px-6 py-2 rounded-lg hover:bg-gray-100 transition-colors">
<i class="fas fa-cog mr-2"></i>管理中心
</a>
</div>
</div>
</div>
<div class="text-center mt-12">
<p class="text-gray-500">
<i class="fas fa-hospital mr-2"></i>
共 {len(get_all_clinic_ids())} 个门诊,{sum(info['expected_patients'] for info in CLINIC_MAPPING.values())} 个患者
</p>
<p class="text-xs text-gray-400 mt-2">
© 2025 通善口腔患者画像系统 v2.0
</p>
</div>
</div>
</body>
</html>"""
with open(main_index_path, 'w', encoding='utf-8') as f:
f.write(html_content)
print("✅ 主索引页面生成完成")
def generate_admin_dashboard(self):
"""生成管理员统计仪表板"""
admin_path = os.path.join(self.base_dir, "admin", "dashboard.html")
# 这里先创建一个简单的管理员页面,后续可以扩展
html_content = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>管理员仪表板 - 通善口腔</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 mb-2">管理员仪表板</h1>
<p class="text-gray-600">跨门诊数据统计与分析</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-hospital text-2xl text-blue-600"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">门诊总数</p>
<p class="text-2xl font-semibold text-gray-900">9</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-users text-2xl text-green-600"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">患者总数</p>
<p class="text-2xl font-semibold text-gray-900">4,133</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-phone text-2xl text-yellow-600"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">回访记录</p>
<p class="text-2xl font-semibold text-gray-900">-</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-chart-line text-2xl text-purple-600"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">成功率</p>
<p class="text-2xl font-semibold text-gray-900">-</p>
</div>
</div>
</div>
</div>
<div class="text-center py-12">
<i class="fas fa-tools text-4xl text-gray-400 mb-4"></i>
<h2 class="text-xl font-semibold text-gray-700 mb-2">功能开发中</h2>
<p class="text-gray-500">跨门诊统计功能正在开发中,敬请期待</p>
<a href="../index.html" class="inline-block mt-4 bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700">
<i class="fas fa-arrow-left mr-2"></i>返回主页
</a>
</div>
</div>
</body>
</html>"""
with open(admin_path, 'w', encoding='utf-8') as f:
f.write(html_content)
print("✅ 管理员仪表板生成完成")
def main():
"""主函数"""
generator = ClinicHTMLGenerator()
# 可以选择生成单个门诊或所有门诊
import sys
if len(sys.argv) > 1:
clinic_id = sys.argv[1]
if clinic_id in get_all_clinic_ids():
generator.generate_clinic_pages(clinic_id)
else:
print(f"❌ 门诊ID不存在: {clinic_id}")
print(f"可用的门诊ID: {', '.join(get_all_clinic_ids())}")
else:
# 生成所有门诊
generator.generate_all_clinics()
if __name__ == "__main__":
main()
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>系统仪表盘 - 回访记录系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
}
.user-info {
display: flex;
align-items: center;
gap: 15px;
}
.user-info .username {
font-weight: 500;
}
.user-info .role {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
background: rgba(255,255,255,0.2);
color: #fff;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 30px 20px;
}
.welcome-card {
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
padding: 40px;
margin-bottom: 30px;
text-align: center;
}
.welcome-card h2 {
font-size: 28px;
margin-bottom: 10px;
color: #333;
}
.welcome-card p {
color: #666;
font-size: 16px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
text-align: center;
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #667eea, #764ba2);
}
.stat-number {
font-size: 48px;
font-weight: 700;
color: #667eea;
margin-bottom: 10px;
line-height: 1;
}
.stat-label {
color: #666;
font-size: 16px;
font-weight: 500;
}
.stat-change {
font-size: 14px;
margin-top: 5px;
}
.stat-change.positive {
color: #27ae60;
}
.stat-change.negative {
color: #e74c3c;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.action-card {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.action-card h3 {
font-size: 20px;
margin-bottom: 15px;
color: #333;
}
.action-card p {
color: #666;
margin-bottom: 20px;
line-height: 1.6;
}
.action-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-outline {
background: transparent;
border: 2px solid #667eea;
color: #667eea;
}
.btn-outline:hover {
background: #667eea;
color: white;
}
.recent-activity {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.recent-activity h3 {
font-size: 20px;
margin-bottom: 20px;
color: #333;
}
.activity-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1px solid #f0f0f0;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
font-size: 18px;
}
.activity-content {
flex: 1;
}
.activity-title {
font-weight: 500;
color: #333;
margin-bottom: 5px;
}
.activity-time {
font-size: 14px;
color: #666;
}
.system-status {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
margin-top: 30px;
}
.system-status h3 {
font-size: 20px;
margin-bottom: 20px;
color: #333;
}
.status-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #f0f0f0;
}
.status-item:last-child {
border-bottom: none;
}
.status-label {
font-weight: 500;
color: #333;
}
.status-value {
display: flex;
align-items: center;
gap: 8px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-indicator.online {
background: #27ae60;
}
.status-indicator.offline {
background: #e74c3c;
}
.status-indicator.warning {
background: #f39c12;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
@media (max-width: 768px) {
.container {
padding: 20px 15px;
}
.welcome-card {
padding: 30px 20px;
}
.welcome-card h2 {
font-size: 24px;
}
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.stat-number {
font-size: 36px;
}
.quick-actions {
grid-template-columns: 1fr;
}
.action-buttons {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<h1>系统仪表盘</h1>
<div class="user-info" id="userInfo"></div>
</div>
</div>
<div class="container">
<!-- 欢迎卡片 -->
<div class="welcome-card">
<h2 id="welcomeTitle">欢迎使用回访记录管理系统</h2>
<p>在这里您可以查看系统概览、管理患者回访记录和系统设置</p>
</div>
<!-- 统计数据 -->
<div class="stats-grid" id="statsGrid">
<div class="loading">正在加载统计数据...</div>
</div>
<!-- 快速操作 -->
<div class="quick-actions">
<div class="action-card">
<h3>患者管理</h3>
<p>查看和管理患者画像,访问回访记录,处理患者相关事务</p>
<div class="action-buttons">
<a href="/patient_profiles/" class="btn btn-primary">
📋 患者列表
</a>
<a href="/patient_profiles/index.html" class="btn btn-outline">
👤 患者画像
</a>
</div>
</div>
<div class="action-card">
<h3>回访记录</h3>
<p>创建、编辑和查看患者回访记录,生成回访报告</p>
<div class="action-buttons">
<button class="btn btn-primary" onclick="createCallbackRecord()">
➕ 新建记录
</button>
<button class="btn btn-outline" onclick="viewReports()">
📊 查看报告
</button>
</div>
</div>
<div class="action-card" id="adminActions" style="display: none;">
<h3>系统管理</h3>
<p>管理用户账户、系统设置和数据备份</p>
<div class="action-buttons">
<a href="/user_management.html" class="btn btn-primary">
👥 用户管理
</a>
<button class="btn btn-outline" onclick="viewSystemLogs()">
📝 系统日志
</button>
<button class="btn btn-secondary" onclick="showChangePasswordModal()">
🔑 修改密码
</button>
</div>
</div>
<div class="action-card">
<h3>数据分析</h3>
<p>查看系统使用统计、回访效果分析和趋势报告</p>
<div class="action-buttons">
<button class="btn btn-primary" onclick="viewAnalytics()">
📈 数据分析
</button>
<button class="btn btn-outline" onclick="exportData()">
💾 数据导出
</button>
</div>
</div>
</div>
<!-- 最近活动 -->
<div class="recent-activity">
<h3>最近活动</h3>
<div id="recentActivityContent">
<div class="loading">正在加载活动记录...</div>
</div>
</div>
<!-- 系统状态 -->
<div class="system-status">
<h3>系统状态</h3>
<div id="systemStatusContent">
<div class="loading">正在检查系统状态...</div>
</div>
</div>
</div>
<!-- 修改密码模态框 -->
<div id="changePasswordModal" class="modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; padding: 30px; width: 90%; max-width: 500px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #eee;">
<h3>修改密码</h3>
<button onclick="hideChangePasswordModal()" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #999;">&times;</button>
</div>
<form id="changePasswordForm">
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: #333;">当前密码</label>
<input type="password" id="oldPassword" required placeholder="请输入当前密码" style="width: 100%; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px;">
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: #333;">新密码</label>
<input type="password" id="newPasswordChange" required minlength="6" placeholder="请输入新密码(至少6位)" style="width: 100%; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px;">
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500; color: #333;">确认新密码</label>
<input type="password" id="confirmPassword" required minlength="6" placeholder="请再次输入新密码" style="width: 100%; padding: 12px; border: 2px solid #e1e5e9; border-radius: 8px; font-size: 14px;">
</div>
</form>
<div style="margin-top: 25px; padding-top: 15px; border-top: 1px solid #eee; display: flex; justify-content: flex-end; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="hideChangePasswordModal()">取消</button>
<button type="submit" class="btn btn-primary" form="changePasswordForm" id="changePasswordBtn">修改密码</button>
</div>
</div>
</div>
<!-- 引入认证客户端 -->
<script src="/auth_client.js"></script>
<script>
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', async function() {
// 要求用户登录
if (!authClient.requireLogin()) {
return;
}
// 显示用户信息
authClient.renderUserInfo(document.getElementById('userInfo'));
// 更新欢迎信息
updateWelcomeMessage();
// 根据用户角色显示/隐藏管理功能
toggleAdminFeatures();
// 加载数据
await Promise.all([
loadStats(),
loadRecentActivity(),
loadSystemStatus()
]);
});
// 更新欢迎信息
function updateWelcomeMessage() {
const user = authClient.getCurrentUser();
if (user) {
const title = document.getElementById('welcomeTitle');
title.textContent = `欢迎回来,${user.username}!`;
}
}
// 切换管理员功能显示
function toggleAdminFeatures() {
const adminActions = document.getElementById('adminActions');
if (authClient.isAdmin()) {
adminActions.style.display = 'block';
}
}
// 加载统计数据
async function loadStats() {
try {
// 模拟统计数据,实际应用中应从API获取
const statsData = [
{ label: '总患者数', value: '1,234', change: '+5.2%', type: 'positive' },
{ label: '本月回访', value: '89', change: '+12.3%', type: 'positive' },
{ label: '待处理', value: '23', change: '-8.1%', type: 'negative' },
{ label: '完成率', value: '87%', change: '+3.4%', type: 'positive' }
];
renderStats(statsData);
} catch (error) {
console.error('加载统计数据失败:', error);
document.getElementById('statsGrid').innerHTML = '<div class="loading">统计数据加载失败</div>';
}
}
// 渲染统计数据
function renderStats(stats) {
const statsGrid = document.getElementById('statsGrid');
statsGrid.innerHTML = stats.map(stat => `
<div class="stat-card">
<div class="stat-number">${stat.value}</div>
<div class="stat-label">${stat.label}</div>
<div class="stat-change ${stat.type}">${stat.change}</div>
</div>
`).join('');
}
// 加载最近活动
async function loadRecentActivity() {
try {
// 模拟活动数据
const activities = [
{ icon: '👤', title: '新患者 李某某 已添加', time: '5分钟前' },
{ icon: '📞', title: '完成回访记录 - 张某某', time: '15分钟前' },
{ icon: '📊', title: '生成月度报告', time: '1小时前' },
{ icon: '🔧', title: '系统配置已更新', time: '2小时前' }
];
renderRecentActivity(activities);
} catch (error) {
console.error('加载活动记录失败:', error);
document.getElementById('recentActivityContent').innerHTML = '<div class="loading">活动记录加载失败</div>';
}
}
// 渲染最近活动
function renderRecentActivity(activities) {
const content = document.getElementById('recentActivityContent');
if (activities.length === 0) {
content.innerHTML = '<div style="text-align: center; padding: 20px; color: #666;">暂无活动记录</div>';
return;
}
content.innerHTML = activities.map(activity => `
<div class="activity-item">
<div class="activity-icon">${activity.icon}</div>
<div class="activity-content">
<div class="activity-title">${activity.title}</div>
<div class="activity-time">${activity.time}</div>
</div>
</div>
`).join('');
}
// 加载系统状态
async function loadSystemStatus() {
try {
const response = await authClient.apiRequest('/health', {
authenticated: false
});
const statusItems = [
{
label: '用户管理服务',
value: response.services.user_manager ? '正常' : '异常',
status: response.services.user_manager ? 'online' : 'offline'
},
{
label: '会话管理服务',
value: response.services.session_manager ? '正常' : '异常',
status: response.services.session_manager ? 'online' : 'offline'
},
{
label: '回访数据库',
value: response.services.callback_db ? '正常' : '异常',
status: response.services.callback_db ? 'online' : 'warning'
},
{
label: '系统状态',
value: response.status === 'ok' ? '正常' : '异常',
status: response.status === 'ok' ? 'online' : 'offline'
}
];
renderSystemStatus(statusItems);
} catch (error) {
console.error('加载系统状态失败:', error);
document.getElementById('systemStatusContent').innerHTML = '<div class="loading">系统状态检查失败</div>';
}
}
// 渲染系统状态
function renderSystemStatus(statusItems) {
const content = document.getElementById('systemStatusContent');
content.innerHTML = statusItems.map(item => `
<div class="status-item">
<div class="status-label">${item.label}</div>
<div class="status-value">
<span>${item.value}</span>
<div class="status-indicator ${item.status}"></div>
</div>
</div>
`).join('');
}
// 快速操作函数
function createCallbackRecord() {
alert('新建回访记录功能开发中...');
}
function viewReports() {
alert('查看报告功能开发中...');
}
function viewSystemLogs() {
alert('系统日志功能开发中...');
}
function viewAnalytics() {
alert('数据分析功能开发中...');
}
function exportData() {
alert('数据导出功能开发中...');
}
// 修改密码相关函数
function showChangePasswordModal() {
document.getElementById('changePasswordModal').style.display = 'block';
document.getElementById('oldPassword').focus();
}
function hideChangePasswordModal() {
document.getElementById('changePasswordModal').style.display = 'none';
document.getElementById('changePasswordForm').reset();
}
// 修改密码表单提交
document.getElementById('changePasswordForm').addEventListener('submit', async function(e) {
e.preventDefault();
const oldPassword = document.getElementById('oldPassword').value;
const newPassword = document.getElementById('newPasswordChange').value;
const confirmPassword = document.getElementById('confirmPassword').value;
// 验证输入
if (!oldPassword || !newPassword || !confirmPassword) {
alert('请填写所有字段');
return;
}
if (newPassword.length < 6) {
alert('新密码长度至少6位');
return;
}
if (newPassword !== confirmPassword) {
alert('两次输入的新密码不一致');
return;
}
const changeBtn = document.getElementById('changePasswordBtn');
const originalText = changeBtn.textContent;
changeBtn.textContent = '修改中...';
changeBtn.disabled = true;
try {
const response = await authClient.changePassword(oldPassword, newPassword);
if (response.success) {
alert('密码修改成功,请重新登录');
hideChangePasswordModal();
// 延迟跳转到登录页面
setTimeout(() => {
authClient.redirectToLogin();
}, 1000);
} else {
alert('修改密码失败: ' + response.message);
}
} catch (error) {
console.error('修改密码失败:', error);
alert('修改密码失败: ' + error.message);
} finally {
changeBtn.textContent = originalText;
changeBtn.disabled = false;
}
});
// 监听认证状态变化
authClient.onAuthChange((isLoggedIn, user) => {
if (isLoggedIn && user) {
updateWelcomeMessage();
toggleAdminFeatures();
// 更新用户信息显示
const userInfoHtml = `
<span class="username">欢迎,${user.username}</span>
<span class="role">${user.role === 'admin' ? '管理员' : '用户'}</span>
<button onclick="authClient.logout()" class="btn btn-secondary" style="padding: 6px 12px; font-size: 12px; margin-left: 10px;">注销</button>
`;
document.getElementById('userInfo').innerHTML = userInfoHtml;
} else {
// 用户未登录,重定向到登录页面
authClient.redirectToLogin();
}
});
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
const modal = document.getElementById('changePasswordModal');
if (e.target === modal) {
hideChangePasswordModal();
}
});
</script>
</body>
</html>
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据库迁移管理器
提供安全的数据库结构更新和数据迁移功能
"""
import os
import re
import pymysql
import hashlib
from datetime import datetime
from typing import List, Dict, Optional, Tuple
import logging
import json
class DatabaseMigrationManager:
"""数据库迁移管理器"""
def __init__(self, db_config: Dict, migrations_dir: str = "migrations"):
"""
初始化迁移管理器
Args:
db_config: 数据库配置
migrations_dir: 迁移文件目录
"""
self.db_config = db_config
self.migrations_dir = migrations_dir
self.logger = self._setup_logger()
# 确保迁移目录存在
os.makedirs(migrations_dir, exist_ok=True)
# 初始化迁移历史表
self._init_migration_table()
def _setup_logger(self) -> logging.Logger:
"""设置日志记录器"""
logger = logging.getLogger('migration_manager')
logger.setLevel(logging.INFO)
if not logger.handlers:
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
def _get_connection(self):
"""获取数据库连接"""
return pymysql.connect(**self.db_config)
def _init_migration_table(self):
"""初始化迁移历史表"""
try:
connection = self._get_connection()
cursor = connection.cursor()
# 创建迁移历史表
cursor.execute("""
CREATE TABLE IF NOT EXISTS migration_history (
id INT AUTO_INCREMENT PRIMARY KEY,
version VARCHAR(50) NOT NULL UNIQUE,
filename VARCHAR(255) NOT NULL,
checksum VARCHAR(64) NOT NULL,
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
execution_time_ms INT,
status ENUM('SUCCESS', 'FAILED', 'ROLLED_BACK') DEFAULT 'SUCCESS',
error_message TEXT,
INDEX idx_version (version),
INDEX idx_executed_at (executed_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
""")
connection.commit()
self.logger.info("✅ 迁移历史表初始化完成")
except Exception as e:
self.logger.error(f"❌ 初始化迁移历史表失败: {e}")
raise
finally:
if 'connection' in locals():
connection.close()
def create_migration(self, name: str, description: str = "") -> str:
"""
创建新的迁移文件
Args:
name: 迁移名称
description: 迁移描述
Returns:
创建的迁移文件路径
"""
# 生成版本号(时间戳格式)
version = datetime.now().strftime("%Y%m%d_%H%M%S")
# 清理文件名
clean_name = re.sub(r'[^\w\-_]', '_', name.lower())
filename = f"{version}_{clean_name}.sql"
filepath = os.path.join(self.migrations_dir, filename)
# 创建迁移文件模板
template = f"""-- Migration: {name}
-- Version: {version}
-- Description: {description}
-- Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-- ==========================================
-- UP Migration (执行迁移)
-- ==========================================
-- 在这里添加你的迁移SQL语句
-- 例如:
-- ALTER TABLE users ADD COLUMN email VARCHAR(255);
-- CREATE INDEX idx_users_email ON users(email);
-- ==========================================
-- DOWN Migration (回滚迁移)
-- ==========================================
-- 注意:DOWN部分用于回滚,请确保操作的可逆性
-- 在这里添加回滚SQL语句
-- 例如:
-- DROP INDEX idx_users_email ON users;
-- ALTER TABLE users DROP COLUMN email;
-- ==========================================
-- 验证脚本 (可选)
-- ==========================================
-- 在这里添加验证SQL,确保迁移成功
-- 例如:
-- SELECT COUNT(*) FROM information_schema.columns
-- WHERE table_name = 'users' AND column_name = 'email';
"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(template)
self.logger.info(f"✅ 创建迁移文件: {filepath}")
return filepath
def get_pending_migrations(self) -> List[str]:
"""获取待执行的迁移文件"""
try:
connection = self._get_connection()
cursor = connection.cursor()
# 获取已执行的迁移版本
cursor.execute("SELECT version FROM migration_history WHERE status = 'SUCCESS'")
executed_versions = {row[0] for row in cursor.fetchall()}
# 获取所有迁移文件
migration_files = []
for filename in os.listdir(self.migrations_dir):
if filename.endswith('.sql'):
version = filename.split('_')[0] + '_' + filename.split('_')[1]
if version not in executed_versions:
migration_files.append(filename)
# 按版本排序
migration_files.sort()
return migration_files
except Exception as e:
self.logger.error(f"❌ 获取待执行迁移失败: {e}")
raise
finally:
if 'connection' in locals():
connection.close()
def _calculate_checksum(self, content: str) -> str:
"""计算文件内容的校验和"""
return hashlib.sha256(content.encode('utf-8')).hexdigest()
def _parse_migration_file(self, filepath: str) -> Dict:
"""解析迁移文件"""
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 提取UP和DOWN部分
up_match = re.search(r'-- UP Migration.*?\n(.*?)-- DOWN Migration', content, re.DOTALL)
down_match = re.search(r'-- DOWN Migration.*?\n(.*?)-- ==========================================', content, re.DOTALL)
up_sql = up_match.group(1).strip() if up_match else ""
down_sql = down_match.group(1).strip() if down_match else ""
# 清理SQL(移除注释和空行)
up_sql = self._clean_sql(up_sql)
down_sql = self._clean_sql(down_sql)
return {
'content': content,
'up_sql': up_sql,
'down_sql': down_sql,
'checksum': self._calculate_checksum(content)
}
def _clean_sql(self, sql: str) -> str:
"""清理SQL语句"""
lines = []
for line in sql.split('\n'):
line = line.strip()
if line and not line.startswith('--'):
lines.append(line)
return '\n'.join(lines)
def _create_backup(self) -> str:
"""创建数据库备份"""
backup_dir = "database_backups"
os.makedirs(backup_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_file = os.path.join(backup_dir, f"migration_backup_{timestamp}.sql")
try:
# 使用mysqldump创建备份
import subprocess
cmd = [
'mysqldump',
f'--host={self.db_config["host"]}',
f'--port={self.db_config["port"]}',
f'--user={self.db_config["user"]}',
f'--password={self.db_config["password"]}',
'--single-transaction',
'--routines',
'--triggers',
self.db_config['database']
]
with open(backup_file, 'w') as f:
subprocess.run(cmd, stdout=f, check=True)
self.logger.info(f"✅ 数据库备份完成: {backup_file}")
return backup_file
except Exception as e:
self.logger.error(f"❌ 数据库备份失败: {e}")
raise
def execute_migration(self, filename: str, dry_run: bool = False) -> bool:
"""
执行单个迁移文件
Args:
filename: 迁移文件名
dry_run: 是否为试运行模式
Returns:
执行是否成功
"""
filepath = os.path.join(self.migrations_dir, filename)
if not os.path.exists(filepath):
raise FileNotFoundError(f"迁移文件不存在: {filepath}")
# 解析迁移文件
migration_data = self._parse_migration_file(filepath)
version = filename.split('_')[0] + '_' + filename.split('_')[1]
if dry_run:
self.logger.info(f"🔍 试运行模式 - 迁移: {filename}")
self.logger.info(f"SQL内容:\n{migration_data['up_sql']}")
return True
# 创建备份
backup_file = self._create_backup()
connection = None
start_time = datetime.now()
try:
connection = self._get_connection()
connection.begin() # 开始事务
cursor = connection.cursor()
self.logger.info(f"🚀 执行迁移: {filename}")
# 执行UP SQL
if migration_data['up_sql']:
# 分割SQL语句(处理多个语句)
statements = [stmt.strip() for stmt in migration_data['up_sql'].split(';') if stmt.strip()]
for stmt in statements:
self.logger.debug(f"执行SQL: {stmt[:100]}...")
cursor.execute(stmt)
# 记录迁移历史
execution_time = int((datetime.now() - start_time).total_seconds() * 1000)
cursor.execute("""
INSERT INTO migration_history
(version, filename, checksum, execution_time_ms, status)
VALUES (%s, %s, %s, %s, 'SUCCESS')
""", (version, filename, migration_data['checksum'], execution_time))
connection.commit()
self.logger.info(f"✅ 迁移执行成功: {filename} (耗时: {execution_time}ms)")
return True
except Exception as e:
if connection:
connection.rollback()
# 记录失败信息
try:
cursor.execute("""
INSERT INTO migration_history
(version, filename, checksum, status, error_message)
VALUES (%s, %s, %s, 'FAILED', %s)
""", (version, filename, migration_data['checksum'], str(e)))
connection.commit()
except:
pass
self.logger.error(f"❌ 迁移执行失败: {filename} - {e}")
return False
finally:
if connection:
connection.close()
def migrate(self, target_version: Optional[str] = None, dry_run: bool = False) -> bool:
"""
执行所有待执行的迁移
Args:
target_version: 目标版本(可选)
dry_run: 是否为试运行模式
Returns:
是否全部执行成功
"""
pending_migrations = self.get_pending_migrations()
if not pending_migrations:
self.logger.info("✅ 没有待执行的迁移")
return True
# 过滤到目标版本
if target_version:
filtered_migrations = []
for migration in pending_migrations:
version = migration.split('_')[0] + '_' + migration.split('_')[1]
filtered_migrations.append(migration)
if version == target_version:
break
pending_migrations = filtered_migrations
self.logger.info(f"📋 待执行迁移数量: {len(pending_migrations)}")
success_count = 0
for migration in pending_migrations:
if self.execute_migration(migration, dry_run):
success_count += 1
else:
self.logger.error(f"❌ 迁移失败,停止执行: {migration}")
break
if success_count == len(pending_migrations):
self.logger.info(f"🎉 所有迁移执行完成! ({success_count}/{len(pending_migrations)})")
return True
else:
self.logger.error(f"⚠️ 部分迁移失败: ({success_count}/{len(pending_migrations)})")
return False
def rollback_migration(self, version: str) -> bool:
"""
回滚指定版本的迁移
Args:
version: 要回滚的版本号
Returns:
回滚是否成功
"""
try:
connection = self._get_connection()
cursor = connection.cursor()
# 检查迁移是否存在且已执行
cursor.execute("""
SELECT filename, checksum FROM migration_history
WHERE version = %s AND status = 'SUCCESS'
""", (version,))
result = cursor.fetchone()
if not result:
self.logger.error(f"❌ 未找到已执行的迁移版本: {version}")
return False
filename, checksum = result
filepath = os.path.join(self.migrations_dir, filename)
if not os.path.exists(filepath):
self.logger.error(f"❌ 迁移文件不存在: {filepath}")
return False
# 解析迁移文件
migration_data = self._parse_migration_file(filepath)
# 验证校验和
if migration_data['checksum'] != checksum:
self.logger.error(f"❌ 迁移文件校验和不匹配,可能已被修改: {filename}")
return False
if not migration_data['down_sql']:
self.logger.error(f"❌ 迁移文件缺少DOWN部分,无法回滚: {filename}")
return False
# 创建备份
backup_file = self._create_backup()
connection.begin() # 开始事务
self.logger.info(f"🔄 回滚迁移: {filename}")
# 执行DOWN SQL
statements = [stmt.strip() for stmt in migration_data['down_sql'].split(';') if stmt.strip()]
for stmt in statements:
self.logger.debug(f"执行回滚SQL: {stmt[:100]}...")
cursor.execute(stmt)
# 更新迁移历史状态
cursor.execute("""
UPDATE migration_history
SET status = 'ROLLED_BACK'
WHERE version = %s
""", (version,))
connection.commit()
self.logger.info(f"✅ 迁移回滚成功: {filename}")
return True
except Exception as e:
if 'connection' in locals() and connection:
connection.rollback()
self.logger.error(f"❌ 迁移回滚失败: {version} - {e}")
return False
finally:
if 'connection' in locals():
connection.close()
def get_migration_status(self) -> Dict:
"""获取迁移状态信息"""
try:
connection = self._get_connection()
cursor = connection.cursor()
# 获取迁移历史
cursor.execute("""
SELECT version, filename, status, executed_at, execution_time_ms, error_message
FROM migration_history
ORDER BY executed_at DESC
""")
history = []
for row in cursor.fetchall():
history.append({
'version': row[0],
'filename': row[1],
'status': row[2],
'executed_at': row[3].strftime('%Y-%m-%d %H:%M:%S') if row[3] else None,
'execution_time_ms': row[4],
'error_message': row[5]
})
# 获取待执行迁移
pending = self.get_pending_migrations()
return {
'executed_count': len([h for h in history if h['status'] == 'SUCCESS']),
'failed_count': len([h for h in history if h['status'] == 'FAILED']),
'rolled_back_count': len([h for h in history if h['status'] == 'ROLLED_BACK']),
'pending_count': len(pending),
'history': history,
'pending_migrations': pending
}
except Exception as e:
self.logger.error(f"❌ 获取迁移状态失败: {e}")
raise
finally:
if 'connection' in locals():
connection.close()
def validate_migrations(self) -> bool:
"""验证所有迁移文件的完整性"""
self.logger.info("🔍 验证迁移文件完整性...")
migration_files = [f for f in os.listdir(self.migrations_dir) if f.endswith('.sql')]
migration_files.sort()
valid_count = 0
for filename in migration_files:
try:
filepath = os.path.join(self.migrations_dir, filename)
migration_data = self._parse_migration_file(filepath)
# 检查基本结构
if not migration_data['up_sql']:
self.logger.warning(f"⚠️ 迁移文件缺少UP部分: {filename}")
continue
# 检查版本号格式
version_match = re.match(r'^\d{8}_\d{6}_', filename)
if not version_match:
self.logger.warning(f"⚠️ 迁移文件名格式不正确: {filename}")
continue
valid_count += 1
self.logger.debug(f"✅ 验证通过: {filename}")
except Exception as e:
self.logger.error(f"❌ 验证失败: {filename} - {e}")
self.logger.info(f"📊 验证完成: {valid_count}/{len(migration_files)} 个文件通过验证")
return valid_count == len(migration_files)
#!/bin/bash
# 生产环境部署脚本(集成数据库迁移)
# 功能:
# 1. 数据库备份
# 2. 代码更新
# 3. 容器重新部署
# 4. 数据库迁移
# 5. 验证和测试
set -e # 遇到错误立即退出
# 配置变量
BACKUP_DIR="database_backups"
PROJECT_DIR="customer-recall"
LOG_FILE="deploy_$(date +%Y%m%d_%H%M%S).log"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# 错误处理函数
handle_error() {
log_error "部署过程中发生错误,正在回滚..."
# 如果容器已经停止,尝试恢复
if ! docker compose ps | grep -q "Up"; then
log_info "尝试恢复容器服务..."
docker compose up -d || log_error "容器恢复失败"
fi
log_error "部署失败!请检查日志文件: $LOG_FILE"
exit 1
}
# 设置错误处理
trap handle_error ERR
# 开始部署
log_info "🚀 开始生产环境部署(集成迁移系统)..."
log_info "📍 项目目录: $PROJECT_DIR"
log_info "📍 备份目录: $BACKUP_DIR"
log_info "📍 日志文件: $LOG_FILE"
# 检查项目目录
if [ ! -d "$PROJECT_DIR" ]; then
log_error "项目目录不存在: $PROJECT_DIR"
exit 1
fi
cd "$PROJECT_DIR"
# 第一步:创建数据库备份
log_info "📦 第一步:创建数据库备份..."
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/migration_backup_$TIMESTAMP.sql"
# 检查MySQL容器是否运行
if docker compose ps mysql | grep -q "Up"; then
log_info "MySQL容器正在运行,创建备份..."
# 使用docker exec执行mysqldump
if docker compose exec -T mysql mysqldump -u root -pdev_password_123 \
--single-transaction --routines --triggers callback_system > "$BACKUP_FILE"; then
log_success "数据库备份完成: $BACKUP_FILE"
# 验证备份文件
if [ -s "$BACKUP_FILE" ]; then
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
log_info "备份文件大小: $BACKUP_SIZE"
else
log_error "备份文件为空,备份可能失败"
exit 1
fi
else
log_error "数据库备份失败"
exit 1
fi
else
log_warning "MySQL容器未运行,跳过备份"
fi
# 第二步:更新代码
log_info "📥 第二步:更新代码..."
if git pull origin master; then
log_success "代码更新成功"
else
log_error "代码更新失败"
exit 1
fi
# 第三步:验证迁移文件
log_info "🔍 第三步:验证迁移文件..."
if [ -f "migrate.py" ] && [ -d "migrations" ]; then
log_info "迁移系统文件存在,验证迁移文件..."
# 在当前环境验证(如果有Python环境)
if command -v python3 &> /dev/null; then
if python3 migrate.py validate; then
log_success "迁移文件验证通过"
else
log_warning "迁移文件验证失败,但继续部署"
fi
else
log_info "本地无Python环境,将在容器中验证"
fi
else
log_warning "迁移系统文件不存在,使用传统部署方式"
fi
# 第四步:重新部署容器
log_info "🐳 第四步:重新部署容器..."
log_info "停止现有容器..."
docker compose down
log_info "重新构建并启动容器..."
if docker compose up -d --build; then
log_success "容器部署成功"
else
log_error "容器部署失败"
exit 1
fi
# 等待容器启动
log_info "⏳ 等待容器完全启动..."
sleep 10
# 检查容器状态
log_info "🔍 检查容器状态..."
docker compose ps
# 第五步:执行数据库迁移
log_info "🗄️ 第五步:执行数据库迁移..."
# 等待数据库完全启动
log_info "等待数据库服务启动..."
for i in {1..30}; do
if docker compose exec -T mysql mysql -u root -pdev_password_123 -e "SELECT 1;" &>/dev/null; then
log_success "数据库连接成功"
break
fi
if [ $i -eq 30 ]; then
log_error "数据库启动超时"
exit 1
fi
log_info "等待数据库启动... ($i/30)"
sleep 2
done
# 执行迁移
if [ -f "migrate.py" ]; then
log_info "使用迁移系统执行数据库更新..."
# 检查迁移状态
log_info "检查当前迁移状态..."
docker compose exec -T patient_callback_app python migrate.py status || log_warning "无法获取迁移状态"
# 试运行迁移
log_info "试运行迁移(验证SQL语法)..."
if docker compose exec -T patient_callback_app python migrate.py migrate --dry-run; then
log_success "迁移试运行成功"
# 执行实际迁移
log_info "执行实际迁移..."
if docker compose exec -T patient_callback_app python migrate.py migrate; then
log_success "数据库迁移执行成功"
else
log_error "数据库迁移执行失败"
exit 1
fi
else
log_warning "迁移试运行失败,跳过迁移"
fi
else
log_info "使用传统方式执行数据导入..."
if [ -f "safe_import_patients.py" ]; then
if docker compose exec -T patient_callback_app python safe_import_patients.py; then
log_success "数据导入成功"
else
log_warning "数据导入失败,但继续部署"
fi
else
log_warning "数据导入脚本不存在"
fi
fi
# 第六步:验证部署结果
log_info "✅ 第六步:验证部署结果..."
# 检查应用健康状态
log_info "检查应用健康状态..."
sleep 5
if curl -f http://localhost:5000/login &>/dev/null; then
log_success "应用健康检查通过"
else
log_warning "应用健康检查失败,但部署继续"
fi
# 检查数据库状态
log_info "检查数据库状态..."
docker compose exec -T mysql mysql -u root -pdev_password_123 -e "
USE callback_system;
SELECT 'users' as table_name, COUNT(*) as count FROM users
UNION ALL
SELECT 'patients', COUNT(*) FROM patients
UNION ALL
SELECT 'callback_records', COUNT(*) FROM callback_records;
" || log_warning "无法获取数据库统计信息"
# 第七步:清理旧备份
log_info "🧹 第七步:清理旧备份文件..."
find "$BACKUP_DIR" -name "migration_backup_*.sql" -mtime +7 -delete
log_success "旧备份文件清理完成"
# 部署完成
log_success "🎉 生产环境部署完成!"
log_info "📊 部署总结:"
log_info " ✅ 数据库备份: $BACKUP_FILE"
log_info " ✅ 代码更新: 完成"
log_info " ✅ 容器部署: 完成"
log_info " ✅ 数据库迁移: 完成"
log_info " ✅ 验证测试: 完成"
log_info " 📍 备份位置: $BACKUP_DIR"
log_info " 📍 日志文件: $LOG_FILE"
log_info " 🕐 完成时间: $(date)"
# 显示最终状态
log_info "📋 最终容器状态:"
docker compose ps
log_info "📋 当前备份文件列表:"
ls -lh "$BACKUP_DIR"/migration_backup_*.sql 2>/dev/null || log_info "暂无备份文件"
log_success "部署脚本执行完成!"
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
开发环境启动脚本
自动设置开发环境变量并启动Flask应用
"""
import os
import sys
def start_dev_server():
"""启动开发服务器"""
print("🚀 启动开发服务器...")
# 设置开发环境变量
os.environ['FLASK_APP'] = 'app.py'
os.environ['FLASK_ENV'] = 'development'
os.environ['FLASK_DEBUG'] = '1'
os.environ['PORT'] = '5001'
print("📋 开发环境配置:")
print(" - 热重载: 启用")
print(" - 调试模式: 启用")
print(" - 端口: 5001")
print(" - 访问地址: http://localhost:5001")
print()
# 导入并启动应用
try:
from app import app
app.run(
host='0.0.0.0',
port=5001,
debug=True,
use_reloader=True
)
except KeyboardInterrupt:
print("\n👋 开发服务器已停止")
except Exception as e:
print(f"❌ 启动失败: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
start_dev_server()
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-29 15:31:21",
"total_count": 10,
"success_count": 10,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K036558",
"patient_name": "患者1",
"age": 39,
"gender": "女",
"generation_time": "2025-07-29 15:25:53",
"callback_script": "好的,这就为您生成针对患者周绍懿女士的专业回访话术。\n\n---\n\n### **回访话术**\n\n**═══ 第一部分:开场白 ═══**\n您好,周女士,我是江苏瑞泰通善口腔医院的回访专员。\n胡婷医生特意交代我来关注您的后续情况。\n(如果是熟悉患者可说:胡婷医生上次还和我提起您呢。)\n您自从12月26号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n在您上次检查中,我们发现了牙槽骨吸收的情况。\n如果牙槽骨吸收问题不及时处理,可能会出现如牙齿松动、牙缝变大等情况。\n您现在39岁,正是维护口腔健康的关键时期,及时关注和干预,能更好地稳住牙槽骨,保住自己的牙齿。\n胡婷医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n为了您的口腔健康,建议来院复查一下。\n这是健康维护的建议,让胡婷医生帮您再仔细看看。\n复查检查大概需要【30-45分钟,需要仔细检查牙周健康状况】的时间。\n建议尽快来看看,胡婷医生【时间段1】和【时间段2】这两个时间段有空(可根据医生具体排班描述时间段),您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-26 00:00:00",
"last_clinic": "江苏瑞泰通善口腔医院",
"last_doctor": "胡婷",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0I021277",
"patient_name": "患者2",
"age": 35,
"gender": "男",
"generation_time": "2025-07-29 15:26:32",
"callback_script": "好的,这就为您生成针对患者周全先生的专业回访话术。\n\n---\n\n### 患者情况分析\n\n1. **年龄判断**: 患者35岁,属于`≥13岁`的范畴,因此执行**“成人漏诊话术模板”**(4模块结构)。\n2. **漏诊项优先级判断**:\n * 患者存在两个漏诊项:`缺失牙`(优先级③)和 `牙槽骨吸收`(优先级④)。\n * 根据优先级排序,**`缺失牙`** 为本次沟通的唯一核心项目,`牙槽骨吸收`将被忽略。\n3. **信息完整性**: 关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n### 专业回访话术\n\n**═══ 第一部分:开场白 ═══**\n* 您好,周先生,我是江苏瑞泰通善口腔红豆门诊的回访专员。\n* 陈淼医生特意交代我来关注您的后续情况。\n* (如果是熟悉患者可说:陈淼医生上次还和我提起您呢。)\n* 您自从6月15号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n* **小节1 - 现状描述(短句1):**\n 在您上次检查中,我们发现了有几颗缺失牙的情况。\n\n* **小节2 - 健康提醒(短句2):**\n 如果缺失牙问题不及时处理,可能会出现邻牙倾斜、对牙伸长的情况。\n\n* **小-节3 - 个人化关怀(短句3):**\n 您现在35岁,正值事业关键期,及时修复不仅能保护好邻牙,恢复正常的咀嚼功能,对个人形象也很有帮助。\n\n* **小节4 - 专业建议(短句4):**\n 陈淼医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n* **小节1 - 复查重要性(短句1):**\n 为了您的口腔健康,建议您抽空来院复查一下。\n\n* **小节2 - 健康维护(短句2):**\n 这是我们给您的健康维护建议,让陈淼医生帮您再仔细看看具体情况。\n\n* **小节3 - 检查说明(短句3):**\n 复查检查大概需要30分钟的时间,主要是了解一下您缺失牙牙区的现状。\n\n* **小节4 - 引导预约(短句4):**\n 建议尽快来看看,陈淼医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段),您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n* **预约成功:**\n * 好的,那我们【具体预约时间】见。\n * 那不打扰您了,祝您生活愉快!\n\n* **预约不成功:**\n * 好的,那我下个星期再跟您联系。\n * 那不打扰您了,祝您生活愉快!\n\n**═══ 附加:赠送礼品(见机行事)═══**\n* 我们为回访患者准备了小礼品。\n* 到时候来检查可以领取,表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-15 00:00:00",
"last_clinic": "江苏瑞泰通善口腔红豆门诊",
"last_doctor": "陈淼",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0L003747",
"patient_name": "患者3",
"age": 61,
"gender": "男",
"generation_time": "2025-07-29 15:27:04",
"callback_script": "好的,这是根据您提供的患者信息和严格的话术模板生成的回访内容。\n\n**患者年龄**:61岁(≥13岁),执行“成人漏诊话术模板”。\n**漏诊项优先级处理**:患者漏诊项为“牙槽骨吸收”,无其他更高优先级项目,将围绕此项生成话术。\n\n***\n\n### **患者【苏衍】专属回访话术**\n\n**═══ 第一部分:开场白 ═══**\n* 您好,我是江苏瑞泰通善口腔新吴门诊的回访专员。\n* 王国盛医生特意交代我来关注您的后续情况。\n* (如果是熟悉患者可说:王国盛医生上次还和我提起您呢)\n* 您自从去年12月检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n* 在您上次检查中,我们发现了牙槽骨吸收的情况。\n* 如果牙槽骨吸收问题不及时处理,可能会出现的如牙齿松动、脱落风险情况。\n* 您这个年纪,虽然牙槽骨吸收比较常见,但现在关注起来,对保住咱们自己的牙齿,提升晚年的生活质量还是很有意义的。\n* 王国盛医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n* 为了您的口腔健康,建议来院复查一下。\n* 这是健康维护的建议,让王国盛医生帮您再仔细看看。\n* 复查检查大概需要【30-45分钟】的时间。\n* 建议尽快来看看,王国盛医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n* 好的,那我们【具体预约时间】见。\n* 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n* 好的,那我下个星期再跟您联系。\n* 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n* 我们为回访患者准备了小礼品。\n* 到时候来检查可以领取。\n* 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔新吴门诊",
"last_doctor": "王国盛",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K068025",
"patient_name": "患者4",
"age": 76,
"gender": "女",
"generation_time": "2025-07-29 15:27:35",
"callback_script": "好的,已收到您的指令。正在为您生成针对患者 **徐涵明** 的专业回访话术。\n\n**逻辑判断:**\n1. **年龄判断**:患者76岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项判断**:漏诊项为“牙槽骨吸收”,是当前唯一需要处理的漏诊项。\n\n**回访话术如下:**\n\n**═══ 第一部分:开场白 ═══**\n• 您好,请问是徐涵明阿姨吗?我是江苏瑞泰通善口腔医院的回访专员。\n• 赵超医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:赵超医生上次还和我提起您呢)\n• 您自从1月9号检查后,口腔情况怎么样?之前补的牙用着还可以吗?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n小节1 - 现状描述(短句1):\n在您上次检查中,我们发现了牙槽骨吸收的情况。\n\n小节2 - 健康提醒(短句2):\n如果牙槽骨吸收问题不及时处理,可能会出现牙齿松动、甚至脱落的风险。\n\n小节3 - 个人化关怀(短句3):\n您这个年纪,保住自己的牙齿、维持好的咀嚼能力对晚年生活质量特别重要。\n\n小节4 - 专业建议(短句4):\n赵超医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n小节1 - 复查重要性(短句1):\n为了您的口腔健康,建议来院复查一下。\n\n小节2 - 健康维护(短句2):\n这是健康维护的建议,让赵超医生帮您再仔细看看。\n\n小节3 - 检查说明(短句3):\n复查检查大概需要30-45分钟的时间,主要是仔细看看您牙周的健康状况。\n\n小节4 - 引导预约(短句4):\n建议尽快来看看,赵超医生这周三上午和周五下午这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快,身体健康!\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快,身体健康!\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 阿姨,我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-01-09 00:00:00",
"last_clinic": "江苏瑞泰通善口腔医院",
"last_doctor": "赵超",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0F028338",
"patient_name": "患者5",
"age": 5,
"gender": "男",
"generation_time": "2025-07-29 15:28:26",
"callback_script": "好的,已收到您的指令。正在为您生成回访话术。\n\n**逻辑判断:**\n1. **年龄判断**:患者【牟子凡】年龄为5岁,≤12岁,执行“儿童早矫话术模板”(5模块结构)。\n2. **漏诊项判断**:患者漏诊项为【恒牙萌出空间不足】,优先级为最高。本次话术将围绕此项进行。\n\n**正在生成话术...**\n\n***\n\n### **儿童早矫回访话术 - 牟子凡**\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔东亭门诊的回访专员。\n• 段芹医生特意交代我来关注宝宝的后续情况。\n• (如果是熟悉患者可说:段芹医生上次还和我提起宝宝呢)\n• 宝宝自从12月17号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知牙齿问题-健康提醒 ═══**\n小节1 - 现状描述(短句1):\n上次段芹医生检查发现,宝宝未来恒牙长出来的位置可能会不够。\n\n小节2 - 原因说明(短句2):\n这主要是因为宝宝现在的几颗乳牙存在一些健康问题,有可能会过早脱落。\n\n小节3 - 不治疗危害(短句3):\n如果乳牙过早掉了,旁边的牙齿就可能会挤占空间。\n\n小节4 - 后果说明(短句4):\n这样将来恒牙萌出时,就没有足够的位置,容易长得不整齐。\n\n小节5 - 解决方案(短句5):\n所以医生建议要密切关注,必要时可能需要一个叫“间隙保持器”的小装置来提前保住位置。\n\n**═══ 第三部分:复查建议 ═══**\n小节1 - 复查时间(短句1):\n建议3个月左右带宝宝来院检查一下。\n\n小节2 - 检查内容(短句2):\n一方面是做全面检查,看看蛀牙的情况,另一方面也是为了评估这个牙齿空间的问题。\n\n小节3 - 预防措施(短句3):\n我们也会看看要不要给宝宝涂氟,更好地保护他的牙齿。\n\n小节4 - 专业建议(短句4):\n也请段芹医生再仔细看一下宝宝的具体情况,给一些针对性的保护建议。\n\n**═══ 第四部分:引导预约 ═══**\n• 建议尽快带宝宝过来看看。\n• 段芹医生【周三下午】和【周五上午】这两个时间段有空。\n• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。\n\n**═══ 第五部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要多观察孩子的牙齿情况。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访的小朋友准备了小礼品。\n• 到时候来检查可以领取。\n• 宝宝一定会喜欢的。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔东亭门诊",
"last_doctor": "段芹",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0I013775",
"patient_name": "患者6",
"age": 36,
"gender": "女",
"generation_time": "2025-07-29 15:29:08",
"callback_script": "好的,这就为您生成针对患者俞科萍女士的专业回访话术。\n\n---\n\n### 回访对象分析\n\n1. **年龄判断**: 患者36岁,大于13岁,执行“**成人漏诊话术模板**”(4模块结构)。\n2. **漏诊项优先级判断**:\n * 患者存在漏诊项:【缺失牙】和【牙槽骨吸收】。\n * 根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕【缺失牙】进行**,忽略牙槽骨吸收。\n3. **信息完整性**: 关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n### 专业回访话术\n\n**═══ 第一部分:开场白 ═══**\n* 您好,俞女士,我是江苏瑞泰通善口腔红豆门诊的回访专员。\n* 叶冯雷医生特意交代我来关注您的后续情况。\n* (如果是熟悉患者可说:叶冯雷医生上次还和我提起您呢)\n* 您自从1月11号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n小节1 - 现状描述(短句1):\n在您上次检查中,我们发现了您有几颗缺失牙的情况。\n\n小节2 - 健康提醒(短句2):\n如果缺失牙问题不及时处理,可能会出现邻牙倾斜、咬合紊乱的情况。\n\n小节3 - 个人化关怀(短句3):\n您今年36岁,正值事业发展的关键时期,及时修复对维持您的个人形象和自信心都特别重要。\n\n小节4 - 专业建议(短句4):\n叶冯雷医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n小节1 - 复查重要性(短句1):\n为了您的口腔健康,建议您抽空来院复查一下。\n\n小节2 - 健康维护(短句2):\n这是一个健康维护的建议,让叶冯雷医生帮您再仔细看看具体情况。\n\n小节3 - 检查说明(短句3):\n复查检查大概需要30分钟左右的时间。\n\n小节4 - 引导预约(短句4):\n建议尽快来看看,叶冯雷医生【本周三下午】和【本周五上午】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n* 好的,那我们【具体预约时间】见。\n* 那不打扰您了,祝您生活愉快!\n\n**预约不成功:**\n* 好的,那我下个星期再跟您联系。\n* 那不打扰您了,祝您生活愉快!\n\n**═══ 附加:赠送礼品(见机行事)═══**\n* 我们为回访患者准备了精美的小礼品。\n* 到时候您来检查可以顺便领取,表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-01-11 00:00:00",
"last_clinic": "江苏瑞泰通善口腔红豆门诊",
"last_doctor": "叶冯雷",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0J005963",
"patient_name": "患者7",
"age": 42,
"gender": "男",
"generation_time": "2025-07-29 15:29:39",
"callback_script": "好的,这是为您生成的专业回访话术。\n\n***\n\n### **回访话术**\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔马山门诊的回访专员。\n• 范鹏军医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:范鹏军医生上次还和我提起您呢)\n• 您自从12月17号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n小节1 - 现状描述(短句1):\n在您上次检查中,我们发现了牙槽骨吸收的情况。\n\n小节2 - 健康提醒(短句2):\n如果牙槽骨吸收问题不及时处理,可能会出现的如牙齿松动、牙缝变大的情况。\n\n小节3 - 个人化关怀(短句3):\n您现在正是维护口腔健康的关键时期,及时处理对稳住牙槽骨、保住自己的牙齿非常重要。\n\n小节4 - 专业建议(短句4):\n范鹏军医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n小节1 - 复查重要性(短句1):\n为了您的口腔健康,建议来院复查一下。\n\n小节2 - 健康维护(短句2):\n这是健康维护的建议,让范鹏军医生帮您再仔细看看。\n\n小节3 - 检查说明(短句3):\n复查检查大概需要30-45分钟的时间。\n\n小节4 - 引导预约(短句4):\n建议尽快来看看,范鹏军医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段),您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔马山门诊",
"last_doctor": "范鹏军",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M005023",
"patient_name": "患者8",
"age": 33,
"gender": "男",
"generation_time": "2025-07-29 15:30:15",
"callback_script": "好的,正在为您生成回访话术...\n\n**逻辑判断过程:**\n1. **年龄判断**:患者年龄为33岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:\n * 患者存在“缺失牙”(诊断:缺失,牙位:21)和“牙槽骨吸收”两个漏诊项。\n * 根据优先级规则:缺失牙(优先级③) > 牙槽骨吸收(优先级④)。\n * **最终决定**:本次话术将严格围绕优先级最高的“**缺失牙**”项目展开,忽略“牙槽骨吸收”及其他所有问题。\n\n---\n\n### **患者关怀回访话术**\n\n**═══ 第一部分:开场白 ═══**\n* 您好,王先生,我是江苏瑞泰通善口腔学前街医院的回访专员。\n* 上次为您看诊的陈洋洋医生特意交代我,打电话来关心一下您的后续情况。\n* 您自从12月17号在我们这里拔完牙后,口腔感觉怎么样?恢复得还好吗?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n* **小节1 - 现状描述(短句1):**\n 在您上次检查中,我们发现了您有缺失牙的情况。\n\n* **小节2 - 健康提醒(短句2):**\n 如果缺失牙问题不及时处理,时间长了可能会出现邻牙倾斜、对牙伸长这些情况。\n\n* **小节3 - 个人化关怀(短句3):**\n 您现在33岁,正是身体状态好的时候,及时修复不仅能恢复正常的吃饭功能,对个人形象和自信心也很有帮助。\n\n* **小节4 - 专业建议(短句4):**\n 所以陈洋洋医生特别叮嘱,建议您要关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n* **小节1 - 复查重要性(短句1):**\n 为了您的口腔健康,建议您方便的时候可以来院复查一下。\n\n* **小节2 - 健康维护(短句2):**\n 这只是一个健康维护的建议,主要是让陈洋洋医生帮您再仔细看看,评估一下现在的情况。\n\n* **小节3 - 检查说明(短句3):**\n 复查检查大概需要【30分钟,了解缺失牙位目前状况】的时间。\n\n* **小节4 - 引导预约(短句4):**\n 建议可以尽快来看看,陈洋洋医生【时间段1,例如:周三下午】和【时间段2,例如:周五上午】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n* **预约成功:**\n 好的,王先生,那我们【具体预约时间】见,到时候我们会提前给您发提醒信息。那不打扰您了,祝您生活愉快!\n\n* **预约不成功:**\n 好的,没关系。那我下个星期再跟您联系,您也可以先考虑一下。那不打扰您了,祝您生活愉快!\n\n**═══ 附加:赠送礼品(见机行事)═══**\n* 对了,王先生,我们为回访患者准备了一份口腔护理小礼品。\n* 您下次过来检查的时候,可以直接到前台领取。\n* 算是我们对您一直以来支持的一点心意。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "陈洋洋",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0G017332",
"patient_name": "患者9",
"age": 41,
"gender": "女",
"generation_time": "2025-07-29 15:30:42",
"callback_script": "好的,这是根据患者卢艳女士的信息生成的专属回访话术。\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔大丰门诊的回访专员。\n• 陈小华医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:陈小华医生上次还和我提起您呢)\n• 您自从去年12月检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n小节1 - 现状描述(短句1):\n在您上次检查中,我们发现了缺失牙的情况。\n\n小节2 - 健康提醒(短句2):\n如果缺失牙问题不及时处理,可能会出现的如邻牙倾斜、对牙伸长情况。\n\n小节3 - 个人化关怀(短句3):\n像您这个年龄,及时修复不仅能恢复咀嚼,对维持您的个人形象也很重要。\n\n小节4 - 专业建议(短句4):\n陈小华医生建议您关注一下这个问题。\n\n**═══ 第三部分:复查建议 ═══**\n小节1 - 复查重要性(短句1):\n为了您的口腔健康,建议来院复查一下。\n\n小节2 - 健康维护(短句2):\n这是健康维护的建议,让陈小华医生帮您再仔细看看。\n\n小节3 - 检查说明(短句3):\n复查检查大概需要30分钟的时间。\n\n小节4 - 引导预约(短句4):\n建议尽快来看看,陈小华医生周三上午和周五下午这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2024-12-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔大丰门诊",
"last_doctor": "陈小华",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0I019780",
"patient_name": "患者10",
"age": 5,
"gender": "女",
"generation_time": "2025-07-29 15:31:21",
"callback_script": "好的,将为您生成一份针对邵依伊小朋友的专业、温馨的回访话术。\n\n---\n\n好的,这是根据患者 **邵依伊**(5岁,女)的信息,为您生成的专属回访话术:\n\n【回访话术】\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔红豆门诊的回访专员。\n• 许海建医生特意交代我来关注宝宝的后续情况。\n• (如果是熟悉患者可说:许海建医生上次还和我提起宝宝呢)\n• 宝宝自从4月20号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知牙齿问题-健康提醒 ═══**\n• 在上次检查中,我们发现宝宝未来恒牙萌出的空间可能不太足够。\n• 这个问题需要我们家长提前关注起来。\n• 如果现在不进行早期干预,将来新换的恒牙可能会因为没有足够的位置而长得不整齐。\n• 这不仅会影响美观和咬合,以后矫正起来也会更复杂。\n• 所以许医生建议,需要通过早期矫正来引导,为恒牙的整齐萌出创造好位置。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请许海建医生再仔细看一下宝宝的牙齿发育情况。\n\n**═══ 第四部分:引导预约 ═══**\n• 建议尽快带宝宝过来看看。\n• 许海建医生**周三下午**和**周六上午**这两个时间段有空(可根据医生实际排班调整)。\n• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。\n\n**═══ 第五部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要多观察孩子的牙齿情况。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访的小朋友准备了小礼品。\n• 到时候来检查可以领取。\n• 宝宝一定会喜欢的。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-04-20 00:00:00",
"last_clinic": "江苏瑞泰通善口腔红豆门诊",
"last_doctor": "许海建",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 15:30:42",
"total_count": 10,
"success_count": 10,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "患者1",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 15:25:45",
"callback_script": "好的,这就为您生成回访话术。\n\n---\n**逻辑判断:**\n1. **年龄判断**: 患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**: 患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行。\n\n**回访话术生成如下:**\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)\n• 您自从去年6月检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,孙吉卿医生注意到您有缺牙的情况。\n• 时间久了,旁边的牙齿可能会发生倾斜,影响到正常的咬合。\n• 趁现在早点处理,不仅能恢复正常的咀嚼,也能保护好旁边的健康牙齿。\n• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下,最好别拖太久。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "患者2",
"age": 52,
"gender": "女",
"generation_time": "2025-07-31 15:26:11",
"callback_script": "好的,这是根据您提供的患者信息和话术模板生成的回访内容。\n\n---\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 胡航医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 时间久了,可能会影响咬合,吃东西也不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好。\n• 这个情况,胡航医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n[根据预约情况选择]\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n---\n**(附加选项,根据沟通氛围决定是否使用)**\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "患者3",
"age": 66,
"gender": "女",
"generation_time": "2025-07-31 15:26:39",
"callback_script": "好的,这就为您生成回访话术。\n\n**分析过程:**\n1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:\n * 患者漏诊项为:`缺失牙`。\n * 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次沟通将**唯一且仅**围绕“缺失牙”进行。\n3. **模板匹配**:严格按照“成人漏诊话术模板”的4个模块结构和要求生成内容。\n4. **信息提取**:\n * 诊所: 江苏瑞泰通善口腔学前街医院\n * 最后一次就诊医生: 王程文\n * 漏诊项: 缺失牙\n * 最后一次就诊时间: 2025-06-30 (智能时间显示为“6月30号”)\n * 复查时长: 缺失牙对应“30分钟”\n\n---\n\n以下是为您生成的专属回访话术:\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 王程文医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 这个问题如果一直拖着,可能会影响到旁边的好牙,造成咬合紊乱,吃东西也不太舒服。\n• 趁现在早点处理,不光能恢复咀嚼,对晚年生活质量也是个保障,以后也省心。\n• 这个情况,王程文医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让王程文医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "患者4",
"age": 38,
"gender": "女",
"generation_time": "2025-07-31 15:27:07",
"callback_script": "好的,这是根据您提供的患者信息和话术模板生成的回访内容。\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 沈佳丽医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不算严重,早点关注和稳住,比以后复杂了更省心。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让沈佳丽医生帮您再仔细看看。\n• 复查检查大概需要30-45分钟的时间。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n[根据预约情况选择]\n\n**如果预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**如果预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "患者5",
"age": 64,
"gender": "男",
"generation_time": "2025-07-31 15:27:49",
"callback_script": "好的,这就为您生成回访话术。\n\n分析过程如下:\n1. **年龄判断**:患者陆志毅先生64岁,属于“≥13岁”的范畴,因此执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者的漏诊项为“缺失牙”,根据优先级“恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收”,本次沟通将只围绕“缺失牙”这一个项目展开。\n3. **模板与数据匹配**:将患者信息(姓名:陆志毅,医生:蒋亚萍,诊所:江苏瑞泰通善口腔学前街医院,漏诊项:缺失牙 等)及话术要点(风险:邻牙倾斜、影响咬合;优势:保护邻牙、维持健康)代入“成人漏-诊话术模板”的四个模块中。\n4. **话术生成**:严格按照四模块结构和语言要求,生成最终话术。\n\n***\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:蒋亚萍医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 这个问题如果放着不管,时间久了可能会影响到旁边的牙齿,造成咬合不太舒服。\n• 趁现在牙槽骨条件还不错,早点关注和处理,对维持口腔整体的健康会更好。\n• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,希望您别忽略了。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看现在的情况。\n• 复查检查大概需要30分钟的时间。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008309",
"patient_name": "患者6",
"age": 41,
"gender": "女",
"generation_time": "2025-07-31 15:28:28",
"callback_script": "好的,这就为您生成针对患者温佼丹女士的专业回访话术。\n\n---\n\n**逻辑判断过程:**\n1. **年龄判断**:患者41岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级规则(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕“缺失牙”**进行,完全忽略牙槽骨吸收。\n3. **数据完整性检查**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 陈洋洋医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。\n• 时间久了,可能会影响到旁边牙齿的健康,吃东西也不太方便。\n• 趁现在早点处理,不仅能保护好旁边的牙齿,对恢复正常的咬合功能也很有帮助。\n• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让陈洋洋医生帮您再仔细看看。\n• 复查检查大概需要30分钟的时间,主要是了解一下缺失牙位置目前的情况。\n• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "陈洋洋",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M006433",
"patient_name": "患者7",
"age": 81,
"gender": "男",
"generation_time": "2025-07-31 15:28:58",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项判断**:漏诊项为【缺失牙】,优先级为3。无更高优先级项目。本次话术将围绕【缺失牙】进行。\n3. **信息完整性**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n以下是为您生成的专属回访话术:\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)\n• 您自从6月29号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,孙红胜医生注意到您有一颗【缺失牙】的情况。\n• 时间久了,旁边的牙齿可能会发生倾斜,影响到您正常的咬合。\n• 趁现在早点处理,也能更好地保护旁边的健康牙齿,对您晚年的生活质量也是个保障。\n• 孙红胜医生也特别嘱咐我们提醒您一下,这个问题最好不要忽略了。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K021609",
"patient_name": "患者8",
"age": 29,
"gender": "女",
"generation_time": "2025-07-31 15:29:36",
"callback_script": "好的,这就为您生成回访话术。\n\n分析结论:\n1. **年龄判断**:患者29岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项判断**:患者存在多个漏诊项,根据优先级 “缺失牙 > 牙槽骨吸收”,本次应处理的最高优先级漏诊项为 **牙槽骨吸收**。\n3. **话术生成**:将严格按照“成人漏诊话术模板”的4模块结构,并围绕“牙槽骨吸收”这一核心问题生成内容。\n\n---\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)\n• 您自从去年6月检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点关注,把牙槽骨稳住会更好。\n• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查大概需要30-45分钟的时间,医生需要仔细检查下您的牙周健康状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K090227",
"patient_name": "患者9",
"age": 34,
"gender": "女",
"generation_time": "2025-07-31 15:30:04",
"callback_script": "好的,这是根据您提供的患者信息和严格的话术模板生成的回访内容。\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 牟苏渝医生特意交代我来关注您的后续情况。\n• 您自从6月29号过来拆完线之后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让牟苏渝医生帮您再仔细看看。\n• 复查检查大概需要【复查检查约30-45分钟,需要仔细检查牙周健康状况】的时间。\n• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "牟苏渝",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0B010384",
"patient_name": "患者10",
"age": 60,
"gender": "女",
"generation_time": "2025-07-31 15:30:42",
"callback_script": "好的,这就为您生成回访话术。\n\n---\n\n**逻辑判断:**\n1. **年龄判断**:患者朱慧娟,60岁,满足“≥13岁”条件,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:漏诊项目中包含“牙槽骨吸收”,无更高优先级的“恒牙萌出空间不足”或“缺失牙”。因此,本次话术将只围绕“牙槽骨吸收”展开。\n3. **模板适用性**:确认使用4模块结构的“成人漏诊话术模板”。\n\n**回访话术详情:**\n\n**═══ 第一部分:开场白 ═══**\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 刘颖聪医生特意交代我来关注您的后续情况。\n• 您自从6月29号检查后,口腔情况怎么样?\n\n**═══ 第二部分:告知漏诊项目 ═══**\n• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不算严重,早点关注和稳住,对您晚年的生活质量也是个保障。\n• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。\n\n**═══ 第三部分:复查建议 ═══**\n• 建议您方便的时候来院复查一下。\n• 让刘颖聪医生帮您再仔细看看。\n• 复查检查大概需要30-45分钟的时间。\n• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n**═══ 第四部分:结束回访语 ═══**\n\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n**═══ 附加:赠送礼品(见机行事)═══**\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:04:58",
"total_count": 5,
"success_count": 5,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "患者1",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 16:02:59",
"callback_script": "好的,将为您生成一份针对患者迟鹏领先生的专业回访话术。\n\n---\n**逻辑判断过程:**\n1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次回访将**仅针对“缺失牙”**进行沟通。\n3. **话术模板确认**:应用“成人漏诊话术模板”,并填充患者信息。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 其实早一点处理,比以后问题变得复杂时要省事也省心。\n• 孙吉卿医生说,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "患者2",
"age": 52,
"gender": "女",
"generation_time": "2025-07-31 16:03:30",
"callback_script": "好的,这就为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行。\n3. **信息完整性判断**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 胡航医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保护好旁边的牙齿。\n• 这个情况,胡航医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "患者3",
"age": 66,
"gender": "女",
"generation_time": "2025-07-31 16:04:05",
"callback_script": "好的,请看根据您提供的患者信息和严格的话术模板生成的回访内容。\n\n**逻辑判断:**\n* **年龄判断:** 患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”。\n* **漏诊项优先级判断:** 漏诊项为“缺失牙”,优先级为③,符合处理要求。\n* **模板执行:** 严格按照“成人漏诊话术模板”的4模块结构生成。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,杜女士,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 王程文医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对您吃东西也会有影响。\n• 趁现在早点处理,不光能保护好旁边的牙齿,对您恢复正常的咀嚼功能也特别重要。\n• 这个情况,王程文医生也特别嘱咐我们提醒您一下,希望您能重视起来。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让王程文医生帮您再仔细看看您缺牙位置的现状。\n• 复查检查大概需要【30分钟】的时间。\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快!\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快!\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "患者4",
"age": 38,
"gender": "女",
"generation_time": "2025-07-31 16:04:33",
"callback_script": "好的,这就为您生成回访话术。\n\n---\n\n已根据您的要求进行逻辑判断:\n* **年龄判断**:患者38岁,≥13岁,执行“成人漏诊话术模板”。\n* **漏诊项判断**:漏诊项为“牙槽骨吸收”,为当前最高优先级项目。\n* **模板确认**:将严格按照“成人漏诊话术模板”的4模块结构生成。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 沈佳丽医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让沈佳丽医生帮您再仔细看看。\n• 复查检查大概需要【30-45分钟】的时间。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "患者5",
"age": 64,
"gender": "男",
"generation_time": "2025-07-31 16:04:58",
"callback_script": "好的,这就为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项判断**:漏诊项为“缺失牙”,优先级为3。无更高优先级项目。将围绕“缺失牙”生成话术。\n3. **模板确认**:将严格按照“成人漏诊话术模板”的4模块结构(开场白、告知漏诊项目、复查建议、结束回访语)生成。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在早点关注,及时处理能更好地保护旁边的牙齿,对将来的生活质量也有好处。\n• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看。\n• 复查检查大概需要【30分钟】的时间。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:10:56",
"total_count": 1,
"success_count": 1,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 16:10:56",
"callback_script": "好的,正在为您生成回访话术...\n\n**逻辑判断与流程选择:**\n1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两项漏诊。“缺失牙”(优先级③)高于“牙槽骨吸收”(优先级④)。\n3. **最终方案**:本次话术将严格按照“成人漏诊话术模板”(4模块结构),并仅针对“缺失牙”这一项进行回访。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也能更好地恢复咀嚼功能。\n• 孙吉卿医生说,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 复查检查大概需要【30分钟左右】的时间,主要是了解一下您缺牙位置目前的情况。\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:24:11",
"total_count": 1,
"success_count": 1,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 16:24:11",
"callback_script": "好的,已收到您的指令。正在根据患者信息和漏诊项优先级进行逻辑判断。\n\n* **年龄判断**: 患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。\n* **漏诊项优先级判断**: 患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”项目**进行,忽略“牙槽骨吸收”。\n* **模板确认**: 使用“成人漏诊话术模板”(4模块结构)。\n\n正在生成话术,请稍候。\n\n---\n\n═══ 第一部分:开场白 ═══\n* 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是迟先生吗?\n* 孙吉卿医生特意交代我来关注您的后续情况。\n* (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)\n* 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n* 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n* 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间久了还会影响吃东西。\n* 其实早一点处理,比以后问题变得复杂了要省事省心很多。\n* 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n* 建议您方便的时候来院复查一下。\n* 让孙吉卿医生帮您再仔细看看,了解一下缺牙位置目前的状况。\n* 复查检查大概需要【30分钟】的时间。\n* 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n* 好的,那我们【具体预约时间】见。\n* 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n* 好的,那我下个星期再跟您联系。\n* 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:36:02",
"total_count": 13,
"success_count": 13,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 16:29:16",
"callback_script": "好的,将为您生成回访话术。\n\n**逻辑判断过程:**\n1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕“缺失牙”**进行。\n3. **模板应用**:严格按照“成人漏诊话术模板”的4模块结构生成内容。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,迟先生,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)\n• 您自从去年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保护好旁边的牙齿。\n• 上次孙吉卿医生也提到,这个问题最好别拖太久。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "钱明艳",
"age": 52,
"gender": "女",
"generation_time": "2025-07-31 16:29:48",
"callback_script": "好的,正在为您生成回访话术。\n\n分析:\n1. **年龄判断**:患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序“缺失牙 > 牙槽骨吸收”,本次话术将**仅针对“缺失牙”**进行回访。\n3. **模板选择**:应用“成人漏诊话术模板”(4模块结构)。\n4. **数据填充**:将患者信息和漏诊项要点填入模板。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,钱女士,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 胡航医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了吃东西也不太舒服。\n• 趁现在早点处理,不仅能保护好旁边的牙齿,对恢复咀嚼功能也很有帮助。\n• 这个情况,胡航医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "杜秋萍",
"age": 66,
"gender": "女",
"generation_time": "2025-07-31 16:30:24",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n**逻辑判断过程:**\n\n1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:\n * 患者漏诊项为:`缺失牙`。\n * 优先级排序为:恒牙萌出空间不足 > **缺失牙** > 牙槽骨吸收。\n * 本次话术将**仅针对“缺失牙”**进行。\n3. **信息完整性判断**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整。\n4. **模板选择**:确定使用“成人漏诊话术模板”(4模块结构)。\n\n正在生成话术...\n\n***\n\n### **患者【杜秋萍】- 漏诊项【缺失牙】回访话术**\n\n═══ 第一部分:开场白 ═══\n* 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是杜女士本人吗?\n* 王程文医生特意交代我来关注您的后续情况。\n* (如果是熟悉患者可说:王程文医生上次还和我提起您呢)\n* 您自从去年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n* 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n* 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n* 趁现在早点处理,对保护好旁边的牙齿有帮助,也能让您以后吃东西更方便,对生活质量很重要的。\n* 王程文医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n* 建议您方便的时候来院复查一下。\n* 让王程文医生帮您再仔细看看,了解一下缺牙位置目前的状况。\n* 复查检查大概需要【30分钟】的时间。\n* 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n* **预约成功:**\n 好的,那我们【具体预约时间】见。那不打扰您了,祝您生活愉快!\n* **预约不成功:**\n 好的,那我下个星期再跟您联系。那不打扰您了,祝您生活愉快!",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "周蓉",
"age": 38,
"gender": "女",
"generation_time": "2025-07-31 16:30:51",
"callback_script": "好的,这就为您生成回访话术。\n\n---\n经分析,该患者年龄为38岁(≥13岁),将执行“成人漏诊话术模板”。\n最高优先级漏诊项为【牙槽骨吸收】,将围绕此项生成话术。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是周女士吗?\n• 沈佳丽医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:沈佳丽医生上次还和我提起您呢)\n• 您自从6月30号补牙后,口腔情况怎么样?之前补的牙齿用着还习惯吗?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响吃东西。\n• 趁现在问题还不严重,早点关注,稳住现状,比以后复杂了再处理要省心很多。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,希望您能重视起来。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让沈佳丽医生帮您再仔细看看,评估一下牙槽骨的现状。\n• 复查检查大概需要【30-45分钟】的时间。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n---\n**【话术自查完毕】**\n- [✅] **模块完整性**:已包含开场白、告知漏诊项目、复查建议、结束回访语,共4个模块。\n- [✅] **内容要素**:已体现医生关怀、分段告知漏诊项、分段提出复查建议并主动引导预约。\n- [✅] **语调风格**:语调温馨、专业,无销售感。\n- [✅] **禁止事项**:未涉及费用、具体治疗方案等禁止内容。\n- [✅] **逻辑判断**:已根据年龄(38岁)和漏诊项优先级(牙槽骨吸收)选择正确模板和重点。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "陆志毅",
"age": 64,
"gender": "男",
"generation_time": "2025-07-31 16:31:21",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n---\n**逻辑判断过程:**\n\n1. **年龄判断**:患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:\n * 患者漏诊信息包含:`缺失牙`。\n * 根据优先级排序 `恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收`,当前最高优先级的漏诊项为 **`缺失牙`**。\n3. **模板选择**:已确定使用“成人漏诊话术模板”(4模块结构)。\n4. **数据完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均已提供,可以生成合规话术。\n\n---\n以下是为您生成的专业回访话术:\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陆先生吗?\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:蒋亚萍医生上次还和我提起您呢)\n• 您自从去年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。\n• 趁现在早点处理,对恢复咀嚼功能、保护好旁边的牙齿都有帮助。\n• 蒋亚萍医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看。\n• 复查检查大概需要30分钟的时间,主要是了解您缺失牙位置目前的状况。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008309",
"patient_name": "温佼丹",
"age": 41,
"gender": "女",
"generation_time": "2025-07-31 16:31:45",
"callback_script": "好的,这就为您生成回访话术。\n\n分析过程如下:\n1. **年龄判断**:患者年龄为41岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”进行沟通**,完全忽略“牙槽骨吸收”。\n3. **模板选择**:使用包含“开场白”、“告知漏诊项目”、“复查建议”、“结束回访语”的四模块成人模板。\n4. **信息填充**:将患者信息(温女士、陈洋洋医生、江苏瑞泰通善口腔学前街医院等)和漏诊项要点填入模板。\n\n---\n\n### **专业回访话术**\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是温佼丹,温女士吗?\n• 陈洋洋医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)\n• 您自从6月30号过来检查之后,口腔情况怎么样?上次做的牙冠用着还习惯吗?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。\n• 医生提到,缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对牙也可能伸长,影响咬合。\n• 趁现在牙槽骨条件还不错,早点处理效果会更好,也避免将来多花功夫。\n• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,希望您能重视起来。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让陈洋洋医生帮您再仔细看看缺失牙部位的现状。\n• 复查检查大概需要【30分钟】的时间。\n• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,温女士,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快!\n\n**预约不成功:**\n• 好的,没关系。那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快!",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "陈洋洋",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M006433",
"patient_name": "华国梁",
"age": 81,
"gender": "男",
"generation_time": "2025-07-31 16:32:34",
"callback_script": "好的,收到指令。正在为您生成回访话术。\n\n分析过程如下:\n1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:漏诊项为“缺失牙”,无更高优先级项目,因此本次沟通将围绕“缺失牙”进行。\n3. **信息完整性检查**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。\n4. **模板应用**:严格按照“成人漏诊话术模板”的4模块结构和要求生成内容。\n\n---\n\n以下是为您生成的专属回访话术:\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是华国梁华先生吗?\n• 孙红胜医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)\n• 您自从去年6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有一颗缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西的咬合。\n• 趁现在早点关注起来,把这个问题处理好,对您之后吃东西、保持口腔健康都更好。\n• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查大概需要30分钟的时间,主要是了解一下这颗缺牙的现状。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K021609",
"patient_name": "周韵ZZ00906",
"age": 29,
"gender": "女",
"generation_time": "2025-07-31 16:33:13",
"callback_script": "好的,这就为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为29岁,大于13岁,执行【成人漏诊话术模板】(4模块结构)。\n2. **漏诊项优先级判断**:\n * 患者漏诊项为:`牙槽骨吸收`。\n * 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),当前最高优先级的漏诊项为【牙槽骨吸收】。\n3. **信息完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均完整。\n4. **话术生成**:开始生成4模块结构的成人漏诊话术。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,周女士,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)\n• 您自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点关注,把牙槽骨稳住会更好。\n• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查大概需要30-45分钟的时间。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K090227",
"patient_name": "裴洋帆(198会员)",
"age": 34,
"gender": "女",
"generation_time": "2025-07-31 16:33:47",
"callback_script": "好的,这就为您生成回访话术。\n\n分析完毕,患者年龄为34岁(≥13岁),将执行“成人漏诊话术模板”。\n最高优先级漏诊项为“牙槽骨吸收”,将围绕此项生成话术。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是裴女士吗?\n• 牟苏渝医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:牟苏渝医生上次还和我提起您呢)\n• 您自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让牟苏渝医生帮您再仔细看看。\n• 复查检查大概需要30-45分钟的时间,需要仔细检查牙周健康状况。\n• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "牟苏渝",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0B010384",
"patient_name": "朱慧娟",
"age": 60,
"gender": "女",
"generation_time": "2025-07-31 16:34:20",
"callback_script": "好的,收到您的指令。正在为您生成针对患者【朱慧娟】的回访话术。\n\n- **年龄判断**:患者60岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。\n- **漏诊项优先级判断**:漏诊项为【牙槽骨吸收】,是当前最高优先级的项目。\n- **模板选择**:确认使用“成人漏诊话术模板”。\n\n话术生成如下:\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,朱女士,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 刘颖聪医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:刘颖聪医生上次还和我提起您呢)\n• 您自从来我们这边检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议您方便的时候来院复查一下。\n• 让刘颖聪医生帮您再仔细看看。\n• 复查检查大概需要【30-45分钟,需要仔细检查牙周健康状况】的时间。\n• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008628",
"patient_name": "陈柯翰",
"age": 7,
"gender": "男",
"generation_time": "2025-07-31 16:34:55",
"callback_script": "好的,这就为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者【陈柯翰】年龄为7岁,小于等于12岁,执行“儿童早矫话术模板”(5模块结构)。\n2. **漏诊项判断**:漏诊项为【恒牙萌出空间不足】,优先级为①,符合处理条件。\n3. **信息完整性**:关键字段(年龄、姓名、医生、诊所、漏诊项)完整。\n\n开始生成话术:\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陈柯翰的家长吗?\n• 孙瑞红医生特意交代我来关注宝宝的后续情况。\n• (如果是熟悉患者可说:孙瑞红医生上次还和我提起宝宝呢)\n• 宝宝自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n小节1 - 现状描述(短句1):\n上次来检查的时候,孙瑞红医生注意到宝宝的恒牙生长空间有点紧张。\n\n小节2 - 位置说明(短句2):\n这个阶段是宝宝换牙的关键期,空间不足可能会影响新牙的排列。\n\n小节3 - 不治疗危害(短句3):\n如果不去管它,将来新长出来的恒牙就容易长得歪歪扭扭,或者互相挤在一起。\n\n小节4 - 后果说明(短句4):\n这样不仅会影响美观,对以后吃东西也可能会有影响。\n\n小节5 - 解决方案(短句5):\n所以医生很关注这个问题,认为现在是引导牙齿正常生长的好时机。\n\n═══ 第三部分:复查建议 ═══\n小节1 - 复查时间(短句1):\n建议3个月左右带宝宝来院检查。\n\n小节2 - 检查内容(短句2):\n一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n\n小节3 - 预防措施(短句3):\n还要看看要不要给宝宝涂氟保护牙齿。\n\n小节4 - 专业建议(短句4):\n也请孙瑞红医生再仔细看一下宝宝的情况。\n\n═══ 第四部分:引导预约 ═══\n• 建议尽快带宝宝过来看看。\n• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空。\n• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。\n\n═══ 第五部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要观察孩子的牙齿情况。\n• 那不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访的小朋友准备了小礼品。\n• 到时候来检查可以领取。\n• 宝宝一定会喜欢的。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙瑞红",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008642",
"patient_name": "陶俊仪",
"age": 4,
"gender": "男",
"generation_time": "2025-07-31 16:35:28",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为4岁,属于≤12岁范畴。\n2. **模板选择**:执行“儿童早矫话术模板”(5模块结构)。\n3. **漏诊项优先级判断**:患者存在“恒牙萌出空间不足”和“缺失牙”两个漏诊项。根据优先级排序,“恒牙萌出空间不足”为最高优先级,本次话术将围绕此项展开。\n\n话术生成如下:\n***\n\n═══ 第一部分:开场白 ═══\n• 您好,陶俊仪的家长,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 张建奎医生特意交代我来关注宝宝的后续情况。\n• (如果是熟悉患者可说:张建奎医生上次还和我提起宝宝呢)\n• 宝宝自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n• 上次检查时,张建奎医生就注意到,现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。\n• 这颗牙的位置在左上前门牙那边。\n• 如果咱们不做处理,这个缺牙留下的空间和位置很可能会慢慢变小。\n• 这样的话,将来恒牙萌出就不会在它该在的位置,容易长歪。\n• 所以医生当时提到,可以做一个叫“间隙保持器”的小装置来先维持住这个空间。\n\n═══ 第三部分:复查建议 ═══\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请张建奎医生再仔细看一下宝宝牙齿和间隙的详细情况。\n\n═══ 第四部分:引导预约 ═══\n• 建议尽快带宝宝过来看看。\n• 张建奎医生【时间段1】和【时间段2】这两个时间段有空。\n• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。\n\n═══ 第五部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要多观察孩子的牙齿情况。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "张建奎",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008644",
"patient_name": "陶威",
"age": 38,
"gender": "男",
"generation_time": "2025-07-31 16:36:02",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n**逻辑判断:**\n1. **年龄判断**:患者年龄为38岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:\n * 患者漏诊项为:`牙槽骨吸收`。\n * 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次通话将只处理“牙槽骨吸收”这一项。\n3. **信息完整性检查**:关键字段(年龄、姓名、医生、诊所、漏诊项)均完整。\n\n开始生成话术...\n\n***\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陶威陶先生吗?\n• 刘颖聪医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:刘颖聪医生上次还和我提起您呢)\n• 您自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n1. 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。\n2. 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响吃东西。\n3. 趁现在问题还不严重,早一点关注和稳住,比以后问题复杂了要省心很多。\n4. 这个情况,刘颖聪医生也特别嘱咐我们,一定要提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n1. 建议您方便的时候可以来院复查一下。\n2. 让刘颖聪医生帮您再仔细看看目前牙槽骨的具体情况。\n3. 复查检查大概需要【复查检查约30-45分钟,需要仔细检查牙周健康状况】的时间。\n4. 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 17:07:01",
"total_count": 12,
"success_count": 12,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-07-31 17:01:05",
"callback_script": "好的,这就为您生成回访话术。\n\n---\n**逻辑判断与话术选择:**\n\n1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行,忽略“牙槽骨吸收”。\n3. **模板选择**:已确定使用“成人漏诊话术模板”(4模块结构)。\n\n---\n**回访话术生成如下:**\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 迟先生您好。\n• 孙吉卿医生特意交代我来关注您后续的情况。\n• 您自从去年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。\n• 其实早一点处理,比以后复杂时省事也省心。\n• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "钱明艳",
"age": 52,
"gender": "女",
"generation_time": "2025-07-31 17:01:34",
"callback_script": "好的,正在为您生成回访话术...\n\n**逻辑判断:**\n1. **年龄判断**: 患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:\n * 漏诊项包含:【缺失牙】、【牙槽骨吸收】。\n * 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次话术将优先且仅处理【缺失牙】项目。\n3. **信息完整性检查**: 关键字段(年龄、漏诊项、医生、诊所)完整,可以生成话术。\n\n---\n\n### **回访话术**\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是钱女士吗?\n• 胡航医生特意交代我来关注您的后续情况。\n• 您自从6月30号来我们这里检查后,最近口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在牙齿的整体条件还不错,早一点处理,比以后复杂时省事也省心。\n• 胡航医生说,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**[预约成功]**\n• 好的,钱女士,那我们【具体预约时间】见。\n• 不打扰您了,祝您生活愉快!\n\n**[预约不成功]**\n• 好的,没关系,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快!",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "杜秋萍",
"age": 66,
"gender": "女",
"generation_time": "2025-07-31 17:02:05",
"callback_script": "好的,收到您的指令。正在为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。\n2. **漏诊项优先级判断**:漏诊项为【缺失牙】。根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次将只处理【缺失牙】项目。\n3. **信息完整性检查**:关键字段(年龄、姓名、医生、诊所、漏诊项)均存在,可以生成话术。\n\n话术生成如下:\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 请问是杜女士吗?\n• 王程文医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长吃东西也不太舒服。\n• 早一点修复,也能更好地保护旁边的健康牙齿,对维持晚年生活质量很有帮助。\n• 这个情况,王程文医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让王程文医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "周蓉",
"age": 38,
"gender": "女",
"generation_time": "2025-07-31 17:02:41",
"callback_script": "好的,这就为您生成回访话术。\n\n分析判断:\n1. **年龄判断**:患者年龄为38岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:\n * 患者漏诊项为:`牙槽骨吸收`。\n * 优先级排序为:恒牙萌出空间不足 > 缺失牙 > **牙槽骨吸收**。\n * 本次话术将围绕唯一的漏诊项“牙槽骨吸收”展开。\n3. **信息完整性判断**:关键信息(年龄、姓名、医生、诊所、漏诊项)完整,可以生成话术。\n\n---\n\n### **回访话术**\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 周女士,您好。\n• 沈佳丽医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,时间久了吃东西也不太舒服。\n• 趁现在问题还不严重,早点关注,及时稳住牙槽骨,比以后问题复杂了再处理要省心很多。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,希望您能重视起来。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让沈佳丽医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "陆志毅",
"age": 64,
"gender": "男",
"generation_time": "2025-07-31 17:03:14",
"callback_script": "好的,这就为您生成回访话术。\n\n**逻辑判断:**\n1. **年龄判断**: 患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项判断**: 漏诊项为“缺失牙”,优先级为③。无更高优先级项目,因此处理此项。\n3. **模板适用性**: 确认使用4模块结构的成人模板。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陆先生吗?\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了吃东西也不太方便。\n• 趁现在早点处理,不光能保护好旁边的牙,也能让晚年生活吃东西更舒心。\n• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008309",
"patient_name": "温佼丹",
"age": 41,
"gender": "女",
"generation_time": "2025-07-31 17:03:52",
"callback_script": "好的,遵照您的指令,现在为您生成回访话术。\n\n---\n**逻辑判断过程:**\n1. **年龄判断**:患者年龄为41岁,≥13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行关怀提醒。\n3. **模板适用性确认**:确认使用4模块结构的“成人漏诊话术模板”,并填充所有必需信息。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是温女士吗?\n• 陈洋洋医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间久了还会影响咬合。\n• 趁现在牙齿的整体状况还不错,早一点处理,比以后问题复杂了再解决要省心很多。\n• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,希望您能重视起来。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让陈洋洋医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**[预约成功]**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**[预约不成功]**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "陈洋洋",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M006433",
"patient_name": "华国梁",
"age": 81,
"gender": "男",
"generation_time": "2025-07-31 17:04:18",
"callback_script": "好的,这就为您生成回访话术。\n\n**逻辑判断过程:**\n1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。\n2. **漏诊项优先级判断**:漏诊项为“缺失牙”,优先级为3。无更高优先级项目。本次话术将围绕“缺失牙”展开。\n3. **信息完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均完整。\n4. **模板选择**:应用“成人漏诊话术模板”(4模块结构)。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 请问是华先生吗?\n• 孙红胜医生特意交代我来关注您的后续情况。\n• 您自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长吃东西也不太舒服。\n• 趁现在早点处理,能更好地保护旁边的健康牙齿,对咱们现在的生活质量也特别重要。\n• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K021609",
"patient_name": "周韵ZZ00906",
"age": 29,
"gender": "女",
"generation_time": "2025-07-31 17:04:47",
"callback_script": "好的,正在为您生成回访话术...\n\n**逻辑判断:**\n* **年龄判断:** 患者年龄为29岁,大于13岁,执行“成人漏诊话术模板”。\n* **漏诊项优先级判断:** 漏诊项为“牙槽骨吸收”,是当前唯一需要处理的项目。\n* **模板确认:** 将严格按照“成人漏诊话术模板”的4模块结构生成。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 周女士您好。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 其实早一点处理,比以后复杂时省事也省心。\n• 孙红胜医生说,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K090227",
"patient_name": "裴洋帆(198会员)",
"age": 34,
"gender": "女",
"generation_time": "2025-07-31 17:05:19",
"callback_script": "好的,正在为您生成回访话术...\n\n分析完毕。患者年龄为34岁,适用“成人漏诊话术模板”。漏诊项为“牙槽骨吸收”,是唯一需要处理的项目。将严格按照4模块结构生成话术。\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,裴女士,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 牟苏渝医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:牟苏渝医生上次还和我提起您呢)\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 其实早一点处理,比以后复杂时省事也省心。\n• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让牟苏渝医生帮您再仔细看看。\n• 【复查检查约30-45分钟,需要仔细检查牙周健康状况】\n• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。\n\n---\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "牟苏渝",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0B010384",
"patient_name": "朱慧娟",
"age": 60,
"gender": "女",
"generation_time": "2025-07-31 17:05:53",
"callback_script": "好的,已收到您的指令。正在为您生成回访话术...\n\n**逻辑判断结果:**\n* **年龄判断:** 60岁 ≥ 13岁,执行“成人漏诊话术模板”。\n* **漏诊项优先级判断:** 漏诊项为“牙槽骨吸收”,优先级为④。无更高优先级项目。将围绕“牙槽骨吸收”生成话术。\n* **信息完整性检查:** 关键字段(姓名、年龄、医生、诊所、漏诊项)完整。\n\n**回访话术生成如下:**\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 朱女士您好。\n• 刘颖聪医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让刘颖聪医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008628",
"patient_name": "陈柯翰",
"age": 7,
"gender": "男",
"generation_time": "2025-07-31 17:06:28",
"callback_script": "好的,已收到您的指令。正在为您生成针对患者陈柯翰的回访话术。\n\n* **年龄判断:** 患者7岁(≤12岁),启动“儿童早矫话术模板”。\n* **漏诊项判断:** 漏诊项为“恒牙萌出空间不足”,优先级为最高,将围绕此项生成话术。\n* **模板确认:** 执行“儿童早矫话术模板”(5模块结构)。\n\n内容生成中...\n\n---\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 请问是陈柯翰小朋友的家长吗?\n• 孙瑞红医生特意交代我来关注宝宝的后续情况。\n• 宝宝自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n• 上次孙医生检查的时候,注意到宝宝给新牙齿预留的生长空间有点紧张。\n• 这在换牙期其实是挺常见的现象。\n• 如果咱们现在不关注,牙齿空间不足的问题可能会影响新牙。\n• 将来新长出来的恒牙可能会长得不整齐,或者不在它该在的位置上。\n• 现在是进行早期干预的好时期,所以想请医生再帮宝宝仔细看看具体情况。\n\n═══ 第三部分:复查建议 ═══\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请孙瑞红医生再仔细看一下宝宝的情况。\n\n═══ 第四部分:引导预约 ═══\n• 如果方便的话您看最近有没有时间,我帮您预约一下,您带宝宝过来看一看。\n• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第五部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。\n• 好的那就不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访的小朋友准备了小礼品。\n• 到时候来检查可以领取。\n• 宝宝一定会喜欢的。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙瑞红",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008642",
"patient_name": "陶俊仪",
"age": 4,
"gender": "男",
"generation_time": "2025-07-31 17:07:01",
"callback_script": "好的,正在为您生成回访话术...\n\n**逻辑判断结果:**\n* **年龄判断:** 患者年龄为4岁,≤12岁,执行“儿童早矫话术模板”。\n* **漏诊项判断:** 存在“恒牙萌出空间不足”和“缺失牙”两个漏诊项。根据优先级,“恒牙萌出空间不足”为最高优先级,话术将围绕此项展开。\n* **信息完整性:** 关键字段(姓名、年龄、医生、诊所、漏诊项)完整。\n\n---\n\n好的,这是为您生成的专属回访话术:\n\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。\n• 请问是陶俊仪的家长吗?\n• 张建奎医生特意交代我来关注宝宝的后续情况。\n• 宝宝自从6月29号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n• 现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。\n• 这颗乳牙的位置在【左上前牙】。\n• 如果咱们不做处理,这颗乳牙的位置和空间可能会丧失。\n• 将来恒牙萌出就不会在它该在的位置。\n• 所以我们要做一个装置来维持这个间隙,这个装置叫间隙保持器。到时候也请医生看一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请张建奎医生再仔细看一下宝宝的情况。\n\n═══ 第四部分:引导预约 ═══\n• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。\n• 张建奎医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间段比较方便?\n\n═══ 第五部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。\n• 好的那就不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访的小朋友准备了小礼品。\n• 到时候来检查可以领取。\n• 宝宝一定会喜欢的。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "张建奎",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-04 12:08:25",
"total_count": 13,
"success_count": 13,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-08-04 12:00:29",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,迟先生,我是瑞泰通善口腔的回访专员{姓名}。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• 您自从去年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。\n• 其实早一点处理,比以后问题变得复杂时要省事也省心。\n• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "钱明艳",
"age": 52,
"gender": "女",
"generation_time": "2025-08-04 12:00:57",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的回访专员小张。\n• 钱女士,您好。\n• 胡航医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了还容易影响吃东西。\n• 其实早一点处理,比以后问题变得复杂了要省心很多。\n• 这个情况,胡航医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "杜秋萍",
"age": 66,
"gender": "女",
"generation_time": "2025-08-04 12:01:28",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的客户关怀专员[信息缺失]。杜女士您好。\n• 王程文医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)\n• 您自从去年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在早点关注,对保护好旁边的健康牙齿有好处,也能更好地保障咱们晚年的生活质量。\n• 这个情况,王程文医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让王程文医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "周蓉",
"age": 38,
"gender": "女",
"generation_time": "2025-08-04 12:02:08",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 周女士,您好!我是瑞泰通善口腔的回访专员。\n• 沈佳丽医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:沈佳丽医生上次还和我提起您呢)\n• 您自从去年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让沈佳丽医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "陆志毅",
"age": 64,
"gender": "男",
"generation_time": "2025-08-04 12:02:46",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员小王。\n• 陆先生您好。\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响到正常的咬合。\n• 趁现在牙槽骨条件还不错,早一点处理对恢复吃东西的功能很有帮助。\n• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n[预约成功]\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n[预约不成功]\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008309",
"patient_name": "温佼丹",
"age": 41,
"gender": "女",
"generation_time": "2025-08-04 12:03:15",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,温女士,我是瑞泰通善口腔的回访专员小王。\n• 陈洋洋医生特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)\n• 您自从去年6月来检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也比以后复杂了省心。\n• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让陈洋洋医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "陈洋洋",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M006433",
"patient_name": "华国梁",
"age": 81,
"gender": "男",
"generation_time": "2025-08-04 12:03:58",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,华先生,我是瑞泰通善口腔学前街医院的回访专员。\n• 您的主治医生,孙红胜医生,特意交代我来关注您的后续情况。\n• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)\n• 您自从上次来院检查后,口腔情况怎么样?活动假牙用着还习惯吗?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您下排牙齿还有一颗缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在早点关注这个问题,对维持您整体的口腔健康和咀嚼功能是很有帮助的。\n• 所以孙红胜医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看这颗缺牙的情况。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K021609",
"patient_name": "周韵ZZ00906",
"age": 29,
"gender": "女",
"generation_time": "2025-08-04 12:04:29",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 周女士,您好!我是瑞泰通善口腔的回访专员。\n• 孙红胜医生特意交代我们,要打电话来关心一下您的后续情况。\n• 您自从2025年6月29号做完种植手术之后,口腔感觉怎么样?恢复得还好吗?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有一点牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。\n• 趁现在问题还不严重,早点稳住会更好。\n• 所以孙红胜医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K090227",
"patient_name": "裴洋帆(198会员)",
"age": 34,
"gender": "女",
"generation_time": "2025-08-04 12:05:00",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 裴女士,您好,我是瑞泰通善口腔的回访专员小王。\n• 牟苏渝医生特意交代我来关注您的后续情况。\n• (牟苏渝医生上次还和我提起您呢)\n• 您自从2025年6月拆线后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。\n• 时间久了,牙齿容易松动,吃东西会不太舒服,而且牙缝也可能慢慢变大。\n• 趁现在问题还不算严重,早点关注,把牙槽骨稳住,对您长远来说是最好的。\n• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让牟苏渝医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n[预约成功]\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n[预约不成功]\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "牟苏渝",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0B010384",
"patient_name": "朱慧娟",
"age": 60,
"gender": "女",
"generation_time": "2025-08-04 12:05:29",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的回访专员小王。\n• 朱女士您好。\n• 刘颖聪医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。\n• 时间久了,牙齿容易松动,吃东西会不太舒服,而且牙缝也可能慢慢变大。\n• 趁现在问题还不算严重,早点关注和稳住会更好。\n• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下,别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让刘颖聪医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008628",
"patient_name": "陈柯翰",
"age": 7,
"gender": "男",
"generation_time": "2025-08-04 12:07:10",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"恒牙萌出空间不足\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的回访专员{姓名}。\n• 陈柯翰的家长您好。\n• 孙瑞红医生特意交代我来关注宝宝的后续情况。\n• 宝宝自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n• 上次来检查的时候,孙瑞红医生注意到宝宝的牙齿有点拥挤,给新牙齿留的位置可能不太够。\n• 这个情况叫恒牙萌出空间不足。\n• 如果不及时干预,以后新长出来的恒牙可能会长得歪歪扭扭,不整齐。\n• 这样不仅影响美观,以后清洁起来也麻烦,还容易蛀牙呢。\n• 所以孙医生很关心这个问题,说现在是干预的黄金时期,需要及早关注。\n\n═══ 第三部分:复查建议 ═══\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请孙瑞红医生再仔细看一下宝宝的情况。\n\n═══ 第四部分:引导预约 ═══\n• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。\n• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第五部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。\n• 好的那就不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙瑞红",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008642",
"patient_name": "陶俊仪",
"age": 4,
"gender": "男",
"generation_time": "2025-08-04 12:07:47",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"恒牙萌出空间不足\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的回访专员(信息缺失:客服姓名)。\n• 陶俊仪的家长您好。\n• 张建奎医生特意交代我来关注宝宝的后续情况。\n• (如果是熟悉患者可说:张建奎医生上次还和我提起宝宝呢)\n• 宝宝自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知牙齿问题-健康提醒 ═══\n• 现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。\n• 这颗乳牙的位置在【左上门牙】。\n• 如果咱们不做处理,这颗乳牙的位置和空间可能会丧失。\n• 将来恒牙萌出就不会在它该在的位置。\n• 所以我们要做一个装置来维持这个间隙,这个装置叫间隙保持器。到时候也请医生看一下。\n\n═══ 第三部分:复查建议 ═══\n• 建议3个月左右带宝宝来院检查。\n• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。\n• 还要看看要不要给宝宝涂氟保护牙齿。\n• 也请张建奎医生再仔细看一下宝宝的情况。\n\n═══ 第四部分:引导预约 ═══\n• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。\n• 张建奎医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段)。\n\n═══ 第五部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。\n• 好的那就不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "张建奎",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008644",
"patient_name": "陶威",
"age": 38,
"gender": "男",
"generation_time": "2025-08-04 12:08:25",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的回访专员,很高兴联系到您。\n• 陶先生,刘颖聪医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,刘颖聪医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西。\n• 其实早一点处理,比以后问题变得复杂时要省事也省心。\n• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让刘颖聪医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-29 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "刘颖聪",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-05 14:28:01",
"total_count": 3,
"success_count": 3,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-08-05 14:26:55",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士长小王。\n• 迟先生,孙吉卿医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响您吃东西。\n• 趁现在早一点处理,比以后问题变得复杂时要省事也省心。\n• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n[预约成功]\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n[预约不成功]\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "钱明艳",
"age": 52,
"gender": "女",
"generation_time": "2025-08-05 14:27:26",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,钱女士,我是瑞泰通善口腔的护士长小王。\n• 胡航医生特意交代我来关注您的后续情况。\n• 您自从去年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合,吃东西也会不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也避免将来多花功夫。\n• 这个情况,胡航医生也特别嘱咐我们提醒您一下,别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "杜秋萍",
"age": 66,
"gender": "女",
"generation_time": "2025-08-05 14:28:01",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,杜女士,我是瑞泰通善口腔的护士长小张。\n• 王程文医生特意交代我来关注您后续的情况。\n• 您自从去年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保障咱们晚年的生活质量。\n• 王程文医生说,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让王程文医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-05 14:32:22",
"total_count": 5,
"success_count": 5,
"error_count": 0
},
"callbacks": [
{
"patient_id": "TS0K064355",
"patient_name": "迟鹏领",
"age": 54,
"gender": "男",
"generation_time": "2025-08-05 14:30:20",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士长小张。\n• 迟先生,您好。\n• 孙吉卿医生特意交代我来关注您的后续情况。\n• 您自从2025年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿还可能伸长出来。\n• 其实早一点处理,比以后复杂时省事也省心。\n• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙吉卿医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙吉卿",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008666",
"patient_name": "钱明艳",
"age": 52,
"gender": "女",
"generation_time": "2025-08-05 14:30:46",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的医生助理小王。\n• 请问是钱女士吗?胡航医生特意交代我来关注您的后续情况。\n• 您自从6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙也会伸长,影响到正常的咬合。\n• 趁现在牙槽骨条件还不错,早点关注,处理起来效果也会更好。\n• 所以胡航医生也特别嘱咐我们提醒您一下,这个问题早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让胡航医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "胡航",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008652",
"patient_name": "杜秋萍",
"age": 66,
"gender": "女",
"generation_time": "2025-08-05 14:31:23",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,杜女士,我是瑞泰通善口腔的护士长小王。\n• 王程文医生特意交代我来关注您后续的口腔情况。\n• 您自从2025年6月那次检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来,影响咬合。\n• 趁现在牙槽骨条件还不错,早点关注这个问题,不仅能保护好邻牙,吃东西也能更踏实。\n• 所以王程文医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让王程文医生帮您再仔细看看。\n• 【复查检查约30分钟,了解缺失牙位目前状况】\n• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "王程文",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0K065136",
"patient_name": "周蓉",
"age": 38,
"gender": "女",
"generation_time": "2025-08-05 14:31:49",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士长小李。\n• 周女士您好,沈佳丽医生特意交代我来关注您的后续情况。\n• 您自从去年6月30号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响您吃东西。\n• 趁现在问题还不算严重,早点关注,把情况稳住,比以后复杂了再处理要省心很多。\n• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,别忽略了。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让沈佳丽医生帮您再仔细看看,评估一下现状。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "沈佳丽",
"visit_count": 0
},
"callback_type": "success"
},
{
"patient_id": "TS0M008662",
"patient_name": "陆志毅",
"age": 64,
"gender": "男",
"generation_time": "2025-08-05 14:32:22",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,陆先生,我是瑞泰通善口腔的健康助理小王。\n• 蒋亚萍医生特意交代我来关注您的后续情况。\n• 您自从2025年6月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。\n• 趁现在早点关注,及时修复,既能保护邻牙,也能让您以后吃东西更省心。\n• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,早点看看会比较安心。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让蒋亚萍医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"has_missed_diagnosis": false,
"patient_data_summary": {
"last_visit_time": "2025-06-30 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "蒋亚萍",
"visit_count": 0
},
"callback_type": "success"
}
]
}
\ No newline at end of file
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-29 15:31:21
总计处理: 10 个患者
成功生成: 10 个话术
生成失败: 0 个
============================================================
【1】患者: 患者1 (TS0K036558)
年龄: 39岁, 性别: 女
最后就诊: 2024-12-26 00:00:00
就诊医生: 胡婷
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成针对患者周绍懿女士的专业回访话术。
---
### **回访话术**
**═══ 第一部分:开场白 ═══**
您好,周女士,我是江苏瑞泰通善口腔医院的回访专员。
胡婷医生特意交代我来关注您的后续情况。
(如果是熟悉患者可说:胡婷医生上次还和我提起您呢。)
您自从12月26号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
在您上次检查中,我们发现了牙槽骨吸收的情况。
如果牙槽骨吸收问题不及时处理,可能会出现如牙齿松动、牙缝变大等情况。
您现在39岁,正是维护口腔健康的关键时期,及时关注和干预,能更好地稳住牙槽骨,保住自己的牙齿。
胡婷医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
为了您的口腔健康,建议来院复查一下。
这是健康维护的建议,让胡婷医生帮您再仔细看看。
复查检查大概需要【30-45分钟,需要仔细检查牙周健康状况】的时间。
建议尽快来看看,胡婷医生【时间段1】和【时间段2】这两个时间段有空(可根据医生具体排班描述时间段),您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【2】患者: 患者2 (TS0I021277)
年龄: 35岁, 性别: 男
最后就诊: 2025-06-15 00:00:00
就诊医生: 陈淼
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成针对患者周全先生的专业回访话术。
---
### 患者情况分析
1. **年龄判断**: 患者35岁,属于`≥13岁`的范畴,因此执行**“成人漏诊话术模板”**(4模块结构)。
2. **漏诊项优先级判断**:
* 患者存在两个漏诊项:`缺失牙`(优先级③)和 `牙槽骨吸收`(优先级④)。
* 根据优先级排序,**`缺失牙`** 为本次沟通的唯一核心项目,`牙槽骨吸收`将被忽略。
3. **信息完整性**: 关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
---
### 专业回访话术
**═══ 第一部分:开场白 ═══**
* 您好,周先生,我是江苏瑞泰通善口腔红豆门诊的回访专员。
* 陈淼医生特意交代我来关注您的后续情况。
* (如果是熟悉患者可说:陈淼医生上次还和我提起您呢。)
* 您自从6月15号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
* **小节1 - 现状描述(短句1):**
在您上次检查中,我们发现了有几颗缺失牙的情况。
* **小节2 - 健康提醒(短句2):**
如果缺失牙问题不及时处理,可能会出现邻牙倾斜、对牙伸长的情况。
* **小-节3 - 个人化关怀(短句3):**
您现在35岁,正值事业关键期,及时修复不仅能保护好邻牙,恢复正常的咀嚼功能,对个人形象也很有帮助。
* **小节4 - 专业建议(短句4):**
陈淼医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
* **小节1 - 复查重要性(短句1):**
为了您的口腔健康,建议您抽空来院复查一下。
* **小节2 - 健康维护(短句2):**
这是我们给您的健康维护建议,让陈淼医生帮您再仔细看看具体情况。
* **小节3 - 检查说明(短句3):**
复查检查大概需要30分钟的时间,主要是了解一下您缺失牙牙区的现状。
* **小节4 - 引导预约(短句4):**
建议尽快来看看,陈淼医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段),您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
* **预约成功:**
* 好的,那我们【具体预约时间】见。
* 那不打扰您了,祝您生活愉快!
* **预约不成功:**
* 好的,那我下个星期再跟您联系。
* 那不打扰您了,祝您生活愉快!
**═══ 附加:赠送礼品(见机行事)═══**
* 我们为回访患者准备了小礼品。
* 到时候来检查可以领取,表示我们对您的感谢。
============================================================
【3】患者: 患者3 (TS0L003747)
年龄: 61岁, 性别: 男
最后就诊: 2024-12-17 00:00:00
就诊医生: 王国盛
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是根据您提供的患者信息和严格的话术模板生成的回访内容。
**患者年龄**:61岁(≥13岁),执行“成人漏诊话术模板”。
**漏诊项优先级处理**:患者漏诊项为“牙槽骨吸收”,无其他更高优先级项目,将围绕此项生成话术。
***
### **患者【苏衍】专属回访话术**
**═══ 第一部分:开场白 ═══**
* 您好,我是江苏瑞泰通善口腔新吴门诊的回访专员。
* 王国盛医生特意交代我来关注您的后续情况。
* (如果是熟悉患者可说:王国盛医生上次还和我提起您呢)
* 您自从去年12月检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
* 在您上次检查中,我们发现了牙槽骨吸收的情况。
* 如果牙槽骨吸收问题不及时处理,可能会出现的如牙齿松动、脱落风险情况。
* 您这个年纪,虽然牙槽骨吸收比较常见,但现在关注起来,对保住咱们自己的牙齿,提升晚年的生活质量还是很有意义的。
* 王国盛医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
* 为了您的口腔健康,建议来院复查一下。
* 这是健康维护的建议,让王国盛医生帮您再仔细看看。
* 复查检查大概需要【30-45分钟】的时间。
* 建议尽快来看看,王国盛医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
* 好的,那我们【具体预约时间】见。
* 那不打扰您了,祝您生活愉快。
**预约不成功:**
* 好的,那我下个星期再跟您联系。
* 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
* 我们为回访患者准备了小礼品。
* 到时候来检查可以领取。
* 表示我们对您的感谢。
============================================================
【4】患者: 患者4 (TS0K068025)
年龄: 76岁, 性别: 女
最后就诊: 2025-01-09 00:00:00
就诊医生: 赵超
就诊次数: 0次
----------------------------------------
回访话术:
好的,已收到您的指令。正在为您生成针对患者 **徐涵明** 的专业回访话术。
**逻辑判断:**
1. **年龄判断**:患者76岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项判断**:漏诊项为“牙槽骨吸收”,是当前唯一需要处理的漏诊项。
**回访话术如下:**
**═══ 第一部分:开场白 ═══**
• 您好,请问是徐涵明阿姨吗?我是江苏瑞泰通善口腔医院的回访专员。
• 赵超医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:赵超医生上次还和我提起您呢)
• 您自从1月9号检查后,口腔情况怎么样?之前补的牙用着还可以吗?
**═══ 第二部分:告知漏诊项目 ═══**
小节1 - 现状描述(短句1):
在您上次检查中,我们发现了牙槽骨吸收的情况。
小节2 - 健康提醒(短句2):
如果牙槽骨吸收问题不及时处理,可能会出现牙齿松动、甚至脱落的风险。
小节3 - 个人化关怀(短句3):
您这个年纪,保住自己的牙齿、维持好的咀嚼能力对晚年生活质量特别重要。
小节4 - 专业建议(短句4):
赵超医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
小节1 - 复查重要性(短句1):
为了您的口腔健康,建议来院复查一下。
小节2 - 健康维护(短句2):
这是健康维护的建议,让赵超医生帮您再仔细看看。
小节3 - 检查说明(短句3):
复查检查大概需要30-45分钟的时间,主要是仔细看看您牙周的健康状况。
小节4 - 引导预约(短句4):
建议尽快来看看,赵超医生这周三上午和周五下午这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快,身体健康!
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快,身体健康!
**═══ 附加:赠送礼品(见机行事)═══**
• 阿姨,我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【5】患者: 患者5 (TS0F028338)
年龄: 5岁, 性别: 男
最后就诊: 2024-12-17 00:00:00
就诊医生: 段芹
就诊次数: 0次
----------------------------------------
回访话术:
好的,已收到您的指令。正在为您生成回访话术。
**逻辑判断:**
1. **年龄判断**:患者【牟子凡】年龄为5岁,≤12岁,执行“儿童早矫话术模板”(5模块结构)。
2. **漏诊项判断**:患者漏诊项为【恒牙萌出空间不足】,优先级为最高。本次话术将围绕此项进行。
**正在生成话术...**
***
### **儿童早矫回访话术 - 牟子凡**
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔东亭门诊的回访专员。
• 段芹医生特意交代我来关注宝宝的后续情况。
• (如果是熟悉患者可说:段芹医生上次还和我提起宝宝呢)
• 宝宝自从12月17号检查后,口腔情况怎么样?
**═══ 第二部分:告知牙齿问题-健康提醒 ═══**
小节1 - 现状描述(短句1):
上次段芹医生检查发现,宝宝未来恒牙长出来的位置可能会不够。
小节2 - 原因说明(短句2):
这主要是因为宝宝现在的几颗乳牙存在一些健康问题,有可能会过早脱落。
小节3 - 不治疗危害(短句3):
如果乳牙过早掉了,旁边的牙齿就可能会挤占空间。
小节4 - 后果说明(短句4):
这样将来恒牙萌出时,就没有足够的位置,容易长得不整齐。
小节5 - 解决方案(短句5):
所以医生建议要密切关注,必要时可能需要一个叫“间隙保持器”的小装置来提前保住位置。
**═══ 第三部分:复查建议 ═══**
小节1 - 复查时间(短句1):
建议3个月左右带宝宝来院检查一下。
小节2 - 检查内容(短句2):
一方面是做全面检查,看看蛀牙的情况,另一方面也是为了评估这个牙齿空间的问题。
小节3 - 预防措施(短句3):
我们也会看看要不要给宝宝涂氟,更好地保护他的牙齿。
小节4 - 专业建议(短句4):
也请段芹医生再仔细看一下宝宝的具体情况,给一些针对性的保护建议。
**═══ 第四部分:引导预约 ═══**
• 建议尽快带宝宝过来看看。
• 段芹医生【周三下午】和【周五上午】这两个时间段有空。
• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。
**═══ 第五部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要多观察孩子的牙齿情况。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访的小朋友准备了小礼品。
• 到时候来检查可以领取。
• 宝宝一定会喜欢的。
============================================================
【6】患者: 患者6 (TS0I013775)
年龄: 36岁, 性别: 女
最后就诊: 2025-01-11 00:00:00
就诊医生: 叶冯雷
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成针对患者俞科萍女士的专业回访话术。
---
### 回访对象分析
1. **年龄判断**: 患者36岁,大于13岁,执行“**成人漏诊话术模板**”(4模块结构)。
2. **漏诊项优先级判断**:
* 患者存在漏诊项:【缺失牙】和【牙槽骨吸收】。
* 根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕【缺失牙】进行**,忽略牙槽骨吸收。
3. **信息完整性**: 关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
---
### 专业回访话术
**═══ 第一部分:开场白 ═══**
* 您好,俞女士,我是江苏瑞泰通善口腔红豆门诊的回访专员。
* 叶冯雷医生特意交代我来关注您的后续情况。
* (如果是熟悉患者可说:叶冯雷医生上次还和我提起您呢)
* 您自从1月11号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
小节1 - 现状描述(短句1):
在您上次检查中,我们发现了您有几颗缺失牙的情况。
小节2 - 健康提醒(短句2):
如果缺失牙问题不及时处理,可能会出现邻牙倾斜、咬合紊乱的情况。
小节3 - 个人化关怀(短句3):
您今年36岁,正值事业发展的关键时期,及时修复对维持您的个人形象和自信心都特别重要。
小节4 - 专业建议(短句4):
叶冯雷医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
小节1 - 复查重要性(短句1):
为了您的口腔健康,建议您抽空来院复查一下。
小节2 - 健康维护(短句2):
这是一个健康维护的建议,让叶冯雷医生帮您再仔细看看具体情况。
小节3 - 检查说明(短句3):
复查检查大概需要30分钟左右的时间。
小节4 - 引导预约(短句4):
建议尽快来看看,叶冯雷医生【本周三下午】和【本周五上午】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
* 好的,那我们【具体预约时间】见。
* 那不打扰您了,祝您生活愉快!
**预约不成功:**
* 好的,那我下个星期再跟您联系。
* 那不打扰您了,祝您生活愉快!
**═══ 附加:赠送礼品(见机行事)═══**
* 我们为回访患者准备了精美的小礼品。
* 到时候您来检查可以顺便领取,表示我们对您的感谢。
============================================================
【7】患者: 患者7 (TS0J005963)
年龄: 42岁, 性别: 男
最后就诊: 2024-12-17 00:00:00
就诊医生: 范鹏军
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是为您生成的专业回访话术。
***
### **回访话术**
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔马山门诊的回访专员。
• 范鹏军医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:范鹏军医生上次还和我提起您呢)
• 您自从12月17号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
小节1 - 现状描述(短句1):
在您上次检查中,我们发现了牙槽骨吸收的情况。
小节2 - 健康提醒(短句2):
如果牙槽骨吸收问题不及时处理,可能会出现的如牙齿松动、牙缝变大的情况。
小节3 - 个人化关怀(短句3):
您现在正是维护口腔健康的关键时期,及时处理对稳住牙槽骨、保住自己的牙齿非常重要。
小节4 - 专业建议(短句4):
范鹏军医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
小节1 - 复查重要性(短句1):
为了您的口腔健康,建议来院复查一下。
小节2 - 健康维护(短句2):
这是健康维护的建议,让范鹏军医生帮您再仔细看看。
小节3 - 检查说明(短句3):
复查检查大概需要30-45分钟的时间。
小节4 - 引导预约(短句4):
建议尽快来看看,范鹏军医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段),您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【8】患者: 患者8 (TS0M005023)
年龄: 33岁, 性别: 男
最后就诊: 2024-12-17 00:00:00
就诊医生: 陈洋洋
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
**逻辑判断过程:**
1. **年龄判断**:患者年龄为33岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:
* 患者存在“缺失牙”(诊断:缺失,牙位:21)和“牙槽骨吸收”两个漏诊项。
* 根据优先级规则:缺失牙(优先级③) > 牙槽骨吸收(优先级④)。
* **最终决定**:本次话术将严格围绕优先级最高的“**缺失牙**”项目展开,忽略“牙槽骨吸收”及其他所有问题。
---
### **患者关怀回访话术**
**═══ 第一部分:开场白 ═══**
* 您好,王先生,我是江苏瑞泰通善口腔学前街医院的回访专员。
* 上次为您看诊的陈洋洋医生特意交代我,打电话来关心一下您的后续情况。
* 您自从12月17号在我们这里拔完牙后,口腔感觉怎么样?恢复得还好吗?
**═══ 第二部分:告知漏诊项目 ═══**
* **小节1 - 现状描述(短句1):**
在您上次检查中,我们发现了您有缺失牙的情况。
* **小节2 - 健康提醒(短句2):**
如果缺失牙问题不及时处理,时间长了可能会出现邻牙倾斜、对牙伸长这些情况。
* **小节3 - 个人化关怀(短句3):**
您现在33岁,正是身体状态好的时候,及时修复不仅能恢复正常的吃饭功能,对个人形象和自信心也很有帮助。
* **小节4 - 专业建议(短句4):**
所以陈洋洋医生特别叮嘱,建议您要关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
* **小节1 - 复查重要性(短句1):**
为了您的口腔健康,建议您方便的时候可以来院复查一下。
* **小节2 - 健康维护(短句2):**
这只是一个健康维护的建议,主要是让陈洋洋医生帮您再仔细看看,评估一下现在的情况。
* **小节3 - 检查说明(短句3):**
复查检查大概需要【30分钟,了解缺失牙位目前状况】的时间。
* **小节4 - 引导预约(短句4):**
建议可以尽快来看看,陈洋洋医生【时间段1,例如:周三下午】和【时间段2,例如:周五上午】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
* **预约成功:**
好的,王先生,那我们【具体预约时间】见,到时候我们会提前给您发提醒信息。那不打扰您了,祝您生活愉快!
* **预约不成功:**
好的,没关系。那我下个星期再跟您联系,您也可以先考虑一下。那不打扰您了,祝您生活愉快!
**═══ 附加:赠送礼品(见机行事)═══**
* 对了,王先生,我们为回访患者准备了一份口腔护理小礼品。
* 您下次过来检查的时候,可以直接到前台领取。
* 算是我们对您一直以来支持的一点心意。
============================================================
【9】患者: 患者9 (TS0G017332)
年龄: 41岁, 性别: 女
最后就诊: 2024-12-17 00:00:00
就诊医生: 陈小华
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是根据患者卢艳女士的信息生成的专属回访话术。
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔大丰门诊的回访专员。
• 陈小华医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:陈小华医生上次还和我提起您呢)
• 您自从去年12月检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
小节1 - 现状描述(短句1):
在您上次检查中,我们发现了缺失牙的情况。
小节2 - 健康提醒(短句2):
如果缺失牙问题不及时处理,可能会出现的如邻牙倾斜、对牙伸长情况。
小节3 - 个人化关怀(短句3):
像您这个年龄,及时修复不仅能恢复咀嚼,对维持您的个人形象也很重要。
小节4 - 专业建议(短句4):
陈小华医生建议您关注一下这个问题。
**═══ 第三部分:复查建议 ═══**
小节1 - 复查重要性(短句1):
为了您的口腔健康,建议来院复查一下。
小节2 - 健康维护(短句2):
这是健康维护的建议,让陈小华医生帮您再仔细看看。
小节3 - 检查说明(短句3):
复查检查大概需要30分钟的时间。
小节4 - 引导预约(短句4):
建议尽快来看看,陈小华医生周三上午和周五下午这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【10】患者: 患者10 (TS0I019780)
年龄: 5岁, 性别: 女
最后就诊: 2025-04-20 00:00:00
就诊医生: 许海建
就诊次数: 0次
----------------------------------------
回访话术:
好的,将为您生成一份针对邵依伊小朋友的专业、温馨的回访话术。
---
好的,这是根据患者 **邵依伊**(5岁,女)的信息,为您生成的专属回访话术:
【回访话术】
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔红豆门诊的回访专员。
• 许海建医生特意交代我来关注宝宝的后续情况。
• (如果是熟悉患者可说:许海建医生上次还和我提起宝宝呢)
• 宝宝自从4月20号检查后,口腔情况怎么样?
**═══ 第二部分:告知牙齿问题-健康提醒 ═══**
• 在上次检查中,我们发现宝宝未来恒牙萌出的空间可能不太足够。
• 这个问题需要我们家长提前关注起来。
• 如果现在不进行早期干预,将来新换的恒牙可能会因为没有足够的位置而长得不整齐。
• 这不仅会影响美观和咬合,以后矫正起来也会更复杂。
• 所以许医生建议,需要通过早期矫正来引导,为恒牙的整齐萌出创造好位置。
**═══ 第三部分:复查建议 ═══**
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请许海建医生再仔细看一下宝宝的牙齿发育情况。
**═══ 第四部分:引导预约 ═══**
• 建议尽快带宝宝过来看看。
• 许海建医生**周三下午**和**周六上午**这两个时间段有空(可根据医生实际排班调整)。
• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。
**═══ 第五部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要多观察孩子的牙齿情况。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访的小朋友准备了小礼品。
• 到时候来检查可以领取。
• 宝宝一定会喜欢的。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 15:30:42
总计处理: 10 个患者
成功生成: 10 个话术
生成失败: 0 个
============================================================
【1】患者: 患者1 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
---
**逻辑判断:**
1. **年龄判断**: 患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**: 患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行。
**回访话术生成如下:**
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙吉卿医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)
• 您自从去年6月检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,孙吉卿医生注意到您有缺牙的情况。
• 时间久了,旁边的牙齿可能会发生倾斜,影响到正常的咬合。
• 趁现在早点处理,不仅能恢复正常的咀嚼,也能保护好旁边的健康牙齿。
• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下,最好别拖太久。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 患者2 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是根据您提供的患者信息和话术模板生成的回访内容。
---
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 胡航医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 时间久了,可能会影响咬合,吃东西也不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好。
• 这个情况,胡航医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
[根据预约情况选择]
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
---
**(附加选项,根据沟通氛围决定是否使用)**
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【3】患者: 患者3 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
**分析过程:**
1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:
* 患者漏诊项为:`缺失牙`。
* 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次沟通将**唯一且仅**围绕“缺失牙”进行。
3. **模板匹配**:严格按照“成人漏诊话术模板”的4个模块结构和要求生成内容。
4. **信息提取**:
* 诊所: 江苏瑞泰通善口腔学前街医院
* 最后一次就诊医生: 王程文
* 漏诊项: 缺失牙
* 最后一次就诊时间: 2025-06-30 (智能时间显示为“6月30号”)
* 复查时长: 缺失牙对应“30分钟”
---
以下是为您生成的专属回访话术:
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 王程文医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 这个问题如果一直拖着,可能会影响到旁边的好牙,造成咬合紊乱,吃东西也不太舒服。
• 趁现在早点处理,不光能恢复咀嚼,对晚年生活质量也是个保障,以后也省心。
• 这个情况,王程文医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让王程文医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【4】患者: 患者4 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是根据您提供的患者信息和话术模板生成的回访内容。
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 沈佳丽医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不算严重,早点关注和稳住,比以后复杂了更省心。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让沈佳丽医生帮您再仔细看看。
• 复查检查大概需要30-45分钟的时间。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
[根据预约情况选择]
**如果预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**如果预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【5】患者: 患者5 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析过程如下:
1. **年龄判断**:患者陆志毅先生64岁,属于“≥13岁”的范畴,因此执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者的漏诊项为“缺失牙”,根据优先级“恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收”,本次沟通将只围绕“缺失牙”这一个项目展开。
3. **模板与数据匹配**:将患者信息(姓名:陆志毅,医生:蒋亚萍,诊所:江苏瑞泰通善口腔学前街医院,漏诊项:缺失牙 等)及话术要点(风险:邻牙倾斜、影响咬合;优势:保护邻牙、维持健康)代入“成人漏-诊话术模板”的四个模块中。
4. **话术生成**:严格按照四模块结构和语言要求,生成最终话术。
***
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 蒋亚萍医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:蒋亚萍医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 这个问题如果放着不管,时间久了可能会影响到旁边的牙齿,造成咬合不太舒服。
• 趁现在牙槽骨条件还不错,早点关注和处理,对维持口腔整体的健康会更好。
• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,希望您别忽略了。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让蒋亚萍医生帮您再仔细看看现在的情况。
• 复查检查大概需要30分钟的时间。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【6】患者: 患者6 (TS0M008309)
年龄: 41岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 陈洋洋
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成针对患者温佼丹女士的专业回访话术。
---
**逻辑判断过程:**
1. **年龄判断**:患者41岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级规则(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕“缺失牙”**进行,完全忽略牙槽骨吸收。
3. **数据完整性检查**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
---
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 陈洋洋医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。
• 时间久了,可能会影响到旁边牙齿的健康,吃东西也不太方便。
• 趁现在早点处理,不仅能保护好旁边的牙齿,对恢复正常的咬合功能也很有帮助。
• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让陈洋洋医生帮您再仔细看看。
• 复查检查大概需要30分钟的时间,主要是了解一下缺失牙位置目前的情况。
• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【7】患者: 患者7 (TS0M006433)
年龄: 81岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项判断**:漏诊项为【缺失牙】,优先级为3。无更高优先级项目。本次话术将围绕【缺失牙】进行。
3. **信息完整性**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
---
以下是为您生成的专属回访话术:
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙红胜医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)
• 您自从6月29号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,孙红胜医生注意到您有一颗【缺失牙】的情况。
• 时间久了,旁边的牙齿可能会发生倾斜,影响到您正常的咬合。
• 趁现在早点处理,也能更好地保护旁边的健康牙齿,对您晚年的生活质量也是个保障。
• 孙红胜医生也特别嘱咐我们提醒您一下,这个问题最好不要忽略了。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【8】患者: 患者8 (TS0K021609)
年龄: 29岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析结论:
1. **年龄判断**:患者29岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项判断**:患者存在多个漏诊项,根据优先级 “缺失牙 > 牙槽骨吸收”,本次应处理的最高优先级漏诊项为 **牙槽骨吸收**。
3. **话术生成**:将严格按照“成人漏诊话术模板”的4模块结构,并围绕“牙槽骨吸收”这一核心问题生成内容。
---
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙红胜医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)
• 您自从去年6月检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点关注,把牙槽骨稳住会更好。
• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查大概需要30-45分钟的时间,医生需要仔细检查下您的牙周健康状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【9】患者: 患者9 (TS0K090227)
年龄: 34岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 牟苏渝
就诊次数: 0次
----------------------------------------
回访话术:
好的,这是根据您提供的患者信息和严格的话术模板生成的回访内容。
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 牟苏渝医生特意交代我来关注您的后续情况。
• 您自从6月29号过来拆完线之后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让牟苏渝医生帮您再仔细看看。
• 复查检查大概需要【复查检查约30-45分钟,需要仔细检查牙周健康状况】的时间。
• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【10】患者: 患者10 (TS0B010384)
年龄: 60岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
---
**逻辑判断:**
1. **年龄判断**:患者朱慧娟,60岁,满足“≥13岁”条件,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:漏诊项目中包含“牙槽骨吸收”,无更高优先级的“恒牙萌出空间不足”或“缺失牙”。因此,本次话术将只围绕“牙槽骨吸收”展开。
3. **模板适用性**:确认使用4模块结构的“成人漏诊话术模板”。
**回访话术详情:**
**═══ 第一部分:开场白 ═══**
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 刘颖聪医生特意交代我来关注您的后续情况。
• 您自从6月29号检查后,口腔情况怎么样?
**═══ 第二部分:告知漏诊项目 ═══**
• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不算严重,早点关注和稳住,对您晚年的生活质量也是个保障。
• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。
**═══ 第三部分:复查建议 ═══**
• 建议您方便的时候来院复查一下。
• 让刘颖聪医生帮您再仔细看看。
• 复查检查大概需要30-45分钟的时间。
• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
**═══ 第四部分:结束回访语 ═══**
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
**═══ 附加:赠送礼品(见机行事)═══**
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 16:04:58
总计处理: 5 个患者
成功生成: 5 个话术
生成失败: 0 个
============================================================
【1】患者: 患者1 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,将为您生成一份针对患者迟鹏领先生的专业回访话术。
---
**逻辑判断过程:**
1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次回访将**仅针对“缺失牙”**进行沟通。
3. **话术模板确认**:应用“成人漏诊话术模板”,并填充患者信息。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙吉卿医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 其实早一点处理,比以后问题变得复杂时要省事也省心。
• 孙吉卿医生说,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 患者2 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行。
3. **信息完整性判断**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 胡航医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保护好旁边的牙齿。
• 这个情况,胡航医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 患者3 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
好的,请看根据您提供的患者信息和严格的话术模板生成的回访内容。
**逻辑判断:**
* **年龄判断:** 患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”。
* **漏诊项优先级判断:** 漏诊项为“缺失牙”,优先级为③,符合处理要求。
* **模板执行:** 严格按照“成人漏诊话术模板”的4模块结构生成。
---
═══ 第一部分:开场白 ═══
• 您好,杜女士,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 王程文医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对您吃东西也会有影响。
• 趁现在早点处理,不光能保护好旁边的牙齿,对您恢复正常的咀嚼功能也特别重要。
• 这个情况,王程文医生也特别嘱咐我们提醒您一下,希望您能重视起来。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让王程文医生帮您再仔细看看您缺牙位置的现状。
• 复查检查大概需要【30分钟】的时间。
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快!
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快!
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【4】患者: 患者4 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
---
已根据您的要求进行逻辑判断:
* **年龄判断**:患者38岁,≥13岁,执行“成人漏诊话术模板”。
* **漏诊项判断**:漏诊项为“牙槽骨吸收”,为当前最高优先级项目。
* **模板确认**:将严格按照“成人漏诊话术模板”的4模块结构生成。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 沈佳丽医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让沈佳丽医生帮您再仔细看看。
• 复查检查大概需要【30-45分钟】的时间。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【5】患者: 患者5 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项判断**:漏诊项为“缺失牙”,优先级为3。无更高优先级项目。将围绕“缺失牙”生成话术。
3. **模板确认**:将严格按照“成人漏诊话术模板”的4模块结构(开场白、告知漏诊项目、复查建议、结束回访语)生成。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 蒋亚萍医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在早点关注,及时处理能更好地保护旁边的牙齿,对将来的生活质量也有好处。
• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让蒋亚萍医生帮您再仔细看看。
• 复查检查大概需要【30分钟】的时间。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 16:10:56
总计处理: 1 个患者
成功生成: 1 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
**逻辑判断与流程选择:**
1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两项漏诊。“缺失牙”(优先级③)高于“牙槽骨吸收”(优先级④)。
3. **最终方案**:本次话术将严格按照“成人漏诊话术模板”(4模块结构),并仅针对“缺失牙”这一项进行回访。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙吉卿医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也能更好地恢复咀嚼功能。
• 孙吉卿医生说,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 复查检查大概需要【30分钟左右】的时间,主要是了解一下您缺牙位置目前的情况。
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 16:24:11
总计处理: 1 个患者
成功生成: 1 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,已收到您的指令。正在根据患者信息和漏诊项优先级进行逻辑判断。
* **年龄判断**: 患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。
* **漏诊项优先级判断**: 患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”项目**进行,忽略“牙槽骨吸收”。
* **模板确认**: 使用“成人漏诊话术模板”(4模块结构)。
正在生成话术,请稍候。
---
═══ 第一部分:开场白 ═══
* 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是迟先生吗?
* 孙吉卿医生特意交代我来关注您的后续情况。
* (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)
* 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
* 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
* 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间久了还会影响吃东西。
* 其实早一点处理,比以后问题变得复杂了要省事省心很多。
* 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
* 建议您方便的时候来院复查一下。
* 让孙吉卿医生帮您再仔细看看,了解一下缺牙位置目前的状况。
* 复查检查大概需要【30分钟】的时间。
* 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
* 好的,那我们【具体预约时间】见。
* 那不打扰您了,祝您生活愉快。
**预约不成功:**
* 好的,那我下个星期再跟您联系。
* 那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 16:36:02
总计处理: 13 个患者
成功生成: 13 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,将为您生成回访话术。
**逻辑判断过程:**
1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅围绕“缺失牙”**进行。
3. **模板应用**:严格按照“成人漏诊话术模板”的4模块结构生成内容。
---
═══ 第一部分:开场白 ═══
• 您好,迟先生,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙吉卿医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙吉卿医生上次还和我提起您呢)
• 您自从去年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保护好旁边的牙齿。
• 上次孙吉卿医生也提到,这个问题最好别拖太久。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 钱明艳 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术。
分析:
1. **年龄判断**:患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序“缺失牙 > 牙槽骨吸收”,本次话术将**仅针对“缺失牙”**进行回访。
3. **模板选择**:应用“成人漏诊话术模板”(4模块结构)。
4. **数据填充**:将患者信息和漏诊项要点填入模板。
---
═══ 第一部分:开场白 ═══
• 您好,钱女士,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 胡航医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:胡航医生上次还和我提起您呢)
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了吃东西也不太舒服。
• 趁现在早点处理,不仅能保护好旁边的牙齿,对恢复咀嚼功能也很有帮助。
• 这个情况,胡航医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查大概需要【复查检查约30分钟,了解缺失牙位目前状况】的时间。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 杜秋萍 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
**逻辑判断过程:**
1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:
* 患者漏诊项为:`缺失牙`。
* 优先级排序为:恒牙萌出空间不足 > **缺失牙** > 牙槽骨吸收。
* 本次话术将**仅针对“缺失牙”**进行。
3. **信息完整性判断**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整。
4. **模板选择**:确定使用“成人漏诊话术模板”(4模块结构)。
正在生成话术...
***
### **患者【杜秋萍】- 漏诊项【缺失牙】回访话术**
═══ 第一部分:开场白 ═══
* 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是杜女士本人吗?
* 王程文医生特意交代我来关注您的后续情况。
* (如果是熟悉患者可说:王程文医生上次还和我提起您呢)
* 您自从去年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
* 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
* 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
* 趁现在早点处理,对保护好旁边的牙齿有帮助,也能让您以后吃东西更方便,对生活质量很重要的。
* 王程文医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
* 建议您方便的时候来院复查一下。
* 让王程文医生帮您再仔细看看,了解一下缺牙位置目前的状况。
* 复查检查大概需要【30分钟】的时间。
* 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
* **预约成功:**
好的,那我们【具体预约时间】见。那不打扰您了,祝您生活愉快!
* **预约不成功:**
好的,那我下个星期再跟您联系。那不打扰您了,祝您生活愉快!
============================================================
【4】患者: 周蓉 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
---
经分析,该患者年龄为38岁(≥13岁),将执行“成人漏诊话术模板”。
最高优先级漏诊项为【牙槽骨吸收】,将围绕此项生成话术。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是周女士吗?
• 沈佳丽医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:沈佳丽医生上次还和我提起您呢)
• 您自从6月30号补牙后,口腔情况怎么样?之前补的牙齿用着还习惯吗?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响吃东西。
• 趁现在问题还不严重,早点关注,稳住现状,比以后复杂了再处理要省心很多。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,希望您能重视起来。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让沈佳丽医生帮您再仔细看看,评估一下牙槽骨的现状。
• 复查检查大概需要【30-45分钟】的时间。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
---
**【话术自查完毕】**
- [✅] **模块完整性**:已包含开场白、告知漏诊项目、复查建议、结束回访语,共4个模块。
- [✅] **内容要素**:已体现医生关怀、分段告知漏诊项、分段提出复查建议并主动引导预约。
- [✅] **语调风格**:语调温馨、专业,无销售感。
- [✅] **禁止事项**:未涉及费用、具体治疗方案等禁止内容。
- [✅] **逻辑判断**:已根据年龄(38岁)和漏诊项优先级(牙槽骨吸收)选择正确模板和重点。
============================================================
【5】患者: 陆志毅 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
---
**逻辑判断过程:**
1. **年龄判断**:患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:
* 患者漏诊信息包含:`缺失牙`。
* 根据优先级排序 `恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收`,当前最高优先级的漏诊项为 **`缺失牙`**。
3. **模板选择**:已确定使用“成人漏诊话术模板”(4模块结构)。
4. **数据完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均已提供,可以生成合规话术。
---
以下是为您生成的专业回访话术:
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陆先生吗?
• 蒋亚萍医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:蒋亚萍医生上次还和我提起您呢)
• 您自从去年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。
• 趁现在早点处理,对恢复咀嚼功能、保护好旁边的牙齿都有帮助。
• 蒋亚萍医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让蒋亚萍医生帮您再仔细看看。
• 复查检查大概需要30分钟的时间,主要是了解您缺失牙位置目前的状况。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【6】患者: 温佼丹 (TS0M008309)
年龄: 41岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 陈洋洋
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析过程如下:
1. **年龄判断**:患者年龄为41岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”进行沟通**,完全忽略“牙槽骨吸收”。
3. **模板选择**:使用包含“开场白”、“告知漏诊项目”、“复查建议”、“结束回访语”的四模块成人模板。
4. **信息填充**:将患者信息(温女士、陈洋洋医生、江苏瑞泰通善口腔学前街医院等)和漏诊项要点填入模板。
---
### **专业回访话术**
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是温佼丹,温女士吗?
• 陈洋洋医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)
• 您自从6月30号过来检查之后,口腔情况怎么样?上次做的牙冠用着还习惯吗?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。
• 医生提到,缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对牙也可能伸长,影响咬合。
• 趁现在牙槽骨条件还不错,早点处理效果会更好,也避免将来多花功夫。
• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,希望您能重视起来。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让陈洋洋医生帮您再仔细看看缺失牙部位的现状。
• 复查检查大概需要【30分钟】的时间。
• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,温女士,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快!
**预约不成功:**
• 好的,没关系。那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快!
============================================================
【7】患者: 华国梁 (TS0M006433)
年龄: 81岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到指令。正在为您生成回访话术。
分析过程如下:
1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:漏诊项为“缺失牙”,无更高优先级项目,因此本次沟通将围绕“缺失牙”进行。
3. **信息完整性检查**:关键信息(姓名、年龄、医生、诊所、漏诊项)完整,可以生成话术。
4. **模板应用**:严格按照“成人漏诊话术模板”的4模块结构和要求生成内容。
---
以下是为您生成的专属回访话术:
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是华国梁华先生吗?
• 孙红胜医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)
• 您自从去年6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有一颗缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西的咬合。
• 趁现在早点关注起来,把这个问题处理好,对您之后吃东西、保持口腔健康都更好。
• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查大概需要30分钟的时间,主要是了解一下这颗缺牙的现状。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【8】患者: 周韵ZZ00906 (TS0K021609)
年龄: 29岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为29岁,大于13岁,执行【成人漏诊话术模板】(4模块结构)。
2. **漏诊项优先级判断**:
* 患者漏诊项为:`牙槽骨吸收`。
* 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),当前最高优先级的漏诊项为【牙槽骨吸收】。
3. **信息完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均完整。
4. **话术生成**:开始生成4模块结构的成人漏诊话术。
---
═══ 第一部分:开场白 ═══
• 您好,周女士,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 孙红胜医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)
• 您自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点关注,把牙槽骨稳住会更好。
• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查大概需要30-45分钟的时间。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【9】患者: 裴洋帆(198会员) (TS0K090227)
年龄: 34岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 牟苏渝
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析完毕,患者年龄为34岁(≥13岁),将执行“成人漏诊话术模板”。
最高优先级漏诊项为“牙槽骨吸收”,将围绕此项生成话术。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是裴女士吗?
• 牟苏渝医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:牟苏渝医生上次还和我提起您呢)
• 您自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让牟苏渝医生帮您再仔细看看。
• 复查检查大概需要30-45分钟的时间,需要仔细检查牙周健康状况。
• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【10】患者: 朱慧娟 (TS0B010384)
年龄: 60岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成针对患者【朱慧娟】的回访话术。
- **年龄判断**:患者60岁,≥13岁,执行“成人漏诊话术模板”(4模块结构)。
- **漏诊项优先级判断**:漏诊项为【牙槽骨吸收】,是当前最高优先级的项目。
- **模板选择**:确认使用“成人漏诊话术模板”。
话术生成如下:
---
═══ 第一部分:开场白 ═══
• 您好,朱女士,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 刘颖聪医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:刘颖聪医生上次还和我提起您呢)
• 您自从来我们这边检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 建议您方便的时候来院复查一下。
• 让刘颖聪医生帮您再仔细看看。
• 复查检查大概需要【30-45分钟,需要仔细检查牙周健康状况】的时间。
• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
【11】患者: 陈柯翰 (TS0M008628)
年龄: 7岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙瑞红
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析判断:
1. **年龄判断**:患者【陈柯翰】年龄为7岁,小于等于12岁,执行“儿童早矫话术模板”(5模块结构)。
2. **漏诊项判断**:漏诊项为【恒牙萌出空间不足】,优先级为①,符合处理条件。
3. **信息完整性**:关键字段(年龄、姓名、医生、诊所、漏诊项)完整。
开始生成话术:
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陈柯翰的家长吗?
• 孙瑞红医生特意交代我来关注宝宝的后续情况。
• (如果是熟悉患者可说:孙瑞红医生上次还和我提起宝宝呢)
• 宝宝自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
小节1 - 现状描述(短句1):
上次来检查的时候,孙瑞红医生注意到宝宝的恒牙生长空间有点紧张。
小节2 - 位置说明(短句2):
这个阶段是宝宝换牙的关键期,空间不足可能会影响新牙的排列。
小节3 - 不治疗危害(短句3):
如果不去管它,将来新长出来的恒牙就容易长得歪歪扭扭,或者互相挤在一起。
小节4 - 后果说明(短句4):
这样不仅会影响美观,对以后吃东西也可能会有影响。
小节5 - 解决方案(短句5):
所以医生很关注这个问题,认为现在是引导牙齿正常生长的好时机。
═══ 第三部分:复查建议 ═══
小节1 - 复查时间(短句1):
建议3个月左右带宝宝来院检查。
小节2 - 检查内容(短句2):
一方面做全面检查,看有没有蛀牙,有没有不良习惯。
小节3 - 预防措施(短句3):
还要看看要不要给宝宝涂氟保护牙齿。
小节4 - 专业建议(短句4):
也请孙瑞红医生再仔细看一下宝宝的情况。
═══ 第四部分:引导预约 ═══
• 建议尽快带宝宝过来看看。
• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空。
• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。
═══ 第五部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要观察孩子的牙齿情况。
• 那不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访的小朋友准备了小礼品。
• 到时候来检查可以领取。
• 宝宝一定会喜欢的。
============================================================
【12】患者: 陶俊仪 (TS0M008642)
年龄: 4岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 张建奎
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为4岁,属于≤12岁范畴。
2. **模板选择**:执行“儿童早矫话术模板”(5模块结构)。
3. **漏诊项优先级判断**:患者存在“恒牙萌出空间不足”和“缺失牙”两个漏诊项。根据优先级排序,“恒牙萌出空间不足”为最高优先级,本次话术将围绕此项展开。
话术生成如下:
***
═══ 第一部分:开场白 ═══
• 您好,陶俊仪的家长,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 张建奎医生特意交代我来关注宝宝的后续情况。
• (如果是熟悉患者可说:张建奎医生上次还和我提起宝宝呢)
• 宝宝自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
• 上次检查时,张建奎医生就注意到,现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。
• 这颗牙的位置在左上前门牙那边。
• 如果咱们不做处理,这个缺牙留下的空间和位置很可能会慢慢变小。
• 这样的话,将来恒牙萌出就不会在它该在的位置,容易长歪。
• 所以医生当时提到,可以做一个叫“间隙保持器”的小装置来先维持住这个空间。
═══ 第三部分:复查建议 ═══
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请张建奎医生再仔细看一下宝宝牙齿和间隙的详细情况。
═══ 第四部分:引导预约 ═══
• 建议尽快带宝宝过来看看。
• 张建奎医生【时间段1】和【时间段2】这两个时间段有空。
• 您看哪个时间比较方便?我帮您预约一个儿牙专家的时间。
═══ 第五部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要多观察孩子的牙齿情况。
• 那不打扰您了,祝您生活愉快。
============================================================
【13】患者: 陶威 (TS0M008644)
年龄: 38岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
**逻辑判断:**
1. **年龄判断**:患者年龄为38岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:
* 患者漏诊项为:`牙槽骨吸收`。
* 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次通话将只处理“牙槽骨吸收”这一项。
3. **信息完整性检查**:关键字段(年龄、姓名、医生、诊所、漏诊项)均完整。
开始生成话术...
***
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陶威陶先生吗?
• 刘颖聪医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:刘颖聪医生上次还和我提起您呢)
• 您自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
1. 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。
2. 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响吃东西。
3. 趁现在问题还不严重,早一点关注和稳住,比以后问题复杂了要省心很多。
4. 这个情况,刘颖聪医生也特别嘱咐我们,一定要提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
1. 建议您方便的时候可以来院复查一下。
2. 让刘颖聪医生帮您再仔细看看目前牙槽骨的具体情况。
3. 复查检查大概需要【复查检查约30-45分钟,需要仔细检查牙周健康状况】的时间。
4. 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-07-31 17:07:01
总计处理: 12 个患者
成功生成: 12 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
---
**逻辑判断与话术选择:**
1. **年龄判断**:患者年龄为54岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行,忽略“牙槽骨吸收”。
3. **模板选择**:已确定使用“成人漏诊话术模板”(4模块结构)。
---
**回访话术生成如下:**
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 迟先生您好。
• 孙吉卿医生特意交代我来关注您后续的情况。
• 您自从去年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。
• 其实早一点处理,比以后复杂时省事也省心。
• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 钱明艳 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
**逻辑判断:**
1. **年龄判断**: 患者年龄为52岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:
* 漏诊项包含:【缺失牙】、【牙槽骨吸收】。
* 根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次话术将优先且仅处理【缺失牙】项目。
3. **信息完整性检查**: 关键字段(年龄、漏诊项、医生、诊所)完整,可以生成话术。
---
### **回访话术**
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是钱女士吗?
• 胡航医生特意交代我来关注您的后续情况。
• 您自从6月30号来我们这里检查后,最近口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在牙齿的整体条件还不错,早一点处理,比以后复杂时省事也省心。
• 胡航医生说,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**[预约成功]**
• 好的,钱女士,那我们【具体预约时间】见。
• 不打扰您了,祝您生活愉快!
**[预约不成功]**
• 好的,没关系,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快!
============================================================
【3】患者: 杜秋萍 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
好的,收到您的指令。正在为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为66岁,大于13岁,执行“成人漏诊话术模板”(4模块结构)。
2. **漏诊项优先级判断**:漏诊项为【缺失牙】。根据优先级排序(恒牙萌出空间不足 > 缺失牙 > 牙槽骨吸收),本次将只处理【缺失牙】项目。
3. **信息完整性检查**:关键字段(年龄、姓名、医生、诊所、漏诊项)均存在,可以生成话术。
话术生成如下:
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 请问是杜女士吗?
• 王程文医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长吃东西也不太舒服。
• 早一点修复,也能更好地保护旁边的健康牙齿,对维持晚年生活质量很有帮助。
• 这个情况,王程文医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让王程文医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【4】患者: 周蓉 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
分析判断:
1. **年龄判断**:患者年龄为38岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:
* 患者漏诊项为:`牙槽骨吸收`。
* 优先级排序为:恒牙萌出空间不足 > 缺失牙 > **牙槽骨吸收**。
* 本次话术将围绕唯一的漏诊项“牙槽骨吸收”展开。
3. **信息完整性判断**:关键信息(年龄、姓名、医生、诊所、漏诊项)完整,可以生成话术。
---
### **回访话术**
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 周女士,您好。
• 沈佳丽医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,时间久了吃东西也不太舒服。
• 趁现在问题还不严重,早点关注,及时稳住牙槽骨,比以后问题复杂了再处理要省心很多。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,希望您能重视起来。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让沈佳丽医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【5】患者: 陆志毅 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
**逻辑判断:**
1. **年龄判断**: 患者年龄为64岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项判断**: 漏诊项为“缺失牙”,优先级为③。无更高优先级项目,因此处理此项。
3. **模板适用性**: 确认使用4模块结构的成人模板。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是陆先生吗?
• 蒋亚萍医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了吃东西也不太方便。
• 趁现在早点处理,不光能保护好旁边的牙,也能让晚年生活吃东西更舒心。
• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让蒋亚萍医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【6】患者: 温佼丹 (TS0M008309)
年龄: 41岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 陈洋洋
就诊次数: 0次
----------------------------------------
回访话术:
好的,遵照您的指令,现在为您生成回访话术。
---
**逻辑判断过程:**
1. **年龄判断**:患者年龄为41岁,≥13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:患者存在“缺失牙”和“牙槽骨吸收”两个漏诊项。根据优先级排序(缺失牙 > 牙槽骨吸收),本次话术将**仅针对“缺失牙”**进行关怀提醒。
3. **模板适用性确认**:确认使用4模块结构的“成人漏诊话术模板”,并填充所有必需信息。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员,请问是温女士吗?
• 陈洋洋医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间久了还会影响咬合。
• 趁现在牙齿的整体状况还不错,早一点处理,比以后问题复杂了再解决要省心很多。
• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,希望您能重视起来。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让陈洋洋医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**[预约成功]**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**[预约不成功]**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【7】患者: 华国梁 (TS0M006433)
年龄: 81岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,这就为您生成回访话术。
**逻辑判断过程:**
1. **年龄判断**:患者年龄为81岁,大于13岁,执行“成人漏诊话术模板”。
2. **漏诊项优先级判断**:漏诊项为“缺失牙”,优先级为3。无更高优先级项目。本次话术将围绕“缺失牙”展开。
3. **信息完整性检查**:关键字段(姓名、年龄、医生、诊所、漏诊项)均完整。
4. **模板选择**:应用“成人漏诊话术模板”(4模块结构)。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 请问是华先生吗?
• 孙红胜医生特意交代我来关注您的后续情况。
• 您自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长吃东西也不太舒服。
• 趁现在早点处理,能更好地保护旁边的健康牙齿,对咱们现在的生活质量也特别重要。
• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【8】患者: 周韵ZZ00906 (TS0K021609)
年龄: 29岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
**逻辑判断:**
* **年龄判断:** 患者年龄为29岁,大于13岁,执行“成人漏诊话术模板”。
* **漏诊项优先级判断:** 漏诊项为“牙槽骨吸收”,是当前唯一需要处理的项目。
* **模板确认:** 将严格按照“成人漏诊话术模板”的4模块结构生成。
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 周女士您好。
• 孙红胜医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 其实早一点处理,比以后复杂时省事也省心。
• 孙红胜医生说,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【9】患者: 裴洋帆(198会员) (TS0K090227)
年龄: 34岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 牟苏渝
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
分析完毕。患者年龄为34岁,适用“成人漏诊话术模板”。漏诊项为“牙槽骨吸收”,是唯一需要处理的项目。将严格按照4模块结构生成话术。
---
═══ 第一部分:开场白 ═══
• 您好,裴女士,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 牟苏渝医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:牟苏渝医生上次还和我提起您呢)
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 其实早一点处理,比以后复杂时省事也省心。
• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让牟苏渝医生帮您再仔细看看。
• 【复查检查约30-45分钟,需要仔细检查牙周健康状况】
• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
---
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【10】患者: 朱慧娟 (TS0B010384)
年龄: 60岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
好的,已收到您的指令。正在为您生成回访话术...
**逻辑判断结果:**
* **年龄判断:** 60岁 ≥ 13岁,执行“成人漏诊话术模板”。
* **漏诊项优先级判断:** 漏诊项为“牙槽骨吸收”,优先级为④。无更高优先级项目。将围绕“牙槽骨吸收”生成话术。
* **信息完整性检查:** 关键字段(姓名、年龄、医生、诊所、漏诊项)完整。
**回访话术生成如下:**
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 朱女士您好。
• 刘颖聪医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让刘颖聪医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【11】患者: 陈柯翰 (TS0M008628)
年龄: 7岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙瑞红
就诊次数: 0次
----------------------------------------
回访话术:
好的,已收到您的指令。正在为您生成针对患者陈柯翰的回访话术。
* **年龄判断:** 患者7岁(≤12岁),启动“儿童早矫话术模板”。
* **漏诊项判断:** 漏诊项为“恒牙萌出空间不足”,优先级为最高,将围绕此项生成话术。
* **模板确认:** 执行“儿童早矫话术模板”(5模块结构)。
内容生成中...
---
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 请问是陈柯翰小朋友的家长吗?
• 孙瑞红医生特意交代我来关注宝宝的后续情况。
• 宝宝自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
• 上次孙医生检查的时候,注意到宝宝给新牙齿预留的生长空间有点紧张。
• 这在换牙期其实是挺常见的现象。
• 如果咱们现在不关注,牙齿空间不足的问题可能会影响新牙。
• 将来新长出来的恒牙可能会长得不整齐,或者不在它该在的位置上。
• 现在是进行早期干预的好时期,所以想请医生再帮宝宝仔细看看具体情况。
═══ 第三部分:复查建议 ═══
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请孙瑞红医生再仔细看一下宝宝的情况。
═══ 第四部分:引导预约 ═══
• 如果方便的话您看最近有没有时间,我帮您预约一下,您带宝宝过来看一看。
• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第五部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。
• 好的那就不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访的小朋友准备了小礼品。
• 到时候来检查可以领取。
• 宝宝一定会喜欢的。
============================================================
【12】患者: 陶俊仪 (TS0M008642)
年龄: 4岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 张建奎
就诊次数: 0次
----------------------------------------
回访话术:
好的,正在为您生成回访话术...
**逻辑判断结果:**
* **年龄判断:** 患者年龄为4岁,≤12岁,执行“儿童早矫话术模板”。
* **漏诊项判断:** 存在“恒牙萌出空间不足”和“缺失牙”两个漏诊项。根据优先级,“恒牙萌出空间不足”为最高优先级,话术将围绕此项展开。
* **信息完整性:** 关键字段(姓名、年龄、医生、诊所、漏诊项)完整。
---
好的,这是为您生成的专属回访话术:
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员。
• 请问是陶俊仪的家长吗?
• 张建奎医生特意交代我来关注宝宝的后续情况。
• 宝宝自从6月29号检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
• 现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。
• 这颗乳牙的位置在【左上前牙】。
• 如果咱们不做处理,这颗乳牙的位置和空间可能会丧失。
• 将来恒牙萌出就不会在它该在的位置。
• 所以我们要做一个装置来维持这个间隙,这个装置叫间隙保持器。到时候也请医生看一下。
═══ 第三部分:复查建议 ═══
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请张建奎医生再仔细看一下宝宝的情况。
═══ 第四部分:引导预约 ═══
• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。
• 张建奎医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间段比较方便?
═══ 第五部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。
• 好的那就不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访的小朋友准备了小礼品。
• 到时候来检查可以领取。
• 宝宝一定会喜欢的。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-08-04 12:08:25
总计处理: 13 个患者
成功生成: 13 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,迟先生,我是瑞泰通善口腔的回访专员{姓名}。
• 孙吉卿医生特意交代我来关注您的后续情况。
• 您自从去年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合。
• 其实早一点处理,比以后问题变得复杂时要省事也省心。
• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 钱明艳 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的回访专员小张。
• 钱女士,您好。
• 胡航医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了还容易影响吃东西。
• 其实早一点处理,比以后问题变得复杂了要省心很多。
• 这个情况,胡航医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 杜秋萍 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的客户关怀专员[信息缺失]。杜女士您好。
• 王程文医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:王程文医生上次还和我提起您呢)
• 您自从去年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在早点关注,对保护好旁边的健康牙齿有好处,也能更好地保障咱们晚年的生活质量。
• 这个情况,王程文医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让王程文医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【4】患者: 周蓉 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 周女士,您好!我是瑞泰通善口腔的回访专员。
• 沈佳丽医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:沈佳丽医生上次还和我提起您呢)
• 您自从去年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让沈佳丽医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【5】患者: 陆志毅 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔学前街医院的回访专员小王。
• 陆先生您好。
• 蒋亚萍医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响到正常的咬合。
• 趁现在牙槽骨条件还不错,早一点处理对恢复吃东西的功能很有帮助。
• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让蒋亚萍医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
[预约成功]
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
[预约不成功]
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【6】患者: 温佼丹 (TS0M008309)
年龄: 41岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 陈洋洋
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,温女士,我是瑞泰通善口腔的回访专员小王。
• 陈洋洋医生特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:陈洋洋医生上次还和我提起您呢)
• 您自从去年6月来检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,陈洋洋医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也比以后复杂了省心。
• 这个情况,陈洋洋医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让陈洋洋医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 陈洋洋医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【7】患者: 华国梁 (TS0M006433)
年龄: 81岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,华先生,我是瑞泰通善口腔学前街医院的回访专员。
• 您的主治医生,孙红胜医生,特意交代我来关注您的后续情况。
• (如果是熟悉患者可说:孙红胜医生上次还和我提起您呢)
• 您自从上次来院检查后,口腔情况怎么样?活动假牙用着还习惯吗?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您下排牙齿还有一颗缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在早点关注这个问题,对维持您整体的口腔健康和咀嚼功能是很有帮助的。
• 所以孙红胜医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看这颗缺牙的情况。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【8】患者: 周韵ZZ00906 (TS0K021609)
年龄: 29岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙红胜
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 周女士,您好!我是瑞泰通善口腔的回访专员。
• 孙红胜医生特意交代我们,要打电话来关心一下您的后续情况。
• 您自从2025年6月29号做完种植手术之后,口腔感觉怎么样?恢复得还好吗?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有一点牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况。
• 趁现在问题还不严重,早点稳住会更好。
• 所以孙红胜医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【9】患者: 裴洋帆(198会员) (TS0K090227)
年龄: 34岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 牟苏渝
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 裴女士,您好,我是瑞泰通善口腔的回访专员小王。
• 牟苏渝医生特意交代我来关注您的后续情况。
• (牟苏渝医生上次还和我提起您呢)
• 您自从2025年6月拆线后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,牟苏渝医生注意到您有牙槽骨吸收的情况。
• 时间久了,牙齿容易松动,吃东西会不太舒服,而且牙缝也可能慢慢变大。
• 趁现在问题还不算严重,早点关注,把牙槽骨稳住,对您长远来说是最好的。
• 这个情况,牟苏渝医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让牟苏渝医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 牟苏渝医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
[预约成功]
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
[预约不成功]
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【10】患者: 朱慧娟 (TS0B010384)
年龄: 60岁, 性别: 女
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的回访专员小王。
• 朱女士您好。
• 刘颖聪医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,刘颖聪医生注意到您有牙槽骨吸收的情况。
• 时间久了,牙齿容易松动,吃东西会不太舒服,而且牙缝也可能慢慢变大。
• 趁现在问题还不算严重,早点关注和稳住会更好。
• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下,别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让刘颖聪医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【11】患者: 陈柯翰 (TS0M008628)
年龄: 7岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 孙瑞红
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "恒牙萌出空间不足"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的回访专员{姓名}。
• 陈柯翰的家长您好。
• 孙瑞红医生特意交代我来关注宝宝的后续情况。
• 宝宝自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
• 上次来检查的时候,孙瑞红医生注意到宝宝的牙齿有点拥挤,给新牙齿留的位置可能不太够。
• 这个情况叫恒牙萌出空间不足。
• 如果不及时干预,以后新长出来的恒牙可能会长得歪歪扭扭,不整齐。
• 这样不仅影响美观,以后清洁起来也麻烦,还容易蛀牙呢。
• 所以孙医生很关心这个问题,说现在是干预的黄金时期,需要及早关注。
═══ 第三部分:复查建议 ═══
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请孙瑞红医生再仔细看一下宝宝的情况。
═══ 第四部分:引导预约 ═══
• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。
• 孙瑞红医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第五部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。
• 好的那就不打扰您了,祝您生活愉快。
============================================================
【12】患者: 陶俊仪 (TS0M008642)
年龄: 4岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 张建奎
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "恒牙萌出空间不足"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的回访专员(信息缺失:客服姓名)。
• 陶俊仪的家长您好。
• 张建奎医生特意交代我来关注宝宝的后续情况。
• (如果是熟悉患者可说:张建奎医生上次还和我提起宝宝呢)
• 宝宝自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知牙齿问题-健康提醒 ═══
• 现在宝宝有一颗乳牙已经脱落了,但是恒牙还没有长出来。
• 这颗乳牙的位置在【左上门牙】。
• 如果咱们不做处理,这颗乳牙的位置和空间可能会丧失。
• 将来恒牙萌出就不会在它该在的位置。
• 所以我们要做一个装置来维持这个间隙,这个装置叫间隙保持器。到时候也请医生看一下。
═══ 第三部分:复查建议 ═══
• 建议3个月左右带宝宝来院检查。
• 一方面做全面检查,看有没有蛀牙,有没有不良习惯。
• 还要看看要不要给宝宝涂氟保护牙齿。
• 也请张建奎医生再仔细看一下宝宝的情况。
═══ 第四部分:引导预约 ═══
• 如果方便的话您看最近有没有时间,我帮您预约一个儿牙专家的时间,您带宝宝过来看一看。
• 张建奎医生【时间段1】和【时间段2】这两个时间段有空(根据具体排班描述时间段)。
═══ 第五部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 您平时要观察孩子的牙齿情况,如果有问题随时联系我。
• 好的那就不打扰您了,祝您生活愉快。
============================================================
【13】患者: 陶威 (TS0M008644)
年龄: 38岁, 性别: 男
最后就诊: 2025-06-29 00:00:00
就诊医生: 刘颖聪
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的回访专员,很高兴联系到您。
• 陶先生,刘颖聪医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,刘颖聪医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间长了也会影响吃东西。
• 其实早一点处理,比以后问题变得复杂时要省事也省心。
• 这个情况,刘颖聪医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让刘颖聪医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 刘颖聪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-08-05 14:28:01
总计处理: 3 个患者
成功生成: 3 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士长小王。
• 迟先生,孙吉卿医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响您吃东西。
• 趁现在早一点处理,比以后问题变得复杂时要省事也省心。
• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
[预约成功]
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
[预约不成功]
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 钱明艳 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,钱女士,我是瑞泰通善口腔的护士长小王。
• 胡航医生特意交代我来关注您的后续情况。
• 您自从去年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,影响咬合,吃东西也会不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也避免将来多花功夫。
• 这个情况,胡航医生也特别嘱咐我们提醒您一下,别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让胡航医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 杜秋萍 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,杜女士,我是瑞泰通善口腔的护士长小张。
• 王程文医生特意交代我来关注您后续的情况。
• 您自从去年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在牙槽骨条件还不错,早点处理效果更好,也能保障咱们晚年的生活质量。
• 王程文医生说,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让王程文医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
============================================================
++ /dev/null
============================================================
Dify平台回访话术批量生成结果
API类型: AGENT_CHAT
生成时间: 2025-08-05 14:32:22
总计处理: 5 个患者
成功生成: 5 个话术
生成失败: 0 个
============================================================
【1】患者: 迟鹏领 (TS0K064355)
年龄: 54岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 孙吉卿
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士长小张。
• 迟先生,您好。
• 孙吉卿医生特意交代我来关注您的后续情况。
• 您自从2025年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙吉卿医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿还可能伸长出来。
• 其实早一点处理,比以后复杂时省事也省心。
• 这个情况,孙吉卿医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙吉卿医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 孙吉卿医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 钱明艳 (TS0M008666)
年龄: 52岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 胡航
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的医生助理小王。
• 请问是钱女士吗?胡航医生特意交代我来关注您的后续情况。
• 您自从6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,胡航医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙也会伸长,影响到正常的咬合。
• 趁现在牙槽骨条件还不错,早点关注,处理起来效果也会更好。
• 所以胡航医生也特别嘱咐我们提醒您一下,这个问题早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让胡航医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 胡航医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 杜秋萍 (TS0M008652)
年龄: 66岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 王程文
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,杜女士,我是瑞泰通善口腔的护士长小王。
• 王程文医生特意交代我来关注您后续的口腔情况。
• 您自从2025年6月那次检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王程文医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来,影响咬合。
• 趁现在牙槽骨条件还不错,早点关注这个问题,不仅能保护好邻牙,吃东西也能更踏实。
• 所以王程文医生也特别嘱咐我们提醒您一下,这个问题别忽略了,早点关注会更好。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让王程文医生帮您再仔细看看。
• 【复查检查约30分钟,了解缺失牙位目前状况】
• 王程文医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【4】患者: 周蓉 (TS0K065136)
年龄: 38岁, 性别: 女
最后就诊: 2025-06-30 00:00:00
就诊医生: 沈佳丽
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士长小李。
• 周女士您好,沈佳丽医生特意交代我来关注您的后续情况。
• 您自从去年6月30号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,沈佳丽医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,影响您吃东西。
• 趁现在问题还不算严重,早点关注,把情况稳住,比以后复杂了再处理要省心很多。
• 这个情况,沈佳丽医生也特别嘱咐我们提醒您一下,别忽略了。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让沈佳丽医生帮您再仔细看看,评估一下现状。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 沈佳丽医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【5】患者: 陆志毅 (TS0M008662)
年龄: 64岁, 性别: 男
最后就诊: 2025-06-30 00:00:00
就诊医生: 蒋亚萍
就诊次数: 0次
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,陆先生,我是瑞泰通善口腔的健康助理小王。
• 蒋亚萍医生特意交代我来关注您的后续情况。
• 您自从2025年6月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,蒋亚萍医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,时间一长,吃东西也会不太舒服。
• 趁现在早点关注,及时修复,既能保护邻牙,也能让您以后吃东西更省心。
• 这个情况,蒋亚萍医生也特别嘱咐我们提醒您一下,早点看看会比较安心。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让蒋亚萍医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 蒋亚萍医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
This source diff could not be displayed because it is too large. You can view the blob instead.
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-29 15:31:21",
"total_processed": 10,
"successful_callbacks": 10,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 2,
"青少年": 0,
"青年": 2,
"中年": 4,
"老年": 2
},
"gender_distribution": {
"男": 5,
"女": 5,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 10
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:5, 女:5, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:10"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 15:30:42",
"total_processed": 10,
"successful_callbacks": 10,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 2,
"中年": 5,
"老年": 3
},
"gender_distribution": {
"男": 3,
"女": 7,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 10
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:3, 女:7, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:10"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:04:58",
"total_processed": 5,
"successful_callbacks": 5,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 0,
"中年": 3,
"老年": 2
},
"gender_distribution": {
"男": 2,
"女": 3,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 5
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:2, 女:3, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:5"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:10:56",
"total_processed": 1,
"successful_callbacks": 1,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 0,
"中年": 1,
"老年": 0
},
"gender_distribution": {
"男": 1,
"女": 0,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 1
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:1, 女:0, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:1"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:24:11",
"total_processed": 1,
"successful_callbacks": 1,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 0,
"中年": 1,
"老年": 0
},
"gender_distribution": {
"男": 1,
"女": 0,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 1
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:1, 女:0, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:1"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 16:36:02",
"total_processed": 13,
"successful_callbacks": 13,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 2,
"青少年": 0,
"青年": 2,
"中年": 6,
"老年": 3
},
"gender_distribution": {
"男": 6,
"女": 7,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 13
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:6, 女:7, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:13"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-07-31 17:07:01",
"total_processed": 12,
"successful_callbacks": 12,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 2,
"青少年": 0,
"青年": 2,
"中年": 5,
"老年": 3
},
"gender_distribution": {
"男": 5,
"女": 7,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 12
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:5, 女:7, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:12"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-04 12:08:25",
"total_processed": 13,
"successful_callbacks": 13,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 2,
"青少年": 0,
"青年": 2,
"中年": 6,
"老年": 3
},
"gender_distribution": {
"男": 6,
"女": 7,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 13
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:6, 女:7, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:13"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-05 14:28:01",
"total_processed": 3,
"successful_callbacks": 3,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 0,
"中年": 2,
"老年": 1
},
"gender_distribution": {
"男": 1,
"女": 2,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 3
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:1, 女:2, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:3"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-05 14:32:22",
"total_processed": 5,
"successful_callbacks": 5,
"failed_callbacks": 0,
"success_rate": "100.0%"
},
"statistics": {
"age_distribution": {
"儿童": 0,
"青少年": 0,
"青年": 0,
"中年": 3,
"老年": 2
},
"gender_distribution": {
"男": 2,
"女": 3,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 5
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:2, 女:3, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:5"
}
}
\ No newline at end of file
{
++ /dev/null
{
"generation_info": {
"platform": "Dify",
"api_type": "agent_chat",
"generation_time": "2025-08-05 22:57:21",
"total_processed": 765,
"successful_callbacks": 764,
"failed_callbacks": 1,
"success_rate": "99.9%"
},
"statistics": {
"age_distribution": {
"儿童": 176,
"青少年": 21,
"青年": 167,
"中年": 232,
"老年": 168
},
"gender_distribution": {
"男": 302,
"女": 462,
"未知": 0
},
"missed_diagnosis_distribution": {
"有漏诊": 0,
"无漏诊": 764
}
},
"summary": {
"most_common_age_group": "中年",
"gender_ratio": "男:302, 女:462, 未知:0",
"missed_diagnosis_ratio": "有漏诊:0, 无漏诊:764"
}
}
\ No newline at end of file
# Docker Compose配置文件 - 患者画像回访话术系统(带完整认证功能)
services:
# MySQL数据库服务
mysql:
image: mysql:8.0
container_name: patient_callback_mysql_auth
environment:
MYSQL_ROOT_PASSWORD: callback_system_2024
MYSQL_DATABASE: callback_system
MYSQL_USER: callback_user
MYSQL_PASSWORD: callback_pass_2024
MYSQL_ROOT_HOST: '%'
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
ports:
- "3308:3306"
networks:
- patient_callback_network
command: --default-authentication-plugin=mysql_native_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# 患者画像回访话术系统(带完整认证功能)
patient_callback_app:
build: .
container_name: patient_callback_app_auth
environment:
# 数据库配置
DB_HOST: mysql
DB_PORT: 3306
DB_USER: callback_user
DB_PASSWORD: callback_pass_2024
DB_NAME: callback_system
# Flask配置
FLASK_ENV: production
FLASK_DEBUG: 0
# 认证系统配置
SERVER_TYPE: auth_system
SECRET_KEY: dev_secret_key_2024
# 用户权限配置
ENABLE_AUTH: "true"
ENABLE_CLINIC_ACCESS_CONTROL: "true"
volumes:
- ./patient_profiles:/app/patient_profiles
- ./progress_saves:/app/progress_saves
- ./dify_callback_results:/app/dify_callback_results
- ./诊所患者json:/app/诊所患者json
- ./诊所患者目录:/app/诊所患者目录
- ./clinic_config.py:/app/clinic_config.py:ro
- ./auth_system.py:/app/auth_system.py:ro
ports:
- "4002:5000"
networks:
- patient_callback_network
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"]
interval: 30s
timeout: 10s
retries: 3
# 回访API服务(备用)
callback_api:
build: .
container_name: patient_callback_api
environment:
DB_HOST: mysql
DB_PORT: 3306
DB_USER: callback_user
DB_PASSWORD: callback_pass_2024
DB_NAME: callback_system
SERVER_TYPE: callback_api
volumes:
- ./patient_profiles:/app/patient_profiles
ports:
- "5003:5000"
networks:
- patient_callback_network
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
command: ["python", "callback_api_server.py"]
profiles:
- backup
volumes:
mysql_data:
driver: local
networks:
patient_callback_network:
driver: bridge
\ No newline at end of file
# Docker Compose配置文件 - 患者画像回访话术系统(本地MySQL版本)
services:
# MySQL数据库服务(使用本地构建)
mysql:
build:
context: .
dockerfile: Dockerfile.mysql
container_name: patient_callback_mysql
environment:
MYSQL_ROOT_PASSWORD: callback_system_2024
MYSQL_DATABASE: callback_system
MYSQL_USER: callback_user
MYSQL_PASSWORD: callback_pass_2024
MYSQL_ROOT_HOST: '%'
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
ports:
- "3306:3306"
networks:
- patient_callback_network
command: --default-authentication-plugin=mysql_native_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# 患者画像回访话术系统
patient_callback_app:
build: .
container_name: patient_callback_app
environment:
# 数据库配置
DB_HOST: mysql
DB_PORT: 3306
DB_USER: callback_user
DB_PASSWORD: callback_pass_2024
DB_NAME: callback_system
# Flask配置
FLASK_ENV: production
FLASK_DEBUG: 0
volumes:
- ./patient_profiles:/app/patient_profiles
- ./progress_saves:/app/progress_saves
- ./dify_callback_results:/app/dify_callback_results
- ./诊所患者json:/app/诊所患者json
- ./诊所患者目录:/app/诊所患者目录
ports:
- "5000:5000"
networks:
- patient_callback_network
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
interval: 30s
timeout: 10s
retries: 3
volumes:
mysql_data:
driver: local
networks:
patient_callback_network:
driver: bridge
\ No newline at end of file
# Docker Compose配置文件 - 患者画像回访话术系统(SQLite版本)
services:
# 患者画像回访话术系统(使用SQLite)
patient_callback_app:
build: .
container_name: patient_callback_app
environment:
# 使用SQLite数据库
DATABASE_TYPE: sqlite
DATABASE_URL: sqlite:///app/callback_records.db
# Flask配置
FLASK_ENV: production
FLASK_DEBUG: 0
volumes:
- ./patient_profiles:/app/patient_profiles
- ./progress_saves:/app/progress_saves
- ./dify_callback_results:/app/dify_callback_results
- ./诊所患者json:/app/诊所患者json
- ./诊所患者目录:/app/诊所患者目录
- ./callback_records.db:/app/callback_records.db
ports:
- "5000:5000"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
interval: 30s
timeout: 10s
retries: 3
volumes:
# 不需要MySQL数据卷
\ No newline at end of file
# 开发环境Docker Compose覆盖配置
services:
patient_callback_app:
environment:
FLASK_ENV: development
FLASK_DEBUG: 1
volumes:
# 开发模式下挂载源代码,支持热重载
- .:/app
- /app/__pycache__
command: ["python", "start_docker.py"]
mysql:
ports:
# 开发环境暴露MySQL端口,便于调试
- "3306:3306"
environment:
# 开发环境使用简单密码
MYSQL_ROOT_PASSWORD: dev_password_123
MYSQL_PASSWORD: dev_password_123
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户登录 - 回访记录系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-container {
background: white;
padding: 40px;
border-radius: 15px;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
position: relative;
overflow: hidden;
}
.login-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 5px;
background: linear-gradient(90deg, #667eea, #764ba2);
}
.login-header {
text-align: center;
margin-bottom: 30px;
}
.login-header h1 {
color: #333;
font-size: 28px;
margin-bottom: 10px;
font-weight: 600;
}
.login-header p {
color: #666;
font-size: 14px;
}
.form-group {
margin-bottom: 20px;
position: relative;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
.form-group input {
width: 100%;
padding: 12px 16px;
border: 2px solid #e1e5e9;
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
background: #f8f9fa;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
background: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.form-group input:invalid {
border-color: #e74c3c;
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
font-size: 14px;
}
.remember-me {
display: flex;
align-items: center;
gap: 8px;
}
.remember-me input[type="checkbox"] {
width: auto;
margin: 0;
}
.forgot-password {
color: #667eea;
text-decoration: none;
transition: color 0.3s ease;
}
.forgot-password:hover {
color: #764ba2;
}
.login-button {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.login-button:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
}
.login-button:active {
transform: translateY(0);
}
.login-button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.loading-spinner {
display: none;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
border: 2px solid transparent;
border-top: 2px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
.error-message {
background: #fee;
color: #c33;
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
border: 1px solid #fcc;
font-size: 14px;
display: none;
}
.success-message {
background: #efe;
color: #3c3;
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
border: 1px solid #cfc;
font-size: 14px;
display: none;
}
.footer-info {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
color: #666;
font-size: 12px;
}
.default-account {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #667eea;
}
.default-account h4 {
color: #333;
margin-bottom: 8px;
font-size: 14px;
}
.default-account p {
color: #666;
font-size: 12px;
margin: 4px 0;
}
/* 响应式设计 */
@media (max-width: 480px) {
.login-container {
padding: 30px 20px;
margin: 10px;
}
.login-header h1 {
font-size: 24px;
}
.form-group input {
padding: 14px 16px;
}
}
/* 暗色主题支持 */
@media (prefers-color-scheme: dark) {
body {
background: linear-gradient(135deg, #2d3748 0%, #4a5568 100%);
}
.login-container {
background: #1a202c;
color: #e2e8f0;
}
.login-header h1 {
color: #e2e8f0;
}
.form-group label {
color: #e2e8f0;
}
.form-group input {
background: #2d3748;
border-color: #4a5568;
color: #e2e8f0;
}
.form-group input:focus {
background: #374151;
border-color: #667eea;
}
}
</style>
</head>
<body>
<div class="login-container">
<div class="login-header">
<h1>用户登录</h1>
<p>欢迎使用回访记录管理系统</p>
</div>
<div class="default-account">
<h4>默认管理员账户</h4>
<p>用户名: admin</p>
<p>密码: admin123</p>
<p style="color: #e74c3c; font-weight: 600;">⚠️ 请登录后立即修改密码!</p>
</div>
<div id="errorMessage" class="error-message"></div>
<div id="successMessage" class="success-message"></div>
<form id="loginForm">
<div class="form-group">
<label for="username">用户名</label>
<input
type="text"
id="username"
name="username"
required
placeholder="请输入用户名"
autocomplete="username"
maxlength="50"
>
</div>
<div class="form-group">
<label for="password">密码</label>
<input
type="password"
id="password"
name="password"
required
placeholder="请输入密码"
autocomplete="current-password"
minlength="6"
>
</div>
<div class="form-options">
<label class="remember-me">
<input type="checkbox" id="rememberMe" name="rememberMe">
<span>记住登录状态</span>
</label>
<a href="#" class="forgot-password" onclick="showForgotPassword()">忘记密码?</a>
</div>
<button type="submit" class="login-button" id="loginButton">
<span id="buttonText">登录</span>
<div class="loading-spinner" id="loadingSpinner"></div>
</button>
</form>
<div class="footer-info">
<p>回访记录管理系统 v1.0</p>
<p>© 2024 All Rights Reserved</p>
</div>
</div>
<script>
// 登录表单处理
document.getElementById('loginForm').addEventListener('submit', async function(e) {
e.preventDefault();
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value;
const rememberMe = document.getElementById('rememberMe').checked;
// 验证输入
if (!username || !password) {
showError('请填写用户名和密码');
return;
}
if (password.length < 6) {
showError('密码长度至少6位');
return;
}
// 显示加载状态
setLoading(true);
hideMessages();
try {
// 发送登录请求
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password,
remember_me: rememberMe
})
});
const result = await response.json();
if (result.success) {
// 登录成功
showSuccess('登录成功,正在跳转...');
// 保存会话信息
if (rememberMe) {
localStorage.setItem('session_id', result.session_id);
localStorage.setItem('user_info', JSON.stringify(result.user));
} else {
sessionStorage.setItem('session_id', result.session_id);
sessionStorage.setItem('user_info', JSON.stringify(result.user));
}
// 延迟跳转
setTimeout(() => {
// 跳转到患者画像索引页面或用户指定页面
const redirectUrl = new URLSearchParams(window.location.search).get('redirect') || '/patient_profiles/';
window.location.href = redirectUrl;
}, 1500);
} else {
// 登录失败
showError(result.message || '登录失败,请检查用户名和密码');
}
} catch (error) {
console.error('登录请求失败:', error);
showError('网络连接失败,请检查网络设置');
} finally {
setLoading(false);
}
});
// 设置加载状态
function setLoading(loading) {
const button = document.getElementById('loginButton');
const buttonText = document.getElementById('buttonText');
const spinner = document.getElementById('loadingSpinner');
if (loading) {
button.disabled = true;
buttonText.style.opacity = '0';
spinner.style.display = 'block';
} else {
button.disabled = false;
buttonText.style.opacity = '1';
spinner.style.display = 'none';
}
}
// 显示错误消息
function showError(message) {
const errorDiv = document.getElementById('errorMessage');
errorDiv.textContent = message;
errorDiv.style.display = 'block';
// 3秒后自动隐藏
setTimeout(hideMessages, 3000);
}
// 显示成功消息
function showSuccess(message) {
const successDiv = document.getElementById('successMessage');
successDiv.textContent = message;
successDiv.style.display = 'block';
}
// 隐藏所有消息
function hideMessages() {
document.getElementById('errorMessage').style.display = 'none';
document.getElementById('successMessage').style.display = 'none';
}
// 忘记密码处理
function showForgotPassword() {
alert('请联系管理员重置密码\n\n管理员可以通过以下方式重置密码:\n1. 使用管理员账户登录\n2. 在用户管理页面重置密码\n3. 或直接删除用户重新创建');
}
// 页面加载时检查是否已登录
window.addEventListener('load', function() {
const sessionId = localStorage.getItem('session_id') || sessionStorage.getItem('session_id');
if (sessionId) {
// 验证会话是否有效
fetch('/api/auth/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
session_id: sessionId
})
})
.then(response => response.json())
.then(result => {
if (result.success) {
// 会话有效,直接跳转到患者画像索引页面
const redirectUrl = new URLSearchParams(window.location.search).get('redirect') || '/patient_profiles/';
window.location.href = redirectUrl;
}
})
.catch(error => {
console.log('会话验证失败:', error);
// 清除无效会话
localStorage.removeItem('session_id');
localStorage.removeItem('user_info');
sessionStorage.removeItem('session_id');
sessionStorage.removeItem('user_info');
});
}
});
// 键盘快捷键
document.addEventListener('keydown', function(e) {
// Ctrl+Enter 或 Cmd+Enter 提交表单
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
document.getElementById('loginForm').dispatchEvent(new Event('submit'));
}
});
// 输入框回车提交
document.getElementById('password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('loginForm').dispatchEvent(new Event('submit'));
}
});
// 自动聚焦到用户名输入框
document.getElementById('username').focus();
</script>
</body>
</html>
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据库迁移命令行工具
用法:
python migrate.py create "add_user_email" "添加用户邮箱字段"
python migrate.py status
python migrate.py migrate [--dry-run] [--target=version]
python migrate.py rollback version
python migrate.py validate
"""
import sys
import argparse
import json
from database_migration_manager import DatabaseMigrationManager
from database_config import get_database_config
def create_migration(manager: DatabaseMigrationManager, name: str, description: str = ""):
"""创建新迁移"""
try:
filepath = manager.create_migration(name, description)
print(f"✅ 迁移文件已创建: {filepath}")
print(f"📝 请编辑文件添加SQL语句")
return True
except Exception as e:
print(f"❌ 创建迁移失败: {e}")
return False
def show_status(manager: DatabaseMigrationManager):
"""显示迁移状态"""
try:
status = manager.get_migration_status()
print("📊 数据库迁移状态")
print("=" * 50)
print(f"✅ 已执行: {status['executed_count']}")
print(f"❌ 失败: {status['failed_count']}")
print(f"🔄 已回滚: {status['rolled_back_count']}")
print(f"⏳ 待执行: {status['pending_count']}")
print()
if status['pending_migrations']:
print("📋 待执行迁移:")
for migration in status['pending_migrations']:
print(f" • {migration}")
print()
if status['history']:
print("📜 迁移历史 (最近10条):")
for item in status['history'][:10]:
status_icon = {
'SUCCESS': '✅',
'FAILED': '❌',
'ROLLED_BACK': '🔄'
}.get(item['status'], '❓')
print(f" {status_icon} {item['version']} - {item['filename']}")
print(f" 执行时间: {item['executed_at']}")
if item['execution_time_ms']:
print(f" 耗时: {item['execution_time_ms']}ms")
if item['error_message']:
print(f" 错误: {item['error_message'][:100]}...")
print()
return True
except Exception as e:
print(f"❌ 获取状态失败: {e}")
return False
def run_migrations(manager: DatabaseMigrationManager, target_version: str = None, dry_run: bool = False):
"""执行迁移"""
try:
if dry_run:
print("🔍 试运行模式 - 不会实际执行迁移")
success = manager.migrate(target_version, dry_run)
if success:
print("🎉 迁移执行完成!")
else:
print("⚠️ 迁移执行过程中出现错误")
return success
except Exception as e:
print(f"❌ 执行迁移失败: {e}")
return False
def rollback_migration(manager: DatabaseMigrationManager, version: str):
"""回滚迁移"""
try:
print(f"🔄 准备回滚迁移版本: {version}")
print("⚠️ 警告: 回滚操作可能导致数据丢失!")
confirm = input("确认继续? (yes/no): ").lower().strip()
if confirm not in ['yes', 'y']:
print("❌ 回滚操作已取消")
return False
success = manager.rollback_migration(version)
if success:
print("✅ 迁移回滚完成!")
else:
print("❌ 迁移回滚失败")
return success
except Exception as e:
print(f"❌ 回滚迁移失败: {e}")
return False
def validate_migrations(manager: DatabaseMigrationManager):
"""验证迁移文件"""
try:
success = manager.validate_migrations()
if success:
print("✅ 所有迁移文件验证通过")
else:
print("⚠️ 部分迁移文件验证失败")
return success
except Exception as e:
print(f"❌ 验证迁移文件失败: {e}")
return False
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='数据库迁移管理工具')
subparsers = parser.add_subparsers(dest='command', help='可用命令')
# create 命令
create_parser = subparsers.add_parser('create', help='创建新迁移')
create_parser.add_argument('name', help='迁移名称')
create_parser.add_argument('description', nargs='?', default='', help='迁移描述')
# status 命令
subparsers.add_parser('status', help='显示迁移状态')
# migrate 命令
migrate_parser = subparsers.add_parser('migrate', help='执行迁移')
migrate_parser.add_argument('--dry-run', action='store_true', help='试运行模式')
migrate_parser.add_argument('--target', help='目标版本')
# rollback 命令
rollback_parser = subparsers.add_parser('rollback', help='回滚迁移')
rollback_parser.add_argument('version', help='要回滚的版本号')
# validate 命令
subparsers.add_parser('validate', help='验证迁移文件')
args = parser.parse_args()
if not args.command:
parser.print_help()
return 1
try:
# 获取数据库配置
db_config = get_database_config()
manager = DatabaseMigrationManager(db_config)
# 执行对应命令
if args.command == 'create':
success = create_migration(manager, args.name, args.description)
elif args.command == 'status':
success = show_status(manager)
elif args.command == 'migrate':
success = run_migrations(manager, args.target, args.dry_run)
elif args.command == 'rollback':
success = rollback_migration(manager, args.version)
elif args.command == 'validate':
success = validate_migrations(manager)
else:
print(f"❌ 未知命令: {args.command}")
return 1
return 0 if success else 1
except Exception as e:
print(f"❌ 执行失败: {e}")
return 1
if __name__ == '__main__':
sys.exit(main())
-- Migration: Initial Schema
-- Version: 20250101_000000
-- Description: 创建初始数据库结构
-- Created: 2025-01-01 00:00:00
-- ==========================================
-- UP Migration (执行迁移)
-- ==========================================
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
salt VARCHAR(32) NOT NULL,
role ENUM('admin', 'clinic_user') NOT NULL DEFAULT 'clinic_user',
clinic_id VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_clinic_id (clinic_id),
INDEX idx_role (role)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建患者表
CREATE TABLE IF NOT EXISTS patients (
id INT AUTO_INCREMENT PRIMARY KEY,
case_number VARCHAR(50) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
age INT,
gender ENUM('男', '女', '未知') DEFAULT '未知',
phone VARCHAR(20),
clinic_name VARCHAR(100),
last_visit_date DATE,
last_doctor VARCHAR(100),
diagnosis TEXT,
missed_diagnosis TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_case_number (case_number),
INDEX idx_name (name),
INDEX idx_clinic_name (clinic_name),
INDEX idx_last_visit_date (last_visit_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建回访记录表
CREATE TABLE IF NOT EXISTS callback_records (
id INT AUTO_INCREMENT PRIMARY KEY,
case_number VARCHAR(50) NOT NULL,
callback_methods TEXT,
callback_result VARCHAR(50),
callback_record TEXT,
operator VARCHAR(100),
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_case_number (case_number),
INDEX idx_callback_result (callback_result),
INDEX idx_operator (operator),
INDEX idx_create_time (create_time),
FOREIGN KEY (case_number) REFERENCES patients(case_number) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ==========================================
-- DOWN Migration (回滚迁移)
-- ==========================================
-- 删除回访记录表
DROP TABLE IF EXISTS callback_records;
-- 删除患者表
DROP TABLE IF EXISTS patients;
-- 删除用户表
DROP TABLE IF EXISTS users;
-- ==========================================
-- 验证脚本 (可选)
-- ==========================================
-- 验证表是否创建成功
-- SELECT COUNT(*) FROM information_schema.tables
-- WHERE table_schema = DATABASE() AND table_name IN ('users', 'patients', 'callback_records');
-- Migration: Add User Email
-- Version: 20250102_120000
-- Description: 为用户表添加邮箱字段和相关索引
-- Created: 2025-01-02 12:00:00
-- ==========================================
-- UP Migration (执行迁移)
-- ==========================================
-- 添加邮箱字段
ALTER TABLE users
ADD COLUMN email VARCHAR(255) NULL AFTER username;
-- 添加邮箱唯一索引
ALTER TABLE users
ADD UNIQUE INDEX idx_users_email (email);
-- 添加邮箱验证状态字段
ALTER TABLE users
ADD COLUMN email_verified BOOLEAN DEFAULT FALSE AFTER email;
-- 添加邮箱验证时间字段
ALTER TABLE users
ADD COLUMN email_verified_at TIMESTAMP NULL AFTER email_verified;
-- ==========================================
-- DOWN Migration (回滚迁移)
-- ==========================================
-- 删除邮箱验证时间字段
ALTER TABLE users
DROP COLUMN email_verified_at;
-- 删除邮箱验证状态字段
ALTER TABLE users
DROP COLUMN email_verified;
-- 删除邮箱索引
ALTER TABLE users
DROP INDEX idx_users_email;
-- 删除邮箱字段
ALTER TABLE users
DROP COLUMN email;
-- ==========================================
-- 验证脚本 (可选)
-- ==========================================
-- 验证邮箱字段是否添加成功
-- SELECT COUNT(*) FROM information_schema.columns
-- WHERE table_schema = DATABASE()
-- AND table_name = 'users'
-- AND column_name IN ('email', 'email_verified', 'email_verified_at');
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
安全的患者数据导入脚本
不删除现有数据,只添加或更新缺失的数据
确保生产环境数据安全
"""
import os
import json
import pymysql
from datetime import datetime
# 数据库配置 - 修复为Docker容器内的正确配置
DB_CONFIG = {
'host': 'mysql', # 使用Docker服务名
'port': 3306, # 使用标准MySQL端口
'user': 'root', # 使用root用户
'password': 'dev_password_123', # 使用正确的root密码
'database': 'callback_system',
'charset': 'utf8mb4'
}
def connect_database():
"""连接数据库"""
try:
connection = pymysql.connect(**DB_CONFIG)
print("✅ 数据库连接成功")
return connection
except Exception as e:
print(f"❌ 数据库连接失败: {e}")
return None
def backup_existing_data(connection):
"""备份现有患者数据"""
try:
with connection.cursor() as cursor:
# 检查现有数据
cursor.execute("SELECT COUNT(*) FROM patients")
existing_count = cursor.fetchone()[0]
print(f"📊 现有患者数据: {existing_count} 条")
if existing_count > 0:
# 备份现有数据到临时表
backup_table = f"patients_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
cursor.execute(f"CREATE TABLE {backup_table} AS SELECT * FROM patients")
connection.commit()
print(f"✅ 现有数据已备份到表: {backup_table}")
# 显示现有数据的诊所分布
cursor.execute("SELECT clinic_name, COUNT(*) as count FROM patients GROUP BY clinic_name ORDER BY count DESC")
clinic_distribution = cursor.fetchall()
print("📋 现有数据诊所分布:")
for clinic_name, count in clinic_distribution:
print(f" {clinic_name}: {count} 人")
return existing_count
except Exception as e:
print(f"❌ 备份数据失败: {e}")
connection.rollback()
return 0
def ensure_patients_table(connection):
"""确保patients表存在,如果不存在则创建"""
try:
with connection.cursor() as cursor:
# 检查表是否存在
cursor.execute("SHOW TABLES LIKE 'patients'")
table_exists = cursor.fetchone()
if not table_exists:
print("📝 patients表不存在,正在创建...")
create_table_sql = """
CREATE TABLE patients (
patient_id INT AUTO_INCREMENT PRIMARY KEY,
case_number VARCHAR(50) UNIQUE NOT NULL,
patient_name VARCHAR(100),
patient_phone VARCHAR(20),
gender VARCHAR(10),
age INT,
clinic_name VARCHAR(100) NOT NULL,
diagnosis JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_case_number (case_number),
INDEX idx_clinic_name (clinic_name),
INDEX idx_patient_name (patient_name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
"""
cursor.execute(create_table_sql)
connection.commit()
print("✅ patients表创建成功")
else:
print("✅ patients表已存在")
except Exception as e:
print(f"❌ 创建patients表失败: {e}")
connection.rollback()
def safe_import_clinic_patients(connection, clinic_name, json_file_path):
"""安全导入单个门诊的患者数据"""
try:
if not os.path.exists(json_file_path):
print(f"⚠️ JSON文件不存在: {json_file_path}")
return 0, 0, 0
with open(json_file_path, 'r', encoding='utf-8') as f:
patients_data = json.load(f)
if not isinstance(patients_data, list):
print(f"⚠️ JSON文件格式错误: {json_file_path}")
return 0, 0, 0
added_count = 0
updated_count = 0
skipped_count = 0
with connection.cursor() as cursor:
for patient in patients_data:
try:
# 提取患者信息(使用中文字段名)
case_number = patient.get('病历号', '')
patient_name = patient.get('姓名', '')
patient_phone = '' # JSON中没有电话字段
gender = patient.get('性别', '')
age = patient.get('年龄')
diagnosis = {
'医生诊断': patient.get('医生诊断', ''),
'上次就诊诊断': patient.get('上次就诊诊断', ''),
'漏诊项': patient.get('漏诊项', ''),
'缺失牙': patient.get('缺失牙', []),
'牙槽骨吸收': patient.get('牙槽骨吸收', ''),
'恒牙萌出空间不足': patient.get('恒牙萌出空间不足', '')
}
if not case_number:
skipped_count += 1
continue
# 检查患者是否已存在
cursor.execute("SELECT patient_id FROM patients WHERE case_number = %s", (case_number,))
existing_patient = cursor.fetchone()
if existing_patient:
# 患者已存在,更新信息(保留created_at)
update_sql = """
UPDATE patients SET
patient_name = %s,
patient_phone = %s,
gender = %s,
age = %s,
clinic_name = %s,
diagnosis = %s,
updated_at = CURRENT_TIMESTAMP
WHERE case_number = %s
"""
cursor.execute(update_sql, (
patient_name,
patient_phone,
gender,
age,
clinic_name,
json.dumps(diagnosis, ensure_ascii=False),
case_number
))
updated_count += 1
else:
# 患者不存在,插入新记录
insert_sql = """
INSERT INTO patients (case_number, patient_name, patient_phone, gender, age, clinic_name, diagnosis)
VALUES (%s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_sql, (
case_number,
patient_name,
patient_phone,
gender,
age,
clinic_name,
json.dumps(diagnosis, ensure_ascii=False)
))
added_count += 1
except Exception as e:
print(f"⚠️ 处理患者 {case_number} 失败: {e}")
skipped_count += 1
continue
connection.commit()
print(f"✅ {clinic_name}: 新增 {added_count} 个患者,更新 {updated_count} 个患者,跳过 {skipped_count} 个患者")
return added_count, updated_count, skipped_count
except Exception as e:
print(f"❌ 导入 {clinic_name} 患者数据失败: {e}")
connection.rollback()
return 0, 0, 0
def verify_import_results(connection):
"""验证导入结果"""
try:
with connection.cursor() as cursor:
# 统计总患者数
cursor.execute("SELECT COUNT(*) as total FROM patients")
total_patients = cursor.fetchone()[0]
# 统计各诊所患者分布
cursor.execute("SELECT clinic_name, COUNT(*) as count FROM patients GROUP BY clinic_name ORDER BY count DESC")
clinic_counts = cursor.fetchall()
# 检查是否有重复的病历号
cursor.execute("SELECT case_number, COUNT(*) as count FROM patients GROUP BY case_number HAVING count > 1")
duplicates = cursor.fetchall()
print(f"\n🎉 导入完成!")
print(f"📊 总患者数: {total_patients}")
print(f"📋 各门诊患者分布:")
for clinic_name, count in clinic_counts:
print(f" {clinic_name}: {count} 人")
if duplicates:
print(f"⚠️ 发现重复病历号: {len(duplicates)} 个")
for case_number, count in duplicates:
print(f" {case_number}: {count} 次")
else:
print("✅ 无重复病历号")
except Exception as e:
print(f"❌ 验证结果失败: {e}")
def main():
"""主函数"""
print("🚀 开始安全导入患者数据...")
print("⚠️ 注意:此脚本不会删除任何现有数据!")
# 连接数据库
connection = connect_database()
if not connection:
return
try:
# 备份现有数据
existing_count = backup_existing_data(connection)
# 确保patients表存在
ensure_patients_table(connection)
# 定义门诊和对应的JSON文件
clinics = [
('学前街门诊', '学前街门诊.json'), # 根目录文件
('大丰门诊', '诊所患者json/大丰门诊.json'),
('东亭门诊', '诊所患者json/东亭门诊.json'),
('河埒门诊', '诊所患者json/河埒门诊.json'),
('红豆门诊', '诊所患者json/红豆门诊.json'),
('惠山门诊', '诊所患者json/惠山门诊.json'),
('马山门诊', '诊所患者json/马山门诊.json'),
('通善口腔医院', '诊所患者json/通善口腔医院.json'),
('新吴门诊', '诊所患者json/新吴门诊.json')
]
total_added = 0
total_updated = 0
total_skipped = 0
# 导入每个门诊的患者数据
for clinic_name, json_file in clinics:
print(f"\n📋 正在处理 {clinic_name}...")
added, updated, skipped = safe_import_clinic_patients(connection, clinic_name, json_file)
total_added += added
total_updated += updated
total_skipped += skipped
# 验证导入结果
verify_import_results(connection)
print(f"\n📈 导入统计:")
print(f" 新增患者: {total_added}")
print(f" 更新患者: {total_updated}")
print(f" 跳过患者: {total_skipped}")
print(f" 原有患者: {existing_count}")
if existing_count > 0:
print(f"\n✅ 所有原有数据已保留!")
print(f"📋 数据已备份到临时表中")
except Exception as e:
print(f"❌ 导入过程出错: {e}")
connection.rollback()
finally:
connection.close()
print("\n✅ 数据库连接已关闭")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
迁移系统设置脚本
用于从旧的数据库管理方式迁移到新的迁移管理系统
"""
import os
import shutil
import subprocess
from datetime import datetime
def setup_migration_system():
"""设置迁移系统"""
print("🚀 开始设置数据库迁移管理系统...")
# 1. 创建迁移目录
print("📁 创建迁移目录...")
os.makedirs("migrations", exist_ok=True)
print("✅ 迁移目录创建完成")
# 2. 备份现有SQL文件
print("📦 备份现有SQL文件...")
sql_files = [
"init.sql",
"create_users.sql",
"create_clinic_users.sql",
"fix_admin_user.sql",
"fix_passwords.sql",
"update_password_hashes.sql",
"update_users_with_salt.sql",
"complete_hashes.sql"
]
backup_dir = f"sql_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
os.makedirs(backup_dir, exist_ok=True)
for sql_file in sql_files:
if os.path.exists(sql_file):
shutil.copy2(sql_file, backup_dir)
print(f" ✅ 备份: {sql_file}")
print(f"📦 SQL文件备份完成: {backup_dir}")
# 3. 设置权限
print("🔧 设置脚本权限...")
scripts = ["migrate.py", "deploy_scripts/deploy_with_migration.sh"]
for script in scripts:
if os.path.exists(script):
os.chmod(script, 0o755)
print(f" ✅ 设置权限: {script}")
# 4. 验证Python依赖
print("🐍 检查Python依赖...")
try:
import pymysql
print(" ✅ pymysql 已安装")
except ImportError:
print(" ❌ pymysql 未安装,请运行: pip install pymysql")
# 5. 测试数据库连接
print("🗄️ 测试数据库连接...")
try:
from database_config import get_database_config
from database_migration_manager import DatabaseMigrationManager
db_config = get_database_config()
manager = DatabaseMigrationManager(db_config)
print(" ✅ 数据库连接测试成功")
print(" ✅ 迁移历史表初始化完成")
except Exception as e:
print(f" ❌ 数据库连接失败: {e}")
print(" 请检查数据库配置和连接")
# 6. 创建示例迁移
print("📝 创建示例迁移文件...")
if not os.path.exists("migrations/20250101_000000_initial_schema.sql"):
print(" ✅ 初始架构迁移文件已存在")
else:
print(" ✅ 示例迁移文件已创建")
# 7. 更新部署脚本权限
print("🚀 更新部署脚本...")
deploy_scripts = [
"deploy_scripts/deploy_with_backup.sh",
"deploy_scripts/deploy_with_migration.sh"
]
for script in deploy_scripts:
if os.path.exists(script):
os.chmod(script, 0o755)
print(f" ✅ 更新权限: {script}")
print("\n🎉 迁移系统设置完成!")
print("\n📋 下一步操作:")
print("1. 检查并更新 database_config.py 中的数据库配置")
print("2. 运行 'python migrate.py status' 检查系统状态")
print("3. 如需要,创建新的迁移文件: 'python migrate.py create \"migration_name\"'")
print("4. 在测试环境验证迁移: 'python migrate.py migrate --dry-run'")
print("5. 更新 CI/CD 配置: 将 .gitlab-ci-new.yml 重命名为 .gitlab-ci.yml")
print("\n📖 详细使用说明请参考: DATABASE_MIGRATION_GUIDE.md")
def cleanup_old_system():
"""清理旧系统文件(可选)"""
print("\n🧹 清理旧系统文件...")
# 询问是否清理
response = input("是否要清理旧的SQL文件?(y/N): ").lower().strip()
if response not in ['y', 'yes']:
print("跳过清理步骤")
return
# 移动旧文件到归档目录
archive_dir = f"legacy_sql_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
os.makedirs(archive_dir, exist_ok=True)
old_files = [
"fix_admin_user.sql",
"fix_passwords.sql",
"update_password_hashes.sql",
"update_users_with_salt.sql",
"complete_hashes.sql"
]
for old_file in old_files:
if os.path.exists(old_file):
shutil.move(old_file, archive_dir)
print(f" ✅ 归档: {old_file}")
print(f"📦 旧文件已归档到: {archive_dir}")
def show_migration_status():
"""显示迁移状态"""
print("\n📊 当前迁移状态:")
try:
result = subprocess.run(
["python", "migrate.py", "status"],
capture_output=True,
text=True
)
print(result.stdout)
if result.stderr:
print("错误信息:", result.stderr)
except Exception as e:
print(f"无法获取迁移状态: {e}")
def main():
"""主函数"""
print("=" * 60)
print("🗄️ 数据库迁移管理系统设置向导")
print("=" * 60)
# 检查当前目录
if not os.path.exists("app.py"):
print("❌ 请在项目根目录运行此脚本")
return 1
# 设置迁移系统
setup_migration_system()
# 显示当前状态
show_migration_status()
# 询问是否清理旧系统
cleanup_old_system()
print("\n✅ 设置完成!迁移系统已就绪。")
return 0
if __name__ == "__main__":
exit(main())
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化认证服务器
不依赖数据库,使用内存存储,提供基本的登录功能
"""
import http.server
import socketserver
import json
import urllib.parse
from datetime import datetime, timedelta
import hashlib
import secrets
import os
class SimpleUser:
"""简化用户类"""
def __init__(self, username, password, role='user'):
self.username = username
self.password = self._hash_password(password)
self.role = role
self.created_at = datetime.now()
self.last_login = None
def _hash_password(self, password):
"""简单的密码哈希"""
return hashlib.sha256(password.encode()).hexdigest()
def verify_password(self, password):
"""验证密码"""
return self.password == self._hash_password(password)
def to_dict(self):
"""转换为字典"""
return {
'username': self.username,
'role': self.role,
'created_at': self.created_at.isoformat(),
'last_login': self.last_login.isoformat() if self.last_login else None
}
class SimpleSession:
"""简化会话类"""
def __init__(self, user, session_id):
self.user = user
self.session_id = session_id
self.created_at = datetime.now()
self.expires_at = self.created_at + timedelta(hours=24)
self.last_activity = datetime.now()
def is_expired(self):
"""检查是否过期"""
return datetime.now() > self.expires_at
def to_dict(self):
"""转换为字典"""
return {
'session_id': self.session_id,
'username': self.user.username,
'role': self.user.role,
'created_at': self.created_at.isoformat(),
'expires_at': self.expires_at.isoformat(),
'last_activity': self.last_activity.isoformat()
}
class SimpleAuthHandler(http.server.SimpleHTTPRequestHandler):
"""简化认证处理器"""
# 内存存储
users = {}
sessions = {}
def __init__(self, *args, **kwargs):
# 创建默认用户
if not self.users:
self.users['admin'] = SimpleUser('admin', 'admin123', 'admin')
self.users['user'] = SimpleUser('user', 'user123', 'user')
print("✅ 默认用户已创建:")
print(" 管理员: admin / admin123")
print(" 普通用户: user / user123")
super().__init__(*args, **kwargs)
def do_GET(self):
"""处理GET请求"""
if self.path.startswith('/api/'):
self.handle_api_get()
elif self.path == '/':
# 重定向到患者画像索引页
self.send_response(302)
self.send_header('Location', '/patient_profiles/')
self.end_headers()
elif self.path == '/login':
self.serve_file('login.html')
elif self.path == '/dashboard.html':
self.serve_file('dashboard.html')
elif self.path == '/user_management.html':
self.serve_file('user_management.html')
elif self.path == '/auth_client.js':
self.serve_file('auth_client.js')
elif self.path == '/test':
self.serve_file('test_server.html')
elif self.path == '/patient_profiles/':
self.serve_file('patient_profiles/index.html')
elif self.path.startswith('/patient_profiles/'):
filename = self.path[17:] # 去掉 '/patient_profiles/'
self.serve_file(f'patient_profiles/{filename}')
else:
super().do_GET()
def do_POST(self):
"""处理POST请求"""
if self.path.startswith('/api/'):
self.handle_api_post()
else:
self.send_error(404, "Not Found")
def do_OPTIONS(self):
"""处理OPTIONS请求(CORS预检)"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Accept, X-Session-ID')
self.end_headers()
def serve_file(self, filepath):
"""提供文件服务"""
try:
if os.path.exists(filepath):
with open(filepath, 'rb') as f:
content = f.read()
# 根据文件扩展名设置Content-Type
if filepath.endswith('.html'):
content_type = 'text/html; charset=utf-8'
elif filepath.endswith('.js'):
content_type = 'application/javascript; charset=utf-8'
elif filepath.endswith('.css'):
content_type = 'text/css; charset=utf-8'
else:
content_type = 'application/octet-stream'
self.send_response(200)
self.send_header('Content-Type', content_type)
self.send_header('Content-Length', str(len(content)))
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(content)
else:
self.send_error(404, f"File not found: {filepath}")
except Exception as e:
print(f"Error serving file {filepath}: {e}")
self.send_error(500, f"Internal server error")
def handle_api_get(self):
"""处理API GET请求"""
if self.path == '/api/health':
self.send_json_response({
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'services': {
'user_manager': True,
'session_manager': True,
'callback_db': False
}
})
elif self.path == '/api/auth/sessions':
# 获取活跃会话(需要管理员权限)
session = self.get_session_from_request()
if session and session.user.role == 'admin':
active_sessions = [s.to_dict() for s in self.sessions.values() if not s.is_expired()]
self.send_json_response({
'success': True,
'sessions': active_sessions,
'count': len(active_sessions)
})
else:
self.send_json_response({'success': False, 'message': '需要管理员权限'}, 403)
elif self.path == '/api/auth/admin/users':
# 获取用户列表(需要管理员权限)
session = self.get_session_from_request()
if session and session.user.role == 'admin':
users_list = [user.to_dict() for user in self.users.values()]
self.send_json_response({
'success': True,
'users': users_list,
'count': len(users_list)
})
else:
self.send_json_response({'success': False, 'message': '需要管理员权限'}, 403)
else:
self.send_error(404, "API endpoint not found")
def handle_api_post(self):
"""处理API POST请求"""
try:
content_length = int(self.headers.get('Content-Length', 0))
if content_length > 0:
post_data = self.rfile.read(content_length)
data = json.loads(post_data.decode('utf-8'))
else:
data = {}
except:
data = {}
if self.path == '/api/auth/login':
self.handle_login(data)
elif self.path == '/api/auth/logout':
self.handle_logout(data)
elif self.path == '/api/auth/validate':
self.handle_validate(data)
elif self.path == '/api/auth/user/change-password':
self.handle_change_password(data)
elif self.path == '/api/auth/admin/users':
self.handle_create_user(data)
else:
self.send_error(404, "API endpoint not found")
def handle_login(self, data):
"""处理登录"""
username = data.get('username', '').strip()
password = data.get('password', '')
if not username or not password:
self.send_json_response({
'success': False,
'message': '用户名和密码不能为空'
}, 400)
return
user = self.users.get(username)
if not user or not user.verify_password(password):
self.send_json_response({
'success': False,
'message': '用户名或密码错误'
}, 401)
return
# 创建会话
session_id = self.generate_session_id()
session = SimpleSession(user, session_id)
self.sessions[session_id] = session
# 更新最后登录时间
user.last_login = datetime.now()
print(f"用户登录: {username}")
self.send_json_response({
'success': True,
'message': '登录成功',
'session_id': session_id,
'user': user.to_dict(),
'expires_at': session.expires_at.isoformat()
})
def handle_logout(self, data):
"""处理注销"""
session = self.get_session_from_request()
if session:
if session.session_id in self.sessions:
del self.sessions[session.session_id]
print(f"用户注销: {session.user.username}")
self.send_json_response({
'success': True,
'message': '注销成功'
})
def handle_validate(self, data):
"""处理会话验证"""
session_id = data.get('session_id')
if not session_id:
self.send_json_response({
'success': False,
'message': '缺少会话ID'
}, 400)
return
session = self.sessions.get(session_id)
if session and not session.is_expired():
session.last_activity = datetime.now()
self.send_json_response({
'success': True,
'session': session.to_dict(),
'user': session.user.to_dict()
})
else:
if session:
del self.sessions[session_id]
self.send_json_response({
'success': False,
'message': '会话无效或已过期'
}, 401)
def handle_change_password(self, data):
"""处理密码修改"""
session = self.get_session_from_request()
if not session:
self.send_json_response({
'success': False,
'message': '需要登录'
}, 401)
return
old_password = data.get('old_password', '')
new_password = data.get('new_password', '')
if not old_password or not new_password:
self.send_json_response({
'success': False,
'message': '缺少必需参数'
}, 400)
return
if len(new_password) < 6:
self.send_json_response({
'success': False,
'message': '新密码长度至少6位'
}, 400)
return
user = session.user
if not user.verify_password(old_password):
self.send_json_response({
'success': False,
'message': '原密码错误'
}, 400)
return
# 修改密码
user.password = user._hash_password(new_password)
# 清除所有会话
user_sessions = [sid for sid, s in self.sessions.items() if s.user.username == user.username]
for sid in user_sessions:
del self.sessions[sid]
print(f"用户修改密码: {user.username}")
self.send_json_response({
'success': True,
'message': '密码修改成功,请重新登录'
})
def handle_create_user(self, data):
"""处理创建用户(管理员功能)"""
session = self.get_session_from_request()
if not session or session.user.role != 'admin':
self.send_json_response({
'success': False,
'message': '需要管理员权限'
}, 403)
return
username = data.get('username', '').strip()
password = data.get('password', '')
role = data.get('role', 'user')
if not username or not password:
self.send_json_response({
'success': False,
'message': '用户名和密码不能为空'
}, 400)
return
if username in self.users:
self.send_json_response({
'success': False,
'message': '用户名已存在'
}, 400)
return
if role not in ['admin', 'user']:
self.send_json_response({
'success': False,
'message': '无效的用户角色'
}, 400)
return
# 创建用户
new_user = SimpleUser(username, password, role)
self.users[username] = new_user
print(f"创建新用户: {username} ({role})")
self.send_json_response({
'success': True,
'message': '用户创建成功',
'user': new_user.to_dict()
})
def get_session_from_request(self):
"""从请求中获取会话"""
session_id = self.headers.get('X-Session-ID')
if session_id:
session = self.sessions.get(session_id)
if session and not session.is_expired():
session.last_activity = datetime.now()
return session
elif session:
del self.sessions[session_id]
return None
def generate_session_id(self):
"""生成会话ID"""
return secrets.token_hex(32)
def send_json_response(self, data, status_code=200):
"""发送JSON响应"""
response = json.dumps(data, ensure_ascii=False).encode('utf-8')
self.send_response(status_code)
self.send_header('Content-Type', 'application/json; charset=utf-8')
self.send_header('Content-Length', str(len(response)))
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Accept, X-Session-ID')
self.end_headers()
self.wfile.write(response)
def main():
"""启动服务器"""
PORT = 8080
print("=" * 60)
print(" 简化认证服务器")
print("=" * 60)
print()
print("🚀 启动服务器...")
print(f"📡 服务器地址: http://localhost:{PORT}")
print()
print("📱 访问地址:")
print(f" 🔐 登录页面:http://localhost:{PORT}/login")
print(f" 📋 患者索引页:http://localhost:{PORT}/patient_profiles/")
print(f" 📊 系统仪表盘:http://localhost:{PORT}/dashboard.html")
print(f" 👥 用户管理:http://localhost:{PORT}/user_management.html")
print()
print("👤 默认账户:")
print(" 管理员: admin / admin123")
print(" 普通用户: user / user123")
print()
print("📝 注意事项:")
print(" 1. 这是简化版本,使用内存存储")
print(" 2. 服务器重启后数据会丢失")
print(" 3. 请登录后立即修改密码")
print()
print("按 Ctrl+C 停止服务器")
print("=" * 60)
try:
with socketserver.TCPServer(("", PORT), SimpleAuthHandler) as httpd:
print(f"✅ 服务器已启动在端口 {PORT}")
httpd.serve_forever()
except KeyboardInterrupt:
print("\n👋 服务器已停止")
except Exception as e:
print(f"❌ 服务器启动失败: {e}")
if __name__ == "__main__":
main()
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简单的HTTP服务器
提供患者画像页面访问和回访记录API服务
"""
import http.server
import socketserver
import json
import urllib.parse
from datetime import datetime
import os
import sys
# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
try:
from callback_record_model import CallbackRecordManager, CallbackRecord
DB_AVAILABLE = True
except ImportError:
DB_AVAILABLE = False
print("警告:数据库模块不可用,将使用文件存储")
class CallbackHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
# 初始化数据库管理器
if DB_AVAILABLE:
try:
self.db_manager = CallbackRecordManager()
print("✅ 数据库初始化成功")
except Exception as e:
print(f"❌ 数据库初始化失败: {e}")
self.db_manager = None
else:
self.db_manager = None
super().__init__(*args, **kwargs)
def do_GET(self):
"""处理GET请求"""
if self.path.startswith('/api/'):
self.handle_api_get()
else:
# 处理静态文件请求
super().do_GET()
def do_POST(self):
"""处理POST请求"""
if self.path.startswith('/api/'):
self.handle_api_post()
else:
self.send_error(404, "Not Found")
def handle_api_get(self):
"""处理API GET请求"""
if self.path == '/api/health':
self.send_json_response({
'status': 'ok',
'timestamp': datetime.now().isoformat(),
'database': 'connected' if self.db_manager else 'disconnected'
})
elif self.path.startswith('/api/callback-records/'):
# 获取回访记录
case_number = self.path.split('/')[-1]
if self.db_manager:
try:
records = self.db_manager.get_records_by_case_number(case_number)
self.send_json_response({
'success': True,
'data': [record.to_dict() for record in records],
'count': len(records)
})
except Exception as e:
self.send_json_response({
'success': False,
'message': str(e)
}, 500)
else:
self.send_json_response({
'success': False,
'message': '数据库不可用'
}, 500)
else:
self.send_error(404, "API endpoint not found")
def handle_api_post(self):
"""处理API POST请求"""
if self.path == '/api/callback-records':
self.save_callback_record()
else:
self.send_error(404, "API endpoint not found")
def save_callback_record(self):
"""保存回访记录"""
try:
# 读取请求数据
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
data = json.loads(post_data.decode('utf-8'))
# 验证必需字段
required_fields = ['caseNumber', 'callbackMethods', 'callbackRecord', 'operator']
for field in required_fields:
if field not in data or not data[field]:
self.send_json_response({
'success': False,
'message': f'缺少必需字段: {field}'
}, 400)
return
if self.db_manager:
# 保存到数据库
record = CallbackRecord(
case_number=data['caseNumber'],
callback_methods=data['callbackMethods'],
callback_record=data['callbackRecord'],
operator=data['operator']
)
record_id = self.db_manager.save_record(record)
self.send_json_response({
'success': True,
'id': record_id,
'message': '保存成功',
'timestamp': datetime.now().isoformat()
})
else:
# 保存到文件
self.save_to_file(data)
self.send_json_response({
'success': True,
'id': int(datetime.now().timestamp()),
'message': '保存成功(文件存储)',
'timestamp': datetime.now().isoformat()
})
except json.JSONDecodeError:
self.send_json_response({
'success': False,
'message': '无效的JSON数据'
}, 400)
except Exception as e:
print(f"保存回访记录失败: {e}")
self.send_json_response({
'success': False,
'message': f'保存失败: {str(e)}'
}, 500)
def save_to_file(self, data):
"""保存到文件(备用方案)"""
filename = 'callback_records_backup.json'
records = []
# 读取现有记录
if os.path.exists(filename):
try:
with open(filename, 'r', encoding='utf-8') as f:
records = json.load(f)
except:
records = []
# 添加新记录
data['id'] = int(datetime.now().timestamp())
data['create_time'] = datetime.now().isoformat()
records.append(data)
# 保存到文件
with open(filename, 'w', encoding='utf-8') as f:
json.dump(records, f, ensure_ascii=False, indent=2)
print(f"记录已保存到文件: {filename}")
def send_json_response(self, data, status_code=200):
"""发送JSON响应"""
response = json.dumps(data, ensure_ascii=False).encode('utf-8')
self.send_response(status_code)
self.send_header('Content-Type', 'application/json; charset=utf-8')
self.send_header('Content-Length', str(len(response)))
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Accept')
self.end_headers()
self.wfile.write(response)
def do_OPTIONS(self):
"""处理OPTIONS请求(CORS预检)"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Accept')
self.end_headers()
def main():
"""启动服务器"""
PORT = 8000
print("🚀 启动简单HTTP服务器...")
print(f"📡 服务器地址: http://localhost:{PORT}")
print(f"📁 患者画像: http://localhost:{PORT}/patient_profiles/")
print(f"🔧 API接口: http://localhost:{PORT}/api/")
print(f"📋 测试页面: http://localhost:{PORT}/patient_profiles/TS0K036558.html")
print("=" * 60)
try:
with socketserver.TCPServer(("", PORT), CallbackHTTPRequestHandler) as httpd:
print(f"✅ 服务器已启动在端口 {PORT}")
print("按 Ctrl+C 停止服务器")
httpd.serve_forever()
except KeyboardInterrupt:
print("\n👋 服务器已停止")
except Exception as e:
print(f"❌ 服务器启动失败: {e}")
if __name__ == "__main__":
main()
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
认证服务器启动脚本
自动检查环境、安装依赖、初始化数据库并启动服务器
"""
import os
import sys
import subprocess
import time
from pathlib import Path
def print_banner():
"""显示启动横幅"""
print("=" * 60)
print(" 回访记录系统 - 认证服务器启动器")
print("=" * 60)
print()
def check_python_version():
"""检查Python版本"""
print("🐍 检查Python版本...")
if sys.version_info < (3, 7):
print("❌ 错误:需要Python 3.7或更高版本")
print(f" 当前版本:{sys.version}")
return False
print(f"✅ Python版本检查通过:{sys.version.split()[0]}")
return True
def check_dependencies():
"""检查并安装依赖包"""
print("\n📦 检查依赖包...")
required_packages = [
'pymysql',
'flask',
'flask-cors',
'configparser'
]
missing_packages = []
for package in required_packages:
try:
__import__(package.replace('-', '_'))
print(f"✅ {package} 已安装")
except ImportError:
missing_packages.append(package)
print(f"❌ {package} 未安装")
if missing_packages:
print(f"\n🔧 安装缺失的依赖包:{', '.join(missing_packages)}")
try:
subprocess.check_call([
sys.executable, '-m', 'pip', 'install'
] + missing_packages)
print("✅ 依赖包安装完成")
except subprocess.CalledProcessError as e:
print(f"❌ 依赖包安装失败:{e}")
print("\n请手动安装依赖包:")
print(f"pip install {' '.join(missing_packages)}")
return False
return True
def check_database_config():
"""检查数据库配置"""
print("\n🗄️ 检查数据库配置...")
config_file = Path("database_config.ini")
if not config_file.exists():
print("⚠️ 数据库配置文件不存在,将创建默认配置")
try:
from database_config import DatabaseConfig
config_manager = DatabaseConfig()
print("✅ 默认数据库配置已创建")
print("📝 请编辑 database_config.ini 文件,配置您的MySQL连接信息")
return False
except Exception as e:
print(f"❌ 创建数据库配置失败:{e}")
return False
try:
from database_config import DatabaseConfig
config_manager = DatabaseConfig()
if config_manager.validate_config():
print("✅ 数据库配置验证通过")
return True
else:
print("❌ 数据库配置无效")
print("📝 请编辑 database_config.ini 文件,检查配置项")
return False
except Exception as e:
print(f"❌ 数据库配置检查失败:{e}")
return False
def test_database_connection():
"""测试数据库连接"""
print("\n🔗 测试数据库连接...")
try:
from user_manager import create_user_manager
user_manager = create_user_manager()
if user_manager and user_manager.test_connection():
print("✅ 数据库连接测试成功")
return True
else:
print("❌ 数据库连接测试失败")
print("\n请检查:")
print("1. MySQL服务是否正在运行")
print("2. 数据库配置是否正确")
print("3. 数据库用户是否有足够权限")
print("4. 防火墙是否允许连接")
return False
except Exception as e:
print(f"❌ 数据库连接测试失败:{e}")
return False
def check_required_files():
"""检查必需的文件"""
print("\n📁 检查必需文件...")
required_files = [
'auth_server.py',
'user_manager.py',
'session_manager.py',
'database_config.py',
'login.html',
'auth_client.js'
]
missing_files = []
for file_path in required_files:
if not Path(file_path).exists():
missing_files.append(file_path)
print(f"❌ {file_path} 文件缺失")
else:
print(f"✅ {file_path} 文件存在")
if missing_files:
print(f"\n❌ 缺失必需文件:{', '.join(missing_files)}")
return False
return True
def start_server():
"""启动认证服务器"""
print("\n🚀 启动认证服务器...")
try:
# 导入并启动服务器
from auth_server import main
main()
except KeyboardInterrupt:
print("\n👋 服务器已停止")
except Exception as e:
print(f"❌ 服务器启动失败:{e}")
return False
return True
def show_usage_info():
"""显示使用说明"""
print("\n" + "=" * 60)
print("🎉 系统已准备就绪!")
print("=" * 60)
print()
print("📱 访问地址:")
print(" 🔐 登录页面:http://localhost:5000/login")
print(" 📋 患者索引页:http://localhost:5000/patient_profiles/ (登录后默认页面)")
print(" 📊 系统仪表盘:http://localhost:5000/dashboard.html")
print(" 👥 用户管理:http://localhost:5000/user_management.html")
print()
print("👤 默认管理员账户:")
print(" 用户名:admin")
print(" 密码:admin123")
print(" ⚠️ 请登录后立即修改密码!")
print()
print("🔧 管理命令:")
print(" 配置数据库:python database_config.py")
print(" 查看数据库:python view_database.py")
print(" 用户管理:python user_manager.py")
print()
print("📝 注意事项:")
print(" 1. 确保MySQL服务正在运行")
print(" 2. 首次使用请配置数据库连接")
print(" 3. 建议定期备份数据库")
print(" 4. 生产环境请修改默认密码")
print()
def main():
"""主函数"""
print_banner()
# 检查Python版本
if not check_python_version():
return False
# 检查依赖包
if not check_dependencies():
return False
# 检查必需文件
if not check_required_files():
return False
# 检查数据库配置
if not check_database_config():
print("\n💡 提示:请按以下步骤配置数据库:")
print(" 1. 启动MySQL服务")
print(" 2. 编辑 database_config.ini 文件")
print(" 3. 重新运行此脚本")
return False
# 测试数据库连接
if not test_database_connection():
return False
# 显示使用说明
show_usage_info()
# 启动服务器
return start_server()
if __name__ == "__main__":
try:
success = main()
if not success:
print("\n❌ 启动失败,请检查上述错误信息")
sys.exit(1)
except KeyboardInterrupt:
print("\n👋 启动已取消")
sys.exit(0)
except Exception as e:
print(f"\n💥 未知错误:{e}")
sys.exit(1)
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
启动SQLite回访记录API服务器
"""
from callback_record_model import create_flask_api
if __name__ == "__main__":
app = create_flask_api()
if app:
print("🚀 启动SQLite回访记录API服务器...")
print("📡 API端点:")
print(" - POST /api/callback-records 保存回访记录")
print(" - GET /api/callback-records/<case> 获取回访记录")
print(" - GET /api/callback-records/statistics 获取统计信息")
print("🌐 服务器地址: http://localhost:5000")
print("按 Ctrl+C 停止服务器")
print("-" * 50)
app.run(host='0.0.0.0', port=5000, debug=True)
else:
print("❌ API服务器启动失败,请检查依赖")
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Docker启动文件
在Docker容器中启动患者画像回访话术系统
"""
import os
import sys
# 添加当前目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# 导入并启动应用
from auth_system import app
if __name__ == "__main__":
# 获取端口配置
port = int(os.environ.get('PORT', 5000))
host = os.environ.get('HOST', '0.0.0.0')
debug = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
print(f"🚀 启动患者画像回访话术系统...")
print(f"📍 监听地址: {host}:{port}")
print(f"🔧 调试模式: {debug}")
# 启动Flask应用
app.run(
host=host,
port=port,
debug=debug,
threaded=True
)
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理 - 回访记录系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 24px;
font-weight: 600;
}
.user-info {
display: flex;
align-items: center;
gap: 15px;
}
.user-info .username {
font-weight: 500;
}
.user-info .role {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
}
.user-info .role.admin {
background: rgba(255,255,255,0.2);
color: #fff;
}
.nav-links {
display: flex;
gap: 15px;
}
.nav-links a {
color: white;
text-decoration: none;
padding: 8px 16px;
border-radius: 6px;
transition: background-color 0.3s;
}
.nav-links a:hover {
background: rgba(255,255,255,0.1);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 30px 20px;
}
.card {
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
padding: 30px;
margin-bottom: 30px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.card-title {
font-size: 20px;
font-weight: 600;
color: #333;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
}
.btn-danger {
background: #e74c3c;
color: white;
}
.btn-danger:hover {
background: #c0392b;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
.users-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.users-table th,
.users-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.users-table th {
background: #f8f9fa;
font-weight: 600;
color: #333;
}
.users-table tr:hover {
background: #f8f9fa;
}
.role-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
}
.role-badge.admin {
background: #e3f2fd;
color: #1976d2;
}
.role-badge.user {
background: #f3e5f5;
color: #7b1fa2;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-badge.active {
background: #e8f5e8;
color: #2e7d32;
}
.status-badge.inactive {
background: #ffebee;
color: #c62828;
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.form-group {
flex: 1;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group select {
width: 100%;
padding: 12px;
border: 2px solid #e1e5e9;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
border-radius: 12px;
padding: 30px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.modal-title {
font-size: 18px;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.close-btn:hover {
color: #333;
}
.modal-footer {
margin-top: 25px;
padding-top: 15px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 10px;
}
.alert {
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
border: 1px solid transparent;
}
.alert-success {
background: #d4edda;
color: #155724;
border-color: #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border-color: #f5c6cb;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border-color: #bee5eb;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #666;
}
.empty-state h3 {
margin-bottom: 10px;
color: #333;
}
.stats-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
text-align: center;
}
.stat-number {
font-size: 36px;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 14px;
}
@media (max-width: 768px) {
.container {
padding: 20px 15px;
}
.card {
padding: 20px;
}
.form-row {
flex-direction: column;
}
.users-table {
font-size: 14px;
}
.users-table th,
.users-table td {
padding: 10px;
}
.stats-row {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<h1>用户管理</h1>
<div class="user-info" id="userInfo"></div>
<div class="nav-links">
<a href="/">返回首页</a>
<a href="/patient_profiles/">患者画像</a>
<a href="#" onclick="authClient.logout()">注销</a>
</div>
</div>
</div>
<div class="container">
<!-- 统计信息 -->
<div class="stats-row" id="statsContainer">
<div class="stat-card">
<div class="stat-number" id="totalUsers">0</div>
<div class="stat-label">总用户数</div>
</div>
<div class="stat-card">
<div class="stat-number" id="adminUsers">0</div>
<div class="stat-label">管理员</div>
</div>
<div class="stat-card">
<div class="stat-number" id="activeUsers">0</div>
<div class="stat-label">活跃用户</div>
</div>
<div class="stat-card">
<div class="stat-number" id="activeSessions">0</div>
<div class="stat-label">在线会话</div>
</div>
</div>
<!-- 用户列表 -->
<div class="card">
<div class="card-header">
<h2 class="card-title">用户列表</h2>
<button class="btn btn-primary" onclick="showCreateUserModal()">
➕ 创建用户
</button>
</div>
<div id="alertContainer"></div>
<div id="usersTableContainer">
<div class="loading">
正在加载用户数据...
</div>
</div>
</div>
<!-- 活跃会话 -->
<div class="card">
<div class="card-header">
<h2 class="card-title">活跃会话</h2>
<button class="btn btn-secondary" onclick="loadActiveSessions()">
🔄 刷新
</button>
</div>
<div id="sessionsTableContainer">
<div class="loading">
正在加载会话数据...
</div>
</div>
</div>
</div>
<!-- 创建用户模态框 -->
<div id="createUserModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">创建用户</h3>
<button class="close-btn" onclick="hideCreateUserModal()">&times;</button>
</div>
<form id="createUserForm">
<div class="form-group">
<label for="newUsername">用户名</label>
<input type="text" id="newUsername" name="username" required maxlength="50"
placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="newPassword">密码</label>
<input type="password" id="newPassword" name="password" required minlength="6"
placeholder="请输入密码(至少6位)">
</div>
<div class="form-group">
<label for="newRole">用户角色</label>
<select id="newRole" name="role" required>
<option value="user">普通用户</option>
<option value="admin">管理员</option>
</select>
</div>
</form>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideCreateUserModal()">取消</button>
<button type="submit" class="btn btn-primary" form="createUserForm" id="createUserBtn">创建用户</button>
</div>
</div>
</div>
<!-- 修改密码模态框 -->
<div id="changePasswordModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">修改我的密码</h3>
<button class="close-btn" onclick="hideChangePasswordModal()">&times;</button>
</div>
<form id="changePasswordForm">
<div class="form-group">
<label for="oldPassword">当前密码</label>
<input type="password" id="oldPassword" name="oldPassword" required
placeholder="请输入当前密码">
</div>
<div class="form-group">
<label for="newPasswordChange">新密码</label>
<input type="password" id="newPasswordChange" name="newPassword" required minlength="6"
placeholder="请输入新密码(至少6位)">
</div>
<div class="form-group">
<label for="confirmPassword">确认新密码</label>
<input type="password" id="confirmPassword" name="confirmPassword" required minlength="6"
placeholder="请再次输入新密码">
</div>
</form>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideChangePasswordModal()">取消</button>
<button type="submit" class="btn btn-primary" form="changePasswordForm" id="changePasswordBtn">修改密码</button>
</div>
</div>
</div>
<!-- 引入认证客户端 -->
<script src="/auth_client.js"></script>
<script>
// 页面加载时检查权限
document.addEventListener('DOMContentLoaded', async function() {
// 要求管理员权限
if (!authClient.requireAdmin()) {
return;
}
// 显示用户信息
authClient.renderUserInfo(document.getElementById('userInfo'));
// 加载数据
await Promise.all([
loadUsers(),
loadActiveSessions(),
loadStats()
]);
});
// 加载统计信息
async function loadStats() {
try {
const [usersResponse, sessionsResponse] = await Promise.all([
authClient.apiRequest('/auth/admin/users', { authenticated: true }),
authClient.apiRequest('/auth/sessions', { authenticated: true })
]);
const users = usersResponse.users || [];
const sessions = sessionsResponse.sessions || [];
document.getElementById('totalUsers').textContent = users.length;
document.getElementById('adminUsers').textContent = users.filter(u => u.role === 'admin').length;
document.getElementById('activeUsers').textContent = users.filter(u => u.is_active).length;
document.getElementById('activeSessions').textContent = sessions.length;
} catch (error) {
console.error('加载统计信息失败:', error);
}
}
// 加载用户列表
async function loadUsers() {
try {
const response = await authClient.apiRequest('/auth/admin/users', {
authenticated: true
});
if (response.success) {
renderUsersTable(response.users);
} else {
showAlert('error', '加载用户列表失败: ' + response.message);
}
} catch (error) {
console.error('加载用户列表失败:', error);
showAlert('error', '加载用户列表失败: ' + error.message);
}
}
// 渲染用户表格
function renderUsersTable(users) {
const container = document.getElementById('usersTableContainer');
if (users.length === 0) {
container.innerHTML = `
<div class="empty-state">
<h3>暂无用户</h3>
<p>点击上方"创建用户"按钮来添加第一个用户</p>
</div>
`;
return;
}
const table = `
<table class="users-table">
<thead>
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>角色</th>
<th>状态</th>
<th>创建时间</th>
<th>最后登录</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${users.map(user => `
<tr>
<td>${user.user_id}</td>
<td>${user.username}</td>
<td><span class="role-badge ${user.role}">${user.role === 'admin' ? '管理员' : '用户'}</span></td>
<td><span class="status-badge ${user.is_active ? 'active' : 'inactive'}">${user.is_active ? '活跃' : '禁用'}</span></td>
<td>${formatDateTime(user.created_at)}</td>
<td>${user.last_login ? formatDateTime(user.last_login) : '从未登录'}</td>
<td>
<button class="btn btn-danger btn-small" onclick="confirmDeleteUser(${user.user_id}, '${user.username}')">
删除
</button>
</td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = table;
}
// 加载活跃会话
async function loadActiveSessions() {
try {
const response = await authClient.apiRequest('/auth/sessions', {
authenticated: true
});
if (response.success) {
renderSessionsTable(response.sessions);
} else {
showAlert('error', '加载会话列表失败: ' + response.message);
}
} catch (error) {
console.error('加载会话列表失败:', error);
showAlert('error', '加载会话列表失败: ' + error.message);
}
}
// 渲染会话表格
function renderSessionsTable(sessions) {
const container = document.getElementById('sessionsTableContainer');
if (sessions.length === 0) {
container.innerHTML = `
<div class="empty-state">
<h3>暂无活跃会话</h3>
<p>当前没有用户在线</p>
</div>
`;
return;
}
const table = `
<table class="users-table">
<thead>
<tr>
<th>用户名</th>
<th>角色</th>
<th>登录时间</th>
<th>最后活动</th>
<th>过期时间</th>
<th>会话ID</th>
</tr>
</thead>
<tbody>
${sessions.map(session => `
<tr>
<td>${session.username}</td>
<td><span class="role-badge ${session.role}">${session.role === 'admin' ? '管理员' : '用户'}</span></td>
<td>${formatDateTime(session.created_at)}</td>
<td>${formatDateTime(session.last_activity)}</td>
<td>${formatDateTime(session.expires_at)}</td>
<td><code>${session.session_id.substring(0, 16)}...</code></td>
</tr>
`).join('')}
</tbody>
</table>
`;
container.innerHTML = table;
}
// 显示创建用户模态框
function showCreateUserModal() {
document.getElementById('createUserModal').style.display = 'block';
document.getElementById('newUsername').focus();
}
// 隐藏创建用户模态框
function hideCreateUserModal() {
document.getElementById('createUserModal').style.display = 'none';
document.getElementById('createUserForm').reset();
}
// 创建用户表单提交
document.getElementById('createUserForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const userData = {
username: formData.get('username').trim(),
password: formData.get('password'),
role: formData.get('role')
};
// 验证输入
if (!userData.username || !userData.password) {
showAlert('error', '请填写所有必需字段');
return;
}
if (userData.password.length < 6) {
showAlert('error', '密码长度至少6位');
return;
}
const createBtn = document.getElementById('createUserBtn');
const originalText = createBtn.textContent;
createBtn.textContent = '创建中...';
createBtn.disabled = true;
try {
const response = await authClient.apiRequest('/auth/admin/users', {
method: 'POST',
authenticated: true,
body: JSON.stringify(userData)
});
if (response.success) {
showAlert('success', '用户创建成功');
hideCreateUserModal();
await Promise.all([loadUsers(), loadStats()]);
} else {
showAlert('error', '创建用户失败: ' + response.message);
}
} catch (error) {
console.error('创建用户失败:', error);
showAlert('error', '创建用户失败: ' + error.message);
} finally {
createBtn.textContent = originalText;
createBtn.disabled = false;
}
});
// 确认删除用户
function confirmDeleteUser(userId, username) {
if (confirm(`确定要删除用户 "${username}" 吗?\n\n此操作不可恢复!`)) {
deleteUser(userId);
}
}
// 删除用户
async function deleteUser(userId) {
try {
// 注意:这里需要后端实现删除用户的API
showAlert('info', '删除用户功能暂未实现,请联系开发人员');
} catch (error) {
console.error('删除用户失败:', error);
showAlert('error', '删除用户失败: ' + error.message);
}
}
// 显示修改密码模态框
function showChangePasswordModal() {
document.getElementById('changePasswordModal').style.display = 'block';
document.getElementById('oldPassword').focus();
}
// 隐藏修改密码模态框
function hideChangePasswordModal() {
document.getElementById('changePasswordModal').style.display = 'none';
document.getElementById('changePasswordForm').reset();
}
// 修改密码表单提交
document.getElementById('changePasswordForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const oldPassword = formData.get('oldPassword');
const newPassword = formData.get('newPassword');
const confirmPassword = formData.get('confirmPassword');
// 验证输入
if (!oldPassword || !newPassword || !confirmPassword) {
showAlert('error', '请填写所有字段');
return;
}
if (newPassword.length < 6) {
showAlert('error', '新密码长度至少6位');
return;
}
if (newPassword !== confirmPassword) {
showAlert('error', '两次输入的新密码不一致');
return;
}
const changeBtn = document.getElementById('changePasswordBtn');
const originalText = changeBtn.textContent;
changeBtn.textContent = '修改中...';
changeBtn.disabled = true;
try {
const response = await authClient.changePassword(oldPassword, newPassword);
if (response.success) {
showAlert('success', '密码修改成功,请重新登录');
hideChangePasswordModal();
// 延迟跳转到登录页面
setTimeout(() => {
authClient.redirectToLogin();
}, 2000);
} else {
showAlert('error', '修改密码失败: ' + response.message);
}
} catch (error) {
console.error('修改密码失败:', error);
showAlert('error', '修改密码失败: ' + error.message);
} finally {
changeBtn.textContent = originalText;
changeBtn.disabled = false;
}
});
// 显示提示信息
function showAlert(type, message) {
const container = document.getElementById('alertContainer');
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
container.appendChild(alert);
// 3秒后自动移除
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 3000);
}
// 格式化日期时间
function formatDateTime(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
// 点击模态框外部关闭
window.addEventListener('click', function(e) {
const createModal = document.getElementById('createUserModal');
const changeModal = document.getElementById('changePasswordModal');
if (e.target === createModal) {
hideCreateUserModal();
}
if (e.target === changeModal) {
hideChangePasswordModal();
}
});
// 添加修改密码到用户信息区域
authClient.onAuthChange((isLoggedIn, user) => {
if (isLoggedIn && user) {
const userInfoHtml = `
<div class="user-info">
<span class="username">欢迎,${user.username}</span>
<span class="role ${user.role}">${user.role === 'admin' ? '管理员' : '用户'}</span>
<button onclick="showChangePasswordModal()" class="btn btn-secondary btn-small">修改密码</button>
</div>
`;
document.getElementById('userInfo').innerHTML = userInfoHtml;
}
});
</script>
</body>
</html>
\ No newline at end of file
#!/usr/bin/env python
++ /dev/null
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
import time
def run_script(script_name, args=None):
"""运行指定的Python脚本"""
cmd = [sys.executable, script_name]
if args:
cmd.extend(args)
print(f"正在运行 {script_name}...")
try:
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
print(result.stdout)
if result.returncode != 0:
print(f"运行脚本失败: {result.stderr}")
return False
return True
except Exception as e:
print(f"执行脚本时出错: {e}")
return False
def main():
"""主函数,执行整个流程"""
# 检查Excel文件是否存在
excel_files = [f for f in os.listdir('.') if f.endswith(('.xlsx', '.xls'))]
if not excel_files:
print("错误:未找到Excel文件")
return
# 选择Excel文件
if len(excel_files) == 1:
excel_file = excel_files[0]
else:
print("发现多个Excel文件:")
for i, file in enumerate(excel_files, 1):
print(f"{i}. {file}")
try:
choice = int(input("请选择要处理的Excel文件 (输入编号): "))
if 1 <= choice <= len(excel_files):
excel_file = excel_files[choice-1]
else:
print("无效的选择")
return
except ValueError:
print("无效的输入")
return
# 1. 转换Excel到JSON
json_file = os.path.splitext(excel_file)[0] + '.json'
if not os.path.exists(json_file) or input(f"文件 {json_file} 已存在,是否重新生成? (y/n): ").lower() == 'y':
if not run_script('excel_to_json.py', [excel_file]):
print("Excel转JSON失败,流程终止")
return
# 2. 生成HTML文件
if not run_script('generate_html.py'):
print("生成HTML失败,流程终止")
return
# 3. 启动服务器
run_script('start_server.py')
if __name__ == "__main__":
# 显示欢迎信息
print("=" * 60)
print(" 利星行客户画像系统 - 一键式启动工具")
print("=" * 60)
main()
\ No newline at end of file
{
++ /dev/null
{
"regeneration_time": "2025-08-07 09:31:07",
"total_patients": 1,
"success_count": 1,
"failed_count": 0,
"callbacks": [
{
"patient_id": "TS0M004718",
"patient_name": "杨建荣ZZ00566",
"clinic": "学前街门诊",
"age": 65,
"gender": "男",
"generation_time": "2025-08-07 09:31:07",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的医生助理小王。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• 您自从2025年3月检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来。\n• 趁现在早一点处理,比以后复杂时省事也省心。\n• 这个问题,孙红胜医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2025-03-25 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
}
}
]
}
\ No newline at end of file
============================================================
++ /dev/null
============================================================
失败患者重新生成结果
重新生成时间: 2025-08-07 09:31:07
总计处理: 1 个患者
============================================================
【1】患者: 杨建荣ZZ00566 (TS0M004718)
诊所: 学前街门诊
年龄: 65岁, 性别: 男
最后就诊: 2025-03-25 00:00:00
就诊医生: 孙红胜
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的医生助理小王。
• 孙红胜医生特意交代我来关注您的后续情况。
• 您自从2025年3月检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来。
• 趁现在早一点处理,比以后复杂时省事也省心。
• 这个问题,孙红胜医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
{
++ /dev/null
{
"regeneration_time": "2025-08-07 09:35:29",
"total_patients": 5,
"success_count": 5,
"failed_count": 0,
"callbacks": [
{
"patient_id": "TS0F028745",
"patient_name": "庹伟",
"clinic": "东亭门诊",
"age": 36,
"gender": "女",
"generation_time": "2025-08-07 09:33:03",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士小王。\n• 您好,董明月医生特意交代我来关注您的后续情况。\n• 您自从2月13号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,董明月医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,而且如果放着不管,牙齿还有脱落的风险,到时候想修复就更麻烦了。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,董明月医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让董明月医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 董明月医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2025-02-13 00:00:00",
"last_clinic": "江苏瑞泰通善口腔东亭门诊",
"last_doctor": "董明月",
"visit_count": 0
}
},
{
"patient_id": "TS0G040130",
"patient_name": "徐中华",
"clinic": "大丰门诊",
"age": 59,
"gender": "男",
"generation_time": "2025-08-07 09:33:40",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士小茅。\n• 您好。\n• 茅晓春医生特意交代我来关注您的后续情况。\n• 您自从5月26号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,茅晓春医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,吃东西也不太舒服。\n• 而且如果放着不管,牙齿还有脱落的风险,到时候想修复就更麻烦了。\n• 这个情况,茅晓春医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让茅晓春医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 茅晓春医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n预约成功:\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n预约不成功:\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2025-05-26 00:00:00",
"last_clinic": "江苏瑞泰通善口腔大丰门诊",
"last_doctor": "茅晓春",
"visit_count": 0
}
},
{
"patient_id": "TS0L003744",
"patient_name": "李燕军",
"clinic": "新吴门诊",
"age": 40,
"gender": "男",
"generation_time": "2025-08-07 09:34:12",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的医生助理小王。\n• 王梓医生特意交代我来关注您的后续情况。\n• 您自从12月28号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,王梓医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,时间久了还有脱落的风险。\n• 趁现在问题还不严重,早点稳住会更好。\n• 这个情况,王梓医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让王梓医生帮您再仔细看看。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 王梓医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n[预约成功]\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n[预约不成功]\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。\n\n═══ 附加:赠送礼品(见机行事)═══\n• 我们为回访患者准备了小礼品。\n• 到时候来检查可以领取。\n• 表示我们对您的感谢。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2024-12-28 00:00:00",
"last_clinic": "江苏瑞泰通善口腔新吴门诊",
"last_doctor": "王梓",
"visit_count": 0
}
},
{
"patient_id": "TS0K083350",
"patient_name": "刘爱军",
"clinic": "通善口腔医院",
"age": 41,
"gender": "女",
"generation_time": "2025-08-07 09:34:45",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"牙槽骨吸收\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是江苏瑞泰通善口腔医院的护士长小王。\n• 您好,张琪医生特意交代我来关注您的后续情况。\n• 您自从5月17号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,张琪医生注意到您有牙槽骨吸收的情况。\n• 这个问题如果拖着,牙齿容易慢慢松动,牙缝也可能变大,影响吃东西。而且时间长了,牙齿还有脱落的风险,到时候想修复就更麻烦了。\n• 趁现在问题还不算严重,早一点关注,对稳定牙齿有很大帮助。\n• 这个情况,张琪医生也特别嘱咐我们一定要提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让张琪医生帮您再仔细看看现在的情况。\n• 复查检查约30-45分钟,需要仔细检查牙周健康状况。\n• 张琪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2025-05-17 00:00:00",
"last_clinic": "江苏瑞泰通善口腔医院",
"last_doctor": "张琪",
"visit_count": 0
}
},
{
"patient_id": "TS0M004718",
"patient_name": "杨建荣ZZ00566",
"clinic": "学前街门诊",
"age": 65,
"gender": "男",
"generation_time": "2025-08-07 09:35:29",
"callback_script": "```json\n{\n \"selected_diagnosis\": \"缺失牙\"\n}\n```\n═══ 第一部分:开场白 ═══\n• 您好,我是瑞泰通善口腔的护士小王。\n• 您好。\n• 孙红胜医生特意交代我来关注您的后续情况。\n• 您自从3月25号检查后,口腔情况怎么样?\n\n═══ 第二部分:告知漏诊项目 ═══\n• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。\n• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来,时间长了吃东西会不太舒服。\n• 而且如果放着不管,牙槽骨还可能慢慢萎缩,到时候想修复就更麻烦了。\n• 趁现在身体还好,早一点处理,比以后复杂时省事也省心。\n• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。\n\n═══ 第三部分:复查建议 ═══\n• 如果方便的话您看最近有没有时间来院复查一下。\n• 让孙红胜医生帮您再仔细看看。\n• 复查检查约30分钟,了解缺失牙位目前状况。\n• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?\n\n═══ 第四部分:结束回访语 ═══\n**预约成功:**\n• 好的,那我们【具体预约时间】见。\n• 那不打扰您了,祝您生活愉快。\n\n**预约不成功:**\n• 好的,那我下个星期再跟您联系。\n• 好的那不打扰您了,祝您生活愉快。",
"api_type": "agent_chat",
"callback_type": "success",
"patient_data_summary": {
"last_visit_time": "2025-03-25 00:00:00",
"last_clinic": "江苏瑞泰通善口腔学前街医院",
"last_doctor": "孙红胜",
"visit_count": 0
}
}
]
}
\ No newline at end of file
============================================================
++ /dev/null
============================================================
失败患者重新生成结果
重新生成时间: 2025-08-07 09:35:29
总计处理: 5 个患者
============================================================
【1】患者: 庹伟 (TS0F028745)
诊所: 东亭门诊
年龄: 36岁, 性别: 女
最后就诊: 2025-02-13 00:00:00
就诊医生: 董明月
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士小王。
• 您好,董明月医生特意交代我来关注您的后续情况。
• 您自从2月13号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,董明月医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,而且如果放着不管,牙齿还有脱落的风险,到时候想修复就更麻烦了。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,董明月医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让董明月医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 董明月医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【2】患者: 徐中华 (TS0G040130)
诊所: 大丰门诊
年龄: 59岁, 性别: 男
最后就诊: 2025-05-26 00:00:00
就诊医生: 茅晓春
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士小茅。
• 您好。
• 茅晓春医生特意交代我来关注您的后续情况。
• 您自从5月26号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,茅晓春医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,吃东西也不太舒服。
• 而且如果放着不管,牙齿还有脱落的风险,到时候想修复就更麻烦了。
• 这个情况,茅晓春医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让茅晓春医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 茅晓春医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
预约成功:
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
预约不成功:
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【3】患者: 李燕军 (TS0L003744)
诊所: 新吴门诊
年龄: 40岁, 性别: 男
最后就诊: 2024-12-28 00:00:00
就诊医生: 王梓
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的医生助理小王。
• 王梓医生特意交代我来关注您的后续情况。
• 您自从12月28号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,王梓医生注意到您有牙槽骨吸收的情况。
• 这个问题如果一直拖着,可能会出现牙齿松动、牙缝变大的情况,时间久了还有脱落的风险。
• 趁现在问题还不严重,早点稳住会更好。
• 这个情况,王梓医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让王梓医生帮您再仔细看看。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 王梓医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
[预约成功]
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
[预约不成功]
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
═══ 附加:赠送礼品(见机行事)═══
• 我们为回访患者准备了小礼品。
• 到时候来检查可以领取。
• 表示我们对您的感谢。
============================================================
【4】患者: 刘爱军 (TS0K083350)
诊所: 通善口腔医院
年龄: 41岁, 性别: 女
最后就诊: 2025-05-17 00:00:00
就诊医生: 张琪
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "牙槽骨吸收"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是江苏瑞泰通善口腔医院的护士长小王。
• 您好,张琪医生特意交代我来关注您的后续情况。
• 您自从5月17号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,张琪医生注意到您有牙槽骨吸收的情况。
• 这个问题如果拖着,牙齿容易慢慢松动,牙缝也可能变大,影响吃东西。而且时间长了,牙齿还有脱落的风险,到时候想修复就更麻烦了。
• 趁现在问题还不算严重,早一点关注,对稳定牙齿有很大帮助。
• 这个情况,张琪医生也特别嘱咐我们一定要提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让张琪医生帮您再仔细看看现在的情况。
• 复查检查约30-45分钟,需要仔细检查牙周健康状况。
• 张琪医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
【5】患者: 杨建荣ZZ00566 (TS0M004718)
诊所: 学前街门诊
年龄: 65岁, 性别: 男
最后就诊: 2025-03-25 00:00:00
就诊医生: 孙红胜
----------------------------------------
回访话术:
```json
{
"selected_diagnosis": "缺失牙"
}
```
═══ 第一部分:开场白 ═══
• 您好,我是瑞泰通善口腔的护士小王。
• 您好。
• 孙红胜医生特意交代我来关注您的后续情况。
• 您自从3月25号检查后,口腔情况怎么样?
═══ 第二部分:告知漏诊项目 ═══
• 上次来检查的时候,孙红胜医生注意到您有缺失牙的情况。
• 缺牙的地方如果不处理,旁边的牙齿可能会慢慢歪掉,对面的牙齿也可能伸长出来,时间长了吃东西会不太舒服。
• 而且如果放着不管,牙槽骨还可能慢慢萎缩,到时候想修复就更麻烦了。
• 趁现在身体还好,早一点处理,比以后复杂时省事也省心。
• 这个情况,孙红胜医生也特别嘱咐我们提醒您一下。
═══ 第三部分:复查建议 ═══
• 如果方便的话您看最近有没有时间来院复查一下。
• 让孙红胜医生帮您再仔细看看。
• 复查检查约30分钟,了解缺失牙位目前状况。
• 孙红胜医生【时间段1】和【时间段2】这两个时间段有空,您看哪个时间比较方便?
═══ 第四部分:结束回访语 ═══
**预约成功:**
• 好的,那我们【具体预约时间】见。
• 那不打扰您了,祝您生活愉快。
**预约不成功:**
• 好的,那我下个星期再跟您联系。
• 好的那不打扰您了,祝您生活愉快。
============================================================
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment