[RFC] tegg 增加 aop 支持
<!-- 相关 RFC 示例：https://git.io/fphwy -->

## 背景

在业务开发中，经常需要处理一些通用的逻辑，比如说:
- 在一个方法执行时，打印是否处理成功、耗时的日志
- 在一个方法执行时，保证其是事物的
- 在某些类型的方法执行时，增加通用的处理

## 思路

### Advice
通过 `Advice` 注解来申明一个实现，可以在 `Pointcut` 中使用，或者对其添加 `Crosscut` 来实现通用的 `Advice`。

#### 注解定义
```ts
interface AdviceContext<T=object> {
  that: T;
  method: PropertyKey;
  args: ...any[];
}

interface IAdvice<T=object> {
  // 在函数调用前执行
  beforeCall?(ctx: AdviceContext<T>): Promise<void>;
  // 在函数执行成功之后执行
  afterReturn?(ctx: AdviceContext<T>, result: any): Promise<void>;
  // 在函数抛错之后执行
  afterThrow?(ctx: AdviceContext<T>, error: Error): Promise<void>;
  // 无论函数成功还是失败都会执行
  afterFinally?(ctx: AdviceContext<T>): Promise<void>;
  // 拦截函数执行
  around?(ctx: AdviceContext<T>, block: () => Promise<any>): Promise<any>;
}

function Advice() {
  return function(advice: IAdvice) {
  }
}
```

#### 使用方式
```ts
@Advice()
class LogAdvice implement IAdvice {
  beforeCall(ctx: AdviceContext) {
    console.log('before call');
  }
}
```

#### 执行顺序
1. `beforeCall`
1. `around(block start)`
1. `function call`
1. `around(block end)`
1. `afterReturn`
1. `afterThrow`
1. `afterFinally`

### Pointcut
在方法上加入 `Pointcut` 注解来添加 `Advice`。可以通过添加多个 `Pointcut` 来实现添加多个 `Advice`。

#### 注解定义
interface PointcutOptions {
 // 默认值 1000
 order?: number;
} 

```ts
function Pointcut(advice: IAdvice, options?: PointcutOptions) {
}
```

#### 使用方式
```ts
@ContextProto()
class Foo {
  @Prointcut(LogAdvice)
  async hello() {
    console.log('hello');
  }
}
```

### Crosscut
在业务开发中，经常会遇到通用的拦截需求，比如说日志或者异常处理。这种情况下使用 `Pointcut` 的方式就不够了，手动添加很容易遗漏。

#### 注解定义
```ts
interface CrosscutOptions {
 // 默认值 100
 order?: number;
} 


enum CrosscutType {
  // 通过基类来拦截
  CLASS = 'CLASS',
  // 通过类名 + 方法名正则的方式来拦截
  NAME = 'NAME';
  // 通过自定义函数的方式来拦截
  // 函数必须是静态定义的，动态定义de
  CUSTOM = 'CUSTOM';
}

interface CrosscutParam {
  type: CrosscutType;
}

interface ClassCrosscutParam extends CrosscutParam {
  type: CrosscutType.CLASS;
  clazz: new (...args: any[]) => object;
}

interface NameCrosscutParam extends CrosscutParam {
  type: CrosscutType.NAME;
  className: RegExp;
  methodName: RegExp;
}

interface CustomCrossCutParam extends CrosscutParam {
  type: CrosscutType.CUSTOM;
  callback: (clazz: new (...args: any[]) => object, method: PropertyKey): boolean;
}

function (param: CrosscutParam, options?: CrosscutOptions) {
  return function (aspect: IAspect) {}
}
```

#### 使用方式
```ts
@CrossCut({
  type: CrosscutType.NAME;
  className: /.*Proxy/,
  method: /.*/,
})
@Aspect()
class ProxyCallLog {
  beforeCall(ctx: AdviceContext) {
    console.log('before call');
  }
}
```

