增加活动页面

Change-Id: I63cf84e250d16b2af401f335562fcadd3be13170
diff --git a/react-ui/src/pages/Activity/service.ts b/react-ui/src/pages/Activity/service.ts
new file mode 100644
index 0000000..25c1d9a
--- /dev/null
+++ b/react-ui/src/pages/Activity/service.ts
@@ -0,0 +1,219 @@
+// service.ts - 活动系统服务层文件
+
+import { request } from '@umijs/max';
+import type {
+    GetActivityListResponse,
+    GetLeaderboardResponse,
+    ParticipateActivityResponse,
+    ActivityListParams,
+    LeaderboardParams,
+    SysActivity,
+    LeaderboardEntry,
+    StandardizedActivityListResponse,
+    StandardizedLeaderboardResponse
+} from './data';
+
+// 开发环境模拟数据开关
+const USE_MOCK = process.env.NODE_ENV === 'development' && false;
+
+// 模拟活动数据 - 适配后端格式
+const mockActivities: SysActivity[] = [
+    {
+        createBy: null,
+        createTime: null,
+        updateBy: null,
+        updateTime: null,
+        remark: null,
+        activityId: 1,
+        activityName: '新手上传挑战',
+        rewardBonus: 100,
+        conditionValue: '10GB',
+        startTime: '2025-06-01T00:00:00.000+08:00',
+        endTime: '2025-06-30T23:59:59.000+08:00',
+        status: 1,
+        activityType: 'UPLOAD'
+    },
+    {
+        createBy: null,
+        createTime: null,
+        updateBy: null,
+        updateTime: null,
+        remark: null,
+        activityId: 2,
+        activityName: '种子下载任务',
+        rewardBonus: 50,
+        conditionValue: '94',
+        startTime: '2025-06-01T00:00:00.000+08:00',
+        endTime: '2025-06-15T23:59:59.000+08:00',
+        status: 1,
+        activityType: 'DOWNLOAD'
+    }
+];
+
+// 模拟排行榜数据
+const mockLeaderboard: LeaderboardEntry[] = [
+    { userId: 1, userName: 'user001', score: 1500 },
+    { userId: 2, userName: 'user002', score: 1200 },
+    { userId: 3, userName: 'user003', score: 1000 }
+];
+
+/**
+ * 时间格式转换工具函数
+ * 将 ISO 8601 格式转换为显示格式
+ */
+export function formatDateTime(isoString: string): string {
+    const date = new Date(isoString);
+    return date.toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+    }).replace(/\//g, '-');
+}
+
+/**
+ * 数据格式标准化函数
+ * 将后端返回格式转换为前端组件期望的格式
+ */
+function normalizeActivityListResponse(
+    backendResponse: GetActivityListResponse,
+    params?: ActivityListParams
+): StandardizedActivityListResponse {
+    return {
+        code: backendResponse.code === 200 ? 0 : backendResponse.code, // 后端成功码是200,前端期望0
+        msg: backendResponse.msg,
+        data: {
+            list: backendResponse.rows,
+            total: backendResponse.total,
+            pageSize: params?.pageSize || 10,
+            current: params?.pageNum || 1,
+        }
+    };
+}
+
+function normalizeLeaderboardResponse(
+    backendResponse: GetLeaderboardResponse,
+    params?: LeaderboardParams
+): StandardizedLeaderboardResponse {
+    return {
+        code: backendResponse.code === 200 ? 0 : backendResponse.code,
+        msg: backendResponse.msg,
+        data: {
+            list: backendResponse.rows,
+            total: backendResponse.total,
+            pageSize: params?.pageSize || 10,
+            current: params?.pageNum || 1,
+        }
+    };
+}
+
+/**
+ * 获取活动列表
+ */
+export async function getActivityList(params?: ActivityListParams): Promise<StandardizedActivityListResponse> {
+    if (USE_MOCK) {
+        let filteredList = mockActivities;
+
+        // 按状态筛选
+        if (params?.status !== undefined) {
+            filteredList = filteredList.filter(item => item.status === params.status);
+        }
+
+        // 按活动类型筛选
+        if (params?.activityType) {
+            filteredList = filteredList.filter(item => item.activityType === params.activityType);
+        }
+
+        // 按活动名称搜索
+        if (params?.activityName) {
+            filteredList = filteredList.filter(item =>
+                item.activityName.includes(params.activityName!)
+            );
+        }
+
+        // 模拟后端返回格式
+        const mockBackendResponse: GetActivityListResponse = {
+            total: filteredList.length,
+            rows: filteredList,
+            code: 200,
+            msg: '查询成功'
+        };
+
+        return normalizeActivityListResponse(mockBackendResponse, params);
+    }
+
+    try {
+        // 调用实际后端接口
+        const response: GetActivityListResponse = await request('/api/system/activity/list', {
+            method: 'GET',
+            params: {
+                pageNum: params?.pageNum,
+                pageSize: params?.pageSize,
+                activityName: params?.activityName,
+                activityType: params?.activityType,
+                status: params?.status,
+            },
+        });
+
+        // 标准化响应格式
+        return normalizeActivityListResponse(response, params);
+    } catch (error) {
+        console.error('获取活动列表失败:', error);
+        throw error;
+    }
+}
+
+/**
+ * 参与活动
+ */
+export async function participateActivity(activityId: number): Promise<ParticipateActivityResponse> {
+    if (USE_MOCK) {
+        return Promise.resolve({
+            code: 0,
+            msg: '参与成功',
+            data: null,
+        });
+    }
+
+    try {
+        return await request(`/api/system/activity/participate/${activityId}`, {
+            method: 'POST',
+        });
+    } catch (error) {
+        console.error('参与活动失败:', error);
+        throw error;
+    }
+}
+
+/**
+ * 获取排行榜
+ */
+export async function getLeaderboard(params?: LeaderboardParams): Promise<StandardizedLeaderboardResponse> {
+    if (USE_MOCK) {
+        const mockBackendResponse: GetLeaderboardResponse = {
+            total: mockLeaderboard.length,
+            rows: mockLeaderboard,
+            code: 200,
+            msg: '查询成功'
+        };
+
+        return normalizeLeaderboardResponse(mockBackendResponse, params);
+    }
+
+    try {
+        const response: GetLeaderboardResponse = await request('/api/system/activity/leaderboard', {
+            method: 'GET',
+            params: {
+                pageNum: params?.pageNum,
+                pageSize: params?.pageSize,
+            },
+        });
+
+        return normalizeLeaderboardResponse(response, params);
+    } catch (error) {
+        console.error('获取排行榜失败:', error);
+        throw error;
+    }
+}
\ No newline at end of file