spring aop之动态生成jquery-easyui数据列表操作按钮

2017-8月

学到了一个java动态生成jquery easyui数据列表操作按钮的技术。故在此分享一下此项技术。

spring aop之动态生成jquery-easyui数据列表操作按钮_第1张图片

在后台管理,我们的常常有很多列表类,比如,商品列表,订单列表,分类列表,等等。做这这种操作按钮我们常常直接写死在页面,这样不利于修改,现在我们利用spring aop动态生成操作按钮。


先说一下思路:

1.生成这些按钮要在每条数据之后生成,所有我们给数据添加一个多余的属性,用于存放生成的按钮html代码。(注意下面的商品实体多了一个routerCall属性)

2.每个这样的操作按钮我们看成一个实体,如(商品列表操作按钮,订单列表操作按钮)对应的我们将这些按钮操作的方法,以及按钮显示名称,放入这个类中。
3.现在有很多列表需要这样的按钮,所以使用spring aop对这些列表请求所对应的控制层进行横切。得到列表的传入参数,以及从数据库获得的数据列表,然后遍历所有的数据列表,构造生成每条数据所对应的操作按钮html代码,并将代码添加到其routerCall属性。

4.每个按钮都有对应的请求,所以我们构造一个RouterCallController,处理每个按钮的请求,在这里用的是反射的原理对相应的方法进行调用。

下面附上相应的代码

1.数据(商品)实体

package cn.tsjcate.groupon.deal.entity;

import cn.tsjcate.framework.base.entity.BaseEntity;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;

/**
 * Deal即一个单品
 */
public class Deal extends BaseEntity {

    @Getter @Setter private Long areaId; // 地区ID

    @Getter @Setter private String areaName; // 地区

    @Getter @Setter private Long skuId; // skuID

    @Getter @Setter private Integer dealClass; // 是否虚拟商品

    @Getter @Setter private Long merchantId; // 商家ID

    @Getter @Setter private Integer merchantSku; // 商家SKU 编号

    @Getter @Setter private String dealTitle; // 商品标题

    @Getter @Setter private Integer dealPrice; // 商品价格

    @Getter @Setter private Integer merchantPrice; // 进货价

    @Getter @Setter private Integer marketPrice; // 市场价

    @Getter @Setter private Integer settlementPriceMax; // 最大可接受结算价格

    @Getter @Setter private Integer settlementPrice; // 结算价

    @Getter @Setter private Integer discount; // 折扣

    @Getter @Setter private Integer bonusPoints; // 积分

    @Getter @Setter private Integer dealType; // 商品类型

    @Getter @Setter private Long imageId; // 对应商品图片

    @Getter @Setter private String imageGenPath; // 对应商品图片路径

    @Getter @Setter private Integer dealLevel; // 商品优先级

    @Getter @Setter private Integer maxPurchaseCount; // 最大购买数量

    @Getter @Setter private Integer publishStatus; // 发布状态

    @Getter @Setter private Integer inventoryAmount; // 商品库存数量

    @Getter @Setter private Integer vendibilityAmount; // 商品可售数量

    @Getter @Setter private Integer oosStatus; // 是否售罄标识

    @Getter @Setter private Date startTime; // 商品销售开始时间

    @Getter @Setter private Date endTime; // 商品销售结束时间

    @Getter @Setter private Date publishTime; // 商品上架时间

    @Setter @Getter private String merchantCode; // 商品唯一编码

    @Getter @Setter private Date createTime; // 商品创建时间

    @Getter @Setter private Date updateTime; // 商品更新时间

    @Getter @Setter private DealDetail dealDetail; // 商品对应描述

    @Getter @Setter private Boolean downShelf; // 是否下架的标识

    @Getter @Setter private Integer categoryId; // 商品类别ID
    @Getter @Setter private String routerCall;

    public boolean isStart() {//是否开始团购
        return new Date().after(this.getStartTime());
    }

