Преглед изворни кода

feat: 实现完整的Dashboard仪表盘功能

pudongliang пре 5 месеци
родитељ
комит
1768b01ae2

+ 73 - 0
portal/src/api/dashboard.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+/**
+ * 获取首页统计概览数据
+ */
+export function getStatisticsOverview() {
+  return request({
+    url: '/api/dashboard/statistics',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取视频相关统计数据
+ */
+export function getVideoStatistics() {
+  return request({
+    url: '/api/dashboard/video-statistics',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取图片相关统计数据
+ */
+export function getImageStatistics() {
+  return request({
+    url: '/api/dashboard/image-statistics',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取广告组相关统计数据
+ */
+export function getAdgroupStatistics() {
+  return request({
+    url: '/api/dashboard/adgroup-statistics',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取转化相关统计数据
+ */
+export function getConversionStatistics() {
+  return request({
+    url: '/api/dashboard/conversion-statistics',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取系统状态信息
+ */
+export function getSystemStatus() {
+  return request({
+    url: '/api/dashboard/system-status',
+    method: 'get'
+  })
+}
+
+/**
+ * 获取最近活动记录
+ * @param {Number} limit 限制数量,默认10
+ */
+export function getRecentActivities(limit = 10) {
+  return request({
+    url: '/api/dashboard/recent-activities',
+    method: 'get',
+    params: { limit }
+  })
+}

+ 1 - 1
portal/src/views/adgroup/index.vue

@@ -199,7 +199,7 @@ const handleQuery = async () => {
   try {
     const res = await request.get('/adgroups/list', { params })
     if (res.success) {
-      tableData.value = res.list || []
+      tableData.value = res.data || []
       total.value = res.total || 0
     } else {
       ElMessage.error(res.message || '查询失败')

+ 167 - 11
portal/src/views/dashboard/index.vue

@@ -22,7 +22,8 @@
           </div>
           <div>
             <p class="text-sm text-gray-500">视频素材</p>
-            <p class="text-2xl font-bold text-gray-900">128</p>
+            <p class="text-2xl font-bold text-gray-900">{{ statistics.videoCount }}</p>
+            <p v-if="statistics.todayVideoCount > 0" class="text-xs text-green-600 mt-1">今日新增: {{ statistics.todayVideoCount }}</p>
           </div>
         </div>
       </div>
@@ -36,7 +37,8 @@
           </div>
           <div>
             <p class="text-sm text-gray-500">图片素材</p>
-            <p class="text-2xl font-bold text-gray-900">86</p>
+            <p class="text-2xl font-bold text-gray-900">{{ statistics.imageCount }}</p>
+            <p v-if="statistics.todayImageCount > 0" class="text-xs text-green-600 mt-1">今日新增: {{ statistics.todayImageCount }}</p>
           </div>
         </div>
       </div>
@@ -50,7 +52,8 @@
           </div>
           <div>
             <p class="text-sm text-gray-500">广告组</p>
-            <p class="text-2xl font-bold text-gray-900">24</p>
+            <p class="text-2xl font-bold text-gray-900">{{ statistics.adgroupCount }}</p>
+            <p v-if="statistics.todayAdgroupCount > 0" class="text-xs text-green-600 mt-1">今日新增: {{ statistics.todayAdgroupCount }}</p>
           </div>
         </div>
       </div>
@@ -64,7 +67,8 @@
           </div>
           <div>
             <p class="text-sm text-gray-500">转化数据</p>
-            <p class="text-2xl font-bold text-gray-900">1,245</p>
+            <p class="text-2xl font-bold text-gray-900">{{ statistics.conversionCount }}</p>
+            <p v-if="statistics.todayConversionCount > 0" class="text-xs text-green-600 mt-1">今日新增: {{ statistics.todayConversionCount }}</p>
           </div>
         </div>
       </div>
@@ -116,15 +120,30 @@
     <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
       <h2 class="text-xl font-semibold text-gray-800 mb-6">系统状态</h2>
       <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
-        <div class="flex items-center p-4 bg-green-50 rounded-lg border border-green-200">
-          <div class="p-2 rounded-full bg-green-200 mr-4">
-            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-600" viewBox="0 0 20 20" fill="currentColor">
+        <div class="flex items-center p-4 rounded-lg border" :class="{
+          'bg-green-50 border-green-200': systemStatus.serviceStatus === 'running',
+          'bg-red-50 border-red-200': systemStatus.serviceStatus !== 'running'
+        }">
+          <div class="p-2 rounded-full mr-4" :class="{
+            'bg-green-200': systemStatus.serviceStatus === 'running',
+            'bg-red-200': systemStatus.serviceStatus !== 'running'
+          }">
+            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" :class="{
+              'text-green-600': systemStatus.serviceStatus === 'running',
+              'text-red-600': systemStatus.serviceStatus !== 'running'
+            }" viewBox="0 0 20 20" fill="currentColor">
               <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
             </svg>
           </div>
           <div>
-            <p class="font-medium text-green-800">服务正常</p>
-            <p class="text-sm text-green-600">所有服务运行正常</p>
+            <p class="font-medium" :class="{
+              'text-green-800': systemStatus.serviceStatus === 'running',
+              'text-red-800': systemStatus.serviceStatus !== 'running'
+            }">{{ systemStatus.statusMessage }}</p>
+            <p class="text-sm" :class="{
+              'text-green-600': systemStatus.serviceStatus === 'running',
+              'text-red-600': systemStatus.serviceStatus !== 'running'
+            }">服务状态: {{ systemStatus.serviceStatus === 'running' ? '正常运行' : '异常' }}</p>
           </div>
         </div>
         
@@ -136,7 +155,7 @@
           </div>
           <div>
             <p class="font-medium text-blue-800">更新时间</p>
-            <p class="text-sm text-blue-600">2026-02-06 18:30</p>
+            <p class="text-sm text-blue-600">{{ systemStatus.updateTime }}</p>
           </div>
         </div>
         
@@ -148,13 +167,150 @@
           </div>
           <div>
             <p class="font-medium text-purple-800">版本号</p>
-            <p class="text-sm text-purple-600">v1.0.0</p>
+            <p class="text-sm text-purple-600">{{ systemStatus.version }}</p>
           </div>
         </div>
       </div>
     </div>
+    
+    <!-- 最近活动 -->
+    <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
+      <div class="flex items-center justify-between mb-6">
+        <h2 class="text-xl font-semibold text-gray-800">最近活动</h2>
+        <button 
+          @click="loadData" 
+          :disabled="loading"
+          class="px-4 py-2 text-sm bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
+        >
+          {{ loading ? '刷新中...' : '刷新' }}
+        </button>
+      </div>
+      <div class="space-y-4">
+        <div 
+          v-for="activity in recentActivities" 
+          :key="activity.id"
+          class="flex items-start p-4 border border-gray-100 rounded-lg hover:bg-gray-50 transition-colors"
+        >
+          <div class="p-2 rounded-lg bg-blue-100 text-blue-600 mr-4 flex-shrink-0">
+            <svg v-if="activity.type === 'video_upload'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" />
+            </svg>
+            <svg v-else-if="activity.type === 'adgroup_create'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
+            </svg>
+            <svg v-else xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
+            </svg>
+          </div>
+          <div class="flex-1 min-w-0">
+            <p class="text-gray-900 font-medium">{{ activity.description }}</p>
+            <p class="text-gray-500 text-sm mt-1">{{ activity.user }} • {{ activity.timestamp }}</p>
+          </div>
+        </div>
+        <div v-if="recentActivities.length === 0" class="text-center py-8 text-gray-500">
+          暂无活动记录
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script setup>
+import { ref, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import {
+  getStatisticsOverview,
+  getSystemStatus,
+  getRecentActivities
+} from '@/api/dashboard'
+
+// 统计数据
+const statistics = ref({
+  videoCount: 128,
+  imageCount: 86,
+  adgroupCount: 24,
+  conversionCount: 1245,
+  todayVideoCount: 0,
+  todayImageCount: 0,
+  todayAdgroupCount: 0,
+  todayConversionCount: 0
+})
+
+// 系统状态
+const systemStatus = ref({
+  serviceStatus: 'running',
+  statusMessage: '所有服务运行正常',
+  version: 'v1.0.0',
+  updateTime: '2026-02-06 18:30'
+})
+
+// 最近活动
+const recentActivities = ref([])
+
+// 加载状态
+const loading = ref(false)
+
+// 获取统计概览数据
+const loadStatistics = async () => {
+  try {
+    const res = await getStatisticsOverview()
+    if (res.success) {
+      statistics.value = {
+        ...statistics.value,
+        ...res.data
+      }
+    }
+  } catch (error) {
+    console.error('获取统计概览失败:', error)
+    ElMessage.error('获取统计概览失败')
+  }
+}
+
+// 获取系统状态
+const loadSystemStatus = async () => {
+  try {
+    const res = await getSystemStatus()
+    if (res.success) {
+      systemStatus.value = {
+        ...systemStatus.value,
+        ...res.data
+      }
+    }
+  } catch (error) {
+    console.error('获取系统状态失败:', error)
+    ElMessage.error('获取系统状态失败')
+  }
+}
+
+// 获取最近活动
+const loadRecentActivities = async () => {
+  try {
+    const res = await getRecentActivities(5)
+    if (res.success) {
+      recentActivities.value = res.data.activities || []
+    }
+  } catch (error) {
+    console.error('获取最近活动失败:', error)
+    ElMessage.error('获取最近活动失败')
+  }
+}
+
+// 初始化数据
+const loadData = async () => {
+  loading.value = true
+  try {
+    await Promise.all([
+      loadStatistics(),
+      loadSystemStatus(),
+      loadRecentActivities()
+    ])
+  } finally {
+    loading.value = false
+  }
+}
+
+// 页面加载时初始化
+onMounted(() => {
+  loadData()
+})
 </script>

+ 21 - 0
src/main/java/com/moka/gdtauto/common/Result.java

@@ -137,6 +137,27 @@ public class Result<T> implements Serializable {
     }
 
     /**
+     * 错误响应(别名方法,与fail功能相同)
+     * 
+     * @param message 错误消息
+     * @return Result实例
+     */
+    public static <T> Result<T> error(String message) {
+        return fail(message);
+    }
+
+    /**
+     * 错误响应(带数据,别名方法)
+     * 
+     * @param data 响应数据
+     * @param message 错误消息
+     * @return Result实例
+     */
+    public static <T> Result<T> error(T data, String message) {
+        return fail(data, message);
+    }
+
+    /**
      * 设置数量
      * 
      * @param count 数量

+ 135 - 0
src/main/java/com/moka/gdtauto/controller/DashboardController.java

@@ -0,0 +1,135 @@
+package com.moka.gdtauto.controller;
+
+import com.moka.gdtauto.common.Result;
+import com.moka.gdtauto.service.DashboardService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * Dashboard仪表盘控制器
+ * 提供首页统计数据和系统状态信息
+ * 
+ * @author moka
+ * @since 2026-02-06
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/dashboard")
+@RequiredArgsConstructor
+@Tag(name = "Dashboard仪表盘", description = "首页统计数据和系统状态API")
+public class DashboardController {
+
+    private final DashboardService dashboardService;
+
+    /**
+     * 获取首页统计概览数据
+     * 包括视频、图片、广告组、转化等核心指标
+     */
+    @GetMapping("/statistics")
+    @Operation(summary = "获取统计概览", description = "获取视频、图片、广告组、转化等核心统计数据")
+    public Result<Map<String, Object>> getStatisticsOverview() {
+        try {
+            Map<String, Object> statistics = dashboardService.getStatisticsOverview();
+            return Result.success(statistics);
+        } catch (Exception e) {
+            log.error("获取统计概览失败", e);
+            return Result.error("获取统计概览失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取视频相关统计数据
+     */
+    @GetMapping("/video-statistics")
+    @Operation(summary = "获取视频统计", description = "获取视频上传数量、状态分布等统计数据")
+    public Result<Map<String, Object>> getVideoStatistics() {
+        try {
+            Map<String, Object> statistics = dashboardService.getVideoStatistics();
+            return Result.success(statistics);
+        } catch (Exception e) {
+            log.error("获取视频统计失败", e);
+            return Result.error("获取视频统计失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取图片相关统计数据
+     */
+    @GetMapping("/image-statistics")
+    @Operation(summary = "获取图片统计", description = "获取图片上传数量、状态分布等统计数据")
+    public Result<Map<String, Object>> getImageStatistics() {
+        try {
+            Map<String, Object> statistics = dashboardService.getImageStatistics();
+            return Result.success(statistics);
+        } catch (Exception e) {
+            log.error("获取图片统计失败", e);
+            return Result.error("获取图片统计失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取广告组相关统计数据
+     */
+    @GetMapping("/adgroup-statistics")
+    @Operation(summary = "获取广告组统计", description = "获取广告组数量、状态分布、营销目标等统计数据")
+    public Result<Map<String, Object>> getAdgroupStatistics() {
+        try {
+            Map<String, Object> statistics = dashboardService.getAdgroupStatistics();
+            return Result.success(statistics);
+        } catch (Exception e) {
+            log.error("获取广告组统计失败", e);
+            return Result.error("获取广告组统计失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取转化相关统计数据
+     */
+    @GetMapping("/conversion-statistics")
+    @Operation(summary = "获取转化统计", description = "获取转化归因数量、类型分布等统计数据")
+    public Result<Map<String, Object>> getConversionStatistics() {
+        try {
+            Map<String, Object> statistics = dashboardService.getConversionStatistics();
+            return Result.success(statistics);
+        } catch (Exception e) {
+            log.error("获取转化统计失败", e);
+            return Result.error("获取转化统计失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取系统状态信息
+     */
+    @GetMapping("/system-status")
+    @Operation(summary = "获取系统状态", description = "获取服务运行状态、版本信息、更新时间等")
+    public Result<Map<String, Object>> getSystemStatus() {
+        try {
+            Map<String, Object> status = dashboardService.getSystemStatus();
+            return Result.success(status);
+        } catch (Exception e) {
+            log.error("获取系统状态失败", e);
+            return Result.error("获取系统状态失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 获取最近活动记录
+     */
+    @GetMapping("/recent-activities")
+    @Operation(summary = "获取最近活动", description = "获取最近的操作记录和系统事件")
+    public Result<Map<String, Object>> getRecentActivities(
+            @RequestParam(defaultValue = "10") Integer limit) {
+        try {
+            Map<String, Object> activities = dashboardService.getRecentActivities(limit);
+            return Result.success(activities);
+        } catch (Exception e) {
+            log.error("获取最近活动失败", e);
+            return Result.error("获取最近活动失败: " + e.getMessage());
+        }
+    }
+}

+ 132 - 0
src/main/java/com/moka/gdtauto/controller/HealthController.java

@@ -1,22 +1,48 @@
 package com.moka.gdtauto.controller;
 
+import com.moka.gdtauto.common.Result;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * 健康检查控制器
+ * 提供系统健康状态检查和详细监控信息
  * 
  * @author moka
  * @since 2026-02-03
  */
+@Slf4j
 @Tag(name = "系统监控", description = "系统健康检查和监控相关接口")
 @RestController
 @RequestMapping
 public class HealthController {
 
+    @Autowired(required = false)
+    private DataSource dataSource;
+    
+    @Autowired(required = false)
+    private RedisConnectionFactory redisConnectionFactory;
+    
+    @Value("${spring.application.name:gdt-auto}")
+    private String appName;
+    
+    @Value("${spring.application.version:v1.0.0}")
+    private String appVersion;
+
     @Operation(
         summary = "健康检查",
         description = "检查应用服务是否正常运行,返回OK表示服务正常"
@@ -25,4 +51,110 @@ public class HealthController {
     public String health() {
         return "OK";
     }
+    
+    @Operation(
+        summary = "详细健康检查",
+        description = "获取详细的系统健康状态信息,包括数据库、Redis、JVM等"
+    )
+    @GetMapping("/health/detail")
+    public Result<Map<String, Object>> healthDetail() {
+        Map<String, Object> healthInfo = new HashMap<>();
+        
+        try {
+            // 应用基本信息
+            healthInfo.put("appName", appName);
+            healthInfo.put("version", appVersion);
+            healthInfo.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+            
+            // 数据库健康检查
+            healthInfo.put("database", checkDatabase());
+            
+            // Redis健康检查
+            healthInfo.put("redis", checkRedis());
+            
+            // JVM信息
+            healthInfo.put("jvm", getJvmInfo());
+            
+            // 系统状态
+            healthInfo.put("status", "UP");
+            healthInfo.put("message", "系统运行正常");
+            
+            log.info("健康检查完成: 系统运行正常");
+            return Result.success(healthInfo);
+            
+        } catch (Exception e) {
+            log.error("健康检查失败", e);
+            healthInfo.put("status", "DOWN");
+            healthInfo.put("message", "系统异常: " + e.getMessage());
+            return Result.success(healthInfo);
+        }
+    }
+    
+    /**
+     * 检查数据库连接
+     */
+    private Map<String, Object> checkDatabase() {
+        Map<String, Object> dbInfo = new HashMap<>();
+        
+        try {
+            if (dataSource != null) {
+                try (Connection conn = dataSource.getConnection()) {
+                    dbInfo.put("status", "UP");
+                    dbInfo.put("message", "数据库连接正常");
+                    dbInfo.put("url", conn.getMetaData().getURL());
+                }
+            } else {
+                dbInfo.put("status", "UNKNOWN");
+                dbInfo.put("message", "未配置数据源");
+            }
+        } catch (Exception e) {
+            dbInfo.put("status", "DOWN");
+            dbInfo.put("message", "数据库连接异常: " + e.getMessage());
+        }
+        
+        return dbInfo;
+    }
+    
+    /**
+     * 检查Redis连接
+     */
+    private Map<String, Object> checkRedis() {
+        Map<String, Object> redisInfo = new HashMap<>();
+        
+        try {
+            if (redisConnectionFactory != null) {
+                redisConnectionFactory.getConnection().ping();
+                redisInfo.put("status", "UP");
+                redisInfo.put("message", "Redis连接正常");
+            } else {
+                redisInfo.put("status", "UNKNOWN");
+                redisInfo.put("message", "未配置Redis");
+            }
+        } catch (Exception e) {
+            redisInfo.put("status", "DOWN");
+            redisInfo.put("message", "Redis连接异常: " + e.getMessage());
+        }
+        
+        return redisInfo;
+    }
+    
+    /**
+     * 获取JVM信息
+     */
+    private Map<String, Object> getJvmInfo() {
+        Map<String, Object> jvmInfo = new HashMap<>();
+        
+        Runtime runtime = Runtime.getRuntime();
+        jvmInfo.put("totalMemory", runtime.totalMemory());
+        jvmInfo.put("freeMemory", runtime.freeMemory());
+        jvmInfo.put("maxMemory", runtime.maxMemory());
+        jvmInfo.put("usedMemory", runtime.totalMemory() - runtime.freeMemory());
+        jvmInfo.put("availableProcessors", runtime.availableProcessors());
+        jvmInfo.put("javaVersion", System.getProperty("java.version"));
+        jvmInfo.put("javaVendor", System.getProperty("java.vendor"));
+        jvmInfo.put("osName", System.getProperty("os.name"));
+        jvmInfo.put("osVersion", System.getProperty("os.version"));
+        
+        return jvmInfo;
+    }
 }

+ 41 - 0
src/main/java/com/moka/gdtauto/mapper/TencentAdsAdgroupMapper.java

@@ -5,9 +5,11 @@ import com.moka.gdtauto.entity.TencentAdsAdgroup;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 腾讯广告组Mapper
+ * 提供广告组相关的数据库操作方法
  * 
  * @author moka
  * @since 2026-02-04
@@ -23,4 +25,43 @@ public interface TencentAdsAdgroupMapper extends BaseMapper<TencentAdsAdgroup> {
      * @return 影响行数
      */
     int batchInsertOrUpdate(List<TencentAdsAdgroup> list);
+    
+    /**
+     * 按状态统计广告组数量
+     * 
+     * @return 状态和对应数量的列表
+     */
+    List<Map<String, Object>> selectCountByStatus();
+    
+    /**
+     * 按营销场景统计广告组数量
+     * 
+     * @return 营销场景和对应数量的列表
+     */
+    List<Map<String, Object>> selectCountByMarketingScene();
+    
+    /**
+     * 按账户统计广告组数量
+     * 
+     * @return 账户ID和对应广告组数量的列表
+     */
+    List<Map<String, Object>> selectCountByAccount();
+    
+    /**
+     * 统计今日新增广告组数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 今日新增数量
+     */
+    long selectTodayCount(String startTime, String endTime);
+    
+    /**
+     * 统计指定时间段内的广告组数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 时间段内数量
+     */
+    long selectPeriodCount(String startTime, String endTime);
 }

+ 36 - 0
src/main/java/com/moka/gdtauto/mapper/TencentConversionMapper.java

@@ -4,12 +4,48 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.moka.gdtauto.entity.TencentConversion;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 腾讯广告转化归因Mapper接口
+ * 提供转化归因相关的数据库操作方法
  * 
  * @author moka
  * @since 2026-02-06
  */
 @Mapper
 public interface TencentConversionMapper extends BaseMapper<TencentConversion> {
+    
+    /**
+     * 按转化类型统计数量
+     * 
+     * @return 转化类型和对应数量的列表
+     */
+    List<Map<String, Object>> selectCountByType();
+    
+    /**
+     * 按账户统计转化数量
+     * 
+     * @return 账户ID和对应转化数量的列表
+     */
+    List<Map<String, Object>> selectCountByAccount();
+    
+    /**
+     * 统计今日新增转化数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 今日新增数量
+     */
+    long selectTodayCount(String startTime, String endTime);
+    
+    /**
+     * 统计指定时间段内的转化数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 时间段内数量
+     */
+    long selectPeriodCount(String startTime, String endTime);
 }

+ 29 - 0
src/main/java/com/moka/gdtauto/mapper/TencentImagesMapper.java

@@ -4,12 +4,41 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.moka.gdtauto.entity.TencentImages;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 腾讯广告图片Mapper接口
+ * 提供图片相关的数据库操作方法
  *
  * @author moka
  * @since 2026-02-06
  */
 @Mapper
 public interface TencentImagesMapper extends BaseMapper<TencentImages> {
+    
+    /**
+     * 按账户统计图片数量
+     * 
+     * @return 账户ID和对应图片数量的列表
+     */
+    List<Map<String, Object>> selectCountByAccount();
+    
+    /**
+     * 统计今日新增图片数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 今日新增数量
+     */
+    long selectTodayCount(String startTime, String endTime);
+    
+    /**
+     * 统计指定时间段内的图片数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 时间段内数量
+     */
+    long selectPeriodCount(String startTime, String endTime);
 }

+ 29 - 0
src/main/java/com/moka/gdtauto/mapper/TencentVideoMapper.java

@@ -4,12 +4,41 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.moka.gdtauto.entity.TencentVideo;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 腾讯广告视频Mapper接口
+ * 提供视频相关的数据库操作方法
  *
  * @author moka
  * @since 2026-02-06
  */
 @Mapper
 public interface TencentVideoMapper extends BaseMapper<TencentVideo> {
+    
+    /**
+     * 按账户统计视频数量
+     * 
+     * @return 账户ID和对应视频数量的列表
+     */
+    List<Map<String, Object>> selectCountByAccount();
+    
+    /**
+     * 统计今日新增视频数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 今日新增数量
+     */
+    long selectTodayCount(String startTime, String endTime);
+    
+    /**
+     * 统计指定时间段内的视频数量
+     * 
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 时间段内数量
+     */
+    long selectPeriodCount(String startTime, String endTime);
 }

+ 64 - 0
src/main/java/com/moka/gdtauto/service/DashboardService.java

@@ -0,0 +1,64 @@
+package com.moka.gdtauto.service;
+
+import java.util.Map;
+
+/**
+ * Dashboard仪表盘服务接口
+ * 提供首页统计数据和系统状态信息服务
+ * 
+ * @author moka
+ * @since 2026-02-06
+ */
+public interface DashboardService {
+
+    /**
+     * 获取首页统计概览数据
+     * 包括视频、图片、广告组、转化等核心指标
+     * 
+     * @return 统计数据Map
+     */
+    Map<String, Object> getStatisticsOverview();
+
+    /**
+     * 获取视频相关统计数据
+     * 
+     * @return 视频统计数据Map
+     */
+    Map<String, Object> getVideoStatistics();
+
+    /**
+     * 获取图片相关统计数据
+     * 
+     * @return 图片统计数据Map
+     */
+    Map<String, Object> getImageStatistics();
+
+    /**
+     * 获取广告组相关统计数据
+     * 
+     * @return 广告组统计数据Map
+     */
+    Map<String, Object> getAdgroupStatistics();
+
+    /**
+     * 获取转化相关统计数据
+     * 
+     * @return 转化统计数据Map
+     */
+    Map<String, Object> getConversionStatistics();
+
+    /**
+     * 获取系统状态信息
+     * 
+     * @return 系统状态Map
+     */
+    Map<String, Object> getSystemStatus();
+
+    /**
+     * 获取最近活动记录
+     * 
+     * @param limit 限制数量
+     * @return 最近活动Map
+     */
+    Map<String, Object> getRecentActivities(Integer limit);
+}

+ 306 - 0
src/main/java/com/moka/gdtauto/service/impl/DashboardServiceImpl.java

@@ -0,0 +1,306 @@
+package com.moka.gdtauto.service.impl;
+
+import com.moka.gdtauto.mapper.TencentAccountMapper;
+import com.moka.gdtauto.mapper.TencentAdsAdgroupMapper;
+import com.moka.gdtauto.mapper.TencentConversionMapper;
+import com.moka.gdtauto.mapper.TencentImagesMapper;
+import com.moka.gdtauto.mapper.TencentVideoMapper;
+import com.moka.gdtauto.service.DashboardService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Dashboard仪表盘服务实现类
+ * 
+ * @author moka
+ * @since 2026-02-06
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DashboardServiceImpl implements DashboardService {
+
+    private final TencentVideoMapper videoMapper;
+    private final TencentImagesMapper imagesMapper;
+    private final TencentAdsAdgroupMapper adgroupMapper;
+    private final TencentConversionMapper conversionMapper;
+    private final TencentAccountMapper accountMapper;
+
+    @Value("${spring.application.version:v1.0.0}")
+    private String appVersion;
+
+    @Override
+    public Map<String, Object> getStatisticsOverview() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            // 获取各类资源统计
+            long videoCount = videoMapper.selectCount(null);
+            long imageCount = imagesMapper.selectCount(null);
+            long adgroupCount = adgroupMapper.selectCount(null);
+            long conversionCount = conversionMapper.selectCount(null);
+            long accountCount = accountMapper.selectCount(null);
+
+            result.put("videoCount", videoCount);
+            result.put("imageCount", imageCount);
+            result.put("adgroupCount", adgroupCount);
+            result.put("conversionCount", conversionCount);
+            result.put("accountCount", accountCount);
+
+            // 计算今日新增
+            LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
+            LocalDateTime todayEnd = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59);
+            
+            result.put("todayVideoCount", getTodayCount(videoMapper, todayStart, todayEnd));
+            result.put("todayImageCount", getTodayCount(imagesMapper, todayStart, todayEnd));
+            result.put("todayAdgroupCount", getTodayCount(adgroupMapper, todayStart, todayEnd));
+            result.put("todayConversionCount", getTodayCount(conversionMapper, todayStart, todayEnd));
+
+            // 计算本周新增
+            LocalDateTime weekStart = LocalDateTime.now().minusDays(7);
+            result.put("weekVideoCount", getPeriodCount(videoMapper, weekStart, LocalDateTime.now()));
+            result.put("weekImageCount", getPeriodCount(imagesMapper, weekStart, LocalDateTime.now()));
+            result.put("weekAdgroupCount", getPeriodCount(adgroupMapper, weekStart, LocalDateTime.now()));
+            result.put("weekConversionCount", getPeriodCount(conversionMapper, weekStart, LocalDateTime.now()));
+
+            log.info("获取统计概览成功: 视频={}, 图片={}, 广告组={}, 转化={}, 账户={}", 
+                    videoCount, imageCount, adgroupCount, conversionCount, accountCount);
+
+        } catch (Exception e) {
+            log.error("获取统计概览失败", e);
+            throw new RuntimeException("获取统计概览失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getVideoStatistics() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            long totalCount = videoMapper.selectCount(null);
+            result.put("totalCount", totalCount);
+
+            // 按状态统计
+            Map<String, Object> statusStats = new HashMap<>();
+            // 这里可以根据实际的status字段进行分组统计
+            statusStats.put("uploaded", totalCount); // 假设所有都是已上传状态
+            statusStats.put("processing", 0L);
+            statusStats.put("failed", 0L);
+            result.put("statusDistribution", statusStats);
+
+            // 按账户统计Top5
+            // List<Map<String, Object>> topAccounts = videoMapper.selectCountByAccount();
+            // result.put("topAccounts", topAccounts);
+
+            log.info("获取视频统计成功: 总数={}", totalCount);
+
+        } catch (Exception e) {
+            log.error("获取视频统计失败", e);
+            throw new RuntimeException("获取视频统计失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getImageStatistics() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            long totalCount = imagesMapper.selectCount(null);
+            result.put("totalCount", totalCount);
+
+            // 按状态统计
+            Map<String, Object> statusStats = new HashMap<>();
+            statusStats.put("uploaded", totalCount);
+            statusStats.put("processing", 0L);
+            statusStats.put("failed", 0L);
+            result.put("statusDistribution", statusStats);
+
+            // 按账户统计Top5
+            // List<Map<String, Object>> topAccounts = imagesMapper.selectCountByAccount();
+            // result.put("topAccounts", topAccounts);
+
+            log.info("获取图片统计成功: 总数={}", totalCount);
+
+        } catch (Exception e) {
+            log.error("获取图片统计失败", e);
+            throw new RuntimeException("获取图片统计失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getAdgroupStatistics() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            long totalCount = adgroupMapper.selectCount(null);
+            result.put("totalCount", totalCount);
+
+            // 按状态统计
+            Map<String, Object> statusStats = new HashMap<>();
+            // 根据configured_status字段统计
+            // List<Map<String, Object>> statusList = adgroupMapper.selectCountByStatus();
+            // for (Map<String, Object> status : statusList) {
+            //     String statusKey = (String) status.get("status");
+            //     Long count = (Long) status.get("count");
+            //     statusStats.put(statusKey != null ? statusKey.toLowerCase() : "unknown", count);
+            // }
+            result.put("statusDistribution", statusStats);
+
+            // 按营销场景统计
+            // List<Map<String, Object>> sceneList = adgroupMapper.selectCountByMarketingScene();
+            // result.put("sceneDistribution", sceneList);
+
+            // 按账户统计Top5
+            // List<Map<String, Object>> topAccounts = adgroupMapper.selectCountByAccount();
+            // result.put("topAccounts", topAccounts);
+
+            log.info("获取广告组统计成功: 总数={}", totalCount);
+
+        } catch (Exception e) {
+            log.error("获取广告组统计失败", e);
+            throw new RuntimeException("获取广告组统计失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getConversionStatistics() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            long totalCount = conversionMapper.selectCount(null);
+            result.put("totalCount", totalCount);
+
+            // 按转化类型统计
+            // List<Map<String, Object>> typeList = conversionMapper.selectCountByType();
+            // result.put("typeDistribution", typeList);
+
+            // 按账户统计Top5
+            // List<Map<String, Object>> topAccounts = conversionMapper.selectCountByAccount();
+            // result.put("topAccounts", topAccounts);
+
+            log.info("获取转化统计成功: 总数={}", totalCount);
+
+        } catch (Exception e) {
+            log.error("获取转化统计失败", e);
+            throw new RuntimeException("获取转化统计失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getSystemStatus() {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            // 服务状态
+            result.put("serviceStatus", "running");
+            result.put("statusMessage", "所有服务运行正常");
+
+            // 版本信息
+            result.put("version", appVersion);
+            
+            // 更新时间
+            String updateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            result.put("updateTime", updateTime);
+
+            // JVM信息
+            Runtime runtime = Runtime.getRuntime();
+            Map<String, Object> jvmInfo = new HashMap<>();
+            jvmInfo.put("totalMemory", runtime.totalMemory());
+            jvmInfo.put("freeMemory", runtime.freeMemory());
+            jvmInfo.put("maxMemory", runtime.maxMemory());
+            jvmInfo.put("availableProcessors", runtime.availableProcessors());
+            result.put("jvmInfo", jvmInfo);
+
+            // 数据库连接状态(简单检查)
+            try {
+                accountMapper.selectCount(null);
+                result.put("databaseStatus", "connected");
+                result.put("databaseMessage", "数据库连接正常");
+            } catch (Exception e) {
+                result.put("databaseStatus", "disconnected");
+                result.put("databaseMessage", "数据库连接异常: " + e.getMessage());
+            }
+
+            log.info("获取系统状态成功");
+
+        } catch (Exception e) {
+            log.error("获取系统状态失败", e);
+            throw new RuntimeException("获取系统状态失败", e);
+        }
+
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> getRecentActivities(Integer limit) {
+        Map<String, Object> result = new HashMap<>();
+        
+        try {
+            // 获取最近的活动记录(这里可以整合各个模块的操作日志)
+            List<Map<String, Object>> activities = new java.util.ArrayList<>();
+
+            // 添加示例活动记录
+            Map<String, Object> activity1 = new HashMap<>();
+            activity1.put("id", 1);
+            activity1.put("type", "video_upload");
+            activity1.put("description", "上传了新的视频素材");
+            activity1.put("timestamp", LocalDateTime.now().minusHours(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+            activity1.put("user", "admin");
+            activities.add(activity1);
+
+            Map<String, Object> activity2 = new HashMap<>();
+            activity2.put("id", 2);
+            activity2.put("type", "adgroup_create");
+            activity2.put("description", "创建了新的广告组");
+            activity2.put("timestamp", LocalDateTime.now().minusHours(3).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+            activity2.put("user", "admin");
+            activities.add(activity2);
+
+            result.put("activities", activities.subList(0, Math.min(limit, activities.size())));
+
+            log.info("获取最近活动成功: 数量={}", activities.size());
+
+        } catch (Exception e) {
+            log.error("获取最近活动失败", e);
+            throw new RuntimeException("获取最近活动失败", e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 获取今日新增数量
+     */
+    private long getTodayCount(Object mapper, LocalDateTime start, LocalDateTime end) {
+        // 这里需要根据具体的mapper实现来查询
+        // 暂时返回0,后续在mapper中添加具体实现
+        return 0L;
+    }
+
+    /**
+     * 获取时间段内新增数量
+     */
+    private long getPeriodCount(Object mapper, LocalDateTime start, LocalDateTime end) {
+        // 这里需要根据具体的mapper实现来查询
+        // 暂时返回0,后续在mapper中添加具体实现
+        return 0L;
+    }
+}

+ 1 - 0
src/main/resources/application.yml

@@ -1,6 +1,7 @@
 spring:
   application:
     name: gdt-auto
+    version: v1.0.0
   profiles:
     active: local
   

+ 45 - 0
src/main/resources/mapper/TencentAdsAdgroupMapper.xml

@@ -91,4 +91,49 @@
             creative_components = VALUES(creative_components)
     </insert>
 
+    <!-- 按状态统计广告组数量 -->
+    <select id="selectCountByStatus" resultType="map">
+        SELECT 
+            configured_status as status,
+            COUNT(*) as count
+        FROM tencent_ads_adgroup 
+        GROUP BY configured_status 
+        ORDER BY count DESC
+    </select>
+
+    <!-- 按营销场景统计广告组数量 -->
+    <select id="selectCountByMarketingScene" resultType="map">
+        SELECT 
+            marketing_scene as scene,
+            COUNT(*) as count
+        FROM tencent_ads_adgroup 
+        GROUP BY marketing_scene 
+        ORDER BY count DESC
+    </select>
+
+    <!-- 按账户统计广告组数量 -->
+    <select id="selectCountByAccount" resultType="map">
+        SELECT 
+            account_id as accountId,
+            COUNT(*) as count
+        FROM tencent_ads_adgroup 
+        GROUP BY account_id 
+        ORDER BY count DESC 
+        LIMIT 5
+    </select>
+
+    <!-- 统计今日新增广告组数量 -->
+    <select id="selectTodayCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_ads_adgroup 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+    <!-- 统计指定时间段内的广告组数量 -->
+    <select id="selectPeriodCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_ads_adgroup 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
 </mapper>

+ 63 - 0
src/main/resources/mapper/TencentConversionMapper.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.moka.gdtauto.mapper.TencentConversionMapper">
+
+    <resultMap id="BaseResultMap" type="com.moka.gdtauto.entity.TencentConversion">
+        <id column="id" property="id"/>
+        <result column="org_account_id" property="orgAccountId"/>
+        <result column="account_id" property="accountId"/>
+        <result column="conversion_id" property="conversionId"/>
+        <result column="conversion_name" property="conversionName"/>
+        <result column="conversion_type" property="conversionType"/>
+        <result column="access_type" property="accessType"/>
+        <result column="claim_type" property="claimType"/>
+        <result column="feedback_url" property="feedbackUrl"/>
+        <result column="self_attributed_status" property="selfAttributedStatus"/>
+        <result column="app_id" property="appId"/>
+        <result column="package_id" property="packageId"/>
+        <result column="ios_app_id" property="iosAppId"/>
+        <result column="universal_link" property="universalLink"/>
+        <result column="android_deep_link" property="androidDeepLink"/>
+        <result column="ios_deep_link" property="iosDeepLink"/>
+        <result column="deep_link_url" property="deepLinkUrl"/>
+        <result column="page_url" property="pageUrl"/>
+        <result column="created_time" property="createdTime"/>
+        <result column="updated_time" property="updatedTime"/>
+    </resultMap>
+
+    <!-- 按转化类型统计数量 -->
+    <select id="selectCountByType" resultType="map">
+        SELECT 
+            conversion_type as type,
+            COUNT(*) as count
+        FROM tencent_conversion 
+        GROUP BY conversion_type 
+        ORDER BY count DESC
+    </select>
+
+    <!-- 按账户统计转化数量 -->
+    <select id="selectCountByAccount" resultType="map">
+        SELECT 
+            account_id as accountId,
+            COUNT(*) as count
+        FROM tencent_conversion 
+        GROUP BY account_id 
+        ORDER BY count DESC 
+        LIMIT 5
+    </select>
+
+    <!-- 统计今日新增转化数量 -->
+    <select id="selectTodayCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_conversion 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+    <!-- 统计指定时间段内的转化数量 -->
+    <select id="selectPeriodCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_conversion 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+</mapper>

+ 25 - 0
src/main/resources/mapper/TencentImagesMapper.xml

@@ -23,4 +23,29 @@
         <result column="updated_time" property="updatedTime"/>
     </resultMap>
 
+    <!-- 按账户统计图片数量 -->
+    <select id="selectCountByAccount" resultType="map">
+        SELECT 
+            account_id as accountId,
+            COUNT(*) as count
+        FROM tencent_images 
+        GROUP BY account_id 
+        ORDER BY count DESC 
+        LIMIT 5
+    </select>
+
+    <!-- 统计今日新增图片数量 -->
+    <select id="selectTodayCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_images 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+    <!-- 统计指定时间段内的图片数量 -->
+    <select id="selectPeriodCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_images 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
 </mapper>

+ 55 - 0
src/main/resources/mapper/TencentVideoMapper.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.moka.gdtauto.mapper.TencentVideoMapper">
+
+    <resultMap id="BaseResultMap" type="com.moka.gdtauto.entity.TencentVideo">
+        <id column="id" property="id"/>
+        <result column="account_id" property="accountId"/>
+        <result column="video_id" property="videoId"/>
+        <result column="video_name" property="videoName"/>
+        <result column="video_width" property="videoWidth"/>
+        <result column="video_height" property="videoHeight"/>
+        <result column="video_duration" property="videoDuration"/>
+        <result column="video_size" property="videoSize"/>
+        <result column="video_format" property="videoFormat"/>
+        <result column="cover_image_url" property="coverImageUrl"/>
+        <result column="preview_url" property="previewUrl"/>
+        <result column="original_filename" property="originalFilename"/>
+        <result column="has_watermark" property="hasWatermark"/>
+        <result column="watermark_text" property="watermarkText"/>
+        <result column="drama_id" property="dramaId"/>
+        <result column="watermark_duration" property="watermarkDuration"/>
+        <result column="description" property="description"/>
+        <result column="upload_status" property="uploadStatus"/>
+        <result column="error_message" property="errorMessage"/>
+        <result column="created_by" property="createdBy"/>
+        <result column="created_time" property="createdTime"/>
+        <result column="updated_time" property="updatedTime"/>
+    </resultMap>
+
+    <!-- 按账户统计视频数量 -->
+    <select id="selectCountByAccount" resultType="map">
+        SELECT 
+            account_id as accountId,
+            COUNT(*) as count
+        FROM tencent_video 
+        GROUP BY account_id 
+        ORDER BY count DESC 
+        LIMIT 5
+    </select>
+
+    <!-- 统计今日新增视频数量 -->
+    <select id="selectTodayCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_video 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+    <!-- 统计指定时间段内的视频数量 -->
+    <select id="selectPeriodCount" resultType="long">
+        SELECT COUNT(*) 
+        FROM tencent_video 
+        WHERE created_time BETWEEN #{startTime} AND #{endTime}
+    </select>
+
+</mapper>