added modules & forms jobcaegory-jobrequest-referral-relation etc
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
5
package-lock.json
generated
5
package-lock.json
generated
@@ -211,6 +211,7 @@
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
@@ -269,6 +270,7 @@
|
||||
"resolved": "https://package-mirror.liara.ir/repository/npm/@types/node/-/node-25.9.0.tgz",
|
||||
"integrity": "sha512-AOQwYUNolgy3VosiRqXrACUXTN8nJUtPl7FJXMqZVyxiiCLhQuG3jXKvCS1ALr+Y2OmZhzzLVlYPEqJaiqkaJQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
}
|
||||
@@ -1172,6 +1174,7 @@
|
||||
"resolved": "https://package-mirror.liara.ir/repository/npm/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -2245,6 +2248,7 @@
|
||||
"resolved": "https://package-mirror.liara.ir/repository/npm/pg/-/pg-8.20.0.tgz",
|
||||
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.12.0",
|
||||
"pg-pool": "^3.13.0",
|
||||
@@ -3148,6 +3152,7 @@
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Router } from 'express';
|
||||
import AuthRouter from '../../modules/auth/router/auth.routes';
|
||||
import formRouter from '../../modules/forms/index.routes';
|
||||
import userRouter from '../../modules/user/routes/user.routes';
|
||||
|
||||
const mainRouter = Router();
|
||||
|
||||
|
||||
// mainRouter.use('/user',userRouter)
|
||||
// mainRouter.use('/applicant',applicantRouter)
|
||||
mainRouter.use('/auth',AuthRouter)
|
||||
// mainRouter.use('/user',userRouter)
|
||||
|
||||
mainRouter.use('/user',userRouter)
|
||||
mainRouter.use('/form',formRouter)
|
||||
export default mainRouter;
|
||||
@@ -0,0 +1,76 @@
|
||||
// modules/.../computer-skill/controller/computerSkill.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import ComputerSkillService from "../service/computerSkill.service";
|
||||
|
||||
class ComputerSkillControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = ComputerSkillService;
|
||||
}
|
||||
|
||||
// (اختیاری) برای ادمین
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.getAll();
|
||||
return res.status(200).json({ status: 200, data, message: "Ok" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// دریافت رکورد مهارتهای کاربر جاری
|
||||
async getMine(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.getMine(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// ایجاد یا بروزرسانی رکورد مهارتهای کاربر جاری
|
||||
async upsertMine(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
|
||||
const data = await this.#service.upsertMine(applicantId, req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "ذخیره شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// حذف رکورد مهارتهای کاربر جاری
|
||||
async removeMine(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
await this.#service.removeMine(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ComputerSkillController = new ComputerSkillControllerClass();
|
||||
export default ComputerSkillController;
|
||||
@@ -0,0 +1,15 @@
|
||||
// modules/.../computer-skill/routes/computerSkill.routes.ts
|
||||
import { Router } from "express";
|
||||
import ComputerSkillController from "../controller/computerSkill.controller";
|
||||
|
||||
const computerSkillRouter = Router();
|
||||
|
||||
// پیشنهاد: این روتها پشت authMiddleware باشند
|
||||
computerSkillRouter.get("/me", ComputerSkillController.getMine.bind(ComputerSkillController));
|
||||
computerSkillRouter.put("/me", ComputerSkillController.upsertMine.bind(ComputerSkillController));
|
||||
computerSkillRouter.delete("/me", ComputerSkillController.removeMine.bind(ComputerSkillController));
|
||||
|
||||
// (اختیاری) فقط برای ادمین:
|
||||
// computerSkillRouter.get("/", ComputerSkillController.getAll.bind(ComputerSkillController));
|
||||
|
||||
export default computerSkillRouter;
|
||||
@@ -0,0 +1,66 @@
|
||||
// modules/.../computer-skill/service/computerSkill.service.ts
|
||||
|
||||
import { ComputerSkill } from "../../../../models/ComputerSkill";
|
||||
|
||||
const SKILL_LEVELS = [
|
||||
"NONE",
|
||||
"VERY_WEAK",
|
||||
"WEAK",
|
||||
"AVERAGE",
|
||||
"GOOD",
|
||||
"VERY_GOOD",
|
||||
"EXCELLENT",
|
||||
] as const;
|
||||
|
||||
type SkillLevel = (typeof SKILL_LEVELS)[number];
|
||||
|
||||
type UpsertComputerSkillDTO = Partial<{
|
||||
pcUsage: SkillLevel;
|
||||
word: SkillLevel;
|
||||
excel: SkillLevel;
|
||||
powerPoint: SkillLevel;
|
||||
rahkaran: SkillLevel;
|
||||
kasra: SkillLevel;
|
||||
didgah: SkillLevel;
|
||||
his: SkillLevel;
|
||||
otherSoftware: string | null;
|
||||
}>;
|
||||
|
||||
class ComputerSkillServiceClass {
|
||||
async getAll() {
|
||||
// فقط برای ادمین/اپراتور (اگر نیاز داری)
|
||||
return await ComputerSkill.findAll({order: [["createdAt", "DESC"]]});
|
||||
}
|
||||
|
||||
async getMine(applicantId: string) {
|
||||
const record = await ComputerSkill.findOne({where: {applicantId}});
|
||||
// میتونی null برگردونی یا خطا بدی؛ من null برمیگردونم تا فرانت بدونه هنوز پر نشده
|
||||
return record;
|
||||
}
|
||||
|
||||
async upsertMine(applicantId: string, data: UpsertComputerSkillDTO) {
|
||||
const existing = await ComputerSkill.findOne({where: {applicantId}});
|
||||
|
||||
if (!existing) {
|
||||
return await ComputerSkill.create({
|
||||
applicantId,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
await existing.update(data);
|
||||
return existing;
|
||||
}
|
||||
|
||||
async removeMine(applicantId: string) {
|
||||
const existing = await ComputerSkill.findOne({where: {applicantId}});
|
||||
if (!existing) {
|
||||
// اگر بخوای idempotent باشه، میتونی true برگردونی
|
||||
throw new Error("رکورد مهارتهای کامپیوتری یافت نشد");
|
||||
}
|
||||
await existing.destroy();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const ComputerSkillService = new ComputerSkillServiceClass();
|
||||
export default ComputerSkillService;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { NextFunction } from "express";
|
||||
import {Controller} from "../../../../core/controller/main.controller";
|
||||
import {ServerResponse} from "../../../../core/types";
|
||||
import {NextFunction} from "express";
|
||||
import CourseInfoService from "../service/courseForm.service";
|
||||
|
||||
class CourseInfoControllerClass extends Controller {
|
||||
@@ -12,7 +12,7 @@ class CourseInfoControllerClass extends Controller {
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const course = await this.#service.create(req.body);
|
||||
res.status(201).json({ status: 200, data: course });
|
||||
res.status(201).json({status: 200, data: course});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class CourseInfoControllerClass extends Controller {
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const course = await this.#service.getById(req.params.id);
|
||||
res.json({ status: 200, data: course });
|
||||
res.json({status: 200, data: course});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
@@ -29,9 +29,9 @@ class CourseInfoControllerClass extends Controller {
|
||||
|
||||
async list(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { applicantId } = req.query as { applicantId?: string };
|
||||
const {applicantId} = req.query as {applicantId?: string};
|
||||
const courses = await this.#service.list(applicantId);
|
||||
res.json({ status: 200, data: courses });
|
||||
res.json({status: 200, data: courses});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class CourseInfoControllerClass extends Controller {
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const updated = await this.#service.update(req.params.id, req.body);
|
||||
res.json({ status: 200, data: updated });
|
||||
res.json({status: 200, data: updated});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
@@ -49,11 +49,35 @@ class CourseInfoControllerClass extends Controller {
|
||||
async delete(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const result = await this.#service.delete(req.params.id);
|
||||
res.json({ status: 200, data: result });
|
||||
res.json({status: 200, data: result});
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
async bulkSave(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id; // دریافت از میدلور احراز هویت
|
||||
const {items} = req.body;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
return res.status(400).json({
|
||||
status: 400,
|
||||
data: {},
|
||||
message: "فیلد items الزامی بوده و باید به صورت آرایه باشد.",
|
||||
});
|
||||
}
|
||||
|
||||
const courses = await this.#service.bulkSync(applicantId, items);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
message: "دورههای آموزشی با موفقیت ذخیره شدند",
|
||||
data: courses,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CourseInfoController = new CourseInfoControllerClass();
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { Course } from "../../../../models/Course";
|
||||
import {Controller} from "../../../../core/controller/main.controller";
|
||||
import {Course} from "../../../../models/Course";
|
||||
import { sequelize } from "../../../../models";
|
||||
|
||||
export interface CoursePayload {
|
||||
applicantId: string;
|
||||
degree: string;
|
||||
field: string;
|
||||
university: string;
|
||||
startYear?: number | null;
|
||||
endYear?: number | null;
|
||||
gpa?: number | null;
|
||||
description?: string | null;
|
||||
certificateImageId?: string | null;
|
||||
title: string;
|
||||
institution: string;
|
||||
year: number;
|
||||
duration: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
class CourseServiceClass extends Controller {
|
||||
@@ -26,21 +23,43 @@ class CourseServiceClass extends Controller {
|
||||
}
|
||||
|
||||
async list(applicantId?: string) {
|
||||
const where = applicantId ? { applicantId } : {};
|
||||
return await Course.findAll({ where });
|
||||
const where = applicantId ? {applicantId} : {};
|
||||
return await Course.findAll({where});
|
||||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
const [affectedRows] = await Course.update(data, { where: { id } });
|
||||
const [affectedRows] = await Course.update(data, {where: {id}});
|
||||
if (affectedRows === 0)
|
||||
throw new createHttpError.NotFound("دوره برای ویرایش یافت نشد");
|
||||
return await Course.findByPk(id);
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const deleted = await Course.destroy({ where: { id } });
|
||||
const deleted = await Course.destroy({where: {id}});
|
||||
if (!deleted) throw new createHttpError.NotFound("دوره برای حذف یافت نشد");
|
||||
return { message: "دوره با موفقیت حذف شد" };
|
||||
return {message: "دوره با موفقیت حذف شد"};
|
||||
}
|
||||
async bulkSync(applicantId: string, items: CoursePayload[]) {
|
||||
return await sequelize.transaction(async (t) => {
|
||||
// ۱. حذف تمام سوابق قبلیِ این کاربر خاص
|
||||
await Course.destroy({
|
||||
where: {applicantId},
|
||||
transaction: t,
|
||||
});
|
||||
|
||||
// ۲. آمادهسازی دیتا برای ثبت (بدون id تا خودکار تولید شود)
|
||||
const dataToInsert = items.map((item) => ({
|
||||
applicantId: applicantId, // امنیت: تحمیل آیدی کاربر احراز هویت شده
|
||||
title: item.title,
|
||||
institution: item.institution,
|
||||
year: item.year,
|
||||
duration: item.duration,
|
||||
description: item.description,
|
||||
}));
|
||||
|
||||
// ۳. ثبت دستهجمعی
|
||||
return await Course.bulkCreate(dataToInsert, {transaction: t});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,27 @@ class EducationInfoControllerClass extends Controller {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
async bulkSave(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id;
|
||||
const { items } = req.body;
|
||||
|
||||
if (!Array.isArray(items)) {
|
||||
throw new Error("دادههای ارسالی باید به صورت آرایه (items) باشند");
|
||||
}
|
||||
|
||||
const data = await this.#service.bulkSync(applicantId, items);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "اطلاعات تحصیلی با موفقیت بروزرسانی شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const EducationInfoController = new EducationInfoControllerClass();
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
import createHttpError from "http-errors";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { Education } from "../../../../models/Education";
|
||||
|
||||
import {Controller} from "../../../../core/controller/main.controller";
|
||||
import {Education, EducationAttributes} from "../../../../models/Education";
|
||||
import {sequelize} from "../../../../models";
|
||||
|
||||
export interface EducationPayload {
|
||||
applicantId: string;
|
||||
degree: string;
|
||||
field: string;
|
||||
university: string;
|
||||
startYear?: number | null;
|
||||
endYear?: number | null;
|
||||
gpa?: number | null;
|
||||
description?: string | null;
|
||||
certificateImageId?: string | null;
|
||||
startYear?: number;
|
||||
endYear?: number;
|
||||
gpa?: number;
|
||||
description?: string;
|
||||
certificateImageId?: string;
|
||||
}
|
||||
|
||||
class EducationServiceClass extends Controller {
|
||||
private validate(data: EducationPayload) {
|
||||
if (
|
||||
typeof data.startYear === "number" &&
|
||||
typeof data.endYear === "number" &&
|
||||
data.endYear < data.startYear
|
||||
) {
|
||||
throw new Error("سال پایان تحصیل نمیتواند کمتر از سال شروع باشد");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// ایجاد رکورد جدید
|
||||
async create(data: EducationPayload) {
|
||||
try {
|
||||
@@ -51,14 +61,14 @@ class EducationServiceClass extends Controller {
|
||||
}
|
||||
|
||||
// لیست سوابق، مثلا بر اساس applicantId
|
||||
async list(filter?: { applicantId?: string }) {
|
||||
async list(filter?: {applicantId?: string}) {
|
||||
const where: any = {};
|
||||
|
||||
if (filter?.applicantId) {
|
||||
where.applicantId = filter.applicantId;
|
||||
}
|
||||
|
||||
const list = await Education.findAll({ where });
|
||||
const list = await Education.findAll({where});
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -106,13 +116,35 @@ class EducationServiceClass extends Controller {
|
||||
|
||||
try {
|
||||
await education.destroy();
|
||||
return { success: true };
|
||||
return {success: true};
|
||||
} catch (err) {
|
||||
throw new createHttpError.InternalServerError(
|
||||
"خطا در حذف سابقه تحصیلی",
|
||||
);
|
||||
throw new createHttpError.InternalServerError("خطا در حذف سابقه تحصیلی");
|
||||
}
|
||||
}
|
||||
async bulkSync(applicantId: string, items: EducationPayload[]) {
|
||||
return await sequelize.transaction(async (t) => {
|
||||
// ۱. حذف رکوردهای قبلی
|
||||
await Education.destroy({where: {applicantId}, transaction: t});
|
||||
|
||||
// ۲. آمادهسازی دیتای جدید (اجبار به استفاده از applicantId صحیح)
|
||||
const dataToInsert = items.map((item) => ({
|
||||
// استخراج تمام فیلدها به جز applicantId احتمالی در ورودی
|
||||
degree: item.degree,
|
||||
field: item.field,
|
||||
university: item.university,
|
||||
startYear: item.startYear,
|
||||
endYear: item.endYear,
|
||||
gpa: item.gpa,
|
||||
description: item.description,
|
||||
certificateImageId: item.certificateImageId,
|
||||
// اعمال آیدی احراز هویت شده
|
||||
applicantId: applicantId,
|
||||
}));
|
||||
|
||||
// ۳. ثبت گروهی
|
||||
return await Education.bulkCreate(dataToInsert, {transaction: t});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const EducationService = new EducationServiceClass();
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { Router } from "express";
|
||||
import {Router} from "express";
|
||||
import PersonalInfoRouter from "./personalInfo/index.routes";
|
||||
import identityRouter from "./identity/index.routes";
|
||||
import PhysicalInfoRouter from "./physicalInfo/index.routes";
|
||||
import EducationInfoRouter from "./education/index.routes";
|
||||
import CourseInfoRouter from "./course/index.routes";
|
||||
import CenterRouter from "./center/routes/center.routes";
|
||||
import referralRouter from "./referral/routes/referral.routes";
|
||||
import JobRequestRouter from "./jobRequest/routes/jobRequest.routes";
|
||||
import computerSkillRouter from "./computerSkill/routes/computerSkill.routes";
|
||||
import LanguageSkillRouter from "./languageSkill/routes/languageSkill.routes";
|
||||
import workExperienceRouter from "./workExperience/routes/workExperience.routes";
|
||||
|
||||
const formRouter = Router();
|
||||
|
||||
@@ -12,5 +18,11 @@ formRouter.use("/personal-info", PersonalInfoRouter);
|
||||
formRouter.use("/physical-info", PhysicalInfoRouter);
|
||||
formRouter.use("/education", EducationInfoRouter);
|
||||
formRouter.use("/course", CourseInfoRouter);
|
||||
formRouter.use("/center", CenterRouter);
|
||||
formRouter.use("/referral", referralRouter);
|
||||
formRouter.use("/job-request", JobRequestRouter);
|
||||
formRouter.use("/computer-skill", computerSkillRouter);
|
||||
formRouter.use("/language-skill", LanguageSkillRouter);
|
||||
formRouter.use("/work-experience", workExperienceRouter);
|
||||
|
||||
export default formRouter;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// controllers/jobRequest.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import JobRequestService from "../service/jobRequest.service";
|
||||
|
||||
class JobRequestControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = JobRequestService;
|
||||
}
|
||||
|
||||
// ثبت نهایی فرم انتخاب مراکز
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id; // شناسه از توکن
|
||||
// فرض: فرانتند آرایهای از انتخابها را میفرستد: [{centerId, categoryId, jobTitle, ...}, ...]
|
||||
const requests = Array.isArray(req.body) ? req.body : [req.body];
|
||||
|
||||
const data = await this.#service.createRequests(applicantId, requests);
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "درخواستهای شغلی با موفقیت ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// مشاهده انتخابهای من
|
||||
async getMyRequests(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.getByApplicant(req.user?.id);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.update(req.params.id, req.user?.id, req.body);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "ویرایش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
await this.#service.remove(req.params.id, req.user?.id);
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data:{},
|
||||
message: "حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JobRequestController = new JobRequestControllerClass();
|
||||
export default JobRequestController;
|
||||
16
src/modules/forms/jobRequest/routes/jobRequest.routes.ts
Normal file
16
src/modules/forms/jobRequest/routes/jobRequest.routes.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Router } from "express";
|
||||
import JobRequestController from "../controller/jobRequest.controller";
|
||||
|
||||
const JobRequestRouter = Router();
|
||||
|
||||
// ثبت انتخابها (مراکز و رستهها)
|
||||
JobRequestRouter.post("/", JobRequestController.create.bind(JobRequestController));
|
||||
|
||||
// لیست انتخابهای متقاضی فعلی
|
||||
JobRequestRouter.get("/my-selections", JobRequestController.getMyRequests.bind(JobRequestController));
|
||||
|
||||
// حذف یا ویرایش یک انتخاب خاص
|
||||
JobRequestRouter.put("/:id", JobRequestController.update.bind(JobRequestController));
|
||||
JobRequestRouter.delete("/:id", JobRequestController.remove.bind(JobRequestController));
|
||||
|
||||
export default JobRequestRouter;
|
||||
53
src/modules/forms/jobRequest/service/jobRequest.service.ts
Normal file
53
src/modules/forms/jobRequest/service/jobRequest.service.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { JobRequest } from "../../../../models/JobRequest";
|
||||
|
||||
class JobRequestServiceClass {
|
||||
// ثبت درخواستهای شغلی (پشتیبانی از ثبت همزمان چند مرکز/رسته)
|
||||
async createRequests(applicantId: string, requests: any[]) {
|
||||
// اضافه کردن applicantId به تمام آبجکتهای ارسالی از سمت فرانت
|
||||
const preparedData = requests.map((req) => ({
|
||||
...req,
|
||||
applicantId,
|
||||
}));
|
||||
|
||||
return await JobRequest.bulkCreate(preparedData);
|
||||
}
|
||||
|
||||
// دریافت لیست تمام درخواستهای یک متقاضی (با جزئیات)
|
||||
async getByApplicant(applicantId: string) {
|
||||
return await JobRequest.findAll({
|
||||
where: {applicantId},
|
||||
// پیشنهاد: اینجا میتوانید Include کنید تا نام مرکز و رسته را هم بگیرید
|
||||
include: ["center", "category"],
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
// دریافت تمام درخواستهای ثبت شده برای یک مرکز خاص (برای پنل مدیریت مرکز)
|
||||
async getByCenter(centerId: string) {
|
||||
return await JobRequest.findAll({
|
||||
where: {centerId},
|
||||
include: ["applicant"], // اطلاعات متقاضی را هم بیاورد
|
||||
});
|
||||
}
|
||||
|
||||
// ویرایش یک درخواست خاص
|
||||
async update(id: string, applicantId: string, data: any) {
|
||||
const request = await JobRequest.findOne({where: {id, applicantId}});
|
||||
if (!request) throw new Error("درخواست یافت نشد.");
|
||||
|
||||
return await request.update(data);
|
||||
}
|
||||
|
||||
// حذف درخواست
|
||||
async remove(id: string, applicantId: string) {
|
||||
const request = await JobRequest.findOne({where: {id, applicantId}});
|
||||
if (!request) throw new Error("درخواست یافت نشد.");
|
||||
|
||||
await request.destroy();
|
||||
return {message: "درخواست با موفقیت حذف شد."};
|
||||
}
|
||||
}
|
||||
|
||||
const JobRequestService = new JobRequestServiceClass();
|
||||
|
||||
export default JobRequestService;
|
||||
@@ -0,0 +1,98 @@
|
||||
// modules/.../language-skill/controller/languageSkill.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import LanguageSkillService from "../service/langugeSkill.service";
|
||||
|
||||
class LanguageSkillControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = LanguageSkillService;
|
||||
}
|
||||
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.create(applicantId, req.body);
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "مهارت زبان با موفقیت ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.getAllByApplicant(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getOne(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const { id } = req.params;
|
||||
|
||||
const data = await this.#service.getById(id, applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const { id } = req.params;
|
||||
|
||||
const data = await this.#service.update(id, applicantId, req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "مهارت زبان ویرایش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const { id } = req.params;
|
||||
|
||||
await this.#service.remove(id, applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "مهارت زبان حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LanguageSkillController = new LanguageSkillControllerClass();
|
||||
export default LanguageSkillController;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Router } from "express";
|
||||
import LanguageSkillController from "../controller/languageSkill.controller";
|
||||
|
||||
const LanguageSkillRouter = Router();
|
||||
|
||||
LanguageSkillRouter.post("/", LanguageSkillController.create.bind(LanguageSkillController));
|
||||
LanguageSkillRouter.get("/", LanguageSkillController.getAll.bind(LanguageSkillController));
|
||||
LanguageSkillRouter.get("/:id", LanguageSkillController.getOne.bind(LanguageSkillController));
|
||||
LanguageSkillRouter.put("/:id", LanguageSkillController.update.bind(LanguageSkillController));
|
||||
LanguageSkillRouter.delete("/:id", LanguageSkillController.remove.bind(LanguageSkillController));
|
||||
|
||||
export default LanguageSkillRouter;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { LanguageSkill } from "../../../../models/LanguageSkill";
|
||||
|
||||
type CreateLanguageSkillDTO = {
|
||||
languageName?: string;
|
||||
proficiency?: string;
|
||||
hasCertificate?: boolean;
|
||||
certificateType?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type UpdateLanguageSkillDTO = Partial<CreateLanguageSkillDTO>;
|
||||
|
||||
class LanguageSkillServiceClass {
|
||||
async create(applicantId: string, data: CreateLanguageSkillDTO) {
|
||||
return await LanguageSkill.create({
|
||||
applicantId,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
async getAllByApplicant(applicantId: string) {
|
||||
return await LanguageSkill.findAll({
|
||||
where: { applicantId },
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
async getById(id: string, applicantId: string) {
|
||||
const record = await LanguageSkill.findOne({
|
||||
where: { id, applicantId },
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
throw new Error("رکورد مهارت زبان یافت نشد");
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
async update(id: string, applicantId: string, data: UpdateLanguageSkillDTO) {
|
||||
const record = await LanguageSkill.findOne({
|
||||
where: { id, applicantId },
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
throw new Error("رکورد مهارت زبان یافت نشد");
|
||||
}
|
||||
|
||||
await record.update(data);
|
||||
return record;
|
||||
}
|
||||
|
||||
async remove(id: string, applicantId: string) {
|
||||
const record = await LanguageSkill.findOne({
|
||||
where: { id, applicantId },
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
throw new Error("رکورد مهارت زبان یافت نشد");
|
||||
}
|
||||
|
||||
await record.destroy();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const LanguageSkillService = new LanguageSkillServiceClass()
|
||||
|
||||
export default LanguageSkillService;
|
||||
94
src/modules/forms/referral/controller/referral.controller.ts
Normal file
94
src/modules/forms/referral/controller/referral.controller.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
// controllers/referral.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import ReferralService from "../service/referral.service";
|
||||
|
||||
class ReferralControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// فرض بر این است که ReferralService به صورت singleton (New شده) اکسپورت شده است.
|
||||
// در غیر این صورت: this.#service = new ReferralService();
|
||||
this.#service = ReferralService;
|
||||
}
|
||||
|
||||
// دریافت لیست معرفهای کاربر فعلی
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
// استخراج آیدی متقاضی از توکن برای امنیت بیشتر (فقط معرفهای خودش را ببیند)
|
||||
const applicantId = req.user?.id || req.query?.applicantId;
|
||||
|
||||
const data = await this.#service.getAll(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error); // اضافه شدن هندلر خطا که قبلاً خالی بود
|
||||
}
|
||||
}
|
||||
|
||||
// ثبت معرف جدید
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id; // شناسه متقاضی از توکن امنیتی
|
||||
|
||||
const data = await this.#service.create({
|
||||
...req.body,
|
||||
applicantId: applicantId || req.body.applicantId // اولویت با توکن است
|
||||
});
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "ساخته شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// ویرایش اطلاعات معرف با بررسی امنیت مالکیت رکورد
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
const applicantId = req.user?.id; // برای بررسی اینکه کاربر فقط معرف خودش را ادیت کند
|
||||
|
||||
const data = await this.#service.update(id, applicantId, req.body);
|
||||
|
||||
return res.status(200).json({ // تغییر وضعیت به 200 (OK)
|
||||
status: 200,
|
||||
data,
|
||||
message: "ویرایش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// حذف معرف با بررسی مالکیت رکورد
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
const applicantId = req.user?.id; // برای جلوگیری از حذف معرفهای دیگران توسط کاربران غریبه
|
||||
|
||||
await this.#service.delete(id, applicantId);
|
||||
|
||||
return res.status(200).json({ // تغییر وضعیت به 200 (OK)
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ReferralController = new ReferralControllerClass();
|
||||
|
||||
export default ReferralController;
|
||||
16
src/modules/forms/referral/routes/referral.routes.ts
Normal file
16
src/modules/forms/referral/routes/referral.routes.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// routes/referral.routes.ts
|
||||
import { Router } from "express";
|
||||
import ReferralController from "../controller/referral.controller";
|
||||
|
||||
|
||||
const ReferralRouter = Router();
|
||||
|
||||
// تمامی عملیاتها نیاز به لاگین دارند
|
||||
// ReferralRouter.use(authMiddleware);
|
||||
|
||||
ReferralRouter.post("/", ReferralController.create);
|
||||
ReferralRouter.get("/", ReferralController.getAll);
|
||||
ReferralRouter.put("/:id", ReferralController.update);
|
||||
ReferralRouter.delete("/:id", ReferralController.remove);
|
||||
|
||||
export default ReferralRouter;
|
||||
41
src/modules/forms/referral/service/referral.service.ts
Normal file
41
src/modules/forms/referral/service/referral.service.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {Referral} from "../../../../models/Referral";
|
||||
|
||||
class ReferralServiceClass {
|
||||
// ثبت معرف جدید
|
||||
async create(data: any) {
|
||||
return await Referral.create(data);
|
||||
}
|
||||
async getAll(applicantId?: string) {
|
||||
const filter = applicantId ? {where: {applicantId}} : {};
|
||||
return await Referral.findAll(filter);
|
||||
}
|
||||
// دریافت تمام معرفهای یک متقاضی خاص
|
||||
async getByApplicant(applicantId: string) {
|
||||
return await Referral.findAll({
|
||||
where: {applicantId},
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
// بروزرسانی اطلاعات معرف
|
||||
async update(id: string, applicantId: string, data: any) {
|
||||
const referral = await Referral.findOne({where: {id, applicantId}});
|
||||
if (!referral)
|
||||
throw new Error("معرف مورد نظر یافت نشد یا دسترسی مجاز نیست.");
|
||||
|
||||
return await referral.update(data);
|
||||
}
|
||||
|
||||
// حذف معرف
|
||||
async delete(id: string, applicantId: string) {
|
||||
const referral = await Referral.findOne({where: {id, applicantId}});
|
||||
if (!referral) throw new Error("معرف مورد نظر یافت نشد.");
|
||||
|
||||
await referral.destroy();
|
||||
return {message: "معرف با موفقیت حذف شد."};
|
||||
}
|
||||
}
|
||||
|
||||
const ReferralService = new ReferralServiceClass();
|
||||
|
||||
export default ReferralService;
|
||||
81
src/modules/forms/relation/controller/relation.controller.ts
Normal file
81
src/modules/forms/relation/controller/relation.controller.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// modules/relation/controller/relation.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
|
||||
import RelationService from "../service/relation.service";
|
||||
import { ServerResponse } from "../../../../core/types";
|
||||
import { Controller } from "../../../../core/controller/main.controller";
|
||||
|
||||
class RelationControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = RelationService;
|
||||
}
|
||||
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id;
|
||||
const data = await this.#service.create(applicantId, req.body);
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "اطلاعات آشنایان با موفقیت ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id;
|
||||
const data = await this.#service.getAllByApplicant(applicantId);
|
||||
|
||||
return res.status(200).json({ status: 200, data, message: "Ok" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const applicantId = req.user.id;
|
||||
const data = await this.#service.update(id, applicantId, req.body);
|
||||
|
||||
return res.status(200).json({ status: 200, data, message: "بروزرسانی انجام شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const applicantId = req.user.id;
|
||||
await this.#service.remove(id, applicantId);
|
||||
|
||||
return res.status(200).json({ status: 200,data:{}, message: "رکورد حذف شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// متد ثبت گروهی (Batch)
|
||||
async bulkSave(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id;
|
||||
const { relations } = req.body; // انتظار یک آرایه داریم
|
||||
const data = await this.#service.bulkSync(applicantId, relations);
|
||||
|
||||
return res.status(200).json({ status: 200, data, message: "اطلاعات همگامسازی شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RelationController = new RelationControllerClass();
|
||||
export default RelationController;
|
||||
16
src/modules/forms/relation/routes/relation.routes.ts
Normal file
16
src/modules/forms/relation/routes/relation.routes.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// modules/relation/routes/relation.routes.ts
|
||||
import { Router } from "express";
|
||||
import RelationController from "../controller/relation.controller";
|
||||
|
||||
const relationRouter = Router();
|
||||
|
||||
// مسیرهای اصلی
|
||||
relationRouter.post("/", RelationController.create.bind(RelationController));
|
||||
relationRouter.get("/", RelationController.getAll.bind(RelationController));
|
||||
relationRouter.put("/:id", RelationController.update.bind(RelationController));
|
||||
relationRouter.delete("/:id", RelationController.remove.bind(RelationController));
|
||||
|
||||
// مسیر کمکی برای ثبت همزمان چند آشنا
|
||||
relationRouter.post("/bulk", RelationController.bulkSave.bind(RelationController));
|
||||
|
||||
export default relationRouter;
|
||||
79
src/modules/forms/relation/service/relation.service.ts
Normal file
79
src/modules/forms/relation/service/relation.service.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Relation } from "../../../../models/Relation";
|
||||
|
||||
|
||||
type CreateRelationDTO = {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
relationship: string;
|
||||
jobTitle?: string;
|
||||
workplaceName?: string;
|
||||
phoneNumber: string;
|
||||
order: number;
|
||||
};
|
||||
|
||||
class RelationServiceClass {
|
||||
async create(applicantId: string, data: CreateRelationDTO) {
|
||||
// بررسی اینکه آیا این اولویت (order) قبلاً برای این متقاضی ثبت شده یا خیر
|
||||
const existingOrder = await Relation.findOne({
|
||||
where: {applicantId, order: data.order},
|
||||
});
|
||||
|
||||
if (existingOrder) {
|
||||
throw new Error(
|
||||
`اطلاعات برای اولویت ${data.order} قبلاً ثبت شده است. از متد آپدیت استفاده کنید.`,
|
||||
);
|
||||
}
|
||||
|
||||
return await Relation.create({
|
||||
applicantId,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
async getAllByApplicant(applicantId: string) {
|
||||
return await Relation.findAll({
|
||||
where: {applicantId},
|
||||
order: [["order", "ASC"]], // مرتبسازی بر اساس اولویت ۱، ۲ و...
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
applicantId: string,
|
||||
data: Partial<CreateRelationDTO>,
|
||||
) {
|
||||
const relation = await Relation.findOne({
|
||||
where: {id, applicantId},
|
||||
});
|
||||
|
||||
if (!relation) throw new Error("رکورد مورد نظر یافت نشد.");
|
||||
|
||||
await relation.update(data);
|
||||
return relation;
|
||||
}
|
||||
|
||||
async remove(id: string, applicantId: string) {
|
||||
const relation = await Relation.findOne({
|
||||
where: {id, applicantId},
|
||||
});
|
||||
|
||||
if (!relation) throw new Error("رکورد مورد نظر یافت نشد.");
|
||||
|
||||
await relation.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ثبت گروهی آشنایان (مثلاً هر دو آشنا در یک مرحله ارسال شوند)
|
||||
*/
|
||||
async bulkSync(applicantId: string, relations: CreateRelationDTO[]) {
|
||||
// ابتدا رکوردهای قبلی را پاک میکنیم (یا میتوان منطق بروزرسانی پیچیده تری داشت)
|
||||
await Relation.destroy({where: {applicantId}});
|
||||
|
||||
const dataWithId = relations.map((r) => ({...r, applicantId}));
|
||||
return await Relation.bulkCreate(dataWithId);
|
||||
}
|
||||
}
|
||||
|
||||
const RelationService = new RelationServiceClass()
|
||||
export default RelationService;
|
||||
@@ -0,0 +1,130 @@
|
||||
// modules/.../work-experience/controller/workExperience.controller.ts
|
||||
import {NextFunction} from "express";
|
||||
import {Controller} from "../../../../core/controller/main.controller";
|
||||
import {ServerResponse} from "../../../../core/types";
|
||||
import WorkExperienceService from "../service/workExperience.service";
|
||||
|
||||
class WorkExperienceControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = WorkExperienceService;
|
||||
}
|
||||
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.create(applicantId, req.body);
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "سابقه کاری ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.getAllByApplicant(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getOne(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const {id} = req.params;
|
||||
|
||||
const data = await this.#service.getById(id, applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const {id} = req.params;
|
||||
|
||||
const data = await this.#service.update(id, applicantId, req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "سابقه کاری ویرایش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const {id} = req.params;
|
||||
|
||||
await this.#service.remove(id, applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "سابقه کاری حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// اختیاری: endpoint جدا برای "هیچ سابقهای ندارم"
|
||||
async setHasNoExperience(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user?.id;
|
||||
const data = await this.#service.setHasNoExperience(applicantId);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "وضعیت «بدون سابقه کاری» ثبت شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
async bulkSave(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const applicantId = req.user.id;
|
||||
const {items} = req.body;
|
||||
|
||||
const data = await this.#service.bulkSync(applicantId, items);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "سوابق کاری با موفقیت ذخیره شدند",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const WorkExperienceController = new WorkExperienceControllerClass();
|
||||
export default WorkExperienceController;
|
||||
@@ -0,0 +1,17 @@
|
||||
// modules/.../work-experience/routes/workExperience.routes.ts
|
||||
import { Router } from "express";
|
||||
import WorkExperienceController from "../controller/workExperience.controller";
|
||||
|
||||
const workExperienceRouter = Router();
|
||||
|
||||
workExperienceRouter.post("/", WorkExperienceController.create.bind(WorkExperienceController));
|
||||
workExperienceRouter.get("/", WorkExperienceController.getAll.bind(WorkExperienceController));
|
||||
workExperienceRouter.get("/:id", WorkExperienceController.getOne.bind(WorkExperienceController));
|
||||
workExperienceRouter.put("/:id", WorkExperienceController.update.bind(WorkExperienceController));
|
||||
workExperienceRouter.delete("/:id", WorkExperienceController.remove.bind(WorkExperienceController));
|
||||
|
||||
// اختیاری:
|
||||
workExperienceRouter.post("/no-experience", WorkExperienceController.setHasNoExperience.bind(WorkExperienceController));
|
||||
workExperienceRouter.post("/bulk", WorkExperienceController.bulkSave.bind(WorkExperienceController));
|
||||
|
||||
export default workExperienceRouter;
|
||||
@@ -0,0 +1,119 @@
|
||||
import {WorkExperience} from "../../../../models/WorkExperience";
|
||||
|
||||
type CreateWorkExperienceDTO = {
|
||||
hasNoExperience?: boolean;
|
||||
companyName?: string;
|
||||
lastPosition?: string;
|
||||
startYear?: number;
|
||||
endYear?: number;
|
||||
leavingReason?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type UpdateWorkExperienceDTO = Partial<CreateWorkExperienceDTO>;
|
||||
|
||||
class WorkExperienceService {
|
||||
private normalize(data: UpdateWorkExperienceDTO) {
|
||||
const normalized: UpdateWorkExperienceDTO = {...data};
|
||||
|
||||
// اگر کاربر گفت هیچ سابقهای ندارم، اطلاعات سابقه را خالی کن
|
||||
if (normalized.hasNoExperience === true) {
|
||||
normalized.companyName = undefined;
|
||||
normalized.lastPosition = undefined;
|
||||
normalized.startYear = undefined;
|
||||
normalized.endYear = undefined;
|
||||
normalized.leavingReason = undefined;
|
||||
normalized.description = undefined;
|
||||
}
|
||||
|
||||
// اگر endYear < startYear شد، خطا بده (کنترل پایه)
|
||||
if (
|
||||
typeof normalized.startYear === "number" &&
|
||||
typeof normalized.endYear === "number" &&
|
||||
normalized.endYear < normalized.startYear
|
||||
) {
|
||||
throw new Error("سال پایان نمیتواند کمتر از سال شروع باشد");
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
async create(applicantId: string, data: CreateWorkExperienceDTO) {
|
||||
const normalized = this.normalize(data);
|
||||
|
||||
return await WorkExperience.create({
|
||||
applicantId,
|
||||
...normalized,
|
||||
});
|
||||
}
|
||||
|
||||
async getAllByApplicant(applicantId: string) {
|
||||
return await WorkExperience.findAll({
|
||||
where: {applicantId},
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
async getById(id: string, applicantId: string) {
|
||||
const record = await WorkExperience.findOne({
|
||||
where: {id, applicantId},
|
||||
});
|
||||
|
||||
if (!record) throw new Error("سابقه کاری یافت نشد");
|
||||
return record;
|
||||
}
|
||||
|
||||
async update(id: string, applicantId: string, data: UpdateWorkExperienceDTO) {
|
||||
const record = await WorkExperience.findOne({
|
||||
where: {id, applicantId},
|
||||
});
|
||||
|
||||
if (!record) throw new Error("سابقه کاری یافت نشد");
|
||||
|
||||
const normalized = this.normalize(data);
|
||||
await record.update(normalized);
|
||||
return record;
|
||||
}
|
||||
|
||||
async remove(id: string, applicantId: string) {
|
||||
const record = await WorkExperience.findOne({
|
||||
where: {id, applicantId},
|
||||
});
|
||||
|
||||
if (!record) throw new Error("سابقه کاری یافت نشد");
|
||||
|
||||
await record.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* کمکی برای سناریویی که کاربر میزند "هیچ سابقهای ندارم":
|
||||
* میتونی همه سوابق قبلیاش را پاک کنی و یک رکورد فلگدار بسازی.
|
||||
* (اختیاری، اگر در UX شما لازم است)
|
||||
*/
|
||||
async setHasNoExperience(applicantId: string) {
|
||||
// پاک کردن سوابق قبلی
|
||||
await WorkExperience.destroy({where: {applicantId}});
|
||||
|
||||
// ساخت یک رکورد فلگدار
|
||||
return await WorkExperience.create({
|
||||
applicantId,
|
||||
hasNoExperience: true,
|
||||
});
|
||||
}
|
||||
|
||||
async bulkSync(applicantId: string, items: CreateWorkExperienceDTO[]) {
|
||||
await WorkExperience.destroy({where: {applicantId}});
|
||||
|
||||
if (!items || items.length === 0) return [];
|
||||
|
||||
const normalizedItems = items.map((item) => ({
|
||||
applicantId,
|
||||
...this.normalize(item),
|
||||
}));
|
||||
|
||||
return await WorkExperience.bulkCreate(normalizedItems);
|
||||
}
|
||||
}
|
||||
|
||||
export default new WorkExperienceService();
|
||||
@@ -0,0 +1,90 @@
|
||||
import {NextFunction} from "express";
|
||||
|
||||
import JobCategoryService from "../service/jobCategory.service";
|
||||
import {ServerResponse} from "../../../core/types";
|
||||
import {Controller} from "../../../core/controller/main.controller";
|
||||
|
||||
class JobCategoryControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = JobCategoryService;
|
||||
}
|
||||
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.getAll();
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getById(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
const data = await this.#service.getById(id);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "Ok",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.create(req.body);
|
||||
|
||||
return res.status(201).json({
|
||||
status: 201,
|
||||
data,
|
||||
message: "ساخته شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
const data = await this.#service.update(id, req.body);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data,
|
||||
message: "ویرایش شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const id = req?.params?.id;
|
||||
await this.#service.remove(id);
|
||||
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: {},
|
||||
message: "حذف شد",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JobCategoryController = new JobCategoryControllerClass();
|
||||
|
||||
export default JobCategoryController;
|
||||
12
src/modules/job-category/routes/jobCategory.routes.ts
Normal file
12
src/modules/job-category/routes/jobCategory.routes.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Router } from "express";
|
||||
import JobCategoryController from "../controller/jobCategory.controller";
|
||||
|
||||
const JobCategoryRouter = Router();
|
||||
|
||||
JobCategoryRouter.get("/", JobCategoryController.getAll.bind(JobCategoryController));
|
||||
JobCategoryRouter.get("/:id", JobCategoryController.getById.bind(JobCategoryController));
|
||||
JobCategoryRouter.post("/", JobCategoryController.create.bind(JobCategoryController));
|
||||
JobCategoryRouter.put("/:id", JobCategoryController.update.bind(JobCategoryController));
|
||||
JobCategoryRouter.delete("/:id", JobCategoryController.remove.bind(JobCategoryController));
|
||||
|
||||
export default JobCategoryRouter;
|
||||
45
src/modules/job-category/service/jobCategory.service.ts
Normal file
45
src/modules/job-category/service/jobCategory.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { JobCategory } from "../../../models/JobCategory";
|
||||
|
||||
class JobCategoryServiceClass {
|
||||
async getAll() {
|
||||
return await JobCategory.findAll({
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
}
|
||||
|
||||
async getById(id: string) {
|
||||
const item = await JobCategory.findByPk(id);
|
||||
if (!item) {
|
||||
throw new Error("رسته شغلی یافت نشد");
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
async create(data: any) {
|
||||
return await JobCategory.create(data);
|
||||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
const item = await JobCategory.findByPk(id);
|
||||
if (!item) {
|
||||
throw new Error("رسته شغلی یافت نشد");
|
||||
}
|
||||
|
||||
await item.update(data);
|
||||
return item;
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
const item = await JobCategory.findByPk(id);
|
||||
if (!item) {
|
||||
throw new Error("رسته شغلی یافت نشد");
|
||||
}
|
||||
|
||||
await item.destroy();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const JobCategoryService = new JobCategoryServiceClass();
|
||||
|
||||
export default JobCategoryService;
|
||||
93
src/modules/user/controller/user.controller.ts
Normal file
93
src/modules/user/controller/user.controller.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// src/modules/user/controller/user.controller.ts
|
||||
import { NextFunction } from "express";
|
||||
import { Controller } from "../../../core/controller/main.controller";
|
||||
import { ServerResponse } from "../../../core/types";
|
||||
import UserService from "../service/user.service";
|
||||
|
||||
class UserControllerClass extends Controller {
|
||||
#service;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#service = UserService;
|
||||
}
|
||||
|
||||
async create(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.create(req.body);
|
||||
return res.status(201).json({ status: 201, data, message: "کاربر ساخته شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.getAll({
|
||||
search: req.query.search as string | undefined,
|
||||
isActive: req.query.isActive !== undefined ? req.query.isActive === "true" : undefined,
|
||||
});
|
||||
|
||||
return res.status(200).json({ status: 200, data, message: "Ok" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getOne(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.getById(req.params.id);
|
||||
return res.status(200).json({ status: 200, data, message: "Ok" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const data = await this.#service.update(req.params.id, req.body);
|
||||
return res.status(200).json({ status: 200, data, message: "آپدیت شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async toggleActive(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { isActive } = req.body as { isActive: boolean };
|
||||
const data = await this.#service.toggleActive(req.params.id, isActive);
|
||||
return res.status(200).json({ status: 200, data, message: "وضعیت بروزرسانی شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
await this.#service.remove(req.params.id);
|
||||
return res.status(200).json({ status: 200, data: {}, message: "حذف شد" });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Auth: لاگین
|
||||
async login(req: any, res: ServerResponse, next: NextFunction) {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
const user = await this.#service.login(email, password);
|
||||
|
||||
// اینجا JWT بسازید و برگردانید (اگر دارید)
|
||||
return res.status(200).json({
|
||||
status: 200,
|
||||
data: { user },
|
||||
message: "ورود موفق",
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UserController = new UserControllerClass();
|
||||
export default UserController;
|
||||
18
src/modules/user/routes/user.routes.ts
Normal file
18
src/modules/user/routes/user.routes.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// src/modules/user/routes/user.routes.ts
|
||||
import { Router } from "express";
|
||||
import UserController from "../controller/user.controller";
|
||||
|
||||
const userRouter = Router();
|
||||
|
||||
// Auth
|
||||
userRouter.post("/login", UserController.login.bind(UserController));
|
||||
|
||||
// CRUD (معمولاً باید پشت adminMiddleware باشد)
|
||||
userRouter.post("/", UserController.create.bind(UserController));
|
||||
userRouter.get("/", UserController.getAll.bind(UserController));
|
||||
userRouter.get("/:id", UserController.getOne.bind(UserController));
|
||||
userRouter.put("/:id", UserController.update.bind(UserController));
|
||||
userRouter.patch("/:id/active", UserController.toggleActive.bind(UserController));
|
||||
userRouter.delete("/:id", UserController.remove.bind(UserController));
|
||||
|
||||
export default userRouter;
|
||||
132
src/modules/user/service/user.service.ts
Normal file
132
src/modules/user/service/user.service.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// src/modules/user/service/user.service.ts
|
||||
import bcrypt from "bcryptjs";
|
||||
import { Op } from "sequelize";
|
||||
import { User } from "../../../models/User";
|
||||
|
||||
type CreateUserDTO = {
|
||||
fullname: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roleId: string;
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
type UpdateUserDTO = Partial<{
|
||||
fullname: string;
|
||||
email: string;
|
||||
password: string;
|
||||
roleId: string;
|
||||
isActive: boolean;
|
||||
}>;
|
||||
|
||||
class UserServiceClass {
|
||||
private async hashPassword(password: string) {
|
||||
const saltRounds = 10;
|
||||
return await bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
|
||||
private sanitize(user: User) {
|
||||
const json = user.toJSON() as any;
|
||||
delete json.password;
|
||||
return json;
|
||||
}
|
||||
|
||||
async create(data: CreateUserDTO) {
|
||||
const exists = await User.findOne({ where: { email: data.email } });
|
||||
if (exists) throw new Error("ایمیل تکراری است");
|
||||
|
||||
const hashed = await this.hashPassword(data.password);
|
||||
|
||||
const user = await User.create({
|
||||
fullname: data.fullname,
|
||||
email: data.email,
|
||||
password: hashed,
|
||||
roleId: data.roleId,
|
||||
isActive: data.isActive ?? true,
|
||||
});
|
||||
|
||||
return this.sanitize(user);
|
||||
}
|
||||
|
||||
async getAll(query?: { search?: string; isActive?: boolean }) {
|
||||
const where: any = {};
|
||||
|
||||
if (typeof query?.isActive === "boolean") where.isActive = query.isActive;
|
||||
|
||||
if (query?.search) {
|
||||
where[Op.or] = [
|
||||
{ fullname: { [Op.iLike]: `%${query.search}%` } },
|
||||
{ email: { [Op.iLike]: `%${query.search}%` } },
|
||||
];
|
||||
}
|
||||
|
||||
const users = await User.findAll({
|
||||
where,
|
||||
order: [["createdAt", "DESC"]],
|
||||
// اگر relation Role را attach کردهاید:
|
||||
// include: [{ association: "role" }],
|
||||
});
|
||||
|
||||
return users.map((u:any) => this.sanitize(u));
|
||||
}
|
||||
|
||||
async getById(id: string) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("کاربر یافت نشد");
|
||||
return this.sanitize(user);
|
||||
}
|
||||
|
||||
async update(id: string, data: UpdateUserDTO) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("کاربر یافت نشد");
|
||||
|
||||
// اگر ایمیل تغییر میکند، تکراری نبودن را چک کن
|
||||
if (data.email && data.email !== user.email) {
|
||||
const exists = await User.findOne({ where: { email: data.email } });
|
||||
if (exists) throw new Error("ایمیل تکراری است");
|
||||
}
|
||||
|
||||
const updatePayload: any = { ...data };
|
||||
|
||||
if (typeof data.password === "string" && data.password.trim().length > 0) {
|
||||
updatePayload.password = await this.hashPassword(data.password);
|
||||
} else {
|
||||
delete updatePayload.password;
|
||||
}
|
||||
|
||||
await user.update(updatePayload);
|
||||
return this.sanitize(user);
|
||||
}
|
||||
|
||||
async toggleActive(id: string, isActive: boolean) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("کاربر یافت نشد");
|
||||
|
||||
await user.update({ isActive });
|
||||
return this.sanitize(user);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("کاربر یافت نشد");
|
||||
await user.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Auth: لاگین با ایمیل و پسورد
|
||||
async login(email: string, password: string) {
|
||||
const user = await User.findOne({ where: { email } });
|
||||
if (!user) throw new Error("ایمیل یا رمز عبور اشتباه است");
|
||||
|
||||
if (!user.isActive) throw new Error("حساب کاربری غیرفعال است");
|
||||
|
||||
const ok = await bcrypt.compare(password, user.password);
|
||||
if (!ok) throw new Error("ایمیل یا رمز عبور اشتباه است");
|
||||
|
||||
// اینجا معمولاً JWT صادر میکنید؛ من فقط user sanitize برمیگردونم
|
||||
return this.sanitize(user);
|
||||
}
|
||||
}
|
||||
|
||||
const UserService = new UserServiceClass()
|
||||
export default UserService;
|
||||
Reference in New Issue
Block a user