1. Bussiness | Logic
Traffic Tool
  • Traffic Tool Docs
    • Tổng Quan
    • project
      • admin
        • Hướng dẫn sử dung
          • Đăng Nhập & Xác Thực Hai Bước
          • Thống Kê Hệ Thống
          • Thống Kê Hàng Ngày
          • Tìm Kiếm Tự Động
          • Trung Tâm Báo Cáo
          • Quản Lý Tài Khoản
          • Quản Lý Server
          • Quản Lý Proxy
          • Quản Lý Gói Proxy
          • Quản Lý Nhà Cung Cấp Proxy
          • Quản Lý Backup Proxy
          • Quản Lý Tài Khoản Google
      • api + tool
        • Hướng dẫn sử dụng
          • User Guide
          • Setup Guide
        • Bussiness | Logic
          • 1. Thống kê hàng ngày (Daily Statistics)
          • 2. Thống kê hệ thống (System Statistics)
          • 3. Tìm kiếm tự động (Automated Discovery)
          • 4. Trung tâm Báo cáo (Report Center)
          • 5. Quản lý tài khoản (Account Management)
          • 6. Quản lý Hệ thống (System Management)
          • 7. Quản lý tài khoản Google (Google Account Management)
        • Architecture
          • Database Schema
          • System Architecture
          • Code Structure
          • Logs and Monitoring
          • Environment & Configuration
        • Deployment
          • Local
          • Staging
          • Product
  • Traffic Backend API
    • 🔑 Identity & Session
      • Đăng nhập hệ thống (Login)
      • Đăng ký tài khoản mới (Public)
      • Lấy danh sách người dùng (Phân trang)
      • Admin tạo người dùng mới
      • Lấy thông tin cá nhân hiện tại
      • Khởi tạo bảo mật 2FA
      • Xác thực mã OTP
      • Chi tiết người dùng theo ID
      • Cập nhật thông tin người dùng
      • Xóa tài khoản người dùng
      • Đổi mật khẩu
      • Kiểm tra Cấu hình Thông báo Telegram Toàn hệ thống
      • Bật/Tắt Thông báo Telegram Toàn cục
    • 📁 Campaign Management
      • Danh sách Chiến dịch Toàn cầu
      • Khởi tạo Chiến dịch Mới
      • Chi tiết Chiến dịch
      • Cập nhật Chiến dịch
      • Xóa Chiến dịch
      • Tắt/Mở Chiến dịch (Hàng loạt)
      • Dữ liệu Hình mẫu SEO (Negative SEO)
    • ⚙️ Project Mechanics
      • Danh sách Dự án (Projects List)
      • Khởi tạo Kịch bản Mô phỏng
      • Lấy chi tiết cấu hình Dự án
      • Cập nhật Kịch bản chạy
      • Xóa Dự án
      • Chỉnh sửa Hàng loạt (Bulk)
      • Bật/Tắt Dự án
    • 👤 Profile Management
      • Danh sách Vân tay số (Profiles Data)
      • Tạo Hồ sơ Đơn lẻ (Tạo Vân tay mới)
      • Nhập kho Tài khoản Email (Bulk Import)
      • Chi tiết Session/Cookies
      • Chỉnh sửa Hồ sơ/Ghi chú
      • Xóa Vân tay số và Dữ liệu Local
      • Mở khóa Captcha/Trạng thái Blocked
      • Tra cứu Kho Profile Khả dụng
      • Báo cáo Sức khỏe Kho Tài Khoản
    • 🦾 Worker Interface
      • Đăng ký Khởi tạo Node (Handshake)
      • Nhịp Tim Khảo Sát Tình Trạng (Heartbeat)
      • Nhận Cấu hình Bypass & Hệ thống
      • Kéo (PULL) Nhiệm vụ Traffic SEO
      • Báo cáo Sự cố Node (Crash Report)
    • 📊 Report: Execution
      • Kéo Công việc Báo cáo (Worker Pull)
      • Quản lý Giám sát Nhiệm vụ (Task Dashboard)
      • Bắn Lại Báo Cáo Thất Bại (Manual Retry)
    • 📊 Report: Discovery
      • Danh sách Tên miền Chờ Xử Lý (Discovery Pool)
      • Nhập Mục Tiêu Thủ Công (Manual Insert)
      • Kích hoạt Heuristic Scanner (Cào tự động)
      • Chi tiết Bằng Chứng (Evidence Data)
      • Dán nhãn Vi Phạm / Cập nhật Screenshot
      • Loại Bỏ Mục Tiêu
      • Duyệt Yêu Cầu (Approve to Execution)
    • 📊 Report: Platforms Configuration
      • Truy vấn Danh sách Nền tảng Đối tác (Vendor)
      • Tạo Nền tảng Vendor Báo cáo Mới
      • Bật/Tắt Trang báo cáo theo Loại
      • Cập nhật Metadata Trang
      • Xóa Trang Báo Cáo
      • Lấy Cài đặt LLM cho Vendor cụ thể
      • Cập nhật Cài đặt Tạo mẫu LLM
      • Lấy Cấu hình Schema Biểu mẫu cho Vendor
      • Cập nhật Yêu cầu Trường Biểu mẫu
    • 📊 Report: Email Automation
      • Danh sách SMTP/Mailer Server
      • Thêm kết nối SMTP Mới
      • Nhật ký Nhiệm vụ Gửi Mail (Email Log)
      • Phân tích Tỉ lệ Chuyển đổi (Email Delivery Analytics)
    • 🌐 Global Proxies
      • Danh sách Kho Proxy Phân trang (Proxy Pool)
      • Thêm mới Tuyến IP (Bulk Import)
      • Cập nhật Thông tin máy chủ Proxy
      • Xóa Proxy (Thu hồi tài nguyên)
      • Kích hoạt Xoay vòng (Rotate IP) Cưỡng bức
    • 📈 System Intelligence
      • Báo cáo Luồng Bảo mật Hợp nhất (Colossal Report)
      • Bản Đồ Lưu Lượng GeoIP (Heatmap)
    • 🪝 Webhook Integrations
      • Callback Hoàn tất Gói Lưu lượng SEO (Traffic Node)
      • Callback Thông báo Hoàn tất Bắn Report AI
    • Schemas
      • AppError
      • LoginRequest
      • UserResponseDto
      • UserModel
      • CreateUserRequest
      • UpdateUserRequest
      • PagingInfo
      • ProfileModel
      • CreateProfileRequest
      • BulkImportProfileReq
      • UpdateProfileRequest
      • CampaignModel
      • CampaignConfigs
      • CreateCampaignRequest
      • UpdateCampaignRequest
      • BulkUpdateCampaignStatusReq
      • NegativeSeoData
      • CreateProjectRequest
      • UpdateProjectRequest
      • ProjectModel
      • BulkUpdateProjectRequest
      • ProjectAttribute
      • WorkerHandshakeRequest
      • WorkerHandshakeResponse
      • WorkerHeartbeatPayload
      • WorkerGlobalSettings
      • TaskPullRequest
      • WorkerFatalLog
      • TaskModel
      • TaskTrafficType
      • TaskUpdateDto
      • ReportTaskModel
      • ReportTaskResult
      • PCReportResponse
      • SummaryStats
      • DailyStats
      • BrandStats
      • ProfileStats
      • ReportTaskItem
      • ReportSiteItem
      • CreateReportSiteRequest
      • GPTConfig
      • ResponseConfig
      • FieldConfig
      • ReportDomainsResponse
      • ReportDomainItem
      • AddReportDomainRequest
      • UpdateReportDomainRequest
      • BulkUpdateDashboardRequest
      • ProxyModel
      • CreateProxyRequest
      • UpdateProxyRequest
      • SMTPServerConfig
      • EmailTaskLog
      • GeoLocation
  • Traffic Tools V2 API
    • Tasks
      • Lọc và Truy vấn Nhật ký Phiên Duyệt lẻ
      • Get all tasks
      • Nạp Kết Quả Chạy của Puppeteer (Update Callback)
      • Xóa toàn bộ Tasks lẻ
      • Get all tasks
      • Xóa một Task (ID)
    • Group task
      • Đẩy nhóm công việc Traffic theo lô (Batch Pipeline Task Creation)
      • Lấy sơ đồ trạng thái hàng đợi Group Tasks
      • Endpoint /api/task-traffics/
      • Clear/Flush Hàng đợi Group Task (Kill Queue)
      • Endpoint /api/task-traffics/
      • Xóa 1 nhóm nhiệm vụ (Theo ObjectID Mongoose)
      • Parse chuỗi Proxy
    • Test
      • Kiểm tra Proxy hoạt động ngầm
      • Endpoint /api/test
      • Kho Fingerprint Thử Nghiệm
      • Endpoint /api/test/account-stats
      • Endpoint /api/test/session-recommendation
      • Endpoint /api/test/check-browser
      • Endpoint /api/test/clear-browser-sessions
    • Proxies
      • Lấy kho Proxy server
      • Khai báo Pool Tuyến Proxy mới
      • Get all proxies
      • Get all proxies
      • Xóa toàn bộ Proxy
      • Get all proxies
      • Xóa Proxy theo ID
      • Reset lại số đếm tiến trình (Cron Proxy Thread)
    • Report Platforms
      • Submit report to multiple platforms
      • Submit report to specific platform
      • Get available platforms
      • Get platform statistics
      • Health check
    • Reports
      • Create a new report
      • Create a new report
      • Get report by ID
      • Get report by ID
      • Get report statistics
      • Get report groups
      • Get report groups
      • Get report group
      • Add report to group
      • Bulk add reports to group
      • Get report group statistics
      • Get analytics data
      • Get platform success rates
      • Get reports by platform
      • Get reports by group
      • Create test reports in bulk
    • Test Report
      • Test Microsoft report submission
      • Test AdGuard report submission
      • Test Google Ads report submission
      • Test Spamhaus report submission
      • Test ESET report submission
      • Test multi-platform report submission
      • Get available platforms
    • ESET Test
      • Simple test
      • Minimal POST test
      • Generate ESET report content
      • Get sample test data
      • Test form filling
      • Test full form submission
    • Spamhaus Test
      • Simple test
      • Minimal POST test
      • Generate Spamhaus report content
      • Get sample test data
      • Test Spamhaus form filling
      • Test full Spamhaus form submission
    • Report Scheduler
      • Get report scheduler statistics
      • Process ad detection result
      • Submit a scheduled report
      • Start report scheduler
      • Stop report scheduler
      • Get scheduler status
      • Process scheduled reports
      • Test browser session (non-headless)
      • Test report submission (non-headless)
    • Comprehensive Analytics
      • Get comprehensive dashboard analytics
      • Get overview statistics
      • Get brand-wise statistics
      • Get daily statistics
      • Get platform success rates
      • Get account performance
      • Get summary report
    • Search
      • Ra lệnh Máy Lọc Dữ Liệu TOP SEO Hàng Loại (Scraper Engine)
      • Đếm tổng số dòng tìm kiếm (Length)
    • Report Tasks
      • Tạo chiến dịch Report (Auto Spawn 14 tasks)
      • Tạo chiến dịch Report Form hàng loạt (Bulk Abuse Report)
      • Create Direct Report (Từ API hệ thống thay vì Frontend)
      • Tạo Nhiệm Vụ Sinh Email Khiếu nại (Automated Bulk Mail Spam)
      • Worker Pull - Lấy task Báo cáo đang Pending
      • Worker Trả kết quả (Report Status)
      • Danh mục Platfoms & Providers
      • Dashboard Statistic Báo Cáo
      • Force Run Cronjob
    • Monitor
      • Xuất dữ liệu đo đếm Hardware (Telemetry OS Watchdog)
    • Schemas
      • CreateTask
      • UpdateTask
      • CreateTaskGroupRequest
      • CreateProxy
      • CreateSearch
      • ReportTaskCreation
      • CreateEmail
      • CreateMultipleSearchRequest
      • CreateMultipleReportRequest
      • UpdateReportStatusRequest
      • TestProxyRequest
  1. Bussiness | Logic