    public boolean isEnd() {//是否结束
        return new Date().after(this.getEndTime());
    }



2.按钮实体

package cn.tsjcate.admin.product.router;

import cn.tsjcate.admin.base.controller.AjaxResult;
import cn.tsjcate.admin.base.router.BaseRouter;
import cn.tsjcate.framework.base.entity.BaseEntity;
import cn.tsjcate.framework.util.DateUtil;
import cn.tsjcate.groupon.deal.constant.DealConstant;
import cn.tsjcate.groupon.deal.entity.Deal;
import cn.tsjcate.groupon.deal.service.DealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;

/**
 * 商品Router
 */
@Component
public class ProductRouter extends BaseRouter {

	@Autowired private DealService dealService; // 商品服务

	public ProductRouter() {
		this.clazz = Deal.class;
		super.addMethodDisplayName("oosStatusInvalid", "可售");
		super.addMethodDisplayName("oosStatusValid", "已卖光");
		super.addMethodDisplayName("deleteProduct", "删除");
		super.addMethodDisplayName("publishProduct", "发布");
		super.addMethodDisplayName("auditProduct", "审核");
		super.addMethodDisplayName("cancelPublish", "取消发布");
	}

	@SuppressWarnings("unchecked")
	@Override
	public Deal loadEntity(Long id) {
		Deal deal = new Deal();
		deal.setId(id);
		return deal;
	}

	/**
	 *
	 * @param entity  商品实体
	 * @param method   操作方法
	 * @return
	 */
	public boolean isButtonDisabled(BaseEntity entity, String method) {
		Deal deal = (Deal) entity;
		boolean disableFlag = false;
		switch (method) {
			case "oosStatusInvalid"://是否售完
				if (deal.getOosStatus() == DealConstant.DEAL_OOS_STATUS_NO) {//售完
					disableFlag = true;
				}
				break;
			case "oosStatusValid"://是否卖完
				if (deal.getOosStatus() == DealConstant.DEAL_OOS_STATUS_YES) {//已卖光
					disableFlag = true;
				}
				break;
			case "deleteProduct":
				if (deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_DELETED) {//已删除
					disableFlag = true;
				}
				break;
			case "publishProduct":
				if (deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_PUBLISH) {
					disableFlag = true;
				}
				break;
			case "auditProduct":
				if (deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_AUDITED || deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_PUBLISH) {
					disableFlag = true;
				}
				break;
			case "cancelPublish":
				if (deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_NEW
						|| deal.getPublishStatus() == DealConstant.DEAL_PUBLISH_STATUS_AUDITED) {
					disableFlag = true;
				}
				break;
			default:
				break;
		}
		return disableFlag;
	}

	/**
	 * 删除商品
	 * @param user
	 * @param params
	 * @return
	 */
	public AjaxResult deleteProduct(BaseEntity user, Deal deal, Map params) {
		deal.setPublishStatus(DealConstant.DEAL_PUBLISH_STATUS_DELETED);
		AjaxResult modifyResult = new AjaxResult();
		int modifyFlag = dealService.modifyStatusById(deal);//删除商品

		if (modifyFlag > -1) {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
		} else {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
		}

		return modifyResult;
	}

