Procházet zdrojové kódy

添加视频日志上报

nieyuge před 1 dnem
rodič
revize
b4c0462642

+ 200 - 0
src/hooks/useLogger.ts

@@ -0,0 +1,200 @@
+import http from '@src/http';
+import { uploadLogApi } from '@src/http/api';
+import sso from '@src/http/sso';
+import { VideoItem } from '@src/views/publishContent/types';
+import { RecentNotUsedType, SortTypeEnum, TagType, VideoLibraryType } from '@src/views/publishContent/weCom/components/videoSelectModal';
+import { VideoSearchPlanType } from '@src/views/publishContent/weCom/type';
+
+type NormalLogParams = {
+	traceId: number;
+	requestId: number;
+	planType?: VideoSearchPlanType;
+	gzhAccountId?: number;
+	subChannel?: string;
+}
+
+type VideoListQueryLogParams = {
+	category?: string;
+	title?: string;
+	recentNotUsed?: RecentNotUsedType;
+	sortType?: SortTypeEnum;
+	tags?: TagType[];
+	videoLibraryType?: VideoLibraryType;
+} & NormalLogParams;
+
+type VideoLogParams = {
+	videoId: number;
+	score?: number;
+	tags?: TagType[];
+	title?: string;
+	cover?: string;
+	libraryType?: VideoLibraryType;
+	playTime?: number;
+	idx?: number;
+	collect?: number;
+} & NormalLogParams;
+
+interface PublishPlanLogParams extends NormalLogParams {
+	videoList: VideoItem[];
+}
+
+const useLogger = () => {
+	const token = sso.getUserInfo()?.token;
+	const channel = sso.getUserInfo()?.channel;
+	const userId = sso.getUserInfo()?.id;
+	const timestamp = Date.now();
+	const userAgent = navigator.userAgent;
+	const page = window.location.pathname;
+	const uploadLog = async ({ businessType, objectType, extParams }: { businessType: string, objectType?: string, extParams?: Record<string, any> }) => {
+		const log = {
+			token,
+			channel,
+			userId,
+			page,
+			userAgent,
+			timestamp,
+			businessType,
+			objectType,
+			extParams,
+		};
+		const response = await http.post(uploadLogApi, {
+			key: 'content-platform-user-behavior',
+			data: log,
+		});
+		return response;
+	}
+
+	const uploadLogVideoListQuery = ({
+		traceId,
+		requestId,
+		planType,
+		gzhAccountId,
+		subChannel,
+		category,
+		title,
+		recentNotUsed,
+		sortType,
+		tags,
+		videoLibraryType,
+	}:VideoListQueryLogParams) => { 
+		return uploadLog({
+			businessType: 'video_list_query',
+			objectType: 'video_list',
+			extParams: {
+				traceId,
+				requestId,
+				planType,
+				gzhAccountId,
+				subChannel,
+				category,
+				title,
+				recentNotUsed,
+				sortType,
+				tags,
+				videoLibraryType,
+			},
+		});
+	}
+
+	const uploadLogVideoPlay = ({videoId, traceId, requestId, planType, gzhAccountId, subChannel}: VideoLogParams) => { 
+		return uploadLog({
+			businessType: 'video_play',
+			objectType: 'video',
+			extParams: {
+				videoId,
+				traceId,
+				requestId,
+				planType,
+				gzhAccountId,
+				subChannel,
+			},
+		});
+	}
+
+	const uploadLogVideoPlayEnd = ({videoId, playTime, traceId, requestId, planType, gzhAccountId, subChannel}: VideoLogParams) => { 
+		return uploadLog({
+			businessType: 'video_play_end',
+			objectType: 'video',
+			extParams: {
+				videoId,
+				playTime,
+				traceId,
+				requestId,
+				planType,
+				gzhAccountId,
+				subChannel,
+			},
+		});
+	}
+
+	const uploadLogVideoView = ({videoId, idx, score, tags, title, cover, libraryType, traceId, requestId, planType, gzhAccountId, subChannel}: VideoLogParams) => { 
+		return uploadLog({
+			businessType: 'video_view',
+			objectType: 'video',
+			extParams: {
+				videoId,
+				idx,
+				score,
+				tags,
+				title,
+				cover,
+				libraryType,
+				traceId,
+				requestId,
+				planType,
+				gzhAccountId,
+				subChannel,
+			},
+		});
+	}
+
+	const uploadLogVideoCollect = ({videoId, traceId, requestId, collect, planType, gzhAccountId, subChannel}: VideoLogParams) => { 
+		return uploadLog({
+			businessType: 'video_collect',
+			objectType: 'video',
+			extParams: {
+				videoId,
+				traceId,
+				requestId,
+				collect,
+				planType,
+				gzhAccountId,
+				subChannel,
+			},
+		});
+	}
+
+	const uploadLogVideoCreatePublish = ({
+		videoList,
+		traceId,
+		requestId,
+		planType,
+		gzhAccountId,
+		subChannel
+	}: PublishPlanLogParams) => { 
+		return uploadLog({
+			businessType: 'publish_plan_create',
+			objectType: 'publish_plan',
+			extParams: {
+				videoList,
+				traceId,
+				requestId,
+				planType,
+				gzhAccountId,
+				subChannel,
+			},
+		});
+	}
+
+	return {
+		uploadLog,
+		uploadLogVideoPlay,
+		uploadLogVideoView,
+		uploadLogVideoPlayEnd,
+		uploadLogVideoCollect,
+		uploadLogVideoListQuery,
+		uploadLogVideoCreatePublish,
+	};
+};
+
+export default useLogger;

