Skip to content

Commit

Permalink
feat(audit): 操作日志增加自定义操作行为 (#1155)
Browse files Browse the repository at this point in the history
### 改动

1. 操作日志增加自定义操作类型

proto传参类型:
```proto
message I18nObject {
  message I18n {
    string default = 1;
    optional string en = 2;
    optional string zh_cn = 3;
  }
  I18n i18n = 1;
}


message CustomEvent {
  string type = 1;
  common.I18nObject name = 2;
  common.I18nObject content = 3;
}
```

展示时,会根据页面国际化直接展示国际化内容


![image](https://github.com/PKUHPC/SCOW/assets/130351655/bf352acb-7c14-4dd8-8296-abd87e7c6204)


![image](https://github.com/PKUHPC/SCOW/assets/130351655/711c51ff-2148-45a3-980b-30fc3b4facbf)


2. 新增一个接口,获取自定义类型里的type类型


![image](https://github.com/PKUHPC/SCOW/assets/130351655/4271048d-ceef-4e20-badc-bae1b8ec199c)


ps: 新增一个type时,需要校验数据库内是否有已有的type,如果存在的话, 需要name一致。
  • Loading branch information
ZihanChen821 authored Mar 13, 2024
1 parent 25f9caf commit 24db413
Show file tree
Hide file tree
Showing 27 changed files with 584 additions and 32 deletions.
9 changes: 9 additions & 0 deletions .changeset/real-spies-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@scow/lib-operation-log": patch
"@scow/audit-server": patch
"@scow/portal-web": patch
"@scow/mis-web": patch
"@scow/ai": patch
---

操作日志增加自定义操作类型
5 changes: 5 additions & 0 deletions .changeset/tall-suns-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scow/grpc-api": patch
---

增加 GetCustomEventTypes 接口获取自定义操作类型,增加 CustomEvent 保存自定义操作类型
1 change: 1 addition & 0 deletions apps/ai/src/models/operationLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,6 @@ export const OperationType: OperationTypeEnum = {
exportOperationLog: "exportOperationLog",
setAccountBlockThreshold: "setAccountBlockThreshold",
setAccountDefaultBlockThreshold: "setAccountDefaultBlockThreshold",
customEvent: "customEvent",
};

11 changes: 10 additions & 1 deletion apps/audit-server/src/entities/OperationLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/

import { Entity, Enum, PrimaryKey, Property } from "@mikro-orm/core";
import { Entity, Enum, Index, PrimaryKey, Property } from "@mikro-orm/core";
import { OperationEvent } from "@scow/lib-operation-log";
import { CURRENT_TIMESTAMP, DATETIME_TYPE } from "src/utils/orm"; ;

Expand Down Expand Up @@ -42,13 +42,19 @@ export class OperationLog {
@Property({ type: "json", nullable: true })
metaData?: OperationEvent & { targetAccountName?: string };

// 用户自定义操作类型
@Index({ name: "custom_event" })
@Property({ nullable: true })
customEventType?: string;

constructor(init: {
operationLogId?: number;
operatorUserId: string;
operatorIp: string;
operationTime?: Date;
operationResult: OperationResult;
metaData: OperationEvent & { targetAccountName?: string };
customEventType?: string;
}) {
if (init.operationLogId) {
this.id = init.operationLogId;
Expand All @@ -60,6 +66,9 @@ export class OperationLog {
}
this.operationResult = init.operationResult;
this.metaData = init.metaData;
if (init.customEventType) {
this.customEventType = init.customEventType;
}
}

}
Expand Down
32 changes: 27 additions & 5 deletions apps/audit-server/src/migrations/.snapshot-scow_audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@
"autoincrement": false,
"primary": false,
"nullable": false,
"length": 0,
"default": "current_timestamp(0)",
"length": 6,
"default": "current_timestamp(6)",
"mappedType": "datetime"
},
"operation_result": {
"name": "operation_result",
"type": "enum('UNKNOWN', 'SUCCESS', 'FAIL')",
"type": "enum('UNKNOWN','SUCCESS','FAIL')",
"unsigned": false,
"autoincrement": false,
"primary": false,
Expand All @@ -64,22 +64,44 @@
"primary": false,
"nullable": true,
"mappedType": "json"
},
"custom_event_type": {
"name": "custom_event_type",
"type": "varchar(255)",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "string"
}
},
"name": "operation_log",
"indexes": [
{
"columnNames": [
"custom_event_type"
],
"composite": false,
"keyName": "custom_event",
"constraint": false,
"primary": false,
"unique": false
},
{
"keyName": "PRIMARY",
"columnNames": [
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {},
"nativeEnums": {}
}
]
],
"nativeEnums": {}
}
18 changes: 18 additions & 0 deletions apps/audit-server/src/migrations/Migration20240313015230.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Migration } from '@mikro-orm/migrations';

export class Migration20240313015230 extends Migration {

async up(): Promise<void> {
this.addSql('alter table `operation_log` add `custom_event_type` varchar(255) null;');
this.addSql('alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(6), modify `operation_result` enum(\'UNKNOWN\', \'SUCCESS\', \'FAIL\') not null comment \'UNKNOWN, SUCCESS, FAIL\';');
this.addSql('alter table `operation_log` add index `custom_event`(`custom_event_type`);');
}

async down(): Promise<void> {
this.addSql('alter table `operation_log` drop index `custom_event`;');
this.addSql('alter table `operation_log` drop column `custom_event_type`;');

this.addSql('alter table `operation_log` modify `operation_time` DATETIME(6) not null default current_timestamp(0), modify `operation_result` enum(\'UNKNOWN\', \'SUCCESS\', \'FAIL\') not null comment \'UNKNOWN, SUCCESS, FAIL\';');
}

}
30 changes: 28 additions & 2 deletions apps/audit-server/src/services/operationLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
import { createWriterExtensions, ServiceError } from "@ddadaal/tsgrpc-common";
import { ensureNotUndefined, plugin } from "@ddadaal/tsgrpc-server";
import { status } from "@grpc/grpc-js";
import { QueryOrder } from "@mikro-orm/core";
import { QueryOrder, raw } from "@mikro-orm/core";
import {
OperationLogServiceServer,
OperationLogServiceService,
operationResultToJSON,
} from "@scow/protos/build/audit/operation_log";
import { I18nObject } from "@scow/protos/build/common/i18n";
import { OperationLog, OperationResult } from "src/entities/OperationLog";
import { filterOperationLogs, getTargetAccountName, toGrpcOperationLog } from "src/utils/operationLogs";
import { checkCustomEventType, filterOperationLogs,
getTargetAccountName, toGrpcOperationLog } from "src/utils/operationLogs";
import { DEFAULT_PAGE_SIZE, paginationProps } from "src/utils/orm";


Expand All @@ -43,11 +45,14 @@ export const operationLogServiceServer = plugin((server) => {

const dbOperationResult: OperationResult = OperationResult[operationResultToJSON(operationResult)];

await checkCustomEventType(em, operationEvent);

const operationLog = new OperationLog({
operatorUserId,
operatorIp,
operationResult: dbOperationResult,
metaData: targetAccountName ? { ...operationEvent, targetAccountName } : operationEvent,
customEventType: operationEvent.$case === "customEvent" ? operationEvent.customEvent.type : undefined,
});
await em.persistAndFlush(operationLog);
return [];
Expand Down Expand Up @@ -124,6 +129,27 @@ export const operationLogServiceServer = plugin((server) => {
}
},

getCustomEventTypes: async ({ em }) => {

const qb = em.createQueryBuilder(OperationLog, "o");
qb
.select([
raw("DISTINCT o.custom_event_type as customType"),
raw("JSON_UNQUOTE(JSON_EXTRACT(meta_data, '$.customEvent.name')) AS name"),
])
.where("o.custom_event_type IS NOT NULL");

const results: { customType: string, name: string }[] = await qb.execute();

const customEventTypes = results.map((r) => ({
type: r.customType,
name: JSON.parse(r.name) as I18nObject,
}));
return [{
customEventTypes,
}];
},

});

});
61 changes: 60 additions & 1 deletion apps/audit-server/src/utils/operationLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
* See the Mulan PSL v2 for more details.
*/

