feat: 接入字典管理接口
This commit is contained in:
parent
cf2c20ba22
commit
bd4937a8c7
@ -171,11 +171,15 @@ VALUES
|
|||||||
(2041700000000120101, 2041700000000100201, 'SystemMenuCreate', NULL, NULL, NULL, 'System:Menu:Create', 'button', '{"title":"common.create"}', 1, 0),
|
(2041700000000120101, 2041700000000100201, 'SystemMenuCreate', NULL, NULL, NULL, 'System:Menu:Create', 'button', '{"title":"common.create"}', 1, 0),
|
||||||
(2041700000000120102, 2041700000000100201, 'SystemMenuEdit', NULL, NULL, NULL, 'System:Menu:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
(2041700000000120102, 2041700000000100201, 'SystemMenuEdit', NULL, NULL, NULL, 'System:Menu:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
||||||
(2041700000000120103, 2041700000000100201, 'SystemMenuDelete', NULL, NULL, NULL, 'System:Menu:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
(2041700000000120103, 2041700000000100201, 'SystemMenuDelete', NULL, NULL, NULL, 'System:Menu:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
||||||
(2041700000000100202, 2041700000000100002, 'SystemDept', '/system/dept', '/system/dept/list', NULL, 'System:Dept:List', 'menu', '{"icon":"carbon:container-services","title":"system.dept.title"}', 1, 3),
|
(2041700000000100204, 2041700000000100002, 'SystemDict', '/system/dict', '/system/dict/list', NULL, 'System:Dict:List', 'menu', '{"icon":"carbon:data-structured","title":"system.dict.title"}', 1, 3),
|
||||||
|
(2041700000000120207, 2041700000000100204, 'SystemDictCreate', NULL, NULL, NULL, 'System:Dict:Create', 'button', '{"title":"common.create"}', 1, 0),
|
||||||
|
(2041700000000120208, 2041700000000100204, 'SystemDictEdit', NULL, NULL, NULL, 'System:Dict:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
||||||
|
(2041700000000120209, 2041700000000100204, 'SystemDictDelete', NULL, NULL, NULL, 'System:Dict:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
||||||
|
(2041700000000100202, 2041700000000100002, 'SystemDept', '/system/dept', '/system/dept/list', NULL, 'System:Dept:List', 'menu', '{"icon":"carbon:container-services","title":"system.dept.title"}', 1, 4),
|
||||||
(2041700000000120201, 2041700000000100202, 'SystemDeptCreate', NULL, NULL, NULL, 'System:Dept:Create', 'button', '{"title":"common.create"}', 1, 0),
|
(2041700000000120201, 2041700000000100202, 'SystemDeptCreate', NULL, NULL, NULL, 'System:Dept:Create', 'button', '{"title":"common.create"}', 1, 0),
|
||||||
(2041700000000120202, 2041700000000100202, 'SystemDeptEdit', NULL, NULL, NULL, 'System:Dept:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
(2041700000000120202, 2041700000000100202, 'SystemDeptEdit', NULL, NULL, NULL, 'System:Dept:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
||||||
(2041700000000120203, 2041700000000100202, 'SystemDeptDelete', NULL, NULL, NULL, 'System:Dept:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
(2041700000000120203, 2041700000000100202, 'SystemDeptDelete', NULL, NULL, NULL, 'System:Dept:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
||||||
(2041700000000100203, 2041700000000100002, 'SystemKtTableDemo', '/system/ktTableDemo', '/system/ktTableDemo/list', NULL, 'System:KtTableDemo:List', 'menu', '{"icon":"lucide:table-2","title":"system.ktTableDemo.title"}', 1, 4),
|
(2041700000000100203, 2041700000000100002, 'SystemKtTableDemo', '/system/ktTableDemo', '/system/ktTableDemo/list', NULL, 'System:KtTableDemo:List', 'menu', '{"icon":"lucide:table-2","title":"system.ktTableDemo.title"}', 1, 5),
|
||||||
(2041700000000120204, 2041700000000100203, 'SystemKtTableDemoCreate', NULL, NULL, NULL, 'System:KtTableDemo:Create', 'button', '{"title":"common.create"}', 1, 0),
|
(2041700000000120204, 2041700000000100203, 'SystemKtTableDemoCreate', NULL, NULL, NULL, 'System:KtTableDemo:Create', 'button', '{"title":"common.create"}', 1, 0),
|
||||||
(2041700000000120205, 2041700000000100203, 'SystemKtTableDemoEdit', NULL, NULL, NULL, 'System:KtTableDemo:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
(2041700000000120205, 2041700000000100203, 'SystemKtTableDemoEdit', NULL, NULL, NULL, 'System:KtTableDemo:Edit', 'button', '{"title":"common.edit"}', 1, 0),
|
||||||
(2041700000000120206, 2041700000000100203, 'SystemKtTableDemoDelete', NULL, NULL, NULL, 'System:KtTableDemo:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
(2041700000000120206, 2041700000000100203, 'SystemKtTableDemoDelete', NULL, NULL, NULL, 'System:KtTableDemo:Delete', 'button', '{"title":"common.delete"}', 1, 0),
|
||||||
|
|||||||
@ -1,16 +1,31 @@
|
|||||||
import {
|
import {
|
||||||
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Delete,
|
||||||
Get,
|
Get,
|
||||||
|
HttpCode,
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
|
Param,
|
||||||
ParseIntPipe,
|
ParseIntPipe,
|
||||||
|
Post,
|
||||||
Query,
|
Query,
|
||||||
Res,
|
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { DictService } from './dict.service';
|
import { DictService } from './dict.service';
|
||||||
import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
|
import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
|
||||||
import { ApiArrayResponse, ToolsService } from '@/common';
|
import {
|
||||||
import { DictDto } from './dict.dto';
|
ApiArrayResponse,
|
||||||
|
ApiPageResponse,
|
||||||
|
vbenPage,
|
||||||
|
vbenSuccess,
|
||||||
|
} from '@/common';
|
||||||
|
import {
|
||||||
|
AdminDictBodyDto,
|
||||||
|
AdminDictDto,
|
||||||
|
AdminDictQueryDto,
|
||||||
|
AdminDictUpdateDto,
|
||||||
|
DictDto,
|
||||||
|
} from './dict.dto';
|
||||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
||||||
|
|
||||||
const componentTypeDictExample = [
|
const componentTypeDictExample = [
|
||||||
@ -39,28 +54,78 @@ const chartDictExample = [
|
|||||||
@Controller('dict')
|
@Controller('dict')
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
export class DictController {
|
export class DictController {
|
||||||
constructor(
|
constructor(private readonly dictService: DictService) {}
|
||||||
private readonly toolsService: ToolsService,
|
|
||||||
private readonly dictService: DictService,
|
@ApiOperation({ summary: '获取字典分页列表' })
|
||||||
) {}
|
@ApiPageResponse(AdminDictDto, [
|
||||||
|
{
|
||||||
|
id: '2041700000000300001',
|
||||||
|
dictCode: 'COMPONENT_TYPE',
|
||||||
|
label: '图表',
|
||||||
|
value: '1',
|
||||||
|
childrenCode: 'CHART',
|
||||||
|
sort: 1,
|
||||||
|
status: 1,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
@Get('list')
|
||||||
|
async list(@Query() query: AdminDictQueryDto) {
|
||||||
|
const page = await this.dictService.page(query);
|
||||||
|
return vbenPage(page.items, page.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '获取字典编码选项' })
|
||||||
|
@Get('codes')
|
||||||
|
async codes() {
|
||||||
|
return vbenSuccess(await this.dictService.getDictCodeOptions());
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: '根据key获取字典' })
|
@ApiOperation({ summary: '根据key获取字典' })
|
||||||
@ApiQuery({ name: 'dictKey', type: String })
|
@ApiQuery({ name: 'dictKey', type: String })
|
||||||
@ApiArrayResponse(DictDto, componentTypeDictExample)
|
@ApiArrayResponse(DictDto, componentTypeDictExample)
|
||||||
@Get('getDictByKey')
|
@Get('getDictByKey')
|
||||||
async getDictByKey(@Res() res, @Query('dictKey') dictKey: string) {
|
async getDictByKey(@Query('dictKey') dictKey: string) {
|
||||||
const dict = await this.dictService.getDictByKey(dictKey);
|
const dict = await this.dictService.getDictByKey(dictKey);
|
||||||
|
|
||||||
return res.send(this.toolsService.res(HttpStatus.OK, '操作成功', dict));
|
return vbenSuccess(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: '根据组件类型获取组件字典' })
|
@ApiOperation({ summary: '根据组件类型获取组件字典' })
|
||||||
@ApiQuery({ name: 'type', type: Number })
|
@ApiQuery({ name: 'type', type: Number })
|
||||||
@ApiArrayResponse(DictDto, chartDictExample)
|
@ApiArrayResponse(DictDto, chartDictExample)
|
||||||
@Get('getComponentDictByType')
|
@Get('getComponentDictByType')
|
||||||
async getComponentDictByType(@Res() res, @Query('type', ParseIntPipe) type) {
|
async getComponentDictByType(@Query('type', ParseIntPipe) type) {
|
||||||
const dict = await this.dictService.getComponentDictByType(type);
|
const dict = await this.dictService.getComponentDictByType(type);
|
||||||
|
|
||||||
return res.send(this.toolsService.res(HttpStatus.OK, '操作成功', dict));
|
return vbenSuccess(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '新增字典项' })
|
||||||
|
@Post('save')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async save(@Body() body: AdminDictBodyDto) {
|
||||||
|
return vbenSuccess(await this.dictService.save(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '编辑字典项' })
|
||||||
|
@Post('update')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async update(@Body() body: AdminDictUpdateDto) {
|
||||||
|
return vbenSuccess(await this.dictService.update(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '删除字典项' })
|
||||||
|
@Delete(':id')
|
||||||
|
async remove(@Param('id') id: string) {
|
||||||
|
return vbenSuccess(await this.dictService.remove(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '启停字典项' })
|
||||||
|
@Post('toggle')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiQuery({ name: 'id', type: String })
|
||||||
|
@ApiQuery({ name: 'status', type: Number })
|
||||||
|
async toggle(@Query('id') id: string, @Query('status') status: string) {
|
||||||
|
return vbenSuccess(await this.dictService.toggle(id, Number(status)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
export class DictDto {
|
export class DictDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@ -15,5 +16,120 @@ export class DictDto {
|
|||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 1,
|
example: 1,
|
||||||
})
|
})
|
||||||
value: number;
|
value: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminDictDto {
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2041700000000300001',
|
||||||
|
})
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'COMPONENT_TYPE',
|
||||||
|
})
|
||||||
|
dictCode: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '图表',
|
||||||
|
})
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
})
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 'CHART',
|
||||||
|
})
|
||||||
|
childrenCode?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
sort?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
status?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: '2026-06-03T12:00:00.000Z',
|
||||||
|
})
|
||||||
|
createTime?: Date;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: '2026-06-03T12:00:00.000Z',
|
||||||
|
})
|
||||||
|
updateTime?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminDictQueryDto {
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
page?: number | string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
pageNo?: number | string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
pageSize?: number | string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
keyword?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
dictCode?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
label?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
value?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
childrenCode?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
status?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminDictBodyDto {
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'COMPONENT_TYPE',
|
||||||
|
})
|
||||||
|
dictCode: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '图表',
|
||||||
|
})
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
})
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 'CHART',
|
||||||
|
})
|
||||||
|
childrenCode?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
sort?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdminDictUpdateDto extends PartialType(AdminDictBodyDto) {
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2041700000000300001',
|
||||||
|
})
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
import { Injectable, OnApplicationBootstrap } from '@nestjs/common';
|
import { HttpStatus, Injectable, OnApplicationBootstrap } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Brackets, Repository } from 'typeorm';
|
||||||
import { setDictDecodeCache } from '@/common';
|
import { setDictDecodeCache, throwVbenError } from '@/common';
|
||||||
import { AdminDict } from './admin-dict.entity';
|
import { AdminDict } from './admin-dict.entity';
|
||||||
|
import {
|
||||||
|
AdminDictBodyDto,
|
||||||
|
AdminDictQueryDto,
|
||||||
|
AdminDictUpdateDto,
|
||||||
|
} from './dict.dto';
|
||||||
|
|
||||||
const COMPONENT_TYPE_DICT_KEY = 'COMPONENT_TYPE';
|
const COMPONENT_TYPE_DICT_KEY = 'COMPONENT_TYPE';
|
||||||
|
|
||||||
@ -32,6 +37,160 @@ export class DictService implements OnApplicationBootstrap {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async page(query: AdminDictQueryDto = {}) {
|
||||||
|
const pageNo = this.toPositiveNumber(query.pageNo ?? query.page, 1);
|
||||||
|
const pageSize = this.toPositiveNumber(query.pageSize, 20);
|
||||||
|
const builder = this.dictRepository
|
||||||
|
.createQueryBuilder('dict')
|
||||||
|
.where('dict.isDeleted = :isDeleted', { isDeleted: false });
|
||||||
|
|
||||||
|
const keyword = this.normalizeText(query.keyword);
|
||||||
|
if (keyword) {
|
||||||
|
builder.andWhere(
|
||||||
|
new Brackets((subBuilder) => {
|
||||||
|
subBuilder
|
||||||
|
.where('dict.dictCode LIKE :keyword', {
|
||||||
|
keyword: `%${keyword}%`,
|
||||||
|
})
|
||||||
|
.orWhere('dict.label LIKE :keyword', {
|
||||||
|
keyword: `%${keyword}%`,
|
||||||
|
})
|
||||||
|
.orWhere('dict.value LIKE :keyword', {
|
||||||
|
keyword: `%${keyword}%`,
|
||||||
|
})
|
||||||
|
.orWhere('dict.childrenCode LIKE :keyword', {
|
||||||
|
keyword: `%${keyword}%`,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applyLikeFilter(builder, 'dictCode', query.dictCode);
|
||||||
|
this.applyLikeFilter(builder, 'label', query.label);
|
||||||
|
this.applyLikeFilter(builder, 'value', query.value);
|
||||||
|
this.applyLikeFilter(builder, 'childrenCode', query.childrenCode);
|
||||||
|
|
||||||
|
if (['0', '1'].includes(String(query.status))) {
|
||||||
|
builder.andWhere('dict.status = :status', {
|
||||||
|
status: Number(query.status),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [items, total] = await builder
|
||||||
|
.orderBy('dict.dictCode', 'ASC')
|
||||||
|
.addOrderBy('dict.sort', 'ASC')
|
||||||
|
.addOrderBy('dict.createTime', 'ASC')
|
||||||
|
.skip((pageNo - 1) * pageSize)
|
||||||
|
.take(pageSize)
|
||||||
|
.getManyAndCount();
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: items.map((item) => this.serializeDict(item)),
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDictCodeOptions() {
|
||||||
|
const rows = await this.dictRepository
|
||||||
|
.createQueryBuilder('dict')
|
||||||
|
.select('DISTINCT dict.dictCode', 'dictCode')
|
||||||
|
.where('dict.isDeleted = :isDeleted', { isDeleted: false })
|
||||||
|
.orderBy('dict.dictCode', 'ASC')
|
||||||
|
.getRawMany<{ dictCode: string }>();
|
||||||
|
|
||||||
|
return rows
|
||||||
|
.filter((item) => !!item.dictCode)
|
||||||
|
.map((item) => ({
|
||||||
|
label: item.dictCode,
|
||||||
|
value: item.dictCode,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(body: AdminDictBodyDto) {
|
||||||
|
const input = this.normalizeInput(body);
|
||||||
|
const existing = await this.findByCodeValue(input.dictCode, input.value);
|
||||||
|
if (existing && !existing.isDeleted) {
|
||||||
|
throwVbenError('同一字典编码下的字典值已存在', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = existing
|
||||||
|
? this.dictRepository.merge(existing, {
|
||||||
|
...input,
|
||||||
|
isDeleted: false,
|
||||||
|
})
|
||||||
|
: this.dictRepository.create(input);
|
||||||
|
|
||||||
|
await this.dictRepository.save(entity);
|
||||||
|
await this.refreshDecodeCache();
|
||||||
|
return entity.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(body: AdminDictUpdateDto) {
|
||||||
|
const id = this.normalizeText(body.id);
|
||||||
|
if (!id) throwVbenError('字典项ID不能为空', HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
const dict = await this.dictRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
isDeleted: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!dict) throwVbenError('字典项不存在', HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
const input = this.normalizeInput({
|
||||||
|
childrenCode: body.childrenCode ?? dict.childrenCode,
|
||||||
|
dictCode: body.dictCode ?? dict.dictCode,
|
||||||
|
label: body.label ?? dict.label,
|
||||||
|
sort: body.sort ?? dict.sort,
|
||||||
|
status: body.status ?? dict.status,
|
||||||
|
value: body.value ?? dict.value,
|
||||||
|
});
|
||||||
|
const existing = await this.findByCodeValue(input.dictCode, input.value);
|
||||||
|
if (existing && existing.id !== id) {
|
||||||
|
throwVbenError('同一字典编码下的字典值已存在', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.dictRepository.save(
|
||||||
|
this.dictRepository.merge(dict, {
|
||||||
|
...input,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await this.refreshDecodeCache();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string) {
|
||||||
|
const normalizedId = this.normalizeText(id);
|
||||||
|
if (!normalizedId)
|
||||||
|
throwVbenError('字典项ID不能为空', HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
const dict = await this.dictRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: normalizedId,
|
||||||
|
isDeleted: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!dict) throwVbenError('字典项不存在', HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
await this.dictRepository.update(
|
||||||
|
{ id: normalizedId },
|
||||||
|
{
|
||||||
|
isDeleted: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await this.refreshDecodeCache();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggle(id: string, status: number) {
|
||||||
|
const normalizedStatus = status === 1 ? 1 : 0;
|
||||||
|
await this.update({
|
||||||
|
id,
|
||||||
|
status: normalizedStatus,
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async getDictItemsByKey(dictKey: string): Promise<AdminDictItem[]> {
|
async getDictItemsByKey(dictKey: string): Promise<AdminDictItem[]> {
|
||||||
const list = await this.dictRepository.find({
|
const list = await this.dictRepository.find({
|
||||||
where: {
|
where: {
|
||||||
@ -89,4 +248,79 @@ export class DictService implements OnApplicationBootstrap {
|
|||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private applyLikeFilter(
|
||||||
|
builder: ReturnType<Repository<AdminDict>['createQueryBuilder']>,
|
||||||
|
field: keyof Pick<
|
||||||
|
AdminDict,
|
||||||
|
'childrenCode' | 'dictCode' | 'label' | 'value'
|
||||||
|
>,
|
||||||
|
value?: string,
|
||||||
|
) {
|
||||||
|
const normalizedValue = this.normalizeText(value);
|
||||||
|
if (!normalizedValue) return;
|
||||||
|
|
||||||
|
builder.andWhere(`dict.${field} LIKE :${field}`, {
|
||||||
|
[field]: `%${normalizedValue}%`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async findByCodeValue(dictCode: string, value: string) {
|
||||||
|
return this.dictRepository.findOne({
|
||||||
|
where: {
|
||||||
|
dictCode,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizeInput(body: AdminDictBodyDto): Partial<AdminDict> {
|
||||||
|
const dictCode = this.normalizeText(body.dictCode);
|
||||||
|
const label = this.normalizeText(body.label);
|
||||||
|
const value = this.normalizeText(body.value);
|
||||||
|
|
||||||
|
if (!dictCode) throwVbenError('字典编码不能为空', HttpStatus.BAD_REQUEST);
|
||||||
|
if (!label) throwVbenError('字典标签不能为空', HttpStatus.BAD_REQUEST);
|
||||||
|
if (!value) throwVbenError('字典值不能为空', HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
return {
|
||||||
|
childrenCode: this.normalizeNullableText(body.childrenCode),
|
||||||
|
dictCode,
|
||||||
|
label,
|
||||||
|
sort: Number.isFinite(Number(body.sort)) ? Number(body.sort) : 0,
|
||||||
|
status: Number(body.status) === 0 ? 0 : 1,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizeNullableText(value?: number | string | null) {
|
||||||
|
const text = this.normalizeText(value);
|
||||||
|
return text || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizeText(value?: number | string | null) {
|
||||||
|
return value === undefined || value === null ? '' : String(value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeDict(dict: AdminDict) {
|
||||||
|
return {
|
||||||
|
childrenCode: dict.childrenCode,
|
||||||
|
createTime: dict.createTime,
|
||||||
|
dictCode: dict.dictCode,
|
||||||
|
id: dict.id,
|
||||||
|
label: dict.label,
|
||||||
|
sort: dict.sort,
|
||||||
|
status: dict.status,
|
||||||
|
updateTime: dict.updateTime,
|
||||||
|
value: dict.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toPositiveNumber(
|
||||||
|
value: number | string | undefined,
|
||||||
|
fallback: number,
|
||||||
|
) {
|
||||||
|
const nextValue = Number(value);
|
||||||
|
return Number.isFinite(nextValue) && nextValue > 0 ? nextValue : fallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -717,6 +717,7 @@ function itemExampleByPath(path: string) {
|
|||||||
return adminMenuExample();
|
return adminMenuExample();
|
||||||
if (path.includes('/system/dept')) return adminDeptExample();
|
if (path.includes('/system/dept')) return adminDeptExample();
|
||||||
if (path.includes('/system/role')) return adminRoleExample();
|
if (path.includes('/system/role')) return adminRoleExample();
|
||||||
|
if (path.includes('/dict')) return adminDictExample();
|
||||||
if (path.includes('/component')) return componentExample();
|
if (path.includes('/component')) return componentExample();
|
||||||
if (path.includes('/user')) return adminUserExample();
|
if (path.includes('/user')) return adminUserExample();
|
||||||
if (path.includes('/timezone')) return { timezone: 'Asia/Shanghai' };
|
if (path.includes('/timezone')) return { timezone: 'Asia/Shanghai' };
|
||||||
@ -742,6 +743,20 @@ function adminLoginExample() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adminDictExample() {
|
||||||
|
return {
|
||||||
|
id: '2041700000000300001',
|
||||||
|
dictCode: 'COMPONENT_TYPE',
|
||||||
|
label: '图表',
|
||||||
|
value: '1',
|
||||||
|
childrenCode: 'CHART',
|
||||||
|
sort: 1,
|
||||||
|
status: 1,
|
||||||
|
createTime: '2026-06-03T12:00:00.000Z',
|
||||||
|
updateTime: '2026-06-03T12:00:00.000Z',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function adminUserExample() {
|
function adminUserExample() {
|
||||||
return {
|
return {
|
||||||
id: '1000000000000000001',
|
id: '1000000000000000001',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user