Files
chat/docs/MOBILE_INTEGRATION_EXAMPLES.md
Andrew K. Choi 7c22664daf
All checks were successful
continuous-integration/drone/push Build is passing
sdf
2025-09-26 12:22:14 +09:00

589 lines
16 KiB
Markdown
Raw 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.

# Примеры использования API для мобильной разработки
## 🚀 Быстрый старт
### 1. Авторизация пользователя
```javascript
// Регистрация
const registerUser = async (userData) => {
const response = await fetch('http://192.168.0.103:8000/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: userData.username,
email: userData.email,
password: userData.password,
full_name: userData.full_name,
phone: userData.phone
})
});
return await response.json();
};
// Вход в систему
const loginUser = async (username, password) => {
const response = await fetch('http://192.168.0.103:8000/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
// Сохранить токен для дальнейших запросов
localStorage.setItem('access_token', data.access_token);
return data;
};
```
### 2. Создание SOS оповещения
```javascript
const createEmergencyAlert = async (location, alertType, message) => {
const token = localStorage.getItem('access_token');
const response = await fetch('http://192.168.0.103:8000/api/emergency/alerts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
latitude: location.latitude,
longitude: location.longitude,
alert_type: alertType, // 'general', 'medical', 'violence', etc.
message: message,
contact_emergency_services: true,
notify_emergency_contacts: true
})
});
return await response.json();
};
```
### 3. Поиск ближайших оповещений
```javascript
const findNearbyAlerts = async (userLocation, radiusKm = 5) => {
const token = localStorage.getItem('access_token');
const response = await fetch(
`http://192.168.0.103:8000/api/emergency/nearby?latitude=${userLocation.latitude}&longitude=${userLocation.longitude}&radius_km=${radiusKm}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
);
return await response.json();
};
```
### 4. Отклик на оповещение
```javascript
const respondToAlert = async (alertId, responseType, message, eta) => {
const token = localStorage.getItem('access_token');
const response = await fetch(`http://192.168.0.103:8000/api/emergency/alerts/${alertId}/responses`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
response_type: responseType, // 'help_on_way', 'contacted_authorities', etc.
message: message,
eta_minutes: eta,
location_sharing: true
})
});
return await response.json();
};
```
## 📱 React Native компоненты
### SOS Button Component
```jsx
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, Alert } from 'react-native';
import Geolocation from '@react-native-community/geolocation';
const SOSButton = ({ onEmergencyCreated }) => {
const [isCreating, setIsCreating] = useState(false);
const handleSOSPress = () => {
Alert.alert(
'Экстренная ситуация',
'Вы уверены, что хотите отправить SOS сигнал?',
[
{ text: 'Отмена', style: 'cancel' },
{ text: 'SOS', style: 'destructive', onPress: createEmergencyAlert }
]
);
};
const createEmergencyAlert = () => {
setIsCreating(true);
Geolocation.getCurrentPosition(
async (position) => {
try {
const alert = await createEmergencyAlert(
{
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
'general',
'Экстренная помощь необходима!'
);
onEmergencyCreated(alert);
Alert.alert('Успешно', 'SOS сигнал отправлен');
} catch (error) {
Alert.alert('Ошибка', 'Не удалось отправить SOS сигнал');
}
setIsCreating(false);
},
(error) => {
Alert.alert('Ошибка', 'Не удалось получить местоположение');
setIsCreating(false);
}
);
};
return (
<View style={{ alignItems: 'center', margin: 20 }}>
<TouchableOpacity
style={{
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: isCreating ? '#ccc' : '#ff4444',
justifyContent: 'center',
alignItems: 'center',
elevation: 5
}}
onPress={handleSOSPress}
disabled={isCreating}
>
<Text style={{ color: 'white', fontSize: 18, fontWeight: 'bold' }}>
{isCreating ? 'Отправка...' : 'SOS'}
</Text>
</TouchableOpacity>
</View>
);
};
```
### Nearby Alerts List
```jsx
import React, { useEffect, useState } from 'react';
import { View, FlatList, Text, TouchableOpacity } from 'react-native';
const NearbyAlertsList = ({ userLocation }) => {
const [alerts, setAlerts] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (userLocation) {
loadNearbyAlerts();
}
}, [userLocation]);
const loadNearbyAlerts = async () => {
setLoading(true);
try {
const nearbyAlerts = await findNearbyAlerts(userLocation);
setAlerts(nearbyAlerts);
} catch (error) {
console.error('Ошибка загрузки оповещений:', error);
}
setLoading(false);
};
const handleRespondToAlert = async (alertId) => {
try {
await respondToAlert(alertId, 'help_on_way', 'Еду на помощь!', 10);
Alert.alert('Успешно', 'Ваш отклик отправлен');
loadNearbyAlerts(); // Обновить список
} catch (error) {
Alert.alert('Ошибка', 'Не удалось отправить отклик');
}
};
const renderAlert = ({ item }) => (
<View style={{
backgroundColor: 'white',
margin: 10,
padding: 15,
borderRadius: 10,
elevation: 2
}}>
<Text style={{ fontWeight: 'bold', fontSize: 16 }}>
{item.alert_type.toUpperCase()}
</Text>
<Text style={{ color: '#666', marginTop: 5 }}>
📍 {item.address || 'Адрес не указан'}
</Text>
<Text style={{ color: '#666' }}>
📏 {item.distance_km.toFixed(1)} км от вас
</Text>
<Text style={{ color: '#666' }}>
👥 Откликов: {item.responded_users_count}
</Text>
<TouchableOpacity
style={{
backgroundColor: '#007AFF',
padding: 10,
borderRadius: 5,
marginTop: 10,
alignItems: 'center'
}}
onPress={() => handleRespondToAlert(item.id)}
>
<Text style={{ color: 'white', fontWeight: 'bold' }}>
Помочь
</Text>
</TouchableOpacity>
</View>
);
return (
<View style={{ flex: 1 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', margin: 15 }}>
Ближайшие оповещения
</Text>
{loading ? (
<Text style={{ textAlign: 'center', margin: 20 }}>
Загрузка...
</Text>
) : (
<FlatList
data={alerts}
renderItem={renderAlert}
keyExtractor={(item) => item.id.toString()}
refreshing={loading}
onRefresh={loadNearbyAlerts}
ListEmptyComponent={
<Text style={{ textAlign: 'center', margin: 20, color: '#666' }}>
Поблизости нет активных оповещений
</Text>
}
/>
)}
</View>
);
};
```
## 🔄 Обновление местоположения в реальном времени
```javascript
class LocationService {
constructor() {
this.watchId = null;
this.lastUpdate = null;
}
startLocationTracking() {
this.watchId = Geolocation.watchPosition(
(position) => {
this.updateLocation(position.coords);
},
(error) => {
console.error('Location error:', error);
},
{
enableHighAccuracy: true,
distanceFilter: 10, // Обновлять при изменении на 10 метров
interval: 30000, // Обновлять каждые 30 секунд
}
);
}
async updateLocation(coords) {
const now = Date.now();
// Не отправляем слишком часто
if (this.lastUpdate && (now - this.lastUpdate) < 30000) {
return;
}
try {
const token = localStorage.getItem('access_token');
await fetch('http://192.168.0.103:8000/api/location/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
latitude: coords.latitude,
longitude: coords.longitude,
accuracy: coords.accuracy,
speed: coords.speed,
heading: coords.heading,
timestamp: new Date().toISOString()
})
});
this.lastUpdate = now;
} catch (error) {
console.error('Failed to update location:', error);
}
}
stopLocationTracking() {
if (this.watchId) {
Geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
}
```
## 🔔 Push уведомления
### Настройка Firebase (Android) / APNS (iOS)
```javascript
import messaging from '@react-native-firebase/messaging';
class PushNotificationService {
async requestPermission() {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Push permissions granted');
await this.getToken();
}
}
async getToken() {
try {
const token = await messaging().getToken();
await this.registerToken(token);
} catch (error) {
console.error('Failed to get push token:', error);
}
}
async registerToken(token) {
const apiToken = localStorage.getItem('access_token');
await fetch('http://192.168.0.103:8000/api/notifications/register-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiToken}`
},
body: JSON.stringify({
token: token,
platform: Platform.OS, // 'ios' or 'android'
app_version: '1.0.0'
});
}
setupForegroundHandler() {
messaging().onMessage(async remoteMessage => {
// Показать уведомление когда приложение активно
Alert.alert(
remoteMessage.notification.title,
remoteMessage.notification.body
);
});
}
setupBackgroundHandler() {
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Background message:', remoteMessage);
});
}
}
```
## 🔐 Безопасность и шифрование
### Хранение токенов
```javascript
import * as Keychain from 'react-native-keychain';
class SecureStorage {
static async storeToken(token) {
try {
await Keychain.setInternetCredentials(
'women_safety_app',
'access_token',
token
);
} catch (error) {
console.error('Failed to store token:', error);
}
}
static async getToken() {
try {
const credentials = await Keychain.getInternetCredentials('women_safety_app');
if (credentials) {
return credentials.password;
}
} catch (error) {
console.error('Failed to get token:', error);
}
return null;
}
static async removeToken() {
try {
await Keychain.resetInternetCredentials('women_safety_app');
} catch (error) {
console.error('Failed to remove token:', error);
}
}
}
```
### Шифрование местоположения
```javascript
import CryptoJS from 'crypto-js';
class LocationEncryption {
static encryptLocation(lat, lng, secretKey) {
const locationData = JSON.stringify({ lat, lng, timestamp: Date.now() });
return CryptoJS.AES.encrypt(locationData, secretKey).toString();
}
static decryptLocation(encryptedData, secretKey) {
try {
const bytes = CryptoJS.AES.decrypt(encryptedData, secretKey);
const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
return JSON.parse(decryptedData);
} catch (error) {
console.error('Failed to decrypt location:', error);
return null;
}
}
}
```
## 📊 Аналитика и мониторинг
### Отслеживание использования
```javascript
class AnalyticsService {
static trackEmergencyAlert(alertType, responseTime) {
// Интеграция с аналитикой (Firebase Analytics, etc.)
analytics().logEvent('emergency_alert_created', {
alert_type: alertType,
response_time_seconds: responseTime,
timestamp: Date.now()
});
}
static trackUserResponse(alertId, responseType) {
analytics().logEvent('emergency_response', {
alert_id: alertId,
response_type: responseType,
timestamp: Date.now()
});
}
static trackLocationUpdate(accuracy) {
analytics().logEvent('location_update', {
accuracy_meters: accuracy,
timestamp: Date.now()
});
}
}
```
## 🧪 Тестирование API
### Unit тесты для API клиента
```javascript
import { APIClient } from './APIClient';
describe('APIClient', () => {
let client;
beforeEach(() => {
client = new APIClient('http://192.168.0.103:8000');
});
test('should login successfully', async () => {
const result = await client.login('testuser', 'testpass');
expect(result.access_token).toBeDefined();
expect(result.token_type).toBe('bearer');
});
test('should create emergency alert', async () => {
// Сначала авторизоваться
await client.login('testuser', 'testpass');
const alert = await client.createAlert({
latitude: 55.7558,
longitude: 37.6176,
alert_type: 'general',
message: 'Test emergency'
});
expect(alert.id).toBeDefined();
expect(alert.alert_type).toBe('general');
expect(alert.status).toBe('active');
});
test('should get nearby alerts', async () => {
await client.login('testuser', 'testpass');
const alerts = await client.getNearbyAlerts(55.7558, 37.6176, 5);
expect(Array.isArray(alerts)).toBe(true);
});
});
```
## 📈 Оптимизация производительности
### Кэширование данных
```javascript
class CacheService {
static cache = new Map();
static CACHE_DURATION = 5 * 60 * 1000; // 5 минут
static set(key, data) {
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
static get(key) {
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > this.CACHE_DURATION) {
this.cache.delete(key);
return null;
}
return cached.data;
}
static async getNearbyAlertsWithCache(lat, lng, radius) {
const cacheKey = `nearby_${lat}_${lng}_${radius}`;
let alerts = this.get(cacheKey);
if (!alerts) {
alerts = await findNearbyAlerts({ latitude: lat, longitude: lng }, radius);
this.set(cacheKey, alerts);
}
return alerts;
}
}
```
Эти примеры покрывают все основные сценарии использования API в реальном мобильном приложении, включая безопасность, производительность и тестирование.