运营平台框架整理(内部用)
1.页面入口的过滤字段定义:
<input name="filter_name_like" type="text" style="margin-left: 0px;width:100px;" class="box_ipt " value="$!filter_name_like"/>
1)字段定义:统一用filter_开头,这样后面可以自动解析成需要过滤的字段,之后是filed名称,可以匹配到数据库里的字段,多单词的定义比如 userName 数据库里是user_name,这里可以直接写userName,后面会自动解析
2)筛选条件:相等(eq),大于(gt),小于(lt),大于等于(ge),小于等于(le),模糊匹配(like),默认为相等 ,例如 filter_name
3)value定义为name相同的名字,后续可以直接映射过来,方便查询和分页操作之后的回带
4)分页部分的隐藏域中同样需要定义,方便分页的操作时可以把查询条件带上
<input type="hidden" name="filter_name_like" value="$!filter_name_like"/>
2. 调度层Action
1)实现modelDriven,方便后续增删改查的时候,相关value到bean的自动映射
public class AlarmCodeAction extends BaseAction implements ModelDriven<AlarmCode> {
2)在action的实现类里需要定义过滤的字段,以及SearchRet等常用的类型,get set方法
/******************************** 页面映射信息 start***********************/ public String filter_keyWord_like; public String filter_pageType_eq; public String filter_pageValue_like; public String filter_activeTime_ge; public String filter_endTime_le; public String filter_platform_eq; public String orderField="active_time"; //排序字段 public String orderBy="desc"; //asc or desc public String sendMsg=""; public SearchRet sr; /********************************* 页面映射信息 end ************************/
注:orderField和orderBy两个filed可以实现table中单击任意thead,实现按照该字段升序,降序的切换,后面会说到
3)过滤方法:
public String list(){ try{ sr = service.list(request, AlarmCode.class, orderField, orderBy, curpage, pagesize, getLink()); }catch(Throwable t){ log.error("AlarmCodeAction|list|error:",t); } return "success"; }
service会根据之前定义的filter开头的字段,自动解析request,省去了自己组装参数的事情,service是根据泛型自动解析的bean,也省去了service的开发,同时,新增修改删除也有泛型的实现方式,后面是代码
3.服务层
public class AlarmCodeService extends BaseService<AlarmCode>{
自己相关业务的service实现baseService之后,增删改查的持久层操作自己就不用写了,BaseService代码片段如下
public class BaseService<T> { public SearchRet list(HttpServletRequest request,Class<T> clazz,String orderName,String orderBy,int curpage,int pagesize,String link){ Pair<ArrayList<T>,Integer> pair = ConsoleDBUtil.queryPageList(clazz , true, DaoUtil.getConsoleUtf8(),PropertyFilterUtil.clazzNameToDBName(clazz.getSimpleName()) , null, PropertyFilterUtil.getParams(request), PropertyFilterUtil.getOrdersMap(orderName, orderBy), curpage, pagesize); SearchUtil su = new SearchUtil(pair.first,new ConvertService(),new Paging(pair.second, curpage, pagesize, link)); SearchRet sr = su.searchNoPage(); return sr; } public int insert(DBClientWrapper db,T o){ int ret = ConsoleDBUtil.insert(db, o,null); return ret; } public int insert(T o){ return insert(DaoUtil.getConsoleDB(),o); } public int update(DBClientWrapper db,T o,String [] updateFields,String [] whereFields){ int ret = ConsoleDBUtil.update(db, o,updateFields , whereFields); return ret; } public int update(T o,String [] updateFields,String [] whereFields){ int ret = ConsoleDBUtil.update(DaoUtil.getConsoleDB(), o,updateFields , whereFields); return ret; } //根据属性删除 public int delete(DBClientWrapper db,T o,String [] whereFields){ int ret = ConsoleDBUtil.delete(db, o, whereFields); return ret; }
BaseService会完成从页面的查询字段向数据库字段的映射,以及bean字段向数据库字段的映射保存等操作。
4.持久层
依托现有bookUtil包下的consoleDB实现,可忽略
5.table支持按任意字段的升序,降序以及分页
<table width="100%" border="0" cellspacing="0" cellpadding="0" class="list_tb"> <tr> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('id');">ID</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('model_type');">模板类型</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('name');">绑定ID</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('name');">名称</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('priority');">优先级</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('start_time');">发布时间</td> <td width="100" class="toptd" style="cursor:pointer;" onclick="javascript:toggleOrder('end_time');">结束时间</td> <td width="100" class="toptd">操作</td> </tr>
如上,click方法里传入参数即对应的数据库字段名称
function toggleOrder(field){ $('[name=orderField]').val(field); var orderby = $('[name=orderBy]').val(); if(orderby=="desc"){ $('[name=orderBy]').val('asc'); }else{ $('[name=orderBy]').val('desc'); } $('#hideform').submit(); }
引入这段js,即实现查询form的提交,之后如上面action定义所说,会自动完成排序的切换,同时,在分页部分的隐藏域中定义这两个字段,分页点击的时候可以保持排序方式不变
6.任务池
背景介绍,据上次的统计,目前运营平台有28个定时任务在不停的执行,实现方式基本是各自开线程,在Action里或者spring配置文件里启动(这两种启动方式都需要手动触发,即访问到相关模块的时候才会触发),执行周期及逻辑方式各样,不方便维护,and,如果集中执行的时候会对系统性能造成冲击
新建了一个任务调度池,统一管理,支持相对周期的定时,绝对时间的定时,以及单词的异步执行。可以在运营平台搜TaskPoolCenter.java
相应代码如下:
public class TaskPoolCenter { private static Logger log = Logger.getLogger("service"); private static int coreTaskNumber = 1; private static int maxTaskNumber = 3; private static long keepAlivetime = 50L; private static int capacity = 5; /******************************周期执行可以调用 相对时间 **********************************/ public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); /** 只是执行一次可以调用 有界队列存储任务 此拒绝策略 提供简单的反馈控制机制,能够减缓新任务的提交速度。 **/ public static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( coreTaskNumber, maxTaskNumber, keepAlivetime, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<Runnable>(capacity), new ThreadPoolExecutor.CallerRunsPolicy()); public static Timer timer = new Timer(); public static void init(){ scheduleTimerTask(new NewUserBagTask(),1,1,0,24,TimeUnit.HOURS);//每天凌晨发送当天的bid新手包到zhiwei log.info("TaskPoolCenter|started"); } /** * 周期执行某个任务 * @param t * @param scehduleTime * @param unit */ public static void schedule(Thread t,long scehduleTime,TimeUnit unit){ scheduler.schedule(t, 6, TimeUnit.SECONDS); } /** * 以绝对时间 周期 执行定时任务 * @param task * @param hour * @param minute * @param second * @param period 间隔周期 毫秒数 */ public static void scheduleTimerTask(TimerTask task,int hour,int minute,int second,int delay,TimeUnit unit){ Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, minute); calendar.set(Calendar.SECOND, second); Date date=calendar.getTime(); //每天零点把当天的bid推送过去 if (date.before(new Date())) { int day = calendar.get(Calendar.DAY_OF_YEAR); calendar.set(Calendar.DAY_OF_YEAR, day+1); date = calendar.getTime(); } timer.schedule(task, date, unit.toMillis(delay)); } /** * 单次任务的异步执行 * @param t */ public static void execute(Thread t){ threadPool.submit(t); }
不推荐以后继续用Timer及TimerTask实现定时任务,原因有
1:Timer只创建了一个线程。当你的任务执行的时间超过设置的延时时间将会产生一些问题。
2:Timer创建的线程没有处理异常,因此一旦抛出非受检异常,该线程会立即终止。
如果有绝对时间的定时任务可以参考quartz.xml里的配置实现。
7.前端的页面验证:
引入jquery.validate.js 简单的配置一下类似这样,可以参考demo.htm及demo.js里的实现
$("#edit_demo").validate({ rules: { name: { required: true, minlength: 2 }, tlength:{ required:true, minlength:5, maxlength:10 }, valid: { required: true, number:true, range:[5,10] } }, messages: { name: { required: "名称不能为空", minlength: "名称需多于两个字" }, valid: { required: "测试数字不能为空", number: "输入内容必须为数字", range:"数字在5到10之间", }, tlength: { required: "不能为空", minlength: "最小为5个字符", maxlength: "最多为10个字符" } } });
不用手动的验证,可以实现实时的页面提示,不用alert,支持时间,整数,浮点字符长度等多种方式的验证