9-通用放行配置
以前使用放行的场景较少,统一在放行菜单中进行放行配置,然后在相应模块中添加功能,随着模块增加需要不断增加原表字段。为了提高可移植性和扩展性,做一个通用的放行配置。
1.设计思路
目的是为了使用一张统一的表,但是不再需要按照模块来新增字段,采用key-value的设计模式,放行 配置存储为json格式即可,按照模块设计唯一key,存储不同的放行配置。
考虑实际业务场景,放行配置和放行记录进行剥离,放行记录单独存储,同样采用key-value的方式,方便后期对记录的维护和追溯。
1.1.放行配置
ID | 模块 | 类型 | 放行配置json数据 | 放行人 | 放行原因 | 允许放行次数 | 放行有效时间 | 已放行此处 |
---|
还有一个condition字段,存储放行条件,这才是唯一key,模块+类型是进行业务区分,🎫 清洗板+清洗板放行,或者是清洗板+清洗烘烤,按照模块+类型区分,conditon则根据实际开发场景进行设计,需要唯一。
1.2.放行记录
ID | 关联ID | 模块 | 类型 | 放行配置json | 放行人 | 放行原因 | 执行人 | 执行时间 | 执行json(放行记录json) |
---|
放行配置提前配,触发放行条件, 且成功的时候生成放行记录,关联ID为放行配置的ID,用来关联数据,同时需要记录本次放行的一个参数情况(json)。
2.实体设计
2.1.新增字段
原有实体类RepairReleaseRecord新增字段:
@ApiModelProperty(value = "模块名")
private String module;
@Lob
@Basic(fetch = FetchType.LAZY)
@MetaData("放行参数")
private String jsonData;
@ApiModelProperty(value = "放行条件")
private String condition;
@ApiModelProperty(value = "允许放行的次数")
private String releaseLimit;
@ApiModelProperty(value = "已放行的次数")
private String releaseNum;
@ApiModelProperty("放行的有效时间")
private String limitTime;
类型沿用原来的limitType字段即可。
2.2.Json格式
需要确定好Json格式,要求后续开发者按照这个格式来传List进行接收,确保每次存入数据库的json参数格式一致。
要求: 前端需要动态构建放行配置和放行记录表单,所以JSON中需要包含字段名,值,以及字段的中文描述。
public class ReleaseJson implements Serializable {
private static final long serialVersionUID = 1L;
String desc;
String key;
String value;
}
- desc:字段描述
- key:字段名
- value:值
💁♂️ 其中这三个字段需要根据放行记录和放行配置进行区分,连两者都使用这个JSON格式存储,但是前缀区分,con-xxx为放行配置,re-xxx为放行记录
2.3.放行记录
public class ReleaseRecord extends BaseNativeEntity {
@MetaData("工厂")
private String site;
@MetaData("放行原因")
private String releaseReason;
@MetaData("放行人")
private String releasePerson;
@MetaData("模块类型")
private String module;
@MetaData("管控类型")
private String limitType;
@MetaData("放行条件")
private String condition;
@Lob
@Basic(fetch = FetchType.LAZY)
@MetaData("放行参数")
@Column(name = "JSON_DATA", columnDefinition="CLOB")
private String jsonData;
@MetaData("执行人")
private String operator;
@MetaData("执行时间")
private String operateTime;
@MetaData("关联ID")
private String corId;
@Lob
@MetaData("执行的JSON数据")
@Basic(fetch = FetchType.LAZY)
private String reJson;
}
记录JSON的数据字段采用CLOB,否则过长会出现数据库操作问题
3.前端动态设计
⚠️ 前端表单要求根据模块+类型动态渲染表单字段,上述说到JSON格式中按照con和re进行放行记录和放行配置数据的区分,JSON中也存储中字段描述解释和字段名。
个人对前端不精通,直接上代码:
3.1.重置搜索条件后的渲染
原 始表单不展示数据,根据模块+类型拉取配置进行表单字段填充,重置后需要杀死grid对象,防止出现Null的情况影响后续渲染。
$("#resetBtn").on("click",function () {
// 数据清空
lastSearchLimitType = null;
lastSearchModule = null;
startTime = "";
endTime = "";
lastReleasePerson = null;
// 重置也销毁grid
if(!!grid) {
//重新渲染将原grid销毁
Ext.getCmp("parametricGrid").destroy();
$("#gridDiv").html('<div><b>请选择管控类型+模块后再进行检索</b>' +
'<button class="layui-btn layui-btn-danger" onclick="ondelete()"><i class="layui-icon layui-icon-delete"></i>删除</button></div>' +
'<div class="table-container ext-table" id="parametricGrid"></div>');
grid = null;
}
})
3.2.onSearch方法
onSearch用来进行表单查询,完成两件事:一、根据配置拉取数据动态填充表单字段名;二、拉取放行记录数据填充
- 根据条件判断是否进行重新渲染:
if(lastSearchLimitType != limitType || lastSearchModule != module){
......
}else{
......
}
- 销毁grid
if(!!grid) {
//重新渲染将原grid销毁
Ext.getCmp("parametricGrid").destroy();
$("#gridDiv").html('<div><b>请选择管控类型+模块后再进行检索</b>' +
'<button class="layui-btn layui-btn-danger" onclick="ondelete()"><i class="layui-icon layui-icon-delete"></i>删除</button></div>' +
'<div class="table-container ext-table" id="parametricGrid"></div>');
grid = null;
}
- 按照放行配置填充表单字段名
$.ajax({
type: 'post',
url: REST + '/tb/defectiveRepair/queryConfTable',
data: {
site:site,
startTime:startTime,
endTime:endTime,
limitType:limitType,
module:module
},
async: false,
dataType: 'json',// 响应数据类型
success: function(res) {
if(res.type == "success"){
if(res.data.length === 0){
layer.msg("未找到查询条件对应的放行配置", {icon: 7});
}
res.data.forEach(item => {
tableCol.push({ text: item.desc, dataIndex: item.key,width:150,align:'center'},)
})
}else{
resetForm();
layer.msg(res.message, {icon: 7});
return false;
}
},
error:function(res){
resetForm();
layer.msg(res.message, {icon: 7});
return false;
},
});
- 重置查询条件/渲染表格
if(tableCol.length === 11){
lastSearchLimitType = limitType;
lastSearchModule = module;
lastReleasePerson = releasePerson;
return;
}
//渲染表格
grid = new ExtGrid({
elemId: 'parametricGrid'
,height: "100%"
,width:"100%"
,url:REST + '/tb/defectiveRepair/queryReleaseRecordConf'
,method:'POST'
,limit:30
,parseData:{
rootProperty:"data.content",
totalProperty:"total"
}
,extraParams:params
,showCheckBox:true
,columns: tableCol
});
lastSearchLimitType = limitType;
lastSearchModule = module;
lastReleasePerson = releasePerson;
4.Utils
本次任务需要两个utils 🌮 方法:
- 获取下拉框(原本设计是module+字段名),需要英文转中文
- 实体类转map
/**
* 查询放行配置的下拉框,英转中
* @param site
* @return
*/
public Map<String, Object> getTypeName(String site){
Map<String,Object> resultMap = new HashMap<>();
String clientCode = environment.getProperty("client.code");
HashMap<String,Object> resultMap1 = baseFunctionService.getFeignMapListData(baseConigFeign.getSiteSelectOptionList(new String[]{"release_type"},clientCode,site));
HashMap<String,Object> resultMap2 = baseFunctionService.getFeignMapListData(baseConigFeign.getSiteSelectOptionList(new String[]{"release_module"},clientCode,site));
Map<String,Object> map1 = (Map<String, Object>) resultMap1.get("data");
Map<String,Object> map2 = (Map<String, Object>) resultMap2.get("data");
List<Map<String,String>> configList1 = (List<Map<String, String>>) map1.get("release_type");
List<Map<String,String>> configList2 = (List<Map<String, String>>) map2.get("release_module");
Map<String,String> limitTypeMap = new HashMap<>();
Map<String,String> moduleypeMap = new HashMap<>();
configList1.forEach(c->{
String temp[] = c.get("name").split("\\*");
limitTypeMap.put(temp[1],temp[0]);
});
configList2.forEach(c->{
String temp[] = c.get("name").split("\\*");
moduleypeMap.put(temp[1],temp[0]);
});
resultMap.put("moduleypeMap",moduleypeMap);
resultMap.put("limitTypeMap",limitTypeMap);
return resultMap;
}
/**
* 实体类转换到Map
* @param entity
* @return
* @throws IllegalAccessException
*/
public Map<String, Object> convertToMap(Object entity) throws IllegalAccessException {
Map<String, Object> result = new HashMap<>();
Class<?> clazz = entity.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
result.put(field.getName(), field.get(entity));
}
return result;
}