forked from Yara724/api
Fixed legacy requestedCounts methods and statistics + claimLink address
This commit is contained in:
@@ -46,64 +46,6 @@ export class DamageExpertDbService {
|
||||
.exec();
|
||||
}
|
||||
|
||||
async updateStats(
|
||||
expertId: string,
|
||||
type: "checked" | "handled",
|
||||
requestId: string,
|
||||
) {
|
||||
if (!expertId || !requestId || !["checked", "handled"].includes(type)) {
|
||||
console.warn("Invalid expertId, requestId, or type");
|
||||
return;
|
||||
}
|
||||
|
||||
const expert = await this.damageExpertModel.findById(expertId);
|
||||
if (!expert) {
|
||||
console.warn("Expert not found:", expertId);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestIdStr = new Types.ObjectId(requestId).toString();
|
||||
const countedRequests =
|
||||
expert.countedRequests?.map((r) => r.toString()) || [];
|
||||
|
||||
if (type === "checked" && countedRequests.includes(requestIdStr)) {
|
||||
console.log(
|
||||
`Request ${requestIdStr} already checked for expert ${expertId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const update: any = { $inc: {}, $push: {} };
|
||||
|
||||
if (type === "checked") {
|
||||
update.$inc["requestStats.totalChecked"] = 1;
|
||||
update.$push["countedRequests"] = requestIdStr;
|
||||
} else if (type === "handled") {
|
||||
update.$inc["requestStats.totalHandled"] = 1;
|
||||
|
||||
if (countedRequests.includes(requestIdStr)) {
|
||||
update.$inc["requestStats.totalChecked"] = -1;
|
||||
}
|
||||
|
||||
if (!countedRequests.includes(requestIdStr)) {
|
||||
update.$push["countedRequests"] = requestIdStr;
|
||||
} else {
|
||||
delete update.$push;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.damageExpertModel.findByIdAndUpdate(
|
||||
expertId,
|
||||
update,
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
console.warn("Failed to update stats for expert:", expertId);
|
||||
} else {
|
||||
console.log(`Stats updated (${type}) for expert:`, expertId);
|
||||
}
|
||||
}
|
||||
|
||||
async updateOne(filter: any, update: any) {
|
||||
return this.damageExpertModel.updateOne(filter, update);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { Model, Types } from "mongoose";
|
||||
import {
|
||||
ExpertFileActivity,
|
||||
ExpertFileActivityDocument,
|
||||
ExpertFileActivityType,
|
||||
ExpertFileKind,
|
||||
} from "../schema/expert-file-activity.schema";
|
||||
|
||||
@Injectable()
|
||||
export class ExpertFileActivityDbService {
|
||||
constructor(
|
||||
@InjectModel(ExpertFileActivity.name)
|
||||
private readonly activityModel: Model<ExpertFileActivityDocument>,
|
||||
) {}
|
||||
|
||||
async recordEvent(args: {
|
||||
expertId: string | Types.ObjectId;
|
||||
tenantId: string | Types.ObjectId;
|
||||
fileId: string | Types.ObjectId;
|
||||
fileType: ExpertFileKind;
|
||||
eventType: ExpertFileActivityType;
|
||||
occurredAt?: Date;
|
||||
idempotencyKey?: string;
|
||||
}): Promise<void> {
|
||||
const payload = {
|
||||
expertId: new Types.ObjectId(String(args.expertId)),
|
||||
tenantId: new Types.ObjectId(String(args.tenantId)),
|
||||
fileId: new Types.ObjectId(String(args.fileId)),
|
||||
fileType: args.fileType,
|
||||
eventType: args.eventType,
|
||||
occurredAt: args.occurredAt ?? new Date(),
|
||||
...(args.idempotencyKey ? { idempotencyKey: args.idempotencyKey } : {}),
|
||||
};
|
||||
|
||||
if (args.idempotencyKey) {
|
||||
await this.activityModel.updateOne(
|
||||
{ idempotencyKey: args.idempotencyKey },
|
||||
{ $setOnInsert: payload },
|
||||
{ upsert: true },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.activityModel.create(payload);
|
||||
}
|
||||
|
||||
async findByTenantAndExperts(
|
||||
tenantId: string | Types.ObjectId,
|
||||
expertIds: Array<string | Types.ObjectId>,
|
||||
): Promise<
|
||||
Array<{
|
||||
expertId: Types.ObjectId;
|
||||
fileId: Types.ObjectId;
|
||||
eventType: ExpertFileActivityType;
|
||||
occurredAt: Date;
|
||||
}>
|
||||
> {
|
||||
if (expertIds.length === 0) return [];
|
||||
const ids = expertIds.map((id) => new Types.ObjectId(String(id)));
|
||||
return this.activityModel
|
||||
.find(
|
||||
{
|
||||
tenantId: new Types.ObjectId(String(tenantId)),
|
||||
expertId: { $in: ids },
|
||||
},
|
||||
{ expertId: 1, fileId: 1, eventType: 1, occurredAt: 1 },
|
||||
)
|
||||
.sort({ occurredAt: 1, _id: 1 })
|
||||
.lean();
|
||||
}
|
||||
}
|
||||
@@ -33,50 +33,6 @@ export class ExpertDbService {
|
||||
return await this.expertModel.find(filter).lean();
|
||||
}
|
||||
|
||||
async updateStats(
|
||||
expertId: string,
|
||||
type: "checked" | "handled",
|
||||
requestId: string,
|
||||
) {
|
||||
if (!expertId || !requestId || !["checked", "handled"].includes(type)) {
|
||||
console.warn("Invalid expertId, requestId, or type");
|
||||
return;
|
||||
}
|
||||
|
||||
const expert = await this.expertModel.findById(expertId);
|
||||
if (!expert) {
|
||||
console.warn("Expert not found:", expertId);
|
||||
return;
|
||||
}
|
||||
|
||||
const requestIdStr = new Types.ObjectId(requestId).toString();
|
||||
const counted = (expert.countedRequests || []).map((r) => r.toString());
|
||||
|
||||
if (counted.includes(requestIdStr)) {
|
||||
console.log("Request already counted for expert:", requestIdStr);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateField =
|
||||
type === "handled"
|
||||
? {
|
||||
"requestStats.totalHandled": 1,
|
||||
"requestStats.totalChecked": -1,
|
||||
}
|
||||
: { "requestStats.totalChecked": 1 };
|
||||
|
||||
const result = await this.expertModel.findByIdAndUpdate(expertId, {
|
||||
$inc: updateField,
|
||||
$push: { countedRequests: requestIdStr },
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
console.warn("Failed to update expert stats for:", expertId);
|
||||
} else {
|
||||
console.log("Updated stats for expert:", expertId);
|
||||
}
|
||||
}
|
||||
|
||||
async updateOne(filter: any, update: any) {
|
||||
return this.expertModel.updateOne(filter, update);
|
||||
}
|
||||
|
||||
@@ -8,16 +8,6 @@ import {
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { UserType } from "src/Types&Enums/userType.enum";
|
||||
|
||||
@Schema({ _id: false })
|
||||
export class RequestStats {
|
||||
@Prop({ default: 0 })
|
||||
totalHandled: number;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
totalChecked: number;
|
||||
}
|
||||
|
||||
const RequestStatsSchema = SchemaFactory.createForClass(RequestStats);
|
||||
|
||||
@Schema({ _id: false })
|
||||
export class PersonalInfo {
|
||||
@@ -166,19 +156,6 @@ export class DamageExpertModel {
|
||||
@Prop({ type: "string" })
|
||||
city: string;
|
||||
|
||||
@Prop({ type: RequestStatsSchema, default: () => ({}) })
|
||||
requestStats: RequestStats;
|
||||
|
||||
@Prop({
|
||||
type: [{ type: String }],
|
||||
default: [],
|
||||
validate: {
|
||||
validator: (arr: any[]) => arr.every((val) => typeof val === "string"),
|
||||
message: "All countedRequests must be strings",
|
||||
},
|
||||
})
|
||||
countedRequests?: string[];
|
||||
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
@@ -190,11 +167,4 @@ DamageExpertDbSchema.pre("save", function (next) {
|
||||
this.username = this.email;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
DamageExpertDbSchema.pre("save", function (next) {
|
||||
if (!this.requestStats) {
|
||||
this.requestStats = { totalChecked: 0, totalHandled: 0 };
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
52
src/users/entities/schema/expert-file-activity.schema.ts
Normal file
52
src/users/entities/schema/expert-file-activity.schema.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { HydratedDocument, Types } from "mongoose";
|
||||
|
||||
export type ExpertFileActivityDocument = HydratedDocument<ExpertFileActivity>;
|
||||
|
||||
export enum ExpertFileActivityType {
|
||||
CHECKED = "checked",
|
||||
HANDLED = "handled",
|
||||
UNCHECKED = "unchecked",
|
||||
}
|
||||
|
||||
export enum ExpertFileKind {
|
||||
BLAME = "blame",
|
||||
CLAIM = "claim",
|
||||
}
|
||||
|
||||
@Schema({ collection: "expertFileActivities", timestamps: true })
|
||||
export class ExpertFileActivity {
|
||||
@Prop({ type: Types.ObjectId, required: true, index: true })
|
||||
expertId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, required: true, index: true })
|
||||
tenantId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, required: true, index: true })
|
||||
fileId: Types.ObjectId;
|
||||
|
||||
@Prop({ type: String, enum: ExpertFileKind, required: true, index: true })
|
||||
fileType: ExpertFileKind;
|
||||
|
||||
@Prop({
|
||||
type: String,
|
||||
enum: ExpertFileActivityType,
|
||||
required: true,
|
||||
index: true,
|
||||
})
|
||||
eventType: ExpertFileActivityType;
|
||||
|
||||
@Prop({ type: Date, default: Date.now, index: true })
|
||||
occurredAt: Date;
|
||||
|
||||
@Prop({ type: String, required: false, index: true })
|
||||
idempotencyKey?: string;
|
||||
}
|
||||
|
||||
export const ExpertFileActivitySchema =
|
||||
SchemaFactory.createForClass(ExpertFileActivity);
|
||||
|
||||
ExpertFileActivitySchema.index(
|
||||
{ idempotencyKey: 1 },
|
||||
{ unique: true, partialFilterExpression: { idempotencyKey: { $exists: true } } },
|
||||
);
|
||||
@@ -3,17 +3,6 @@ import { Degrees } from "src/Types&Enums/degrees.enum";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { UserType } from "src/Types&Enums/userType.enum";
|
||||
|
||||
@Schema({ _id: false })
|
||||
export class RequestStats {
|
||||
@Prop({ default: 0 })
|
||||
totalHandled: number;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
totalChecked: number;
|
||||
}
|
||||
|
||||
const RequestStatsSchema = SchemaFactory.createForClass(RequestStats);
|
||||
|
||||
@Schema({ collection: "expert", versionKey: false, timestamps: true })
|
||||
export class ExpertModel {
|
||||
@Prop({})
|
||||
@@ -79,27 +68,8 @@ export class ExpertModel {
|
||||
@Prop({ type: "string" })
|
||||
otp: string;
|
||||
|
||||
@Prop({ type: RequestStatsSchema, default: () => ({}) })
|
||||
requestStats: RequestStats;
|
||||
|
||||
@Prop({
|
||||
type: [{ type: String }],
|
||||
default: [],
|
||||
validate: {
|
||||
validator: (arr: any[]) => arr.every((val) => typeof val === "string"),
|
||||
message: "All countedRequests must be strings",
|
||||
},
|
||||
})
|
||||
countedRequests?: string[];
|
||||
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export const ExpertDbSchema = SchemaFactory.createForClass(ExpertModel);
|
||||
|
||||
ExpertDbSchema.pre("save", function (next) {
|
||||
if (!this.requestStats) {
|
||||
this.requestStats = { totalChecked: 0, totalHandled: 0 };
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -2,17 +2,6 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { Types } from "mongoose";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
|
||||
@Schema({ _id: false })
|
||||
export class RequestStats {
|
||||
@Prop({ default: 0 })
|
||||
totalHandled: number;
|
||||
|
||||
@Prop({ default: 0 })
|
||||
totalChecked: number;
|
||||
}
|
||||
|
||||
const RequestStatsSchema = SchemaFactory.createForClass(RequestStats);
|
||||
|
||||
@Schema({
|
||||
collection: "field-expert",
|
||||
versionKey: false,
|
||||
@@ -48,31 +37,8 @@ export class FieldExpertModel {
|
||||
@Prop({ type: "string", default: "" })
|
||||
otp: string;
|
||||
|
||||
@Prop({ type: RequestStatsSchema, default: () => ({}) })
|
||||
requestStats: RequestStats;
|
||||
|
||||
@Prop({
|
||||
type: [{ type: String }],
|
||||
default: [],
|
||||
validate: {
|
||||
validator: (arr: any[]) => arr.every((val) => typeof val === "string"),
|
||||
message: "All countedRequests must be strings",
|
||||
},
|
||||
})
|
||||
countedRequests?: string[];
|
||||
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export const FieldExpertDbSchema =
|
||||
SchemaFactory.createForClass(FieldExpertModel);
|
||||
|
||||
FieldExpertDbSchema.pre("save", function (next) {
|
||||
if (!this.username) {
|
||||
this.username = this.email;
|
||||
}
|
||||
if (!this.requestStats) {
|
||||
this.requestStats = { totalChecked: 0, totalHandled: 0 };
|
||||
}
|
||||
next();
|
||||
});
|
||||
SchemaFactory.createForClass(FieldExpertModel);
|
||||
Reference in New Issue
Block a user