	/**
	 * 发布商品
	 * @param user
	 * @param deal
	 * @param params
	 * @return
	 */
	public AjaxResult publishProduct(BaseEntity user, Deal deal, Map params) {
		AjaxResult modifyResult = null;
		try {
			deal.setPublishStatus(DealConstant.DEAL_PUBLISH_STATUS_PUBLISH);
			modifyResult = new AjaxResult();
			Deal existInfo = dealService.getById(deal.getId());
			if (null != existInfo) {
				if (null != existInfo.getEndTime()) {
					if (existInfo.getEndTime().compareTo(new Date()) < 0) {
						deal.setEndTime(DateUtil.getSevenDaysAfterOnSale());
					}
				} else {
					deal.setEndTime(DateUtil.getSevenDaysAfterOnSale());
				}
			}

			int modifyFlag = dealService.modifyStatusById(deal);
			if (modifyFlag > -1) {
				modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
			} else {
				modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return modifyResult;
	}

	/**
	 * 取消发布商品
	 * @param user
	 * @param deal
	 * @param params
	 * @return
	 */
	public AjaxResult cancelPublish(BaseEntity user, Deal deal, Map params) {
		// 取消发布,默认为已审核
		deal.setPublishStatus(DealConstant.DEAL_PUBLISH_STATUS_AUDITED);
		AjaxResult modifyResult = new AjaxResult();
		int modifyFlag = dealService.modifyStatusById(deal);

		if (modifyFlag > -1) {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
		} else {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
		}
		return modifyResult;
	}

	/**
	 * 审核商品
	 * @param user
	 * @param deal
	 * @param params
	 * @return
	 */
	public AjaxResult auditProduct(BaseEntity user, Deal deal, Map params) {
		deal.setPublishStatus(DealConstant.DEAL_PUBLISH_STATUS_AUDITED);
		AjaxResult modifyResult = new AjaxResult();
		int modifyFlag = dealService.modifyStatusById(deal);

		if (modifyFlag > -1) {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
		} else {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
		}
		return modifyResult;
	}

	/**
	 * 设置已卖光
	 * @param user
	 * @param deal
	 * @param params
	 * @return
	 */
	public AjaxResult oosStatusValid(BaseEntity user, Deal deal, Map params) {
		deal.setOosStatus(DealConstant.DEAL_OOS_STATUS_YES);
		AjaxResult modifyResult = new AjaxResult();
		int modifyFlag = dealService.modifyOosStatusById(deal);

		if (modifyFlag > -1) {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
		} else {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
		}
		return modifyResult;
	}

	/**
	 * 可售
	 * @param user
	 * @param productInfo
	 * @param params
	 * @return
	 */
	public AjaxResult oosStatusInvalid(BaseEntity user, Deal productInfo, Map params) {
		productInfo.setOosStatus(DealConstant.DEAL_OOS_STATUS_NO);
		AjaxResult modifyResult = new AjaxResult();
		int modifyFlag = dealService.modifyOosStatusById(productInfo);

		if (modifyFlag > -1) {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_SUCCESS);
		} else {
			modifyResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
		}
		return modifyResult;
	}

}

package cn.tsjcate.admin.base.router;

import cn.tsjcate.admin.base.controller.AjaxResult;
import cn.tsjcate.framework.base.entity.BaseEntity;
import cn.tsjcate.framework.base.exception.BusinessException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 *
 */
public abstract class BaseRouter {

    protected Class clazz;

    private Map methodDisplayNameMap = new HashMap<>();

    public boolean isAuthorizedToCall(Long userId, String method) {
        return true;
    }

    public boolean isButtonDisabled(BaseEntity entity, String method) {
        return false;
    }

    public String getMethodDisplayName(String method) {
        return methodDisplayNameMap.get(method);
    }
//添加操作按钮名称
    protected void addMethodDisplayName(String method, String methodDisplayName) {
        methodDisplayNameMap.put(method, methodDisplayName);
    }

    public abstract  T loadEntity(Long entityId);

