[完结14章]Flink 从0到1实战实时风控系统
网盘地址:https://pan.baidu.com/s/1gHz7xsniPotnoL6oVFyGmA 提取码:hf2e
腾讯微云下载地址:https://share.weiyun.com/zUoCBRio 密码:dc5hxc
对于程序化交易用户而言,无论是证券还是期货市场,每一个交易指令都需要进行充分的业务检查,通过后才能进入交易所的订单队列进行匹配成交。
在程序化交易中,除了验资、验持仓等基础的风控检查外,符合交易所异常交易管理办法规定的监管标准,杜绝和防范异常交易行为也是程序化交易风控的重中之重,比如是否存在自成交、日内过度交易、频繁报撤单、大额报撤单、报单流速控制等情况。
事前风控是指在交易指令发送到交易所前,对交易指令进行风险检测,通过检测的交易指令则提交到交易模块进行报单,未通过检测的交易指令将直接予以拒绝。对于追求低延时的交易策略,事前风控需要在极短的时间内完成。
风控系统是用来给业务风控定义风控规则的,风控规则经过规则引擎的解析得到结果,表示规则是否命中,如果命中需要触发什么样的处置。
例如,业务风控定义了一个风控规则a+b>5,在规则引擎上执行的时候需要知道a,b的值,这两个值的获取一般需要通过大数据获得,然后在规则引擎上执行,从而得到结果是否命中,以及执行命中后的操作。
下面我们给内容区添加Tab标签。用户点击标签,就可以切换不同的面板内容。
<div class="site-content__wrapper">
<main class="site-content">
<el-tabs>
<el-tab-pane label="标签1" name="Tab_1">
<el-card>
<h1>HelloWorld</h1>
</el-card>
</el-tab-pane>
<el-tab-pane label="标签2" name="Tab_2">
<el-card>
<h1>你好世界</h1>
</el-card>
</el-tab-pane>
</el-tabs>
</main>
</div>
鉴权类是需要我们自己实现的,必须要扩展StpInterface接口才可以。在com.example.his.api.config.sa_token包中,创建StpInterfaceImpl.java类。
package com.example.his.api.config.sa_token;
import cn.dev33.satoken.stp.StpInterface;
import com.example.his.api.db.dao.UserDao;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Component
public class StpInterfaceImpl implements StpInterface {
@Resource
private UserDao userDao;
/**
* 返回一个用户所拥有的权限集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> list = new ArrayList<>();
int userId = Integer.parseInt(loginId.toString());
Set<String> set = userDao.searchUserPermissions(userId);
list.addAll(set);
return list;
}
/**
* 返回一个用户所拥有的角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginKey) {
ArrayList<String> list = new ArrayList();
return list;
}
}
由于MIS系统中的权限是固定的,而角色是可以动态增减的。如果我们用@SaCheckRole注解,规定访问Web方法的用户必须具备什么角色。如果将来该角色被删除掉,难道我们还要修改大量的Web方法注解吗?就像下面的Web方法,如果角色1被删除,难不成还要让程序员重新修改代码,重新打包程序,重新发布项目吗?
@RestController("MisAppointmentController")
@RequestMapping("/mis/appointment")
public class AppointmentController {
@Resource
private AppointmentService appointmentService;
@PostMapping("/searchByPage")
@SaCheckRole(value = {"超级管理员", "部门经理", "角色1"}, mode = SaMode.OR)
public R searchByPage(@RequestBody @Valid SearchAppointmentByPageForm form) {
……
}
}
仔细观察不难发现,上面的截图中标签盖住了卡片控件。为了解决这个问题,我们可以让<main>标签引用site-content--tabs样式。但是也不是所有的内容页面都需要有Tab标签,比如说Home页面就不需要Tab标签栏,所以我们要用表达式切换CSS样式。
<div class="site-content__wrapper">
<main class="site-content" :class="{ 'site-content--tabs': true }">
<el-tabs>
<el-tab-pane label="标签1" name="Tab_1">
<el-card>
<h1>HelloWorld</h1>
</el-card>
</el-tab-pane>
<el-tab-pane label="标签2" name="Tab_2">
<el-card>
<h1>你好世界</h1>
</el-card>
</el-tab-pane>
</el-tabs>
</main>
</div>
为了测试路由标签是否可以引用其他内容页面,我们不妨创建三个Vue页面试试。在/src/views/mis目录中创建home.vue、dept.vue和role.vue页面,页面视图层的标签可以随便写,只要能区分开三个页面即可。
//计算网页可见区域的高度
function resetDocumentClientHeight() {
//获取网页可见区域的高度
siteContent.documentClientHeight = document.documentElement.clientHeight;
}
//计算内容区卡片控件高度
function loadSiteContentViewHeight() {
//卡片控件高度 = 网页可见区域高度 - 导航区高度 - 卡片控件上下外填充 - 上下边框
let height = siteContent.documentClientHeight - 50 - 30 - 2;
if (route.meta.isTab) {
//如果引用的Vue页面需要Tab控件,卡片控件高度还要减去40
height -= 40;
}
//保存卡片控件高度
siteContent.height = height
//声明CSS样式
siteContent.siteContentViewHeight = { minHeight: height + 'px' };
}
//浏览器尺寸发生变化的回调函数
window.onresize = () => {
//更新保存的网页可见区域高度
siteContent.documentClientHeight = document.documentElement.clientHeight;
//重新计算内容区的高度
loadSiteContentViewHeight();
};
具体说来风控系统由以下部分组成:
规则:风控规则定义,是由因子和操作符组成的布尔表达式,例如a+b>5
因子:风控规则的最小组成部分,例如规则a+b>5中的a,b为因子
检查点:定义外部系统和风控系统的对接关系,例如用户中心在用户注册的时候需要调用风控系统,检查点定义了外部系统的调用参数,以及需要运行哪些规则。
数据源:提供因子数据,例如规则a>5表示的业务含义为用户连续登录失败次数大于5次,用户连续登录失败次数作为因子a,a的数据来源通过数据源来提供。数据源是对因子来源的抽象。
<div class="site-content__wrapper">
<main class="site-content"
:class="{ 'site-content--tabs': $route.meta.isTab }">
<el-tabs v-if="$route.meta.isTab"
v-model="siteContent.mainTabsActiveName" :closable="true">
<el-tab-pane v-for="item in siteContent.mainTabs"
:label="item.title" :name="item.name">
<el-card :body-style="siteContent.siteContentViewHeight">
<router-view :key="router.currentRoute.value.query.random" />
</el-card>
</el-tab-pane>
</el-tabs>
<el-card v-else :body-style="siteContent.siteContentViewHeight">
<router-view :key="router.currentRoute.value.query.random" />
</el-card>
</main>
</div>
我们先给框架页面<el-tabs>标签添加事件处理。其中@tab-click捕获的是切换Tab标签事件;@tab-remove捕获的是关闭Tab标签事件。
<div class="site-content__wrapper">
<main class="site-content"
:class="{ 'site-content--tabs': $route.meta.isTab }">
<el-tabs v-if="$route.meta.isTab"
v-model="siteContent.mainTabsActiveName" :closable="true"
@tab-click="selectedTabHandle"
@tab-remove="removeTabHandle">
<el-tab-pane v-for="item in siteContent.mainTabs"
:label="item.title" :name="item.name">
<el-card :body-style="siteContent.siteContentViewHeight">
<router-view :key="router.currentRoute.value.query.random" />
</el-card>
</el-tab-pane>
</el-tabs>
<el-card v-else :body-style="siteContent.siteContentViewHeight">
<router-view :key="router.currentRoute.value.query.random" />
</el-card>
</main>
</div>
在框架页面的模型层中,定义removeTabHandle()封装函数。
function removeTabHandle(tabName) {
//让mainTabs数组剔除要关闭的Tab
siteContent.mainTabs = siteContent.mainTabs.filter(item => item.name !== tabName);
//如果还存在剩余的Tab,就切换到最后的Tab上面
if (siteContent.mainTabs.length >= 1) {
//获取mainTabs数组最后一个元素
let tab = siteContent.mainTabs[siteContent.mainTabs.length - 1];
//选中这个Tab控件
siteContent.mainTabsActiveName = tab.name;
//内容区切换引用的页面
router.push({ name: tab.name });
} else {
siteContent.mainTabsActiveName = '';
router.push({ name: 'MisHome' });
}
}
其实也不是所有的Web方法或者HTML页面都需要用户登陆之后才能访问,比如登陆页面和对应的后端Web方法。但是有些Web方法必须用户登陆之后才能访问,我们可以给Web方法添加@SaCheckLogin注解。这个注解会拦截Web方法的请求,让SaToken验证客户端提交的Token令牌。如果令牌合法就允许调用Web方法,反之就拒绝HTTP请求,返回401状态码。
@RestController
@RequestMapping("/mis/user")
public class UserController {
……
@GetMapping("/searchUserSummary")
@SaCheckLogin
public R searchUserSummary() {
……
}
}
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传