3. Tìm kiếm tự động (Automated Discovery)

TÀI LIỆU ĐẶC TẢ KỸ THUẬT: MODULE TÌM KIẾM TỰ ĐỘNG (AUTO SEARCH)#

Route: /auto-search
Source Frontend: admin/src/views/auto-search/
Source Backend: api/internal/keyword/
Database chính: MongoDB (collection keyword, keyword_result)
Database phụ: MySQL (bảng projects, project_details, thông qua ConfirmFire)

MỤC LỤC#

I. Tổng quan chức năng
II. Kiến trúc và luồng dữ liệu chính
III. Giao diện (IndexView và các sub-tab)
IV. Tab Google Ads (GoogleAds.vue)
V. Tab CocCoc Ads (CocCocAds.vue)
VI. Modal Tạo Keyword (CreateModal.vue)
VII. Modal Chỉnh sửa Keyword (UpdateModal.vue)
VIII. Modal Smart Import (SmartImportModal.vue)
IX. Pinia Store (useStore.ts)
X. API Service (autoSearch.ts)
XI. Backend: Handler Layer
XII. Backend: Service Layer — Tạo Keyword
XIII. Backend: Service Layer — Tìm kiếm (SearchKeyword)
XIV. Backend: Service Layer — Lấy danh sách (GetList)
XV. Backend: Service Layer — Confirm Fire (Xác nhận bắn)
XV.1. confirm_fire.go — Entry Point
XV.2. confirm_fire_config.go — Đọc cấu hình
XV.3. confirm_fire_projects.go — Tìm project đang chạy
XV.4. confirm_fire_traffic.go — Tính toán traffic
XV.5. confirm_fire_creation.go — Tạo project
XVI. Backend: MongoDB Repository
XVII. Model dữ liệu (keyword.go)
XVIII. Phân quyền truy cập
XIX. Điều gì xảy ra nếu cấu hình sai
XX. Bảng tra cứu nhanh: Engine, Device, OS

