Merge branch 'mayankSprint2' of http://git.wdipl.com/Mayank.Mishra/MinglarBackendNestJS into paritosh-main1
This commit is contained in:
@@ -107,6 +107,21 @@ getLandingPageDetails:
|
||||
path: /user/activities/get-landing-page-details
|
||||
method: get
|
||||
|
||||
getSurpriseMePageDetails:
|
||||
handler: src/modules/user/handlers/activities/surpriseMePage.handler
|
||||
memorySize: 384
|
||||
package:
|
||||
patterns:
|
||||
- 'src/modules/user/activities/**'
|
||||
- ${file(./serverless/patterns/base.yml):pattern1}
|
||||
- ${file(./serverless/patterns/base.yml):pattern2}
|
||||
- ${file(./serverless/patterns/base.yml):pattern3}
|
||||
- ${file(./serverless/patterns/base.yml):pattern4}
|
||||
events:
|
||||
- httpApi:
|
||||
path: /user/activities/get-surprise-me-page-details
|
||||
method: get
|
||||
|
||||
getActivityDetailsById:
|
||||
handler: src/modules/user/handlers/activities/getByIdActivityDetails.handler
|
||||
memorySize: 384
|
||||
|
||||
61
src/modules/user/handlers/activities/surpriseMePage.ts
Normal file
61
src/modules/user/handlers/activities/surpriseMePage.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
||||
import { safeHandler } from '../../../../common/utils/handlers/safeHandler';
|
||||
import { prismaClient } from '../../../../common/database/prisma.lambda.service';
|
||||
import ApiError from '../../../../common/utils/helper/ApiError';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { verifyUserToken } from '../../../../common/middlewares/jwt/authForUser';
|
||||
|
||||
const userService = new UserService(prismaClient);
|
||||
|
||||
export const handler = safeHandler(async (
|
||||
event: APIGatewayProxyEvent,
|
||||
context?: Context
|
||||
): Promise<APIGatewayProxyResult> => {
|
||||
// Extract token from headers
|
||||
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
||||
if (!token) {
|
||||
throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
|
||||
}
|
||||
|
||||
// Verify token and get user info
|
||||
const userInfo = await verifyUserToken(token);
|
||||
const userId = Number(userInfo.id);
|
||||
|
||||
if (!userId || isNaN(userId)) {
|
||||
throw new ApiError(400, 'Invalid user ID');
|
||||
}
|
||||
|
||||
const page = Number(event.queryStringParameters?.page ?? 1);
|
||||
const limit = Number(event.queryStringParameters?.limit ?? 20);
|
||||
const countryName = event.queryStringParameters?.countryName ?? '';
|
||||
const stateName = event.queryStringParameters?.stateName ?? '';
|
||||
const cityName = event.queryStringParameters?.cityName ?? '';
|
||||
|
||||
if (page < 1 || limit < 1) {
|
||||
throw new ApiError(400, 'Invalid pagination values');
|
||||
}
|
||||
|
||||
// Fetch user with their HostHeader stepper info
|
||||
const result = await userService.getSurpriseMeDetails(
|
||||
userId,
|
||||
page,
|
||||
limit,
|
||||
countryName,
|
||||
stateName,
|
||||
cityName,
|
||||
);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
message: 'Data retrieved successfully',
|
||||
data: result,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -851,6 +851,335 @@ export class UserService {
|
||||
return data;
|
||||
}
|
||||
|
||||
async getSurpriseMeDetails(
|
||||
userId: number,
|
||||
page: number,
|
||||
limit: number,
|
||||
countryName: string,
|
||||
stateName: string,
|
||||
cityName: string
|
||||
) {
|
||||
const data = await this.prisma.$transaction(async (tx) => {
|
||||
|
||||
/* =====================================================
|
||||
1️⃣ USER LOCATION
|
||||
===================================================== */
|
||||
const userAddressDetails = await tx.userAddressDetails.findFirst({
|
||||
where: { userXid: userId },
|
||||
select: {
|
||||
stateXid: true,
|
||||
cityXid: true,
|
||||
countryXid: true,
|
||||
},
|
||||
});
|
||||
|
||||
let effectiveLocation: {
|
||||
countryXid?: number | null;
|
||||
stateXid?: number | null;
|
||||
cityXid?: number | null;
|
||||
} | null = null;
|
||||
|
||||
if (countryName && stateName && cityName) {
|
||||
effectiveLocation = await findOrCreateLocation(tx, {
|
||||
countryName,
|
||||
stateName,
|
||||
cityName,
|
||||
});
|
||||
} else if (userAddressDetails) {
|
||||
effectiveLocation = {
|
||||
countryXid: userAddressDetails.countryXid,
|
||||
stateXid: userAddressDetails.stateXid,
|
||||
cityXid: userAddressDetails.cityXid,
|
||||
};
|
||||
}
|
||||
|
||||
const effectiveCountryXid = effectiveLocation?.countryXid ?? null;
|
||||
const effectiveStateXid = effectiveLocation?.stateXid ?? null;
|
||||
|
||||
/* =====================================================
|
||||
2️⃣ USER INTERESTS (TO EXCLUDE)
|
||||
===================================================== */
|
||||
const userInterests = await tx.userInterests.findMany({
|
||||
where: { userXid: userId, isActive: true },
|
||||
select: { interestXid: true },
|
||||
});
|
||||
|
||||
const userInterestTypeIds = await tx.activityTypes.findMany({
|
||||
where: {
|
||||
interestXid: { in: userInterests.map(i => i.interestXid) },
|
||||
isActive: true,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
const excludedActivityTypeIds = userInterestTypeIds.map(a => a.id);
|
||||
|
||||
const excludeUserInterestCondition =
|
||||
excludedActivityTypeIds.length > 0
|
||||
? { activityTypeXid: { notIn: excludedActivityTypeIds } }
|
||||
: {};
|
||||
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
/* =====================================================
|
||||
3️⃣ OTHER INTERESTS (GROUPED WITH ACTIVITIES)
|
||||
===================================================== */
|
||||
const otherInterests = await tx.interests.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
id: { notIn: userInterests.map(i => i.interestXid) },
|
||||
},
|
||||
orderBy: { interestName: 'asc' },
|
||||
select: {
|
||||
id: true,
|
||||
interestName: true,
|
||||
interestColor: true,
|
||||
interestImage: true,
|
||||
},
|
||||
});
|
||||
|
||||
const otherInterestActivities = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
...excludeUserInterestCondition,
|
||||
},
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { id: 'desc' },
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityType: {
|
||||
select: {
|
||||
interestXid: true,
|
||||
energyLevel: true,
|
||||
},
|
||||
},
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: { id: true, mediaFileName: true, mediaType: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const formattedOtherInterestActivities = await Promise.all(
|
||||
otherInterestActivities.map(async a => ({
|
||||
interestXid: a.activityType.interestXid,
|
||||
activityId: a.id,
|
||||
activityTitle: a.activityTitle,
|
||||
energyLevel: a.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(a.ActivitiesMedia),
|
||||
}))
|
||||
);
|
||||
|
||||
const interestsWithActivities = otherInterests.map(interest => ({
|
||||
interestId: interest.id,
|
||||
interestName: interest.interestName,
|
||||
interestColor: interest.interestColor,
|
||||
interestImage: interest.interestImage,
|
||||
page,
|
||||
limit,
|
||||
hasMore: formattedOtherInterestActivities.length === limit,
|
||||
activities: formattedOtherInterestActivities.filter(
|
||||
a => a.interestXid === interest.id
|
||||
).map(({ interestXid, ...rest }) => rest),
|
||||
}));
|
||||
|
||||
/* =====================================================
|
||||
4️⃣ MOST HYPED
|
||||
===================================================== */
|
||||
const mostHypedGrouped = await tx.userBucketInterested.groupBy({
|
||||
by: ['activityXid'],
|
||||
where: {
|
||||
isActive: true,
|
||||
isBucket: false,
|
||||
Activities: excludeUserInterestCondition,
|
||||
},
|
||||
_count: { activityXid: true },
|
||||
orderBy: { _count: { activityXid: 'desc' } },
|
||||
skip,
|
||||
take: limit,
|
||||
});
|
||||
|
||||
const totalHypedCount = (
|
||||
await tx.userBucketInterested.groupBy({
|
||||
by: ['activityXid'],
|
||||
where: {
|
||||
isActive: true,
|
||||
isBucket: false,
|
||||
Activities: excludeUserInterestCondition,
|
||||
},
|
||||
})
|
||||
).length;
|
||||
|
||||
const hypedActivities = await tx.activities.findMany({
|
||||
where: { id: { in: mostHypedGrouped.map(h => h.activityXid) } },
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityType: { select: { energyLevel: true } },
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: { id: true, mediaFileName: true, mediaType: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const mostHypedActivities = await Promise.all(
|
||||
mostHypedGrouped.map(async g => {
|
||||
const act = hypedActivities.find(a => a.id === g.activityXid);
|
||||
if (!act) return null;
|
||||
return {
|
||||
activityId: act.id,
|
||||
activityTitle: act.activityTitle,
|
||||
hypeCount: g._count.activityXid,
|
||||
energyLevel: act.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(act.ActivitiesMedia),
|
||||
};
|
||||
})
|
||||
).then(a => a.filter(Boolean));
|
||||
|
||||
/* =====================================================
|
||||
5️⃣ NEW ARRIVALS
|
||||
===================================================== */
|
||||
const newArrivalsWhere = {
|
||||
isActive: true,
|
||||
createdAt: { gte: new Date(Date.now() - 31 * 24 * 60 * 60 * 1000) },
|
||||
...excludeUserInterestCondition,
|
||||
};
|
||||
|
||||
const newArrivalsCount = await tx.activities.count({
|
||||
where: newArrivalsWhere,
|
||||
});
|
||||
|
||||
const newArrivalsRaw = await tx.activities.findMany({
|
||||
where: newArrivalsWhere,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { id: 'desc' },
|
||||
select: {
|
||||
activityTitle: true,
|
||||
activityType: { select: { energyLevel: true } },
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: { id: true, mediaFileName: true, mediaType: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/* =====================================================
|
||||
6️⃣ OTHER STATES & OVERSEAS
|
||||
===================================================== */
|
||||
const otherStatesWhere: any = {
|
||||
isActive: true,
|
||||
...excludeUserInterestCondition,
|
||||
};
|
||||
if (effectiveCountryXid) otherStatesWhere.checkInCountryXid = effectiveCountryXid;
|
||||
if (effectiveStateXid) otherStatesWhere.checkInStateXid = { not: effectiveStateXid };
|
||||
|
||||
const overseasWhere: any = {
|
||||
isActive: true,
|
||||
...excludeUserInterestCondition,
|
||||
};
|
||||
if (effectiveCountryXid) overseasWhere.checkInCountryXid = { not: effectiveCountryXid };
|
||||
|
||||
const [otherStatesCount, overseasCount] = await Promise.all([
|
||||
tx.activities.count({ where: otherStatesWhere }),
|
||||
tx.activities.count({ where: overseasWhere }),
|
||||
]);
|
||||
|
||||
const [otherStatesRaw, overseasRaw] = await Promise.all([
|
||||
tx.activities.findMany({
|
||||
where: otherStatesWhere,
|
||||
skip,
|
||||
take: limit,
|
||||
select: {
|
||||
activityTitle: true,
|
||||
activityType: { select: { energyLevel: true } },
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: { id: true, mediaFileName: true, mediaType: true },
|
||||
},
|
||||
},
|
||||
}),
|
||||
tx.activities.findMany({
|
||||
where: overseasWhere,
|
||||
skip,
|
||||
take: limit,
|
||||
select: {
|
||||
activityTitle: true,
|
||||
activityType: { select: { energyLevel: true } },
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: { id: true, mediaFileName: true, mediaType: true },
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
/* =====================================================
|
||||
7️⃣ FINAL RESPONSE
|
||||
===================================================== */
|
||||
return {
|
||||
pagination: { page, limit },
|
||||
interests: interestsWithActivities,
|
||||
|
||||
mostHypedActivities: {
|
||||
page,
|
||||
limit,
|
||||
totalCount: totalHypedCount,
|
||||
hasMore: skip + limit < totalHypedCount,
|
||||
activities: mostHypedActivities,
|
||||
},
|
||||
|
||||
newArrivalsActivities: {
|
||||
page,
|
||||
limit,
|
||||
totalCount: newArrivalsCount,
|
||||
hasMore: skip + limit < newArrivalsCount,
|
||||
activities: await Promise.all(
|
||||
newArrivalsRaw.map(async a => ({
|
||||
activityTitle: a.activityTitle,
|
||||
energyLevel: a.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(a.ActivitiesMedia),
|
||||
}))
|
||||
),
|
||||
},
|
||||
|
||||
otherStatesActivities: {
|
||||
page,
|
||||
limit,
|
||||
totalCount: otherStatesCount,
|
||||
hasMore: skip + limit < otherStatesCount,
|
||||
activities: await Promise.all(
|
||||
otherStatesRaw.map(async a => ({
|
||||
activityTitle: a.activityTitle,
|
||||
energyLevel: a.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(a.ActivitiesMedia),
|
||||
}))
|
||||
),
|
||||
},
|
||||
|
||||
overSeasActivities: {
|
||||
page,
|
||||
limit,
|
||||
totalCount: overseasCount,
|
||||
hasMore: skip + limit < overseasCount,
|
||||
activities: await Promise.all(
|
||||
overseasRaw.map(async a => ({
|
||||
activityTitle: a.activityTitle,
|
||||
energyLevel: a.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(a.ActivitiesMedia),
|
||||
}))
|
||||
),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
async getActivityDetailsById(
|
||||
userId: number,
|
||||
activityXid: number) {
|
||||
|
||||
Reference in New Issue
Block a user