856 lines
25 KiB
Markdown
856 lines
25 KiB
Markdown
---
|
||
slug: tech-v1-0-mvp-architecture
|
||
---
|
||
|
||
# 「伴享」技术方案 V1.0 — MVP架构
|
||
|
||
**版本**: V1.0
|
||
**对应PRD**: PRD-V1.0-MVP核心功能
|
||
**更新日期**: 2026-02-16
|
||
|
||
---
|
||
|
||
## 1. 技术架构总览
|
||
|
||
### 1.1 系统架构图
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────┐
|
||
│ 客户端层 │
|
||
│ ┌──────────────────┐ ┌───────────────────────┐ │
|
||
│ │ Flutter APP │ │ 子女端(V1.2预留) │ │
|
||
│ │ iOS + Android │ │ 微信小程序 │ │
|
||
│ └────────┬─────────┘ └───────────────────────┘ │
|
||
└───────────┼────────────────────────────────────────┘
|
||
│ HTTPS + WebSocket
|
||
┌───────────▼────────────────────────────────────────┐
|
||
│ API网关层(Nginx) │
|
||
│ SSL终止 │ 限流 │ 日志 │ 路由 │ CORS │ 压缩 │
|
||
└───────────┬────────────────────────────────────────┘
|
||
│
|
||
┌───────────▼────────────────────────────────────────┐
|
||
│ 业务服务层(Node.js + Express) │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ 用户服务 │ │ 活动服务 │ │ AI管家 │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ 医疗服务 │ │ 支付服务 │ │ 通知服务 │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||
└───────────┬────────────────────────────────────────┘
|
||
│
|
||
┌───────────▼────────────────────────────────────────┐
|
||
│ 数据层 │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │PostgreSQL│ │ Redis │ │ 阿里云OSS │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||
└────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 1.2 技术选型
|
||
|
||
| 层级 | 技术 | 版本 | 选型理由 |
|
||
|------|------|------|----------|
|
||
| 前端 | Flutter | 3.x | 跨平台、性能好、适老化组件丰富 |
|
||
| 后端 | Node.js + Express | 22 + 4.x | 生态成熟、AI集成方便 |
|
||
| 数据库 | PostgreSQL | 15+ | JSON支持好、地理空间查询 |
|
||
| 缓存 | Redis | 7.x | 会话管理、限流、缓存 |
|
||
| 文件存储 | 阿里云OSS | - | 图片/语音文件 |
|
||
| AI模型 | 通义千问 | qwen-max | 国产、函数调用支持好 |
|
||
| 语音 | 讯飞ASR/TTS | - | 中文识别准确率高 |
|
||
| 地图 | 高德地图 | - | 国内POI数据全 |
|
||
| 支付 | 微信支付V3 | - | 银发群体使用率高 |
|
||
| 推送 | JPush | - | 全平台推送 |
|
||
| 部署 | Docker Compose | - | MVP阶段够用 |
|
||
|
||
---
|
||
|
||
## 2. 数据库设计
|
||
|
||
### 2.1 ER关系概览
|
||
|
||
```
|
||
users ──< activity_participants >── activities
|
||
users ──< ai_conversations
|
||
users ──< orders
|
||
users ──< appointments >── doctors >── departments >── hospitals
|
||
users ──< consultations >── doctors
|
||
users ──< family_links
|
||
users ──< notifications
|
||
users ── ai_user_preferences
|
||
```
|
||
|
||
### 2.2 完整DDL
|
||
|
||
```sql
|
||
-- ===========================
|
||
-- 伴享 V1.0 DDL
|
||
-- PostgreSQL 15+
|
||
-- ===========================
|
||
|
||
CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;
|
||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||
|
||
-- 1. 用户表
|
||
CREATE TABLE users (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
phone VARCHAR(11) UNIQUE NOT NULL,
|
||
nickname VARCHAR(50),
|
||
avatar_url TEXT,
|
||
birth_year INTEGER,
|
||
birth_month INTEGER,
|
||
gender VARCHAR(10) CHECK (gender IN ('male', 'female', 'other')),
|
||
city VARCHAR(50) DEFAULT '成都',
|
||
district VARCHAR(50),
|
||
interests TEXT[] DEFAULT '{}',
|
||
real_name VARCHAR(50),
|
||
id_card_hash VARCHAR(64),
|
||
id_card_encrypted TEXT,
|
||
verified BOOLEAN DEFAULT FALSE,
|
||
emergency_contact_name VARCHAR(50),
|
||
emergency_contact_phone VARCHAR(11),
|
||
font_size VARCHAR(10) DEFAULT 'medium',
|
||
speech_speed VARCHAR(10) DEFAULT 'normal',
|
||
auto_voice BOOLEAN DEFAULT TRUE,
|
||
last_login_at TIMESTAMP,
|
||
login_count INTEGER DEFAULT 0,
|
||
status VARCHAR(20) DEFAULT 'active',
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_users_phone ON users(phone);
|
||
CREATE INDEX idx_users_city ON users(city);
|
||
CREATE INDEX idx_users_interests ON users USING GIN(interests);
|
||
|
||
-- 2. 短信验证码
|
||
CREATE TABLE sms_codes (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
phone VARCHAR(11) NOT NULL,
|
||
code VARCHAR(6) NOT NULL,
|
||
purpose VARCHAR(20) DEFAULT 'login',
|
||
used BOOLEAN DEFAULT FALSE,
|
||
expires_at TIMESTAMP NOT NULL,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 3. 家庭关联
|
||
CREATE TABLE family_links (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
parent_user_id BIGINT REFERENCES users(id),
|
||
child_user_id BIGINT,
|
||
invite_code VARCHAR(6),
|
||
status VARCHAR(20) DEFAULT 'active',
|
||
linked_at TIMESTAMP DEFAULT NOW(),
|
||
UNIQUE(parent_user_id, child_user_id)
|
||
);
|
||
|
||
CREATE TABLE invite_codes (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
user_id BIGINT REFERENCES users(id),
|
||
code VARCHAR(6) UNIQUE NOT NULL,
|
||
used BOOLEAN DEFAULT FALSE,
|
||
expires_at TIMESTAMP NOT NULL,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 4. 活动表
|
||
CREATE TABLE activities (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
creator_id BIGINT REFERENCES users(id) NOT NULL,
|
||
title VARCHAR(100) NOT NULL,
|
||
category VARCHAR(50) NOT NULL,
|
||
description TEXT,
|
||
cover_image_url TEXT,
|
||
location_lat DECIMAL(10,7) NOT NULL,
|
||
location_lng DECIMAL(10,7) NOT NULL,
|
||
location_address TEXT NOT NULL,
|
||
location_name VARCHAR(100),
|
||
start_time TIMESTAMP NOT NULL,
|
||
end_time TIMESTAMP,
|
||
max_participants INTEGER DEFAULT 10 CHECK (max_participants BETWEEN 5 AND 15),
|
||
current_participants INTEGER DEFAULT 0,
|
||
status VARCHAR(20) DEFAULT 'upcoming',
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_activities_status_time ON activities(status, start_time);
|
||
CREATE INDEX idx_activities_category ON activities(category);
|
||
|
||
-- 5. 活动报名
|
||
CREATE TABLE activity_participants (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
activity_id BIGINT REFERENCES activities(id),
|
||
user_id BIGINT REFERENCES users(id),
|
||
status VARCHAR(20) DEFAULT 'registered',
|
||
signed_in_at TIMESTAMP,
|
||
signed_in_lat DECIMAL(10,7),
|
||
signed_in_lng DECIMAL(10,7),
|
||
rating INTEGER CHECK (rating BETWEEN 1 AND 5),
|
||
review TEXT,
|
||
joined_at TIMESTAMP DEFAULT NOW(),
|
||
cancelled_at TIMESTAMP,
|
||
UNIQUE(activity_id, user_id)
|
||
);
|
||
|
||
-- 6. AI对话
|
||
CREATE TABLE ai_conversations (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
user_id BIGINT REFERENCES users(id),
|
||
session_id VARCHAR(36) NOT NULL,
|
||
role VARCHAR(20) NOT NULL,
|
||
content TEXT NOT NULL,
|
||
content_type VARCHAR(20) DEFAULT 'text',
|
||
voice_url TEXT,
|
||
intent VARCHAR(50),
|
||
metadata JSONB,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_ai_conv_user ON ai_conversations(user_id, session_id);
|
||
|
||
CREATE TABLE ai_user_preferences (
|
||
user_id BIGINT PRIMARY KEY REFERENCES users(id),
|
||
preferred_hospital VARCHAR(100),
|
||
preferred_doctor VARCHAR(100),
|
||
home_address TEXT,
|
||
common_destinations JSONB,
|
||
dietary_restrictions TEXT[],
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 7. 医院/科室/医生/排班
|
||
CREATE TABLE hospitals (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
address TEXT,
|
||
lat DECIMAL(10,7),
|
||
lng DECIMAL(10,7),
|
||
phone VARCHAR(20),
|
||
logo_url TEXT,
|
||
level VARCHAR(20),
|
||
status VARCHAR(20) DEFAULT 'active',
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE TABLE departments (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
hospital_id BIGINT REFERENCES hospitals(id),
|
||
name VARCHAR(50) NOT NULL,
|
||
description TEXT,
|
||
sort_order INTEGER DEFAULT 0
|
||
);
|
||
|
||
CREATE TABLE doctors (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
hospital_id BIGINT REFERENCES hospitals(id),
|
||
department_id BIGINT REFERENCES departments(id),
|
||
name VARCHAR(50) NOT NULL,
|
||
title VARCHAR(50),
|
||
avatar_url TEXT,
|
||
specialty TEXT,
|
||
rating DECIMAL(2,1) DEFAULT 5.0,
|
||
rating_count INTEGER DEFAULT 0,
|
||
status VARCHAR(20) DEFAULT 'active'
|
||
);
|
||
|
||
CREATE TABLE doctor_schedules (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
doctor_id BIGINT REFERENCES doctors(id),
|
||
schedule_date DATE NOT NULL,
|
||
start_time TIME NOT NULL,
|
||
end_time TIME NOT NULL,
|
||
max_patients INTEGER DEFAULT 30,
|
||
current_patients INTEGER DEFAULT 0,
|
||
fee DECIMAL(10,2),
|
||
status VARCHAR(20) DEFAULT 'available'
|
||
);
|
||
|
||
-- 8. 预约/问诊
|
||
CREATE TABLE appointments (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
order_no VARCHAR(20) UNIQUE NOT NULL,
|
||
user_id BIGINT REFERENCES users(id),
|
||
hospital_id BIGINT REFERENCES hospitals(id),
|
||
department_id BIGINT REFERENCES departments(id),
|
||
doctor_id BIGINT REFERENCES doctors(id),
|
||
schedule_id BIGINT REFERENCES doctor_schedules(id),
|
||
patient_name VARCHAR(50),
|
||
patient_phone VARCHAR(11),
|
||
appointment_date DATE,
|
||
appointment_time TIME,
|
||
fee DECIMAL(10,2),
|
||
status VARCHAR(20) DEFAULT 'pending',
|
||
paid_at TIMESTAMP,
|
||
cancelled_at TIMESTAMP,
|
||
cancel_reason TEXT,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE TABLE consultations (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
order_no VARCHAR(20) UNIQUE NOT NULL,
|
||
user_id BIGINT REFERENCES users(id),
|
||
doctor_id BIGINT REFERENCES doctors(id),
|
||
symptom_description TEXT,
|
||
images TEXT[],
|
||
doctor_reply TEXT,
|
||
fee DECIMAL(10,2) DEFAULT 19.90,
|
||
status VARCHAR(20) DEFAULT 'pending',
|
||
replied_at TIMESTAMP,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 9. 订单
|
||
CREATE TABLE orders (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
order_no VARCHAR(20) UNIQUE NOT NULL,
|
||
user_id BIGINT REFERENCES users(id),
|
||
order_type VARCHAR(50) NOT NULL,
|
||
amount DECIMAL(10,2) NOT NULL,
|
||
status VARCHAR(20) DEFAULT 'pending',
|
||
detail JSONB,
|
||
wx_transaction_id VARCHAR(50),
|
||
paid_at TIMESTAMP,
|
||
completed_at TIMESTAMP,
|
||
cancelled_at TIMESTAMP,
|
||
refund_amount DECIMAL(10,2),
|
||
refund_at TIMESTAMP,
|
||
refund_reason TEXT,
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_orders_user ON orders(user_id, created_at DESC);
|
||
|
||
-- 10. 通知
|
||
CREATE TABLE notifications (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
user_id BIGINT REFERENCES users(id),
|
||
type VARCHAR(50) NOT NULL,
|
||
title VARCHAR(100),
|
||
content TEXT,
|
||
data JSONB,
|
||
is_read BOOLEAN DEFAULT FALSE,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX idx_notif_user ON notifications(user_id, is_read, created_at DESC);
|
||
|
||
-- 11. 审计日志
|
||
CREATE TABLE audit_logs (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
user_id BIGINT,
|
||
action VARCHAR(50),
|
||
resource_type VARCHAR(50),
|
||
resource_id BIGINT,
|
||
detail JSONB,
|
||
ip_address INET,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 3. API文档
|
||
|
||
### 3.1 规范
|
||
|
||
- **Base URL**: `https://api.banxiang.com/api/v1`
|
||
- **认证**: `Authorization: Bearer <JWT>`
|
||
- **响应格式**: `{ "success": true, "data": {...} }` / `{ "success": false, "error": {"code":"...", "message":"..."} }`
|
||
|
||
### 3.2 完整接口清单
|
||
|
||
| 模块 | 方法 | 路径 | 描述 |
|
||
|------|------|------|------|
|
||
| 认证 | POST | /auth/send-code | 发送验证码 |
|
||
| 认证 | POST | /auth/login | 登录/注册 |
|
||
| 认证 | POST | /auth/refresh | 刷新Token |
|
||
| 用户 | GET | /users/me | 获取当前用户 |
|
||
| 用户 | PUT | /users/profile | 更新资料 |
|
||
| 用户 | POST | /users/verify | 实名认证 |
|
||
| 用户 | PUT | /users/emergency-contact | 紧急联系人 |
|
||
| 用户 | PUT | /users/settings | 更新设置 |
|
||
| 家庭 | POST | /family/generate-code | 生成邀请码 |
|
||
| 家庭 | POST | /family/link | 绑定子女 |
|
||
| 家庭 | GET | /family/members | 家人列表 |
|
||
| 活动 | GET | /activities | 活动列表 |
|
||
| 活动 | GET | /activities/:id | 活动详情 |
|
||
| 活动 | POST | /activities | 创建活动 |
|
||
| 活动 | PUT | /activities/:id | 修改活动 |
|
||
| 活动 | DELETE | /activities/:id | 取消活动 |
|
||
| 活动 | POST | /activities/:id/join | 报名 |
|
||
| 活动 | POST | /activities/:id/cancel | 取消报名 |
|
||
| 活动 | POST | /activities/:id/sign-in | 签到 |
|
||
| 活动 | POST | /activities/:id/review | 评价 |
|
||
| 活动 | GET | /users/me/activities | 我的活动 |
|
||
| AI | POST | /ai/chat | 文字对话(SSE) |
|
||
| AI | POST | /ai/voice | 语音对话 |
|
||
| AI | GET | /ai/history | 对话历史 |
|
||
| 医疗 | GET | /medical/hospitals | 医院列表 |
|
||
| 医疗 | GET | /medical/hospitals/:id/departments | 科室 |
|
||
| 医疗 | GET | /medical/doctors | 医生列表 |
|
||
| 医疗 | GET | /medical/doctors/:id/schedules | 排班 |
|
||
| 医疗 | POST | /medical/appointments | 创建预约 |
|
||
| 医疗 | POST | /medical/appointments/:id/cancel | 取消 |
|
||
| 医疗 | POST | /medical/consultations | 创建问诊 |
|
||
| 订单 | POST | /orders/create | 创建订单 |
|
||
| 订单 | GET | /orders | 订单列表 |
|
||
| 订单 | GET | /orders/:orderNo | 订单详情 |
|
||
| 订单 | POST | /orders/:orderNo/refund | 退款 |
|
||
| 通知 | GET | /notifications | 通知列表 |
|
||
| 通知 | PUT | /notifications/:id/read | 标记已读 |
|
||
| 上传 | POST | /upload/image | 上传图片 |
|
||
| 上传 | POST | /upload/voice | 上传语音 |
|
||
|
||
---
|
||
|
||
## 4. Flutter组件架构
|
||
|
||
### 4.1 项目结构
|
||
|
||
```
|
||
banxiang_app/
|
||
├── lib/
|
||
│ ├── main.dart
|
||
│ ├── app.dart
|
||
│ ├── config/
|
||
│ │ ├── theme.dart # 适老化主题
|
||
│ │ ├── routes.dart
|
||
│ │ └── constants.dart
|
||
│ ├── core/
|
||
│ │ ├── api/
|
||
│ │ │ ├── api_client.dart # Dio封装
|
||
│ │ │ └── endpoints.dart
|
||
│ │ ├── models/ # 数据模型
|
||
│ │ ├── providers/ # Riverpod状态管理
|
||
│ │ ├── services/ # 业务服务
|
||
│ │ └── utils/
|
||
│ ├── features/
|
||
│ │ ├── auth/
|
||
│ │ ├── home/
|
||
│ │ ├── activity/
|
||
│ │ ├── ai_butler/
|
||
│ │ ├── medical/
|
||
│ │ ├── profile/
|
||
│ │ └── notification/
|
||
│ └── shared/
|
||
│ ├── widgets/ # BX适老化组件库
|
||
│ └── layouts/
|
||
├── assets/
|
||
│ ├── images/
|
||
│ ├── fonts/
|
||
│ └── animations/
|
||
├── test/
|
||
└── pubspec.yaml
|
||
```
|
||
|
||
### 4.2 适老化组件库(BX Design System)
|
||
|
||
```dart
|
||
/// BX适老化按钮
|
||
class BxButton extends StatelessWidget {
|
||
final String text;
|
||
final VoidCallback onPressed;
|
||
final BxButtonStyle style; // primary / secondary / danger
|
||
final bool loading;
|
||
|
||
// 最小高度48dp,字号18sp,圆角12dp
|
||
// 点击区域≥48x48dp
|
||
// loading状态显示转圈+文字
|
||
}
|
||
|
||
/// BX适老化输入框
|
||
class BxInput extends StatelessWidget {
|
||
final String label;
|
||
final String hint;
|
||
final TextInputType keyboardType;
|
||
final bool voiceEnabled; // 右侧语音输入按钮
|
||
|
||
// 高度56dp,字号18sp
|
||
// label在输入框上方
|
||
// 错误提示在下方,红色16sp
|
||
}
|
||
|
||
/// BX确认弹窗(防误操作)
|
||
class BxConfirmDialog extends StatelessWidget {
|
||
final String title;
|
||
final String message;
|
||
final String confirmText;
|
||
final String cancelText;
|
||
|
||
// 标题22sp,正文18sp
|
||
// 按钮大且间隔远
|
||
// 危险操作confirmText用红色
|
||
}
|
||
|
||
/// BX活动卡片
|
||
class BxActivityCard extends StatelessWidget {
|
||
final Activity activity;
|
||
final double distance;
|
||
|
||
// 大标题18sp,时间地点16sp
|
||
// 报名按钮在右侧
|
||
// 已满员显示灰色
|
||
}
|
||
```
|
||
|
||
### 4.3 核心代码示例
|
||
|
||
#### API客户端
|
||
|
||
```dart
|
||
// core/api/api_client.dart
|
||
class ApiClient {
|
||
late final Dio _dio;
|
||
|
||
ApiClient() {
|
||
_dio = Dio(BaseOptions(
|
||
baseUrl: 'https://api.banxiang.com/api/v1',
|
||
connectTimeout: const Duration(seconds: 10),
|
||
receiveTimeout: const Duration(seconds: 30),
|
||
));
|
||
|
||
_dio.interceptors.addAll([
|
||
AuthInterceptor(), // 自动附加JWT
|
||
LogInterceptor(),
|
||
ErrorInterceptor(), // 统一错误处理
|
||
]);
|
||
}
|
||
|
||
Future<T> get<T>(String path, {Map<String, dynamic>? params}) async {
|
||
final response = await _dio.get(path, queryParameters: params);
|
||
return response.data['data'] as T;
|
||
}
|
||
|
||
Future<T> post<T>(String path, {dynamic data}) async {
|
||
final response = await _dio.post(path, data: data);
|
||
return response.data['data'] as T;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### AI管家对话页面
|
||
|
||
```dart
|
||
// features/ai_butler/chat_page.dart
|
||
class AiChatPage extends ConsumerStatefulWidget {
|
||
@override
|
||
ConsumerState<AiChatPage> createState() => _AiChatPageState();
|
||
}
|
||
|
||
class _AiChatPageState extends ConsumerState<AiChatPage> {
|
||
final _controller = TextEditingController();
|
||
bool _isRecording = false;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final messages = ref.watch(aiChatProvider);
|
||
|
||
return Scaffold(
|
||
appBar: AppBar(title: Text('🤖 小伴 · AI管家')),
|
||
body: Column(
|
||
children: [
|
||
// 消息列表
|
||
Expanded(
|
||
child: ListView.builder(
|
||
reverse: true,
|
||
itemCount: messages.length,
|
||
itemBuilder: (ctx, i) => ChatBubble(
|
||
message: messages[i],
|
||
fontSize: ref.watch(fontSizeProvider),
|
||
),
|
||
),
|
||
),
|
||
// 快捷指令栏
|
||
QuickActions(
|
||
actions: ['附近活动', '挂号', '买菜', '天气'],
|
||
onTap: (action) => _sendMessage(action),
|
||
),
|
||
// 输入栏
|
||
ChatInputBar(
|
||
controller: _controller,
|
||
onSend: () => _sendMessage(_controller.text),
|
||
onVoiceStart: _startRecording,
|
||
onVoiceEnd: _stopRecording,
|
||
isRecording: _isRecording,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
void _sendMessage(String text) {
|
||
ref.read(aiChatProvider.notifier).sendMessage(text);
|
||
_controller.clear();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 活动推荐算法
|
||
|
||
```dart
|
||
// core/services/recommendation_service.dart
|
||
double calculateRecommendScore(User user, Activity activity, double distanceKm) {
|
||
// 兴趣匹配度 (0-1)
|
||
final commonInterests = user.interests
|
||
.where((i) => i == activity.category)
|
||
.length;
|
||
final interestScore = commonInterests > 0 ? 1.0 : 0.0;
|
||
|
||
// 距离分 (0-1), 10km内线性衰减
|
||
final distanceScore = 1 - (distanceKm / 10).clamp(0, 1);
|
||
|
||
// 时间分 (0-1), 168小时(一周)内线性衰减
|
||
final hoursDiff = activity.startTime.difference(DateTime.now()).inHours;
|
||
final timeScore = 1 - (hoursDiff / 168).clamp(0, 1);
|
||
|
||
// 热度分 (0-1)
|
||
final hotScore = activity.currentParticipants / activity.maxParticipants;
|
||
|
||
return interestScore * 0.4 + distanceScore * 0.3 + timeScore * 0.2 + hotScore * 0.1;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. AI管家技术方案
|
||
|
||
### 5.1 架构
|
||
|
||
```
|
||
用户输入(文字/语音)
|
||
↓
|
||
┌─────────────┐
|
||
│ 语音处理 │ ← 讯飞ASR(语音→文字)
|
||
└──────┬──────┘
|
||
↓
|
||
┌─────────────┐
|
||
│ 意图识别 │ ← 通义千问 Function Calling
|
||
└──────┬──────┘
|
||
↓
|
||
┌─────────────────────────────┐
|
||
│ 任务路由 │
|
||
│ ├── 活动查询 → 调用活动API │
|
||
│ ├── 挂号预约 → 调用医疗API │
|
||
│ ├── 生鲜购买 → 调用商品API │
|
||
│ └── 闲聊/咨询 → 直接对话 │
|
||
└──────┬──────────────────────┘
|
||
↓
|
||
┌─────────────┐
|
||
│ 响应生成 │ ← 通义千问生成回复
|
||
└──────┬──────┘
|
||
↓
|
||
┌─────────────┐
|
||
│ 语音合成 │ ← 讯飞TTS(文字→语音)
|
||
└──────┬──────┘
|
||
↓
|
||
返回用户(文字+语音+操作按钮)
|
||
```
|
||
|
||
### 5.2 Function Calling定义
|
||
|
||
```javascript
|
||
// AI工具定义
|
||
const tools = [
|
||
{
|
||
type: 'function',
|
||
function: {
|
||
name: 'search_activities',
|
||
description: '搜索附近的活动',
|
||
parameters: {
|
||
type: 'object',
|
||
properties: {
|
||
category: { type: 'string', description: '活动类型' },
|
||
timeRange: { type: 'string', enum: ['today', 'tomorrow', 'week'] },
|
||
lat: { type: 'number' },
|
||
lng: { type: 'number' }
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: 'function',
|
||
function: {
|
||
name: 'book_appointment',
|
||
description: '预约挂号',
|
||
parameters: {
|
||
type: 'object',
|
||
properties: {
|
||
hospitalName: { type: 'string' },
|
||
departmentName: { type: 'string' },
|
||
doctorName: { type: 'string' },
|
||
date: { type: 'string' },
|
||
time: { type: 'string' }
|
||
}
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: 'function',
|
||
function: {
|
||
name: 'order_grocery',
|
||
description: '生鲜配送下单',
|
||
parameters: {
|
||
type: 'object',
|
||
properties: {
|
||
items: {
|
||
type: 'array',
|
||
items: {
|
||
type: 'object',
|
||
properties: {
|
||
name: { type: 'string' },
|
||
quantity: { type: 'number' }
|
||
}
|
||
}
|
||
},
|
||
deliveryAddress: { type: 'string' }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
];
|
||
```
|
||
|
||
### 5.3 System Prompt
|
||
|
||
```
|
||
你是「小伴」,「伴享」APP的AI智能管家。你服务的是50-70岁的银发群体。
|
||
|
||
规则:
|
||
1. 说话简洁、亲切、温暖,像家人一样
|
||
2. 每次回复不超过100字
|
||
3. 称呼用户为"X阿姨"或"X叔叔"(根据性别和姓氏)
|
||
4. 不使用网络用语、不说英文
|
||
5. 涉及医疗健康的建议必须加"仅供参考,建议咨询医生"
|
||
6. 涉及支付前必须明确告知金额并确认
|
||
7. 不确定的信息不要编造,说"我帮您查查"
|
||
8. 提供操作选项时,用简短的按钮文字
|
||
|
||
用户信息:
|
||
- 姓名:{userName}
|
||
- 城市:{city}
|
||
- 兴趣:{interests}
|
||
- 常去医院:{preferredHospital}
|
||
- 家庭住址:{homeAddress}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 部署方案
|
||
|
||
### 6.1 服务器配置(MVP阶段)
|
||
|
||
| 服务 | 配置 | 月费用 |
|
||
|------|------|--------|
|
||
| 应用服务器 | 阿里云ECS 2核4G | ¥200/月 |
|
||
| 数据库 | 阿里云RDS PostgreSQL 2核4G | ¥300/月 |
|
||
| Redis | 阿里云Redis 1G | ¥100/月 |
|
||
| OSS | 按量计费 | ¥50/月 |
|
||
| CDN | 按量计费 | ¥50/月 |
|
||
| **合计** | | **¥700/月** |
|
||
|
||
### 6.2 Docker Compose
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
|
||
services:
|
||
app:
|
||
build: .
|
||
ports:
|
||
- "3000:3000"
|
||
environment:
|
||
- NODE_ENV=production
|
||
- DATABASE_URL=postgres://banxiang:xxx@db:5432/banxiang
|
||
- REDIS_URL=redis://redis:6379
|
||
- QWEN_API_KEY=${QWEN_API_KEY}
|
||
- XUNFEI_APP_ID=${XUNFEI_APP_ID}
|
||
- WX_PAY_MCH_ID=${WX_PAY_MCH_ID}
|
||
depends_on:
|
||
- db
|
||
- redis
|
||
restart: always
|
||
|
||
db:
|
||
image: postgres:15
|
||
volumes:
|
||
- pgdata:/var/lib/postgresql/data
|
||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||
environment:
|
||
- POSTGRES_DB=banxiang
|
||
- POSTGRES_USER=banxiang
|
||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||
restart: always
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
volumes:
|
||
- redisdata:/data
|
||
restart: always
|
||
|
||
nginx:
|
||
image: nginx:alpine
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
volumes:
|
||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||
- ./ssl:/etc/nginx/ssl
|
||
depends_on:
|
||
- app
|
||
restart: always
|
||
|
||
volumes:
|
||
pgdata:
|
||
redisdata:
|
||
```
|
||
|
||
### 6.3 CI/CD流程
|
||
|
||
```
|
||
代码推送 → GitHub Actions
|
||
├── 运行测试
|
||
├── 构建Docker镜像
|
||
├── 推送到阿里云容器镜像
|
||
└── SSH到服务器执行 docker compose pull && docker compose up -d
|
||
```
|
||
|
||
### 6.4 监控方案
|
||
|
||
| 维度 | 工具 | 说明 |
|
||
|------|------|------|
|
||
| 应用监控 | PM2 + 阿里云ARMS | 错误率、响应时间 |
|
||
| 服务器监控 | 阿里云云监控 | CPU/内存/磁盘 |
|
||
| 日志 | Winston + 阿里云SLS | 结构化日志 |
|
||
| 报警 | 钉钉/飞书Webhook | 异常自动通知 |
|
||
|
||
---
|
||
|
||
## 7. 安全方案
|
||
|
||
### 7.1 数据安全
|
||
|
||
| 层面 | 措施 |
|
||
|------|------|
|
||
| 传输 | HTTPS + TLS 1.3 |
|
||
| 存储 | 敏感字段AES-256加密 |
|
||
| 身份证 | 哈希索引 + 加密存储,不存明文 |
|
||
| JWT | HS256签名,7天有效期 |
|
||
| 密钥管理 | 环境变量,不入代码库 |
|
||
|
||
### 7.2 接口安全
|
||
|
||
| 措施 | 配置 |
|
||
|------|------|
|
||
| 全局限流 | 100次/分钟/IP |
|
||
| 登录限流 | 5次/分钟/手机号 |
|
||
| 验证码限流 | 3次/5分钟/手机号 |
|
||
| SQL注入防护 | 参数化查询 |
|
||
| XSS防护 | 输出转义 + CSP头 |
|
||
| CORS | 白名单域名 |
|
||
|
||
---
|
||
|
||
**文档结束** |