I. Tổng quan chức năng#

Module Tìm kiếm tự động phục vụ mục tiêu theo dõi và phân tích kết quả tìm kiếm của Google và CocCoc một cách tự động, không cần biết trước URL mục tiêu. Thay vì Admin phải tự vào Google kiểm tra xem ai đang chạy quảng cáo hay SEO cho một từ khóa nào đó, module này thực hiện điều đó tự động theo lịch.
Luồng tổng quát:
1.
Admin thêm từ khóa cần theo dõi (ví dụ: "mb88", "nhà cái uy tín") vào MongoDB thông qua CreateModal.
2.
Hệ thống Backend Go tự động lập lịch (hoặc bắn ngay lập tức) các task tìm kiếm cho Worker.
3.
Worker (Node.js chạy trình duyệt thật) thực hiện tìm kiếm trên Google hoặc CocCoc, bóc tách toàn bộ kết quả (cả Ads và kết quả tự nhiên).
4.
Worker gửi kết quả về API qua Webhook URL.
5.
Backend lưu kết quả vào MongoDB collection keyword_result.
6.
Giao diện Frontend tự động refresh mỗi 10 giây để hiển thị kết quả mới nhất, phân loại theo Ads/SEO.
7.
Khi Admin thấy một domain xuất hiện trong kết quả và muốn bắn traffic vào đó, Admin nhấn nút "Xác nhận bắn" (Confirm Fire).
8.
Backend Go tự động tạo một Project mới trong MySQL (giống hệt như tạo thủ công trên Dashboard1), liên kết với chiến dịch mặc định, và kích hoạt ngay lập tức.
Module này có 4 sub-tab:
google-search — Theo dõi Google (engine value = 1)
coccoc-search — Theo dõi CocCoc (engine value = 2)
server-performance — Giám sát hiệu năng node Worker (chỉ một số user được phép)
location — Quản lý danh sách địa điểm địa lý phục vụ proxy routing

II. Kiến trúc và luồng dữ liệu chính#

