forked from wangziqi/gongxue-base
feat(task1): restructure directories for turborepo monorepo
- Move backend/ to apps/server/ via git mv - Move frontend/ to apps/admin/ via git mv - Create packages/typescript-config/ with base, nestjs, and react-vite presets
This commit is contained in:
49
apps/server/src/tenants/dto/tenant.dto.ts
Normal file
49
apps/server/src/tenants/dto/tenant.dto.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { IsOptional, IsString, IsNotEmpty, IsEnum } from 'class-validator';
|
||||
|
||||
export class CreateTenantDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
contact?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
color?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export class UpdateTenantDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
contact?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
color?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
notes?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(['active', 'archived'])
|
||||
status?: string;
|
||||
}
|
||||
52
apps/server/src/tenants/tenants.controller.ts
Normal file
52
apps/server/src/tenants/tenants.controller.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, Request } from '@nestjs/common';
|
||||
import { TenantsService } from './tenants.service';
|
||||
import { CreateTenantDto, UpdateTenantDto } from './dto/tenant.dto';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
||||
import { extractRequestInfo } from '../common/request-utils';
|
||||
import { RequirePermission } from '../auth/decorators/permission.decorator';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Controller('tenants')
|
||||
export class TenantsController {
|
||||
constructor(private service: TenantsService, private logService: OperationLogsService) {}
|
||||
|
||||
@Get()
|
||||
@RequirePermission('tenant:view')
|
||||
findAll(@Query('includeArchived') includeArchived?: string) {
|
||||
return this.service.findAll({ includeArchived: includeArchived === 'true' });
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@RequirePermission('tenant:view')
|
||||
findOne(@Param('id') id: string) {
|
||||
return this.service.findOne(+id);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@RequirePermission('tenant:create')
|
||||
async create(@Body() dto: CreateTenantDto, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.create(dto);
|
||||
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '租赁方', action: '新增租赁方', targetId: result.id, targetType: 'tenant', detail: dto.name, ipAddress, userAgent });
|
||||
return result;
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@RequirePermission('tenant:edit')
|
||||
async update(@Param('id') id: string, @Body() dto: UpdateTenantDto, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.update(+id, dto);
|
||||
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '租赁方', action: '编辑租赁方', targetId: +id, targetType: 'tenant', detail: JSON.stringify(dto), ipAddress, userAgent });
|
||||
return result;
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@RequirePermission('tenant:delete')
|
||||
async remove(@Param('id') id: string, @Request() req: any) {
|
||||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||||
const result = await this.service.remove(+id);
|
||||
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '租赁方', action: '归档租赁方', targetId: +id, targetType: 'tenant', ipAddress, userAgent });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
14
apps/server/src/tenants/tenants.module.ts
Normal file
14
apps/server/src/tenants/tenants.module.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Tenant } from '../entities/tenant.entity';
|
||||
import { TenantsService } from './tenants.service';
|
||||
import { TenantsController } from './tenants.controller';
|
||||
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Tenant]), OperationLogsModule],
|
||||
controllers: [TenantsController],
|
||||
providers: [TenantsService],
|
||||
exports: [TenantsService],
|
||||
})
|
||||
export class TenantsModule {}
|
||||
50
apps/server/src/tenants/tenants.service.ts
Normal file
50
apps/server/src/tenants/tenants.service.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Not } from 'typeorm';
|
||||
import { Tenant } from '../entities/tenant.entity';
|
||||
import { CreateTenantDto, UpdateTenantDto } from './dto/tenant.dto';
|
||||
|
||||
// 预设色板(避开红绿盲敏感色,保证差异度)
|
||||
const COLOR_PALETTE = [
|
||||
'#ff7875', '#ffa940', '#ffc53d', '#73d13d', '#36cfc9',
|
||||
'#40a9ff', '#597ef7', '#9254de', '#f759ab', '#8c8c8c',
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class TenantsService {
|
||||
constructor(@InjectRepository(Tenant) private repo: Repository<Tenant>) {}
|
||||
|
||||
async findAll(query?: { includeArchived?: boolean }) {
|
||||
const where: any = {};
|
||||
if (!query?.includeArchived) where.status = Not('archived');
|
||||
return this.repo.find({ where, order: { createdAt: 'DESC' } });
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const tenant = await this.repo.findOne({ where: { id } });
|
||||
if (!tenant) throw new NotFoundException('租赁方不存在');
|
||||
return tenant;
|
||||
}
|
||||
|
||||
async create(dto: CreateTenantDto) {
|
||||
// 颜色未指定则自动分配(按当前租赁方数量取模)
|
||||
let color = dto.color;
|
||||
if (!color) {
|
||||
const total = await this.repo.count();
|
||||
color = COLOR_PALETTE[total % COLOR_PALETTE.length];
|
||||
}
|
||||
return this.repo.save(this.repo.create({ ...dto, color }));
|
||||
}
|
||||
|
||||
async update(id: number, dto: UpdateTenantDto) {
|
||||
await this.findOne(id);
|
||||
await this.repo.update(id, dto);
|
||||
return this.repo.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
await this.findOne(id);
|
||||
await this.repo.update(id, { status: 'archived' });
|
||||
return { message: '已归档' };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user