feat(tenant): 租户品牌定制(brand_name/logo_url/theme_color/copyright + GET /auth/brand)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bad8639175
commit
f747ad5442
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `sys_tenant` ADD COLUMN `brand_name` VARCHAR(64) NULL,
|
||||||
|
ADD COLUMN `copyright` VARCHAR(255) NULL,
|
||||||
|
ADD COLUMN `logo_url` VARCHAR(255) NULL,
|
||||||
|
ADD COLUMN `theme_color` VARCHAR(16) NULL;
|
||||||
@ -12,14 +12,18 @@ datasource db {
|
|||||||
|
|
||||||
/// 租户
|
/// 租户
|
||||||
model Tenant {
|
model Tenant {
|
||||||
id String @id @default(uuid(7)) @db.VarChar(36)
|
id String @id @default(uuid(7)) @db.VarChar(36)
|
||||||
code String @unique @db.VarChar(64)
|
code String @unique @db.VarChar(64)
|
||||||
name String @db.VarChar(128)
|
name String @db.VarChar(128)
|
||||||
status String @default("enabled") @db.VarChar(16)
|
status String @default("enabled") @db.VarChar(16)
|
||||||
remark String? @db.VarChar(255)
|
remark String? @db.VarChar(255)
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
brandName String? @map("brand_name") @db.VarChar(64)
|
||||||
createdBy String? @map("created_by") @db.VarChar(36)
|
logoUrl String? @map("logo_url") @db.VarChar(255)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
themeColor String? @map("theme_color") @db.VarChar(16)
|
||||||
|
copyright String? @db.VarChar(255)
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
createdBy String? @map("created_by") @db.VarChar(36)
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@map("sys_tenant")
|
@@map("sys_tenant")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,6 +49,14 @@ export class AuthController {
|
|||||||
return this.auth.getPermissionSet(ctx.tenantCode, ctx.userId);
|
return this.auth.getPermissionSet(ctx.tenantCode, ctx.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前租户品牌(仅需登录)。 */
|
||||||
|
@Get('brand')
|
||||||
|
brand() {
|
||||||
|
const ctx = getRequestContext();
|
||||||
|
if (!ctx) throw new UnauthorizedException('未登录');
|
||||||
|
return this.auth.getBrand(ctx.tenantCode);
|
||||||
|
}
|
||||||
|
|
||||||
/** 修改本人密码(仅需登录)。 */
|
/** 修改本人密码(仅需登录)。 */
|
||||||
@Patch('me/password')
|
@Patch('me/password')
|
||||||
changePassword(@Body() dto: ChangePasswordDto) {
|
changePassword(@Body() dto: ChangePasswordDto) {
|
||||||
|
|||||||
@ -439,6 +439,25 @@ export class AuthService {
|
|||||||
return (await this.redis.getJson(sessionRevokedKey(sid))) != null;
|
return (await this.redis.getJson(sessionRevokedKey(sid))) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前租户品牌(登录后前端拉取应用)。 */
|
||||||
|
async getBrand(tenantCode: string): Promise<{
|
||||||
|
name: string;
|
||||||
|
brandName: string | null;
|
||||||
|
logoUrl: string | null;
|
||||||
|
themeColor: string | null;
|
||||||
|
copyright: string | null;
|
||||||
|
}> {
|
||||||
|
const tenant = await this.prisma.client.tenant.findUnique({ where: { code: tenantCode } });
|
||||||
|
if (!tenant) throw new UnauthorizedException('租户不存在');
|
||||||
|
return {
|
||||||
|
name: tenant.name,
|
||||||
|
brandName: tenant.brandName,
|
||||||
|
logoUrl: tenant.logoUrl,
|
||||||
|
themeColor: tenant.themeColor,
|
||||||
|
copyright: tenant.copyright,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** 聚合权限码与超管标志。 */
|
/** 聚合权限码与超管标志。 */
|
||||||
private async resolveAuth(tenantCode: string, userId: string): Promise<PermissionSet> {
|
private async resolveAuth(tenantCode: string, userId: string): Promise<PermissionSet> {
|
||||||
const roleRows = await this.prisma.client.userRole.findMany({
|
const roleRows = await this.prisma.client.userRole.findMany({
|
||||||
|
|||||||
@ -33,6 +33,26 @@ export class CreateTenantDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '品牌名' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
brandName?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Logo 地址' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
logoUrl?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '主色(如 #2E7FA3)' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
themeColor?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '版权文案' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
copyright?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateTenantDto {
|
export class UpdateTenantDto {
|
||||||
@ -50,6 +70,26 @@ export class UpdateTenantDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '品牌名' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
brandName?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Logo 地址' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
logoUrl?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '主色(如 #2E7FA3)' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
themeColor?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '版权文案' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
copyright?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ListTenantQuery extends PageQuery {
|
export class ListTenantQuery extends PageQuery {
|
||||||
|
|||||||
@ -30,6 +30,10 @@ export class TenantsService extends BaseCrudService {
|
|||||||
name: dto.name,
|
name: dto.name,
|
||||||
status: dto.status ?? 'enabled',
|
status: dto.status ?? 'enabled',
|
||||||
remark: dto.remark,
|
remark: dto.remark,
|
||||||
|
brandName: dto.brandName,
|
||||||
|
logoUrl: dto.logoUrl,
|
||||||
|
themeColor: dto.themeColor,
|
||||||
|
copyright: dto.copyright,
|
||||||
createdBy: ctx?.userId ?? null,
|
createdBy: ctx?.userId ?? null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user