| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- import axios from "axios";
- import { useUserStore } from "@/store/user";
- const API_URL = import.meta.env.VITE_API_URL;
- const VIDEO_API_URL = import.meta.env.VITE_VIDEO_API_URL;
- const plat_id = import.meta.env.VITE_VIDEO_PLAT_ID;
- const channel_id = import.meta.env.VITE_VIDEO_CHANNEL_ID;
- const user_id = import.meta.env.VITE_VIDEO_USER_ID;
- const token = import.meta.env.VITE_VIDEO_TOKEN;
- const api = axios.create({
- baseURL: API_URL,
- headers: { "Content-Type": "application/json" },
- });
- const videoApi = axios.create({
- baseURL: VIDEO_API_URL,
- headers: { "Content-Type": "multipart/form-data" },
- });
- // api请求拦截器
- api.interceptors.request.use(
- (config) => {
- const userStore = useUserStore();
- if (userStore.token) {
- config.headers.Authorization = `Bearer ${userStore.token}`;
- }
- return config;
- },
- (error) => Promise.reject(error)
- );
- // api响应拦截器
- api.interceptors.response.use(
- (response) => response,
- (error) => {
- if (error.response?.status === 401) {
- const userStore = useUserStore();
- userStore.logout();
- }
- console.error("API Error:", error.response?.data || error);
- return Promise.reject(error.response?.data || error);
- }
- );
- // videoApi响应拦截器
- videoApi.interceptors.response.use(
- (response) => response,
- (error) => {
- console.error("Video API Error:", error.response?.data || error);
- return Promise.reject(error.response?.data || error);
- }
- );
- // videoApi共用参数
- function createVideoFormData(
- device: string,
- page_count = 1,
- page_size = 20,
- extraParams: Record<string, string> = {}
- ): FormData {
- const formData = new FormData();
- const commonParams: Record<string, string> = {
- plat_id,
- channel_id,
- user_id,
- app_type: "4",
- token,
- device,
- device_type: "3",
- page_count: String(page_count),
- page_size: String(page_size),
- };
- Object.entries({ ...commonParams, ...extraParams }).forEach(([k, v]) =>
- formData.append(k, v)
- );
- return formData;
- }
- // 统一视频请求
- async function videoRequest(
- endpoint: string,
- formData: FormData
- ): Promise<any> {
- const res = await videoApi.post(endpoint, formData);
- return res.data;
- }
- /**
- * ===================== 用户相关接口 =====================
- */
- export const login = async (name: string, password: string): Promise<any> => {
- const res = await api.post("/member/login", { name, password });
- return res.data;
- };
- export const register = async (
- name: string,
- password: string,
- email?: string,
- phone?: string,
- code?: string
- ): Promise<any> => {
- const res = await api.post("/member/register", {
- name,
- password,
- email: email || null,
- phone: phone || null,
- code: code || null,
- });
- return res.data;
- };
- export const profile = async (): Promise<any> => {
- const res = await api.get("/member/profile");
- return res.data;
- };
- export const newGuest = async (code?: string, ref?: string): Promise<any> => {
- const params: any = {};
- if (code) params.code = code;
- if (ref) params.ref = ref;
- const res = await api.get("/member/guest", { params });
- return res.data;
- };
- export const upgradeGuest = async (
- userId: number,
- name: string,
- password: string,
- email?: string,
- phone?: string
- ): Promise<any> => {
- const res = await api.post("/member/guestUpgrade", {
- userId,
- name,
- password,
- email: email || null,
- phone: phone || null,
- });
- return res.data;
- };
- // 会员购买
- export const purchaseMember = async (
- userId: number,
- type: string
- ): Promise<any> => {
- const res = await api.post("/payment/vip/create", { userId, type });
- return res.data;
- };
- // 单片购买
- export const purchaseSingle = async (
- userId: number,
- resourceId: string
- ): Promise<any> => {
- const res = await api.post("/payment/single/create", { userId, resourceId });
- return res.data;
- };
- // 用户支付订单查询
- export const userQueryOrder = async (
- orderNo?: string,
- resourceId?: string
- ): Promise<any> => {
- const params = {
- orderNo: orderNo || undefined,
- resourceId: resourceId || undefined,
- };
- const res = await api.get(`/payment/user/query`, {
- params,
- });
- return res.data;
- };
- // 查询用户是否已购买特定单片资源
- export const checkSinglePurchase = async (resourceId: string): Promise<any> => {
- const res = await api.get("/payment/single/query", {
- params: { resourceId },
- });
- return res.data;
- };
- // 获取用户的单片机购买记录列表(分页)
- export const getSinglePurchaseList = async (
- page = 0,
- size = 20
- ): Promise<any> => {
- const res = await api.get("/payment/single/list", {
- params: { page, size },
- });
- return res.data;
- };
- // 获取价格配置
- export const getPriceConfig = async (): Promise<any> => {
- const res = await api.get("/config/team/user");
- return res.data;
- };
- // 更新用户信息(用户名和邮箱)
- export const updateProfile = async (
- name: string,
- email?: string
- ): Promise<any> => {
- const res = await api.put("/member/profile", {
- name,
- email: email || null,
- });
- return res.data;
- };
- // 重置密码
- export const resetPassword = async (password: string): Promise<any> => {
- const res = await api.post("/member/reset-password", {
- password,
- });
- return res.data;
- };
- // 获取团队主题颜色
- export const getTeamTheme = async (): Promise<{ themeColor: string }> => {
- const res = await api.get("/teams/my-theme");
- return res.data;
- };
- // 封禁当前用户
- export const banUser = async (): Promise<any> => {
- const res = await api.post("/member/ban");
- return res.data;
- };
- // 检查IP是否被封禁
- export const checkBanStatus = async (): Promise<{ ip: string; isBanned: boolean }> => {
- const res = await api.get("/member/check-ban");
- return res.data;
- };
- /**
- * ===================== 广告栏相关接口 =====================
- */
- // 根据位置获取广告栏列表(公开接口)
- export const getBannersByPosition = async (
- position: "top" | "middle" | "bottom"
- ): Promise<any[]> => {
- const res = await api.get(`/banners/position/${position}`);
- return res.data;
- };
- // 记录广告栏点击
- export const recordBannerClick = async (bannerId: number): Promise<any> => {
- const res = await api.post(`/banners/${bannerId}/click`);
- return res.data;
- };
- /**
- * ===================== 视频相关接口 =====================
- */
- // 视频关键字 查询接口
- export const searchVideoByKeyword = async (
- device: string,
- keyword: string,
- page_count = 1,
- page_size = 20,
- resource_type?: "long" | "short",
- lang?: string
- ): Promise<any> => {
- const formData = createVideoFormData(device, page_count, page_size, {
- keyword,
- ...(resource_type && { resource_type }),
- ...(lang && { lang }),
- });
- return videoRequest(`/media/${plat_id}/keyword`, formData);
- };
- // 视频查询接口(支持标签分类和类型查询)
- export const searchVideoByTags = async (
- device: string,
- page_count = 1,
- page_size = 20,
- tag?: string,
- resource_type?: "long" | "short",
- sort?: "view" | "like" | "time"
- ): Promise<any> => {
- const formData = createVideoFormData(device, page_count, page_size, {
- ...(tag && { tag }),
- ...(resource_type && { resource_type }),
- ...(sort && { sort }),
- });
- return videoRequest(`/media/${plat_id}/search`, formData);
- };
- // 视频详情接口
- export const getVideoDetail = async (
- device: string,
- resource_id: string
- ): Promise<any> => {
- const formData = createVideoFormData(device, 1, 20, { resource_id });
- return videoRequest(`/media/${user_id}/movie/play`, formData);
- };
- // 视频顶部标签 查询接口 - 已废弃,现在使用固定的菜单数据
- // export const getVideoMenu = async (
- // device: string,
- // type: "1" | "2",
- // page_count = 1,
- // page_size = 20
- // ): Promise<any> => {
- // const formData = createVideoFormData(device, page_count, page_size, { type });
- // return videoRequest(`/media/${plat_id}/menu`, formData);
- // };
- // 点播集 查询接口
- export const getVodList = async (
- device: string,
- hash: string,
- page_count = 1,
- page_size = 20
- ): Promise<any> => {
- const formData = createVideoFormData(device, page_count, page_size, { hash });
- return videoRequest(`/media/${plat_id}/menu/vods`, formData);
- };
- // ===================== 全局请求拦截器:移除 HLS 视频相关文件的 Range 请求头 =====================
- let originalXHROpen: any = null;
- let originalXHRSetRequestHeader: any = null;
- let originalFetch: any = null;
- let hlsInterceptorActive = false;
- const shouldRemoveRangeHeader = (url: string): boolean => {
- if (!url) return false;
- if (/\.m3u8(\?|$)/i.test(url)) {
- return true;
- }
- if (
- /\/dx\d+/i.test(url) || // 匹配 dx002, dx003 等
- /\/[^\/]+\.ts(\?|$)/i.test(url) || // 匹配 .ts 分片文件
- /\/segment\d+/i.test(url) || // 匹配 segment1, segment2 等
- /\/chunk\d+/i.test(url)
- ) {
- return true;
- }
- return false;
- };
- /**
- * 设置全局拦截器以移除 HLS 视频相关文件的 Range 请求头
- */
- const setupHLSInterceptor = (): void => {
- if (hlsInterceptorActive) return;
- originalXHROpen = XMLHttpRequest.prototype.open;
- originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
- originalFetch = window.fetch;
- XMLHttpRequest.prototype.open = function (
- method: string,
- url: string | URL,
- ...rest: any[]
- ) {
- (this as any)._url = url.toString();
- return originalXHROpen.apply(this, [method, url, ...rest]);
- };
- XMLHttpRequest.prototype.setRequestHeader = function (
- header: string,
- value: string
- ) {
- const url = (this as any)._url || "";
- if (shouldRemoveRangeHeader(url) && header.toLowerCase() === "range") {
- return;
- }
- return originalXHRSetRequestHeader.apply(this, [header, value]);
- };
- window.fetch = function (
- input: RequestInfo | URL,
- init?: RequestInit
- ): Promise<Response> {
- const url =
- typeof input === "string"
- ? input
- : input instanceof URL
- ? input.toString()
- : (input as Request).url;
- if (shouldRemoveRangeHeader(url)) {
- const modifiedInit = { ...init };
- if (modifiedInit.headers) {
- const headers = new Headers(modifiedInit.headers);
- headers.delete("Range");
- headers.delete("range");
- modifiedInit.headers = headers;
- } else {
- modifiedInit.headers = new Headers();
- }
- return originalFetch.apply(this, [input, modifiedInit]);
- }
- return originalFetch.apply(this, [input, init]);
- };
- hlsInterceptorActive = true;
- console.log("HLS Range Header Interceptor Activated");
- };
- if (typeof window !== "undefined") {
- setupHLSInterceptor();
- }
- export default api;
|