[Admin click "Thêm từ khóa"]
        |
        v
[CreateModal.vue] --> POST /api/keywords
        |
        v
[handler/create_keyword.go] --> service.ProcessKeywords()
        |
        v
[service/create_keyword.go]
  - Kiểm tra từ khóa đã tồn tại chưa (repository.FindByKeyword)
  - Tạo mới vào MongoDB (repository.CreateMany)
  - Nếu status = 1 (Enable), gọi ngay SearchKeyword()
        |
        v
[service/search_keyword.go] -- createTaskSearchKeyword()
  - Lấy random Location từ MySQL bảng locations
  - Với mỗi keyword × engine × device × deviceOS → tạo 1 task
  - Đẩy tất cả task vào keyword_pool_task (MongoDB)
        |
        v
[Worker Node] nhận task từ pool
  - Mở trình duyệt với Proxy (lấy qua env.WebhookProxyGetProxyUrl)
  - Tìm kiếm từ khóa trên Engine tương ứng
  - Bóc tách kết quả Ads (.googleadservices.com link) và Search
  - POST kết quả về Webhook URL: env.WebhookSearchKeywordUrl/{keyword_id}
        |
        v
[Backend nhận webhook] --> Lưu vào MongoDB collection keyword_result
        |
        v
[Frontend polling mỗi 10 giây]
  GET /api/keywords?page=1&limit=200&engine=1
  --> Hiển thị danh sách domain theo từng keyword
        |
        v
[Admin chọn domain, nhấn "Xác nhận bắn"]
  POST /api/keywords/confirm-fire
        |
        v
[ConfirmFire Service] --> Tạo Project trong MySQL
  --> Cập nhật confirm_status = true trong MongoDB

III. Giao diện (IndexView và các sub-tab)#

File: admin/src/views/auto-search/IndexView.vue
IndexView là container đơn giản, chứa thanh tab và <RouterView>. Nó không tự fetch dữ liệu mà để mỗi sub-tab tự quản lý vòng đời của mình.
Thanh tab:
Thanh tab hiển thị dạng pill navigation, màu nền là #09484a0d (xanh đen mờ). Khi active, tab đổi sang nền #0a4e4f và chữ trắng.
Kiểm soát truy cập Server Performance:
Tab Server Performance chỉ render nếu:
Danh sách này là hard-code trong file. Nếu cần thêm user mới có quyền xem, phải sửa trực tiếp file này.
Emit tabChange:
IndexView emit sự kiện tabChange với payload là route.name hiện tại mỗi khi route thay đổi. Sự kiện này được các component cha (layout) dùng để pause/resume interval refresh khi người dùng chuyển tab.

IV. Tab Google Ads (GoogleAds.vue)#

File: admin/src/views/auto-search/components/GoogleAds.vue
Component này hiển thị danh sách từ khóa Google và kết quả quét. Nó có hai chế độ hiển thị:
Chế độ bình thường: autoSearchStore.select2h === 0 — Hiển thị danh sách keyword dạng NCollapse.
Chế độ 2h: autoSearchStore.select2h === 1 — Ẩn danh sách chính, hiển thị component GoogleAds2h (xem kết quả 2 giờ gần nhất theo định dạng khác).
Lifecycle:
Khi component mount (onBeforeMount):
1.
Đặt state.isPending = true.
2.
Đặt autoSearchStore.engine = 1 (đánh dấu đang ở tab Google).
3.
Đặt autoSearchStore.isFetching2h = true.
4.
Gọi song song autoSearchStore.fetchDataGoogleAds() và autoSearchStore.fetchLocation().
5.
Khi cả hai hoàn tất, đặt state.isPending = false.
Khi component unmount (onUnmounted):
1.
Gọi autoSearchStore.$reset() — Xóa toàn bộ state về giá trị mặc định.
2.
Gọi pause() — Dừng interval refresh ngay lập tức.
Interval Refresh Logic (useIntervalFn):
Cứ mỗi 10.000ms (10 giây), nếu urlParams.page hoặc urlParams.limit tồn tại hoặc engine là 1:
Gọi fetchDataGoogleAds(true) — true là tham số silent, nghĩa là không hiển thị loading spinner, làm mới ngầm.
Sau khi fetch xong, kiểm tra data.every(item => !item.is_running). Nếu không còn keyword nào đang chạy thì gọi pause() để dừng interval (tránh tải server vô ích).
Với mỗi item không còn is_running, đặt item.is_loading = false và state.isLoading[item.id] = false.
Keyboard Shortcut:
Hệ thống có một Easter egg: tổ hợp phím A + D + S (được detect qua useMagicKeys() của VueUse) sẽ toggle biến autoSearchStore.isFilterCC. Khi bật, hệ thống hiển thị thêm cột phân loại engine trong bảng kết quả, hữu ích khi debug cross-engine data.
Watch listDataGoogle:
Mỗi khi listDataGoogle.data thay đổi:
Cập nhật expandedNames = tất cả ID của keyword (mở hết NCollapse cha).
Cập nhật expandedChildren = tất cả ID của từng history_keyword entry (mở hết NCollapse con).
Điều này đảm bảo khi data mới về, UI tự động expand để người dùng thấy kết quả ngay mà không cần click.
các Modal được render trong component này:
CreateModal — Mở khi stateModal.isShowCreateModal = true.
CreateProjectModal — Mở khi stateModal.isShowCreateProjectModal = true.
UpdateModal — Mở khi stateModal.isShowUpdateModal = true. Khi mở, autoSearchStore.idEditKeyword phải đã được set bằng ID keyword cần sửa.
SmartImportModal — Controlled bởi biến local isShowSmartImportModal (không qua store).