    /**
     * 调用具体Router的业务方法(通过反射)
     * @param user 用户
     * @param entity 实体
     * @param method 方法(字符串)
     * @param params Map参数
     * @param  实体泛型
     * @return
     */
    public  AjaxResult callMethod(BaseEntity user, T entity, String method, Map params) {
        Method defineMethod; //根据方法名获得到具体Router的方法
        try {
            //反射得到方法对象,注:那个类调用的这个对象,getClass()就获得的是哪个对象
            defineMethod = getClass().getMethod(method, new Class[]{BaseEntity.class, clazz, Map.class});
            if (null != defineMethod) {
                //方法调用
                return (AjaxResult) defineMethod.invoke(this,user, entity, params);
            }
            throw new RuntimeException("No method " + method + " defined for router " + getClass().getName());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }  catch (InvocationTargetException e) {
            e.printStackTrace();
            if (e.getTargetException() instanceof BusinessException) {
                throw (BusinessException)e.getTargetException();
            }
            throw new RuntimeException(e.getMessage());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

}



3.前端列表对应的代码product_list.ftl及search.ftl


---------------------------------------search.ftl-------------------------------------------------------------
<#macro search2 gridId>
<#nested/> 查询
----------------------------------------product_list.ftl------------------------------------------------------ <#import "/macro/search.ftl" as search_macro/> <@search_macro.search2 gridId="productListGrid">
<@product_macro.generateProductTypeSelect name="search_dealType" defaultValue=search_dealType hasHeader=true/> <@product_macro.generateProductStatusSelect name="search_publishStatus" defaultValue=search_publishStatus hasHeader=true/>
地区 Sku ID 商家 商家Sku 产品编码 名称 类型 商品类型 销售价格 发布状态 发布时间 销售库存 总库存 销售开始时间 销售结束时间 操作


4.列表请求controller:

/**
	 * 加载商品列表页数据
	 * 利用aop实现router的构造
	 * @param search
	 * @return
     */
	@RequestMapping(value = "/listProduct", method = RequestMethod.POST)
	public @ResponseBody PagingResult listProduct(Search search) {
		PagingResult dealListPage = dealService.getDealList(search);
		if (dealListPage != null && dealListPage.getRows().size() > 0) {
			/*给每个商品添加图片url*/
			for (Deal deal : dealListPage.getRows()) {
				String imagePath = getObjectImageUrl(deal.getImageId(), DealConstant.PICTURE_MODULE_DEAL, 2);
				deal.setImageGenPath(imagePath);
			}
		}
		return dealListPage;
	}

/**给查询出来的每条数据(商品)在这里是entity加上router属性
 * 生成Router按钮(HTML)//
 * @param pagingResult
 * @param search
 * @param 
    */
public  void generateRouterCallButtons(PagingResult pagingResult, Search search) {
   /*如果没有传入参数则不执行*/
   if (!search.containsRouterCall()) {
      return;
   }
   //依照routername获得router实例
   BaseRouter router = getRouter(search.getRouterName());
   if (null == router) {
      return;
   }

   String[] methods = search.getMethods();//获得router所有的方法
   for (T entity : pagingResult.getRows()) {//遍历每条数据(如商品),写入routerCall属性
      StringBuilder routerCall = new StringBuilder();
      for (String method : methods) {//循环生成每个method的html
         String button = generateRouterCallButton(router, search.getRouterName(), entity, method,
               ArrayUtils.contains(search.getConfirmMethods(), method), search.getGridId());
         if (null != button) {
            routerCall.append(button);
         }
      }
      entity.setRouterCall(routerCall.toString());//每个实体添加router属性
   }
}

/**
 *生成某个列表一条数据的操作方法
 * @param router  实例
 * @param routerName  要生成的router名称
 * @param entity  当前要操作的实体(如商品,订单。。。)
 * @param method  每个router方法/按钮(如已售完,发布。。)
 * @param confirm
 * @param gridId  router的id
 * @param 
 * @return
 */
private  String generateRouterCallButton(BaseRouter router, String routerName, T entity,
                                                String method, boolean confirm, String gridId) {
   
   String methodName = router.getMethodDisplayName(method);//method显示名称
   StringBuilder html = new StringBuilder();
   html.append("");
   return html.toString();
   /**/
}


5.控制层切面

package cn.tsjcate.admin.base.aspect;

import cn.tsjcate.admin.base.controller.BaseAdminController;
import cn.tsjcate.framework.common.page.PagingResult;
import cn.tsjcate.framework.common.search.Search;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * Controller中方法拦截
 * 找到返回结果和参数相匹配的那个方法,在方法执行完,构造所对应的router
 */
@Component
@Aspect
public class ControllerAspect {

	@Around(value = "execution(public * cn.tsjcate..controller.*Controller.*(..))")
	public Object aroundControllerMethod(final ProceedingJoinPoint pjp) {
		try {
			Object returnVal = pjp.proceed();//返回方法(连接点)执行完成的结果(PagingResult)
//			MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
//			methodSignature.getMethod();
			for (Object obj : pjp.getArgs()) {//获得所有的参数并且遍历
				if (obj instanceof Search && returnVal instanceof PagingResult &&
						((Search)obj).containsRouterCall()) {
					((BaseAdminController)pjp.getTarget()).generateRouterCallButtons(
							(PagingResult)returnVal, (Search)obj);
				}
			}
			return returnVal;
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
		return null;
	}

}
6.生成的按钮js请求代码

/**
 *
 * @param ctx 项目路径
 * @param routerName
 * @param method  方法名称
 * @param methodName method显示名称
 * @param objectId      实体类id(如商品)
 * @param confirmOperation   是否显示此操作
 * @param gridId  router的id
 */
function ajaxRouterCall(ctx, routerName, method, methodName, objectId, confirmOperation, gridId) {
	//TODO: confirm 名称转换成中文,可以通过method对应名称
	//TODO: reload data/button block
	if (confirmOperation) {
		$.messager.confirm('确认', '是否确认要执行'+methodName+'操作?', function(result){
            if (result){
            	$.ajax({
            		type: "post",
            		url: ctx + "/router/call/operation?"+'routerName='+routerName+'&method='+method+'&entityId='+objectId,
            		//data: postData,
            		beforeSend: function(XMLHttpRequest){
            			//ShowLoading(); or disableLink
            		},
            		success: function(data, textStatus){
            			processAjaxRouterCallResult(objectId,data,gridId);//在这里提示操作成功
            		},
            		complete: function(XMLHttpRequest, textStatus){
            			//HideLoading(); or enableLink
            		},
            		error: function(){
            			//请求出错处理
            		}
            	});
            }
        });
	}
}



7.按钮请求controller

@Controller
@RequestMapping(value = "/router/call")
public class RouterCallController extends BaseAdminController {

    /**
     * Router统一入口
     * @param routerName 名
     * @param method 方法
     * @param entityId 实体ID
     * @param  实体泛型
     * @return
     */
    @RequestMapping(value = "/operation", method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public  AjaxResult routerCall(String routerName, String method, Long entityId) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_WARN);

        //参数判断
        if (null == entityId || StringUtil.isEmpty(routerName) || StringUtil.isEmpty(method)) {
            return ajaxResult;
        }

        //获得Router实例
        BaseRouter router = getRouter(routerName);

        if (null == router) {
            return ajaxResult;
        }

        //加载实体
        T entity = router.loadEntity(entityId);
        if (null == entity) {
            return ajaxResult;
        }

        //鉴权
        if (!router.isAuthorizedToCall(getCurrentUser().getId(), method)) {
//            return ajaxResult;
        }

        //call router's method
        try {
            //两个对象有共同的设置
            //Router调用实例的方法(method名的方法)
            AjaxResult routerResult = router.callMethod(getCurrentUser(), entity, method, null);
            ajaxResult.setStatusCode(routerResult.getStatusCode());
        } catch (BusinessException e) {
//            e.printStackTrace();
            ajaxResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
            ajaxResult.setMessage(e.getMessage());
        } catch (Exception e) {
//            e.printStackTrace();
            ajaxResult.setStatusCode(AjaxResult.AJAX_STATUS_CODE_ERROR);
            ajaxResult.setMessage(e.getMessage());
        }
        return ajaxResult;
    }

}







你可能感兴趣的:(spring)