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.
 
 
pac-auth/src/application/core-env/core-env.service.ts

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));
}
}