Update: 2026-08-03 13:22:27
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
// ============================================================
|
||||||
|
// Admin/marketing/get_social_reports.php
|
||||||
|
// تقارير الذكاء الاجتماعي للوحة التحكم
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// لماذا نقطة جديدة بدل استعمال marketing_engine/index.php?action=get_reports:
|
||||||
|
//
|
||||||
|
// • تلك نقطة **البوت** لا نقطة إدارية: تُصادَق بترويسة X-Bot-Token
|
||||||
|
// المخبوزة في APK، وتخدم طابور المهام. استعمالها من اللوحة يعني إما
|
||||||
|
// تسريب توكن البوت إلى المتصفح، أو إبقاء النقطة مفتوحة للجميع —
|
||||||
|
// وقد كانت مفتوحة فعلاً حتى فُعّلت المصادقة، فصارت تُرجع 401 للّوحة.
|
||||||
|
//
|
||||||
|
// • الشاشة كانت تستدعيها بـ http.get الخام بلا JWT ولا X-Device-FP،
|
||||||
|
// فلا تظهر في سجل CRUD ولا تخضع لأي تحقّق صلاحيات.
|
||||||
|
//
|
||||||
|
// هذه النقطة تحت /Admin فتغطّيها البوّابة الافتراضية في connect.php.
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$limit = (int) (filterRequest('limit', 'int') ?? 50);
|
||||||
|
$limit = max(1, min(200, $limit));
|
||||||
|
|
||||||
|
// is_weekly=1 لتقارير الأسبوع فقط، أو 0 للحظية، أو تُترك فارغة للكل
|
||||||
|
$weeklyOnly = filterRequest('weekly');
|
||||||
|
|
||||||
|
$where = '';
|
||||||
|
$params = [];
|
||||||
|
if ($weeklyOnly !== null && $weeklyOnly !== '') {
|
||||||
|
$where = 'WHERE is_weekly = :weekly';
|
||||||
|
$params[':weekly'] = ((string) $weeklyOnly === '1') ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $con->prepare("
|
||||||
|
SELECT id, platform, report_html, is_weekly, created_at
|
||||||
|
FROM marketing_reports
|
||||||
|
$where
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT $limit
|
||||||
|
");
|
||||||
|
$stmt->execute($params);
|
||||||
|
$reports = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($reports as &$r) {
|
||||||
|
$r['id'] = (int) $r['id'];
|
||||||
|
$r['is_weekly'] = (int) $r['is_weekly'] === 1;
|
||||||
|
}
|
||||||
|
unset($r);
|
||||||
|
|
||||||
|
jsonSuccess($reports);
|
||||||
|
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// الجدول قد يكون غير موجود على نشر لم يُطبَّق عليه الترحيل
|
||||||
|
// 2026_08_03_create_marketing_engine_tables.sql — نُبلغ بوضوح بدل
|
||||||
|
// إرجاع خطأ عام يُقرأ كـ "لا بيانات".
|
||||||
|
error_log('[get_social_reports] ' . $e->getMessage());
|
||||||
|
jsonError('تعذّر جلب تقارير الذكاء الاجتماعي. راجع سجل الخادم.', 500);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- marketing_tasks + marketing_reports — جدولا محرّك التسويق والبوت الاجتماعي
|
||||||
|
-- ============================================================
|
||||||
|
-- كلا الجدولين مستعمل في الكود ولا وجود لهما في schema_primary.sql — نفس
|
||||||
|
-- حالة admin_audit_log بالضبط. الأثر:
|
||||||
|
--
|
||||||
|
-- • marketing_tasks هو طابور مهام البوت الاجتماعي:
|
||||||
|
-- يكتب فيه cron_insert_task.php (كل 4 ساعات) و insert_autonomous_task.php
|
||||||
|
-- ويقرأ منه marketing_engine/index.php ليخدم socialBot.
|
||||||
|
-- بغيابه لا يستلم البوت مهمة واحدة أبداً، ويفشل الكرون بصمت.
|
||||||
|
--
|
||||||
|
-- • marketing_reports هو مصدر شاشة "الذكاء الاجتماعي":
|
||||||
|
-- يكتب فيه telegram_scraper.php (كل 3 ساعات) و cron_weekly_report.php
|
||||||
|
-- و index.php (evaluate_posts).
|
||||||
|
-- بغيابه تظهر الشاشة فارغة دائماً — وهو ما لوحظ.
|
||||||
|
--
|
||||||
|
-- الأعمدة مستخرجة من مواضع الاستعمال الفعلية في:
|
||||||
|
-- marketing_engine/index.php, cron_insert_task.php, cron_weekly_report.php,
|
||||||
|
-- insert_autonomous_task.php, telegram_scraper.php, insert_test_task.php
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `marketing_tasks` (
|
||||||
|
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`platform` VARCHAR(50) NOT NULL DEFAULT 'facebook',
|
||||||
|
`type` VARCHAR(100) NOT NULL,
|
||||||
|
-- 'pending' هي الحالة التي يبحث عنها get_task؛ أي قيمة أخرى تعني
|
||||||
|
-- أن المهمة قيد التنفيذ أو انتهت.
|
||||||
|
`status` VARCHAR(30) NOT NULL DEFAULT 'pending',
|
||||||
|
`target_url` TEXT DEFAULT NULL,
|
||||||
|
`content_text` TEXT DEFAULT NULL,
|
||||||
|
`media_url` TEXT DEFAULT NULL,
|
||||||
|
`error_message` TEXT DEFAULT NULL,
|
||||||
|
`completed_at` TIMESTAMP NULL DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
-- get_task يرشّح بـ status + platform ويرتّب بـ id
|
||||||
|
KEY `idx_status_platform` (`status`, `platform`),
|
||||||
|
KEY `idx_created_at` (`created_at`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `marketing_reports` (
|
||||||
|
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`platform` VARCHAR(50) NOT NULL DEFAULT 'all',
|
||||||
|
`report_html` LONGTEXT NOT NULL,
|
||||||
|
-- cron_weekly_report.php يكتب 1؛ التقارير اللحظية تترك 0
|
||||||
|
`is_weekly` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
-- get_reports يرتّب بـ created_at تنازلياً
|
||||||
|
KEY `idx_created_at` (`created_at`),
|
||||||
|
KEY `idx_is_weekly` (`is_weekly`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
@@ -413,6 +413,11 @@ class AppLink {
|
|||||||
static String withdrawalRequests =
|
static String withdrawalRequests =
|
||||||
"$paymentServerV2/ride/driverPayment/withdrawal_requests.php";
|
"$paymentServerV2/ride/driverPayment/withdrawal_requests.php";
|
||||||
|
|
||||||
|
// تقارير الذكاء الاجتماعي — نقطة إدارية بديلة عن
|
||||||
|
// marketing_engine/index.php (نقطة البوت المحميّة بـ X-Bot-Token)
|
||||||
|
static String get socialReports =>
|
||||||
|
"$server/Admin/marketing/get_social_reports.php";
|
||||||
|
|
||||||
// وحدة الطعام — الباك إند جاهز في backend/food/admin/ ولا واجهة له
|
// وحدة الطعام — الباك إند جاهز في backend/food/admin/ ولا واجهة له
|
||||||
static String get foodMerchants => "$server/food/admin/merchants.php";
|
static String get foodMerchants => "$server/food/admin/merchants.php";
|
||||||
static String get foodMerchantApprove =>
|
static String get foodMerchantApprove =>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ class DriverDetailsPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cs = Theme.of(context).colorScheme;
|
|
||||||
controller.getDriverDetails(driverId);
|
controller.getDriverDetails(driverId);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ class RegisterCaptain extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cs = Theme.of(context).colorScheme;
|
|
||||||
final controller = Get.put(RegisterCaptainController());
|
final controller = Get.put(RegisterCaptainController());
|
||||||
// String text = '';
|
// String text = '';
|
||||||
controller.driveInit();
|
controller.driveInit();
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:siro_admin/constant/theme.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart'; // For Get.width if needed, and .tr
|
import 'package:get/get.dart'; // For Get.width if needed, and .tr
|
||||||
import 'package:siro_admin/constant/colors.dart'; // Assuming AppColor is here
|
import 'package:siro_admin/constant/colors.dart'; // Assuming AppColor is here
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||||
import '../../../constant/links.dart';
|
import '../../../constant/links.dart';
|
||||||
|
import '../../../controller/functions/crud.dart';
|
||||||
|
import '../../widgets/snackbar.dart';
|
||||||
|
|
||||||
class SocialIntelligenceController extends GetxController {
|
class SocialIntelligenceController extends GetxController {
|
||||||
var isLoading = true.obs;
|
var isLoading = true.obs;
|
||||||
@@ -18,18 +19,32 @@ class SocialIntelligenceController extends GetxController {
|
|||||||
Future<void> fetchReports() async {
|
Future<void> fetchReports() async {
|
||||||
try {
|
try {
|
||||||
isLoading(true);
|
isLoading(true);
|
||||||
final url = Uri.parse(
|
|
||||||
'${AppLink.server}/marketing_engine/index.php?action=get_reports');
|
|
||||||
final response = await http.get(url);
|
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
// كانت تستدعي marketing_engine/index.php?action=get_reports بـ
|
||||||
final data = json.decode(response.body);
|
// http.get الخام: نقطة بوت محميّة بـ X-Bot-Token، بلا JWT ولا
|
||||||
if (data['status'] == 'success') {
|
// X-Device-FP، فترجع 401 بعد تفعيل مصادقة البوت. النقطة الإدارية
|
||||||
reports.value = data['data'] ?? [];
|
// البديلة تمرّ عبر CRUD فتحمل التوكن وبصمة الجهاز.
|
||||||
}
|
final res =
|
||||||
|
await CRUD().get(link: AppLink.socialReports, payload: {});
|
||||||
|
|
||||||
|
if (res == 'failure' || res == null) {
|
||||||
|
mySnackbarError('تعذّر الاتصال بخادم التقارير');
|
||||||
|
reports.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = res is String ? json.decode(res) : res;
|
||||||
|
if (data['status'] == 'success') {
|
||||||
|
// jsonSuccess يغلّف الحمولة في message لا data
|
||||||
|
final payload = data['message'] ?? data['data'];
|
||||||
|
reports.value = payload is List ? payload : [];
|
||||||
|
} else {
|
||||||
|
mySnackbarError('${data['message'] ?? 'تعذّر جلب التقارير'}');
|
||||||
|
reports.value = [];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching reports: $e');
|
mySnackbarError('$e');
|
||||||
|
reports.value = [];
|
||||||
} finally {
|
} finally {
|
||||||
isLoading(false);
|
isLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,7 +651,6 @@ class _LegendDot extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cs = Theme.of(context).colorScheme;
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:siro_admin/constant/theme.dart';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:siro_admin/constant/theme.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user