V. Tab CocCoc Ads (CocCocAds.vue)#

File: admin/src/views/auto-search/components/CocCocAds.vue
Cấu trúc gần như giống hệt GoogleAds.vue, nhưng có một số điểm khác biệt quan trọng:
Khác biệt so với GoogleAds:
1.
Khi mount, đặt autoSearchStore.engine = 2 thay vì 1.
2.
Gọi fetchDataCocCocAds() thay vì fetchDataGoogleAds().
3.
Interval refresh gọi fetchDataCocCocAds(true) thay vì fetchDataGoogleAds(true).
4.
Component item là CocCocAdsKeywordItem thay vì GoogleAdsKeywordItem.
5.
Không có SmartImportModal — Tab CocCoc không hỗ trợ Smart Import.
6.
onChangeFilter dùng useDebounceFn với delay 500ms, trong khi GoogleAds dùng useGoogleAdsFilters composable riêng.
Lý do CocCoc tách riêng thay vì dùng chung component:
CocCoc có cấu trúc response và logic hiển thị kết quả Ads khác với Google (do CocCoc có định dạng riêng cho sponsored results). Việc tách component giúp customize riêng mà không ảnh hưởng đến Google tab.

VI. Modal Tạo Keyword (CreateModal.vue)#

File: admin/src/views/auto-search/components/CreateModal.vue
Đây là modal thêm từ khóa mới vào hệ thống theo dõi.
Các trường input:
1.
Từ khóa (textarea):
Mỗi dòng là một từ khóa.
Khi người dùng nhập, hàm onInputNames split theo \n, filter dòng rỗng, lưu vào formData.keyword dưới dạng string[].
Lưu ý: formData.keyword là mảng để backend có thể tạo hàng loạt từ khóa trong một request.
2.
Số kết quả (result_num):
Số lượng kết quả tìm kiếm muốn lấy (mặc định: 10).
Ví dụ: Nếu đặt là 20, Worker sẽ cuộn xuống và lấy tất cả kết quả trong Top 20 của trang tìm kiếm.
3.
Tìm kiếm trên (engine):
NSelect multiple, giá trị: 1 = Google, 2 = Coccoc.
Có thể chọn cả hai, khi đó hệ thống sẽ tạo task riêng cho từng engine.
4.
Thiết bị (device):
NSelect multiple với 4 option:
0 = Desktop (Window)
1 = Desktop (Macbook)
2 = Mobile (Android)
3 = Mobile (IOS)
Quan trọng: Option này không trực tiếp map vào device và device_os của MongoDB. Trước khi gửi, code chuyển đổi như sau:
Kết quả: device chỉ có thể là [0], [1], hoặc [0, 1]. device_os chứa phân loại OS theo từng device type.
5.
Thứ tự ưu tiên (sort):
NSelect với giá trị 1-5, mặc định: 1.
Trường này có ảnh hưởng trực tiếp đến lượng traffic khi Confirm Fire. Xem chi tiết tại XV.4.
6.
Trạng thái (status):
NSwitch: 1 = Hoạt động, 0 = Không hoạt động.
Nếu tạo với status = 1, hệ thống sẽ bắn task ngay lập tức sau khi lưu.
Nếu tạo với status = 0, keyword được lưu nhưng chưa có task nào chạy.
Flow submit:
1.
Validate form (NForm).
2.
Build payload formDataCreate từ formData.
3.
Gọi apiAutoSearch.create(formDataCreate) — POST về /keywords.
4.
Nếu thành công: reset form, emit success, đóng modal.
5.
Nếu lỗi: gọi showMessageError(error).

VII. Modal Chỉnh sửa Keyword (UpdateModal.vue)#

File: admin/src/views/auto-search/components/UpdateModal.vue
Modal edit có cấu trúc tương tự Create, nhưng có các điểm khác biệt:
1.
Khi mount (onMounted): Tự động gọi fetchData() → apiAutoSearch.getDetail(autoSearchStore.idEditKeyword) để load dữ liệu hiện tại.
2.
Trường Từ khóa là readonly và disabled: Không thể sửa từ khóa sau khi tạo. Đây là thiết kế có chủ ý để tránh breaking các keyword_result đang liên kết.
3.
Mapping ngược khi load dữ liệu:
4.
Submit: Gọi apiAutoSearch.update(autoSearchStore.idEditKeyword, formDataUpdate) — PUT về /keywords/{id}.

VIII. Modal Smart Import (SmartImportModal.vue)#

File: admin/src/views/auto-search/components/SmartImportModal.vue
Smart Import là tính năng nhập hàng loạt. Nó không tạo keyword vào MongoDB như CreateModal — thay vào đó, nó trực tiếp tạo Projects vào MySQL (bỏ qua bước trinh sát tự động).
Trường cấu hình:
1.
Loại Traffic (importType):
Radio: kill (Google Kill) hoặc home (Home Traffic).
Giá trị này ảnh hưởng đến cờ is_kill của project:
kill → is_kill = true
home → is_kill = false
2.
Tổng Traffic mỗi dự án (totalTraffic):
NInputNumber, mặc định: 2000.
Sẽ trở thành total_process của mỗi project tạo ra.
3.
Dữ liệu nhập (inputText):
Textarea tự do, người dùng dán dữ liệu thô vào.
Cách Parser đọc dữ liệu:
Logic parse trong hàm handleImport:
Ví dụ định dạng dữ liệu nhập hợp lệ:
chạy trên desktop
- mb88 https://mb88.net/
- w88 https://w88.com/

