diff --git a/services/mkt-facebook-service-net/docs/en/AI_CHATBOT.md b/services/mkt-facebook-service-net/docs/en/AI_CHATBOT.md new file mode 100644 index 00000000..d8be618c --- /dev/null +++ b/services/mkt-facebook-service-net/docs/en/AI_CHATBOT.md @@ -0,0 +1,361 @@ +# AI Chatbot Configuration Guide + +Guide to configure and optimize AI-powered chatbot using OpenAI or Azure OpenAI. + +## Overview + +The AI Chatbot uses GPT-4 or GPT-3.5 to provide intelligent, context-aware responses to customer messages. It can understand natural language, maintain conversation context, and provide helpful information. + +## Choosing AI Provider + +### Option 1: OpenAI + +**Pros**: +- ✅ Easy setup +- ✅ Pay-as-you-go pricing +- ✅ Latest models (GPT-4 Turbo) +- ✅ Good for global deployment + +**Cons**: +- ❌ External API (latency) +- ❌ Data processed outside your region + +**Setup**: +1. Create account at [platform.openai.com](https://platform.openai.com/) +2. Generate API key from **API Keys** page +3. Add credits to billing account + +```bash +# .env configuration +OPENAI_API_KEY="sk-proj-xxxxxxxxxxxxxxxxx" +OPENAI_MODEL="gpt-4-turbo" +``` + +### Option 2: Azure OpenAI + +**Pros**: +- ✅ Enterprise security & compliance +- ✅ Data residency control +- ✅ SLA guarantees +- ✅ VNET integration + +**Cons**: +- ❌ More complex setup +- ❌ Requires Azure subscription +- ❌ Slower model updates + +**Setup**: +1. Create Azure OpenAI resource in Azure Portal +2. Deploy model (e.g., `gpt-4`) +3. Copy endpoint and key + +```bash +# .env configuration +AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" +AZURE_OPENAI_KEY="your-key-here" +AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4" +``` + +--- + +## Model Selection + +| Model | Best For | Cost | Speed | +|-------|----------|------|-------| +| **GPT-4 Turbo** | Complex reasoning, accurate responses | $$ | Medium | +| **GPT-4** | High quality, nuanced understanding | $$$ | Slow | +| **GPT-3.5 Turbo** | Fast responses, simple queries | $ | Fast | + +**Recommendation**: +- **E-commerce Support**: `gpt-3.5-turbo` (fast & cost-effective) +- **Technical Support**: `gpt-4-turbo` (better accuracy) +- **Multilingual Support**: `gpt-4-turbo` (better language handling) + +--- + +## System Prompt Best Practices + +### E-commerce Example + +``` +You are the AI assistant for "GoodGo Shop", a multi-category e-commerce platform in Vietnam. + +PRODUCTS: +- Electronics (phones, laptops, accessories) +- Fashion (clothing, shoes, bags) +- Home & Living (furniture, kitchenware) + +CAPABILITIES: +- Check order status (ask for order ID) +- Provide product information +- Explain shipping & return policies +- Help with account issues + +TONE: +- Friendly and conversational +- Use Vietnamese when customer uses Vietnamese +- Use simple language, avoid jargon + +LIMITATIONS: +- Cannot process refunds (refer to human agent) +- Cannot modify orders (refer to customer service) +- Cannot access payment information + +When you cannot help, say: "Let me connect you with our customer service team for this request." +``` + +--- + +## Configuration Parameters + +### Temperature + +Controls randomness (0.0 - 2.0) + +| Value | Behavior | Use Case | +|-------|----------|----------| +| **0.0 - 0.3** | Deterministic, focused | FAQ, policy questions | +| **0.4 - 0.7** | Balanced creativity | General customer service | +| **0.8 - 1.0** | More creative | Marketing, engagement | + +**Recommendation**: `0.7` for customer service + +### Max Tokens + +Maximum length of response + +| Tokens | Words | Use Case | +|--------|-------|----------| +| 150 | ~100 | Quick answers | +| 300 | ~200 | Standard responses | +| 500 | ~350 | Detailed explanations | + +**Recommendation**: `300-500` tokens for most cases + +### Top P (Nucleus Sampling) + +Alternative to temperature (0.0 - 1.0) + +- **0.9**: Recommended default +- Use either `temperature` OR `top_p`, not both + +--- + +## Context Management + +### Conversation History + +Include recent messages for context: + +```csharp +var messages = new List +{ + new ChatMessage(ChatRole.System, systemPrompt), + new ChatMessage(ChatRole.User, "What are your opening hours?"), + new ChatMessage(ChatRole.Assistant, "We're open 10AM - 10PM daily."), + new ChatMessage(ChatRole.User, "Can I order delivery now?"), +}; +``` + +**Best Practice**: +- Keep last **5-10 messages** for context +- Trim old messages to save tokens +- Always include System message + +### Token Budget + +| Component | Tokens | Note | +|-----------|--------|------| +| System Prompt | 100-200 | Fixed | +| Conversation History | 50-100 per message | Variable | +| Max Response | 300-500 | Configurable | +| **Total per request** | ~500-1500 | Estimate | + +**Cost Estimation** (GPT-4 Turbo): +- Input: $0.01 / 1K tokens +- Output: $0.03 / 1K tokens +- **Average cost per conversation**: $0.02 - $0.05 + +--- + +## Fallback Strategies + +### 1. Fallback to Automation + +When AI confidence is low, fallback to automation rules. + +### 2. Human Handoff + +Escalate to human agent for complex issues. + +**Trigger Phrases** (in AI response): +- "I'm not sure" +- "Let me connect you" +- "human agent" + +**Action**: Update conversation status to `NeedsHumanAgent` + +### 3. Timeout Handling + +If OpenAI API times out, use fallback response: +``` +"I apologize for the delay. Let me connect you with our team." +``` + +--- + +## Multilingual Support + +### Auto-detect Language + +GPT-4 can auto-detect and respond in the customer's language. + +**System Prompt**: +``` +You are a multilingual customer service assistant. +- Detect the customer's language +- Respond in the same language +- Support: English, Vietnamese, Thai, Chinese +``` + +--- + +## Testing & Optimization + +### A/B Testing System Prompts + +| Metric | Target | Measurement | +|--------|--------|-------------| +| **Response Quality** | User satisfaction > 80% | Thumbs up/down | +| **Resolution Rate** | > 70% without human | Track escalations | +| **Response Time** | < 3 seconds | API latency | +| **Cost per Conversation** | < $0.05 | Token usage | + +--- + +## Cost Optimization + +### 1. Use Shorter System Prompts + +```diff +- You are a helpful customer service assistant for GoodGo Shop, an e-commerce platform selling electronics, fashion, home & living... ++ You are a customer service AI for GoodGo, an e-commerce platform. +``` + +**Savings**: ~50 tokens per request + +### 2. Limit Conversation History + +```csharp +// Only keep recent 5 messages +var contextMessages = conversation.Messages + .OrderByDescending(m => m.SentAt) + .Take(5) + .Reverse(); +``` + +**Savings**: ~200-400 tokens per request + +### 3. Use GPT-3.5 for Simple Queries + +```csharp +if (IsSimpleQuery(message)) // FAQ, product lookup +{ + model = "gpt-3.5-turbo"; // 10x cheaper +} +else +{ + model = "gpt-4-turbo"; +} +``` + +### 4. Cache Common Responses + +Cache responses for FAQs in Redis. + +--- + +## Example Configurations + +### Configuration 1: High-Quality Support + +```json +{ + "provider": "OpenAI", + "model": "gpt-4-turbo", + "systemPrompt": "Detailed customer service prompt...", + "temperature": 0.6, + "maxTokens": 500 +} +``` + +**Use Case**: Premium brands, complex products +**Cost**: ~$0.04 per conversation + +### Configuration 2: Fast & Cost-Effective + +```json +{ + "provider": "OpenAI", + "model": "gpt-3.5-turbo", + "systemPrompt": "Concise customer service prompt...", + "temperature": 0.7, + "maxTokens": 300 +} +``` + +**Use Case**: High-volume, simple queries +**Cost**: ~$0.005 per conversation + +--- + +## Troubleshooting + +### Issue 1: Responses Too Generic + +**Solution**: +- ✅ Add more context to system prompt +- ✅ Include conversation history +- ✅ Lower temperature (0.5) + +### Issue 2: High Costs + +**Solution**: +- ✅ Shorten system prompt +- ✅ Limit conversation history +- ✅ Use GPT-3.5 for simple queries +- ✅ Cache common FAQs + +### Issue 3: Slow Responses + +**Solution**: +- ✅ Use GPT-3.5 Turbo (faster) +- ✅ Reduce max_tokens +- ✅ Enable streaming +- ✅ Use Azure OpenAI (regional deployment) + +### Issue 4: AI Hallucinates + +**Solution**: +- ✅ Add: "Do not make up information" +- ✅ Lower temperature (0.3-0.5) +- ✅ Provide factual context in system prompt + +--- + +## Resources + +### Official Documentation +- [OpenAI API Reference](https://platform.openai.com/docs/api-reference) +- [Azure OpenAI Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/) +- [GPT Best Practices](https://platform.openai.com/docs/guides/prompt-engineering) + +### Tools +- [OpenAI Playground](https://platform.openai.com/playground) +- [Tokenizer](https://platform.openai.com/tokenizer) +- [Azure OpenAI Studio](https://oai.azure.com/) + +### Internal Docs +- [API Documentation](./API.md) +- [Architecture Documentation](./ARCHITECTURE.md) +- [Facebook Setup Guide](./FACEBOOK_SETUP.md) diff --git a/services/mkt-facebook-service-net/docs/en/API.md b/services/mkt-facebook-service-net/docs/en/API.md new file mode 100644 index 00000000..da783344 --- /dev/null +++ b/services/mkt-facebook-service-net/docs/en/API.md @@ -0,0 +1,498 @@ +# API Documentation + +**Service**: mkt-facebook-service-net +**Base URL**: `http://localhost/api/v1/mkt-facebook` +**API Version**: v1.0 + +## Authentication + +All REST API endpoints (except webhooks) require JWT Bearer token. + +```http +Authorization: Bearer {jwt_token} +``` + +## Webhooks API + +### Verify Facebook Webhook + +Facebook uses this endpoint to verify webhook ownership during setup. + +```http +GET /api/v1/webhooks/facebook +``` + +**Query Parameters**: +| Parameter | Type | Description | +|-----------|------|-------------| +| `hub.mode` | string | Must be "subscribe" | +| `hub.verify_token` | string | Token configured in Facebook App | +| `hub.challenge` | string | Random string from Facebook | + +**Response** (200 OK): +``` +{hub.challenge} +``` + +**Example**: +```bash +curl "http://localhost/api/v1/webhooks/facebook?hub.mode=subscribe&hub.verify_token=my_verify_token&hub.challenge=1234567890" +``` + +--- + +### Receive Facebook Messenger Events + +Receives messages, postbacks, and other events from Facebook Messenger Platform. + +```http +POST /api/v1/webhooks/facebook +``` + +**Headers**: +``` +X-Hub-Signature-256: sha256={signature} +Content-Type: application/json +``` + +**Request Body** (Message Event): +```json +{ + "object": "page", + "entry": [{ + "id": "PAGE_ID", + "time": 1234567890, + "messaging": [{ + "sender": { "id": "USER_ID" }, + "recipient": { "id": "PAGE_ID" }, + "timestamp": 1234567890, + "message": { + "mid": "MESSAGE_ID", + "text": "Hello chatbot!", + "quick_reply": { + "payload": "QUICK_REPLY_PAYLOAD" + } + } + }] + }] +} +``` + +**Response** (200 OK): +```json +{ + "success": true +} +``` + +--- + +## Conversations API + +### List Conversations + +Retrieve a paginated list of conversations. + +```http +GET /api/v1/conversations +``` + +**Query Parameters**: +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `shopId` | guid | Yes | - | Shop/Business ID | +| `status` | string | No | - | Filter by status (Active, Closed) | +| `skip` | int | No | 0 | Number of records to skip | +| `take` | int | No | 20 | Number of records to return | + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "conversations": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "customerId": "650e8400-e29b-41d4-a716-446655440001", + "customerName": "John Doe", + "pageId": "123456789", + "status": "Active", + "channel": "FacebookMessenger", + "lastMessageText": "Thank you!", + "lastMessageAt": "2026-01-18T10:30:00Z", + "createdAt": "2026-01-18T09:00:00Z" + } + ], + "totalCount": 150 + }, + "pagination": { + "page": 1, + "limit": 20, + "total": 150, + "totalPages": 8 + } +} +``` + +--- + +### Get Conversation by ID + +Retrieve a specific conversation with full message history. + +```http +GET /api/v1/conversations/{id} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "customerId": "650e8400-e29b-41d4-a716-446655440001", + "customer": { + "id": "650e8400-e29b-41d4-a716-446655440001", + "name": "John Doe", + "facebookUserId": "1234567890", + "profilePicUrl": "https://...", + "tags": ["VIP", "Returning Customer"] + }, + "messages": [ + { + "id": "msg-001", + "senderId": "1234567890", + "content": "Hello, I need help", + "messageType": "Text", + "direction": "Incoming", + "sentAt": "2026-01-18T09:00:00Z" + }, + { + "id": "msg-002", + "senderId": "PAGE_ID", + "content": "Hi! How can I help you today?", + "messageType": "Text", + "direction": "Outgoing", + "sentAt": "2026-01-18T09:00:05Z" + } + ] + } +} +``` + +--- + +### Send Message in Conversation + +Send a message to a customer in an active conversation. + +```http +POST /api/v1/conversations/{id}/messages +``` + +**Request Body**: +```json +{ + "messageText": "Thank you for contacting us!", + "quickReplies": [ + { + "contentType": "text", + "title": "Yes", + "payload": "YES" + }, + { + "contentType": "text", + "title": "No", + "payload": "NO" + } + ] +} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "messageId": "msg-003", + "sentAt": "2026-01-18T10:35:00Z" + } +} +``` + +--- + +## Customers API + +### List Customers + +Retrieve a paginated list of customers. + +```http +GET /api/v1/customers +``` + +**Query Parameters**: +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `shopId` | guid | Yes | - | Shop/Business ID | +| `tags` | string[] | No | - | Filter by tags (comma-separated) | +| `search` | string | No | - | Search by name or email | +| `skip` | int | No | 0 | Pagination offset | +| `take` | int | No | 20 | Page size | + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "customers": [ + { + "id": "650e8400-e29b-41d4-a716-446655440001", + "facebookUserId": "1234567890", + "name": "John Doe", + "email": "john@example.com", + "profilePicUrl": "https://...", + "tags": ["VIP", "Returning"], + "firstSeenAt": "2026-01-15T08:00:00Z", + "lastInteractionAt": "2026-01-18T10:30:00Z" + } + ], + "totalCount": 450 + } +} +``` + +--- + +### Update Customer + +Update customer tags and custom fields. + +```http +PUT /api/v1/customers/{id} +``` + +**Request Body**: +```json +{ + "tags": ["VIP", "Returning", "HighValue"], + "customFields": { + "preferredLanguage": "English", + "membershipTier": "Platinum" + } +} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "customerId": "650e8400-e29b-41d4-a716-446655440001", + "updatedAt": "2026-01-18T11:00:00Z" + } +} +``` + +--- + +## Chatbots API + +### List Automation Flows + +Retrieve all chatbot automation flows for a shop. + +```http +GET /api/v1/chatbots/flows +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": [ + { + "id": "flow-001", + "name": "Welcome Flow", + "shopId": "shop-123", + "triggerType": "GetStarted", + "triggerValue": "GET_STARTED", + "isActive": true, + "nodeCount": 8, + "createdAt": "2026-01-10T00:00:00Z" + } + ] +} +``` + +--- + +### Create Chatbot Flow + +Create a new automation chatbot flow. + +```http +POST /api/v1/chatbots/flows +``` + +**Request Body**: +```json +{ + "shopId": "shop-123", + "name": "Product Inquiry Flow", + "triggerType": "Keyword", + "triggerValue": "product|price|buy", + "nodes": [ + { + "type": "Start", + "content": null, + "config": {}, + "nextNodeIds": ["node-002"] + }, + { + "type": "Message", + "content": "Welcome! I can help you find products.", + "config": {}, + "nextNodeIds": ["node-003"] + } + ] +} +``` + +**Response** (201 Created): +```json +{ + "success": true, + "data": { + "flowId": "flow-003", + "createdAt": "2026-01-18T12:00:00Z" + } +} +``` + +--- + +### Get AI Chatbot Config + +Retrieve AI chatbot configuration for a shop. + +```http +GET /api/v1/chatbots/ai-config?shopId={shopId} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "id": "ai-config-001", + "shopId": "shop-123", + "provider": "OpenAI", + "model": "gpt-4-turbo", + "systemPrompt": "You are a helpful customer service assistant...", + "temperature": 0.7, + "maxTokens": 500, + "isEnabled": true, + "updatedAt": "2026-01-17T10:00:00Z" + } +} +``` + +--- + +### Update AI Chatbot Config + +Update AI chatbot configuration. + +```http +PUT /api/v1/chatbots/ai-config +``` + +**Request Body**: +```json +{ + "shopId": "shop-123", + "provider": "OpenAI", + "model": "gpt-4-turbo", + "systemPrompt": "You are a helpful and friendly customer service assistant...", + "temperature": 0.8, + "maxTokens": 500, + "isEnabled": true +} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "configId": "ai-config-001", + "updatedAt": "2026-01-18T12:30:00Z" + } +} +``` + +--- + +## Error Responses + +### Standard Error Format + +```json +{ + "success": false, + "error": "Error message in English", + "errorCode": "ERROR_CODE", + "details": { + "field": "Field-specific error" + } +} +``` + +### Common Error Codes + +| Status Code | Error Code | Description | +|-------------|------------|-------------| +| 400 | `INVALID_REQUEST` | Invalid request parameters | +| 400 | `VALIDATION_ERROR` | Validation failed | +| 401 | `UNAUTHORIZED` | Missing or invalid token | +| 403 | `FORBIDDEN` | No permission | +| 403 | `INVALID_SIGNATURE` | Facebook webhook signature invalid | +| 404 | `NOT_FOUND` | Resource not found | +| 409 | `CONFLICT` | Duplicate resource | +| 429 | `RATE_LIMIT_EXCEEDED` | Too many requests | +| 500 | `INTERNAL_ERROR` | Server error | +| 503 | `SERVICE_UNAVAILABLE` | External service unavailable | + +--- + +## Rate Limiting + +API rate limits per API key: +- **100 requests/minute** for read operations (GET) +- **20 requests/minute** for write operations (POST, PUT, DELETE) + +**Rate Limit Headers**: +``` +X-RateLimit-Limit: 100 +X-RateLimit-Remaining: 95 +X-RateLimit-Reset: 1234567890 +``` + +--- + +## Webhooks Best Practices + +1. **Always return 200 immediately** +2. **Process asynchronously** +3. **Verify signature** +4. **Handle retries** (Facebook will retry if no 200 received) +5. **Use HTTPS** + +## Resources + +- [Facebook Messenger Platform Webhooks](https://developers.facebook.com/docs/messenger-platform/webhooks) +- [Facebook Send API Reference](https://developers.facebook.com/docs/messenger-platform/send-messages) +- [Architecture Documentation](./ARCHITECTURE.md) +- [Facebook Setup Guide](./FACEBOOK_SETUP.md) diff --git a/services/mkt-facebook-service-net/docs/en/FACEBOOK_SETUP.md b/services/mkt-facebook-service-net/docs/en/FACEBOOK_SETUP.md new file mode 100644 index 00000000..6424a8f6 --- /dev/null +++ b/services/mkt-facebook-service-net/docs/en/FACEBOOK_SETUP.md @@ -0,0 +1,224 @@ +# Facebook Messenger Setup Guide + +Step-by-step guide to set up Facebook Messenger Platform integration. + +## Prerequisites + +- Facebook account +- Facebook Page (already created) +- Developer account on Meta for Developers +- HTTPS endpoint for webhook (production) +- ngrok (for local testing) + +## Step 1: Create Facebook App + +### 1.1 Access Meta for Developers + +1. Go to [developers.facebook.com](https://developers.facebook.com/) +2. Click "My Apps" → "Create App" +3. Select use case: **"Other"** +4. Select app type: **"Business"** + +### 1.2 App Configuration + +- **App Name**: e.g., "My Shop Chatbot" +- **App Contact Email**: your email +- **Business Account**: Select or create + +> **Note**: Copy your **App ID** and **App Secret** (Settings → Basic). You'll need these later. + +--- + +## Step 2: Add Messenger Product + +### 2.1 Add Messenger + +1. In your app dashboard, click **"Add Product"** +2. Find **"Messenger"** and click **"Set Up"** + +### 2.2 Generate Page Access Token + +1. In Messenger Settings, scroll to **"Access Tokens"** +2. Click **"Add or Remove Pages"** +3. Select your Facebook Page +4. Click **"Generate Token"** +5. **Copy and save** the Page Access Token + +```bash +# Save to .env file +FACEBOOK_PAGE_ACCESS_TOKEN="EAAxxxxxxxxxx..." +``` + +> **Warning**: Page Access Token is sensitive! Never commit to Git or expose publicly. + +--- + +## Step 3: Configure Webhook + +### 3.1 Deploy Service or Use ngrok + +**Option A: Local Development with ngrok** + +```bash +# Install ngrok +brew install ngrok # macOS + +# Run service locally +dotnet run --project src/MktFacebookService.API + +# Expose local service +ngrok http 5000 + +# Copy HTTPS URL, e.g., https://abc123.ngrok.io +``` + +**Option B: Production Deployment** + +Deploy service to production server with HTTPS enabled. + +### 3.2 Add Webhook in Facebook App + +1. Go to Messenger → Settings → **"Webhooks"** +2. Click **"Add Callback URL"** +3. Enter: + - **Callback URL**: `https://your-domain.com/api/v1/webhooks/facebook` + - **Verify Token**: Create a random string (e.g., `my_verify_token_12345`) +4. Click **"Verify and Save"** + +```bash +# Save to .env +FACEBOOK_VERIFY_TOKEN="my_verify_token_12345" +``` + +> **Important**: Verify Token must match the value in service configuration! + +### 3.3 Subscribe to Webhook Events + +After webhook is verified, select fields: + +- [x] **messages** - Receive customer messages +- [x] **messaging_postbacks** - Receive button clicks +- [x] **message_reads** - Receive read receipts (optional) +- [x] **messaging_optins** - Receive opt-in events + +Click **"Subscribe"**. + +--- + +## Step 4: Test Integration + +### 4.1 Send Test Message + +1. Go to your Facebook Page +2. Click **"Send Message"** (as a user, not admin) +3. Type: `Hello` +4. Check service logs for incoming webhook event + +**Expected Log**: +``` +[INFO] Received message event from user: 1234567890 +[INFO] Processing incoming message: "Hello" +[INFO] Sending response via Facebook Messenger +``` + +### 4.2 Test Get Started Button + +Configure Get Started button to trigger welcome flow. + +**cURL Command**: +```bash +curl -X POST "https://graph.facebook.com/v21.0/me/messenger_profile" \ + -H "Content-Type: application/json" \ + -d '{ + "get_started": { + "payload": "GET_STARTED" + } + }' \ + -d "access_token=YOUR_PAGE_ACCESS_TOKEN" +``` + +--- + +## Environment Variables Summary + +```bash +# Facebook App Configuration +FACEBOOK_APP_ID="123456789012345" +FACEBOOK_APP_SECRET="abcdef1234567890abcdef1234567890" +FACEBOOK_VERIFY_TOKEN="my_verify_token_12345" + +# Facebook Page Access Token +FACEBOOK_PAGE_ACCESS_TOKEN="EAAxxxxxxxxxx..." + +# Facebook API Version +FACEBOOK_API_VERSION="v21.0" +``` + +--- + +## Troubleshooting + +### Issue 1: Webhook Verification Failed + +**Problem**: +``` +The URL couldn't be validated. Callback verification failed... +``` + +**Solution**: +1. Check service is running and accessible via HTTPS +2. Verify `FACEBOOK_VERIFY_TOKEN` matches exactly +3. Check logs for webhook GET request +4. Ensure returning hub.challenge as plain text (not JSON) + +### Issue 2: Not Receiving Messages + +**Problem**: Service not receiving messages + +**Solution**: +1. Verify webhook subscriptions are active (messages, messaging_postbacks) +2. Check Page Access Token hasn't expired +3. Test webhook manually with curl + +### Issue 3: Cannot Send Messages + +**Problem**: +``` +(#200) This user can't receive messages from this bot +``` + +**Solution**: +- User must send a message first (GDPR compliance) +- Or user must opt-in via checkbox plugin +- Page Access Token must have `pages_messaging` permission + +--- + +## Testing Checklist + +- [ ] Facebook App created and Messenger product added +- [ ] Page Access Token generated and saved +- [ ] Webhook configured and verified +- [ ] Webhook events subscribed (messages, postbacks) +- [ ] Test message sent and received successfully +- [ ] Bot response sent back to user +- [ ] Get Started button triggers welcome flow +- [ ] Signature verification working + +--- + +## Resources + +### Official Facebook Documentation +- [Messenger Platform Overview](https://developers.facebook.com/docs/messenger-platform/) +- [Webhooks Reference](https://developers.facebook.com/docs/messenger-platform/webhooks) +- [Send API Reference](https://developers.facebook.com/docs/messenger-platform/send-messages) + +### Tools +- [Facebook Graph API Explorer](https://developers.facebook.com/tools/explorer/) +- [ngrok](https://ngrok.com/) - Local HTTPS tunnel + +### Internal Docs +- [API Documentation](./API.md) +- [Architecture Documentation](./ARCHITECTURE.md) +- [AI Chatbot Configuration](./AI_CHATBOT.md) diff --git a/services/mkt-facebook-service-net/docs/vi/AI_CHATBOT.md b/services/mkt-facebook-service-net/docs/vi/AI_CHATBOT.md new file mode 100644 index 00000000..994351ad --- /dev/null +++ b/services/mkt-facebook-service-net/docs/vi/AI_CHATBOT.md @@ -0,0 +1,361 @@ +# Hướng Dẫn Cấu Hình AI Chatbot + +Hướng dẫn cấu hình và tối ưu chatbot AI sử dụng OpenAI hoặc Azure OpenAI. + +## Tổng Quan + +AI Chatbot sử dụng GPT-4 hoặc GPT-3.5 để cung cấp phản hồi thông minh, theo ngữ cảnh cho tin nhắn khách hàng. Nó có thể hiểu ngôn ngữ tự nhiên, duy trì ngữ cảnh hội thoại, và cung cấp thông tin hữu ích. + +## Chọn AI Provider + +### Option 1: OpenAI + +**Ưu điểm**: +- ✅ Dễ dàng setup +- ✅ Pay-as-you-go pricing +- ✅ Models mới nhất (GPT-4 Turbo) +- ✅ Tốt cho global deployment + +**Nhược điểm**: +- ❌ External API (latency) +- ❌ Dữ liệu xử lý bên ngoài region của bạn + +**Setup**: +1. Tạo account tại [platform.openai.com](https://platform.openai.com/) +2. Generate API key từ trang **API Keys** +3. Thêm credits vào billing account + +```bash +# Cấu hình .env +OPENAI_API_KEY="sk-proj-xxxxxxxxxxxxxxxxx" +OPENAI_MODEL="gpt-4-turbo" +``` + +### Option 2: Azure OpenAI + +**Ưu điểm**: +- ✅ Bảo mật & tuân thủ Enterprise +- ✅ Kiểm soát data residency +- ✅ SLA guarantees +- ✅ VNET integration + +**Nhược điểm**: +- ❌ Setup phức tạp hơn +- ❌ Yêu cầu Azure subscription +- ❌ Model updates chậm hơn + +**Setup**: +1. Tạo Azure OpenAI resource trong Azure Portal +2. Deploy model (ví dụ, `gpt-4`) +3. Copy endpoint và key + +```bash +# Cấu hình .env +AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" +AZURE_OPENAI_KEY="your-key-here" +AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4" +``` + +--- + +## Lựa Chọn Model + +| Model | Phù Hợp Cho | Chi Phí | Tốc Độ | +|-------|-------------|---------|--------| +| **GPT-4 Turbo** | Reasoning phức tạp, responses chính xác | $$ | Trung bình | +| **GPT-4** | Chất lượng cao, hiểu sâu sắc | $$$ | Chậm | +| **GPT-3.5 Turbo** | Responses nhanh, queries đơn giản | $ | Nhanh | + +**Đề Nghị**: +- **E-commerce Support**: `gpt-3.5-turbo` (nhanh & tiết kiệm) +- **Technical Support**: `gpt-4-turbo` (chính xác hơn) +- **Multilingual Support**: `gpt-4-turbo` (xử lý ngôn ngữ tốt hơn) + +--- + +## Thực Hành Tốt Nhất System Prompt + +### Ví Dụ E-commerce + +``` +Bạn là trợ lý AI cho "GoodGo Shop", nền tảng thương mại điện tử tại Việt Nam. + +SẢN PHẨM: +- Điện tử (điện thoại, laptop, phụ kiện) +- Thời trang (quần áo, giày, túi xách) +- Nhà cửa & Đời sống (nội thất, đồ bếp) + +KHẢ NĂNG: +- Kiểm tra trạng thái đơn hàng (hỏi order ID) +- Cung cấp thông tin sản phẩm +- Giải thích chính sách vận chuyển & đổi trả +- Hỗ trợ vấn đề tài khoản + +TONE: +- Thân thiện và tự nhiên +- Sử dụng tiếng Việt khi khách hàng dùng tiếng Việt +- Dùng ngôn ngữ đơn giản, tránh thuật ngữ phức tạp + +GIỚI HẠN: +- Không thể xử lý hoàn tiền (chuyển sang nhân viên) +- Không thể sửa đơn hàng (chuyển sang customer service) +- Không thể truy cập thông tin thanh toán + +Khi không thể giúp, nói: "Để tôi kết nối bạn với đội ngũ chăm sóc khách hàng." +``` + +--- + +## Tham Số Cấu Hình + +### Temperature + +Kiểm soát độ ngẫu nhiên (0.0 - 2.0) + +| Giá trị | Hành vi | Use Case | +|---------|---------|----------| +| **0.0 - 0.3** | Nhất quán, tập trung | FAQ, câu hỏi chính sách | +| **0.4 - 0.7** | Cân bằng sáng tạo | Customer service chung | +| **0.8 - 1.0** | Sáng tạo hơn | Marketing, engagement | + +**Đề nghị**: `0.7` cho customer service + +### Max Tokens + +Độ dài tối đa của phản hồi + +| Tokens | Từ | Use Case | +|--------|-----|----------| +| 150 | ~100 | Câu trả lời ngắn | +| 300 | ~200 | Responses chuẩn | +| 500 | ~350 | Giải thích chi tiết | + +**Đề nghị**: `300-500` tokens cho hầu hết trường hợp + +### Top P (Nucleus Sampling) + +Thay thế cho temperature (0.0 - 1.0) + +- **0.9**: Mặc định đề nghị +- Dùng `temperature` HOẶC `top_p`, không dùng cả hai + +--- + +## Quản Lý Ngữ Cảnh + +### Conversation History + +Bao gồm tin nhắn gần đây cho ngữ cảnh: + +```csharp +var messages = new List +{ + new ChatMessage(ChatRole.System, systemPrompt), + new ChatMessage(ChatRole.User, "Giờ mở cửa là mấy giờ?"), + new ChatMessage(ChatRole.Assistant, "Chúng tôi mở cửa 10 giờ sáng - 10 giờ tối hàng ngày."), + new ChatMessage(ChatRole.User, "Bây giờ có thể order giao hàng không?"), +}; +``` + +**Thực Hành Tốt**: +- Giữ **5-10 messages** gần nhất +- Xóa messages cũ để tiết kiệm tokens +- Luôn bao gồm System message + +### Token Budget + +| Component | Tokens | Ghi Chú | +|-----------|--------|---------| +| System Prompt | 100-200 | Cố định | +| Conversation History | 50-100 per message | Biến đổi | +| Max Response | 300-500 | Cấu hình được | +| **Tổng mỗi request** | ~500-1500 | Ước tính | + +**Ước Tính Chi Phí** (GPT-4 Turbo): +- Input: $0.01 / 1K tokens +- Output: $0.03 / 1K tokens +- **Chi phí trung bình mỗi conversation**: $0.02 - $0.05 + +--- + +## Chiến Lược Dự Phòng + +### 1. Fallback về Automation + +Khi AI confidence thấp, fallback về automation rules. + +### 2. Chuyển Sang Người + +Chuyển sang agent người khi vấn đề phức tạp. + +**Trigger Phrases** (trong AI response): +- "Tôi không chắc" +- "Để tôi kết nối" +- "nhân viên" + +**Hành động**: Update conversation status thành `NeedsHumanAgent` + +### 3. Xử Lý Timeout + +Nếu OpenAI API timeout, dùng fallback response: +``` +"Xin lỗi vì sự chậm trễ. Để tôi kết nối bạn với đội ngũ của chúng tôi." +``` + +--- + +## Hỗ Trợ Đa Ngôn Ngữ + +### Tự Động Phát Hiện Ngôn Ngữ + +GPT-4 có thể tự động phát hiện và phản hồi bằng ngôn ngữ khách hàng. + +**System Prompt**: +``` +Bạn là trợ lý chăm sóc khách hàng đa ngôn ngữ. +- Phát hiện ngôn ngữ của khách hàng +- Phản hồi bằng cùng ngôn ngữ +- Hỗ trợ: Tiếng Anh, Tiếng Việt, Tiếng Thái, Tiếng Trung +``` + +--- + +## Kiểm Thử & Tối Ưu + +### A/B Testing System Prompts + +| Chỉ Số | Mục Tiêu | Đo Lường | +|--------|----------|----------| +| **Chất Lượng Response** | Hài lòng > 80% | Thumbs up/down | +| **Tỷ Lệ Giải Quyết** | > 70% không cần người | Track escalations | +| **Thời Gian Response** | < 3 giây | API latency | +| **Chi Phí/Conversation** | < $0.05 | Sử dụng token | + +--- + +## Tối Ưu Chi Phí + +### 1. Dùng System Prompts Ngắn Hơn + +```diff +- Bạn là trợ lý chăm sóc khách hàng hữu ích cho GoodGo Shop, nền tảng thương mại điện tử bán điện tử, thời trang, nhà cửa... ++ Bạn là trợ lý AI cho GoodGo, nền tảng thương mại điện tử. +``` + +**Tiết kiệm**: ~50 tokens mỗi request + +### 2. Giới Hạn Conversation History + +```csharp +// Chỉ giữ 5 messages gần nhất +var contextMessages = conversation.Messages + .OrderByDescending(m => m.SentAt) + .Take(5) + .Reverse(); +``` + +**Tiết kiệm**: ~200-400 tokens mỗi request + +### 3. Dùng GPT-3.5 Cho Queries Đơn Giản + +```csharp +if (IsSimpleQuery(message)) // FAQ, product lookup +{ + model = "gpt-3.5-turbo"; // Rẻ hơn 10 lần +} +else +{ + model = "gpt-4-turbo"; +} +``` + +### 4. Cache Responses Thường Dùng + +Cache responses cho FAQs trong Redis. + +--- + +## Ví Dụ Cấu Hình + +### Cấu Hình 1: Support Chất Lượng Cao + +```json +{ + "provider": "OpenAI", + "model": "gpt-4-turbo", + "systemPrompt": "Prompt customer service chi tiết...", + "temperature": 0.6, + "maxTokens": 500 +} +``` + +**Use Case**: Thương hiệu cao cấp, sản phẩm phức tạp +**Chi phí**: ~$0.04 mỗi conversation + +### Cấu Hình 2: Nhanh & Tiết Kiệm + +```json +{ + "provider": "OpenAI", + "model": "gpt-3.5-turbo", + "systemPrompt": "Prompt customer service ngắn gọn...", + "temperature": 0.7, + "maxTokens": 300 +} +``` + +**Use Case**: High-volume, queries đơn giản +**Chi phí**: ~$0.005 mỗi conversation + +--- + +## Xử Lý Sự Cố + +### Issue 1: Responses Quá Chung Chung + +**Giải pháp**: +- ✅ Thêm context vào system prompt +- ✅ Bao gồm conversation history +- ✅ Giảm temperature (0.5) + +### Issue 2: Chi Phí Cao + +**Giải pháp**: +- ✅ Rút ngắn system prompt +- ✅ Giới hạn conversation history +- ✅ Dùng GPT-3.5 cho queries đơn giản +- ✅ Cache FAQs thường gặp + +### Issue 3: Responses Chậm + +**Giải pháp**: +- ✅ Dùng GPT-3.5 Turbo (nhanh hơn) +- ✅ Giảm max_tokens +- ✅ Enable streaming +- ✅ Dùng Azure OpenAI (regional deployment) + +### Issue 4: AI Hallucinates + +**Giải pháp**: +- ✅ Thêm: "Không được bịa đặt thông tin" +- ✅ Giảm temperature (0.3-0.5) +- ✅ Cung cấp factual context trong system prompt + +--- + +## Tài Nguyên + +### Tài Liệu Chính Thức +- [OpenAI API Reference](https://platform.openai.com/docs/api-reference) +- [Azure OpenAI Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/) +- [GPT Best Practices](https://platform.openai.com/docs/guides/prompt-engineering) + +### Công Cụ +- [OpenAI Playground](https://platform.openai.com/playground) +- [Tokenizer](https://platform.openai.com/tokenizer) +- [Azure OpenAI Studio](https://oai.azure.com/) + +### Tài Liệu Nội Bộ +- [Tài Liệu API](./API.md) +- [Tài Liệu Kiến Trúc](./ARCHITECTURE.md) +- [Hướng Dẫn Setup Facebook](./FACEBOOK_SETUP.md) diff --git a/services/mkt-facebook-service-net/docs/vi/API.md b/services/mkt-facebook-service-net/docs/vi/API.md new file mode 100644 index 00000000..299dc244 --- /dev/null +++ b/services/mkt-facebook-service-net/docs/vi/API.md @@ -0,0 +1,285 @@ +# Tài Liệu API + +**Dịch vụ**: mkt-facebook-service-net +**Base URL**: `http://localhost/api/v1/mkt-facebook` +**Phiên bản API**: v1.0 + +## Xác Thực + +Tất cả REST API endpoints (trừ webhooks) yêu cầu JWT Bearer token. + +```http +Authorization: Bearer {jwt_token} +``` + +## Webhooks API + +### Verify Facebook Webhook + +Facebook dùng endpoint này để verify webhook trong quá trình setup. + +```http +GET /api/v1/webhooks/facebook +``` + +**Query Parameters**: +| Parameter | Type | Mô Tả | +|-----------|------|-------| +| `hub.mode` | string | Phải là "subscribe" | +| `hub.verify_token` | string | Token được cấu hình trong Facebook App | +| `hub.challenge` | string | Chuỗi random từ Facebook | + +**Response** (200 OK): +``` +{hub.challenge} +``` + +--- + +### Nhận Facebook Messenger Events + +Nhận messages, postbacks, và các events khác từ Facebook Messenger Platform. + +```http +POST /api/v1/webhooks/facebook +``` + +**Headers**: +``` +X-Hub-Signature-256: sha256={signature} +Content-Type: application/json +``` + +**Request Body** (Message Event): +```json +{ + "object": "page", + "entry": [{ + "id": "PAGE_ID", + "time": 1234567890, + "messaging": [{ + "sender": { "id": "USER_ID" }, + "recipient": { "id": "PAGE_ID" }, + "timestamp": 1234567890, + "message": { + "mid": "MESSAGE_ID", + "text": "Hello chatbot!", + "quick_reply": { + "payload": "QUICK_REPLY_PAYLOAD" + } + } + }] + }] +} +``` + +**Response** (200 OK): +```json +{ + "success": true +} +``` + +--- + +## Conversations API + +### Danh Sách Conversations + +Lấy danh sách conversations có phân trang. + +```http +GET /api/v1/conversations +``` + +**Query Parameters**: +| Parameter | Type | Required | Default | Mô Tả | +|-----------|------|----------|---------|-------| +| `shopId` | guid | Yes | - | Shop/Business ID | +| `status` | string | No | - | Lọc theo status (Active, Closed) | +| `skip` | int | No | 0 | Số records bỏ qua | +| `take` | int | No | 20 | Số records trả về | + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "conversations": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "customerId": "650e8400-e29b-41d4-a716-446655440001", + "customerName": "Nguyễn Văn A", + "pageId": "123456789", + "status": "Active", + "channel": "FacebookMessenger", + "lastMessageText": "Cảm ơn!", + "lastMessageAt": "2026-01-18T10:30:00Z", + "createdAt": "2026-01-18T09:00:00Z" + } + ], + "totalCount": 150 + }, + "pagination": { + "page": 1, + "limit": 20, + "total": 150, + "totalPages": 8 + } +} +``` + +--- + +### Lấy Conversation Theo ID + +Lấy một conversation với toàn bộ lịch sử tin nhắn. + +```http +GET /api/v1/conversations/{id} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "id": "550e8400-e29b-41d4-a716-446655440000", + "customer": { + "id": "650e8400-e29b-41d4-a716-446655440001", + "name": "Nguyễn Văn A", + "tags": ["VIP", "Khách hàng quay lại"] + }, + "messages": [ + { + "id": "msg-001", + "senderId": "1234567890", + "content": "Xin chào, tôi cần hỗ trợ", + "messageType": "Text", + "direction": "Incoming", + "sentAt": "2026-01-18T09:00:00Z" + } + ] + } +} +``` + +--- + +## Customers API + +### Danh Sách Khách Hàng + +Lấy danh sách khách hàng có phân trang. + +```http +GET /api/v1/customers +``` + +**Query Parameters**: +| Parameter | Type | Required | Default | Mô Tả | +|-----------|------|----------|---------|-------| +| `shopId` | guid | Yes | - | Shop/Business ID | +| `tags` | string[] | No | - | Lọc theo tags | +| `search` | string | No | - | Tìm kiếm theo tên hoặc email | +| `skip` | int | No | 0 | Pagination offset | +| `take` | int | No | 20 | Page size | + +--- + +## Chatbots API + +### Danh Sách Automation Flows + +Lấy tất cả automation flows của shop. + +```http +GET /api/v1/chatbots/flows +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": [ + { + "id": "flow-001", + "name": "Welcome Flow", + "shopId": "shop-123", + "triggerType": "GetStarted", + "isActive": true, + "nodeCount": 8 + } + ] +} +``` + +--- + +### Lấy AI Chatbot Config + +Lấy cấu hình AI chatbot của shop. + +```http +GET /api/v1/chatbots/ai-config?shopId={shopId} +``` + +**Response** (200 OK): +```json +{ + "success": true, + "data": { + "id": "ai-config-001", + "shopId": "shop-123", + "provider": "OpenAI", + "model": "gpt-4-turbo", + "systemPrompt": "Bạn là trợ lý AI...", + "temperature": 0.7, + "maxTokens": 500, + "isEnabled": true + } +} +``` + +--- + +## Phản Hồi Lỗi + +### Format Lỗi Chuẩn + +```json +{ + "success": false, + "error": "Thông báo lỗi", + "errorCode": "ERROR_CODE", + "details": { + "field": "Lỗi cụ thể của field" + } +} +``` + +### Mã Lỗi Thường Gặp + +| Status Code | Error Code | Mô Tả | +|-------------|------------|-------| +| 400 | `INVALID_REQUEST` | Request parameters không hợp lệ | +| 401 | `UNAUTHORIZED` | Thiếu hoặc token không hợp lệ | +| 403 | `FORBIDDEN` | Không có quyền | +| 404 | `NOT_FOUND` | Không tìm thấy resource | +| 429 | `RATE_LIMIT_EXCEEDED` | Quá nhiều requests | +| 500 | `INTERNAL_ERROR` | Lỗi server | + +--- + +## Giới Hạn Tỷ Lệ + +Giới hạn API mỗi API key: +- **100 requests/phút** cho read operations (GET) +- **20 requests/phút** cho write operations (POST, PUT, DELETE) + +--- + +## Tài Nguyên + +- [Tài Liệu Kiến Trúc](./ARCHITECTURE.md) +- [Hướng Dẫn Setup Facebook](./FACEBOOK_SETUP.md) diff --git a/services/mkt-facebook-service-net/docs/vi/ARCHITECTURE.md b/services/mkt-facebook-service-net/docs/vi/ARCHITECTURE.md new file mode 100644 index 00000000..d3932596 --- /dev/null +++ b/services/mkt-facebook-service-net/docs/vi/ARCHITECTURE.md @@ -0,0 +1,505 @@ +# Tài Liệu Kiến Trúc + +**Dịch vụ**: mkt-facebook-service-net +**Cập nhật lần cuối**: 2026-01-18 + +## Tổng Quan + +Dịch vụ Marketing Facebook cung cấp giải pháp toàn diện cho doanh nghiệp tương tác với khách hàng trên Facebook Messenger thông qua chatbots tự động và hội thoại AI. + +## Kiến Trúc Hệ Thống + +### Kiến Trúc Tổng Quát + +```mermaid +graph TB + subgraph "Facebook Messenger Platform" + EndUser[Người dùng cuối] + MessengerApp[Messenger App] + end + + subgraph "GoodGo Platform" + direction TB + + subgraph "mkt-facebook-service-net" + Webhook[Webhooks Controller] + API[REST API] + + subgraph "Application Layer" + Commands[Commands] + Queries[Queries] + Handlers[MediatR Handlers] + end + + subgraph "Domain Layer" + Conversation[Conversation Aggregate] + Customer[Customer Aggregate] + ChatbotFlow[ChatbotFlow Aggregate] + AIChatbot[AIChatbotConfig Aggregate] + end + + subgraph "Infrastructure Layer" + FBClient[FacebookMessengerClient] + AIClient[OpenAI Client] + Repos[Repositories] + DbContext[EF Core DbContext] + end + end + + subgraph "Data & Messaging" + PostgreSQL[(PostgreSQL)] + Redis[(Redis)] + RabbitMQ[RabbitMQ] + end + end + + subgraph "External Services" + FacebookAPI[Facebook Graph API] + OpenAI[OpenAI / Azure OpenAI] + end + + EndUser -->|Gửi Message| MessengerApp + MessengerApp -->|Webhook Event| Webhook + Webhook -->|Dispatch Command| Commands + Commands --> Handlers + Handlers --> Domain Layer + Handlers --> Repos + Repos --> DbContext + DbContext --> PostgreSQL + Handlers -->|Cache| Redis + Handlers -->|Publish Events| RabbitMQ + + API -->|Commands/Queries| Handlers + + FBClient -->|HTTP| FacebookAPI + AIClient -->|HTTP| OpenAI + + Handlers -->|Gửi Message| FBClient + Handlers -->|AI Completion| AIClient +``` + +## Mô Hình Domain + +### Các Aggregate + +#### 1. Conversation Aggregate + +```mermaid +classDiagram + class Conversation { + +Guid Id + +Guid CustomerId + +string PageId + +ConversationStatus Status + +Channel Channel + +DateTime LastMessageAt + +DateTime CreatedAt + -List~Message~ _messages + +IReadOnlyCollection~Message~ Messages + + +AddMessage(Message) void + +MarkAsRead() void + +Assign(string agentId) void + +Close() void + } + + class Message { + +Guid Id + +string SenderId + +string Content + +MessageType Type + +MessageDirection Direction + +DateTime SentAt + +Dictionary Metadata + } + + class ConversationStatus { + <> + Active + Closed + Archived + } + + class MessageDirection { + <> + Incoming + Outgoing + } + + Conversation "1" *-- "many" Message + Conversation --> ConversationStatus + Message --> MessageDirection +``` + +Aggregate Conversation quản lý vòng đời của một cuộc hội thoại khách hàng, bao gồm tất cả tin nhắn trao đổi. + +**Quy Tắc Nghiệp Vụ**: +- Chỉ có thể thêm messages vào conversations Active +- Một conversation phải có ít nhất 1 message +- Messages không thể bị xóa, chỉ có thể archive conversation + +#### 2. Customer Aggregate + +```mermaid +classDiagram + class Customer { + +Guid Id + +string FacebookUserId + +string Name + +string Email + +string Phone + +string ProfilePicUrl + +string Locale + +string Timezone + +List~string~ Tags + +Dictionary~string,string~ CustomFields + +DateTime FirstSeenAt + +DateTime LastInteractionAt + + +UpdateProfile(CustomerProfile) void + +AddTag(string tag) void + +RemoveTag(string tag) void + +SetCustomField(string key, string value) void + } +``` + +Aggregate Customer đại diện cho một Facebook user tương tác với chatbot. + +**Quy Tắc Nghiệp Vụ**: +- `FacebookUserId` là unique identifier +- Tags phải là valid strings (không rỗng) +- Custom fields giới hạn 50 fields per customer +- Profile được sync từ Facebook Messenger Platform + +#### 3. ChatbotFlow Aggregate (Automation) + +```mermaid +classDiagram + class ChatbotFlow { + +Guid Id + +string Name + +Guid ShopId + +FlowTriggerType TriggerType + +string TriggerValue + +bool IsActive + -List~FlowNode~ _nodes + +IReadOnlyCollection~FlowNode~ Nodes + + +AddNode(FlowNode) void + +ConnectNodes(Guid from, Guid to) void + +Activate() void + +Deactivate() void + } + + class FlowNode { + +Guid Id + +NodeType Type + +string Content + +Dictionary Config + +List~Guid~ NextNodeIds + } + + class NodeType { + <> + Start + Message + Question + Condition + Action + End + } + + ChatbotFlow "1" *-- "many" FlowNode + FlowNode --> NodeType +``` + +Aggregate ChatbotFlow định nghĩa quy tắc tự động và luồng hội thoại. + +**Quy Tắc Nghiệp Vụ**: +- Mỗi flow phải có đúng 1 Start node +- Mỗi flow phải có ít nhất 1 End node +- Không được có circular references (vòng lặp vô hạn) +- `TriggerValue` phải unique trong cùng ShopId + +#### 4. AIChatbotConfig Aggregate + +```mermaid +classDiagram + class AIChatbotConfig { + +Guid Id + +Guid ShopId + +AIProvider Provider + +string Model + +string SystemPrompt + +float Temperature + +int MaxTokens + +bool IsEnabled + +DateTime CreatedAt + +DateTime UpdatedAt + + +UpdateConfig(ConfigUpdate) void + +Enable() void + +Disable() void + } + + class AIProvider { + <> + OpenAI + AzureOpenAI + } + + AIChatbotConfig --> AIProvider +``` + +Aggregate AIChatbotConfig cấu hình hành vi chatbot AI. + +**Quy Tắc Nghiệp Vụ**: +- Temperature phải trong khoảng [0.0, 2.0] +- MaxTokens phải > 0 và <= 4096 +- SystemPrompt không được rỗng +- Chỉ có 1 active config per ShopId + +## Luồng Xử Lý Tin Nhắn + +```mermaid +sequenceDiagram + participant User as Người dùng + participant FB as Facebook Messenger + participant Webhook as Webhooks Controller + participant Handler as ProcessIncomingMessage Handler + participant Domain as Domain Layer + participant Strategy as Response Strategy + participant AI as OpenAI Client + participant FBClient as Facebook Client + participant DB as PostgreSQL + + User->>FB: Gửi tin nhắn + FB->>Webhook: POST /webhooks/facebook + Webhook->>Webhook: Verify signature + Webhook-->>FB: 200 OK (ngay lập tức) + + Webhook->>Handler: Dispatch ProcessIncomingMessageCommand + + Handler->>DB: Get or Create Customer + Handler->>DB: Get or Create Conversation + Handler->>Domain: conversation.AddMessage(incoming) + Handler->>DB: Save changes + + Handler->>Strategy: DetermineStrategy(conversation) + + alt AI Chatbot Enabled + Strategy-->>Handler: Use AI + Handler->>AI: GetChatCompletion(context) + AI-->>Handler: AI Response + else Automation Flow + Strategy-->>Handler: Use Automation + Handler->>DB: Find matching FlowNode + Handler->>Handler: Execute flow logic + end + + Handler->>FBClient: SendMessage(response) + FBClient->>FB: POST /messages + FB-->>FBClient: Success + + Handler->>Domain: conversation.AddMessage(outgoing) + Handler->>DB: Save changes +``` + +**Các Bước Chính**: + +1. **Nhận Webhook**: Facebook gửi event đến service +2. **Xác thực Chữ ký**: Verify Facebook request signature +3. **Quản lý Khách hàng & Hội thoại**: Get hoặc tạo mới +4. **Lựa chọn Chiến lược**: Automation vs AI +5. **Tạo Phản hồi**: Dựa trên strategy +6. **Gửi Tin nhắn**: Qua Facebook Messenger API +7. **Lưu trữ**: Save message history + +## Mẫu Tích Hợp + +### Tích Hợp Facebook Messenger Platform + +Dịch vụ tích hợp với Facebook Messenger Platform qua: +- **Webhooks**: Nhận messages, postbacks, read receipts +- **Send API**: Gửi messages, quick replies, templates +- **User Profile API**: Lấy thông tin user + +### Tích Hợp OpenAI + +AI chatbot sử dụng OpenAI Chat Completions API với: +- **Streaming**: Tạo phản hồi real-time +- **Context Management**: Hội thoại nhiều lượt +- **Retry Logic**: Polly resilience policies +- **Cost Tracking**: Theo dõi sử dụng tokens + +## Mô Hình Dữ Liệu + +### Lược Đồ Cơ Sở Dữ Liệu + +```mermaid +erDiagram + CONVERSATIONS ||--o{ MESSAGES : contains + CUSTOMERS ||--o{ CONVERSATIONS : has + CHATBOT_FLOWS ||--o{ FLOW_NODES : contains + AI_CHATBOT_CONFIGS ||--|| SHOPS : belongs_to + + CONVERSATIONS { + uuid id PK + uuid customer_id FK + string page_id + string status + string channel + timestamp last_message_at + timestamp created_at + } + + MESSAGES { + uuid id PK + uuid conversation_id FK + string sender_id + text content + string message_type + string direction + jsonb metadata + timestamp sent_at + } + + CUSTOMERS { + uuid id PK + string facebook_user_id UK + string name + string email + string profile_pic_url + string locale + string timezone + text[] tags + jsonb custom_fields + timestamp first_seen_at + timestamp last_interaction_at + } + + CHATBOT_FLOWS { + uuid id PK + uuid shop_id FK + string name + string trigger_type + string trigger_value UK + boolean is_active + timestamp created_at + } + + FLOW_NODES { + uuid id PK + uuid flow_id FK + string node_type + text content + jsonb config + uuid[] next_node_ids + int order_index + } + + AI_CHATBOT_CONFIGS { + uuid id PK + uuid shop_id FK UK + string provider + string model + text system_prompt + float temperature + int max_tokens + boolean is_enabled + timestamp updated_at + } +``` + +### Chỉ Mục + +**Tối Ưu Hiệu Năng**: +- `idx_conversations_customer_id` - Fast lookup by customer +- `idx_conversations_page_id_status` - Filter active conversations by page +- `idx_messages_conversation_id_sent_at` - Sort messages chronologically +- `idx_customers_facebook_user_id` - Unique lookup +- `idx_chatbot_flows_shop_id_trigger` - Match triggers +- `idx_ai_configs_shop_id` - One config per shop + +## Bảo Mật + +### Xác Thực Facebook Webhook + +Tất cả webhooks phải: +1. Có header `X-Hub-Signature-256` +2. Signature được verify bằng App Secret +3. Signature không hợp lệ bị reject với 403 + +### Xác Thực API + +REST API endpoints yêu cầu: +- JWT Bearer token từ IAM service +- Authorization theo role (Admin, Agent, Viewer) + +### Quyền Riêng Tư Dữ Liệu + +- **GDPR Compliance**: Customer data export và deletion +- **Data Encryption**: At rest (PostgreSQL) và in transit (HTTPS) +- **PII Protection**: Customer info không log ra files + +## Cân Nhắc Khả Năng Mở Rộng + +### Mở Rộng Ngang + +Service hỗ trợ mở rộng ngang với: +- **Stateless API**: Không có server-side sessions +- **Redis Cache**: Cache chia sẻ giữa các instances +- **RabbitMQ**: Message queue phân tán +- **Load Balancer**: Traefik phân phối traffic + +### Giới Hạn Tỷ Lệ + +Giới hạn Facebook Messenger API: +- **100 tin nhắn/giây** mỗi app +- **Burst tolerance**: Queue messages khi tăng đột biến +- **Retry with exponential backoff**: Cho responses 429 + +## Giám Sát + +### Chỉ Số Chính + +| Chỉ số | Mô Tả | Mục Tiêu | +|--------|-------|----------| +| **Webhook Latency** | Thời gian trả 200 cho Facebook | < 500ms | +| **Message Processing Time** | Thời gian tạo & gửi response | < 2s | +| **AI Response Time** | OpenAI API latency | < 5s | +| **Message Success Rate** | % messages gửi thành công | > 99% | +| **Conversation Active Count** | Số conversations đang active | - | +| **AI Cost per Conversation** | Chi phí OpenAI trung bình | Monitor | + +### Kiểm Tra Sức Khỏe + +- **Database**: PostgreSQL connection pooling +- **Redis**: Cache availability +- **RabbitMQ**: Message broker connection +- **Facebook API**: API availability check +- **OpenAI API**: Provider availability + +## Quyết Định Thiết Kế + +### Tại Sao CQRS? + +Tách biệt operations đọc và ghi để: +- **Queries Tối Ưu**: Dùng Dapper cho reads nhanh +- **Commands Phức Tạp**: Dùng EF Core với Domain Model +- **Khả Năng Mở Rộng**: Scale reads và writes độc lập + +### Tại Sao DDD? + +Logic nghiệp vụ phức tạp yêu cầu: +- **Rich Domain Model**: Conversation, Customer aggregates +- **Encapsulation**: Business rules trong domain +- **Domain Events**: Tách biệt side effects + +## Tài Nguyên + +- [Tài Liệu API](./API.md) +- [Hướng Dẫn Setup Facebook](./FACEBOOK_SETUP.md) +- [Hướng Dẫn AI Chatbot](./AI_CHATBOT.md) +- [Domain-Driven Design Skill](../../../.agent/skills/domain-driven-design/SKILL.md) +- [CQRS MediatR Skill](../../../.agent/skills/cqrs-mediatr/SKILL.md) diff --git a/services/mkt-facebook-service-net/docs/vi/FACEBOOK_SETUP.md b/services/mkt-facebook-service-net/docs/vi/FACEBOOK_SETUP.md new file mode 100644 index 00000000..8334b7d2 --- /dev/null +++ b/services/mkt-facebook-service-net/docs/vi/FACEBOOK_SETUP.md @@ -0,0 +1,224 @@ +# Hướng Dẫn Thiết Lập Facebook Messenger + +Hướng dẫn từng bước để thiết lập tích hợp Facebook Messenger Platform. + +## Yêu Cầu + +- Tài khoản Facebook +- Facebook Page (đã tạo sẵn) +- Tài khoản developer trên Meta for Developers +- HTTPS endpoint cho webhook (production) +- ngrok (cho local testing) + +## Bước 1: Tạo Facebook App + +### 1.1 Truy Cập Meta for Developers + +1. Truy cập [developers.facebook.com](https://developers.facebook.com/) +2. Click "My Apps" → "Create App" +3. Chọn use case: **"Other"** +4. Chọn app type: **"Business"** + +### 1.2 Cấu Hình App + +- **App Name**: ví dụ, "My Shop Chatbot" +- **App Contact Email**: email của bạn +- **Business Account**: Chọn hoặc tạo mới + +> **Lưu ý**: Copy **App ID** và **App Secret** (Settings → Basic). Bạn sẽ cần chúng sau. + +--- + +## Bước 2: Thêm Messenger Product + +### 2.1 Thêm Messenger + +1. Trong app dashboard, click **"Add Product"** +2. Tìm **"Messenger"** và click **"Set Up"** + +### 2.2 Generate Page Access Token + +1. Trong Messenger Settings, scroll đến **"Access Tokens"** +2. Click **"Add or Remove Pages"** +3. Chọn Facebook Page của bạn +4. Click **"Generate Token"** +5. **Copy và lưu** Page Access Token + +```bash +# Lưu vào file .env +FACEBOOK_PAGE_ACCESS_TOKEN="EAAxxxxxxxxxx..." +``` + +> **Cảnh báo**: Page Access Token rất nhạy cảm! Không bao giờ commit vào Git hoặc công khai. + +--- + +## Bước 3: Cấu Hình Webhook + +### 3.1 Deploy Service hoặc Dùng ngrok + +**Option A: Local Development với ngrok** + +```bash +# Cài đặt ngrok +brew install ngrok # macOS + +# Chạy service local +dotnet run --project src/MktFacebookService.API + +# Expose service local +ngrok http 5000 + +# Copy HTTPS URL, ví dụ, https://abc123.ngrok.io +``` + +**Option B: Production Deployment** + +Deploy service lên production server với HTTPS enabled. + +### 3.2 Thêm Webhook trong Facebook App + +1. Vào Messenger → Settings → **"Webhooks"** +2. Click **"Add Callback URL"** +3. Nhập: + - **Callback URL**: `https://your-domain.com/api/v1/webhooks/facebook` + - **Verify Token**: Tạo chuỗi random (ví dụ, `my_verify_token_12345`) +4. Click **"Verify and Save"** + +```bash +# Lưu vào .env +FACEBOOK_VERIFY_TOKEN="my_verify_token_12345" +``` + +> **Quan trọng**: Verify Token phải match với giá trị trong service configuration! + +### 3.3 Subscribe Webhook Events + +Sau khi webhook được verified, chọn các fields: + +- [x] **messages** - Nhận customer messages +- [x] **messaging_postbacks** - Nhận button clicks +- [x] **message_reads** - Nhận read receipts (tùy chọn) +- [x] **messaging_optins** - Nhận opt-in events + +Click **"Subscribe"**. + +--- + +## Bước 4: Kiểm Tra Tích Hợp + +### 4.1 Gửi Test Message + +1. Vào Facebook Page của bạn +2. Click **"Send Message"** (với tư cách user, không phải admin) +3. Gõ: `Hello` +4. Kiểm tra logs của service cho incoming webhook event + +**Log mong đợi**: +``` +[INFO] Received message event from user: 1234567890 +[INFO] Processing incoming message: "Hello" +[INFO] Sending response via Facebook Messenger +``` + +### 4.2 Test Nút Get Started + +Cấu hình nút Get Started để trigger welcome flow. + +**Lệnh cURL**: +```bash +curl -X POST "https://graph.facebook.com/v21.0/me/messenger_profile" \ + -H "Content-Type: application/json" \ + -d '{ + "get_started": { + "payload": "GET_STARTED" + } + }' \ + -d "access_token=YOUR_PAGE_ACCESS_TOKEN" +``` + +--- + +## Tổng Hợp Biến Môi Trường + +```bash +# Cấu hình Facebook App +FACEBOOK_APP_ID="123456789012345" +FACEBOOK_APP_SECRET="abcdef1234567890abcdef1234567890" +FACEBOOK_VERIFY_TOKEN="my_verify_token_12345" + +# Facebook Page Access Token +FACEBOOK_PAGE_ACCESS_TOKEN="EAAxxxxxxxxxx..." + +# Facebook API Version +FACEBOOK_API_VERSION="v21.0" +``` + +--- + +## Xử Lý Sự Cố + +### Issue 1: Webhook Verification Failed + +**Vấn đề**: +``` +The URL couldn't be validated. Callback verification failed... +``` + +**Giải pháp**: +1. Kiểm tra service đang chạy và accessible qua HTTPS +2. Verify `FACEBOOK_VERIFY_TOKEN` match exactly +3. Check logs cho webhook GET request +4. Đảm bảo returning hub.challenge as plain text (không phải JSON) + +### Issue 2: Không Nhận Được Messages + +**Vấn đề**: Service không nhận được messages + +**Giải pháp**: +1. Verify webhook subscriptions active (messages, messaging_postbacks) +2. Check Page Access Token chưa expire +3. Test webhook manually với curl + +### Issue 3: Không Gửi Được Messages + +**Vấn đề**: +``` +(#200) This user can't receive messages from this bot +``` + +**Giải pháp**: +- User phải gửi message trước (GDPR compliance) +- Hoặc user phải opt-in qua checkbox plugin +- Page Access Token phải có `pages_messaging` permission + +--- + +## Danh Sách Kiểm Tra + +- [ ] Facebook App đã tạo và Messenger product đã thêm +- [ ] Page Access Token đã generate và save +- [ ] Webhook đã configure và verified +- [ ] Webhook events đã subscribe (messages, postbacks) +- [ ] Test message đã gửi và nhận thành công +- [ ] Bot response đã gửi lại cho user +- [ ] Nút Get Started triggers welcome flow +- [ ] Signature verification hoạt động + +--- + +## Tài Nguyên + +### Tài Liệu Chính Thức Facebook +- [Tổng Quan Messenger Platform](https://developers.facebook.com/docs/messenger-platform/) +- [Webhooks Reference](https://developers.facebook.com/docs/messenger-platform/webhooks) +- [Send API Reference](https://developers.facebook.com/docs/messenger-platform/send-messages) + +### Công Cụ +- [Facebook Graph API Explorer](https://developers.facebook.com/tools/explorer/) +- [ngrok](https://ngrok.com/) - Local HTTPS tunnel + +### Tài Liệu Nội Bộ +- [Tài Liệu API](./API.md) +- [Tài Liệu Kiến Trúc](./ARCHITECTURE.md) +- [Cấu Hình AI Chatbot](./AI_CHATBOT.md) diff --git a/services/mkt-x-service-net/docs/README.md b/services/mkt-x-service-net/docs/README.md index 05fa9147..82d36aa5 100644 --- a/services/mkt-x-service-net/docs/README.md +++ b/services/mkt-x-service-net/docs/README.md @@ -1,118 +1,125 @@ -# MKT X/Twitter Service - Documentation Index +# MKT X/Twitter Service - Documentation ## Tài Liệu Dịch Vụ Twitter Marketing --- ## English Documentation -### Core Documentation - | Document | Description | Link | |----------|-------------|------| -| **README** | Service overview, quick start, features | [README.md](../README.md) | -| **Architecture** | System architecture, domain model, components | [ARCHITECTURE.md](ARCHITECTURE.md) | -| **API Reference** | Complete API documentation with examples | [API.md](API.md) | -| **Twitter Setup** | Step-by-step Twitter API integration guide | [TWITTER_SETUP.md](TWITTER_SETUP.md) | - -### Quick Links - -- **Getting Started**: [README.md#quick-start](../README.md#quick-start) -- **API Endpoints**: [API.md#api-overview](API.md#api-overview) -- **Domain Model**: [ARCHITECTURE.md#domain-model](ARCHITECTURE.md#domain-model) -- **Deployment**: [README.md#development](../README.md#development) +| **README** | Service overview, quick start, features | [../README.md](../README.md) | +| **Architecture** | System architecture, domain model, components | [en/ARCHITECTURE.md](en/ARCHITECTURE.md) | +| **API Reference** | Complete API documentation with examples | [en/API.md](en/API.md) | +| **Twitter Setup** | Step-by-step Twitter API integration guide | [en/TWITTER_SETUP.md](en/TWITTER_SETUP.md) | --- -## Vietnamese Documentation / Tài Liệu Tiếng Việt - -> **Note**: Vietnamese documentation provides the same content in Vietnamese language for easier understanding by Vietnamese-speaking developers. -> -> **Lưu ý**: Tài liệu tiếng Việt cung cấp nội dung tương tự bằng tiếng Việt để dễ hiểu hơn cho lập trình viên Việt Nam. - -### Tài Liệu Chính +## Tài Liệu Tiếng Việt | Tài Liệu | Mô Tả | Link | |----------|-------|------| -| **README** | Tổng quan dịch vụ, hướng dẫn nhanh | [README-vi.md](vi/README.md) (Coming soon) | -| **Kiến Trúc** | Kiến trúc hệ thống, mô hình domain | [KIEN-TRUC.md](vi/KIEN-TRUC.md) (Coming soon) | -| **Tài Liệu API** | Tài liệu API đầy đủ với ví dụ | [API.md](vi/API.md) (Coming soon) | -| **Thiết Lập Twitter** | Hướng dẫn tích hợp Twitter API | [CAI-DAT-TWITTER.md](vi/CAI-DAT-TWITTER.md) (Coming soon) | +| **README** | Tổng quan dịch vụ, hướng dẫn nhanh | [../README.md](../README.md) | +| **Kiến Trúc** | Kiến trúc hệ thống, mô hình domain | [vi/KIEN-TRUC.md](vi/KIEN-TRUC.md) | +| **Thiết Lập Twitter** | Hướng dẫn tích hợp Twitter API | [vi/CAI-DAT-TWITTER.md](vi/CAI-DAT-TWITTER.md) | --- -## Documentation Structure / Cấu Trúc Tài Liệu +## Quick Links / Liên Kết Nhanh -``` -docs/ -├── README.md # This file / File này -├── ARCHITECTURE.md # EN: System architecture -├── API.md # EN: API reference -├── TWITTER_SETUP.md # EN: Twitter setup guide -└── vi/ # Vietnamese documentation - ├── README.md # VI: Service overview (planned) - ├── KIEN-TRUC.md # VI: Architecture (planned) - ├── API.md # VI: API documentation (planned) - └── CAI-DAT-TWITTER.md # VI: Twitter setup (planned) -``` +### Getting Started / Bắt Đầu +- [Quick Start Guide](../README.md#quick-start) / [Hướng Dẫn Nhanh](../README.md#bắt-đầu-nhanh) +- [Prerequisites](../README.md#prerequisites) / [Yêu Cầu](../README.md#yêu-cầu) + +### Architecture / Kiến Trúc +- [System Overview](en/ARCHITECTURE.md#system-architecture) / [Tổng Quan Hệ Thống](vi/KIEN-TRUC.md#kiến-trúc-hệ-thống) +- [Domain Model](en/ARCHITECTURE.md#domain-model) / [Mô Hình Domain](vi/KIEN-TRUC.md#mô-hình-domain) +- [Integration](en/ARCHITECTURE.md#integration-architecture) / [Tích Hợp](vi/KIEN-TRUC.md#kiến-trúc-tích-hợp) + +### API / API +- [API Overview](en/API.md#api-overview) +- [Account Management](en/API.md#account-management) / [Quản Lý Tài Khoản](en/API.md#account-management) +- [Contacts](en/API.md#contact-management) / [Liên Hệ](en/API.md#contact-management) +- [Campaigns](en/API.md#campaign-management) / [Chiến Dịch](en/API.md#campaign-management) + +### Setup / Thiết Lập +- [Twitter API Setup](en/TWITTER_SETUP.md) / [Thiết Lập Twitter API](vi/CAI-DAT-TWITTER.md) +- [Configuration](../README.md#configuration) / [Cấu Hình](../README.md#cấu-hình) +- [Deployment](../README.md#development) / [Triển Khai](../README.md#phát-triển) --- -## Key Concepts / Khái Niệm Chính +## Service Features / Tính Năng Dịch Vụ -### Service Features / Tính Năng Dịch Vụ +### 1. CHATBOT Automation Messenger -1. **CHATBOT Automation Messenger** / **CHATBOT Tự Động** - - Template-based messaging / Tin nhắn dựa trên mẫu - - Visual workflow designer / Thiết kế workflow trực quan - - Campaign scheduling / Lịch trình chiến dịch - - A/B testing / Kiểm thử A/B +**EN**: Template-based messaging with workflows and scheduling +- Message template library +- Visual workflow designer +- Event-triggered messaging +- Campaign scheduling +- A/B testing -2. **CHATBOT AI Messenger** / **CHATBOT AI** - - Natural language understanding / Hiểu ngôn ngữ tự nhiên - - OpenAI integration / Tích hợp OpenAI - - Intent detection / Phát hiện ý định - - Auto escalation / Tự động chuyển tiếp +**VI**: Gửi tin nhắn dựa trên mẫu với workflows và lịch trình +- Thư viện mẫu tin nhắn +- Thiết kế workflow trực quan +- Tin nhắn kích hoạt sự kiện +- Lịch chiến dịch +- Kiểm thử A/B -3. **Customer Management** / **Quản Lý Khách Hàng** - - Contact database / Cơ sở dữ liệu liên hệ - - Segmentation / Phân khúc - - Analytics / Phân tích - - GDPR compliance / Tuân thủ GDPR +### 2. CHATBOT AI Messenger + +**EN**: AI-powered conversationsusing OpenAI +- Natural language understanding +- Intent detection +- Context-aware responses +- Auto escalation to human + +**VI**: Trò chuyện được hỗ trợ AI dùng OpenAI +- Hiểu ngôn ngữ tự nhiên +- Phát hiện ý định +- Phản hồi theo ngữ cảnh +- Tự động chuyển cho người + +### 3. Customer Management + +**EN**: Complete CRM for Twitter contacts +- Contact profiles +- Segmentation +- Engagement analytics +- GDPR compliance + +**VI**: CRM hoàn chỉnh cho liên hệ Twitter +- Hồ sơ liên hệ +- Phân khúc +- Phân tích tương tác +- Tuân thủ GDPR --- -## API Overview / Tổng Quan API - -### Base URL +## API Endpoints Overview ``` -Development: http://localhost:5000/api/v1/mkt-x -Production: https://api.goodgo.com/api/v1/mkt-x -``` +Base URL: /api/v1/mkt-x -### Main Endpoints / Endpoints Chính - -``` -# Account Management / Quản Lý Tài Khoản -POST /accounts # Connect Twitter account / Kết nối tài khoản -GET /accounts # List accounts / Danh sách tài khoản +# Accounts / Tài Khoản +POST /accounts # Connect Twitter / Kết nối Twitter +GET /accounts # List / Danh sách DELETE /accounts/{id} # Disconnect / Ngắt kết nối -# Contact Management / Quản Lý Liên Hệ -GET /contacts # List contacts / Danh sách liên hệ +# Contacts / Liên Hệ +GET /contacts # List / Danh sách POST /contacts/{id}/tags # Add tags / Thêm nhãn -# Conversations / Trò Chuyện -GET /conversations # List conversations / Danh sách hội thoại -POST /conversations/{id}/messages # Send message / Gửi tin nhắn +# Conversations / Hội Thoại +GET /conversations # List / Danh sách +POST /conversations/{id}/messages # Send / Gửi tin nhắn # Campaigns / Chiến Dịch -POST /campaigns # Create campaign / Tạo chiến dịch -POST /campaigns/{id}/start # Start campaign / Bắt đầu chiến dịch +POST /campaigns # Create / Tạo +POST /campaigns/{id}/start # Start / Bắt đầu # Automation / Tự Động Hóa POST /automation/flows # Create flow / Tạo workflow -POST /automation/flows/{id}/activate # Activate / Kích hoạt # AI Chatbot POST /ai/conversations/{id}/message # AI chat @@ -121,52 +128,35 @@ POST /ai/conversations/{id}/message # AI chat GET /analytics/overview # Dashboard / Bảng điều khiển ``` -**Full documentation**: See [API.md](API.md) / Xem [API.md](API.md) +**Full documentation** / **Tài liệu đầy đủ**: [en/API.md](en/API.md) --- -## Architecture Highlights / Điểm Nổi Bật Kiến Trúc +## Technology Stack / Công Nghệ -### Clean Architecture / Kiến Trúc Sạch - -``` -API Layer → Controllers, Swagger -Application Layer → CQRS (Commands, Queries), MediatR -Domain Layer → Aggregates, Entities, Domain Events -Infrastructure → EF Core, Repositories, External APIs -``` - -### 9 Aggregate Roots / 9 Aggregate Chính - -1. **TwitterAccount** - Twitter account management -2. **Contact** - Customer profiles -3. **Conversation** - Message threads -4. **Template** - Message templates -5. **Campaign** - Marketing campaigns -6. **Segment** - Customer segments -7. **AutomationFlow** - Workflows -8. **AIConversationSession** - AI context -9. **Attachment** - Media files - -**Details**: See [ARCHITECTURE.md](ARCHITECTURE.md) +| Component | Technology | Version | +|-----------|-----------|---------| +| **Runtime** | .NET | 8.0+ | +| **Framework** | ASP.NET Core | 8.0+ | +| **Database** | PostgreSQL | 15+ | +| **Cache** | Redis | 7+ | +| **Message Broker** | RabbitMQ | 3.12+ | +| **Mediator** | MediatR | 12+ | --- -## Getting Help / Nhận Trợ Giúp +## External Dependencies / Dependencies Bên Ngoài -### For Developers / Dành Cho Lập Trình Viên +### Required / Bắt Buộc +- **Twitter API v2** - Direct Messages, Account Activity API +- **PostgreSQL** - Primary database +- **Redis** - Caching +- **RabbitMQ** - Message broker -- **Quick Start**: Follow [README.md#quick-start](../README.md#quick-start) -- **API Questions**: See [API.md](API.md) -- **Architecture Questions**: See [ARCHITECTURE.md](ARCHITECTURE.md) -- **Twitter Integration**: See [TWITTER_SETUP.md](TWITTER_SETUP.md) - -### For Issues / Vấn Đề - -1. **Check Documentation**: Search this documentation first -2. **Check Logs**: `docker-compose logs -f mkt-x-service-net` -3. **Health Check**: `curl http://localhost:5000/health` -4. **Contact Support**: api-support@goodgo.com +### Optional / Tùy Chọn +- **OpenAI API** - AI chatbot features / Tính năng chatbot AI +- **Prometheus + Grafana** - Monitoring / Giám sát +- **Loki** - Logging / Ghi log --- @@ -175,7 +165,7 @@ Infrastructure → EF Core, Repositories, External APIs ### 1. Setup / Thiết Lập ```bash -# Install dependencies / Cài đặt dependencies +# Restore dependencies / Cài dependencies dotnet restore # Setup database / Thiết lập database @@ -195,7 +185,7 @@ dotnet test ### 3. Deployment / Triển Khai ```bash -# Build Docker image / Build image +# Docker build docker-compose build mkt-x-service-net # Deploy / Triển khai @@ -204,98 +194,24 @@ docker-compose up -d mkt-x-service-net --- -## External Dependencies / Dependencies Bên Ngoài +## Support / Hỗ Trợ -### Required / Bắt Buộc +### For Developers / Dành Cho Lập Trình Viên -- **Twitter API v2** - Direct Messages API, Account Activity API -- **PostgreSQL 15+** - Primary database -- **Redis 7+** - Caching layer -- **RabbitMQ 3.12+** - Message broker +- **Quick Start**: [README.md#quick-start](../README.md#quick-start) +- **API Questions**: [en/API.md](en/API.md) +- **Architecture**: [en/ARCHITECTURE.md](en/ARCHITECTURE.md) -### Optional / Tùy Chọn +### Issues / Vấn Đề -- **OpenAI API** - For AI chatbot features -- **Prometheus + Grafana** - For monitoring -- **Loki** - For centralized logging +1. Check documentation / Kiểm tra tài liệu +2. Check logs / Kiểm tra logs: `docker-compose logs -f mkt-x-service-net` +3. Health check: `curl http://localhost:5000/health` +4. Contact support / Liên hệ hỗ trợ: api-support@goodgo.com --- -## Performance Targets / Mục Tiêu Hiệu Năng - -| Metric / Chỉ Số | Target / Mục Tiêu | Notes / Ghi Chú | -|------------------|-------------------|-----------------| -| API Response (P95) | < 500ms | Excluding external APIs | -| Message Processing | 100 msg/sec | Per account | -| Campaign Throughput | 10,000/min | With rate limiting | -| Database Query (P95) | < 100ms | Single-table queries | -| Cache Hit Ratio | > 80% | Contact lookups | - ---- - -## Security Compliance / Tuân Thủ Bảo Mật - -- ✅ **OAuth 1.0a** for Twitter authentication -- ✅ **JWT tokens** for API authentication -- ✅ **Encrypted** OAuth tokens at rest (AES-256) -- ✅ **RBAC** for merchant isolation -- ✅ **GDPR** compliant (data retention, right to deletion) -- ✅ **Input validation** with FluentValidation -- ✅ **Rate limiting** on all endpoints - ---- - -## Technology Stack / Công Nghệ Sử Dụng - -### Backend - -- .NET 8.0, ASP.NET Core 8.0 -- MediatR 12+ (CQRS) -- Entity Framework Core 8.0 -- FluentValidation - -### Data / Dữ Liệu - -- PostgreSQL 15+ (primary database) -- Redis 7+ (caching) -- RabbitMQ 3.12+ (message broker) - -### Infrastructure / Hạ Tầng - -- Docker, Kubernetes -- Traefik (API Gateway) -- Prometheus + Grafana (monitoring) -- Loki (logging) - -### External APIs / APIs Bên Ngoài - -- Twitter API v2 - - OpenAI API (GPT-4) - ---- - -## Contributing / Đóng Góp - -See main project contributing guide: [CONTRIBUTING.md](../../docs/CONTRIBUTING.md) - ---- - -## License / Giấy Phép - -MIT License - ---- - -## Related Documentation / Tài Liệu Liên Quan - -### GoodGo Platform - -- [API Design Standards](../../.agent/skills/api-design/SKILL.md) -- [CQRS MediatR Patterns](../../.agent/skills/cqrs-mediatr/SKILL.md) -- [Domain-Driven Design](../../.agent/skills/domain-driven-design/SKILL.md) -- [Project Rules](../../.agent/skills/project-rules/SKILL.md) - -### Related Services / Dịch Vụ Liên Quan +## Related Services / Dịch Vụ Liên Quan - [MKT Facebook Service](../../mkt-facebook-service-net/docs) - [MKT Zalo Service](../../mkt-zalo-service-net/docs) @@ -303,14 +219,5 @@ MIT License --- -## Contact / Liên Hệ - -- **Technical Support**: api-support@goodgo.com -- **Documentation Issues**: GitHub Issues -- **Emergency Support**: support-priority@goodgo.com - ---- - **Last Updated**: 2026-01-18 -**Version**: 1.0 **Maintained by**: GoodGo Platform Team diff --git a/services/mkt-zalo-service-net/docs/en/API.md b/services/mkt-zalo-service-net/docs/en/API.md new file mode 100644 index 00000000..2b327718 --- /dev/null +++ b/services/mkt-zalo-service-net/docs/en/API.md @@ -0,0 +1,392 @@ +# API Reference - MKT Zalo Service + +Complete API documentation for all endpoints. + +## Base URL + +``` +http://localhost/api/v1/zalo +``` + +## Authentication + +- **Webhooks**: Signature verification (HMAC SHA256) +- **Customer APIs**: JWT Bearer token +- **Admin APIs**: JWT Bearer token + `admin` or `marketing_manager` role + +--- + +## Webhook APIs + +### POST /webhooks + +Receive events from Zalo Official Account. + +**Authentication**: Signature verification + +**Headers**: +``` +X-Zalo-Signature: +Content-Type: application/json +``` + +**Request Body**: +```json +{ + "app_id": "1234567890", + "event_name": "user_send_text", + "timestamp": 1705586400000, + "sender": { + "id": "1234567890123456789" + }, + "recipient": { + "id": "9876543210" + }, + "message": { + "text": "Xin chào, tôi cần hỗ trợ", + "msg_id": "msg_123456" + } +} +``` + +**Response**: `200 OK` + +--- + +## Conversation APIs + +### GET /conversations + +List conversations with pagination. + +**Authentication**: Required (JWT) + +**Query Parameters**: +- `skip` (int, default: 0) +- `take` (int, default: 20, max: 100) +- `status` (enum: active, closed, all) +- `zaloUserId` (string, optional) +- `fromDate` (datetime, optional) +- `toDate` (datetime, optional) + +**Response**: +```json +{ + "success": true, + "data": { + "conversations": [ + { + "id": "uuid", + "zaloUserId": "...", + "customerName": "Nguyễn Văn A", + "status": "active", + "lastMessage": "Cảm ơn bạn", + "lastMessageAt": "2026-01-18T10:30:00Z", + "messageCount": 15 + } + ], + "totalCount": 120 + }, + "pagination": { + "page": 1, + "limit": 20, + "total": 120, + "totalPages": 6 + } +} +``` + +### GET /conversations/{id} + +Get conversation details with message history. + +**Authentication**: Required (JWT) + +**Response**: +```json +{ + "success": true, + "data": { + "id": "uuid", + "zaloUserId": "...", + "customer": { + "id": "uuid", + "displayName": "Nguyễn Văn A", + "tags": ["vip", "interested-in-retail"] + }, + "messages": [ + { + "id": "msg-001", + "type": "text", + "content": "Xin chào", + "direction": "incoming", + "isFromBot": false, + "sentAt": "2026-01-18T10:00:00Z" + } + ] + } +} +``` + +### POST /conversations/{id}/messages + +Send a message in conversation. + +**Authentication**: Required (JWT) + +**Request Body**: +```json +{ + "type": "text", + "content": "Cảm ơn bạn đã liên hệ." +} +``` + +**Response**: `201 Created` + +### POST /conversations/{id}/close + +Close an active conversation. + +**Authentication**: Required (JWT) + +**Response**: `200 OK` + +--- + +## Customer APIs + +### GET /customers + +List Zalo customers. + +**Authentication**: Required (JWT) + +**Query Parameters**: +- `skip`, `take` (pagination) +- `search` (string): Search by name, phone, email +- `segment` (enum): new, regular, active, vip +- `tags` (string[]): Comma-separated tags + +**Response**: +```json +{ + "success": true, + "data": { + "customers": [ + { + "id": "uuid", + "zaloUserId": "...", + "displayName": "Nguyễn Văn A", + "segment": "vip", + "tags": ["vip", "high-value"], + "conversationCount": 12 + } + ] + } +} +``` + +### GET /customers/{id} + +Get customer details. + +**Authentication**: Required (JWT) + +### PUT /customers/{id} + +Update customer profile. + +**Authentication**: Required (JWT) + +**Request Body**: +```json +{ + "phoneNumber": "+84901234567", + "email": "customer@example.com", + "tags": ["vip", "interested-in-retail"] +} +``` + +--- + +## Chatbot Rule APIs + +### GET /chatbot-rules + +List automation rules. + +**Authentication**: Required (JWT) + +**Response**: +```json +{ + "success": true, + "data": { + "rules": [ + { + "id": "uuid", + "name": "Greeting Rule", + "type": "keyword", + "priority": 100, + "isActive": true, + "conditions": [ + { + "field": "message_text", + "operator": "contains", + "value": "xin chào" + } + ], + "action": { + "type": "send_text", + "responseText": "Xin chào! Tôi có thể giúp gì cho bạn?" + } + } + ] + } +} +``` + +### POST /chatbot-rules + +Create automation rule. + +**Authentication**: Required (JWT) + +### PUT /chatbot-rules/{id} + +Update rule. + +**Authentication**: Required (JWT) + +### DELETE /chatbot-rules/{id} + +Delete rule. + +**Authentication**: Required (JWT) + +**Response**: `204 No Content` + +--- + +## Template APIs + +### GET /templates + +List ZNS templates. + +**Authentication**: Required (JWT) + +### POST /templates/{id}/send + +Send template message. + +**Authentication**: Required (JWT) + +**Request Body**: +```json +{ + "zaloUserId": "...", + "parameters": { + "order_id": "ORD-12345", + "total_amount": "500000" + } +} +``` + +--- + +## Admin APIs + +### GET /admin/analytics + +Get analytics dashboard. + +**Authentication**: Required (JWT + Admin role) + +**Query Parameters**: +- `fromDate` (date, required) +- `toDate` (date, required) +- `groupBy` (enum): day, week, month + +**Response**: +```json +{ + "success": true, + "data": { + "metrics": { + "totalConversations": 1523, + "activeConversations": 234, + "automationRate": 0.72 + }, + "timeline": [ + { + "date": "2026-01-01", + "conversations": 45, + "messages": 567 + } + ] + } +} +``` + +### GET /admin/messages + +Search all messages (admin only). + +**Authentication**: Required (JWT + Admin role) + +### POST /admin/broadcast + +Broadcast message to segments. + +**Authentication**: Required (JWT + Admin role) + +**Request Body**: +```json +{ + "targetSegments": ["vip", "active"], + "message": { + "type": "text", + "content": "Khuyến mãi đặc biệt!" + }, + "scheduleAt": "2026-01-20T10:00:00Z" +} +``` + +--- + +## Error Responses + +### Standard Error Format + +```json +{ + "success": false, + "error": "Error message", + "details": { + "code": "ERROR_CODE", + "field": "fieldName" + } +} +``` + +### HTTP Status Codes + +| Code | Meaning | +|------|---------| +| 200 | OK | +| 201 | Created | +| 204 | No Content | +| 400 | Bad Request | +| 401 | Unauthorized | +| 403 | Forbidden | +| 404 | Not Found | +| 422 | Unprocessable Entity | +| 500 | Internal Server Error | + +--- + +**Total Endpoints**: 17 +**Document Version**: 1.0 +**Last Updated**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/en/DATABASE.md b/services/mkt-zalo-service-net/docs/en/DATABASE.md new file mode 100644 index 00000000..63dbae8b --- /dev/null +++ b/services/mkt-zalo-service-net/docs/en/DATABASE.md @@ -0,0 +1,214 @@ +# Database Schema - MKT Zalo Service + +Database design and schema documentation. + +## Tables Overview + +9 tables with relationships: +- `ZaloCustomers` - Customer profiles +- `CustomerTags` - Customer tags (many-to-many) +- `Conversations` - Chat sessions +- `Messages` - All messages +- `ChatbotRules` - Automation rules +- `RuleConditions` - Rule conditions +- `RuleActions` - Rule actions +- `MessageTemplates` - ZNS templates +- `TemplateParameters` - Template parameters + +--- + +## Core Tables + +### ZaloCustomers + +```sql +CREATE TABLE "ZaloCustomers" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ZaloUserId" VARCHAR(50) NOT NULL UNIQUE, + "DisplayName" VARCHAR(255) NOT NULL, + "AvatarUrl" VARCHAR(500), + "PhoneNumber" VARCHAR(20), + "Email" VARCHAR(255), + "Segment" INT NOT NULL DEFAULT 0, + "FirstInteractionAt" TIMESTAMPTZ NOT NULL, + "LastInteractionAt" TIMESTAMPTZ NOT NULL, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_ZaloCustomers_ZaloUserId" ON "ZaloCustomers" ("ZaloUserId"); +CREATE INDEX "IX_ZaloCustomers_Segment" ON "ZaloCustomers" ("Segment"); +``` + +### Conversations + +```sql +CREATE TABLE "Conversations" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "CustomerId" UUID NOT NULL REFERENCES "ZaloCustomers"("Id") ON DELETE CASCADE, + "ZaloUserId" VARCHAR(50) NOT NULL, + "Status" INT NOT NULL DEFAULT 0, + "StartedAt" TIMESTAMPTZ NOT NULL, + "EndedAt" TIMESTAMPTZ, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_Conversations_CustomerId" ON "Conversations" ("CustomerId"); +CREATE INDEX "IX_Conversations_Status_StartedAt" ON "Conversations" ("Status", "StartedAt" DESC); +``` + +### Messages + +```sql +CREATE TABLE "Messages" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ConversationId" UUID NOT NULL REFERENCES "Conversations"("Id") ON DELETE CASCADE, + "Type" INT NOT NULL, + "Content" TEXT NOT NULL, + "Direction" INT NOT NULL, + "IsFromBot" BOOLEAN NOT NULL DEFAULT FALSE, + "SentAt" TIMESTAMPTZ NOT NULL, + "ZaloMessageId" VARCHAR(100), + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_Messages_ConversationId_SentAt" ON "Messages" ("ConversationId", "SentAt" DESC); +CREATE INDEX "IX_Messages_Content_FullText" ON "Messages" USING GIN (to_tsvector('english', "Content")); +``` + +--- + +## Automation Tables + +### ChatbotRules + +```sql +CREATE TABLE "ChatbotRules" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "Name" VARCHAR(255) NOT NULL, + "Type" INT NOT NULL, + "Priority" INT NOT NULL DEFAULT 50, + "IsActive" BOOLEAN NOT NULL DEFAULT TRUE, + "MatchCount" INT NOT NULL DEFAULT 0, + "LastMatchedAt" TIMESTAMPTZ, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_ChatbotRules_IsActive_Priority" ON "ChatbotRules" ("IsActive", "Priority" DESC); +``` + +### RuleConditions + +```sql +CREATE TABLE "RuleConditions" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "RuleId" UUID NOT NULL REFERENCES "ChatbotRules"("Id") ON DELETE CASCADE, + "Field" VARCHAR(50) NOT NULL, + "Operator" VARCHAR(50) NOT NULL, + "Value" TEXT NOT NULL, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_RuleConditions_RuleId" ON "RuleConditions" ("RuleId"); +``` + +--- + +## Template Tables + +### MessageTemplates + +```sql +CREATE TABLE "MessageTemplates" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ZaloTemplateId" VARCHAR(50) NOT NULL UNIQUE, + "Name" VARCHAR(255) NOT NULL, + "Content" TEXT NOT NULL, + "Status" INT NOT NULL DEFAULT 0, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_MessageTemplates_ZaloTemplateId" ON "MessageTemplates" ("ZaloTemplateId"); +``` + +--- + +## Relationships + +``` +ZaloCustomers (1) ──── (M) Conversations +Conversations (1) ──── (M) Messages +ZaloCustomers (1) ──── (M) CustomerTags +ChatbotRules (1) ──── (M) RuleConditions +ChatbotRules (1) ──── (1) RuleActions +MessageTemplates (1) ──── (M) TemplateParameters +``` + +--- + +## Indexes + +### Performance-Critical Indexes + +| Table | Index | Purpose | +|-------|-------|---------| +| ZaloCustomers | IX_ZaloCustomers_ZaloUserId | Fast lookup by Zalo user ID | +| Conversations | IX_Conversations_Status_StartedAt | List active/recent conversations | +| Messages | IX_Messages_ConversationId_SentAt | Get conversation history | +| Messages | IX_Messages_Content_FullText | Full-text search | +| ChatbotRules | IX_ChatbotRules_IsActive_Priority | Fast rule evaluation | + +--- + +## Migrations + +### Initial Migration + +```bash +dotnet ef migrations add InitialSchema \ + --project src/MktZaloService.Infrastructure + +dotnet ef database update \ + --project src/MktZaloService.Infrastructure +``` + +### Seed Data + +```sql +INSERT INTO "ChatbotRules" ("Id", "Name", "Type", "Priority", "IsActive") +VALUES + (gen_random_uuid(), 'Greeting Rule', 0, 100, TRUE), + (gen_random_uuid(), 'Pricing Inquiry', 0, 90, TRUE), + (gen_random_uuid(), 'Store Hours', 0, 90, TRUE); +``` + +--- + +## Data Volume Estimates + +| Table | 6 Months | Growth Rate | +|-------|----------|-------------| +| ZaloCustomers | 10,000 | +500/day | +| Conversations | 50,000 | +2,000/day | +| Messages | 750,000 | +30,000/day | + +**Total DB Size**: ~2GB (6 months) + +--- + +## Backup Strategy + +```bash +# Daily automated backups +- Full backup: Daily at 02:00 UTC +- Retention: 30 days +- Point-in-time recovery: Last 7 days +``` + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/en/DOMAIN_MODELS.md b/services/mkt-zalo-service-net/docs/en/DOMAIN_MODELS.md new file mode 100644 index 00000000..d96a244b --- /dev/null +++ b/services/mkt-zalo-service-net/docs/en/DOMAIN_MODELS.md @@ -0,0 +1,251 @@ +# Domain Models - MKT Zalo Service + +Domain-Driven Design implementation for Zalo marketing service. + +## Aggregates + +### 1. Conversation Aggregate + +Manages conversation lifecycle and messages. + +```csharp +public class Conversation : Entity, IAggregateRoot +{ + private readonly List _messages = new(); + + public string ZaloUserId { get; private set; } + public Guid CustomerId { get; private set; } + public ConversationStatus Status { get; private set; } + public DateTime StartedAt { get; private set; } + public DateTime? EndedAt { get; private set; } + public IReadOnlyCollection Messages => _messages.AsReadOnly(); + + public void AddMessage(MessageType type, string content, + MessageDirection direction, bool isFromBot) + { + if (Status == ConversationStatus.Closed) + throw new ZaloDomainException( + "Cannot add message to closed conversation"); + + var message = new Message(type, content, direction, isFromBot); + _messages.Add(message); + + AddDomainEvent(new MessageReceivedDomainEvent(Id, message.Id, content)); + } + + public void Close() + { + if (Status == ConversationStatus.Closed) return; + + Status = ConversationStatus.Closed; + EndedAt = DateTime.UtcNow; + + AddDomainEvent(new ConversationClosedDomainEvent(Id)); + } +} +``` + +**Business Rules**: +- Messages cannot be added to closed conversations +- Conversation status must be Active or Closed +- Messages are immutable once created + +--- + +### 2. ZaloCustomer Aggregate + +Manages customer profiles and segmentation. + +```csharp +public class ZaloCustomer : Entity, IAggregateRoot +{ + private readonly List _tags = new(); + + public string ZaloUserId { get; private set; } + public CustomerProfile Profile { get; private set; } + public CustomerSegment Segment { get; private set; } + public DateTime FirstInteractionAt { get; private set; } + public DateTime LastInteractionAt { get; private set; } + public IReadOnlyCollection Tags => _tags.AsReadOnly(); + + public void UpdateProfile(string displayName, string avatarUrl, + string? phoneNumber = null, string? email = null) + { + if (string.IsNullOrWhiteSpace(displayName)) + throw new ArgumentException("Display name cannot be empty"); + + Profile = new CustomerProfile(displayName, avatarUrl, phoneNumber, email); + AddDomainEvent(new CustomerProfileUpdatedDomainEvent(Id, ZaloUserId)); + } + + public void AddTag(string tagName) + { + if (_tags.Any(t => t.Name.Equals(tagName, + StringComparison.OrdinalIgnoreCase))) + return; + + _tags.Add(new Tag(tagName)); + } + + public void UpdateSegment(int conversationCount, int messageCount) + { + Segment = (conversationCount, messageCount) switch + { + (> 50, > 500) => CustomerSegment.VIP, + (> 20, > 200) => CustomerSegment.Active, + (> 5, > 50) => CustomerSegment.Regular, + _ => CustomerSegment.New + }; + } +} +``` + +**Business Rules**: +- ZaloUserId must be unique +- DisplayName is required +- Auto-segmentation based on engagement metrics +- No duplicate tags per customer + +--- + +### 3. ChatbotRule Aggregate + +Defines automation rules for chatbot responses. + +```csharp +public class ChatbotRule : Entity, IAggregateRoot +{ + private readonly List _conditions = new(); + + public string Name { get; private set; } + public RuleType Type { get; private set; } + public IReadOnlyCollection Conditions => _conditions.AsReadOnly(); + public RuleAction Action { get; private set; } + public int Priority { get; private set; } + public bool IsActive { get; private set; } + + public bool Evaluate(string userMessage) + { + if (!IsActive) return false; + + return Type switch + { + RuleType.Keyword => _conditions.Any(c => + userMessage.Contains(c.Value, StringComparison.OrdinalIgnoreCase)), + RuleType.Regex => _conditions.Any(c => + Regex.IsMatch(userMessage, c.Value)), + _ => false + }; + } + + public void RecordMatch() + { + MatchCount++; + LastMatchedAt = DateTime.UtcNow; + } +} +``` + +**Business Rules**: +- Rules must have at least one condition +- Priority must be 0-100 +- Higher priority rules evaluated first +- Inactive rules are skipped + +--- + +### 4. MessageTemplate Aggregate + +Manages ZNS templates. + +```csharp +public class MessageTemplate : Entity, IAggregateRoot +{ + private readonly List _parameters = new(); + + public string ZaloTemplateId { get; private set; } + public string Name { get; private set; } + public string Content { get; private set; } + public TemplateStatus Status { get; private set; } + public IReadOnlyCollection Parameters => _parameters.AsReadOnly(); + + public Dictionary ValidateParameters( + Dictionary providedParams) + { + var missingParams = _parameters + .Where(p => p.IsRequired && !providedParams.ContainsKey(p.Name)) + .Select(p => p.Name) + .ToList(); + + if (missingParams.Any()) + throw new ZaloDomainException( + $"Missing required parameters: {string.Join(", ", missingParams)}"); + + return providedParams; + } +} +``` + +--- + +## Value Objects + +### CustomerProfile + +```csharp +public class CustomerProfile : ValueObject +{ + public string DisplayName { get; } + public string AvatarUrl { get; } + public string? PhoneNumber { get; } + public string? Email { get; } + + protected override IEnumerable GetEqualityComponents() + { + yield return DisplayName; + yield return AvatarUrl; + yield return PhoneNumber; + yield return Email; + } +} +``` + +### RuleCondition + +```csharp +public class RuleCondition : ValueObject +{ + public string Field { get; } + public string Operator { get; } + public string Value { get; } +} +``` + +### RuleAction + +```csharp +public class RuleAction : ValueObject +{ + public ActionType ActionType { get; } + public string? ResponseText { get; } + public Guid? TemplateId { get; } +} +``` + +--- + +## Enumerations + +```csharp +public enum ConversationStatus { Active = 0, Closed = 1 } +public enum MessageType { Text = 0, Image = 1, Link = 2, Sticker = 3, Audio = 4 } +public enum MessageDirection { Incoming = 0, Outgoing = 1 } +public enum CustomerSegment { New = 0, Regular = 1, Active = 2, VIP = 3 } +public enum RuleType { Keyword = 0, Regex = 1, Intent = 2 } +public enum ActionType { SendText = 0, SendTemplate = 1, ForwardToHuman = 2 } +``` + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/en/ZALO_SETUP.md b/services/mkt-zalo-service-net/docs/en/ZALO_SETUP.md new file mode 100644 index 00000000..aaa87085 --- /dev/null +++ b/services/mkt-zalo-service-net/docs/en/ZALO_SETUP.md @@ -0,0 +1,302 @@ +# Zalo Official Account Integration Guide + +Complete guide for integrating with Zalo Official Account API. + +## Prerequisites + +### 1. Zalo Official Account + +Register and verify your business account at https://oa.zalo.me/ + +**Requirements**: +- Business license +- Vietnamese business registration +- 3-5 business days for verification + +### 2. Zalo Application + +Create application at https://developers.zalo.me/ + +1. Create new application +2. Link to your Official Account +3. Note `App ID` and `Secret Key` + +### 3. Access Token + +Get OA Access Token (1-year validity): + +**Via Portal** (Development): +1. Go to https://developers.zalo.me/tools/explorer +2. Select your application and OA +3. Click "Get Access Token" +4. Copy the token + +**Via API** (Production): +```http +POST https://oauth.zaloapp.com/v4/oa/access_token +Content-Type: application/x-www-form-urlencoded + +app_id={app_id}&secret_key={secret_key}&code={authorization_code} +``` + +--- + +## Webhook Configuration + +### 1. Set Webhook URL + +In Zalo Developers portal: +1. Navigate to "Webhooks" tab +2. Set URL: `https://yourdomain.com/api/v1/zalo/webhooks` +3. Set secret key for signature verification + +### 2. Enable Events + +Required events: +- ✅ `user_send_text` - Text messages +- ✅ `user_send_image` - Image messages +- ✅ `follow` - New followers +- ✅ `unfollow` - Unfollows + +### 3. Signature Verification + +```csharp +public class ZaloWebhookVerifier +{ + private readonly string _secretKey; + + public bool VerifySignature(string body, string signature) + { + using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_secretKey)); + var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body)); + var computed = BitConverter.ToString(hash).Replace("-", "").ToLower(); + + return signature.Equals(computed, StringComparison.OrdinalIgnoreCase); + } +} +``` + +**Headers**: +``` +X-Zalo-Signature: +``` + +### 4. Response Requirements + +**CRITICAL**: Must return `200 OK` within 2 seconds + +```csharp +[HttpPost("webhooks")] +public async Task ReceiveWebhook([FromBody] ZaloWebhookEvent webhook) +{ + // 1. Verify signature (FAST) + if (!_verifier.VerifySignature(RequestBody, Request.Headers["X-Zalo-Signature"])) + return Unauthorized(); + + // 2. Queue for async processing (FAST) + await _messageQueue.PublishAsync(webhook); + + // 3. Return immediately (< 2s) + return Ok(new { success = true }); +} +``` + +--- + +## Zalo API Endpoints + +### Base URL +``` +https://openapi.zalo.me +``` + +### 1. Send Text Message + +```http +POST /v3.0/oa/message/cs +Authorization: access_token {your_access_token} +Content-Type: application/json + +{ + "recipient": { + "user_id": "1234567890123456789" + }, + "message": { + "text": "Xin chào! Tôi có thể giúp gì cho bạn?" + } +} +``` + +**Response**: +```json +{ + "error": 0, + "message": "Success", + "data": { + "message_id": "zalo_msg_xyz123" + } +} +``` + +### 2. Get User Profile + +```http +GET /v3.0/oa/user/detail?data={"user_id":"1234567890123456789"} +Authorization: access_token {your_access_token} +``` + +**Response**: +```json +{ + "error": 0, + "data": { + "user_id": "1234567890123456789", + "display_name": "Nguyễn Văn A", + "avatar": "https://s.zalo.me/avatar.jpg", + "user_gender": 1 + } +} +``` + +### 3. Send ZNS Template + +```http +POST /v3.0/oa/message/template +Authorization: access_token {your_access_token} + +{ + "phone": "+84901234567", + "template_id": "283746", + "template_data": { + "order_id": "ORD-12345", + "total_amount": "500000" + } +} +``` + +--- + +## Event Types + +### user_send_text + +```json +{ + "event_name": "user_send_text", + "sender": { "id": "1234567890123456789" }, + "message": { + "text": "Xin chào", + "msg_id": "msg_123456" + } +} +``` + +### user_send_image + +```json +{ + "event_name": "user_send_image", + "message": { + "attachments": [{ + "type": "image", + "payload": { + "url": "https://zalo-api.zadn.vn/...", + "thumbnail": "https://..." + } + }] + } +} +``` + +### follow + +```json +{ + "event_name": "follow", + "follower": { "id": "1234567890123456789" } +} +``` + +--- + +## Rate Limits + +| API | Limit | +|-----|-------| +| Send Message (CS) | 1000 requests/day per user | +| Send ZNS | 10000 requests/day | +| Get User Profile | 5000 requests/day | + +--- + +## Error Handling + +### Common Error Codes + +| Code | Message | Resolution | +|------|---------|------------| +| -201 | Invalid access token | Refresh token | +| -213 | User blocked OA | Mark customer inactive | +| -214 | User inactive 7+ days | Use ZNS instead | +| -300 | Rate limit exceeded | Wait and retry | + +### Retry Strategy + +```csharp +var retryPolicy = Policy + .Handle() + .OrResult(r => (int)r.StatusCode >= 500) + .WaitAndRetryAsync(3, retryAttempt => + TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); +``` + +--- + +## Best Practices + +### Webhook Processing + +✅ **DO**: +- Verify signature on every webhook +- Return 200 within 2 seconds +- Process asynchronously via queue +- Store raw events for 7 days + +❌ **DON'T**: +- Make database queries in handler +- Call external APIs synchronously +- Process LLM in webhook handler + +### Message Quality + +✅ **DO**: +- Keep messages under 2000 characters +- Use ZNS for important notifications +- Respect OA block status + +❌ **DON'T**: +- Send spam +- Send marketing without consent + +--- + +## Testing + +### Manual Testing + +```bash +# Send test webhook +curl -X POST http://localhost:8080/api/v1/zalo/webhooks \ + -H "Content-Type: application/json" \ + -H "X-Zalo-Signature: " \ + -d '{ + "event_name": "user_send_text", + "sender": {"id": "test_user"}, + "message": {"text": "hello"} + }' +``` + +--- + +**Document Version**: 1.0 +**Last Updated**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/vi/API.md b/services/mkt-zalo-service-net/docs/vi/API.md new file mode 100644 index 00000000..7c90130e --- /dev/null +++ b/services/mkt-zalo-service-net/docs/vi/API.md @@ -0,0 +1,392 @@ +# Tài Liệu API - MKT Zalo Service + +Tài liệu API đầy đủ cho tất cả endpoints. + +## Base URL + +``` +http://localhost/api/v1/zalo +``` + +## Xác Thực + +- **Webhooks**: Xác thực chữ ký (HMAC SHA256) +- **Customer APIs**: JWT Bearer token +- **Admin APIs**: JWT Bearer token + role `admin` hoặc `marketing_manager` + +--- + +## Webhook APIs + +### POST /webhooks + +Nhận events từ Zalo Official Account. + +**Xác thực**: Xác thực chữ ký + +**Headers**: +``` +X-Zalo-Signature: +Content-Type: application/json +``` + +**Request Body**: +```json +{ + "app_id": "1234567890", + "event_name": "user_send_text", + "timestamp": 1705586400000, + "sender": { + "id": "1234567890123456789" + }, + "recipient": { + "id": "9876543210" + }, + "message": { + "text": "Xin chào, tôi cần hỗ trợ", + "msg_id": "msg_123456" + } +} +``` + +**Response**: `200 OK` + +--- + +## Conversation APIs + +### GET /conversations + +Danh sách hội thoại với phân trang. + +**Xác thực**: Bắt buộc (JWT) + +**Query Parameters**: +- `skip` (int, mặc định: 0) +- `take` (int, mặc định: 20, tối đa: 100) +- `status` (enum: active, closed, all) +- `zaloUserId` (string, tùy chọn) +- `fromDate` (datetime, tùy chọn) +- `toDate` (datetime, tùy chọn) + +**Response**: +```json +{ + "success": true, + "data": { + "conversations": [ + { + "id": "uuid", + "zaloUserId": "...", + "customerName": "Nguyễn Văn A", + "status": "active", + "lastMessage": "Cảm ơn bạn", + "lastMessageAt": "2026-01-18T10:30:00Z", + "messageCount": 15 + } + ], + "totalCount": 120 + }, + "pagination": { + "page": 1, + "limit": 20, + "total": 120, + "totalPages": 6 + } +} +``` + +### GET /conversations/{id} + +Lấy chi tiết hội thoại với lịch sử tin nhắn. + +**Xác thực**: Bắt buộc (JWT) + +**Response**: +```json +{ + "success": true, + "data": { + "id": "uuid", + "zaloUserId": "...", + "customer": { + "id": "uuid", + "displayName": "Nguyễn Văn A", + "tags": ["vip", "interested-in-retail"] + }, + "messages": [ + { + "id": "msg-001", + "type": "text", + "content": "Xin chào", + "direction": "incoming", + "isFromBot": false, + "sentAt": "2026-01-18T10:00:00Z" + } + ] + } +} +``` + +### POST /conversations/{id}/messages + +Gửi tin nhắn trong hội thoại. + +**Xác thực**: Bắt buộc (JWT) + +**Request Body**: +```json +{ + "type": "text", + "content": "Cảm ơn bạn đã liên hệ." +} +``` + +**Response**: `201 Created` + +### POST /conversations/{id}/close + +Đóng hội thoại đang hoạt động. + +**Xác thực**: Bắt buộc (JWT) + +**Response**: `200 OK` + +--- + +## Customer APIs + +### GET /customers + +Danh sách khách hàng Zalo. + +**Xác thực**: Bắt buộc (JWT) + +**Query Parameters**: +- `skip`, `take` (phân trang) +- `search` (string): Tìm theo tên, số điện thoại, email +- `segment` (enum): new, regular, active, vip +- `tags` (string[]): Tags cách nhau bởi dấu phẩy + +**Response**: +```json +{ + "success": true, + "data": { + "customers": [ + { + "id": "uuid", + "zaloUserId": "...", + "displayName": "Nguyễn Văn A", + "segment": "vip", + "tags": ["vip", "high-value"], + "conversationCount": 12 + } + ] + } +} +``` + +### GET /customers/{id} + +Lấy chi tiết khách hàng. + +**Xác thực**: Bắt buộc (JWT) + +### PUT /customers/{id} + +Cập nhật hồ sơ khách hàng. + +**Xác thực**: Bắt buộc (JWT) + +**Request Body**: +```json +{ + "phoneNumber": "+84901234567", + "email": "customer@example.com", + "tags": ["vip", "interested-in-retail"] +} +``` + +--- + +## Chatbot Rule APIs + +### GET /chatbot-rules + +Danh sách quy tắc tự động hóa. + +**Xác thực**: Bắt buộc (JWT) + +**Response**: +```json +{ + "success": true, + "data": { + "rules": [ + { + "id": "uuid", + "name": "Quy tắc chào hỏi", + "type": "keyword", + "priority": 100, + "isActive": true, + "conditions": [ + { + "field": "message_text", + "operator": "contains", + "value": "xin chào" + } + ], + "action": { + "type": "send_text", + "responseText": "Xin chào! Tôi có thể giúp gì cho bạn?" + } + } + ] + } +} +``` + +### POST /chatbot-rules + +Tạo quy tắc tự động hóa. + +**Xác thực**: Bắt buộc (JWT) + +### PUT /chatbot-rules/{id} + +Cập nhật quy tắc. + +**Xác thực**: Bắt buộc (JWT) + +### DELETE /chatbot-rules/{id} + +Xóa quy tắc. + +**Xác thực**: Bắt buộc (JWT) + +**Response**: `204 No Content` + +--- + +## Template APIs + +### GET /templates + +Danh sách ZNS templates. + +**Xác thực**: Bắt buộc (JWT) + +### POST /templates/{id}/send + +Gửi tin nhắn template. + +**Xác thực**: Bắt buộc (JWT) + +**Request Body**: +```json +{ + "zaloUserId": "...", + "parameters": { + "order_id": "ORD-12345", + "total_amount": "500000" + } +} +``` + +--- + +## Admin APIs + +### GET /admin/analytics + +Lấy analytics dashboard. + +**Xác thực**: Bắt buộc (JWT + Admin role) + +**Query Parameters**: +- `fromDate` (date, bắt buộc) +- `toDate` (date, bắt buộc) +- `groupBy` (enum): day, week, month + +**Response**: +```json +{ + "success": true, + "data": { + "metrics": { + "totalConversations": 1523, + "activeConversations": 234, + "automationRate": 0.72 + }, + "timeline": [ + { + "date": "2026-01-01", + "conversations": 45, + "messages": 567 + } + ] + } +} +``` + +### GET /admin/messages + +Tìm kiếm tất cả tin nhắn (chỉ admin). + +**Xác thực**: Bắt buộc (JWT + Admin role) + +### POST /admin/broadcast + +Broadcast tin nhắn đến các segments. + +**Xác thực**: Bắt buộc (JWT + Admin role) + +**Request Body**: +```json +{ + "targetSegments": ["vip", "active"], + "message": { + "type": "text", + "content": "Khuyến mãi đặc biệt!" + }, + "scheduleAt": "2026-01-20T10:00:00Z" +} +``` + +--- + +## Error Responses + +### Định Dạng Lỗi Chuẩn + +```json +{ + "success": false, + "error": "Thông báo lỗi", + "details": { + "code": "ERROR_CODE", + "field": "fieldName" + } +} +``` + +### Mã HTTP Status Codes + +| Mã | Ý Nghĩa | +|----|---------| +| 200 | OK | +| 201 | Created | +| 204 | No Content | +| 400 | Bad Request | +| 401 | Unauthorized | +| 403 | Forbidden | +| 404 | Not Found | +| 422 | Unprocessable Entity | +| 500 | Internal Server Error | + +--- + +**Tổng Số Endpoints**: 17 +**Phiên Bản Tài Liệu**: 1.0 +**Cập Nhật Lần Cuối**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/vi/ARCHITECTURE.md b/services/mkt-zalo-service-net/docs/vi/ARCHITECTURE.md new file mode 100644 index 00000000..2ce6ae06 --- /dev/null +++ b/services/mkt-zalo-service-net/docs/vi/ARCHITECTURE.md @@ -0,0 +1,437 @@ +# MKT Zalo Service - Kiến Trúc Giải Pháp + +## Mục Lục + +1. [Ngữ Cảnh Nghiệp Vụ](#ngữ-cảnh-nghiệp-vụ) +2. [Kiến Trúc Hệ Thống](#kiến-trúc-hệ-thống) +3. [Mô Hình Domain](#mô-hình-domain) +4. [CQRS Commands & Queries](#cqrs-commands--queries) +5. [Patterns Tích Hợp](#patterns-tích-hợp) +6. [Luồng Dữ Liệu](#luồng-dữ-liệu) +7. [Bảo Mật & Xác Thực](#bảo-mật--xác-thực) +8. [Hiệu Năng & Mở Rộng](#hiệu-năng--mở-rộng) +9. [Giám Sát & Observability](#giám-sát--observability) + +--- + +## Ngữ Cảnh Nghiệp Vụ + +### Vấn Đề + +Doanh nghiệp cần tương tác khách hàng trên Zalo, nền tảng nhắn tin hàng đầu Việt Nam với 80M+ người dùng. Yêu cầu: +- Phản hồi tự động cho câu hỏi thường gặp (24/7) +- Hội thoại AI cho nhu cầu phức tạp +- Dữ liệu khách hàng và lịch sử tập trung +- Hạ tầng nhắn tin mở rộng cho chiến dịch + +### Mục Tiêu Nghiệp Vụ + +1. **Tương Tác Khách Hàng**: 90% tỷ lệ phản hồi trong vòng 1 phút +2. **Tự Động Hóa**: 70% câu hỏi xử lý không cần con người +3. **Cá Nhân Hóa**: Phản hồi nhận biết ngữ cảnh dựa trên lịch sử +4. **Mở Rộng**: Hỗ trợ 10,000+ hội thoại đồng thời + +--- + +## Kiến Trúc Hệ Thống + +### Kiến Trúc Tổng Quan + +``` +┌─────────────────────────────────────────────────────────┐ +│ Zalo Official Account │ +│ (Khách hàng gửi tin nhắn qua Zalo) │ +└────────────────────┬────────────────────────────────────┘ + │ Webhook Events + ▼ +┌─────────────────────────────────────────────────────────┐ +│ mkt-zalo-service-net │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Webhook Handler (API Layer) │ │ +│ │ • Xác thực chữ ký │ │ +│ │ • Parse events (message, follow, buttons) │ │ +│ │ • Trả về 200 nhanh │ │ +│ └───────────────┬──────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Engine Xử Lý Tin Nhắn │ │ +│ │ ┌────────────────┐ ┌────────────────┐ │ │ +│ │ │ Rule Engine │ │ AI Engine │ │ │ +│ │ │ (Tự động hóa) │ │ (Dựa LLM) │ │ │ +│ │ └────────────────┘ └────────────────┘ │ │ +│ └───────────────┬──────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Domain Layer │ │ +│ │ • Conversation Aggregate │ │ +│ │ • ZaloCustomer Aggregate │ │ +│ │ • ChatbotRule Entity │ │ +│ │ • MessageTemplate Entity │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Integration Layer │ │ +│ │ • Zalo OA API Client (Gửi tin nhắn) │ │ +│ │ • LLM Provider (OpenAI, Vertex AI) │ │ +│ └──────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ + ▼ + ┌──────────────┐ + │ PostgreSQL │ + │ (Customer, │ + │ Conversation,│ + │ Templates) │ + └──────────────┘ +``` + +### Các Tầng Clean Architecture + +``` +┌────────────────────────────────────────────────────────────┐ +│ API Layer │ +│ Controllers → MediatR → Commands/Queries │ +│ • WebhooksController (public, không auth) │ +│ • ConversationsController (authenticated) │ +│ • CustomersController (quản lý CRM) │ +│ • ChatbotRulesController (cấu hình automation) │ +│ • Admin/* (analytics, broadcast) │ +└────────────────────────┬───────────────────────────────────┘ + │ +┌────────────────────────▼───────────────────────────────────┐ +│ Application Layer │ +│ Commands: ProcessWebhookCommand, SendMessageCommand │ +│ Queries: GetConversationHistoryQuery, GetCustomerQuery │ +│ Handlers: ProcessWebhookCommandHandler (routes to engines)│ +│ Behaviors: ValidationBehavior, LoggingBehavior │ +└────────────────────────┬───────────────────────────────────┘ + │ +┌────────────────────────▼───────────────────────────────────┐ +│ Domain Layer │ +│ Aggregates: │ +│ • Conversation (messages, status, context) │ +│ • ZaloCustomer (profile, tags, segment) │ +│ • ChatbotRule (conditions, actions) │ +│ • MessageTemplate (ZNS templates) │ +│ │ +│ Business Rules: │ +│ • Conversation.AddMessage() - validate message type │ +│ • ZaloCustomer.UpdateProfile() - enforce data quality │ +│ • ChatbotRule.Evaluate() - match conditions │ +└────────────────────────┬───────────────────────────────────┘ + │ +┌────────────────────────▼───────────────────────────────────┐ +│ Infrastructure Layer │ +│ • EF Core DbContext (persistence) │ +│ • Repositories (ConversationRepository, CustomerRepository)│ +│ • ZaloOfficialAccountClient (HTTP client với Polly) │ +│ • RuleBasedChatbotEngine (keyword matching, regex) │ +│ • AiChatbotEngine (tích hợp LLM) │ +│ • Redis Caching (conversation context) │ +└────────────────────────────────────────────────────────────┘ +``` + +--- + +## Mô Hình Domain + +### Tổng Quan Aggregates + +**4 Aggregate Roots:** + +1. **Conversation** - Quản lý vòng đời hội thoại và tin nhắn +2. **ZaloCustomer** - Hồ sơ khách hàng, tags, và phân khúc +3. **ChatbotRule** - Quy tắc tự động hóa (keyword/regex/intent) +4. **MessageTemplate** - Quản lý template ZNS + +Xem [DOMAIN_MODELS.md](./DOMAIN_MODELS.md) để biết chi tiết triển khai. + +--- + +## CQRS Commands & Queries + +### Commands (Thao Tác Ghi) + +```csharp +// Xử lý webhook từ Zalo +public record ProcessWebhookCommand( + string EventName, + string ZaloUserId, + string MessageText, + string MessageType, + DateTime Timestamp) : IRequest; + +// Gửi tin nhắn đến khách hàng Zalo +public record SendMessageCommand( + Guid ConversationId, + string ZaloUserId, + string MessageText, + MessageType Type = MessageType.Text) : IRequest; + +// Tạo quy tắc tự động chatbot +public record CreateChatbotRuleCommand( + string Name, + RuleType Type, + List Conditions, + RuleActionDto Action, + int Priority) : IRequest; +``` + +### Queries (Thao Tác Đọc) + +```csharp +// Lấy lịch sử hội thoại +public record GetConversationHistoryQuery( + Guid ConversationId, + int Skip = 0, + int Take = 50) : IRequest; + +// Lấy chi tiết khách hàng với tóm tắt tương tác +public record GetCustomerQuery( + Guid CustomerId) : IRequest; + +// Tìm kiếm hội thoại +public record SearchConversationsQuery( + string? ZaloUserId = null, + ConversationStatus? Status = null, + DateTime? FromDate = null, + DateTime? ToDate = null, + int Skip = 0, + int Take = 20) : IRequest>; +``` + +--- + +## Patterns Tích Hợp + +### 1. Tích Hợp Zalo Official Account + +**Luồng Xử Lý Webhook:** + +``` +Zalo → POST /webhooks → Xác thực chữ ký → Parse event → Queue vào RabbitMQ → Trả về 200 + ↓ + RabbitMQ Consumer → Load customer → Route to Engine → Tạo response → Gửi qua Zalo API +``` + +**Yêu Cầu Quan Trọng:** +- Phải trả về 200 trong vòng 2 giây +- Xác thực chữ ký (HMAC SHA256) +- Xử lý async qua message queue + +### 2. Rule-Based Chatbot Engine + +```csharp +public class RuleBasedChatbotEngine : IChatbotEngine +{ + public async Task ProcessAsync( + string userMessage, + ConversationContext context, + CancellationToken ct) + { + var rules = await _ruleRepository.GetActiveRulesOrderedByPriorityAsync(ct); + + foreach (var rule in rules) + { + if (rule.Evaluate(userMessage)) + { + return new ChatbotResponse( + ResponseText: rule.Action.ResponseText, + MatchedRuleId: rule.Id, + Confidence: 1.0); + } + } + + return ChatbotResponse.NoMatch; + } +} +``` + +### 3. AI Chatbot Engine (LLM) + +```csharp +public class AiChatbotEngine : IChatbotEngine +{ + public async Task ProcessAsync( + string userMessage, + ConversationContext context, + CancellationToken ct) + { + // Load lịch sử hội thoại để có context + var conversationHistory = await _cache.GetOrSetAsync( + $"conversation:{context.ConversationId}", + async () => await _conversationRepository.GetMessagesAsync( + context.ConversationId, take: 10, ct), + TimeSpan.FromMinutes(30), + ct); + + // Xây dựng prompt LLM với context + var prompt = BuildPrompt(userMessage, conversationHistory, context.CustomerProfile); + + // Gọi LLM provider + var llmResponse = await _llmService.GenerateResponseAsync(prompt, ct); + + return new ChatbotResponse( + ResponseText: llmResponse.Text, + Confidence: llmResponse.Confidence); + } +} +``` + +--- + +## Luồng Dữ Liệu + +### Luồng Xử Lý Webhook + +``` +1. Zalo gửi webhook → WebhooksController +2. Xác thực chữ ký (HMAC SHA256) +3. Parse event (user_send_text, follow, etc.) +4. Publish vào RabbitMQ +5. Trả về 200 cho Zalo (< 2s) + +6. Consumer xử lý event +7. Load hoặc tạo ZaloCustomer +8. Load hoặc tạo Conversation +9. Route vào Rule Engine hoặc AI Engine +10. Tạo response +11. Lưu vào database +12. Gửi qua Zalo API +13. Cập nhật Redis cache +``` + +--- + +## Bảo Mật & Xác Thực + +### 1. Xác Thực Chữ Ký Webhook + +```csharp +public class ZaloWebhookSignatureValidator +{ + private readonly string _webhookSecret; + + public bool ValidateSignature(string body, string signature) + { + var computedSignature = ComputeHmac256(body, _webhookSecret); + return signature.Equals(computedSignature, StringComparison.OrdinalIgnoreCase); + } + + private string ComputeHmac256(string message, string secret) + { + var encoding = new UTF8Encoding(); + var keyBytes = encoding.GetBytes(secret); + using var hmac = new HMACSHA256(keyBytes); + var hashBytes = hmac.ComputeHash(encoding.GetBytes(message)); + return BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); + } +} +``` + +### 2. Xác Thực API + +- **Public APIs** (Webhooks): Chỉ xác thực chữ ký +- **Customer APIs**: JWT Bearer tokens từ IAM service +- **Admin APIs**: JWT + Admin role claim + +--- + +## Hiệu Năng & Mở Rộng + +### Mục Tiêu Hiệu Năng + +| M ục Tiêu | Target | Chiến Lược | +|-----------|--------|------------| +| Thời gian phản hồi webhook | < 2s | Queue ngay, trả về 200 | +| Thời gian xử lý tin nhắn | < 5s | Xử lý async qua RabbitMQ | +| Tạo phản hồi AI | < 3s | LLM caching, tối ưu prompt | +| Thời gian query database | < 100ms | Indexed queries, read replicas | +| Hội thoại đồng thời | 10,000+ | Xử lý stateless, Redis cache | + +### Chiến Lược Caching + +```csharp +// Cache conversation context trong Redis +Key: "conversation:{conversationId}" +TTL: 30 phút +Data: 10 tin nhắn cuối, customer profile, active rules + +// Cache chatbot rules +Key: "chatbot:rules:active" +TTL: 5 phút +Data: Tất cả rules active sắp theo priority +``` + +--- + +## Giám Sát & Observability + +### Các Metrics Quan Trọng + +``` +# Webhook metrics +zalo_webhook_events_total{event_type="user_send_text"} +zalo_webhook_processing_duration_seconds + +# Chatbot metrics +zalo_messages_processed_total{engine="rule_based|ai"} +zalo_chatbot_rule_matches_total{rule_id="..."} +zalo_ai_llm_latency_seconds + +# Business metrics +zalo_active_conversations_total +zalo_customer_satisfaction_score +zalo_automation_rate +``` + +### Chiến Lược Logging + +``` +INFO: Nhận tin nhắn user, khớp quy tắc +WARN: Không khớp quy tắc, fallback sang AI +ERROR: Lỗi gửi tin Zalo, đang retry +``` + +--- + +## Cân Nhắc Triển Khai + +### Cấu Hình Môi Trường + +```yaml +# Production +- Webhook URL: https://api.goodgo.vn/api/v1/zalo/webhooks +- Database: Neon PostgreSQL (multi-region) +- Redis: ElastiCache cluster +- LLM: Vertex AI (gcp-asia-southeast1) + +# Staging +- Webhook URL: https://staging-api.goodgo.vn/api/v1/zalo/webhooks +- Zalo OA: Test account +``` + +### Khôi Phục Thảm Họa + +- **Database backups**: Snapshot hàng ngày, giữ 30 ngày +- **Message queue**: Queues bền vững với replication +- **Webhook replay**: Lưu raw webhook events trong 7 ngày + +--- + +## Cải Tiến Tương Lai + +1. **Hỗ trợ đa ngôn ngữ** - Chatbot Tiếng Việt + English +2. **Xử lý tin nhắn thoại** - Tích hợp speech-to-text +3. **Phân tích cảm xúc** - Phát hiện cảm xúc khách hàng +4. **A/B testing** - Test các phản hồi chatbot khác nhau +5. **Tích hợp Zalo Mini App** - Deep linking vào mini apps + +--- + +**Phiên Bản Tài Liệu**: 1.0 +**Cập Nhật Lần Cuối**: 2026-01-18 +**Tác Giả**: GoodGo Platform Team diff --git a/services/mkt-zalo-service-net/docs/vi/DATABASE.md b/services/mkt-zalo-service-net/docs/vi/DATABASE.md new file mode 100644 index 00000000..69ff999c --- /dev/null +++ b/services/mkt-zalo-service-net/docs/vi/DATABASE.md @@ -0,0 +1,214 @@ +# Schema Cơ Sở Dữ Liệu - MKT Zalo Service + +Thiết kế và tài liệu database schema. + +## Tổng Quan Tables + +9 tables với relationships: +- `ZaloCustomers` - Hồ sơ khách hàng +- `CustomerTags` - Tags khách hàng (many-to-many) +- `Conversations` - Phiên chat +- `Messages` - Tất cả tin nhắn +- `ChatbotRules` - Quy tắc tự động +- `RuleConditions` - Điều kiện quy tắc +- `RuleActions` - Hành động quy tắc +- `MessageTemplates` - ZNS templates +- `TemplateParameters` - Parameters template + +--- + +## Tables Chính + +### ZaloCustomers + +```sql +CREATE TABLE "ZaloCustomers" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ZaloUserId" VARCHAR(50) NOT NULL UNIQUE, + "DisplayName" VARCHAR(255) NOT NULL, + "AvatarUrl" VARCHAR(500), + "PhoneNumber" VARCHAR(20), + "Email" VARCHAR(255), + "Segment" INT NOT NULL DEFAULT 0, + "FirstInteractionAt" TIMESTAMPTZ NOT NULL, + "LastInteractionAt" TIMESTAMPTZ NOT NULL, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_ZaloCustomers_ZaloUserId" ON "ZaloCustomers" ("ZaloUserId"); +CREATE INDEX "IX_ZaloCustomers_Segment" ON "ZaloCustomers" ("Segment"); +``` + +### Conversations + +```sql +CREATE TABLE "Conversations" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "CustomerId" UUID NOT NULL REFERENCES "ZaloCustomers"("Id") ON DELETE CASCADE, + "ZaloUserId" VARCHAR(50) NOT NULL, + "Status" INT NOT NULL DEFAULT 0, + "StartedAt" TIMESTAMPTZ NOT NULL, + "EndedAt" TIMESTAMPTZ, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_Conversations_CustomerId" ON "Conversations" ("CustomerId"); +CREATE INDEX "IX_Conversations_Status_StartedAt" ON "Conversations" ("Status", "StartedAt" DESC); +``` + +### Messages + +```sql +CREATE TABLE "Messages" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ConversationId" UUID NOT NULL REFERENCES "Conversations"("Id") ON DELETE CASCADE, + "Type" INT NOT NULL, + "Content" TEXT NOT NULL, + "Direction" INT NOT NULL, + "IsFromBot" BOOLEAN NOT NULL DEFAULT FALSE, + "SentAt" TIMESTAMPTZ NOT NULL, + "ZaloMessageId" VARCHAR(100), + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_Messages_ConversationId_SentAt" ON "Messages" ("ConversationId", "SentAt" DESC); +CREATE INDEX "IX_Messages_Content_FullText" ON "Messages" USING GIN (to_tsvector('english', "Content")); +``` + +--- + +## Tables Automation + +### ChatbotRules + +```sql +CREATE TABLE "ChatbotRules" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "Name" VARCHAR(255) NOT NULL, + "Type" INT NOT NULL, + "Priority" INT NOT NULL DEFAULT 50, + "IsActive" BOOLEAN NOT NULL DEFAULT TRUE, + "MatchCount" INT NOT NULL DEFAULT 0, + "LastMatchedAt" TIMESTAMPTZ, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_ChatbotRules_IsActive_Priority" ON "ChatbotRules" ("IsActive", "Priority" DESC); +``` + +### RuleConditions + +```sql +CREATE TABLE "RuleConditions" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "RuleId" UUID NOT NULL REFERENCES "ChatbotRules"("Id") ON DELETE CASCADE, + "Field" VARCHAR(50) NOT NULL, + "Operator" VARCHAR(50) NOT NULL, + "Value" TEXT NOT NULL, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_RuleConditions_RuleId" ON "RuleConditions" ("RuleId"); +``` + +--- + +## Tables Template + +### MessageTemplates + +```sql +CREATE TABLE "MessageTemplates" ( + "Id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "ZaloTemplateId" VARCHAR(50) NOT NULL UNIQUE, + "Name" VARCHAR(255) NOT NULL, + "Content" TEXT NOT NULL, + "Status" INT NOT NULL DEFAULT 0, + "CreatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "UpdatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX "IX_MessageTemplates_ZaloTemplateId" ON "MessageTemplates" ("ZaloTemplateId"); +``` + +--- + +## Relationships + +``` +ZaloCustomers (1) ──── (M) Conversations +Conversations (1) ──── (M) Messages +ZaloCustomers (1) ──── (M) CustomerTags +ChatbotRules (1) ──── (M) RuleConditions +ChatbotRules (1) ──── (1) RuleActions +MessageTemplates (1) ──── (M) TemplateParameters +``` + +--- + +## Indexes + +### Indexes Quan Trọng Về Hiệu Năng + +| Table | Index | Mục Đích | +|-------|-------|----------| +| ZaloCustomers | IX_ZaloCustomers_ZaloUserId | Tra cứu nhanh theo Zalo user ID | +| Conversations | IX_Conversations_Status_StartedAt | List hội thoại active/gần đây | +| Messages | IX_Messages_ConversationId_SentAt | Lấy lịch sử hội thoại | +| Messages | IX_Messages_Content_FullText | Tìm kiếm full-text | +| ChatbotRules | IX_ChatbotRules_IsActive_Priority | Đánh giá quy tắc nhanh | + +--- + +## Migrations + +### Migration Ban Đầu + +```bash +dotnet ef migrations add InitialSchema \ + --project src/MktZaloService.Infrastructure + +dotnet ef database update \ + --project src/MktZaloService.Infrastructure +``` + +### Seed Data + +```sql +INSERT INTO "ChatbotRules" ("Id", "Name", "Type", "Priority", "IsActive") +VALUES + (gen_random_uuid(), 'Quy tắc chào hỏi', 0, 100, TRUE), + (gen_random_uuid(), 'Hỏi giá', 0, 90, TRUE), + (gen_random_uuid(), 'Giờ mở cửa', 0, 90, TRUE); +``` + +--- + +## Ước Tính Dung Lượng Dữ Liệu + +| Table | 6 Tháng | Tốc Độ Tăng | +|-------|---------|-------------| +| ZaloCustomers | 10,000 | +500/ngày | +| Conversations | 50,000 | +2,000/ngày | +| Messages | 750,000 | +30,000/ngày | + +**Tổng Dung Lượng DB**: ~2GB (6 tháng) + +--- + +## Chiến Lược Backup + +```bash +# Backup tự động hàng ngày +- Full backup: Hàng ngày lúc 02:00 UTC +- Retention: 30 ngày +- Point-in-time recovery: 7 ngày gần nhất +``` + +--- + +**Phiên Bản Tài Liệu**: 1.0 +**Cập Nhật Lần Cuối**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/vi/DOMAIN_MODELS.md b/services/mkt-zalo-service-net/docs/vi/DOMAIN_MODELS.md new file mode 100644 index 00000000..573fa551 --- /dev/null +++ b/services/mkt-zalo-service-net/docs/vi/DOMAIN_MODELS.md @@ -0,0 +1,251 @@ +# Mô Hình Domain - MKT Zalo Service + +Triển khai Domain-Driven Design cho dịch vụ marketing Zalo. + +## Aggregates + +### 1. Conversation Aggregate + +Quản lý vòng đời hội thoại và tin nhắn. + +```csharp +public class Conversation : Entity, IAggregateRoot +{ + private readonly List _messages = new(); + + public string ZaloUserId { get; private set; } + public Guid CustomerId { get; private set; } + public ConversationStatus Status { get; private set; } + public DateTime StartedAt { get; private set; } + public DateTime? EndedAt { get; private set; } + public IReadOnlyCollection Messages => _messages.AsReadOnly(); + + public void AddMessage(MessageType type, string content, + MessageDirection direction, bool isFromBot) + { + if (Status == ConversationStatus.Closed) + throw new ZaloDomainException( + "Không thể thêm tin nhắn vào hội thoại đã đóng"); + + var message = new Message(type, content, direction, isFromBot); + _messages.Add(message); + + AddDomainEvent(new MessageReceivedDomainEvent(Id, message.Id, content)); + } + + public void Close() + { + if (Status == ConversationStatus.Closed) return; + + Status = ConversationStatus.Closed; + EndedAt = DateTime.UtcNow; + + AddDomainEvent(new ConversationClosedDomainEvent(Id)); + } +} +``` + +**Quy Tắc Nghiệp Vụ**: +- Không thể thêm tin nhắn vào hội thoại đã đóng +- Trạng thái hội thoại phải là Active hoặc Closed +- Tin nhắn không thể thay đổi sau khi tạo + +--- + +### 2. ZaloCustomer Aggregate + +Quản lý hồ sơ khách hàng và phân khúc. + +```csharp +public class ZaloCustomer : Entity, IAggregateRoot +{ + private readonly List _tags = new(); + + public string ZaloUserId { get; private set; } + public CustomerProfile Profile { get; private set; } + public CustomerSegment Segment { get; private set; } + public DateTime FirstInteractionAt { get; private set; } + public DateTime LastInteractionAt { get; private set; } + public IReadOnlyCollection Tags => _tags.AsReadOnly(); + + public void UpdateProfile(string displayName, string avatarUrl, + string? phoneNumber = null, string? email = null) + { + if (string.IsNullOrWhiteSpace(displayName)) + throw new ArgumentException("Tên hiển thị không được để trống"); + + Profile = new CustomerProfile(displayName, avatarUrl, phoneNumber, email); + AddDomainEvent(new CustomerProfileUpdatedDomainEvent(Id, ZaloUserId)); + } + + public void AddTag(string tagName) + { + if (_tags.Any(t => t.Name.Equals(tagName, + StringComparison.OrdinalIgnoreCase))) + return; + + _tags.Add(new Tag(tagName)); + } + + public void UpdateSegment(int conversationCount, int messageCount) + { + Segment = (conversationCount, messageCount) switch + { + (> 50, > 500) => CustomerSegment.VIP, + (> 20, > 200) => CustomerSegment.Active, + (> 5, > 50) => CustomerSegment.Regular, + _ => CustomerSegment.New + }; + } +} +``` + +**Quy Tắc Nghiệp Vụ**: +- ZaloUserId phải unique +- DisplayName là bắt buộc +- Tự động phân khúc dựa trên metrics tương tác +- Không có tags trùng lặp mỗi khách hàng + +--- + +### 3. ChatbotRule Aggregate + +Định nghĩa quy tắc tự động hóa cho phản hồi chatbot. + +```csharp +public class ChatbotRule : Entity, IAggregateRoot +{ + private readonly List _conditions = new(); + + public string Name { get; private set; } + public RuleType Type { get; private set; } + public IReadOnlyCollection Conditions => _conditions.AsReadOnly(); + public RuleAction Action { get; private set; } + public int Priority { get; private set; } + public bool IsActive { get; private set; } + + public bool Evaluate(string userMessage) + { + if (!IsActive) return false; + + return Type switch + { + RuleType.Keyword => _conditions.Any(c => + userMessage.Contains(c.Value, StringComparison.OrdinalIgnoreCase)), + RuleType.Regex => _conditions.Any(c => + Regex.IsMatch(userMessage, c.Value)), + _ => false + }; + } + + public void RecordMatch() + { + MatchCount++; + LastMatchedAt = DateTime.UtcNow; + } +} +``` + +**Quy Tắc Nghiệp Vụ**: +- Quy tắc phải có ít nhất một điều kiện +- Priority phải từ 0-100 +- Quy tắc priority cao được đánh giá trước +- Quy tắc không active bị bỏ qua + +--- + +### 4. MessageTemplate Aggregate + +Quản lý ZNS templates. + +```csharp +public class MessageTemplate : Entity, IAggregateRoot +{ + private readonly List _parameters = new(); + + public string ZaloTemplateId { get; private set; } + public string Name { get; private set; } + public string Content { get; private set; } + public TemplateStatus Status { get; private set; } + public IReadOnlyCollection Parameters => _parameters.AsReadOnly(); + + public Dictionary ValidateParameters( + Dictionary providedParams) + { + var missingParams = _parameters + .Where(p => p.IsRequired && !providedParams.ContainsKey(p.Name)) + .Select(p => p.Name) + .ToList(); + + if (missingParams.Any()) + throw new ZaloDomainException( + $"Thiếu parameters bắt buộc: {string.Join(", ", missingParams)}"); + + return providedParams; + } +} +``` + +--- + +## Value Objects + +### CustomerProfile + +```csharp +public class CustomerProfile : ValueObject +{ + public string DisplayName { get; } + public string AvatarUrl { get; } + public string? PhoneNumber { get; } + public string? Email { get; } + + protected override IEnumerable GetEqualityComponents() + { + yield return DisplayName; + yield return AvatarUrl; + yield return PhoneNumber; + yield return Email; + } +} +``` + +### RuleCondition + +```csharp +public class RuleCondition : ValueObject +{ + public string Field { get; } + public string Operator { get; } + public string Value { get; } +} +``` + +### RuleAction + +```csharp +public class RuleAction : ValueObject +{ + public ActionType ActionType { get; } + public string? ResponseText { get; } + public Guid? TemplateId { get; } +} +``` + +--- + +## Enumerations + +```csharp +public enum ConversationStatus { Active = 0, Closed = 1 } +public enum MessageType { Text = 0, Image = 1, Link = 2, Sticker = 3, Audio = 4 } +public enum MessageDirection { Incoming = 0, Outgoing = 1 } +public enum CustomerSegment { New = 0, Regular = 1, Active = 2, VIP = 3 } +public enum RuleType { Keyword = 0, Regex = 1, Intent = 2 } +public enum ActionType { SendText = 0, SendTemplate = 1, ForwardToHuman = 2 } +``` + +--- + +**Phiên Bản Tài Liệu**: 1.0 +**Cập Nhật Lần Cuối**: 2026-01-18 diff --git a/services/mkt-zalo-service-net/docs/vi/ZALO_SETUP.md b/services/mkt-zalo-service-net/docs/vi/ZALO_SETUP.md new file mode 100644 index 00000000..d92de10f --- /dev/null +++ b/services/mkt-zalo-service-net/docs/vi/ZALO_SETUP.md @@ -0,0 +1,302 @@ +# Hướng Dẫn Tích Hợp Zalo Official Account + +Hướng dẫn đầy đủ để tích hợp với Zalo Official Account API. + +## Yêu Cầu Tiên Quyết + +### 1. Zalo Official Account + +Đăng ký và xác minh tài khoản doanh nghiệp tại https://oa.zalo.me/ + +**Yêu cầu**: +- Giấy phép kinh doanh +- Đăng ký kinh doanh Việt Nam +- 3-5 ngày làm việc để xác minh + +### 2. Zalo Application + +Tạo ứng dụng tại https://developers.zalo.me/ + +1. Tạo ứng dụng mới +2. Liên kết với Official Account của bạn +3. Ghi chú `App ID` và `Secret Key` + +### 3. Access Token + +Lấy OA Access Token (hợp lệ 1 năm): + +**Qua Portal** (Development): +1. Vào https://developers.zalo.me/tools/explorer +2. Chọn ứng dụng và OA của bạn +3. Click "Get Access Token" +4. Copy token + +**Qua API** (Production): +```http +POST https://oauth.zaloapp.com/v4/oa/access_token +Content-Type: application/x-www-form-urlencoded + +app_id={app_id}&secret_key={secret_key}&code={authorization_code} +``` + +--- + +## Cấu Hình Webhook + +### 1. Đặt Webhook URL + +Trong Zalo Developers portal: +1. Vào tab "Webhooks" +2. Đặt URL: `https://yourdomain.com/api/v1/zalo/webhooks` +3. Đặt secret key để xác thực chữ ký + +### 2. Bật Events + +Events cần thiết: +- ✅ `user_send_text` - Tin nhắn text +- ✅ `user_send_image` - Tin nhắn hình ảnh +- ✅ `follow` - Người theo dõi mới +- ✅ `unfollow` - Bỏ theo dõi + +### 3. Xác Thực Chữ Ký + +```csharp +public class ZaloWebhookVerifier +{ + private readonly string _secretKey; + + public bool VerifySignature(string body, string signature) + { + using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_secretKey)); + var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body)); + var computed = BitConverter.ToString(hash).Replace("-", "").ToLower(); + + return signature.Equals(computed, StringComparison.OrdinalIgnoreCase); + } +} +``` + +**Headers**: +``` +X-Zalo-Signature: +``` + +### 4. Yêu Cầu Response + +**QUAN TRỌNG**: Phải trả về `200 OK` trong vòng 2 giây + +```csharp +[HttpPost("webhooks")] +public async Task ReceiveWebhook([FromBody] ZaloWebhookEvent webhook) +{ + // 1. Xác thực chữ ký (NHANH) + if (!_verifier.VerifySignature(RequestBody, Request.Headers["X-Zalo-Signature"])) + return Unauthorized(); + + // 2. Queue để xử lý async (NHANH) + await _messageQueue.PublishAsync(webhook); + + // 3. Trả về ngay (< 2s) + return Ok(new { success = true }); +} +``` + +--- + +## Zalo API Endpoints + +### Base URL +``` +https://openapi.zalo.me +``` + +### 1. Gửi Tin Nhắn Text + +```http +POST /v3.0/oa/message/cs +Authorization: access_token {your_access_token} +Content-Type: application/json + +{ + "recipient": { + "user_id": "1234567890123456789" + }, + "message": { + "text": "Xin chào! Tôi có thể giúp gì cho bạn?" + } +} +``` + +**Response**: +```json +{ + "error": 0, + "message": "Success", + "data": { + "message_id": "zalo_msg_xyz123" + } +} +``` + +### 2. Lấy Thông Tin User + +```http +GET /v3.0/oa/user/detail?data={"user_id":"1234567890123456789"} +Authorization: access_token {your_access_token} +``` + +**Response**: +```json +{ + "error": 0, + "data": { + "user_id": "1234567890123456789", + "display_name": "Nguyễn Văn A", + "avatar": "https://s.zalo.me/avatar.jpg", + "user_gender": 1 + } +} +``` + +### 3. Gửi ZNS Template + +```http +POST /v3.0/oa/message/template +Authorization: access_token {your_access_token} + +{ + "phone": "+84901234567", + "template_id": "283746", + "template_data": { + "order_id": "ORD-12345", + "total_amount": "500000" + } +} +``` + +--- + +## Loại Events + +### user_send_text + +```json +{ + "event_name": "user_send_text", + "sender": { "id": "1234567890123456789" }, + "message": { + "text": "Xin chào", + "msg_id": "msg_123456" + } +} +``` + +### user_send_image + +```json +{ + "event_name": "user_send_image", + "message": { + "attachments": [{ + "type": "image", + "payload": { + "url": "https://zalo-api.zadn.vn/...", + "thumbnail": "https://..." + } + }] + } +} +``` + +### follow + +```json +{ + "event_name": "follow", + "follower": { "id": "1234567890123456789" } +} +``` + +--- + +## Giới Hạn Rate + +| API | Giới Hạn | +|-----|----------| +| Gửi Message (CS) | 1000 requests/ngày mỗi user | +| Gửi ZNS | 10000 requests/ngày | +| Lấy User Profile | 5000 requests/ngày | + +--- + +## Xử Lý Lỗi + +### Mã Lỗi Thường Gặp + +| Mã | Thông Báo | Giải Pháp | +|----|-----------|-----------| +| -201 | Invalid access token | Làm mới token | +| -213 | User đã chặn OA | Đánh dấu khách hàng inactive | +| -214 | User không hoạt động 7+ ngày | Dùng ZNS thay vì CS | +| -300 | Vượt giới hạn rate | Đợi và thử lại | + +### Chiến Lược Retry + +```csharp +var retryPolicy = Policy + .Handle() + .OrResult(r => (int)r.StatusCode >= 500) + .WaitAndRetryAsync(3, retryAttempt => + TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); +``` + +--- + +## Best Practices + +### Xử Lý Webhook + +✅ **NÊN**: +- Xác thực chữ ký mỗi webhook +- Trả về 200 trong vòng 2 giây +- Xử lý bất đồng bộ qua queue +- Lưu raw events trong 7 ngày + +❌ **KHÔNG NÊN**: +- Query database trong handler +- Gọi API bên ngoài đồng bộ +- Xử lý LLM trong webhook handler + +### Chất Lượng Tin Nhắn + +✅ **NÊN**: +- Giữ tin nhắn dưới 2000 ký tự +- Dùng ZNS cho thông báo quan trọng +- Tôn trọng trạng thái chặn OA + +❌ **KHÔNG NÊN**: +- Gửi spam +- Gửi marketing không có sự đồng ý + +--- + +## Kiểm Thử + +### Kiểm Thử Thủ Công + +```bash +# Gửi test webhook +curl -X POST http://localhost:8080/api/v1/zalo/webhooks \ + -H "Content-Type: application/json" \ + -H "X-Zalo-Signature: " \ + -d '{ + "event_name": "user_send_text", + "sender": {"id": "test_user"}, + "message": {"text": "xin chào"} + }' +``` + +--- + +**Phiên Bản Tài Liệu**: 1.0 +**Cập Nhật Lần Cuối**: 2026-01-18