Quay lại danh sách bài viết
Dự án Phần mềm cho khách hàng: 📱 MÀN HÌNH ĐĂNG NHẬP - TÀI LIỆU CHI TIẾT
07 tháng 07, 2025
•Admin# 📱 MÀN HÌNH ĐĂNG NHẬP - TÀI LIỆU CHI TIẾT
## **📋 Mục Lục**
1. [Yêu Cầu Business](#yêu-cầu-business)
2. [Triển Khai Kỹ Thuật](#triển-khai-kỹ-thuật)
3. [Thiết Kế Database](#thiết-kế-database)
4. [Trải Nghiệm Người Dùng](#trải-nghiệm-người-dùng)
5. [Triển Khai Bảo Mật](#triển-khai-bảo-mật)
6. [Tài Liệu API](#tài-liệu-api)
7. [Hướng Dẫn Testing](#hướng-dẫn-testing)
8. [Hướng Dẫn Deploy](#hướng-dẫn-deploy)
9. [Khắc Phục Sự Cố](#khắc-phục-sự-cố)
10. [Bảo Trì & Cập Nhật](#bảo-trì--cập-nhật)
---
## **🎯 Yêu Cầu Business**
### **Yêu Cầu Chức Năng**
#### **YC-001: Xác Thực Người Dùng**
- **Mô tả**: Người dùng phải có thể đăng nhập bằng email và mật khẩu
- **Độ ưu tiên**: Quan trọng nhất
- **Tiêu chí chấp nhận**:
- Người dùng nhập email đúng định dạng
- Người dùng nhập mật khẩu (tối thiểu 6 ký tự)
- Hệ thống xác thực với Supabase backend
- Hệ thống chuyển đến dashboard khi đăng nhập thành công
- Hệ thống hiển thị lỗi khi thông tin không đúng
#### **YC-002: Tính Năng Ghi Nhớ Đăng Nhập**
- **Mô tả**: Người dùng có thể chọn lưu thông tin đăng nhập cho lần sau
- **Độ ưu tiên**: Cao
- **Tiêu chí chấp nhận**:
- Có checkbox "Ghi nhớ đăng nhập" trên form
- Khi tick, email và mật khẩu được lưu cục bộ
- Khi mở app lại, thông tin đã lưu tự động điền vào form
- Người dùng có thể xóa thông tin đã lưu
- Tính năng hoạt động offline (lưu trữ cục bộ)
#### **YC-003: Kiểm Tra Quyền Admin**
- **Mô tả**: Chỉ người dùng có quyền admin mới được truy cập ứng dụng
- **Độ ưu tiên**: Quan trọng nhất
- **Tiêu chí chấp nhận**:
- Hệ thống kiểm tra profile người dùng sau khi xác thực
- Chỉ người dùng có role = 'admin' được phép truy cập
- Người dùng không phải admin nhận thông báo lỗi phù hợp
- Trạng thái admin được xác minh từ bảng profiles trong database
#### **YC-004: Kiểm Tra Dữ Liệu Đầu Vào**
- **Mô tả**: Tất cả dữ liệu đầu vào phải được kiểm tra trước khi xử lý
- **Độ ưu tiên**: Cao
- **Tiêu chí chấp nhận**:
- Kiểm tra định dạng email (chứa @)
- Kiểm tra độ dài mật khẩu (tối thiểu 6 ký tự)
- Kiểm tra trường bắt buộc
- Phản hồi kiểm tra theo thời gian thực
### **Yêu Cầu Phi Chức Năng**
#### **YC-001: Hiệu Suất**
- Quá trình đăng nhập hoàn thành trong vòng 3 giây trong điều kiện mạng bình thường
- App khởi động và tải thông tin đã lưu trong vòng 1 giây
- Animation và chuyển cảnh mượt mà
#### **YC-002: Bảo Mật**
- Mật khẩu được truyền an toàn (HTTPS)
- Lưu trữ cục bộ sử dụng bảo mật theo thiết bị
- Không lưu mật khẩu dạng text thô trong logs
- Session tokens có thời gian hết hạn phù hợp
#### **YC-003: Khả Năng Sử Dụng**
- Giao diện trực quan theo nguyên tắc thiết kế React Native
- Dễ tiếp cận cho người khuyết tật
- Thiết kế responsive cho các kích thước màn hình khác nhau
- Thông báo lỗi rõ ràng và phản hồi người dùng
#### **YC-004: Độ Tin Cậy**
- 99.9% uptime cho dịch vụ xác thực
- Xử lý lỗi mạng một cách graceful
- Khả năng offline cho thông tin đã lưu
---
## **💻 Triển Khai Kỹ Thuật**
### **Tổng Quan Kiến Trúc**
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ LoginScreen │───▶│ AuthContext │───▶│ AuthService │
│ (Tầng UI) │ │ (Quản lý State)│ │ (Tầng API) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AsyncStorage │ │ React Context │ │ Supabase API │
│ (Lưu trữ cục bộ)│ │ (Toàn cục) │ │ (Backend) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### **Cấu Trúc Component**
#### **LoginScreen.js**
```javascript
// Component đăng nhập chính
const LoginScreen = ({ navigation }) => {
// Quản lý state
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [justLoggedIn, setJustLoggedIn] = useState(false);
// Context hooks
const { login, isLoading, isAuthenticated } = useAuth();
// Effects
useEffect(() => loadSavedLogin(), []); // Tải thông tin đã lưu khi mount
// Functions
const loadSavedLogin = async () => { /* Triển khai */ };
const saveLogin = async () => { /* Triển khai */ };
const handleLogin = async () => { /* Triển khai */ };
// Render UI
};
```
#### **AuthContext.js**
```javascript
// Quản lý state xác thực toàn cục
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const login = async (email, password) => {
// Gọi AuthService, cập nhật state, xử lý lỗi
};
const logout = async () => {
// Xóa auth tokens, reset state
};
const checkAuthStatus = async () => {
// Xác minh xác thực hiện tại khi khởi động app
};
};
```
#### **AuthService.js**
```javascript
// Tầng giao tiếp API
class AuthService {
async login(email, password) {
// Xác thực Supabase
// Kiểm tra profile
// Lưu trữ token
}
async getProfile(userId) {
// Lấy profile người dùng từ database
// Kiểm tra quyền admin
}
async logout() {
// Xóa stored tokens
// Giữ thông tin đã lưu nếu "ghi nhớ đăng nhập" được bật
}
}
```
### **Luồng Dữ Liệu**
1. **Khởi Động App**:
```
App Start → AuthContext.checkAuthStatus() → AuthService.isAuthenticated()
↓
LoginScreen.loadSavedLogin() → AsyncStorage.getItem() → Tự động điền form
```
2. **Quá Trình Đăng Nhập**:
```
Input Người Dùng → LoginScreen.handleLogin() → AuthContext.login()
↓
AuthService.login() → Supabase API → Kiểm Tra Profile
↓
Thành Công → saveLogin() → AsyncStorage.setItem() → Chuyển đến Dashboard
```
3. **Luồng Ghi Nhớ Đăng Nhập**:
```
Đăng Nhập Thành Công → saveLogin() → AsyncStorage.setItem('saved_email', 'saved_password')
↓
Khởi Động Lại App → loadSavedLogin() → AsyncStorage.getItem() → setEmail(), setPassword()
```
### **Cấu Trúc File**
```
src/
├── screens/
│ └── LoginScreen.js # Component UI đăng nhập chính
├── contexts/
│ └── AuthContext.js # Quản lý state auth toàn cục
├── services/
│ └── AuthService.js # Giao tiếp API
└── components/
└── common/ # Components UI tái sử dụng
```
---
## **🗄️ Thiết Kế Database**
### **Cấu Hình Supabase**
#### **Bảng Authentication (auth.users)**
```sql
-- Bảng built-in của Supabase
CREATE TABLE auth.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
encrypted_password VARCHAR(255),
email_confirmed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
```
#### **Bảng Profiles (public.profiles)**
```sql
-- Bảng profile tùy chỉnh
CREATE TABLE public.profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email VARCHAR(255),
role VARCHAR(50) DEFAULT 'user' CHECK (role IN ('admin', 'user', 'moderator')),
full_name VARCHAR(255),
avatar_url TEXT,
phone VARCHAR(20),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indexes để tối ưu hiệu suất
CREATE INDEX idx_profiles_email ON public.profiles(email);
CREATE INDEX idx_profiles_role ON public.profiles(role);
-- Row Level Security (RLS)
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
-- RLS Policies
CREATE POLICY "Người dùng có thể đọc profile của mình"
ON public.profiles FOR SELECT
TO authenticated
USING (auth.uid() = id);
CREATE POLICY "Admin có thể đọc tất cả profiles"
ON public.profiles FOR SELECT
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
```
### **Hướng Dẫn Setup Database**
#### **Bước 1: Tạo Admin User**
```sql
-- Tạo user trong Supabase Auth Dashboard
-- Email: admin@company.com
-- Password: SecurePassword123
-- Email Confirmed: ✓
-- Sau đó tạo profile
INSERT INTO public.profiles (id, email, role, full_name)
VALUES (
'[USER_ID_TỪ_AUTH_USERS]',
'admin@company.com',
'admin',
'Quản Trị Viên Hệ Thống'
);
```
#### **Bước 2: Setup RLS Policies**
```sql
-- Cho phép admin quản lý tất cả profiles
CREATE POLICY "Admin có thể quản lý profiles"
ON public.profiles FOR ALL
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
```
#### **Bước 3: Tạo Functions (Tùy chọn)**
```sql
-- Function tự động tạo profile khi user đăng ký
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email, role, full_name)
VALUES (NEW.id, NEW.email, 'user', '');
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Trigger để thực thi function
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
```
### **Mối Quan Hệ Dữ Liệu**
```
auth.users (1) ←──── (1) public.profiles
│ │
│ ├── role (admin/user)
│ ├── full_name
│ ├── avatar_url
│ └── phone
│
└── Dùng cho xác thực
```
---
## **🎨 Trải Nghiệm Người Dùng**
### **Đặc Tả UI/UX**
#### **Thiết Kế Hình Ảnh**
- **Bảng Màu**:
- Chính: `#1e3a8a` (Xanh Dương)
- Thành Công: `#16a34a` (Xanh Lá)
- Lỗi: `#dc2626` (Đỏ)
- Nền: `#f5f5f5` (Xám Nhạt)
- Text: `#333333` (Xám Đậm)
- **Typography**:
- Tiêu đề: 28px, Đậm
- Nhãn: 16px, Bán đậm
- Input: 16px, Thường
- Nút: 18px, Đậm
- **Khoảng Cách**:
- Padding container: 20px
- Margin elements: 20px
- Chiều cao nút: 50px
- Chiều cao input: 48px
#### **Cấu Trúc Layout**
```
┌─────────────────────────────────┐
│ Logo (⚽) │
│ Admin Đặt Sân │
│ Hệ thống quản lý sân bóng │
├─────────────────────────────────┤
│ ┌─────────────────────────────┐ │
│ │ Nhập Email │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ thanhdt9279@gmail.com │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ Nhập Mật Khẩu │ │
│ │ ┌─────────────────────┬─┐ │ │
│ │ │ **************** │👁│ │ │
│ │ └─────────────────────┴─┘ │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ ☑ Ghi nhớ đăng nhập │ │
│ └─────────────────────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ ĐĂNG NHẬP │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────┘
```
#### **Hành Trình Người Dùng**
1. **Khởi Động App**:
- Người dùng mở app
- Màn hình loading (1-2 giây)
- Màn hình đăng nhập xuất hiện
- Thông tin đã lưu tự động điền (nếu có)
2. **Đăng Nhập Lần Đầu**:
- Người dùng thấy form trống
- Người dùng nhập email và mật khẩu
- Người dùng có thể tick "Ghi nhớ đăng nhập"
- Người dùng nhấn "Đăng nhập"
- Hiển thị loading indicator
- Thành công → Chuyển đến dashboard
- Lỗi → Hiển thị thông báo lỗi
3. **Người Dùng Quay Lại (với Ghi Nhớ Đăng Nhập)**:
- Người dùng mở app
- Email và mật khẩu tự động điền
- Checkbox "Ghi nhớ đăng nhập" đã được tick
- Người dùng có thể ngay lập tức nhấn "Đăng nhập"
- Hoặc chỉnh sửa thông tin nếu cần
4. **Các Tình Huống Lỗi**:
- Email sai định dạng → Viền đỏ, text lỗi
- Trường trống → Alert dialog
- Lỗi mạng → Tùy chọn thử lại
- Sai thông tin đăng nhập → Xóa trường mật khẩu
- Người dùng không phải admin → Thông báo lỗi cụ thể
#### **Tính Năng Accessibility**
- **Hỗ Trợ Screen Reader**:
```javascript
<TextInput
accessibilityLabel="Nhập địa chỉ email"
accessibilityHint="Email để đăng nhập vào hệ thống"
placeholder="Nhập email"
/>
```
- **Điều Hướng Bàn Phím**:
- Thứ tự tab: Email → Mật khẩu → Ghi nhớ đăng nhập → Đăng nhập
- Phím Return: Chuyển trường tiếp theo
- Submit ở trường cuối
- **Hỗ Trợ High Contrast**:
- Tỷ lệ tương phản màu đủ
- Indicator focus rõ ràng
- Touch targets lớn (tối thiểu 44px)
---
## **🔒 Triển Khai Bảo Mật**
### **Bảo Mật Xác Thực**
#### **Yêu Cầu Mật Khẩu**
- Tối thiểu 6 ký tự (có thể cấu hình)
- Không giới hạn độ dài tối đa
- Hỗ trợ ký tự đặc biệt
- Kiểm tra phân biệt hoa thường
#### **Giao Tiếp An Toàn**
```javascript
// Tất cả API calls sử dụng HTTPS
const API_BASE_URL = 'https://prmdkpesbdtqlysttxka.supabase.co';
// API Key được bao gồm trong headers
headers: {
'Content-Type': 'application/json',
'apikey': API_KEY,
'Authorization': `Bearer ${token}`
}
```
#### **Quản Lý Token**
```javascript
// Lưu trữ token an toàn
await AsyncStorage.setItem('authToken', data.access_token);
await AsyncStorage.setItem('refreshToken', data.refresh_token);
// Xử lý hết hạn token
// Tokens tự động hết hạn theo cấu hình Supabase
// Refresh tokens được sử dụng để gia hạn tự động
```
### **Bảo Mật Lưu Trữ Cục Bộ**
#### **Triển Khai Ghi Nhớ Đăng Nhập**
```javascript
// Lưu trữ cục bộ trên thiết bị (được mã hóa bởi OS)
const saveLogin = async () => {
if (rememberMe) {
await AsyncStorage.setItem('saved_email', email.trim());
await AsyncStorage.setItem('saved_password', password); // Cân nhắc mã hóa
await AsyncStorage.setItem('remember_me', 'true');
}
};
```
#### **Những Điều Cần Cân Nhắc Về Bảo Mật**
- **Bảo Mật Thiết Bị**: AsyncStorage được mã hóa bởi OS thiết bị
- **Riêng Cho App**: Dữ liệu chỉ app này truy cập được
- **Không Sync Cloud**: Dữ liệu ở lại trên thiết bị
- **Cleanup Khi Gỡ**: Dữ liệu bị xóa khi gỡ app
#### **Khuyến Nghị Cho Production**
```javascript
// Cho production, cân nhắc mã hóa dữ liệu nhạy cảm
import CryptoJS from 'crypto-js';
const encryptPassword = (password) => {
return CryptoJS.AES.encrypt(password, deviceId).toString();
};
const decryptPassword = (encryptedPassword) => {
const bytes = CryptoJS.AES.decrypt(encryptedPassword, deviceId);
return bytes.toString(CryptoJS.enc.Utf8);
};
```
### **Kiểm Soát Truy Cập Dựa Trên Vai Trò**
#### **Kiểm Tra Admin**
```javascript
// Kiểm tra phía server
const profileResponse = await this.getProfile(data.user.id);
if (profileResponse.success && profileResponse.profile.role === 'admin') {
// Cho phép truy cập
} else {
throw new Error('Bạn không có quyền truy cập trang admin');
}
```
#### **Bảo Mật Database**
```sql
-- Row Level Security đảm bảo users chỉ truy cập dữ liệu phù hợp
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
-- Policy kiểm tra admin
CREATE POLICY "Chỉ admin được truy cập"
ON sensitive_table FOR ALL
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
```
---
## **🔌 Tài Liệu API**
### **Endpoints Xác Thực**
#### **Endpoint Đăng Nhập**
```http
POST https://prmdkpesbdtqlysttxka.supabase.co/auth/v1/token?grant_type=password
Content-Type: application/json
apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
{
"email": "admin@company.com",
"password": "password123"
}
```
**Response (Thành Công - 200)**:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "v1.MjAyNC0wMy0xNVQxMDowMDowMFo...",
"user": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"email": "admin@company.com",
"email_confirmed_at": "2024-03-15T10:00:00Z",
"created_at": "2024-03-15T10:00:00Z"
}
}
```
**Response (Lỗi - 400)**:
```json
{
"error": "invalid_grant",
"error_description": "Invalid login credentials"
}
```
#### **Endpoint Profile**
```http
GET https://prmdkpesbdtqlysttxka.supabase.co/rest/v1/profiles?id=eq.f47ac10b-58cc-4372-a567-0e02b2c3d479&select=*
apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Response (Thành Công - 200)**:
```json
[
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"email": "admin@company.com",
"role": "admin",
"full_name": "Quản Trị Viên Hệ Thống",
"avatar_url": null,
"phone": "+84123456789",
"created_at": "2024-03-15T10:00:00Z",
"updated_at": "2024-03-15T10:00:00Z"
}
]
```
### **Mã Lỗi**
| Mã | Mô Tả | Hành Động |
|-----|-------|-----------|
| 200 | Thành công | Tiếp tục luồng bình thường |
| 400 | Thông tin đăng nhập không hợp lệ | Hiển thị lỗi, xóa mật khẩu |
| 401 | Không được ủy quyền | Chuyển về đăng nhập |
| 403 | Bị cấm (không phải admin) | Hiển thị lỗi quyền |
| 404 | Không tìm thấy user | Hiển thị thông báo lỗi |
| 500 | Lỗi server | Hiển thị tùy chọn thử lại |
| Mạng | Kết nối thất bại | Hiển thị lỗi mạng |
### **Rate Limiting**
- **Lần đăng nhập**: 5 lần/phút mỗi IP
- **API calls**: 100 requests/phút mỗi user
- **Chiến lược retry**: Exponential backoff
---
## **🧪 Hướng Dẫn Testing**
### **Unit Testing**
#### **Tests LoginScreen Component**
```javascript
// File test: __tests__/LoginScreen.test.js
describe('LoginScreen', () => {
test('render form đăng nhập đúng', () => {
render(<LoginScreen />);
expect(screen.getByPlaceholderText('Nhập email')).toBeInTheDocument();
expect(screen.getByPlaceholderText('Nhập mật khẩu')).toBeInTheDocument();
expect(screen.getByText('Ghi nhớ đăng nhập')).toBeInTheDocument();
});
test('kiểm tra định dạng email', async () => {
render(<LoginScreen />);
const emailInput = screen.getByPlaceholderText('Nhập email');
const loginButton = screen.getByText('Đăng nhập');
fireEvent.changeText(emailInput, 'email-không-hợp-lệ');
fireEvent.press(loginButton);
await waitFor(() => {
expect(screen.getByText('Email không hợp lệ')).toBeInTheDocument();
});
});
test('lưu đăng nhập khi tick ghi nhớ', async () => {
const mockSaveLogin = jest.fn();
render(<LoginScreen />);
// Điền form và tick ghi nhớ
fireEvent.changeText(screen.getByPlaceholderText('Nhập email'), 'test@test.com');
fireEvent.changeText(screen.getByPlaceholderText('Nhập mật khẩu'), 'password');
fireEvent.press(screen.getByText('Ghi nhớ đăng nhập'));
// Mock đăng nhập thành công
mockLogin.mockResolvedValue({ success: true });
fireEvent.press(screen.getByText('Đăng nhập'));
await waitFor(() => {
expect(AsyncStorage.setItem).toHaveBeenCalledWith('saved_email', 'test@test.com');
});
});
});
```
#### **Tests AuthService**
```javascript
// File test: __tests__/AuthService.test.js
describe('AuthService', () => {
test('đăng nhập với thông tin hợp lệ', async () => {
fetch.mockResolvedValueOnce({
status: 200,
json: () => Promise.resolve(mockLoginResponse)
});
const result = await AuthService.login('test@test.com', 'password');
expect(result.success).toBe(true);
expect(result.user.email).toBe('test@test.com');
});
test('đăng nhập với thông tin không hợp lệ', async () => {
fetch.mockResolvedValueOnce({
status: 400,
json: () => Promise.resolve({ error_description: 'Invalid credentials' })
});
await expect(AuthService.login('sai@test.com', 'sai'))
.rejects.toThrow('Invalid credentials');
});
test('kiểm tra quyền admin', async () => {
const mockProfile = { success: true, profile: { role: 'user' } };
AuthService.getProfile = jest.fn().mockResolvedValue(mockProfile);
await expect(AuthService.login('user@test.com', 'password'))
.rejects.toThrow('Bạn không có quyền truy cập trang admin');
});
});
```
### **Integration Testing**
#### **End-to-End Luồng Đăng Nhập**
```javascript
// File test: e2e/LoginFlow.e2e.js
describe('Luồng Đăng Nhập', () => {
test('luồng đăng nhập hoàn chỉnh với ghi nhớ', async () => {
// Khởi động app
await device.launchApp();
// Điền form đăng nhập
await element(by.id('email-input')).typeText('admin@test.com');
await element(by.id('password-input')).typeText('password123');
await element(by.id('remember-checkbox')).tap();
// Submit đăng nhập
await element(by.id('login-button')).tap();
// Xác minh chuyển đến dashboard
await waitFor(element(by.id('dashboard-screen')))
.toBeVisible()
.withTimeout(5000);
// Khởi động lại app
await device.reloadReactNative();
// Xác minh tự động điền
await expect(element(by.id('email-input'))).toHaveText('admin@test.com');
await expect(element(by.id('remember-checkbox'))).toHaveValue('1');
});
});
```
### **Checklist Testing Thủ Công**
#### **Testing Chức Năng**
- [ ] Đăng nhập với thông tin admin hợp lệ → Thành công
- [ ] Đăng nhập với thông tin non-admin hợp lệ → Thông báo lỗi
- [ ] Đăng nhập với email sai định dạng → Lỗi validation
- [ ] Đăng nhập với trường trống → Lỗi trường bắt buộc
- [ ] Đăng nhập với mật khẩu sai → Lỗi xác thực
- [ ] Tick ghi nhớ đăng nhập → Thông tin được lưu
- [ ] Không tick ghi nhớ → Thông tin không lưu
- [ ] Tự động điền khi khởi động lại app → Thông tin đã lưu được tải
- [ ] Xóa thông tin đã lưu → Dữ liệu bị xóa
- [ ] Toggle hiển thị mật khẩu → Hiển thị/ẩn mật khẩu
- [ ] Đăng xuất → Quay về màn hình đăng nhập
#### **Testing UI/UX**
- [ ] Responsive design trên các kích thước màn hình khác nhau
- [ ] Animation và chuyển cảnh mượt mà
- [ ] Loading indicators trong lúc gọi API
- [ ] Thông báo lỗi rõ ràng và hữu ích
- [ ] Phản hồi validation form ngay lập tức
- [ ] Touch targets có kích thước phù hợp
- [ ] Tính năng accessibility hoạt động đúng
#### **Testing Hiệu Suất**
- [ ] App khởi động trong vòng 2 giây
- [ ] Đăng nhập hoàn thành trong vòng 3 giây
- [ ] Cuộn và tương tác mượt mà
- [ ] Sử dụng memory trong giới hạn chấp nhận được
- [ ] Tối ưu hóa sử dụng pin
#### **Testing Bảo Mật**
- [ ] Mật khẩu không hiển thị trong logs
- [ ] Giao tiếp HTTPS an toàn
- [ ] Lưu trữ token an toàn
- [ ] Session timeout hoạt động đúng
- [ ] Bảo vệ SQL injection
- [ ] Bảo vệ XSS
### **Dữ Liệu Test**
#### **Tài Khoản Test Hợp Lệ**
```javascript
const testAccounts = {
admin: {
email: 'admin@test.com',
password: 'admin123',
role: 'admin',
expectedResult: 'thành công'
},
user: {
email: 'user@test.com',
password: 'user123',
role: 'user',
expectedResult: 'từ chối quyền'
},
invalid: {
email: 'invalid@test.com',
password: 'sai',
role: null,
expectedResult: 'xác thực thất bại'
}
};
```
#### **Edge Cases**
```javascript
const edgeCases = [
{ email: '', password: '', expected: 'lỗi trường bắt buộc' },
{ email: 'email-không-hợp-lệ', password: '123', expected: 'lỗi định dạng' },
{ email: 'test@test.com', password: '', expected: 'lỗi trường bắt buộc' },
{ email: 'email.rất.dài.vượt.quá.độ.dài.bình.thường@test.com', password: '123456', expected: 'thành công hoặc lỗi validation' },
{ email: 'test@test.com', password: 'mật_khẩu_rất_dài_có_thể_gây_vấn_đề_với_một_số_hệ_thống', expected: 'thành công' }
];
```
---
## **🚀 Hướng Dẫn Deploy**
### **Setup Môi Trường**
#### **Môi Trường Development**
```bash
# Cài đặt dependencies
npm install
# Setup iOS
cd ios && pod install && cd ..
# Setup Android
# Đảm bảo Android SDK và emulator đã được cài đặt
# Khởi động Metro bundler
npm start
# Chạy trên iOS
npm run ios
# Chạy trên Android
npm run android
```
#### **Môi Trường Staging**
```javascript
// config/staging.js
export const config = {
API_BASE_URL: 'https://staging-api.company.com',
API_KEY: 'staging_api_key_here',
ENVIRONMENT: 'staging',
DEBUG_MODE: true
};
```
#### **Môi Trường Production**
```javascript
// config/production.js
export const config = {
API_BASE_URL: 'https://api.company.com',
API_KEY: 'production_api_key_here',
ENVIRONMENT: 'production',
DEBUG_MODE: false
};
```
### **Cấu Hình Build**
#### **iOS Build**
```bash
# Debug build
npx react-native run-ios --configuration Debug
# Release build
npx react-native run-ios --configuration Release
# Archive cho App Store
# Sử dụng Xcode → Product → Archive
```
#### **Android Build**
```bash
# Debug build
npx react-native run-android --variant=debug
# Release build
cd android
./gradlew assembleRelease
# Signed APK
./gradlew bundleRelease
```
### **Environment Variables**
```javascript
// .env.development
API_BASE_URL=https://dev-api.company.com
API_KEY=dev_api_key
DEBUG_MODE=true
// .env.production
API_BASE_URL=https://api.company.com
API_KEY=prod_api_key
DEBUG_MODE=false
```
### **Code Signing**
#### **iOS Code Signing**
```xml
<!-- ios/AdminDatSanSimple/Info.plist -->
<key>CFBundleIdentifier</key>
<string>com.company.admindatsan</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
```
#### **Android Code Signing**
```gradle
// android/app/build.gradle
android {
signingConfigs {
release {
storeFile file('release.keystore')
storePassword 'store_password'
keyAlias 'key_alias'
keyPassword 'key_password'
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
```
### **Checklist Deploy**
#### **Trước Deploy**
- [ ] Tất cả tests đều pass
- [ ] Code review hoàn thành
- [ ] Security audit hoàn thành
- [ ] Performance testing hoàn thành
- [ ] Database migrations sẵn sàng
- [ ] Environment variables đã cấu hình
- [ ] API keys đã cập nhật
- [ ] Build configuration đã xác minh
#### **Các Bước Deploy**
1. **Tạo Build**
- Tạo signed builds cho cả hai platform
- Xác minh tính toàn vẹn build
- Test cài đặt trên thiết bị sạch
2. **Submit Store**
- iOS: Submit lên App Store Connect
- Android: Upload lên Google Play Console
- Bao gồm mô tả app và screenshots
3. **Cập Nhật Backend**
- Deploy các thay đổi API cần thiết
- Cập nhật database schema nếu cần
- Xác minh API endpoints có thể truy cập
4. **Setup Monitoring**
- Cấu hình crash reporting
- Setup analytics tracking
- Monitor API performance
#### **Sau Deploy**
- [ ] Xác minh cài đặt app từ stores
- [ ] Test các user flows quan trọng
- [ ] Monitor crash reports
- [ ] Kiểm tra metrics hiệu suất API
- [ ] Thu thập feedback người dùng
---
## **🔧 Khắc Phục Sự Cố**
### **Các Vấn Đề Thường Gặp**
#### **Vấn Đề Đăng Nhập**
**Vấn đề**: Lỗi "Email không hợp lệ"
- **Nguyên nhân**: Email validation thất bại
- **Giải pháp**: Đảm bảo email chứa ký tự @ và đúng định dạng
- **Kiểm tra code**:
```javascript
if (!email.includes('@')) {
Alert.alert('Lỗi', 'Email không hợp lệ');
}
```
**Vấn đề**: Lỗi "Đăng nhập thất bại"
- **Nguyên nhân**: Thông tin đăng nhập không đúng hoặc vấn đề mạng
- **Giải pháp**:
1. Xác minh thông tin trong Supabase dashboard
2. Kiểm tra kết nối mạng
3. Xác minh API endpoints có thể truy cập
- **Debug**: Kiểm tra network tab cho API response
**Vấn đề**: "Bạn không có quyền truy cập trang admin"
- **Nguyên nhân**: User role không phải 'admin'
- **Giải pháp**: Cập nhật user role trong bảng profiles
- **SQL**:
```sql
UPDATE profiles SET role = 'admin' WHERE email = 'user@example.com';
```
#### **Vấn Đề Ghi Nhớ Đăng Nhập**
**Vấn đề**: Thông tin không được lưu
- **Nguyên nhân**: AsyncStorage permission hoặc chưa tick ghi nhớ
- **Giải pháp**:
1. Xác minh checkbox ghi nhớ đã được tick
2. Kiểm tra AsyncStorage permissions
3. Test trên thiết bị thật (không phải simulator)
- **Debug**: Sử dụng debug buttons để kiểm tra storage
**Vấn đề**: Thông tin không load khi khởi động lại app
- **Nguyên nhân**: AsyncStorage data không tồn tại
- **Giải pháp**:
1. Kiểm tra device storage permissions
2. Xác minh app không bị force-close bởi hệ thống
3. Test function loadSavedLogin
- **Debug**: Kiểm tra console logs cho loading errors
#### **Vấn Đề Hiệu Suất**
**Vấn đề**: Đăng nhập chậm
- **Nguyên nhân**: Network latency hoặc hiệu suất API
- **Giải pháp**:
1. Kiểm tra tốc độ mạng
2. Monitor thời gian phản hồi API
3. Implement request timeout
- **Monitoring**: Thêm performance logging
**Vấn đề**: App crash khi đăng nhập
- **Nguyên nhân**: Vấn đề memory hoặc unhandled exceptions
- **Giải pháp**:
1. Kiểm tra crash logs
2. Test trên các thiết bị khác nhau
3. Review error handling code
- **Debug**: Sử dụng crash reporting tools
### **Debug Tools**
#### **Console Logging**
```javascript
// Bật detailed logging
console.log('🔍 Đang tải thông tin đã lưu...');
console.log('📖 Tìm thấy dữ liệu đã lưu:', { savedEmail, savedPassword, remember });
console.log('✅ Login API thành công');
console.log('💾 Đang lưu thông tin đăng nhập... rememberMe:', rememberMe);
```
#### **Network Debugging**
```javascript
// Thêm request/response logging
const response = await fetch(url, options);
console.log('API Request:', { url, options });
console.log('API Response:', { status: response.status, data: await response.json() });
```
#### **AsyncStorage Debugging**
```javascript
// Kiểm tra tất cả AsyncStorage keys
const getAllKeys = async () => {
const keys = await AsyncStorage.getAllKeys();
const items = await AsyncStorage.multiGet(keys);
console.log('Nội dung AsyncStorage:', items);
};
```
#### **State Debugging**
```javascript
// Monitor state changes
useEffect(() => {
console.log('State thay đổi:', { email, password, rememberMe, isAuthenticated });
}, [email, password, rememberMe, isAuthenticated]);
```
### **Tham Chiếu Mã Lỗi**
| Lỗi | Mã | Mô Tả | Giải Pháp |
|-----|-----|-------|-----------|
| INVALID_EMAIL | 001 | Email định dạng không hợp lệ | Kiểm tra định dạng email |
| EMPTY_FIELDS | 002 | Thiếu trường bắt buộc | Điền tất cả trường bắt buộc |
| AUTH_FAILED | 003 | Xác thực thất bại | Kiểm tra thông tin đăng nhập |
| NO_ADMIN_ROLE | 004 | User không phải admin | Cập nhật user role |
| NETWORK_ERROR | 005 | Vấn đề kết nối mạng | Kiểm tra kết nối internet |
| API_ERROR | 006 | Lỗi server | Kiểm tra trạng thái API |
| STORAGE_ERROR | 007 | Lỗi AsyncStorage | Kiểm tra permissions app |
| UNKNOWN_ERROR | 999 | Lỗi không xác định | Kiểm tra logs và báo cáo |
---
## **📈 Bảo Trì & Cập Nhật**
### **Công Việc Bảo Trì Định Kỳ**
#### **Công Việc Hàng Tuần**
- [ ] Monitor crash reports và sửa các vấn đề critical
- [ ] Review user feedback và support tickets
- [ ] Kiểm tra metrics hiệu suất API
- [ ] Cập nhật dependencies nếu có security patches
#### **Công Việc Hàng Tháng**
- [ ] Review và cập nhật user roles theo nhu cầu
- [ ] Dọn dẹp các accounts không sử dụng trong database
- [ ] Tối ưu hiệu suất dựa trên metrics
- [ ] Security audit và review password policy
- [ ] Cập nhật documentation với các thay đổi
#### **Công Việc Hàng Quý**
- [ ] Đánh giá bảo mật toàn diện
- [ ] Performance benchmarking
- [ ] Phân tích trải nghiệm người dùng
- [ ] Cập nhật technology stack
- [ ] Testing backup và disaster recovery
### **Quy Trình Cập Nhật**
#### **Cập Nhật Nhỏ (Bug Fixes)**
1. **Xác Định Vấn Đề**
- Review crash reports
- Phân tích user feedback
- Ưu tiên theo tác động
2. **Triển Khai Fix**
- Tạo fix branch
- Implement solution
- Thêm unit tests
- Code review
3. **Testing**
- Unit testing
- Integration testing
- Manual testing trên nhiều thiết bị
- Regression testing
4. **Deployment**
- Deploy lên staging
- User acceptance testing
- Deploy lên production
- Monitor các vấn đề
#### **Cập Nhật Lớn (Thêm Tính Năng)**
1. **Giai Đoạn Planning**
- Thu thập requirements
- Thiết kế kỹ thuật
- Impact assessment
- Lập kế hoạch timeline
2. **Giai Đoạn Development**
- Feature implementation
- Comprehensive testing
- Cập nhật documentation
- Security review
3. **Giai Đoạn Release**
- Staged rollout
- Monitor key metrics
- Thu thập user feedback
- Post-release support
### **Quản Lý Version**
#### **Semantic Versioning**
- **MAJOR.MINOR.PATCH** (ví dụ: 1.2.3)
- **MAJOR**: Breaking changes
- **MINOR**: Tính năng mới (backward compatible)
- **PATCH**: Bug fixes (backward compatible)
#### **Template Release Notes**
```markdown
# Phiên bản 1.2.3 - 15/03/2024
## 🆕 Tính Năng Mới
- Thêm tùy chọn xác thực sinh trắc học
- Cải thiện validation độ mạnh mật khẩu
## 🐛 Bug Fixes
- Sửa lỗi ghi nhớ đăng nhập không hoạt động trên iOS
- Giải quyết vấn đề timeout đăng nhập
## 🔧 Cải Thiện
- Nâng cao thông báo lỗi
- Tối ưu thời gian khởi động app
## 🔒 Bảo Mật
- Cập nhật thuật toán mã hóa
- Nâng cao quản lý session
## 🛠️ Kỹ Thuật
- Nâng cấp lên React Native 0.72
- Cập nhật Supabase client library
```
### **Monitoring & Analytics**
#### **Metrics Quan Trọng Cần Theo Dõi**
- **User Metrics**:
- Daily/Monthly active users
- Tỷ lệ đăng nhập thành công
- Tỷ lệ giữ chân người dùng
- Thống kê sử dụng tính năng
- **Performance Metrics**:
- Thời gian khởi động app
- Thời gian hoàn thành đăng nhập
- Thời gian phản hồi API
- Tỷ lệ crash
- **Security Metrics**:
- Số lần đăng nhập thất bại
- Security incidents
- Yêu cầu reset password
- Patterns truy cập bất thường
#### **Hệ Thống Alerting**
```javascript
// Ví dụ cấu hình alerting
const alerts = {
loginFailureRate: {
threshold: '> 5%',
action: 'thông báo security team'
},
apiResponseTime: {
threshold: '> 3 giây',
action: 'thông báo dev team'
},
crashRate: {
threshold: '> 1%',
action: 'điều tra ngay lập tức'
}
};
```
### **Bảo Trì Documentation**
#### **Cần Cập Nhật**
- [ ] API documentation với các thay đổi endpoint
- [ ] Database schema documentation
- [ ] User guides và help documentation
- [ ] Technical documentation cho developers
- [ ] Security policies và procedures
#### **Lịch Review**
- **Hàng tháng**: User-facing documentation
- **Hàng quý**: Technical documentation
- **Hàng năm**: Complete documentation audit
---
## **📞 Thông Tin Hỗ Trợ & Liên Hệ**
### **Team Development**
- **Lead Developer**: dev-lead@company.com
- **Backend Developer**: backend-dev@company.com
- **Mobile Developer**: mobile-dev@company.com
- **QA Engineer**: qa@company.com
### **Team Business**
- **Product Manager**: pm@company.com
- **Business Analyst**: ba@company.com
- **Project Manager**: project-mgr@company.com
### **Team Operations**
- **DevOps Engineer**: devops@company.com
- **Database Administrator**: dba@company.com
- **Security Officer**: security@company.com
### **Liên Hệ Khẩn Cấp**
- **Vấn Đề Nghiêm Trọng**: emergency@company.com
- **Security Incidents**: security-incident@company.com
- **Hỗ Trợ Ngoài Giờ**: +84-xxx-xxx-xxxx
---
## **📝 Thông Tin Tài Liệu**
- **Phiên Bản Tài Liệu**: 1.0.0
- **Cập Nhật Lần Cuối**: 15 tháng 3, 2024
- **Ngày Review Tiếp Theo**: 15 tháng 6, 2024
- **Được Chuẩn Bị Bởi**: Development Team
- **Được Phê Duyệt Bởi**: Technical Lead & Product Manager
---
**© 2024 Company Name. Bảo lưu mọi quyền.**
Chia sẻ:
Bài viết liên quan
⚽ Dự Án Đặt Sân Thể Thao – Kiểm Thử API bằng Postman
⚽ Dự Án Đặt Sân Thể Thao – Kiểm Thử API bằng Postman
Học Python & Flutter – Có Dự Án Thực Tập Ngay!
Học Python & Flutter – Có Dự Án Thực Tập Ngay!
Ưu điểm của Agile so với Waterfall
So sánh chi tiết về phương pháp Agile và Waterfall trong quản lý dự án phần mềm