import { ServiceError } from "@ddadaal/tsgrpc-common";
import { Status } from "@grpc/grpc-js/build/src/constants";
import { FilterQuery } from "@mikro-orm/core";
import { MySqlDriver, SqlEntityManager } from "@mikro-orm/mysql";
import {
CreateOperationLogRequest,
OperationLog,
Expand All @@ -30,6 +33,7 @@ export async function filterOperationLogs(
operationType,
operationTargetAccountName,
operationDetail,
customEventType,
}: OperationLogFilter,
) {

Expand All @@ -41,6 +45,7 @@ export async function filterOperationLogs(
...((operationType) ? [{ metaData: { $case: operationType } as OperationEvent }] : []),
...((operationTargetAccountName) ? [{ metaData: { targetAccountName: operationTargetAccountName } }] : []),
...(operationDetail ? [ { metaData: { $like: `%${operationDetail}%` } }] : []),
...(customEventType ? [{ customEventType }] : []),
],
...(operationResult ? { operation_result: operationResultToJSON(operationResult) } : {}),
};
Expand All @@ -55,7 +60,10 @@ export function toGrpcOperationLog(x: OperationLogEntity): OperationLog {
operatorIp: x.operatorIp,
operationTime: x.operationTime?.toISOString(),
operationResult: operationResultFromJSON(x.operationResult),
operationEvent: (x.metaData),
operationEvent: x.metaData?.$case === "customEvent" ? { ...x.metaData, "customEvent": {
... x.metaData.customEvent,
type: x.customEventType || "",
} } : (x.metaData),
};
return grpcOperationLog;
}
Expand Down Expand Up @@ -87,3 +95,54 @@ export const getTargetAccountName = (operationEvent: OperationEvent): string | u
: undefined;
}
};


/**
*
* @param em
* @param operationEvent
* @returns
* @description
* 如果是自定义操作类型,检查是否存在相同类型的自定义操作,且其国际化名称对象是否一致
*/
export const checkCustomEventType = async (em: SqlEntityManager<MySqlDriver>, operationEvent: OperationEvent) => {

if (operationEvent?.$case !== "customEvent") {
return;
}

const customEvent = operationEvent.customEvent;
const customEventType = customEvent.type;
const nameI18n = customEvent.name?.i18n;

const existTypeLog = await em.findOne(OperationLogEntity, {
metaData: { $case: "customEvent" },
customEventType,
});

if (
!existTypeLog
|| !existTypeLog.metaData
|| !existTypeLog.metaData.$case
|| existTypeLog.metaData.$case !== "customEvent"
|| !existTypeLog.metaData.customEvent
|| !existTypeLog.metaData.customEvent.name
|| !existTypeLog.metaData.customEvent.name.i18n
) {
return;
}

const existNameI18n = existTypeLog.metaData.customEvent.name.i18n;

const isNameMatch = existNameI18n.default === nameI18n?.default &&
existNameI18n.en === nameI18n?.en &&
existNameI18n.zhCn === nameI18n?.zhCn;

if (!isNameMatch) {
throw new ServiceError({
code: Status.INVALID_ARGUMENT,
message: "Custom event type name not match with exist type name.",
});
}

};
Loading

0 comments on commit 24db413

Please sign in to comment.