banxiang/docs/技术方案-V1.0-MVP架构.md
2026-02-18 18:06:31 +00:00

856 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 | 白名单域名 |
---
**文档结束**