chạy trên mobile
- bet88 https://bet88.me/
Kết quả: 2 project chạy Desktop, 1 project chạy Mobile, engine Google VN, traffic cố định theo totalTraffic.
Lưu ý quan trọng:
campaign_id lấy từ useCampaignStore().campaignId. Nếu store này chưa có giá trị (user chưa chọn campaign nào), fallback cứng về 11.
organic_engine luôn là 0 (Google VN), không tùy chỉnh được trong modal này.
Attribute time_on_page cố định 5-10 giây — rất thấp, chỉ phù hợp click Ads thuần túy.
Toàn bộ quá trình gọi API tuần tự (không parallel) để tránh overload server.

IX. Pinia Store (useStore.ts)#

File: admin/src/views/auto-search/stores/useStore.ts
Store autoSearchStore quản lý toàn bộ state của module.
State đầy đủ:
resetUrlParams:
Store sử dụng Zod để validate và ép kiểu tham số URL:
Điều này giúp tránh lỗi khi URL bị tamper hoặc có giá trị không hợp lệ. limit mặc định là 200, tức là API luôn trả về tối đa 200 keyword trong một request.

X. API Service (autoSearch.ts)#

File: admin/src/services/api/autoSearch.ts
Base prefix: /keywords
MethodEndpointMô tảPayload/Params
GET/keywordsLấy danh sách keyword kèm kết quả{ page, limit, keywords, status }
GET/campaign/fired/ggLấy thông tin campaign Google dùng để Fire-
GET/campaign/fired/ccLấy thông tin campaign CocCoc dùng để Fire-
GET/keywords/{id}Lấy chi tiết một keyword-
POST/keywordsTạo keyword mới{ keyword[], engine[], device[], device_os, result_num, status, sort }
PUT/keywords/{id}Cập nhật keywordTương tự Create
PUT/keywords/update-status/{id}Cập nhật chỉ trạng thái`{ status: 0
POST/keywords/confirm-fireTạo project từ kết quả scan{ keyword_id, domain, type_data, device, device_os, ... }
GET/keywords/{id}/results/Xem lịch sử kết quả scan của keyword{ date, device, device_os, url }
POST/keywords/reset/{id}Xóa toàn bộ kết quả scan, chạy lại-
POST/keywords/reset-allReset tất cả keyword-
DELETE/keywords/{id}Xóa keyword-
GET/keywords/export-excelXuất Excel báo cáo-
Endpoint đặc biệt:
PUT /keyword-result/update-search-data/{id} → Cập nhật trạng thái của một domain cụ thể trong kết quả (không thuộc prefix /keywords).

XI. Backend: Handler Layer#

File: api/internal/keyword/handler/handler.go
Handler đăng ký tất cả route dưới /keywords:
Handler inject hai service: keywordService và activityLogService.
Handler UpdateStatus (update_status.go):
Quy trình xử lý:
1.
Lấy :id từ URL path, parse thành primitive.ObjectID.
2.
Parse body thành dto.UpdateStatusRequest { Status int }.
3.
Validate bằng validate.Validate(dto).
4.
Kiểm tra keyword tồn tại: handler.keywordService.FindByID(objectId).
5.
Gọi handler.keywordService.UpdateStatus(dto.Status, objectId).
6.
Trả về 200 kèm thông báo thành công.
Handler GetKeywordResultsByKeywordID (get_keyword_results_by_keyword_id.go):
Endpoint này trả về lịch sử quét của một keyword cụ thể. Hỗ trợ filter theo:
date (format 2006-01-02)
device (int)
device_os (int)
url (string) — lọc kết quả theo URL cụ thể
Kết quả là mảng []KeywordSearchDataAdsResult, mỗi phần tử chứa SearchData với cả Ads và Search results.

XII. Backend: Service Layer — Tạo Keyword#

File: api/internal/keyword/service/create_keyword.go
Hàm ProcessKeywords:
Nhận dto.CreateKeyword và userID, trả về:
keywordExist []string — danh sách từ khóa đã tồn tại, không tạo lại.
newKeywords []keyword.Keyword — danh sách object sẵn sàng để insert MongoDB.
Với mỗi keyword trong params.Keyword:
1.
Bỏ qua nếu là chuỗi rỗng.
2.
Kiểm tra tồn tại bằng repository.FindByKeyword(keyword).
3.
Nếu chưa tồn tại: tạo keyword.Keyword object với primitive.NewObjectID() và strings.TrimSpace(keyword).
Hàm Create:
Wrapper của saveNewKeywords + trigger SearchKeyword:
Quan trọng: SearchKeyword được gọi đồng bộ (không goroutine), nên response có thể hơi chậm khi tạo nhiều keyword cùng lúc vì phải tạo hết task trước khi return.

XIII. Backend: Service Layer — Tìm kiếm (SearchKeyword)#

File: api/internal/keyword/service/search_keyword.go
Đây là hàm cốt lõi tạo task thực thi cho Worker.
Hàm SearchKeyword(keywords_param ...keyword.Keyword):
1.
Nếu không truyền parameter, tự động lấy tất cả keyword đang active từ MongoDB (repository.GetAllActiveKeywords()). Đây là mode được dùng bởi Cron Job định kỳ (e.g. mỗi 30 phút hệ thống scan lại tất cả keyword).
2.
Lấy ngẫu nhiên một Location từ MySQL (locationRepository.FindRandom()). Location bao gồm GeoLocation Lat/Lng dùng để fake vị trí GPS và routing proxy theo vùng.
3.
Gọi createTaskSearchKeyword() để tạo danh sách task.
4.
Gọi keywordPoolTaskService.CreateMany(tasks) để đẩy task vào pool MongoDB.
Hàm createTaskSearchKeyword:
Với mỗi keyword, tạo task cho mọi tổ hợp engine × device × deviceOS:
Ví dụ: Keyword "mb88" có Engine=[1,2], Device=[0,1], desktop DeviceOS=[0,1], mobile DeviceOS=[0] sẽ tạo ra:
Engine1/Desktop/Win, Engine1/Desktop/Mac, Engine1/Mobile/Android
Engine2/Desktop/Win, Engine2/Desktop/Mac, Engine2/Mobile/Android
= 6 task riêng lẻ.
Worker nhận từng task và thực hiện độc lập. Kết quả về qua Webhook URL /webhookSearchKeyword/{keyword_id}.

XIV. Backend: Service Layer — Lấy danh sách (GetList)#

File: api/internal/keyword/service/get_list.go
Luồng xử lý:
1.
Gọi repository.GetList(dto, paging) → Lấy danh sách keyword từ MongoDB.
2.
Gọi keywordResultRepository.GetSearchDataByKeywordIDs(keywords) → Một query batch để lấy tất cả kết quả scan gần nhất cho mọi keyword, tránh N+1 query.
3.
Với mỗi keyword, merge kết quả Ads và Search thành một mảng searchData thống nhất:
4.
Build KeywordsResponse cho mỗi keyword bao gồm keyword.IsRunning (để UI biết có cần hiện spinner không).
Trường Type trong SearchResult:
Phân biệt domain là Ads hay SEO tự nhiên. Frontend dùng trường này để tô màu khác nhau trên UI (Ads thường là màu cam/vàng, SEO là màu xanh).
Trường ConfirmStatus:
true = Domain này đã được Admin bấm "Xác nhận bắn" và một Project đã tồn tại. UI sẽ hiển thị nhãn "Đã xác nhận" thay vì nút "Bắn".

XV. Backend: Service Layer — Confirm Fire (Xác nhận bắn)#

Đây là tính năng phức tạp nhất của module, kết nối MongoDB sang MySQL. Toàn bộ logic được chia thành 5 file.

XV.1. confirm_fire.go — Entry Point#

File: api/internal/keyword/service/confirm_fire.go
Đây là hàm điều phối, gọi các hàm helper theo thứ tự:

XV.2. confirm_fire_config.go — Đọc cấu hình#

File: api/internal/keyword/service/confirm_fire_config.go
Hàm getCampaignConfig:
Đọc cấu hình từ bảng setup_fire (MySQL) và biến môi trường:
Điều quan trọng: configTotalProcess là số traffic mặc định PER PROJECT, lấy từ biến môi trường. Số này có thể bị điều chỉnh xuống trong hàm tính toán traffic nếu đã có project khác đang chạy.
Hàm isProjectRunning:
Project "đang chạy" khi: Status=1 VÀ State không phải Dead VÀ TotalProcess > 0.

XV.3. confirm_fire_projects.go — Tìm project đang chạy#

File: api/internal/keyword/service/confirm_fire_projects.go
Hàm getRunningProjects(campaign):
Lấy danh sách project đang active trong campaign của type "fired":
1.
Duyệt campaign.Projects.
2.
Với mỗi project có Status == 1, truy vấn chi tiết từ MySQL (projectRepository.GetOne).
3.
Kiểm tra State != State_Dead.
4.
Build project.Project đầy đủ và append vào kết quả.
Danh sách runningProjects này được dùng trong bước tính toán traffic để phân bổ lại.

XV.4. confirm_fire_traffic.go — Tính toán traffic#

File: api/internal/keyword/service/confirm_fire_traffic.go
Đây là logic thông minh nhất trong toàn bộ module. Thay vì cứ cho project mới nhận đủ configTotalProcess, hệ thống tự động phân bổ lại traffic cho tất cả project trong cùng campaign để tổng traffic không vượt quá pool.
Hàm calculateTotalProcess:
Ví dụ thực tế:
configTotalProcess = 1000 (biến môi trường).
Campaign Google đang có 2 project chạy với TotalProcess = 500 mỗi.
Admin fire project thứ 3 với cùng engine và newSort <= maxRunningSort.
Hệ thống tính: ceil(1000 / (2+1)) = 334.
Cả 2 project cũ bị update xuống 334 traffic mỗi.
Project mới tạo với 334 traffic.
Tổng: 334 × 3 = 1002 ≈ 1000 (làm tròn lên).
Hàm getMaxRunningSort:
Tìm giá trị Sort lớn nhất trong số các project đang chạy để làm ngưỡng so sánh.

XV.5. confirm_fire_creation.go — Tạo project#

File: api/internal/keyword/service/confirm_fire_creation.go
Hàm createNewProject:
Tạo object campaign.Project với toàn bộ thông tin cần thiết:
Quan trọng về các flag cố định:
OnlyAds = true — Project từ Confirm Fire luôn là "Click Ads" mode. Không có cách nào tạo Home Traffic hay Kill Traffic qua Confirm Fire.
IsKill = false — Tương tự.
TrafficType = 1 — Luôn là Search traffic (không phải Direct/Home).
Hàm createProjectDetailForNewProject:
Ngay sau khi tạo project, hàm này tạo một project_detail record cho giờ hiện tại:
Record này có TotalProcess = 0 ban đầu. Cron Job tạo task sẽ cập nhật nó sau. Mục đích tạo ngay là để Dashboard1 không bị thiếu record khi query trong cùng ngày.
Sau khi project được lưu:
Đặt confirm_status = true cho domain đó trong MongoDB để UI hiện "Đã xác nhận".

XVI. Backend: MongoDB Repository#

File: api/internal/keyword/repository/repository.go
Repository làm việc trực tiếp với MongoDB driver (go.mongodb.org/mongo-driver/mongo).
Các operation chính:
GetList(dto, paging) — Find với filter status/engine, sort, skip/limit.
FindByKeyword(keyword) — FindOne để check duplicate.
CreateMany(keywords) — InsertMany.
GetKeywordByID(id) — FindOne theo ObjectID.
GetAllActiveKeywords() — Find tất cả keyword có status = 1.

XVII. Model dữ liệu (keyword.go)#

File: api/internal/keyword/keyword.go
Struct Keyword (Lưu trong MongoDB collection "keyword"):
Constants Engine:
Constants Device:
Constants DeviceOS:
Struct KeywordsResponse (Trả về API, có kèm results):
Struct KeywordSearchDataResult (Một batch kết quả theo device+engine):

XVIII. Phân quyền truy cập#

Tab Server Performance bị ẩn với user thông thường. Danh sách user được phép xem hard-code trong IndexView.vue:
Điều kiện render tab:
userAllow.includes(auth.profile?.name) || auth.profile?.email === 'tron@tron.com'
Các tab còn lại (google-search, coccoc-search, location) không có hạn chế.

XIX. Điều gì xảy ra nếu cấu hình sai#

1. Tạo keyword với status = 0 rồi muốn activate:
Bật Toggle trạng thái trên UI → gọi PUT /keywords/update-status/{id}.
Backend cập nhật status = 1 trong MongoDB.
Tuy nhiên, kích hoạt status không tự trigger SearchKeyword(). Hệ thống chỉ bắn task vào lần scan Cron Job tiếp theo (hoặc khi gọi Reset).
Nếu muốn scan ngay: Nhấn nút Reset trên keyword đó.
2. Chọn result_num quá cao (ví dụ 50):
Worker phải cuộn qua nhiều trang kết quả hơn, tốn thêm thời gian.
Proxy dễ bị detect hơn do thời gian session dài.
Khuyến nghị: Dùng 10-20 cho hầu hết từ khóa.
3. Confirm Fire cho domain có newSort > maxRunningSort:
calculateTotalProcess trả về 0.
Project được tạo với TotalProcess = 0.
Project này sẽ không có task nào chạy (vì isProjectRunning kiểm tra TotalProcess > 0).
Trên Dashboard1, project này hiển thị nhưng không có traffic nào.
Cách fix: Vào Dashboard1, sửa TotalProcess thủ công cho project đó.
4. setup_fire không được cấu hình đúng:
getCampaignConfig trả về campaignID = 0.
ConfirmFire trả về lỗi "campaign not found" ngay ở bước đầu.
API trả về 500 với message "Lỗi khi xác nhận bắn".
Cần vào bảng setup_fire trong MySQL và cấu hình CampaignGGID và CampaignCCID.
5. Smart Import với dòng không có URL:
Parser bỏ qua dòng đó, ghi log lỗi màu đỏ: Skipped: No URL found in "..."
Các dòng khác vẫn được xử lý bình thường.

XX. Bảng tra cứu nhanh: Engine, Device, OS#

Engine values (MongoDB và MySQL):
Giá trịTênPhạm viGhi chú
0Google VNgoogle.com.vnKết quả địa phương, phù hợp SEO Việt Nam
1Google INTgoogle.comKết quả quốc tế
2CocCoccoccoc.comDùng riêng cho tab CocCoc Ads
Device values:
Giá trịTênDeviceOS DesktopDeviceOS Mobile
0Desktop0=Windows, 1=MacOS-
1Mobile-0=Android, 1=iOS
Mapping từ UI CreateModal sang MongoDB:
UI OptionUI Valuedevice[]device_os.desktopdevice_os.mobile
Desktop (Window)0[0][0]-
Desktop (Macbook)1[0][1]-
Mobile (Android)2[1]-[0]
Mobile (IOS)3[1]-[1]
Window + Macbook + Android0,1,2[0,1][0,1][0]
Trạng thái Project sau ConfirmFire:
TrườngGiá trị cố địnhLý do
only_adstrueLuôn là Click Ads mode
is_killfalseConfirmFire không hỗ trợ Kill mode
traffic_type1Luôn là Search (có từ khóa)
status1Chạy ngay lập tức
stateState_UnknownChưa biết trạng thái domain
Modified at 2026-03-23 05:18:34
Previous
2. Thống kê hệ thống (System Statistics)
Next
4. Trung tâm Báo cáo (Report Center)
Built with