Merge branch 'main' of https://gittea.biveki.ru/Sfera/sfera
This commit is contained in:
@ -1033,6 +1033,70 @@ export const REMOVE_FROM_FAVORITES = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
// Мутации для внешней рекламы
|
||||
export const CREATE_EXTERNAL_AD = gql`
|
||||
mutation CreateExternalAd($input: ExternalAdInput!) {
|
||||
createExternalAd(input: $input) {
|
||||
success
|
||||
message
|
||||
externalAd {
|
||||
id
|
||||
name
|
||||
url
|
||||
cost
|
||||
date
|
||||
nmId
|
||||
clicks
|
||||
organizationId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_EXTERNAL_AD = gql`
|
||||
mutation UpdateExternalAd($id: ID!, $input: ExternalAdInput!) {
|
||||
updateExternalAd(id: $id, input: $input) {
|
||||
success
|
||||
message
|
||||
externalAd {
|
||||
id
|
||||
name
|
||||
url
|
||||
cost
|
||||
date
|
||||
nmId
|
||||
clicks
|
||||
organizationId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_EXTERNAL_AD = gql`
|
||||
mutation DeleteExternalAd($id: ID!) {
|
||||
deleteExternalAd(id: $id) {
|
||||
success
|
||||
message
|
||||
externalAd {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_EXTERNAL_AD_CLICKS = gql`
|
||||
mutation UpdateExternalAdClicks($id: ID!, $clicks: Int!) {
|
||||
updateExternalAdClicks(id: $id, clicks: $clicks) {
|
||||
success
|
||||
message
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Мутации для категорий
|
||||
export const CREATE_CATEGORY = gql`
|
||||
mutation CreateCategory($input: CategoryInput!) {
|
||||
@ -1248,3 +1312,25 @@ export const UPDATE_SUPPLY_ORDER_STATUS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Мутации для кеша склада WB
|
||||
export const SAVE_WB_WAREHOUSE_CACHE = gql`
|
||||
mutation SaveWBWarehouseCache($input: WBWarehouseCacheInput!) {
|
||||
saveWBWarehouseCache(input: $input) {
|
||||
success
|
||||
message
|
||||
fromCache
|
||||
cache {
|
||||
id
|
||||
organizationId
|
||||
cacheDate
|
||||
data
|
||||
totalProducts
|
||||
totalStocks
|
||||
totalReserved
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -829,6 +829,27 @@ export const GET_WILDBERRIES_CAMPAIGNS_LIST = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_EXTERNAL_ADS = gql`
|
||||
query GetExternalAds($dateFrom: String!, $dateTo: String!) {
|
||||
getExternalAds(dateFrom: $dateFrom, dateTo: $dateTo) {
|
||||
success
|
||||
message
|
||||
externalAds {
|
||||
id
|
||||
name
|
||||
url
|
||||
cost
|
||||
date
|
||||
nmId
|
||||
clicks
|
||||
organizationId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// Админ запросы
|
||||
export const ADMIN_ME = gql`
|
||||
query AdminMe {
|
||||
@ -934,3 +955,25 @@ export const GET_PENDING_SUPPLIES_COUNT = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Запросы для кеша склада WB
|
||||
export const GET_WB_WAREHOUSE_DATA = gql`
|
||||
query GetWBWarehouseData {
|
||||
getWBWarehouseData {
|
||||
success
|
||||
message
|
||||
fromCache
|
||||
cache {
|
||||
id
|
||||
organizationId
|
||||
cacheDate
|
||||
data
|
||||
totalProducts
|
||||
totalStocks
|
||||
totalReserved
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -5115,6 +5115,59 @@ export const resolvers = {
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
updateExternalAdClicks: async (
|
||||
_: unknown,
|
||||
{ id, clicks }: { id: string; clicks: number },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
// Проверяем, что реклама принадлежит организации пользователя
|
||||
const existingAd = await prisma.externalAd.findFirst({
|
||||
where: {
|
||||
id,
|
||||
organizationId: user.organization.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingAd) {
|
||||
throw new GraphQLError("Внешняя реклама не найдена");
|
||||
}
|
||||
|
||||
await prisma.externalAd.update({
|
||||
where: { id },
|
||||
data: { clicks },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Клики успешно обновлены",
|
||||
externalAd: null,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating external ad clicks:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка обновления кликов",
|
||||
externalAd: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Резолверы типов
|
||||
@ -6117,14 +6170,394 @@ const wildberriesQueries = {
|
||||
},
|
||||
};
|
||||
|
||||
// Резолверы для внешней рекламы
|
||||
const externalAdQueries = {
|
||||
getExternalAds: async (
|
||||
_: unknown,
|
||||
{ dateFrom, dateTo }: { dateFrom: string; dateTo: string },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
const externalAds = await prisma.externalAd.findMany({
|
||||
where: {
|
||||
organizationId: user.organization.id,
|
||||
date: {
|
||||
gte: new Date(dateFrom),
|
||||
lte: new Date(dateTo + 'T23:59:59.999Z'),
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
date: 'desc',
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: null,
|
||||
externalAds: externalAds.map(ad => ({
|
||||
...ad,
|
||||
cost: parseFloat(ad.cost.toString()),
|
||||
date: ad.date.toISOString().split('T')[0],
|
||||
createdAt: ad.createdAt.toISOString(),
|
||||
updatedAt: ad.updatedAt.toISOString(),
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching external ads:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка получения внешней рекламы",
|
||||
externalAds: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const externalAdMutations = {
|
||||
createExternalAd: async (
|
||||
_: unknown,
|
||||
{ input }: { input: { name: string; url: string; cost: number; date: string; nmId: string } },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
const externalAd = await prisma.externalAd.create({
|
||||
data: {
|
||||
name: input.name,
|
||||
url: input.url,
|
||||
cost: input.cost,
|
||||
date: new Date(input.date),
|
||||
nmId: input.nmId,
|
||||
organizationId: user.organization.id,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Внешняя реклама успешно создана",
|
||||
externalAd: {
|
||||
...externalAd,
|
||||
cost: parseFloat(externalAd.cost.toString()),
|
||||
date: externalAd.date.toISOString().split('T')[0],
|
||||
createdAt: externalAd.createdAt.toISOString(),
|
||||
updatedAt: externalAd.updatedAt.toISOString(),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating external ad:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка создания внешней рекламы",
|
||||
externalAd: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
updateExternalAd: async (
|
||||
_: unknown,
|
||||
{ id, input }: { id: string; input: { name: string; url: string; cost: number; date: string; nmId: string } },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
// Проверяем, что реклама принадлежит организации пользователя
|
||||
const existingAd = await prisma.externalAd.findFirst({
|
||||
where: {
|
||||
id,
|
||||
organizationId: user.organization.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingAd) {
|
||||
throw new GraphQLError("Внешняя реклама не найдена");
|
||||
}
|
||||
|
||||
const externalAd = await prisma.externalAd.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: input.name,
|
||||
url: input.url,
|
||||
cost: input.cost,
|
||||
date: new Date(input.date),
|
||||
nmId: input.nmId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Внешняя реклама успешно обновлена",
|
||||
externalAd: {
|
||||
...externalAd,
|
||||
cost: parseFloat(externalAd.cost.toString()),
|
||||
date: externalAd.date.toISOString().split('T')[0],
|
||||
createdAt: externalAd.createdAt.toISOString(),
|
||||
updatedAt: externalAd.updatedAt.toISOString(),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error updating external ad:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка обновления внешней рекламы",
|
||||
externalAd: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
deleteExternalAd: async (
|
||||
_: unknown,
|
||||
{ id }: { id: string },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
// Проверяем, что реклама принадлежит организации пользователя
|
||||
const existingAd = await prisma.externalAd.findFirst({
|
||||
where: {
|
||||
id,
|
||||
organizationId: user.organization.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existingAd) {
|
||||
throw new GraphQLError("Внешняя реклама не найдена");
|
||||
}
|
||||
|
||||
await prisma.externalAd.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Внешняя реклама успешно удалена",
|
||||
externalAd: null,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error deleting external ad:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка удаления внешней рекламы",
|
||||
externalAd: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// Резолверы для кеша склада WB
|
||||
const wbWarehouseCacheQueries = {
|
||||
getWBWarehouseData: async (
|
||||
_: unknown,
|
||||
__: unknown,
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
// Получаем текущую дату без времени
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
// Ищем кеш за сегодня
|
||||
const cache = await prisma.wBWarehouseCache.findFirst({
|
||||
where: {
|
||||
organizationId: user.organization.id,
|
||||
cacheDate: today,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
|
||||
if (cache) {
|
||||
// Возвращаем данные из кеша
|
||||
return {
|
||||
success: true,
|
||||
message: "Данные получены из кеша",
|
||||
cache: {
|
||||
...cache,
|
||||
cacheDate: cache.cacheDate.toISOString().split('T')[0],
|
||||
createdAt: cache.createdAt.toISOString(),
|
||||
updatedAt: cache.updatedAt.toISOString(),
|
||||
},
|
||||
fromCache: true,
|
||||
};
|
||||
} else {
|
||||
// Кеша нет, нужно загрузить данные из API
|
||||
return {
|
||||
success: true,
|
||||
message: "Кеш не найден, требуется загрузка из API",
|
||||
cache: null,
|
||||
fromCache: false,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting WB warehouse cache:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка получения кеша склада WB",
|
||||
cache: null,
|
||||
fromCache: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const wbWarehouseCacheMutations = {
|
||||
saveWBWarehouseCache: async (
|
||||
_: unknown,
|
||||
{ input }: { input: { data: string; totalProducts: number; totalStocks: number; totalReserved: number } },
|
||||
context: Context
|
||||
) => {
|
||||
if (!context.user) {
|
||||
throw new GraphQLError("Требуется авторизация", {
|
||||
extensions: { code: "UNAUTHENTICATED" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!user?.organization) {
|
||||
throw new GraphQLError("Организация не найдена");
|
||||
}
|
||||
|
||||
// Получаем текущую дату без времени
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
// Используем upsert для создания или обновления кеша
|
||||
const cache = await prisma.wBWarehouseCache.upsert({
|
||||
where: {
|
||||
organizationId_cacheDate: {
|
||||
organizationId: user.organization.id,
|
||||
cacheDate: today,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
data: input.data,
|
||||
totalProducts: input.totalProducts,
|
||||
totalStocks: input.totalStocks,
|
||||
totalReserved: input.totalReserved,
|
||||
},
|
||||
create: {
|
||||
organizationId: user.organization.id,
|
||||
cacheDate: today,
|
||||
data: input.data,
|
||||
totalProducts: input.totalProducts,
|
||||
totalStocks: input.totalStocks,
|
||||
totalReserved: input.totalReserved,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Кеш склада WB успешно сохранен",
|
||||
cache: {
|
||||
...cache,
|
||||
cacheDate: cache.cacheDate.toISOString().split('T')[0],
|
||||
createdAt: cache.createdAt.toISOString(),
|
||||
updatedAt: cache.updatedAt.toISOString(),
|
||||
},
|
||||
fromCache: false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error saving WB warehouse cache:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : "Ошибка сохранения кеша склада WB",
|
||||
cache: null,
|
||||
fromCache: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Добавляем админ запросы и мутации к основным резолверам
|
||||
resolvers.Query = {
|
||||
...resolvers.Query,
|
||||
...adminQueries,
|
||||
...wildberriesQueries,
|
||||
...externalAdQueries,
|
||||
...wbWarehouseCacheQueries,
|
||||
};
|
||||
|
||||
resolvers.Mutation = {
|
||||
...resolvers.Mutation,
|
||||
...adminMutations,
|
||||
...externalAdMutations,
|
||||
...wbWarehouseCacheMutations,
|
||||
};
|
||||
|
@ -108,6 +108,12 @@ export const typeDefs = gql`
|
||||
|
||||
# Список кампаний Wildberries
|
||||
getWildberriesCampaignsList: WildberriesCampaignsListResponse!
|
||||
|
||||
# Типы для внешней рекламы
|
||||
getExternalAds(dateFrom: String!, dateTo: String!): ExternalAdsResponse!
|
||||
|
||||
# Типы для кеша склада WB
|
||||
getWBWarehouseData: WBWarehouseCacheResponse!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
@ -244,6 +250,12 @@ export const typeDefs = gql`
|
||||
# Админ мутации
|
||||
adminLogin(username: String!, password: String!): AdminAuthResponse!
|
||||
adminLogout: Boolean!
|
||||
|
||||
# Типы для внешней рекламы
|
||||
createExternalAd(input: ExternalAdInput!): ExternalAdResponse!
|
||||
updateExternalAd(id: ID!, input: ExternalAdInput!): ExternalAdResponse!
|
||||
deleteExternalAd(id: ID!): ExternalAdResponse!
|
||||
updateExternalAdClicks(id: ID!, clicks: Int!): ExternalAdResponse!
|
||||
}
|
||||
|
||||
# Типы данных
|
||||
@ -1149,4 +1161,84 @@ export const typeDefs = gql`
|
||||
advertId: Int!
|
||||
changeTime: String!
|
||||
}
|
||||
|
||||
# Типы для внешней рекламы
|
||||
type ExternalAd {
|
||||
id: ID!
|
||||
name: String!
|
||||
url: String!
|
||||
cost: Float!
|
||||
date: String!
|
||||
nmId: String!
|
||||
clicks: Int!
|
||||
organizationId: String!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
input ExternalAdInput {
|
||||
name: String!
|
||||
url: String!
|
||||
cost: Float!
|
||||
date: String!
|
||||
nmId: String!
|
||||
}
|
||||
|
||||
type ExternalAdResponse {
|
||||
success: Boolean!
|
||||
message: String
|
||||
externalAd: ExternalAd
|
||||
}
|
||||
|
||||
type ExternalAdsResponse {
|
||||
success: Boolean!
|
||||
message: String
|
||||
externalAds: [ExternalAd!]!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
getExternalAds(dateFrom: String!, dateTo: String!): ExternalAdsResponse!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createExternalAd(input: ExternalAdInput!): ExternalAdResponse!
|
||||
updateExternalAd(id: ID!, input: ExternalAdInput!): ExternalAdResponse!
|
||||
deleteExternalAd(id: ID!): ExternalAdResponse!
|
||||
updateExternalAdClicks(id: ID!, clicks: Int!): ExternalAdResponse!
|
||||
}
|
||||
|
||||
# Типы для кеша склада WB
|
||||
type WBWarehouseCache {
|
||||
id: ID!
|
||||
organizationId: String!
|
||||
cacheDate: String!
|
||||
data: String! # JSON строка с данными
|
||||
totalProducts: Int!
|
||||
totalStocks: Int!
|
||||
totalReserved: Int!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
type WBWarehouseCacheResponse {
|
||||
success: Boolean!
|
||||
message: String
|
||||
cache: WBWarehouseCache
|
||||
fromCache: Boolean! # Указывает, получены ли данные из кеша
|
||||
}
|
||||
|
||||
input WBWarehouseCacheInput {
|
||||
data: String! # JSON строка с данными склада
|
||||
totalProducts: Int!
|
||||
totalStocks: Int!
|
||||
totalReserved: Int!
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
getWBWarehouseData: WBWarehouseCacheResponse!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
saveWBWarehouseCache(input: WBWarehouseCacheInput!): WBWarehouseCacheResponse!
|
||||
}
|
||||
`;
|
||||
|
Reference in New Issue
Block a user