You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
18 KiB
456 lines
18 KiB
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
|
import { CreateCoreEnvDto } from './dto/create-core-env.dto';
|
|
import { UpdateCoreEnvDto } from './dto/update-core-env.dto';
|
|
import { LoggerService } from '@service/logger/logger.service';
|
|
import { MysqlService } from '@common/service/mysql/mysql.service';
|
|
import { RedisService } from '@common/service/redis/redis.service';
|
|
import { Snowflake } from '@service/snowflake/snowflake.service';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { pacAuthUser, pacCoreDict, pacCoreEnv, pacCoreService } from '@entities/schema';
|
|
import { and, asc, desc, eq, isNull, like, or, sql } from 'drizzle-orm';
|
|
import { isExistKey, isTrueEnum } from '@utils/boolean.enum';
|
|
import { CoreEnvKeyDTO, CoreEnvTargetListDTO, GetCoreEnvDTO } from '@app/core-env/dto/get-core-env.dto';
|
|
import { likeQuery } from '@utils/likeQuery';
|
|
import { QueryBuilder, alias } from 'drizzle-orm/mysql-core';
|
|
import { customDrizzleRowWithRecursive } from '@utils/customDrizzleRowWithRecursive';
|
|
import { PacInfoType } from '@utils/myType';
|
|
|
|
@Injectable()
|
|
export class CoreEnvService {
|
|
// # 详细的环境变量信息
|
|
private readonly envListDataType = {
|
|
envId: pacCoreEnv.envId,
|
|
pid: pacCoreEnv.pid,
|
|
envName: pacCoreEnv.envName,
|
|
envKey: pacCoreEnv.envKey,
|
|
envVal: pacCoreEnv.envVal,
|
|
envDictVal: pacCoreDict.dictKey,
|
|
envDictName: pacCoreDict.dictName,
|
|
valIsDict: pacCoreEnv.valIsDict,
|
|
haveChildren: pacCoreEnv.haveChildren,
|
|
root: pacCoreEnv.root,
|
|
orderNum: pacCoreEnv.orderNum,
|
|
status: pacCoreEnv.status,
|
|
serviceKey: pacCoreEnv.serviceKey,
|
|
};
|
|
|
|
// # 环境变量列表信息
|
|
private readonly envMoreDataType = {
|
|
envId: pacCoreEnv.envId,
|
|
pid: pacCoreEnv.pid,
|
|
envName: pacCoreEnv.envName,
|
|
envKey: pacCoreEnv.envKey,
|
|
envVal: pacCoreEnv.envVal,
|
|
envDictVal: pacCoreDict.dictKey,
|
|
envDictName: pacCoreDict.dictName,
|
|
valIsDict: pacCoreEnv.valIsDict,
|
|
envDesc: pacCoreEnv.envDesc,
|
|
haveChildren: pacCoreEnv.haveChildren,
|
|
root: pacCoreEnv.root,
|
|
orderNum: pacCoreEnv.orderNum,
|
|
status: pacCoreEnv.status,
|
|
serviceKey: pacCoreEnv.serviceKey,
|
|
serviceName: pacCoreService.serviceName,
|
|
createby: pacCoreEnv.createby,
|
|
createName: pacAuthUser.nickname,
|
|
createtime: pacCoreEnv.createtime,
|
|
updateby: pacCoreEnv.updateby,
|
|
updatetime: pacCoreEnv.updatetime,
|
|
};
|
|
constructor(
|
|
private readonly logger: LoggerService,
|
|
private readonly mysqlService: MysqlService,
|
|
private readonly redisService: RedisService,
|
|
private readonly snowflake: Snowflake,
|
|
private readonly config: ConfigService,
|
|
) {}
|
|
|
|
/** Service
|
|
* NAME: create
|
|
* DESC: 创建环境变量内容
|
|
* DATE: 2024-06-05 11:48:04 -
|
|
* */
|
|
public async create(createCoreEnvDto: CreateCoreEnvDto, pacInfo: PacInfoType) {
|
|
// ! 判断是否是root账户,非root账户无法设置为root
|
|
if (this.config.get<number>('masterId') !== pacInfo.userId && isTrueEnum(createCoreEnvDto.root)) {
|
|
throw new HttpException('没有权限创建原始变量!', HttpStatus.UNAUTHORIZED);
|
|
}
|
|
|
|
// ! 加目标锁
|
|
const lock = await this.redisService.distributedLock('ENV' + createCoreEnvDto.envKey, createCoreEnvDto.envKey);
|
|
|
|
// ? 存在正在进行写入的环境变量
|
|
if (!lock) throw new HttpException('服务繁忙,环境变量标识重复!', HttpStatus.CONFLICT);
|
|
|
|
// @ 核心内容
|
|
try {
|
|
// ! 查重
|
|
const result = await this.getEnvForEnvKey(createCoreEnvDto.envKey);
|
|
|
|
// ? 是否存在重复的环境变量
|
|
if (result.length > 0) throw new HttpException('环境变量标识重复!', HttpStatus.CONFLICT);
|
|
|
|
// ! 添加环境变量数据
|
|
const newPacCoreEnv = await this.addEnvData(createCoreEnvDto, pacInfo);
|
|
|
|
// ! 解锁
|
|
lock();
|
|
|
|
// !更新父节点的子节点数量
|
|
await this.haveChildrenSelfIncreasing(createCoreEnvDto.pid);
|
|
|
|
// ! 返回结果
|
|
return newPacCoreEnv;
|
|
} catch (e) {
|
|
// ! 解锁
|
|
lock();
|
|
|
|
// ! 抛出错误
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/** Service
|
|
* NAME: findAll
|
|
* DESC: 查找环境变量分页/列表
|
|
* DATE: 2024-06-05 12:58:53 -
|
|
* */
|
|
public async findAll(getCoreEnvDTO: GetCoreEnvDTO) {
|
|
// ? 是否查询列表
|
|
if (isExistKey(getCoreEnvDTO, 'isList') && isTrueEnum(getCoreEnvDTO['isList'])) {
|
|
// ! 返回列表数据
|
|
return await this.getEnvList(getCoreEnvDTO);
|
|
} else {
|
|
// ! 返回分页数据
|
|
return await this.getEnvPage(getCoreEnvDTO);
|
|
}
|
|
}
|
|
|
|
/** Service
|
|
* NAME: findOne
|
|
* DESC: 查找环境变量项详细信息
|
|
* DATE: 2024-06-05 14:58:44 -
|
|
* */
|
|
public async findOne(envId: string) {
|
|
return (await this.getMore(envId))[0];
|
|
}
|
|
|
|
/** Service
|
|
* NAME: findTargetList
|
|
* DESC: 查找环境变量下的列表或树
|
|
* DATE: 2024-06-05 15:15:33 -
|
|
* */
|
|
public async findTargetList(coreEnvKeyDTO: CoreEnvKeyDTO, coreEnvTargetListDTO: CoreEnvTargetListDTO) {
|
|
const [result] = await this.getTargetDictList(coreEnvKeyDTO, coreEnvTargetListDTO);
|
|
return result;
|
|
}
|
|
|
|
public async update(envId: string, updateCoreEnvDto: UpdateCoreEnvDto, pacInfo: PacInfoType) {
|
|
// ! 判断是否是root账户,非root账户无法设置为root
|
|
if (this.config.get<number>('masterId') !== pacInfo.userId && updateCoreEnvDto.root !== undefined) {
|
|
throw new HttpException('没有权限修改原始字典!', HttpStatus.UNAUTHORIZED);
|
|
}
|
|
|
|
// ! 查变量是否存在
|
|
const oldEnv = await this.getMore(envId);
|
|
if (oldEnv.length == 0) {
|
|
throw new HttpException('未找到目标变量,无法修改!', HttpStatus.BAD_REQUEST);
|
|
}
|
|
|
|
// ! root数据,非root用户不允许修改
|
|
if (oldEnv[0].root && this.config.get<number>('masterId') !== pacInfo.userId) {
|
|
throw new HttpException('原始变量,权限不足无法修改!', HttpStatus.BAD_REQUEST);
|
|
}
|
|
|
|
// ! 上锁
|
|
const lock = await this.redisService.distributedLock('ENV' + updateCoreEnvDto.envKey, updateCoreEnvDto.envKey);
|
|
|
|
// ? 存在正在运行的环境变量
|
|
if (!lock) throw new HttpException('服务繁忙,环境变量标识重复!', HttpStatus.CONFLICT);
|
|
|
|
// @ 核心业务
|
|
try {
|
|
// ! 通过dictKey获取环境变量信息 重复
|
|
const checkRepeat = await this.getEnvForEnvKey(updateCoreEnvDto.envKey);
|
|
|
|
// ? 判断是否存在重名的环境变量,但是不包括自己
|
|
if (checkRepeat.length > 0 && checkRepeat[0].id != envId) {
|
|
throw new HttpException('环境变量标识重复!', HttpStatus.CONFLICT);
|
|
}
|
|
|
|
// ! 更新数据
|
|
const result = await this.updateEnvData(envId, updateCoreEnvDto, pacInfo);
|
|
|
|
// ! 解锁
|
|
lock();
|
|
|
|
// ! 返回
|
|
return result;
|
|
} catch (e) {
|
|
// ! 解锁
|
|
lock();
|
|
|
|
// ! 抛出错误
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/** Service
|
|
* NAME: remove
|
|
* DESC: 删除环境变量
|
|
* DATE: 2024-06-05 17:17:28 -
|
|
* */
|
|
public async remove(envId: string, pacInfo: PacInfoType) {
|
|
// ! 查看目标环境变量
|
|
const result = await this.getMore(envId);
|
|
|
|
// ? 判断是否存在
|
|
if (result.length > 0) {
|
|
// ! root数据,非root用户不允许删除
|
|
if (result[0].root && this.config.get<number>('masterId') !== pacInfo.userId) {
|
|
throw new HttpException('原始变量,权限不足无法删除!', HttpStatus.BAD_REQUEST);
|
|
}
|
|
|
|
// ! 存在目标环境变量
|
|
|
|
// ? 判断是否存在子项
|
|
if (result[0].haveChildren == 0) {
|
|
// ! 删除目标环境变量
|
|
await this.deleteEnvItem(envId, pacInfo);
|
|
} else {
|
|
throw new HttpException('目标环境变量存在子项!', HttpStatus.BAD_REQUEST);
|
|
}
|
|
|
|
// ! 判断父节点是否存在
|
|
if (result[0].pid != 0) {
|
|
// ! 减少父级子节点数量
|
|
await this.haveChildrenSelfIncreasing(result[0].pid, false);
|
|
}
|
|
return '删除目标环境变量成功';
|
|
} else {
|
|
// ! 不存在目标环境变量,直接返回
|
|
throw new HttpException('未找到目标环境变量!', HttpStatus.BAD_REQUEST);
|
|
}
|
|
}
|
|
|
|
// DB 通过EnvKey获取env信息,主要用于查重
|
|
private getEnvForEnvKey(envKey: string) {
|
|
return this.mysqlService.db
|
|
.select({
|
|
id: pacCoreEnv.envId,
|
|
})
|
|
.from(pacCoreEnv)
|
|
.where(eq(pacCoreEnv.envKey, envKey));
|
|
}
|
|
|
|
// DB 添加Env数据
|
|
private async addEnvData(createCoreEnvDto: CreateCoreEnvDto, userInfo) {
|
|
// ! 生成雪花id,用于环境变量主键
|
|
const id = await this.snowflake.generate();
|
|
|
|
// ! 定义即将写入的环境变量数据
|
|
const newEnvData = {
|
|
envId: id,
|
|
pid: createCoreEnvDto.pid,
|
|
envName: createCoreEnvDto.envName,
|
|
envKey: createCoreEnvDto.envKey,
|
|
envVal: createCoreEnvDto.envVal,
|
|
valIsDict: isTrueEnum(createCoreEnvDto.valIsDict) ? 1 : 0,
|
|
envDesc: createCoreEnvDto.envDesc,
|
|
root: isTrueEnum(createCoreEnvDto.root) ? 1 : 0,
|
|
orderNum: createCoreEnvDto.orderNum,
|
|
serviceKey: createCoreEnvDto.serviceKey,
|
|
createby: userInfo.userId,
|
|
createtime: sql`now()`,
|
|
};
|
|
|
|
// ! 返回写入的数据
|
|
return this.mysqlService.db.insert(pacCoreEnv).values(newEnvData);
|
|
}
|
|
|
|
// DB 更新父级子元素数量
|
|
private haveChildrenSelfIncreasing(envId: string, isAdd = true) {
|
|
return this.mysqlService.db
|
|
.update(pacCoreEnv)
|
|
.set({
|
|
haveChildren: isAdd ? sql`${pacCoreEnv.haveChildren} + 1` : sql`${pacCoreEnv.haveChildren} - 1`,
|
|
})
|
|
.where(eq(pacCoreEnv.envId as any, envId));
|
|
}
|
|
|
|
// DB 查Env项的查询函数QueryBuilder,作用于列表和分页查询
|
|
private getEnv(data, selectData = undefined) {
|
|
// ! 定义基础查询函数
|
|
// 启用动态查询模式 $dynamic
|
|
const query = this.mysqlService.db
|
|
.select(selectData)
|
|
.from(pacCoreEnv)
|
|
.leftJoin(pacCoreDict, and(eq(pacCoreDict.dictId, pacCoreEnv.envVal), eq(pacCoreEnv.valIsDict, 1)))
|
|
.orderBy(
|
|
isTrueEnum(data.isAsc) ? asc(pacCoreEnv.orderNum) : desc(pacCoreEnv.orderNum),
|
|
isTrueEnum(data.isAsc) ? asc(pacCoreEnv.envId) : desc(pacCoreEnv.envId),
|
|
)
|
|
.$dynamic();
|
|
|
|
const wl = [];
|
|
|
|
// ? 未删除
|
|
|
|
wl.push(isNull(pacCoreEnv.deleteby));
|
|
|
|
// ? 模糊查询
|
|
wl.push(
|
|
or(
|
|
like(pacCoreEnv.envKey, likeQuery(data.envInfo)),
|
|
like(pacCoreEnv.envName, likeQuery(data.envInfo)),
|
|
like(pacCoreEnv.envDesc, likeQuery(data.envInfo)),
|
|
like(pacCoreEnv.envVal, likeQuery(data.envInfo)),
|
|
like(pacCoreDict.dictKey, likeQuery(data.envInfo)),
|
|
like(pacCoreDict.dictName, likeQuery(data.envInfo)),
|
|
).if(isExistKey(data, 'envInfo')),
|
|
);
|
|
|
|
// ? 来源于字典
|
|
// if (isTrueEnum(data.valIsDict)) {
|
|
// }
|
|
|
|
// ? 是否查pac的数据
|
|
wl.push(eq(pacCoreEnv.root, isTrueEnum(data['root']) ? 0 : 1).if(isExistKey(data, 'root') && isTrueEnum(data['root'])));
|
|
|
|
// ? 按照层级查
|
|
wl.push(eq(pacCoreEnv.pid, data.hierarchy).if(isExistKey(data, 'hierarchy')));
|
|
|
|
// ? 是否存在目标service
|
|
wl.push(eq(pacCoreEnv.serviceKey, data.serviceKey).if(isExistKey(data, 'serviceKey')));
|
|
|
|
// ? 是否查环境变量状态
|
|
wl.push(eq(pacCoreEnv.status, data.status).if(isExistKey(data, 'status')));
|
|
|
|
query.where(and(...wl));
|
|
return query;
|
|
}
|
|
|
|
// DB 查Env列表
|
|
private getEnvList(getCoreEnvDTO: GetCoreEnvDTO) {
|
|
return this.getEnv(getCoreEnvDTO, this.envListDataType);
|
|
}
|
|
|
|
// DB 查Env分页
|
|
private async getEnvPage(getCoreEnvDTO: GetCoreEnvDTO) {
|
|
const offset = (getCoreEnvDTO.pageNumber - 1) * getCoreEnvDTO.pageSize;
|
|
|
|
// 使用基础查询构建查询总记录数
|
|
const totalCountQuery = this.getEnv(getCoreEnvDTO, {
|
|
totalCount: sql`COUNT(*)`,
|
|
});
|
|
|
|
// 使用基础查询构建分页查询
|
|
// 重命名表
|
|
const userTable1 = alias(pacAuthUser, 'userTable1');
|
|
const paginatedQuery = this.getEnv(getCoreEnvDTO, { ...this.envMoreDataType, updateName: userTable1.nickname })
|
|
.leftJoin(pacAuthUser, eq(pacCoreEnv.createby, pacAuthUser.userId))
|
|
.leftJoin(userTable1, eq(pacCoreEnv.updateby, userTable1.userId))
|
|
.leftJoin(pacCoreService, eq(pacCoreEnv.serviceKey, pacCoreService.serviceKey))
|
|
.limit(getCoreEnvDTO.pageSize)
|
|
.offset(offset);
|
|
|
|
return {
|
|
total: (await totalCountQuery)[0].totalCount,
|
|
rowData: await paginatedQuery,
|
|
searchData: getCoreEnvDTO,
|
|
};
|
|
}
|
|
|
|
// DB 通过ID查找Env详细信息
|
|
private getMore(envId: string) {
|
|
// 重命名表
|
|
const userTable1 = alias(pacAuthUser, 'userTable1');
|
|
return this.mysqlService.db
|
|
.select({ ...this.envMoreDataType, updateName: userTable1.nickname })
|
|
.from(pacCoreEnv)
|
|
.leftJoin(pacAuthUser, eq(pacCoreEnv.createby, pacAuthUser.userId))
|
|
.leftJoin(userTable1, eq(pacCoreEnv.updateby, userTable1.userId))
|
|
.leftJoin(pacCoreService, eq(pacCoreEnv.serviceKey, pacCoreService.serviceKey))
|
|
.leftJoin(pacCoreDict, and(eq(pacCoreDict.dictId, pacCoreEnv.envVal), eq(pacCoreEnv.valIsDict, 1)))
|
|
.where(and(eq(pacCoreEnv.envId, envId), isNull(pacCoreEnv.deleteby)));
|
|
}
|
|
|
|
// DB 通过Key查找目标Env的信息?树结构
|
|
private getTargetDictList(coreEnvKeyDTO: CoreEnvKeyDTO, coreEnvTargetListDTO: CoreEnvTargetListDTO) {
|
|
if (isExistKey(coreEnvTargetListDTO, 'isTree') && isTrueEnum(coreEnvTargetListDTO['isTree'])) {
|
|
// ! 基础层级
|
|
const baseQueryBuilder = new QueryBuilder();
|
|
const baseQuery = baseQueryBuilder
|
|
.select({
|
|
...this.envListDataType,
|
|
level: sql`0`.as('level'),
|
|
})
|
|
.from(pacCoreEnv)
|
|
.leftJoin(pacCoreDict, and(eq(pacCoreDict.dictId, pacCoreEnv.envVal), eq(pacCoreEnv.valIsDict, 1)))
|
|
.where(and(isNull(pacCoreEnv.deleteby), eq(pacCoreEnv.envKey, coreEnvKeyDTO.envKey)));
|
|
|
|
// ! 递归层级
|
|
const recursiveQueryBuilder = new QueryBuilder();
|
|
const recursiveQuery = recursiveQueryBuilder
|
|
.select({
|
|
...this.envListDataType,
|
|
level: sql`envHierarchy.level + 1`.as('level'),
|
|
})
|
|
.from(pacCoreEnv)
|
|
.leftJoin(pacCoreDict, and(eq(pacCoreDict.dictId, pacCoreEnv.envVal), eq(pacCoreEnv.valIsDict, 1)))
|
|
.where(isNull(pacCoreEnv.deleteby))
|
|
.innerJoin(sql`envHierarchy`, sql`envHierarchy.envId = ${pacCoreEnv.pid}`);
|
|
|
|
const rowName = customDrizzleRowWithRecursive(this.envListDataType);
|
|
|
|
// ! 定义SQL
|
|
const SQL = sql`WITH RECURSIVE envHierarchy (${rowName}) AS(${baseQuery} UNION ALL ${recursiveQuery}) SELECT * FROM envHierarchy`;
|
|
|
|
// ! 执行原始SQL查询
|
|
return this.mysqlService.db.execute(SQL);
|
|
} else {
|
|
// ! 查询自己
|
|
// 重命名表
|
|
const userTable1 = alias(pacAuthUser, 'userTable1');
|
|
return this.mysqlService.db
|
|
.select({ ...this.envMoreDataType, updateName: userTable1.nickname })
|
|
.from(pacCoreEnv)
|
|
.leftJoin(pacAuthUser, eq(pacCoreEnv.createby, pacAuthUser.userId))
|
|
.leftJoin(userTable1, eq(pacCoreEnv.updateby, userTable1.userId))
|
|
.leftJoin(pacCoreService, eq(pacCoreEnv.serviceKey, pacCoreService.serviceKey))
|
|
.leftJoin(pacCoreDict, and(eq(pacCoreDict.dictId, pacCoreEnv.envVal), eq(pacCoreEnv.valIsDict, 1)))
|
|
.where(and(eq(pacCoreEnv.envKey, coreEnvKeyDTO.envKey), isNull(pacCoreEnv.deleteby)));
|
|
}
|
|
}
|
|
|
|
// DB 通过ID删除Env
|
|
private deleteEnvItem(envId: string, pacInfo: PacInfoType) {
|
|
return this.mysqlService.db
|
|
.update(pacCoreEnv)
|
|
.set({
|
|
deletetime: sql`now()`,
|
|
deleteby: pacInfo.userId,
|
|
})
|
|
.where(eq(pacCoreEnv.envId, envId));
|
|
}
|
|
|
|
// DB 更新Env
|
|
private updateEnvData(envId: string, updateCoreEnvDto: UpdateCoreEnvDto, pacInfo: PacInfoType) {
|
|
return this.mysqlService.db
|
|
.update(pacCoreEnv)
|
|
.set({
|
|
envName: updateCoreEnvDto.envName,
|
|
envKey: updateCoreEnvDto.envKey,
|
|
envVal: updateCoreEnvDto.envVal,
|
|
valIsDict: isTrueEnum(updateCoreEnvDto.valIsDict) ? 1 : 0,
|
|
envDesc: updateCoreEnvDto.envDesc,
|
|
root: isTrueEnum(updateCoreEnvDto.root) ? 1 : 0,
|
|
orderNum: updateCoreEnvDto.orderNum,
|
|
status: updateCoreEnvDto.status,
|
|
serviceKey: updateCoreEnvDto.serviceKey,
|
|
updateby: pacInfo.userId,
|
|
updatetime: sql`now()`,
|
|
})
|
|
.where(eq(pacCoreEnv.envId, envId));
|
|
}
|
|
}
|
|
|