summaryrefslogtreecommitdiff
path: root/lib/users/department-domain/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/users/department-domain/service.ts')
-rw-r--r--lib/users/department-domain/service.ts439
1 files changed, 439 insertions, 0 deletions
diff --git a/lib/users/department-domain/service.ts b/lib/users/department-domain/service.ts
new file mode 100644
index 00000000..570ef2cf
--- /dev/null
+++ b/lib/users/department-domain/service.ts
@@ -0,0 +1,439 @@
+"use server";
+
+import { revalidatePath, revalidateTag } from "next/cache";
+import { unstable_cache, unstable_noStore } from "next/cache";
+import db from "@/db/db";
+import {
+ departmentDomainAssignments,
+ departmentDomainAssignmentHistory
+} from "@/db/schema/departmentDomainAssignments";
+import { and, eq, inArray, desc } from "drizzle-orm";
+import { getServerSession } from "next-auth/next";
+import { authOptions } from "@/app/api/auth/[...nextauth]/route";
+import { getErrorMessage } from "@/lib/handle-error";
+import { getCurrentCompanyCode } from "@/lib/users/knox-service";
+
+// 도메인 타입
+export type UserDomain = "pending" | "evcp" | "procurement" | "sales" | "engineering" | "partners";
+
+// 부서별 도메인 할당 정보 조회
+export async function getDepartmentDomainAssignments() {
+ return unstable_cache(
+ async () => {
+ try {
+ const assignments = await db
+ .select({
+ id: departmentDomainAssignments.id,
+ companyCode: departmentDomainAssignments.companyCode,
+ departmentCode: departmentDomainAssignments.departmentCode,
+ departmentName: departmentDomainAssignments.departmentName,
+ assignedDomain: departmentDomainAssignments.assignedDomain,
+ isActive: departmentDomainAssignments.isActive,
+ description: departmentDomainAssignments.description,
+ createdAt: departmentDomainAssignments.createdAt,
+ updatedAt: departmentDomainAssignments.updatedAt,
+ })
+ .from(departmentDomainAssignments)
+ .where(eq(departmentDomainAssignments.isActive, true))
+ .orderBy(
+ desc(departmentDomainAssignments.updatedAt)
+ );
+
+ return assignments;
+ } catch (error) {
+ console.error("부서별 도메인 할당 정보 조회 실패:", error);
+ return [];
+ }
+ },
+ ["department-domain-assignments"],
+ {
+ revalidate: 3600, // 1시간 캐시
+ tags: ["department-domain-assignments"],
+ }
+ )();
+}
+
+// 특정 부서들의 도메인 할당 정보 조회
+export async function getDepartmentDomainAssignmentsByDepartments(departmentCodes: string[]) {
+ return unstable_cache(
+ async () => {
+ try {
+ if (departmentCodes.length === 0) return [];
+
+ const assignments = await db
+ .select()
+ .from(departmentDomainAssignments)
+ .where(
+ and(
+ inArray(departmentDomainAssignments.departmentCode, departmentCodes),
+ eq(departmentDomainAssignments.isActive, true)
+ )
+ );
+
+ return assignments;
+ } catch (error) {
+ console.error("부서별 도메인 할당 정보 조회 실패:", error);
+ return [];
+ }
+ },
+ [`department-assignments-${departmentCodes.sort().join(',')}`],
+ {
+ revalidate: 3600,
+ tags: ["department-domain-assignments"],
+ }
+ )();
+}
+
+// 부서별 도메인 할당
+export async function assignDomainToDepartments(params: {
+ departmentCodes: string[];
+ domain: UserDomain;
+ description?: string;
+ departmentNames?: Record<string, string>; // departmentCode -> departmentName 매핑
+}) {
+ unstable_noStore();
+
+ try {
+ // 세션 확인
+ const session = await getServerSession(authOptions);
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ message: "인증이 필요합니다.",
+ };
+ }
+
+ const { departmentCodes, domain, description, departmentNames = {} } = params;
+ const userId = parseInt(session.user.id);
+
+ if (!departmentCodes.length || !domain) {
+ return {
+ success: false,
+ message: "부서 코드와 도메인이 필요합니다.",
+ };
+ }
+
+ // 현재 회사 코드 가져오기
+ const companyCode = await getCurrentCompanyCode();
+
+ await db.transaction(async (tx) => {
+ // 기존 할당 정보를 비활성화 (soft delete)
+ const existingAssignments = await tx
+ .select()
+ .from(departmentDomainAssignments)
+ .where(
+ and(
+ inArray(departmentDomainAssignments.departmentCode, departmentCodes),
+ eq(departmentDomainAssignments.isActive, true)
+ )
+ );
+
+ // 기존 할당이 있으면 비활성화하고 히스토리 기록
+ for (const existing of existingAssignments) {
+ // 히스토리 기록
+ await tx.insert(departmentDomainAssignmentHistory).values({
+ assignmentId: existing.id,
+ action: "deactivated",
+ previousValues: JSON.stringify({
+ assignedDomain: existing.assignedDomain,
+ isActive: true,
+ description: existing.description,
+ }),
+ newValues: JSON.stringify({
+ isActive: false,
+ }),
+ changedBy: userId,
+ changeReason: `새로운 도메인 할당으로 인한 기존 할당 비활성화: ${domain}`,
+ });
+
+ // 기존 할당 비활성화
+ await tx
+ .update(departmentDomainAssignments)
+ .set({
+ isActive: false,
+ updatedBy: userId,
+ updatedAt: new Date(),
+ })
+ .where(eq(departmentDomainAssignments.id, existing.id));
+ }
+
+ // 새로운 할당 생성
+ const newAssignments = departmentCodes.map(departmentCode => {
+ return {
+ companyCode,
+ departmentCode,
+ departmentName: departmentNames[departmentCode] || departmentCode,
+ assignedDomain: domain,
+ description,
+ isActive: true,
+ createdBy: userId,
+ updatedBy: userId,
+ };
+ });
+
+ const insertedAssignments = await tx
+ .insert(departmentDomainAssignments)
+ .values(newAssignments)
+ .returning();
+
+ // 신규 생성 히스토리 기록
+ for (let i = 0; i < insertedAssignments.length; i++) {
+ const assignment = insertedAssignments[i];
+ await tx.insert(departmentDomainAssignmentHistory).values({
+ assignmentId: assignment.id,
+ action: "created",
+ newValues: JSON.stringify({
+ companyCode: assignment.companyCode,
+ departmentCode: assignment.departmentCode,
+ assignedDomain: assignment.assignedDomain,
+ description: assignment.description,
+ }),
+ changedBy: userId,
+ changeReason: description || "부서별 도메인 할당",
+ });
+ }
+ });
+
+ // 캐시 무효화
+ revalidateTag("department-domain-assignments");
+ revalidatePath("/evcp/menu-access-dept");
+
+ return {
+ success: true,
+ message: `${departmentCodes.length}개 부서에 ${domain} 도메인이 성공적으로 할당되었습니다.`,
+ };
+
+ } catch (error) {
+ console.error("부서별 도메인 할당 실패:", error);
+ return {
+ success: false,
+ message: getErrorMessage(error),
+ };
+ }
+}
+
+// 부서별 도메인 할당 수정
+export async function updateDepartmentDomainAssignment(params: {
+ assignmentId: number;
+ domain: UserDomain;
+ description?: string;
+ isActive?: boolean;
+}) {
+ unstable_noStore();
+
+ try {
+ const session = await getServerSession(authOptions);
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ message: "인증이 필요합니다.",
+ };
+ }
+
+ const { assignmentId, domain, description, isActive = true } = params;
+ const userId = parseInt(session.user.id);
+
+ await db.transaction(async (tx) => {
+ // 기존 할당 정보 조회
+ const existing = await tx
+ .select()
+ .from(departmentDomainAssignments)
+ .where(eq(departmentDomainAssignments.id, assignmentId))
+ .limit(1);
+
+ if (existing.length === 0) {
+ throw new Error("존재하지 않는 할당 정보입니다.");
+ }
+
+ const currentAssignment = existing[0];
+
+ // 히스토리 기록
+ await tx.insert(departmentDomainAssignmentHistory).values({
+ assignmentId,
+ action: "updated",
+ previousValues: JSON.stringify({
+ assignedDomain: currentAssignment.assignedDomain,
+ description: currentAssignment.description,
+ isActive: currentAssignment.isActive,
+ }),
+ newValues: JSON.stringify({
+ assignedDomain: domain,
+ description,
+ isActive,
+ }),
+ changedBy: userId,
+ changeReason: description || "부서별 도메인 할당 수정",
+ });
+
+ // 할당 정보 업데이트
+ await tx
+ .update(departmentDomainAssignments)
+ .set({
+ assignedDomain: domain,
+ description,
+ isActive,
+ updatedBy: userId,
+ updatedAt: new Date(),
+ })
+ .where(eq(departmentDomainAssignments.id, assignmentId));
+ });
+
+ // 캐시 무효화
+ revalidateTag("department-domain-assignments");
+ revalidatePath("/evcp/menu-access-dept");
+
+ return {
+ success: true,
+ message: "도메인 할당 정보가 성공적으로 수정되었습니다.",
+ };
+
+ } catch (error) {
+ console.error("부서별 도메인 할당 수정 실패:", error);
+ return {
+ success: false,
+ message: getErrorMessage(error),
+ };
+ }
+}
+
+// 부서별 도메인 할당 삭제 (soft delete)
+export async function deleteDepartmentDomainAssignment(assignmentId: number) {
+ unstable_noStore();
+
+ try {
+ const session = await getServerSession(authOptions);
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ message: "인증이 필요합니다.",
+ };
+ }
+
+ const userId = parseInt(session.user.id);
+
+ await db.transaction(async (tx) => {
+ // 기존 할당 정보 조회
+ const existing = await tx
+ .select()
+ .from(departmentDomainAssignments)
+ .where(eq(departmentDomainAssignments.id, assignmentId))
+ .limit(1);
+
+ if (existing.length === 0) {
+ throw new Error("존재하지 않는 할당 정보입니다.");
+ }
+
+ const currentAssignment = existing[0];
+
+ // 히스토리 기록
+ await tx.insert(departmentDomainAssignmentHistory).values({
+ assignmentId,
+ action: "deleted",
+ previousValues: JSON.stringify({
+ assignedDomain: currentAssignment.assignedDomain,
+ description: currentAssignment.description,
+ isActive: currentAssignment.isActive,
+ }),
+ newValues: JSON.stringify({
+ isActive: false,
+ }),
+ changedBy: userId,
+ changeReason: "부서별 도메인 할당 삭제",
+ });
+
+ // 할당 정보 비활성화
+ await tx
+ .update(departmentDomainAssignments)
+ .set({
+ isActive: false,
+ updatedBy: userId,
+ updatedAt: new Date(),
+ })
+ .where(eq(departmentDomainAssignments.id, assignmentId));
+ });
+
+ // 캐시 무효화
+ revalidateTag("department-domain-assignments");
+ revalidatePath("/evcp/menu-access-dept");
+
+ return {
+ success: true,
+ message: "도메인 할당 정보가 성공적으로 삭제되었습니다.",
+ };
+
+ } catch (error) {
+ console.error("부서별 도메인 할당 삭제 실패:", error);
+ return {
+ success: false,
+ message: getErrorMessage(error),
+ };
+ }
+}
+
+// 부서별 도메인 할당 히스토리 조회
+export async function getDepartmentDomainAssignmentHistory(assignmentId?: number) {
+ return unstable_cache(
+ async () => {
+ try {
+ const query = db
+ .select({
+ id: departmentDomainAssignmentHistory.id,
+ assignmentId: departmentDomainAssignmentHistory.assignmentId,
+ action: departmentDomainAssignmentHistory.action,
+ previousValues: departmentDomainAssignmentHistory.previousValues,
+ newValues: departmentDomainAssignmentHistory.newValues,
+ changedBy: departmentDomainAssignmentHistory.changedBy,
+ changeReason: departmentDomainAssignmentHistory.changeReason,
+ createdAt: departmentDomainAssignmentHistory.createdAt,
+ })
+ .from(departmentDomainAssignmentHistory);
+
+ if (assignmentId) {
+ query.where(eq(departmentDomainAssignmentHistory.assignmentId, assignmentId));
+ }
+
+ const history = await query
+ .orderBy(desc(departmentDomainAssignmentHistory.createdAt))
+ .limit(100); // 최근 100개 제한
+
+ return history;
+ } catch (error) {
+ console.error("부서별 도메인 할당 히스토리 조회 실패:", error);
+ return [];
+ }
+ },
+ [`department-domain-assignment-history-${assignmentId || 'all'}`],
+ {
+ revalidate: 1800, // 30분 캐시
+ tags: ["department-domain-assignment-history"],
+ }
+ )();
+}
+
+// 도메인별 통계 조회
+export async function getDepartmentDomainStats() {
+ return unstable_cache(
+ async () => {
+ try {
+ const stats = await db
+ .select({
+ domain: departmentDomainAssignments.assignedDomain,
+ count: db.$count(departmentDomainAssignments),
+ })
+ .from(departmentDomainAssignments)
+ .where(eq(departmentDomainAssignments.isActive, true))
+ .groupBy(departmentDomainAssignments.assignedDomain);
+
+ return stats;
+ } catch (error) {
+ console.error("도메인별 통계 조회 실패:", error);
+ return [];
+ }
+ },
+ ["department-domain-stats"],
+ {
+ revalidate: 3600,
+ tags: ["department-domain-assignments"],
+ }
+ )();
+} \ No newline at end of file