+ 3 - 0
src/http/api.ts

@@ -63,3 +63,6 @@ export const uploadDeleteVideo = `${import.meta.env.VITE_API_URL}/contentPlatfor
 // 设置绑定微信用户
 export const getBindPQUserInfo = `${import.meta.env.VITE_API_URL}/contentPlatform/setting/getBindPQUserInfo`
 export const bindPQUser = `${import.meta.env.VITE_API_URL}/contentPlatform/setting/webLogin`
+
+// 日志上报
+export const uploadLogApi = `${import.meta.env.VITE_API_URL}/contentPlatform/logHub/upload`

+ 32 - 0
src/views/publishContent/types.ts

@@ -0,0 +1,32 @@
+import { TagType } from "./weCom/components/videoSelectModal";
+
+export enum CollectedStatusEnum {
+	已收藏 = 1,
+	未收藏 = 0,
+}
+
+export enum VideoStatusEnum {
+	已下架 = 0,
+	正常 = 1,
+}
+export interface VideoItem {
+  videoId: number;
+  video: string;
+  title: string;
+  cover: string;
+  customCover?: string;
+  customCoverType?: number;
+  customTitle?: string;
+  score?: number;
+  pageUrl?: string;
+  shareCover?: string;
+  industryFissionRate?: number;
+  channelFissionRate?: number;
+  videoLibraryType?: number;
+  recommendScore?: number;
+  tags?: TagType[];
+	scene?: 0 | 1;
+	collect?: CollectedStatusEnum;
+	status?: VideoStatusEnum;
+	requestId?: number;
+}

+ 10 - 0
src/views/publishContent/weCom/components/addPlanModal/index.tsx

@@ -6,6 +6,7 @@ import { WeComPlanType, WeVideoItem, AddWeComPlanParam } from '../../type';
 import { CloseOutlined, PlusOutlined, CaretRightFilled } from '@ant-design/icons';
 import VideoSelectModal from '../videoSelectModal';
 import VideoPlayModal from '../videoPlayModal';
+import useLogger from '@src/hooks/useLogger';
 
 const { Paragraph } = Typography;
 
@@ -21,6 +22,7 @@ const AddPlanModal: React.FC<{
 	const [selectedVideos, setSelectedVideos] = useState<WeVideoItem[]>([]);
 	const [isVideoSelectVisible, setIsVideoSelectVisible] = useState(false);
 	const [playingVideo, setPlayingVideo] = useState<WeVideoItem | null>(null);
+	const { uploadLogVideoCreatePublish } = useLogger();
 	
 	useEffect(() => {
 		form.setFieldsValue({
@@ -51,6 +53,14 @@ const AddPlanModal: React.FC<{
 				message.error('请选择发布内容');
 				return;
 			}
+			// 上报日志
+			uploadLogVideoCreatePublish({
+				videoList: selectedVideos,
+				traceId: Date.now(),
+				requestId: Date.now(),
+				planType: type === WeComPlanType.社群 ? '企微社群' : '企微自动回复',
+				subChannel: values.subChannel || 'weCom',
+			});
 			onOk({
 				type: values.type,
 				subChannel: values.subChannel,

+ 51 - 0
src/views/publishContent/weCom/components/videoSelectModal/index.tsx

@@ -18,6 +18,7 @@ import { getVideoContentListApi, getUploadVideoContentListApi } from '@src/http/
 import { useVideoCategoryOptions } from '@src/views/publishContent/weGZH/hooks/useVideoCategoryOptions';
 import { WeComPlanType, WeVideoItem, VideoSearchPlanType } from '@src/views/publishContent/weCom/type'
 import { enumToOptions } from '@src/utils/helper';
+import useLogger from '@src/hooks/useLogger';
 
 export enum VideoLibraryType {
 	平台视频库 = 0,
@@ -56,12 +57,25 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 	const [playingVideo, setPlayingVideo] = useState<WeVideoItem | null>(null);
 	const [videoLibraryType, setVideoLibraryType] = useState<VideoLibraryType>(VideoLibraryType.平台视频库);
 	const MAX_SELECTION = 3;
+	const { uploadLogVideoPlay, uploadLogVideoPlayEnd, uploadLogVideoCollect, uploadLogVideoListQuery, uploadLogVideoView } = useLogger();
 
 	const getVideoList = async (pageNum?: number, _pageSize?: number) => {
 		setLoading(true);
 		setCurrentPage(pageNum || currentPage);
 		setPageSize(_pageSize || pageSize);
 
+		// 上报视频列表查询日志
+		uploadLogVideoListQuery({
+			traceId: Date.now(),
+			requestId: Date.now(),
+			planType: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
+			subChannel: 'weCom',
+			category,
+			title: searchTerm,
+			sortType: sort,
+			videoLibraryType,
+		});
+
 		// 根据视频库类型选择不同的API
 		const apiUrl = videoLibraryType === VideoLibraryType.平台视频库 ? getVideoContentListApi : getUploadVideoContentListApi;
 
@@ -114,12 +128,30 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 			const newSet = new Set(prev);
 			if (newSet.has(videoId)) {
 				newSet.delete(videoId);
+				// 上报取消收藏日志
+				uploadLogVideoCollect({
+					videoId,
+					traceId: Date.now(),
+					requestId: Date.now(),
+					collect: 0,
+					planType: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
+					subChannel: 'weCom',
+				});
 			} else {
 				if (newSet.size >= MAX_SELECTION) {
 					message.warning(`最多只能选择 ${MAX_SELECTION} 条视频`);
 					return prev;
 				}
 				newSet.add(videoId);
+				// 上报收藏日志
+				uploadLogVideoCollect({
+					videoId,
+					traceId: Date.now(),
+					requestId: Date.now(),
+					collect: 1,
+					planType: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
+					subChannel: 'weCom',
+				});
 			}
 			return newSet;
 		});
@@ -135,10 +167,29 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 	};
 
 	const playVideo = (video: WeVideoItem) => {
+		// 上报视频播放日志
+		uploadLogVideoPlay({
+			videoId: video.videoId,
+			traceId: Date.now(),
+			requestId: Date.now(),
+			planType: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
+			subChannel: 'weCom',
+		});
 		setPlayingVideo(video);
 	};
 
 	const closeVideoPlayer = () => {
+		if (playingVideo) {
+			// 上报视频播放结束日志
+			uploadLogVideoPlayEnd({
+				videoId: playingVideo.videoId,
+				playTime: 0, // 实际播放时间可以从视频元素中获取,这里暂时设为0
+				traceId: Date.now(),
+				requestId: Date.now(),
+				planType: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
+				subChannel: 'weCom',
+			});
+		}
 		setPlayingVideo(null);
 	};
 

+ 11 - 0
src/views/publishContent/weGZH/components/publishPlanModal/index.tsx

@@ -8,6 +8,7 @@ import { VideoItem } from '../types'; // Import from common types
 import { GzhPlanDataType, GzhPlanType } from '../../hooks/useGzhPlanList';
 import { useAccountOptions } from '../../hooks/useAccountOptions';
 import { VideoLibraryType } from '@src/views/publishContent/weCom/components/videoSelectModal';
+import useLogger from '@src/hooks/useLogger';
 
 const { Option } = Select;
 const { Paragraph } = Typography;
@@ -35,6 +36,7 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, isSu
 	const [initialSelectedVideoId, setInitialSelectedVideoId] = useState<string | null>(null); // State for initial video selection
 	const [videoLibraryType, setVideoLibraryType] = useState<VideoLibraryType>(VideoLibraryType.平台视频库);
 	const { accountOptions, getAccountList } = useAccountOptions();
+	const { uploadLogVideoCreatePublish } = useLogger();
 
 	// 处理code参数
 	useEffect(() => {
@@ -99,6 +101,15 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({ visible, isSu
 					formData.scene = 0;
 				}
 				formData.videoList = selectedVideos;
+				// 上报日志
+				uploadLogVideoCreatePublish({
+					videoList: selectedVideos,
+					traceId: Date.now(),
+					requestId: Date.now(),
+					planType: planType,
+					gzhAccountId: formData.accountId,
+					subChannel: 'weGZH'
+				});
 				onOk(formData);
 			})
 			.catch((info) => {