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

617
docs/DATA_SCHEMAS.md Normal file
View File

@@ -0,0 +1,617 @@
# Полные схемы данных для мобильного приложения Women's Safety App
## 📋 Содержание
1. [Схемы авторизации](#схемы-авторизации)
2. [Схемы пользователей](#схемы-пользователей)
3. [Схемы экстренных ситуаций](#схемы-экстренных-ситуаций)
4. [Схемы местоположения](#схемы-местоположения)
5. [Схемы уведомлений](#схемы-уведомлений)
6. [Схемы календаря](#схемы-календаря)
7. [TypeScript интерфейсы](#typescript-интерфейсы)
---
## 🔐 Схемы авторизации
### UserRegister
```json
{
"username": "string", // Имя пользователя (уникальное)
"email": "string", // Email (уникальный)
"password": "string", // Пароль (мин 8 символов)
"full_name": "string?", // Полное имя
"phone": "string?", // Номер телефона
"date_of_birth": "date?", // Дата рождения (YYYY-MM-DD)
"bio": "string?" // Биография (макс 500 символов)
}
```
### UserLogin
```json
{
"username": "string?", // Имя пользователя ИЛИ
"email": "string?", // Email (один из двух обязателен)
"password": "string" // Пароль
}
```
### TokenResponse
```json
{
"access_token": "string", // JWT токен
"token_type": "bearer" // Тип токена
}
```
---
## 👤 Схемы пользователей
### UserProfile (полная информация)
```json
{
"id": 123, // int, ID пользователя
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID пользователя
"username": "testuser", // string, имя пользователя
"email": "test@example.com", // string, email
"phone": "+1234567890", // string?, телефон
"first_name": "John", // string?, имя
"last_name": "Doe", // string?, фамилия
"date_of_birth": "1990-01-01", // date?, дата рождения
"bio": "Краткая биография", // string?, биография
"avatar_url": "https://...", // string?, URL аватара
"location_sharing_enabled": true, // bool, разрешение на геолокацию
"emergency_notifications_enabled": true, // bool, экстренные уведомления
"push_notifications_enabled": true, // bool, push уведомления
"email_verified": false, // bool, email подтвержден
"phone_verified": true, // bool, телефон подтвержден
"is_active": true, // bool, активен ли аккаунт
"created_at": "2024-01-01T10:00:00Z", // datetime, дата регистрации
"updated_at": "2024-01-15T10:00:00Z" // datetime, последнее обновление
}
```
### UserDashboard
```json
{
"user_info": { // Краткая информация о пользователе
"id": 123,
"username": "testuser",
"email": "test@example.com",
"first_name": "John",
"last_name": "Doe"
},
"recent_alerts": [ // Последние оповещения (макс 5)
{
"id": 456,
"alert_type": "general",
"message": "Нужна помощь",
"created_at": "2024-01-15T09:30:00Z",
"is_resolved": false,
"responded_users_count": 2
}
],
"emergency_contacts": [ // Экстренные контакты
{
"id": 789,
"name": "Мама",
"phone_number": "+1234567890",
"relationship": "mother"
}
],
"safety_stats": { // Статистика безопасности
"total_alerts_created": 5,
"total_responses_given": 12,
"safety_checks_count": 45,
"last_safety_check": "2024-01-15T08:00:00Z"
}
}
```
### EmergencyContact
```json
{
"id": 789, // int, ID контакта
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID контакта
"user_id": 123, // int, ID владельца
"name": "Мама", // string, имя контакта
"phone_number": "+1234567890", // string, телефон
"relationship": "mother", // string?, отношение (mother, father, spouse, friend, etc.)
"notes": "Основной контакт", // string?, заметки
"is_active": true, // bool, активен ли контакт
"created_at": "2024-01-01T10:00:00Z", // datetime, дата создания
"updated_at": "2024-01-15T10:00:00Z" // datetime, последнее обновление
}
```
---
## 🆘 Схемы экстренных ситуаций
### EmergencyAlert (полная схема)
```json
{
"id": 123, // int, ID оповещения
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID оповещения
"user_id": 26, // int, ID создателя
"latitude": 55.7558, // float, широта (-90 до 90)
"longitude": 37.6176, // float, долгота (-180 до 180)
"address": "Красная площадь, Москва", // string?, адрес
"alert_type": "general", // AlertType, тип оповещения
"status": "active", // AlertStatus, текущий статус
"message": "Нужна помощь", // string?, описание ситуации
"is_resolved": false, // bool, решено ли
"resolved_at": null, // datetime?, время решения
"resolved_by": null, // int?, кем решено
"resolved_notes": null, // string?, заметки о решении
"contact_emergency_services": true, // bool, связаться со службами
"notify_emergency_contacts": true, // bool, уведомить контакты
"notified_users_count": 5, // int, количество уведомленных
"responded_users_count": 2, // int, количество откликнувшихся
"created_at": "2024-01-15T10:30:00Z", // datetime, время создания
"updated_at": "2024-01-15T10:35:00Z" // datetime, последнее обновление
}
```
### EmergencyResponse (отклик на оповещение)
```json
{
"id": 456, // int, ID отклика
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отклика
"alert_id": 123, // int, ID оповещения
"user_id": 27, // int, ID откликнувшегося
"response_type": "help_on_way", // ResponseType, тип отклика
"message": "Еду к вам!", // string?, сообщение
"eta_minutes": 15, // int?, время прибытия в минутах
"location_sharing": true, // bool, делиться местоположением
"created_at": "2024-01-15T10:32:00Z" // datetime, время создания
}
```
### EmergencyReport (отчет о происшествии)
```json
{
"id": 789, // int, ID отчета
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отчета
"user_id": 26, // int?, ID автора (null для анонимных)
"latitude": 55.7558, // float, широта
"longitude": 37.6176, // float, долгота
"address": "Тверская улица", // string?, адрес
"report_type": "harassment", // string, тип происшествия
"description": "Подробное описание...", // string, описание (10-1000 символов)
"is_anonymous": false, // bool, анонимный отчет
"severity": 4, // int, серьезность (1-5)
"status": "pending", // string, статус (pending/investigating/resolved)
"created_at": "2024-01-15T10:45:00Z" // datetime, время создания
}
```
### SafetyCheck (отметка безопасности)
```json
{
"id": 101, // int, ID отметки
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отметки
"user_id": 26, // int, ID пользователя
"message": "Добрался домой безопасно", // string?, сообщение
"location_latitude": 55.7600, // float?, широта
"location_longitude": 37.6100, // float?, долгота
"created_at": "2024-01-15T22:00:00Z" // datetime, время создания
}
```
### EmergencyStatistics
```json
{
"total_alerts": 150, // int, общее количество оповещений
"active_alerts": 12, // int, активные оповещения
"resolved_alerts": 138, // int, решенные оповещения
"total_responders": 89, // int, всего откликнувшихся
"avg_response_time_minutes": 8.5 // float, среднее время отклика
}
```
### NearbyAlert (ближайшие оповещения)
```json
{
"id": 123, // int, ID оповещения
"alert_type": "medical", // string, тип оповещения
"latitude": 55.7558, // float, широта
"longitude": 37.6176, // float, долгота
"address": "Больница №1", // string?, адрес
"distance_km": 2.5, // float, расстояние в километрах
"created_at": "2024-01-15T09:15:00Z", // datetime, время создания
"responded_users_count": 3 // int, количество откликов
}
```
---
## 📍 Схемы местоположения
### LocationUpdate
```json
{
"latitude": 55.7558, // float, широта (-90 до 90)
"longitude": 37.6176, // float, долгота (-180 до 180)
"accuracy": 10.0, // float?, точность в метрах
"altitude": 150.0, // float?, высота в метрах
"speed": 0.0, // float?, скорость м/с
"heading": 90.0, // float?, направление (0-360 градусов)
"timestamp": "2024-01-15T10:30:00Z" // datetime?, время получения координат
}
```
### NearbyUser
```json
{
"user_id": 27, // int, ID пользователя
"distance_meters": 500.0, // float, расстояние в метрах
"latitude": 55.7568, // float, широта (может быть приблизительной)
"longitude": 37.6186, // float, долгота (может быть приблизительной)
"last_seen": "2024-01-15T10:25:00Z", // datetime, последнее обновление местоположения
"is_available": true // bool, готов ли помочь
}
```
### GeocodeResult
```json
{
"address": "Красная площадь, 1, Москва, Россия", // string, полный адрес
"street": "Красная площадь", // string?, улица
"house_number": "1", // string?, номер дома
"city": "Москва", // string?, город
"state": "Москва", // string?, регион/область
"country": "Россия", // string?, страна
"postal_code": "109012", // string?, почтовый индекс
"latitude": 55.7558, // float, широта
"longitude": 37.6176 // float, долгота
}
```
---
## 🔔 Схемы уведомлений
### NotificationCreate
```json
{
"user_id": 123, // int, ID получателя
"title": "Экстренное оповещение", // string, заголовок
"message": "Новое оповещение рядом с вами", // string, текст сообщения
"type": "emergency_alert", // string, тип уведомления
"data": { // object?, дополнительные данные
"alert_id": 456,
"latitude": 55.7558,
"longitude": 37.6176
},
"priority": "high", // string?, приоритет (low/normal/high/urgent)
"schedule_at": null // datetime?, время отправки (null = сейчас)
}
```
### PushTokenRegistration
```json
{
"token": "fcm_or_apns_token_here", // string, токен устройства
"platform": "ios", // string, платформа (ios/android)
"app_version": "1.0.0", // string?, версия приложения
"device_info": { // object?, информация об устройстве
"model": "iPhone 14",
"os_version": "17.0"
}
}
```
### NotificationHistory
```json
{
"id": 789, // int, ID уведомления
"user_id": 123, // int, ID получателя
"title": "Экстренное оповещение", // string, заголовок
"message": "Текст уведомления", // string, сообщение
"type": "emergency_alert", // string, тип
"status": "delivered", // string, статус (sent/delivered/read/failed)
"sent_at": "2024-01-15T10:30:00Z", // datetime, время отправки
"delivered_at": "2024-01-15T10:30:05Z", // datetime?, время доставки
"read_at": null // datetime?, время прочтения
}
```
---
## 📅 Схемы календаря
### CalendarEntry
```json
{
"id": 456, // int, ID записи
"user_id": 123, // int, ID пользователя
"date": "2024-01-15", // date, дата записи (YYYY-MM-DD)
"entry_type": "period_start", // string, тип записи
"notes": "Болезненные ощущения", // string?, заметки
"mood_score": 3, // int?, настроение (1-5)
"energy_level": 4, // int?, уровень энергии (1-5)
"symptoms": [ // array?, симптомы
"headache",
"fatigue",
"cramps"
],
"flow_intensity": "medium", // string?, интенсивность (light/medium/heavy)
"temperature": 36.6, // float?, температура тела
"weight": 65.5, // float?, вес
"created_at": "2024-01-15T08:00:00Z", // datetime, время создания
"updated_at": "2024-01-15T08:05:00Z" // datetime, последнее обновление
}
```
### CalendarAnalytics
```json
{
"cycle_length": 28, // int?, средняя длина цикла
"period_length": 5, // int?, средняя длина месячных
"next_period_prediction": "2024-02-10", // date?, прогноз следующих месячных
"fertility_window": { // object?, окно фертильности
"start": "2024-01-20",
"end": "2024-01-25"
},
"mood_trends": { // object?, тренды настроения
"average_score": 3.5,
"lowest_day": 2,
"highest_day": 12
},
"symptoms_frequency": { // object?, частота симптомов
"headache": 0.3,
"cramps": 0.8,
"fatigue": 0.6
}
}
```
---
## 📱 TypeScript интерфейсы
```typescript
// Перечисления
export enum AlertType {
GENERAL = 'general',
MEDICAL = 'medical',
VIOLENCE = 'violence',
HARASSMENT = 'harassment',
UNSAFE_AREA = 'unsafe_area',
ACCIDENT = 'accident',
FIRE = 'fire',
NATURAL_DISASTER = 'natural_disaster'
}
export enum AlertStatus {
ACTIVE = 'active',
RESOLVED = 'resolved',
CANCELLED = 'cancelled',
INVESTIGATING = 'investigating'
}
export enum ResponseType {
HELP_ON_WAY = 'help_on_way',
CONTACTED_AUTHORITIES = 'contacted_authorities',
SAFE_NOW = 'safe_now',
FALSE_ALARM = 'false_alarm',
INVESTIGATING = 'investigating',
RESOLVED = 'resolved'
}
// Интерфейсы авторизации
export interface UserRegister {
username: string;
email: string;
password: string;
full_name?: string;
phone?: string;
date_of_birth?: string;
bio?: string;
}
export interface UserLogin {
username?: string;
email?: string;
password: string;
}
export interface TokenResponse {
access_token: string;
token_type: string;
}
// Интерфейсы пользователя
export interface UserProfile {
id: number;
uuid: string;
username: string;
email: string;
phone?: string;
first_name?: string;
last_name?: string;
date_of_birth?: string;
bio?: string;
avatar_url?: string;
location_sharing_enabled: boolean;
emergency_notifications_enabled: boolean;
push_notifications_enabled: boolean;
email_verified: boolean;
phone_verified: boolean;
is_active: boolean;
created_at: string;
updated_at?: string;
}
export interface EmergencyContact {
id: number;
uuid: string;
user_id: number;
name: string;
phone_number: string;
relationship?: string;
notes?: string;
is_active: boolean;
created_at: string;
updated_at?: string;
}
// Интерфейсы экстренных ситуаций
export interface EmergencyAlertCreate {
latitude: number;
longitude: number;
alert_type: AlertType;
message?: string;
address?: string;
contact_emergency_services?: boolean;
notify_emergency_contacts?: boolean;
}
export interface EmergencyAlert {
id: number;
uuid: string;
user_id: number;
latitude: number;
longitude: number;
address?: string;
alert_type: AlertType;
status: AlertStatus;
message?: string;
is_resolved: boolean;
resolved_at?: string;
resolved_by?: number;
resolved_notes?: string;
contact_emergency_services: boolean;
notify_emergency_contacts: boolean;
notified_users_count: number;
responded_users_count: number;
created_at: string;
updated_at?: string;
}
export interface EmergencyResponseCreate {
response_type: ResponseType;
message?: string;
eta_minutes?: number;
location_sharing?: boolean;
}
export interface EmergencyResponse {
id: number;
uuid: string;
alert_id: number;
user_id: number;
response_type: ResponseType;
message?: string;
eta_minutes?: number;
location_sharing: boolean;
created_at: string;
}
export interface EmergencyStatistics {
total_alerts: number;
active_alerts: number;
resolved_alerts: number;
total_responders: number;
avg_response_time_minutes: number;
}
export interface NearbyAlert {
id: number;
alert_type: string;
latitude: number;
longitude: number;
address?: string;
distance_km: number;
created_at: string;
responded_users_count: number;
}
// Интерфейсы местоположения
export interface LocationUpdate {
latitude: number;
longitude: number;
accuracy?: number;
altitude?: number;
speed?: number;
heading?: number;
timestamp?: string;
}
export interface NearbyUser {
user_id: number;
distance_meters: number;
latitude: number;
longitude: number;
last_seen: string;
is_available: boolean;
}
// Интерфейсы уведомлений
export interface PushTokenRegistration {
token: string;
platform: 'ios' | 'android';
app_version?: string;
device_info?: {
model?: string;
os_version?: string;
};
}
// API клиент
export interface APIClient {
setToken(token: string): void;
login(username: string, password: string): Promise<TokenResponse>;
register(userData: UserRegister): Promise<UserProfile>;
getProfile(): Promise<UserProfile>;
createAlert(alertData: EmergencyAlertCreate): Promise<EmergencyAlert>;
getNearbyAlerts(lat: number, lng: number, radius?: number): Promise<NearbyAlert[]>;
respondToAlert(alertId: number, response: EmergencyResponseCreate): Promise<EmergencyResponse>;
updateLocation(location: LocationUpdate): Promise<void>;
createSafetyCheck(data: { message?: string; location_latitude?: number; location_longitude?: number }): Promise<any>;
}
```
---
## 📋 Валидация данных
### Ограничения полей
- **Координаты**: latitude (-90 до 90), longitude (-180 до 180)
- **Пароль**: минимум 8 символов, максимум 70 (для совместимости с bcrypt)
- **Email**: валидный формат email
- **Телефон**: рекомендуется международный формат (+1234567890)
- **Сообщения**: максимум 500 символов для alert message, 200 для safety check
- **Описания**: 10-1000 символов для emergency reports
### Обязательные поля
- При регистрации: username, email, password
- При создании оповещения: latitude, longitude, alert_type
- При отклике: response_type
- При обновлении местоположения: latitude, longitude
### Рекомендации по обработке ошибок
```typescript
interface APIError {
detail: string;
status_code: number;
field_errors?: {
[field: string]: string[];
};
}
// Обработка ошибок валидации
try {
await api.createAlert(alertData);
} catch (error) {
if (error.status_code === 422) {
// Показать ошибки валидации для каждого поля
Object.entries(error.field_errors || {}).forEach(([field, errors]) => {
console.error(`${field}: ${errors.join(', ')}`);
});
}
}
```
Эти схемы данных обеспечивают полную интеграцию мобильного приложения с Women's Safety App API.

View File

@@ -0,0 +1,600 @@
# Emergency Service API Документация
## Обзор
Emergency Service предоставляет API для работы с экстренными ситуациями, включая создание оповещений, получение статистики и управление чрезвычайными ситуациями.
**Базовый URL:** `http://192.168.0.103:8002`
**Аутентификация:** Bearer Token (JWT)
---
## Схемы данных (Schemas)
### 1. AlertType (Enum)
Типы экстренных ситуаций:
```python
GENERAL = "general" # Общая помощь
MEDICAL = "medical" # Медицинская помощь
VIOLENCE = "violence" # Насилие
HARASSMENT = "harassment" # Преследование
UNSAFE_AREA = "unsafe_area" # Небезопасная зона
ACCIDENT = "accident" # ДТП/авария
FIRE = "fire" # Пожар
NATURAL_DISASTER = "natural_disaster" # Стихийное бедствие
```
### 2. AlertStatus (Enum)
Статусы оповещений:
```python
ACTIVE = "active" # Активно
RESOLVED = "resolved" # Решено
CANCELLED = "cancelled" # Отменено
INVESTIGATING = "investigating" # Расследуется
```
### 3. ResponseType (Enum)
Типы ответов на оповещения:
```python
HELP_ON_WAY = "help_on_way" # Помощь в пути
CONTACTED_AUTHORITIES = "contacted_authorities" # Связались с властями
SAFE_NOW = "safe_now" # Сейчас в безопасности
FALSE_ALARM = "false_alarm" # Ложная тревога
INVESTIGATING = "investigating" # Расследуется
RESOLVED = "resolved" # Решено
```
### 4. EmergencyAlertCreate
Создание экстренного оповещения:
```json
{
"latitude": 55.7558, // float, координата широты (-90 до 90)
"longitude": 37.6176, // float, координата долготы (-180 до 180)
"alert_type": "general", // AlertType, тип оповещения
"message": "Описание ситуации", // string?, описание (макс 500 символов)
"address": "Адрес места", // string?, адрес (макс 500 символов)
"contact_emergency_services": true, // bool, связываться ли со службами экстренного реагирования
"notify_emergency_contacts": true // bool, уведомлять ли экстренные контакты
}
```
### 5. EmergencyAlertResponse
Ответ с данными оповещения:
```json
{
"id": 123, // int, ID оповещения
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID оповещения
"user_id": 26, // int, ID пользователя
"latitude": 55.7558, // float, широта
"longitude": 37.6176, // float, долгота
"address": "Красная площадь, Москва", // string?, адрес
"alert_type": "general", // AlertType, тип оповещения
"status": "active", // AlertStatus, статус
"message": "Нужна помощь", // string?, описание
"is_resolved": false, // bool, решено ли
"resolved_at": null, // datetime?, время решения
"resolved_notes": null, // string?, заметки о решении
"notified_users_count": 5, // int, количество уведомленных пользователей
"responded_users_count": 2, // int, количество ответивших пользователей
"created_at": "2024-01-15T10:30:00Z", // datetime, время создания
"updated_at": "2024-01-15T10:35:00Z", // datetime?, время обновления
"user_name": "Test User", // string?, имя пользователя
"user_phone": "+1234567890" // string?, телефон пользователя
}
```
### 6. EmergencyResponseCreate
Создание ответа на оповещение:
```json
{
"response_type": "help_on_way", // ResponseType, тип ответа
"message": "Еду к вам!", // string?, сообщение (макс 500 символов)
"eta_minutes": 15, // int?, предполагаемое время прибытия в минутах (0-240)
"location_sharing": true // bool, делиться ли местоположением
}
```
### 7. EmergencyResponseResponse
Ответ с данными отклика:
```json
{
"id": 456, // int, ID отклика
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отклика
"alert_id": 123, // int, ID оповещения
"user_id": 27, // int, ID откликнувшегося пользователя
"response_type": "help_on_way", // ResponseType, тип отклика
"message": "Еду к вам!", // string?, сообщение
"eta_minutes": 15, // int?, время прибытия
"location_sharing": true, // bool, делиться местоположением
"created_at": "2024-01-15T10:32:00Z", // datetime, время создания
"responder_name": "Helper User", // string?, имя откликнувшегося
"responder_phone": "+9876543210" // string?, телефон откликнувшегося
}
```
### 8. EmergencyReportCreate
Создание отчета о происшествии:
```json
{
"latitude": 55.7558, // float, широта (-90 до 90)
"longitude": 37.6176, // float, долгота (-180 до 180)
"report_type": "violence", // string, тип происшествия
"description": "Детальное описание происшествия...", // string, описание (10-1000 символов)
"address": "Адрес происшествия", // string?, адрес (макс 500 символов)
"is_anonymous": false, // bool, анонимный отчет
"severity": 4 // int, серьезность от 1 до 5
}
```
### 9. EmergencyReportResponse
Ответ с данными отчета:
```json
{
"id": 789, // int, ID отчета
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отчета
"user_id": 26, // int?, ID пользователя (null для анонимных)
"latitude": 55.7558, // float, широта
"longitude": 37.6176, // float, долгота
"address": "Красная площадь", // string?, адрес
"report_type": "violence", // string, тип происшествия
"description": "Описание...", // string, описание
"is_anonymous": false, // bool, анонимный ли
"severity": 4, // int, серьезность (1-5)
"status": "pending", // string, статус (pending/investigating/resolved)
"created_at": "2024-01-15T10:45:00Z" // datetime, время создания
}
```
### 10. SafetyCheckCreate
Создание отметки безопасности:
```json
{
"message": "Я в порядке", // string?, сообщение (макс 200 символов)
"location_latitude": 55.7558, // float?, широта (-90 до 90)
"location_longitude": 37.6176 // float?, долгота (-180 до 180)
}
```
### 11. SafetyCheckResponse
Ответ с данными отметки безопасности:
```json
{
"id": 101, // int, ID отметки
"uuid": "8ed4fb51-8a90-4b22...", // string, UUID отметки
"user_id": 26, // int, ID пользователя
"message": "Я в порядке", // string?, сообщение
"location_latitude": 55.7558, // float?, широта
"location_longitude": 37.6176, // float?, долгота
"created_at": "2024-01-15T11:00:00Z" // datetime, время создания
}
```
### 12. EmergencyStatistics
Статистика экстренных ситуаций:
```json
{
"total_alerts": 150, // int, общее количество оповещений
"active_alerts": 12, // int, активные оповещения
"resolved_alerts": 138, // int, решенные оповещения
"total_responders": 89, // int, общее количество откликнувшихся
"avg_response_time_minutes": 8.5 // float, среднее время отклика в минутах
}
```
### 13. NearbyAlertResponse
Ближайшие оповещения:
```json
{
"id": 123, // int, ID оповещения
"alert_type": "medical", // string, тип оповещения
"latitude": 55.7558, // float, широта
"longitude": 37.6176, // float, долгота
"address": "Больница №1", // string?, адрес
"distance_km": 2.5, // float, расстояние в километрах
"created_at": "2024-01-15T09:15:00Z", // datetime, время создания
"responded_users_count": 3 // int, количество откликов
}
```
---
## API Endpoints
### 1. Проверка здоровья сервиса
**GET** `/health`
**Описание:** Проверка статуса работы сервиса
**Авторизация:** Не требуется
**Ответ:**
```json
{
"status": "healthy",
"service": "emergency_service"
}
```
---
### 2. Создание экстренного оповещения
**POST** `/api/v1/alert`
**Описание:** Создание нового экстренного оповещения
**Авторизация:** Bearer Token
**Тело запроса:** `EmergencyAlertCreate`
**Пример запроса:**
```bash
curl -X POST http://192.168.0.103:8002/api/v1/alert \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"latitude": 55.7558,
"longitude": 37.6176,
"alert_type": "general",
"message": "Нужна помощь, подозрительная активность",
"address": "Красная площадь, Москва"
}'
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `EmergencyAlertResponse`
---
### 3. Ответить на оповещение
**POST** `/api/v1/alert/{alert_id}/respond`
**Описание:** Отправка отклика на экстренное оповещение
**Авторизация:** Bearer Token
**Параметры URL:**
- `alert_id` (int) - ID оповещения
**Тело запроса:** `EmergencyResponseCreate`
**Пример запроса:**
```bash
curl -X POST http://192.168.0.103:8002/api/v1/alert/123/respond \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"response_type": "help_on_way",
"message": "Еду к вам, буду через 10 минут!",
"eta_minutes": 10
}'
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `EmergencyResponseResponse`
**Ошибки:**
- `404` - Оповещение не найдено
- `400` - Пользователь уже откликнулся на это оповещение
---
### 4. Решить оповещение
**PUT** `/api/v1/alert/{alert_id}/resolve`
**Описание:** Помечает оповещение как решенное
**Авторизация:** Bearer Token
**Параметры URL:**
- `alert_id` (int) - ID оповещения
**Пример запроса:**
```bash
curl -X PUT http://192.168.0.103:8002/api/v1/alert/123/resolve \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Успешный ответ:** `200 OK`
```json
{
"message": "Alert resolved successfully"
}
```
**Ошибки:**
- `404` - Оповещение не найдено
- `403` - Только создатель может решить оповещение
---
### 5. Обновить оповещение
**PUT** `/api/v1/alert/{alert_id}`
**Описание:** Обновление существующего оповещения
**Авторизация:** Bearer Token
**Параметры URL:**
- `alert_id` (int) - ID оповещения
**Тело запроса:** `EmergencyAlertUpdate`
```json
{
"status": "resolved", // AlertStatus?, новый статус
"message": "Обновленное описание", // string?, новое описание
"resolved_notes": "Помощь получена" // string?, заметки о решении
}
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `EmergencyAlertResponse`
---
### 6. Получить мои оповещения
**GET** `/api/v1/alerts/my`
**Описание:** Получение оповещений текущего пользователя
**Авторизация:** Bearer Token
**Пример запроса:**
```bash
curl -X GET http://192.168.0.103:8002/api/v1/alerts/my \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[EmergencyAlertResponse]`
---
### 7. Получить активные оповещения
**GET** `/api/v1/alerts/active`
**Описание:** Получение всех активных оповещений
**Авторизация:** Bearer Token
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[EmergencyAlertResponse]`
---
### 8. Получить ближайшие оповещения
**GET** `/api/v1/alerts/nearby`
**Описание:** Поиск оповещений в указанном радиусе
**Авторизация:** Bearer Token
**Query параметры:**
- `latitude` (float, обязательный) - Широта (-90 до 90)
- `longitude` (float, обязательный) - Долгота (-180 до 180)
- `radius_km` (float, опциональный) - Радиус поиска в км (по умолчанию 10.0, макс 100.0)
**Пример запроса:**
```bash
curl -X GET "http://192.168.0.103:8002/api/v1/alerts/nearby?latitude=55.7558&longitude=37.6176&radius_km=5.0" \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[NearbyAlertResponse]` (отсортированы по расстоянию)
---
### 9. Получить отклики на оповещение
**GET** `/api/v1/alert/{alert_id}/responses`
**Описание:** Получение всех откликов на конкретное оповещение
**Авторизация:** Bearer Token
**Параметры URL:**
- `alert_id` (int) - ID оповещения
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[EmergencyResponseResponse]`
---
### 10. Создать отчет о происшествии
**POST** `/api/v1/report`
**Описание:** Создание отчета о происшествии
**Авторизация:** Bearer Token
**Тело запроса:** `EmergencyReportCreate`
**Пример запроса:**
```bash
curl -X POST http://192.168.0.103:8002/api/v1/report \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"latitude": 55.7558,
"longitude": 37.6176,
"report_type": "harassment",
"description": "Преследование в районе метро...",
"severity": 4,
"is_anonymous": false
}'
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `EmergencyReportResponse`
---
### 11. Получить отчеты
**GET** `/api/v1/reports`
**Описание:** Получение списка отчетов о происшествиях
**Авторизация:** Bearer Token
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[EmergencyReportResponse]`
---
### 12. Создать отметку безопасности
**POST** `/api/v1/safety-check`
**Описание:** Отметка о том, что пользователь в безопасности
**Авторизация:** Bearer Token
**Тело запроса:** `SafetyCheckCreate`
**Пример запроса:**
```bash
curl -X POST http://192.168.0.103:8002/api/v1/safety-check \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"message": "Добрался домой, все хорошо",
"location_latitude": 55.7558,
"location_longitude": 37.6176
}'
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `SafetyCheckResponse`
---
### 13. Получить отметки безопасности
**GET** `/api/v1/safety-checks`
**Описание:** Получение отметок безопасности пользователя
**Авторизация:** Bearer Token
**Успешный ответ:** `200 OK`
**Тело ответа:** `List[SafetyCheckResponse]`
---
### 14. Получить статистику
**GET** `/api/v1/stats`
**Описание:** Получение статистики по экстренным ситуациям
**Авторизация:** Bearer Token
**Пример запроса:**
```bash
curl -X GET http://192.168.0.103:8002/api/v1/stats \
-H "Authorization: Bearer YOUR_TOKEN"
```
**Успешный ответ:** `200 OK`
**Тело ответа:** `EmergencyStatistics`
---
## Коды ошибок
### Общие ошибки
- `400 Bad Request` - Неверные данные в запросе
- `401 Unauthorized` - Требуется авторизация
- `403 Forbidden` - Недостаточно прав
- `404 Not Found` - Ресурс не найден
- `422 Unprocessable Entity` - Ошибка валидации данных
- `500 Internal Server Error` - Внутренняя ошибка сервера
### Специфические ошибки
- `400` - "You have already responded to this alert" (при повторном отклике)
- `403` - "Only alert creator can resolve/update the alert" (при попытке изменения чужого оповещения)
- `404` - "Alert not found" (оповещение не найдено)
---
## Примеры интеграции с мобильным приложением
### 1. Создание экстренного оповещения
```javascript
const createEmergencyAlert = async (alertData) => {
const response = await fetch('http://192.168.0.103:8002/api/v1/alert', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userToken}`
},
body: JSON.stringify({
latitude: alertData.latitude,
longitude: alertData.longitude,
alert_type: alertData.type || 'general',
message: alertData.message,
address: alertData.address
})
});
return await response.json();
};
```
### 2. Получение ближайших оповещений
```javascript
const getNearbyAlerts = async (latitude, longitude, radiusKm = 10) => {
const params = new URLSearchParams({
latitude: latitude.toString(),
longitude: longitude.toString(),
radius_km: radiusKm.toString()
});
const response = await fetch(
`http://192.168.0.103:8002/api/v1/alerts/nearby?${params}`,
{
headers: {
'Authorization': `Bearer ${userToken}`
}
}
);
return await response.json();
};
```
### 3. Отклик на оповещение
```javascript
const respondToAlert = async (alertId, responseData) => {
const response = await fetch(
`http://192.168.0.103:8002/api/v1/alert/${alertId}/respond`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userToken}`
},
body: JSON.stringify({
response_type: responseData.type,
message: responseData.message,
eta_minutes: responseData.etaMinutes
})
}
);
return await response.json();
};
```
---
## Дополнительные возможности
### WebSocket подключения (планируется)
Для получения уведомлений в реальном времени о новых оповещениях и откликах.
### Push уведомления
API интегрируется с Notification Service для отправки push уведомлений на мобильные устройства.
### Геолокация
Все координаты используют WGS84 (стандарт GPS). Расстояния вычисляются по формуле гаверсинусов.
### Безопасность
- Все данные о местоположении шифруются
- Анонимные отчеты не содержат идентификационных данных
- JWT токены имеют ограниченный срок действия

495
docs/MOBILE_API_SPECS.md Normal file
View File

@@ -0,0 +1,495 @@
# API Спецификации для мобильного приложения Women's Safety App
## Обзор системы
Микросервисная архитектура с 6 сервисами:
- **API Gateway** (порт 8000) - точка входа
- **User Service** (порт 8001) - управление пользователями
- **Emergency Service** (порт 8002) - экстренные ситуации
- **Location Service** (порт 8003) - геолокация
- **Calendar Service** (порт 8004) - календарь здоровья
- **Notification Service** (порт 8005) - уведомления
**Базовый URL:** `http://192.168.0.103:8000` (API Gateway)
**Прямые сервисы:** `http://192.168.0.103:800X` (где X - номер сервиса)
---
## 🔐 Система авторизации
### Регистрация пользователя
**POST** `/api/v1/auth/register`
```json
{
"username": "testuser",
"email": "test@example.com",
"password": "password123",
"full_name": "Test User",
"phone": "+1234567890"
}
```
### Вход в систему
**POST** `/api/v1/auth/login`
```json
{
"username": "testuser", // или "email": "test@example.com"
"password": "password123"
}
```
**Ответ:**
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
```
---
## 👤 User Service API
### Профиль пользователя
**GET** `/api/v1/user/profile`
**Ответ:** Полная информация о пользователе
**PUT** `/api/v1/user/profile` - Обновление профиля
### Дашборд пользователя
**GET** `/api/v1/user/dashboard`
**Ответ:**
```json
{
"user_info": {...},
"recent_alerts": [...],
"emergency_contacts": [...],
"safety_stats": {...}
}
```
### Экстренные контакты
**GET** `/api/v1/user/emergency-contacts` - Список контактов
**POST** `/api/v1/user/emergency-contacts` - Добавить контакт
**PUT** `/api/v1/user/emergency-contacts/{id}` - Обновить контакт
**DELETE** `/api/v1/user/emergency-contacts/{id}` - Удалить контакт
**Схема контакта:**
```json
{
"name": "Мама",
"phone_number": "+1234567890",
"relationship": "mother",
"notes": "Основной контакт",
"is_active": true
}
```
---
## 🆘 Emergency Service API (Полная спецификация)
### Типы данных
```typescript
enum AlertType {
GENERAL = "general",
MEDICAL = "medical",
VIOLENCE = "violence",
HARASSMENT = "harassment",
UNSAFE_AREA = "unsafe_area",
ACCIDENT = "accident",
FIRE = "fire",
NATURAL_DISASTER = "natural_disaster"
}
enum AlertStatus {
ACTIVE = "active",
RESOLVED = "resolved",
CANCELLED = "cancelled",
INVESTIGATING = "investigating"
}
enum ResponseType {
HELP_ON_WAY = "help_on_way",
CONTACTED_AUTHORITIES = "contacted_authorities",
SAFE_NOW = "safe_now",
FALSE_ALARM = "false_alarm"
}
```
### Основные endpoints
#### 1. Создание экстренного оповещения
**POST** `/api/v1/alert` (через Emergency Service напрямую: порт 8002)
```json
{
"latitude": 55.7558,
"longitude": 37.6176,
"alert_type": "general",
"message": "Нужна помощь",
"address": "Красная площадь, Москва",
"contact_emergency_services": true,
"notify_emergency_contacts": true
}
```
#### 2. Получение оповещений
- **GET** `/api/v1/alerts/my` - Мои оповещения
- **GET** `/api/v1/alerts/active` - Активные оповещения
- **GET** `/api/v1/alerts/nearby?latitude=55.7558&longitude=37.6176&radius_km=10` - Ближайшие
#### 3. Отклик на оповещение
**POST** `/api/v1/alert/{alert_id}/respond`
```json
{
"response_type": "help_on_way",
"message": "Еду к вам!",
"eta_minutes": 15,
"location_sharing": true
}
```
#### 4. Управление оповещением
- **PUT** `/api/v1/alert/{alert_id}` - Обновить оповещение
- **PUT** `/api/v1/alert/{alert_id}/resolve` - Пометить как решенное
- **GET** `/api/v1/alert/{alert_id}/responses` - Получить отклики
#### 5. Отчеты о происшествиях
**POST** `/api/v1/report`
```json
{
"latitude": 55.7558,
"longitude": 37.6176,
"report_type": "harassment",
"description": "Описание происшествия",
"severity": 4,
"is_anonymous": false
}
```
#### 6. Отметки безопасности
**POST** `/api/v1/safety-check`
```json
{
"message": "Я в безопасности",
"location_latitude": 55.7558,
"location_longitude": 37.6176
}
```
#### 7. Статистика
**GET** `/api/v1/stats`
```json
{
"total_alerts": 150,
"active_alerts": 12,
"resolved_alerts": 138,
"total_responders": 89,
"avg_response_time_minutes": 8.5
}
```
---
## 📍 Location Service API
### Обновление местоположения
**POST** `/api/v1/location/update`
```json
{
"latitude": 55.7558,
"longitude": 37.6176,
"accuracy": 10.0,
"altitude": 150.0
}
```
### Поиск пользователей рядом
**GET** `/api/v1/nearby-users?latitude=55.7558&longitude=37.6176&radius_km=1.0`
### Геокодирование
**GET** `/api/v1/geocode/reverse?latitude=55.7558&longitude=37.6176`
**GET** `/api/v1/geocode/forward?address=Красная площадь, Москва`
---
## 📅 Calendar Service API
### Календарь здоровья
**GET** `/api/v1/calendar/entries` - Записи календаря
**POST** `/api/v1/calendar/entries` - Добавить запись
**Схема записи:**
```json
{
"date": "2024-01-15",
"entry_type": "period_start", // period_start, period_end, mood, symptoms
"notes": "Заметки",
"mood_score": 4, // 1-5
"symptoms": ["headache", "fatigue"]
}
```
### Аналитика
**GET** `/api/v1/calendar/analytics` - Аналитика цикла
---
## 🔔 Notification Service API
### Отправка уведомления
**POST** `/api/v1/send-notification`
```json
{
"user_id": 123,
"title": "Экстренное оповещение",
"message": "Новое оповещение рядом с вами",
"type": "emergency_alert",
"data": {"alert_id": 456}
}
```
### Push токены
**POST** `/api/v1/push-token`
```json
{
"token": "fcm_or_apns_token",
"platform": "ios" // или "android"
}
```
---
## 🔗 Интеграция для мобильного приложения
### Базовый HTTP клиент
```javascript
class WomenSafetyAPI {
constructor(baseUrl = 'http://192.168.0.103:8000') {
this.baseUrl = baseUrl;
this.token = null;
}
setToken(token) {
this.token = token;
}
async request(method, endpoint, data = null) {
const config = {
method,
headers: {
'Content-Type': 'application/json',
}
};
if (this.token) {
config.headers.Authorization = `Bearer ${this.token}`;
}
if (data && (method === 'POST' || method === 'PUT')) {
config.body = JSON.stringify(data);
}
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, config);
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'API Error');
}
return await response.json();
}
}
```
### Основные методы API
```javascript
class EmergencyAPI extends WomenSafetyAPI {
// Авторизация
async login(username, password) {
const response = await this.request('POST', '/api/v1/auth/login', {
username,
password
});
this.setToken(response.access_token);
return response;
}
async register(userData) {
return await this.request('POST', '/api/v1/auth/register', userData);
}
// Экстренные ситуации (через порт 8002)
async createAlert(alertData) {
const emergencyUrl = this.baseUrl.replace('8000', '8002');
return await fetch(`${emergencyUrl}/api/v1/alert`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(alertData)
}).then(r => r.json());
}
async getNearbyAlerts(latitude, longitude, radiusKm = 10) {
const emergencyUrl = this.baseUrl.replace('8000', '8002');
const params = new URLSearchParams({
latitude: latitude.toString(),
longitude: longitude.toString(),
radius_km: radiusKm.toString()
});
return await fetch(`${emergencyUrl}/api/v1/alerts/nearby?${params}`, {
headers: { 'Authorization': `Bearer ${this.token}` }
}).then(r => r.json());
}
async respondToAlert(alertId, responseData) {
const emergencyUrl = this.baseUrl.replace('8000', '8002');
return await fetch(`${emergencyUrl}/api/v1/alert/${alertId}/respond`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(responseData)
}).then(r => r.json());
}
// Профиль пользователя
async getProfile() {
return await this.request('GET', '/api/v1/user/profile');
}
async getDashboard() {
return await this.request('GET', '/api/v1/user/dashboard');
}
// Экстренные контакты
async getEmergencyContacts() {
return await this.request('GET', '/api/v1/user/emergency-contacts');
}
async addEmergencyContact(contactData) {
return await this.request('POST', '/api/v1/user/emergency-contacts', contactData);
}
// Местоположение
async updateLocation(locationData) {
return await this.request('POST', '/api/v1/location/update', locationData);
}
// Отметка безопасности
async createSafetyCheck(safetyData) {
const emergencyUrl = this.baseUrl.replace('8000', '8002');
return await fetch(`${emergencyUrl}/api/v1/safety-check`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(safetyData)
}).then(r => r.json());
}
}
```
### Примеры использования
```javascript
// Инициализация
const api = new EmergencyAPI('http://192.168.0.103:8000');
// Авторизация
await api.login('testuser', 'password123');
// Создание экстренного оповещения
const alert = await api.createAlert({
latitude: 55.7558,
longitude: 37.6176,
alert_type: 'general',
message: 'Нужна помощь!',
address: 'Красная площадь, Москва'
});
// Поиск ближайших оповещений
const nearbyAlerts = await api.getNearbyAlerts(55.7558, 37.6176, 5);
// Отклик на оповещение
const response = await api.respondToAlert(alert.id, {
response_type: 'help_on_way',
message: 'Еду к вам!',
eta_minutes: 10
});
// Получение профиля
const profile = await api.getProfile();
// Отметка безопасности
const safetyCheck = await api.createSafetyCheck({
message: 'Добрался домой безопасно',
location_latitude: 55.7600,
location_longitude: 37.6100
});
```
---
## 🛡️ Безопасность и аутентификация
### JWT токены
- Время жизни: настраивается в конфигурации
- Формат: `Bearer {token}`
- Включают: user_id, email, exp (время истечения)
### Заголовки запросов
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
```
### Коды ошибок
- `401 Unauthorized` - Неверные учетные данные
- `403 Forbidden` - Недостаточно прав
- `422 Unprocessable Entity` - Ошибки валидации
- `500 Internal Server Error` - Внутренняя ошибка
---
## 📱 Рекомендации для мобильной разработки
### Push уведомления
1. Зарегистрируйте push токен через `/api/v1/push-token`
2. Обрабатывайте входящие уведомления для экстренных ситуаций
3. Показывайте локальные уведомления при получении emergency_alert
### Геолокация
1. Запрашивайте разрешения на точное местоположение
2. Обновляйте координаты через `/api/v1/location/update`
3. Кэшируйте последнее известное местоположение
### Оффлайн режим
1. Кэшируйте экстренные контакты локально
2. Сохраняйте черновики оповещений для отправки при восстановлении связи
3. Показывайте статус подключения к сервису
### UI/UX
1. Красная кнопка SOS должна быть всегда доступна
2. Быстрый доступ к созданию оповещения (виджет, ярлык)
3. Показывайте статус отправленных оповещений
4. Уведомления о новых откликах на оповещения
---
## 🧪 Тестирование
Используйте прилагаемый скрипт `test_emergency_api.sh` для полного тестирования API:
```bash
./test_emergency_api.sh
```
Скрипт проверит:
- Регистрацию и авторизацию
- Создание и управление оповещениями
- Отклики на оповещения
- Отчеты и отметки безопасности
- Статистику и аналитику

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 в реальном мобильном приложении, включая безопасность, производительность и тестирование.