refactor(PermissionController): getPermission
This commit is contained in:
parent
c1a4df63e5
commit
fe1ebaa1cf
3 changed files with 230 additions and 13 deletions
|
|
@ -57,17 +57,26 @@ export class PermissionController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply = await getAsync("role_" + profile.id);
|
// Query ตำแหน่งรักษาการโดยใช้ service ที่มีอยู่
|
||||||
|
const orgRevision = await this.orgRevisionRepository.findOne({
|
||||||
|
select: ["id"],
|
||||||
|
where: {
|
||||||
|
orgRevisionIsDraft: false,
|
||||||
|
orgRevisionIsCurrent: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const actingData = await actingPositionService.getActingPositionsWithPrivilege(
|
||||||
|
profile.id,
|
||||||
|
orgRevision?.id
|
||||||
|
);
|
||||||
|
|
||||||
|
// ใช้ cache key ที่รวมสถานะ acting
|
||||||
|
const cacheKey = `role_${profile.id}_${actingData.isAct ? 'acting' : 'normal'}`;
|
||||||
|
let reply = await getAsync(cacheKey);
|
||||||
if (reply != null) {
|
if (reply != null) {
|
||||||
reply = JSON.parse(reply);
|
reply = JSON.parse(reply);
|
||||||
} else {
|
} else {
|
||||||
const orgRevision = await this.orgRevisionRepository.findOne({
|
|
||||||
select: ["id"],
|
|
||||||
where: {
|
|
||||||
orgRevisionIsDraft: false,
|
|
||||||
orgRevisionIsCurrent: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
let posMaster: any = await this.posMasterRepository.findOne({
|
let posMaster: any = await this.posMasterRepository.findOne({
|
||||||
select: ["authRoleId"],
|
select: ["authRoleId"],
|
||||||
where: {
|
where: {
|
||||||
|
|
@ -111,11 +120,140 @@ export class PermissionController extends Controller {
|
||||||
where: { authRoleId: getDetail.id },
|
where: { authRoleId: getDetail.id },
|
||||||
});
|
});
|
||||||
|
|
||||||
reply = {
|
// ถ้า User มีตำแหน่งรักษาการ ให้รวมสิทธิ์
|
||||||
...getDetail,
|
if (actingData.isAct && actingData.posMasterActs.length > 0) {
|
||||||
roles: roleAttrData,
|
// ดึง authRoleId ของทุกตำแหน่งรักษาการ
|
||||||
};
|
const actingAuthRoleIds = await this.posMasterActRepo
|
||||||
redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply));
|
.createQueryBuilder("posMasterAct")
|
||||||
|
.leftJoin("posMasterAct.posMaster", "posMaster")
|
||||||
|
.select("posMaster.authRoleId", "authRoleId")
|
||||||
|
.leftJoin("posMasterAct.posMasterChild", "posMasterChild")
|
||||||
|
.leftJoin("posMasterChild.current_holder", "profile")
|
||||||
|
.where("profile.id = :profileId", { profileId: profile.id })
|
||||||
|
.andWhere("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: orgRevision?.id })
|
||||||
|
.getRawMany();
|
||||||
|
|
||||||
|
// ดึง AuthRoleAttr ทั้งหมดของ acting roles
|
||||||
|
const actingRoleIds = actingAuthRoleIds.map(x => x.authRoleId).filter(id => id != null);
|
||||||
|
const actingRoleAttrs = await this.authRoleAttrRepo.find({
|
||||||
|
select: [
|
||||||
|
"authSysId",
|
||||||
|
"parentNode",
|
||||||
|
"attrOwnership",
|
||||||
|
"attrIsCreate",
|
||||||
|
"attrIsList",
|
||||||
|
"attrIsGet",
|
||||||
|
"attrIsUpdate",
|
||||||
|
"attrIsDelete",
|
||||||
|
"attrPrivilege",
|
||||||
|
],
|
||||||
|
where: { authRoleId: In(actingRoleIds) as any },
|
||||||
|
});
|
||||||
|
|
||||||
|
// สร้าง map ของ authSysId -> สิทธิ์ที่ดีที่สุดจาก acting
|
||||||
|
const actingPermissionMap = new Map<string, any>();
|
||||||
|
|
||||||
|
// ลำดับความสำคัญของ privilege (มากไปน้อย)
|
||||||
|
const privilegePriority: Record<string, number> = {
|
||||||
|
"OWNER": 7,
|
||||||
|
"PARENT": 6,
|
||||||
|
"ROOT": 5,
|
||||||
|
"BROTHER": 4,
|
||||||
|
"CHILD": 3,
|
||||||
|
"NORMAL": 2,
|
||||||
|
"SPECIFIC": 1,
|
||||||
|
"null": 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ฟังก์ชันเปรียบเทียบ privilege
|
||||||
|
const getHigherPrivilege = (priv1: string | null, priv2: string | null): string | null => {
|
||||||
|
const p1 = priv1 ?? "null";
|
||||||
|
const p2 = priv2 ?? "null";
|
||||||
|
const priority1 = privilegePriority[p1] ?? 0;
|
||||||
|
const priority2 = privilegePriority[p2] ?? 0;
|
||||||
|
return priority1 >= priority2 ? priv1 : priv2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ฟังก์ชันเปรียบเทียบ ownership (OWNER > STAFF > null)
|
||||||
|
const getHigherOwnership = (own1: string | null, own2: string | null): string | null => {
|
||||||
|
// OWNER สูงสุด
|
||||||
|
if (own1 === "OWNER" || own2 === "OWNER") return "OWNER";
|
||||||
|
// STAFF รองลงมา
|
||||||
|
if (own1 === "STAFF" || own2 === "STAFF") return "STAFF";
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const attr of actingRoleAttrs) {
|
||||||
|
const key = attr.authSysId;
|
||||||
|
if (!actingPermissionMap.has(key)) {
|
||||||
|
actingPermissionMap.set(key, attr);
|
||||||
|
} else {
|
||||||
|
// รวมสิทธิ์: ใช้ OR logic สำหรับ CRUD
|
||||||
|
// สำหรับ attrOwnership และ attrPrivilege ใช้ค่าที่ใหญ่ที่สุด
|
||||||
|
const existing = actingPermissionMap.get(key);
|
||||||
|
actingPermissionMap.set(key, {
|
||||||
|
...attr,
|
||||||
|
attrIsCreate: existing.attrIsCreate || attr.attrIsCreate,
|
||||||
|
attrIsList: existing.attrIsList || attr.attrIsList,
|
||||||
|
attrIsGet: existing.attrIsGet || attr.attrIsGet,
|
||||||
|
attrIsUpdate: existing.attrIsUpdate || attr.attrIsUpdate,
|
||||||
|
attrIsDelete: existing.attrIsDelete || attr.attrIsDelete,
|
||||||
|
attrPrivilege: getHigherPrivilege(attr.attrPrivilege, existing.attrPrivilege),
|
||||||
|
parentNode: attr.parentNode, // ใช้ parentNode ของ acting role
|
||||||
|
attrOwnership: getHigherOwnership(attr.attrOwnership, existing.attrOwnership),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// รวมกับสิทธิ์พื้นฐานของ User
|
||||||
|
// สำหรับระบบที่อยู่ใน acting: ใช้สิทธิ์จาก acting
|
||||||
|
// สำหรับระบบที่ไม่อยู่ใน acting: ใช้สิทธิ์พื้นฐาน
|
||||||
|
const mergedRoleAttrs = roleAttrData.map((baseAttr) => {
|
||||||
|
const actingAttr = actingPermissionMap.get(baseAttr.authSysId);
|
||||||
|
if (actingAttr) {
|
||||||
|
// ระบบนี้มีสิทธิ์จาก acting - ใช้ค่าจาก acting role
|
||||||
|
return {
|
||||||
|
...baseAttr,
|
||||||
|
parentNode: actingAttr.parentNode,
|
||||||
|
attrOwnership: actingAttr.attrOwnership,
|
||||||
|
attrIsCreate: actingAttr.attrIsCreate,
|
||||||
|
attrIsList: actingAttr.attrIsList,
|
||||||
|
attrIsGet: actingAttr.attrIsGet,
|
||||||
|
attrIsUpdate: actingAttr.attrIsUpdate,
|
||||||
|
attrIsDelete: actingAttr.attrIsDelete,
|
||||||
|
attrPrivilege: actingAttr.attrPrivilege,
|
||||||
|
// เพิ่ม metadata เพื่อระบุว่ามาจาก acting
|
||||||
|
_isActing: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// เก็บสิทธิ์พื้นฐานสำหรับระบบที่ไม่ได้รักษาการ
|
||||||
|
return baseAttr;
|
||||||
|
});
|
||||||
|
|
||||||
|
// เพิ่มระบบที่มีเฉพาะใน acting roles
|
||||||
|
for (const [authSysId, actingAttr] of actingPermissionMap) {
|
||||||
|
if (!roleAttrData.find(a => a.authSysId === authSysId)) {
|
||||||
|
mergedRoleAttrs.push({
|
||||||
|
...actingAttr,
|
||||||
|
_isActing: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = {
|
||||||
|
...getDetail,
|
||||||
|
roles: mergedRoleAttrs,
|
||||||
|
isActing: true, // Flag ระบุสถานะ acting
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// ไม่มี acting - ใช้ response เดิม
|
||||||
|
reply = {
|
||||||
|
...getDetail,
|
||||||
|
roles: roleAttrData,
|
||||||
|
isActing: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
redisClient.setex(cacheKey, 86400, JSON.stringify(reply));
|
||||||
}
|
}
|
||||||
return new HttpSuccess(reply);
|
return new HttpSuccess(reply);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ import { ProfileActposition } from "../entities/ProfileActposition";
|
||||||
import { RequestWithUser } from "../middlewares/user";
|
import { RequestWithUser } from "../middlewares/user";
|
||||||
import { escape } from "querystring";
|
import { escape } from "querystring";
|
||||||
|
|
||||||
|
const REDIS_HOST = process.env.REDIS_HOST;
|
||||||
|
const REDIS_PORT = process.env.REDIS_PORT;
|
||||||
|
|
||||||
@Route("api/v1/org/pos/act")
|
@Route("api/v1/org/pos/act")
|
||||||
@Tags("PosMasterAct")
|
@Tags("PosMasterAct")
|
||||||
@Security("bearerAuth")
|
@Security("bearerAuth")
|
||||||
|
|
@ -37,6 +40,23 @@ export class PosMasterActController extends Controller {
|
||||||
private posMasterActRepository = AppDataSource.getRepository(PosMasterAct);
|
private posMasterActRepository = AppDataSource.getRepository(PosMasterAct);
|
||||||
private posMasterRepository = AppDataSource.getRepository(PosMaster);
|
private posMasterRepository = AppDataSource.getRepository(PosMaster);
|
||||||
private actpositionRepository = AppDataSource.getRepository(ProfileActposition);
|
private actpositionRepository = AppDataSource.getRepository(ProfileActposition);
|
||||||
|
private redis = require("redis");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function สำหรับลบ cache เมื่อมีการเปลี่ยนแปลง acting position
|
||||||
|
*/
|
||||||
|
private async invalidatePermissionCache(profileIds: string[]) {
|
||||||
|
const redisClient = await this.redis.createClient({
|
||||||
|
host: REDIS_HOST,
|
||||||
|
port: REDIS_PORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const profileId of profileIds) {
|
||||||
|
redisClient.del(`role_${profileId}_acting`);
|
||||||
|
redisClient.del(`role_${profileId}_normal`);
|
||||||
|
redisClient.del(`menu_${profileId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API เพิ่มรักษาการในตำแหน่ง
|
* API เพิ่มรักษาการในตำแหน่ง
|
||||||
|
|
@ -89,6 +109,12 @@ export class PosMasterActController extends Controller {
|
||||||
posMasterAct.createdAt = new Date();
|
posMasterAct.createdAt = new Date();
|
||||||
posMasterAct.lastUpdatedAt = new Date();
|
posMasterAct.lastUpdatedAt = new Date();
|
||||||
await this.posMasterActRepository.save(posMasterAct);
|
await this.posMasterActRepository.save(posMasterAct);
|
||||||
|
|
||||||
|
// ลบ cache ของผู้ถูกรักษาการ (current_holder ของ posMasterChild)
|
||||||
|
if (posMasterChild.current_holderId) {
|
||||||
|
await this.invalidatePermissionCache([posMasterChild.current_holderId]);
|
||||||
|
}
|
||||||
|
|
||||||
return new HttpSuccess(posMasterAct);
|
return new HttpSuccess(posMasterAct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,6 +321,7 @@ export class PosMasterActController extends Controller {
|
||||||
where: {
|
where: {
|
||||||
id: id,
|
id: id,
|
||||||
},
|
},
|
||||||
|
relations: ["posMasterChild"],
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
result = await this.posMasterActRepository.delete({ id: id });
|
result = await this.posMasterActRepository.delete({ id: id });
|
||||||
|
|
@ -318,6 +345,11 @@ export class PosMasterActController extends Controller {
|
||||||
p.posMasterOrder = i + 1;
|
p.posMasterOrder = i + 1;
|
||||||
await this.posMasterActRepository.save(p);
|
await this.posMasterActRepository.save(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ลบ cache ของผู้ถูกรักษาการ
|
||||||
|
if (posMasterAct.posMasterChild?.current_holderId) {
|
||||||
|
await this.invalidatePermissionCache([posMasterAct.posMasterChild.current_holderId]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new HttpSuccess();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
@ -768,6 +800,9 @@ export class PosMasterActController extends Controller {
|
||||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลรักษาการในตำแหน่งของหน่วยงานนี้");
|
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลรักษาการในตำแหน่งของหน่วยงานนี้");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// เก็บ profileIds ที่ได้รับผลกระทบเพื่อลบ cache
|
||||||
|
const affectedProfileIds: string[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
posMasterActs.map(async (posMasterAct) => {
|
posMasterActs.map(async (posMasterAct) => {
|
||||||
const orgShortName =
|
const orgShortName =
|
||||||
|
|
@ -782,6 +817,8 @@ export class PosMasterActController extends Controller {
|
||||||
const profileId = posMasterAct.posMasterChild?.current_holderId;
|
const profileId = posMasterAct.posMasterChild?.current_holderId;
|
||||||
|
|
||||||
if (profileId) {
|
if (profileId) {
|
||||||
|
affectedProfileIds.push(profileId);
|
||||||
|
|
||||||
const existingActivePositions = await this.actpositionRepository.find({
|
const existingActivePositions = await this.actpositionRepository.find({
|
||||||
select: [
|
select: [
|
||||||
"id",
|
"id",
|
||||||
|
|
@ -834,6 +871,11 @@ export class PosMasterActController extends Controller {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ลบ cache ของผู้ถูกรักษาการทั้งหมด
|
||||||
|
if (affectedProfileIds.length > 0) {
|
||||||
|
await this.invalidatePermissionCache(affectedProfileIds);
|
||||||
|
}
|
||||||
|
|
||||||
return new HttpSuccess();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ import HttpStatus from "../interfaces/http-status";
|
||||||
import HttpSuccess from "../interfaces/http-success";
|
import HttpSuccess from "../interfaces/http-success";
|
||||||
import permission from "../interfaces/permission";
|
import permission from "../interfaces/permission";
|
||||||
import { setLogDataDiff } from "../interfaces/utils";
|
import { setLogDataDiff } from "../interfaces/utils";
|
||||||
|
|
||||||
|
const REDIS_HOST = process.env.REDIS_HOST;
|
||||||
|
const REDIS_PORT = process.env.REDIS_PORT;
|
||||||
|
|
||||||
@Route("api/v1/org/profile/actposition")
|
@Route("api/v1/org/profile/actposition")
|
||||||
@Tags("ProfileActposition")
|
@Tags("ProfileActposition")
|
||||||
@Security("bearerAuth")
|
@Security("bearerAuth")
|
||||||
|
|
@ -32,6 +36,23 @@ export class ProfileActpositionController extends Controller {
|
||||||
private profileRepo = AppDataSource.getRepository(Profile);
|
private profileRepo = AppDataSource.getRepository(Profile);
|
||||||
private profileActpositionRepo = AppDataSource.getRepository(ProfileActposition);
|
private profileActpositionRepo = AppDataSource.getRepository(ProfileActposition);
|
||||||
private profileActpositionHistoryRepo = AppDataSource.getRepository(ProfileActpositionHistory);
|
private profileActpositionHistoryRepo = AppDataSource.getRepository(ProfileActpositionHistory);
|
||||||
|
private redis = require("redis");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function สำหรับลบ cache เมื่อมีการเปลี่ยนแปลง acting position
|
||||||
|
*/
|
||||||
|
private async invalidatePermissionCache(profileIds: string[]) {
|
||||||
|
const redisClient = await this.redis.createClient({
|
||||||
|
host: REDIS_HOST,
|
||||||
|
port: REDIS_PORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const profileId of profileIds) {
|
||||||
|
redisClient.del(`role_${profileId}_acting`);
|
||||||
|
redisClient.del(`role_${profileId}_normal`);
|
||||||
|
redisClient.del(`menu_${profileId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Get("user")
|
@Get("user")
|
||||||
public async detailProfileActpositionUser(@Request() request: { user: Record<string, any> }) {
|
public async detailProfileActpositionUser(@Request() request: { user: Record<string, any> }) {
|
||||||
|
|
@ -161,6 +182,10 @@ export class ProfileActpositionController extends Controller {
|
||||||
history.profileActpositionId = data.id;
|
history.profileActpositionId = data.id;
|
||||||
await this.profileActpositionHistoryRepo.save(history, { data: req });
|
await this.profileActpositionHistoryRepo.save(history, { data: req });
|
||||||
//setLogDataDiff(req, { before, after: history });
|
//setLogDataDiff(req, { before, after: history });
|
||||||
|
|
||||||
|
// ลบ cache เมื่อสร้าง acting position ใหม่
|
||||||
|
await this.invalidatePermissionCache([body.profileId]);
|
||||||
|
|
||||||
return new HttpSuccess(data.id);
|
return new HttpSuccess(data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,6 +223,9 @@ export class ProfileActpositionController extends Controller {
|
||||||
// setLogDataDiff(req, { before: before_null, after: history }),
|
// setLogDataDiff(req, { before: before_null, after: history }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// ลบ cache เมื่อแก้ไข acting position
|
||||||
|
await this.invalidatePermissionCache([record.profileId]);
|
||||||
|
|
||||||
return new HttpSuccess();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,6 +264,9 @@ export class ProfileActpositionController extends Controller {
|
||||||
this.profileActpositionHistoryRepo.save(history, { data: req }),
|
this.profileActpositionHistoryRepo.save(history, { data: req }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// ลบ cache เมื่อ soft delete acting position
|
||||||
|
await this.invalidatePermissionCache([record.profileId]);
|
||||||
|
|
||||||
return new HttpSuccess();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,6 +276,7 @@ export class ProfileActpositionController extends Controller {
|
||||||
@Request() req: RequestWithUser,
|
@Request() req: RequestWithUser,
|
||||||
) {
|
) {
|
||||||
const _record = await this.profileActpositionRepo.findOneBy({ id: actpositionId });
|
const _record = await this.profileActpositionRepo.findOneBy({ id: actpositionId });
|
||||||
|
const profileId = _record?.profileId;
|
||||||
if (_record) {
|
if (_record) {
|
||||||
await new permission().PermissionOrgUserDelete(
|
await new permission().PermissionOrgUserDelete(
|
||||||
req,
|
req,
|
||||||
|
|
@ -261,6 +293,11 @@ export class ProfileActpositionController extends Controller {
|
||||||
if (result.affected == undefined || result.affected <= 0)
|
if (result.affected == undefined || result.affected <= 0)
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
|
throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล");
|
||||||
|
|
||||||
|
// ลบ cache เมื่อ delete acting position
|
||||||
|
if (profileId) {
|
||||||
|
await this.invalidatePermissionCache([profileId]);
|
||||||
|
}
|
||||||
|
|
||||||
return new HttpSuccess();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue