260218:1712 20260218 TASK-BEFE-001n
All checks were successful
Build and Deploy / deploy (push) Successful in 4m55s

This commit is contained in:
admin
2026-02-18 17:12:11 +07:00
parent 01ce68acda
commit b84284f8a9
54 changed files with 1307 additions and 339 deletions

View File

@@ -102,7 +102,8 @@ export class AsBuiltDrawingService {
// 5. Commit Files
if (createDto.attachmentIds?.length) {
await this.fileStorageService.commit(
createDto.attachmentIds.map(String)
createDto.attachmentIds.map(String),
{ issueDate: revision.revisionDate, documentType: 'AsBuiltDrawing' }
);
}
@@ -188,7 +189,8 @@ export class AsBuiltDrawingService {
if (createDto.attachmentIds?.length) {
await this.fileStorageService.commit(
createDto.attachmentIds.map(String)
createDto.attachmentIds.map(String),
{ issueDate: revision.revisionDate, documentType: 'AsBuiltDrawing' }
);
}

View File

@@ -85,7 +85,8 @@ export class ContractDrawingService {
if (createDto.attachmentIds?.length) {
// ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง
await this.fileStorageService.commit(
createDto.attachmentIds.map(String)
createDto.attachmentIds.map(String),
{ documentType: 'ContractDrawing' }
);
}
@@ -213,7 +214,8 @@ export class ContractDrawingService {
// Commit new files
// ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง
await this.fileStorageService.commit(
updateDto.attachmentIds.map(String)
updateDto.attachmentIds.map(String),
{ documentType: 'ContractDrawing' }
);
}

View File

@@ -101,7 +101,8 @@ export class ShopDrawingService {
// 5. Commit Files
if (createDto.attachmentIds?.length) {
await this.fileStorageService.commit(
createDto.attachmentIds.map(String)
createDto.attachmentIds.map(String),
{ issueDate: revision.revisionDate, documentType: 'ShopDrawing' }
);
}
@@ -188,7 +189,8 @@ export class ShopDrawingService {
if (createDto.attachmentIds?.length) {
await this.fileStorageService.commit(
createDto.attachmentIds.map(String)
createDto.attachmentIds.map(String),
{ issueDate: revision.revisionDate, documentType: 'ShopDrawing' }
);
}

View File

@@ -0,0 +1,41 @@
import {
IsArray,
IsEnum,
IsInt,
IsOptional,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
export enum ActionType {
ADD = 'ADD',
REMOVE = 'REMOVE',
}
export class AssignmentActionDto {
@IsInt()
userId!: number;
@IsEnum(ActionType)
action!: ActionType;
// Add more fields if we need to update specific assignment properties
// For now, we assume simple Add/Remove Role logic
@IsInt()
roleId!: number;
@IsInt()
@IsOptional()
organizationId?: number;
@IsInt()
@IsOptional()
projectId?: number;
}
export class BulkAssignmentDto {
@IsArray()
@ValidateNested({ each: true })
@Type(() => AssignmentActionDto)
assignments!: AssignmentActionDto[];
}

View File

@@ -1,25 +1,29 @@
import { Injectable, BadRequestException } from '@nestjs/common';
import { Injectable, BadRequestException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserAssignment } from './entities/user-assignment.entity'; // ต้องไปสร้าง Entity นี้ก่อน (ดูข้อ 3)
import { Repository, DataSource } from 'typeorm';
import { UserAssignment } from './entities/user-assignment.entity';
import { AssignRoleDto } from './dto/assign-role.dto.js';
import { BulkAssignmentDto, ActionType } from './dto/bulk-assignment.dto';
import { User } from './entities/user.entity';
@Injectable()
export class UserAssignmentService {
private readonly logger = new Logger(UserAssignmentService.name);
constructor(
@InjectRepository(UserAssignment)
private assignmentRepo: Repository<UserAssignment>,
private dataSource: DataSource
) {}
async assignRole(dto: AssignRoleDto, assigner: User) {
// Validation: ตรวจสอบกฎเหล็ก (เลือกได้แค่ Scope เดียว)
const scopes = [dto.organizationId, dto.projectId, dto.contractId].filter(
(v) => v != null,
(v) => v != null
);
if (scopes.length > 1) {
throw new BadRequestException(
'Cannot assign multiple scopes at once. Choose one of Org, Project, or Contract.',
'Cannot assign multiple scopes at once. Choose one of Org, Project, or Contract.'
);
}
@@ -35,4 +39,57 @@ export class UserAssignmentService {
return this.assignmentRepo.save(assignment);
}
async bulkUpdateAssignments(dto: BulkAssignmentDto, assigner: User) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const results = [];
for (const assignmentAction of dto.assignments) {
const { userId, roleId, action, organizationId, projectId } =
assignmentAction;
if (action === ActionType.ADD) {
// Validation (Scope)
const scopes = [organizationId, projectId].filter((v) => v != null);
if (scopes.length > 1) {
throw new BadRequestException(
`User ${userId}: Cannot assign multiple scopes.`
);
}
const newAssignment = queryRunner.manager.create(UserAssignment, {
userId,
roleId,
organizationId,
projectId,
assignedByUserId: assigner.user_id,
});
results.push(await queryRunner.manager.save(newAssignment));
} else if (action === ActionType.REMOVE) {
// Construct delete criteria
const criteria: any = { userId, roleId };
if (organizationId) criteria.organizationId = organizationId;
if (projectId) criteria.projectId = projectId;
await queryRunner.manager.delete(UserAssignment, criteria);
results.push({ ...criteria, status: 'removed' });
}
}
await queryRunner.commitTransaction();
this.logger.log(`Bulk assignments updated by user ${assigner.user_id}`);
return results;
} catch (err) {
await queryRunner.rollbackTransaction();
this.logger.error(
`Failed to bulk update assignments: ${(err as Error).message}`
);
throw err;
} finally {
await queryRunner.release();
}
}
}

View File

@@ -27,6 +27,7 @@ import { UpdateUserDto } from './dto/update-user.dto';
import { AssignRoleDto } from './dto/assign-role.dto';
import { SearchUserDto } from './dto/search-user.dto';
import { UpdatePreferenceDto } from './dto/update-preference.dto';
import { BulkAssignmentDto } from './dto/bulk-assignment.dto';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { RbacGuard } from '../../common/guards/rbac.guard';
@@ -94,7 +95,7 @@ export class UserController {
}
@Patch('roles/:id/permissions')
@RequirePermission('permission.assign')
@RequirePermission('role.assign_permissions')
@ApiOperation({ summary: 'Update role permissions' })
async updateRolePermissions(
@Param('id', ParseIntPipe) id: number,
@@ -159,8 +160,21 @@ export class UserController {
@ApiOperation({ summary: 'Assign role to user' })
@ApiBody({ type: AssignRoleDto })
@ApiResponse({ status: 201, description: 'Role assigned' })
@RequirePermission('permission.assign')
@RequirePermission('user.manage_assignments')
assignRole(@Body() dto: AssignRoleDto, @CurrentUser() user: User) {
return this.assignmentService.assignRole(dto, user);
}
@Post('assignments/bulk')
@ApiOperation({ summary: 'Bulk update user assignments' })
@ApiBody({ type: BulkAssignmentDto })
@ApiResponse({ status: 200, description: 'Assignments updated' })
// @RequirePermission('user.manage_assignments')
@RequirePermission('user.manage_assignments')
bulkUpdateAssignments(
@Body() dto: BulkAssignmentDto,
@CurrentUser() user: User
) {
return this.assignmentService.bulkUpdateAssignments(dto, user);
}
}