Files
bizmatch-project/bizmatch-server/src/user/user.service.ts
2026-01-18 19:48:45 +01:00

196 lines
8.3 KiB
TypeScript

import { Inject, Injectable } from '@nestjs/common';
import { and, asc, count, desc, eq, inArray, or, SQL, sql } from 'drizzle-orm';
import { NodePgDatabase } from 'drizzle-orm/node-postgres/driver';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import * as schema from '../drizzle/schema';
import { customerSubTypeEnum, PG_CONNECTION } from '../drizzle/schema';
import { FileService } from '../file/file.service';
import { GeoService } from '../geo/geo.service';
import { User, UserSchema } from '../models/db.model';
import { createDefaultUser, emailToDirName, JwtUser, UserListingCriteria } from '../models/main.model';
import { getDistanceQuery, splitName } from '../utils';
type CustomerSubType = (typeof customerSubTypeEnum.enumValues)[number];
@Injectable()
export class UserService {
constructor(
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,
@Inject(PG_CONNECTION) private conn: NodePgDatabase<typeof schema>,
private fileService: FileService,
private geoService: GeoService,
) { }
private getWhereConditions(criteria: UserListingCriteria): SQL[] {
const whereConditions: SQL[] = [];
whereConditions.push(sql`(${schema.users_json.data}->>'customerType') = 'professional'`);
if (criteria.city && criteria.searchType === 'exact') {
whereConditions.push(sql`(${schema.users_json.data}->'location'->>'name') ILIKE ${criteria.city.name}`);
}
if (criteria.city && criteria.radius && criteria.searchType === 'radius' && criteria.radius) {
const cityGeo = this.geoService.getCityWithCoords(criteria.state, criteria.city.name);
const distanceQuery = getDistanceQuery(schema.users_json, cityGeo.latitude, cityGeo.longitude);
whereConditions.push(sql`${distanceQuery} <= ${criteria.radius}`);
}
if (criteria.types && criteria.types.length > 0) {
// whereConditions.push(inArray(schema.users.customerSubType, criteria.types));
whereConditions.push(inArray(sql`${schema.users_json.data}->>'customerSubType'`, criteria.types as CustomerSubType[]));
}
if (criteria.brokerName) {
const { firstname, lastname } = splitName(criteria.brokerName);
whereConditions.push(sql`(${schema.users_json.data}->>'firstname') ILIKE ${`%${firstname}%`} OR (${schema.users_json.data}->>'lastname') ILIKE ${`%${lastname}%`}`);
}
if (criteria.companyName) {
whereConditions.push(sql`(${schema.users_json.data}->>'companyName') ILIKE ${`%${criteria.companyName}%`}`);
}
if (criteria.counties && criteria.counties.length > 0) {
whereConditions.push(or(...criteria.counties.map(county => sql`(${schema.users_json.data}->'location'->>'county') ILIKE ${`%${county}%`}`)));
}
if (criteria.state) {
whereConditions.push(sql`(${schema.users_json.data}->'location'->>'state') = ${criteria.state}`);
}
//never show user which denied
whereConditions.push(sql`(${schema.users_json.data}->>'showInDirectory')::boolean IS TRUE`);
return whereConditions;
}
async searchUserListings(criteria: UserListingCriteria): Promise<{ results: User[]; totalCount: number }> {
const start = criteria.start ? criteria.start : 0;
const length = criteria.length ? criteria.length : 12;
const query = this.conn.select().from(schema.users_json);
const whereConditions = this.getWhereConditions(criteria);
if (whereConditions.length > 0) {
const whereClause = and(...whereConditions);
query.where(whereClause);
}
// Sortierung
switch (criteria.sortBy) {
case 'nameAsc':
query.orderBy(asc(sql`${schema.users_json.data}->>'lastname'`));
break;
case 'nameDesc':
query.orderBy(desc(sql`${schema.users_json.data}->>'lastname'`));
break;
default:
// Keine spezifische Sortierung, Standardverhalten kann hier eingefügt werden
break;
}
// Paginierung
query.limit(length).offset(start);
const data = await query;
const results = data.map(u => ({ id: u.id, email: u.email, ...(u.data as User) }) as User);
const totalCount = await this.getUserListingsCount(criteria);
return {
results,
totalCount,
};
}
async getUserListingsCount(criteria: UserListingCriteria): Promise<number> {
const countQuery = this.conn.select({ value: count() }).from(schema.users_json);
const whereConditions = this.getWhereConditions(criteria);
if (whereConditions.length > 0) {
const whereClause = and(...whereConditions);
countQuery.where(whereClause);
}
const [{ value: totalCount }] = await countQuery;
return totalCount;
}
async getUserByMail(email: string, jwtuser?: JwtUser) {
const users = await this.conn.select().from(schema.users_json).where(eq(schema.users_json.email, email));
if (users.length === 0) {
const user: User = { id: undefined, customerType: 'professional', ...createDefaultUser(email, '', '', null) };
const u = await this.saveUser(user, false);
return u;
} else {
const user = { id: users[0].id, email: users[0].email, ...(users[0].data as User) } as User;
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
return user;
}
}
async getUserById(id: string) {
const users = await this.conn.select().from(schema.users_json).where(eq(schema.users_json.id, id));
const user = { id: users[0].id, email: users[0].email, ...(users[0].data as User) } as User;
user.hasCompanyLogo = this.fileService.hasCompanyLogo(emailToDirName(user.email));
user.hasProfile = this.fileService.hasProfile(emailToDirName(user.email));
return user;
}
async getAllUser() {
const users = await this.conn.select().from(schema.users_json);
return users.map(u => ({ id: u.id, email: u.email, ...(u.data as User) }) as User);
}
async saveUser(user: User, processValidation = true): Promise<User> {
try {
user.updated = new Date();
if (user.id) {
user.created = new Date(user.created);
} else {
user.created = new Date();
}
let validatedUser = user;
if (processValidation) {
validatedUser = UserSchema.parse(user);
}
//const drizzleUser = convertUserToDrizzleUser(validatedUser);
const { id: _, ...rest } = validatedUser;
const drizzleUser = { email: user.email, data: rest };
if (user.id) {
const [updateUser] = await this.conn.update(schema.users_json).set(drizzleUser).where(eq(schema.users_json.id, user.id)).returning();
return { id: updateUser.id, email: updateUser.email, ...(updateUser.data as User) } as User;
} else {
const [newUser] = await this.conn.insert(schema.users_json).values(drizzleUser).returning();
return { id: newUser.id, email: newUser.email, ...(newUser.data as User) } as User;
}
} catch (error) {
throw error;
}
}
async addFavorite(id: string, user: JwtUser): Promise<void> {
const existingUser = await this.getUserById(id);
if (!existingUser) return;
const favorites = existingUser.favoritesForUser || [];
if (!favorites.includes(user.email)) {
existingUser.favoritesForUser = [...favorites, user.email];
const { id: _, ...rest } = existingUser;
const drizzleUser = { email: existingUser.email, data: rest };
await this.conn.update(schema.users_json).set(drizzleUser).where(eq(schema.users_json.id, id));
}
}
async deleteFavorite(id: string, user: JwtUser): Promise<void> {
const existingUser = await this.getUserById(id);
if (!existingUser) return;
const favorites = existingUser.favoritesForUser || [];
if (favorites.includes(user.email)) {
existingUser.favoritesForUser = favorites.filter(email => email !== user.email);
const { id: _, ...rest } = existingUser;
const drizzleUser = { email: existingUser.email, data: rest };
await this.conn.update(schema.users_json).set(drizzleUser).where(eq(schema.users_json.id, id));
}
}
async getFavoriteUsers(user: JwtUser): Promise<User[]> {
const data = await this.conn
.select()
.from(schema.users_json)
.where(sql`${schema.users_json.data}->'favoritesForUser' ? ${user.email}`);
return data.map(u => ({ id: u.id, email: u.email, ...(u.data as User) }) as User);
}
}