sdf
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-09-26 12:22:14 +09:00
parent ca32dc8867
commit 7c22664daf
33 changed files with 3267 additions and 1429 deletions

View File

@@ -0,0 +1,589 @@
# Примеры использования 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 в реальном мобильном приложении, включая безопасность, производительность и тестирование.