java b2b2c多用户开源商城系统基于脚本引擎的促销架构源码分享

39 篇文章 1 订阅

需求分析

  • 在分享源码之前,先将b2b2c系统中促销模块需求整理、明确,方便源码的理解。

业务需求

  • b2b2c电子商务系统中促销活动相关规则需以脚本数据的方式存放至redis缓存,在购物车与结算页面计算商品价格时从redis缓存中获取促销规则信息,实现商品价格的计算。

技术需求

  1. 促销规则脚本需要使用freemarker模板引擎,需向其中设置内置变量。
  2. 渲染脚本和调用脚本的方法放入工具类中,方便随时调用。

架构思路

一、脚本生成规则

1、需要生成脚本引擎的促销活动包括:满减满赠、单品立减、第二件半价、团购、限时抢购、拼团、优惠券和积分兑换。

2、根据促销活动规则的不同,生成脚本引擎的时机也不同,大致可分为四类:

第一类:满减满赠、单品立减、第二件半价和优惠券,这四种是在活动生效时生成脚本。需要设置延时任务,活动生效自动生成脚本。

第二类:拼团,由于拼团活动生效后,也可以再次添加或修改参与拼团活动的商品,并且平台可以关闭和开启拼团活动,因此与第一类稍有不同,除活动生效时需要生成脚本外,上述这些操作也要生成或更新脚本。

第三类:团购、限时抢购,这两种促销活动是平台发布商家选择商品进行参与的,参与的商品需要商家进行审核,因此是在审核通过时生成脚本。

第四类:积分兑换,积分兑换针对的是商品,因此是在商家新增和修改商品信息时,生成或更新脚本。

3、促销活动生成的脚本都需要放入缓存中,以便于减少查库操作。

4、清除缓存中无用的脚本引擎:除积分兑换外,其他促销活动都需要利用延时任务,在促销活动失效时,将缓存中的脚本数据清除掉。积分兑换在商家关闭商品的积分兑换操作时才对缓存中的脚本数据进行删除。

二、脚本生成流程图

在这里插入图片描述

三、缓存数据结构

1、根据促销活动的不同规则,分为三种缓存数据结构,分别是:SKU级别缓存、店铺级别缓存和优惠券级别缓存。
2、结构图:

 SKU级别缓存结构和店铺级别缓存结构级别一致,如下:
 在这里插入图片描述
 而优惠券级别的缓存结构如下:
 在这里插入图片描述
3、缓存结构说明
 
(1)、SKU级别缓存:

缓存key:{SKUPROMOTION} 加上SKU的ID,例如:{SKU_PROMOTION}_100。

缓存value:是一个泛型为PromotionScriptVO的List集合。

(2)、店铺级别缓存:

缓存key:{CARTPROMOTION} 加上店铺的ID,例如:{CART_PROMOTION}_100。

缓存value:是一个泛型为PromotionScriptVO的List集合。

(3)、优惠券级别缓存:

缓存key:{COUPONPROMOTION} 加上优惠券的ID,例如:{COUPON_PROMOTION}_100。

缓存value:是一个String类型的脚本字符串。

4、促销活动存储的缓存结构区分

(1)、针对满减满赠、单品立减、第二件半价这三种促销活动,如果商家在发布活动时选择的是全部商品参与,那么则存储的是店铺级别的缓存结构,如果选择的是部分商品参与,那么则存储的是SKU级别的缓存结构。

(2)、针对拼团、团购、显示抢购和积分兑换这些促销活动,都是存储的SKU级别的缓存结构。

(3)、针对优惠券,无论是店铺优惠券还是平台优惠券,存储的都是优惠券级别的缓存结构。

四、脚本规范

1、调用脚本传入的变量规范:

变量名称类型说明
$currentTimeint当前时间,为了验证活动是否有效
$skuObject详见下表
$pricedouble其他优惠活动优惠后总价

$sku说明:

名称类型说明
$pricedouble商品单价
$numint商品数量
$skuIdint商品skuID
$totalPricedouble商品小计(单价*数量)

2、各个促销活动脚本中的方法说明

满减满赠、优惠券促销活动脚本方法

方法名参数返回值类型返回值示例说明
validTime$currentTimeBooleantrue/false
countPrice$priceDouble100.00
giveGift$priceObject[{“type”:“freeShip”,“value”:true},{“type”:“point”,“value”:100},{“type”:“gift”,“value”:10},{“type”:“coupon”,“value”:20}]优惠券脚本没有此方法

单品立减、第二件半价、团购、限时抢购、团购活动脚本方法

方法名参数返回值类型返回值示例
validTime$currentTimeBooleantrue/false
countPrice$priceDouble100.00

积分兑换活动脚本方法

方法名参数返回值类型返回值示例说明
validTime$currentTimeBooleantrue/false此方法会直接返回true,积分兑换不涉及有效期,脚本中有此方法是为了脚本内容统一
countPrice$priceDouble100.00
countPoint$priceInteger50

源码分享

由于促销活动类型较多,此处只以团购活动为例进行相关代码的分享。
ScriptUtil
促销脚本渲染与调用工具类

import com.enation.app.javashop.framework.logs.Logger;
import com.enation.app.javashop.framework.logs.LoggerFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;

/**
 * 脚本生成工具类
 * @author duanmingyu
 * @version v1.0
 * @since v7.2.0
 * @date 2020-01-06
 */
public class ScriptUtil {

    private static final Logger log = LoggerFactory.getLogger(ScriptUtil.class);

    /**
     * 渲染并读取脚本内容
     * @param name 脚本模板名称(例:test.js,test.html,test.ftl等)
     * @param model 渲染脚本需要的数据内容
     * @return
     */
    public static String renderScript(String name, Map<String, Object> model) {
        StringWriter stringWriter = new StringWriter();

        try {
            Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);

            cfg.setClassLoaderForTemplateLoading(Thread.currentThread().getContextClassLoader(),"/script_tpl");
            cfg.setDefaultEncoding("UTF-8");
            cfg.setNumberFormat("#.##");

            Template temp = cfg.getTemplate(name);

            temp.process(model, stringWriter);

            stringWriter.flush();

            return stringWriter.toString();

        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            try {
                stringWriter.close();
            } catch (IOException ex) {
                log.error(ex.getMessage());
            }
        }

        return null;
    }

    /**
     * @Description:执行script脚本
     * @param method script方法名
     * @param params 参数
     * @param script 脚本
     * @return: 返回执行结果
     * @Author: liuyulei
     * @Date: 2020/1/7
     */
    public static Object executeScript(String method,Map<String,Object> params,String script)  {
        if (StringUtil.isEmpty(script)){
            log.debug("script is " + script);
            return new Object();
        }

        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");


            log.debug("脚本参数:");
            for (String key:params.keySet()) {
                log.debug(key + "=" + params.get(key));
                engine.put(key, params.get(key));
            }

            engine.eval(script);
            log.debug("script 脚本 :");
            log.debug(script);

            Invocable invocable = (Invocable) engine;

            return invocable.invokeFunction(method);
        } catch (ScriptException e) {
            log.error(e.getMessage(),e);
        } catch (NoSuchMethodException e) {
            log.error(e.getMessage(),e);
        }
        return new Object();
    }
}

groupbuy.ftl
团购活动脚本模板

<#--
 验证促销活动是否在有效期内
 @param promotionActive 活动信息对象(内置常量)
        .startTime 获取开始时间
        .endTime 活动结束时间
 @param $currentTime 当前时间(变量)
 @returns {boolean}
 -->
function validTime(){
    if (${promotionActive.startTime} <= $currentTime && $currentTime <= ${promotionActive.endTime}) {
        return true;
    }
    return false;
}

<#--
活动金额计算
@param promotionActive 活动信息对象(内置常量)
       .price 商品促销活动价格
@param $sku 商品SKU信息对象(变量)
       .$num 商品数量
@returns {*}
-->
function countPrice() {
    var resultPrice = $sku.$num * ${promotionActive.price};
    return resultPrice < 0 ? 0 : resultPrice.toString();
}

PromotionScriptVO
促销活动脚本数据结构实体

import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import java.io.Serializable;

/**
 * @description: 促销脚本VO
 * @author: liuyulei
 * @create: 2020-01-09 09:43
 * @version:1.0
 * @since:7.1.5
 **/
@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class)
public class PromotionScriptVO implements Serializable {
    private static final long serialVersionUID = 3566902764098210013L;

    @ApiModelProperty(value = "促销活动id")
    private Integer promotionId;

    @ApiModelProperty(value = "促销活动名称")
    private String promotionName;

    @ApiModelProperty(value = "促销活动类型")
    private String promotionType;

    @ApiModelProperty(value = "是否可以被分组")
    private Boolean isGrouped;

    @ApiModelProperty(value = "促销脚本",hidden = true)
    private String promotionScript;

    @ApiModelProperty(value = "商品skuID")
    private Integer skuId;


    public Integer getPromotionId() {
        return promotionId;
    }

    public void setPromotionId(Integer promotionId) {
        this.promotionId = promotionId;
    }

    public String getPromotionName() {
        return promotionName;
    }

    public void setPromotionName(String promotionName) {
        this.promotionName = promotionName;
    }

    public String getPromotionType() {
        return promotionType;
    }

    public void setPromotionType(String promotionType) {
        this.promotionType = promotionType;
    }

    public Boolean getIsGrouped() {
        return isGrouped;
    }

    public void setIsGrouped(Boolean grouped) {
        isGrouped = grouped;
    }

    public String getPromotionScript() {
        return promotionScript;
    }

    public void setPromotionScript(String promotionScript) {
        this.promotionScript = promotionScript;
    }

    public Integer getSkuId() {
        return skuId;
    }

    public void setSkuId(Integer skuId) {
        this.skuId = skuId;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        PromotionScriptVO that = (PromotionScriptVO) o;

        return new EqualsBuilder()
                .append(promotionId, that.promotionId)
                .append(promotionName, that.promotionName)
                .append(promotionType, that.promotionType)
                .append(isGrouped, that.isGrouped)
                .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(promotionId)
                .append(promotionName)
                .append(promotionType)
                .append(isGrouped)
                .toHashCode();
    }

    @Override
    public String toString() {
        return "PromotionScriptVO{" +
                "promotionId=" + promotionId +
                ", promotionName='" + promotionName + '\'' +
                ", promotionType='" + promotionType + '\'' +
                ", isGrouped=" + isGrouped +
                ", promotionScript='" + promotionScript + '\'' +
                ", skuId=" + skuId +
                '}';
    }
}

GroupbuyScriptManager
团购促销活动脚本业务接口

import com.enation.app.javashop.core.promotion.tool.model.dos.PromotionGoodsDO;

import java.util.List;

/**
 * 团购促销活动脚本业务接口
 * @author duanmingyu
 * @version v1.0
 * @since v7.2.0
 * 2020-02-18
 */
public interface GroupbuyScriptManager {

    /**
     * 创建参与团购促销活动商品的脚本数据信息
     * @param promotionId 团购促销活动ID
     * @param goodsList 参与团购促销活动的商品集合
     */
    void createCacheScript(Integer promotionId, List<PromotionGoodsDO> goodsList);

    /**
     * 删除商品存放在缓存中的团购促销活动相关的脚本数据信息
     * @param promotionId 团购促销活动ID
     * @param goodsList 参与团购促销活动的商品集合
     */
    void deleteCacheScript(Integer promotionId, List<PromotionGoodsDO> goodsList);
}

GroupbuyScriptManagerImpl
团购促销活动脚本业务接口实现

import com.enation.app.javashop.core.base.CachePrefix;
import com.enation.app.javashop.core.promotion.groupbuy.model.dos.GroupbuyActiveDO;
import com.enation.app.javashop.core.promotion.groupbuy.service.GroupbuyActiveManager;
import com.enation.app.javashop.core.promotion.groupbuy.service.GroupbuyScriptManager;
import com.enation.app.javashop.core.promotion.tool.model.dos.PromotionGoodsDO;
import com.enation.app.javashop.core.promotion.tool.model.enums.PromotionTypeEnum;
import com.enation.app.javashop.core.promotion.tool.model.vo.PromotionScriptVO;
import com.enation.app.javashop.framework.cache.Cache;
import com.enation.app.javashop.framework.logs.Logger;
import com.enation.app.javashop.framework.logs.LoggerFactory;
import com.enation.app.javashop.framework.util.ScriptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 团购促销活动脚本业务接口
 * @author duanmingyu
 * @version v1.0
 * @since v7.2.0
 * 2020-02-18
 */
@Service
public class GroupbuyScriptManagerImpl implements GroupbuyScriptManager {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Cache cache;

    @Autowired
    private GroupbuyActiveManager groupbuyActiveManager;

    @Override
    public void createCacheScript(Integer promotionId, List<PromotionGoodsDO> goodsList) {
        //如果参与团购促销活动的商品集合不为空并且集合长度不为0
        if (goodsList != null && goodsList.size() != 0) {
            //获取团购活动详细信息
            GroupbuyActiveDO groupbuyActiveDO = this.groupbuyActiveManager.getModel(promotionId);

            //批量放入缓存的数据集合
            Map<String, List<PromotionScriptVO>> cacheMap = new HashMap<>();

            //循环参与团购活动的商品集合,将脚本放入缓存中
            for (PromotionGoodsDO goods : goodsList) {

                //缓存key
                String cacheKey = CachePrefix.SKU_PROMOTION.getPrefix() + goods.getSkuId();

                //获取拼团活动脚本信息
                PromotionScriptVO scriptVO = new PromotionScriptVO();

                //渲染并读取团购促销活动脚本信息
                String script = renderScript(groupbuyActiveDO.getStartTime().toString(), groupbuyActiveDO.getEndTime().toString(), goods.getPrice());

                scriptVO.setPromotionScript(script);
                scriptVO.setPromotionId(promotionId);
                scriptVO.setPromotionType(PromotionTypeEnum.GROUPBUY.name());
                scriptVO.setIsGrouped(false);
                scriptVO.setPromotionName("团购");
                scriptVO.setSkuId(goods.getSkuId());

                //从缓存中读取脚本信息
                List<PromotionScriptVO> scriptList = (List<PromotionScriptVO>) cache.get(cacheKey);
                if (scriptList == null) {
                    scriptList = new ArrayList<>();
                }

                scriptList.add(scriptVO);

                cacheMap.put(cacheKey, scriptList);
            }

            //将sku促销脚本数据批量放入缓存中
            cache.multiSet(cacheMap);
        }
    }

    @Override
    public void deleteCacheScript(Integer promotionId, List<PromotionGoodsDO> goodsList) {
        //如果参与团购促销活动的商品集合不为空并且集合长度不为0
        if (goodsList != null && goodsList.size() != 0) {
            //需要批量更新的缓存数据集合
            Map<String, List<PromotionScriptVO>> updateCacheMap = new HashMap<>();

            //需要批量删除的缓存key集合
            List<String> delKeyList = new ArrayList<>();

            for (PromotionGoodsDO goods : goodsList) {
                //缓存key
                String cacheKey = CachePrefix.SKU_PROMOTION.getPrefix() + goods.getSkuId();

                //从缓存中读取促销脚本缓存
                List<PromotionScriptVO> scriptCacheList = (List<PromotionScriptVO>) cache.get(cacheKey);

                if (scriptCacheList != null && scriptCacheList.size() != 0) {
                    //循环促销脚本缓存数据集合
                    for (PromotionScriptVO script : scriptCacheList) {
                        //如果脚本数据的促销活动信息与当前修改的促销活动信息一致,那么就将此信息删除
                        if (PromotionTypeEnum.GROUPBUY.name().equals(script.getPromotionType())
                                && script.getPromotionId().intValue() == promotionId.intValue()) {
                            scriptCacheList.remove(script);
                            break;
                        }
                    }

                    if (scriptCacheList.size() == 0) {
                        delKeyList.add(cacheKey);
                    } else {
                        updateCacheMap.put(cacheKey, scriptCacheList);
                    }
                }
            }

            cache.multiDel(delKeyList);
            cache.multiSet(updateCacheMap);
        }
    }

    /**
     * 渲染并读取团购促销活动脚本信息
     * @param startTime 活动开始时间
     * @param endTime 活动结束时间
     * @param price 限时抢购商品价格
     * @return
     */
    private String renderScript(String startTime, String endTime, Double price) {
        Map<String, Object> model = new HashMap<>();

        Map<String, Object> params = new HashMap<>();
        params.put("startTime", startTime);
        params.put("endTime", endTime);
        params.put("price", price);

        model.put("promotionActive", params);

        String path = "groupbuy.ftl";
        String script = ScriptUtil.renderScript(path, model);

        logger.debug("生成团购促销活动脚本:" + script);

        return script;
    }
}

以上是Javashop中基于脚本引擎的促销活动架构思路与部分源码分享。
易族智汇(Javashop)原创文章

Javashop开发规范V2.2 版本 说明 提交人 V1.0 初稿 定义了包名、异常、事务、和路径的规范 王峰 V2.0 1. 重新整理了命名规范 2. 增加常用命词对照表 王峰 V2.1 1.增加数据库操作的说明 2.增加常用方法介绍 王峰 V2.2 增加关于数据导入导出的说明 王峰 V2.3 增加校验使用说明(5.3章节) 王峰 1 命名规范 1.1 包命 1. 小写字母 2. 以com.enation.javashop开头 3. 组件以com.enation.compoent开头 1.2 类名 一、 action 以Action结尾,如:UserAction 二、 业务类 1.接口: 以I开头,以Manager结尾,如:IUserManager 2.实现类 以Manager结尾,如:UserManager 三、 组件类 以Component结尾,如:ShopEmailComponent 四、 挂件类 以Widget结尾,如:MemberAddressWidget 五、 插件类 以Plugin结尾,如:SendRegMailPlugin 1.3 变量/属性命名规则 1. 全部小写 2. 可用下划线连接 如:username、 userid 1.4 方法命名 1.4.1 Action类 add 到添加页 edit 到修改页 saveAdd 保存添加 saveEdit 保存修改 delete 删除 list 列表 1.4.2 业务类 add 添加 edit 修改 delete 删除 list 列表 get 读取详细 2 数据库开发规范 2.1 数据操作支持类 一、 业务类数据库调用 1.继承于BaseSupport 2.在spring文件中声明parent为baseSupport <bean id="xxxManagerImpl" class="xxx.xxx.xxx.XxxxManager" parent="baseSupport"/> 3.通过this.baseDaoSupport操作数据库 实际使用的是:com.enation.eop.sdk.database.BaseJdbcDaoSupport 此种操作示例: com.enation.javashop.core.service.impl.BrandManager 注意事项:见【BaseDaoSupport的意义和存在的问题】 二、 直接声明baseDaoSupport操作数据库 在一些挂件类中或某些特殊情况,可以直接需要直接声明baseDaoSupport 此种操作示例: com.enation.javashop.core.service.impl.batchimport.GoodsSpecImporter 注意事项:见【BaseDaoSupport的意义和存在的问题】 2.2 BaseDaoSupport的意义存在的问题 BaseDaoSupport对JdbcDaoSupport进行包装,通过 baseDBRouter 获取表名, 为什么要通过 baseDBRouter 来获取表名呢?Eop机制是支持SAAS(多租户)模式运行的,在SAAS会为每个用户提供如:es_goods_<userid>_<siteid>这样的表。 为了保证在单机版和SAAS模式中都运行正常,BaseDaoSupport将过滤sql中的相应表名。 但目前还只能支持简单的单表select、insert、update,对于多表的联合查询或更新不能支持。 在这种情况下,就需要通过 daoSupport(com.enation.framework.database.impl.JdbcDaoSupport)来操作,daoSupport不对sql进行任何更改,这时为了保证兼容saas模式兼容性,就要使用BaseSupport.getTableName(String tablename)方法 或baseDBRouter.getTableName(String tablename);来保证表名的正确。 (够混乱?真心希望出现一位大侠拯救这个状况,使basedaosupport可以处理所有情况的sql) 2.3 实体Bean和数据库表对照 Javahop数据库操作支持,将对象直接保存或修改,如: this.baseDaoSupport.insert("brand", brand); this.baseDaoSupport.update("brand", brand, "brand_id=" + brand.getBrand_id()); 规则为:实体中的属性名和数据库表的字段名相同,如: 对应的数据库字段: 2.4 注解的使用 2.4.1 @NotDbField 在某个实体Bean中,我们可能会有一些属性不对应数据库字段,这时我们需要在相应的 Geter方法中加上@NotDbField注解,以便使数据库机制知道这个字段不转为sql语句,如: private File file; @NotDbField public File getFile() { return file; } 2.4.2 @PrimaryKeyField 在实合格bean和数据库对照过程中,数据库机制需要识别主键,所以需要我们在主键的对应属性的Geter方法中加上@PrimaryKeyFiled注解,如: private Integer brand_id @PrimaryKeyField public Integer getBrand_id() { return brand_id; } 3 数据导入 3.1 导入接口 DBSolutionFactory.dbImport("file:com/enation/javashop/component/coupon/add.xml","es_"); 3.2 数据Xml文件说明 3.2.1 创建表 <action> <command>create</command> <table>tablename</table> <field> <name>id</name> <type>int</type> <size>8</size> <option>11</option> </field> <field> <name>name</name> <type>varchar</type> <size>255</size> <option>00</option> </field> </action> 3.2.2 删除表 <action> <command>drop</command> <table>tablename</table> </action> 3.2.3 添加、删除列 <action> <command>alter</command> <table>goods</table> <field type="add"> <name>isgroupbuy</name> <type>int</type> <size>1</size> <default>0</default> </field> <field type="drop"> <name>isgroup</name> </field> </action> 3.2.4 创建索引 <action> <command>index</command> <table>goods</table> <field > <name>goodsid</name> </field> </action> 3.2.5 删除索引 <action> <command>unindex</command> <table>goods</table> <field > <name>goodsid</name> </field> </action> 3.2.6 插入数据 <action> <command>insert</command> <table>es_adcolumn</table> <fields>acid,cname,width,height,atype,disabled</fields> <values>5,'列表页上部横幅','972px','67px',0,'false'</values> </action> 3.2.7 删除数据 暂未支持 3.2.8 更新数据 暂未支持 3.3 数据类型对照表 xml Mysql Oracle SqlServer int int NUMBER smallint int(1) smallint(1) NUMBER(2) int memo text CLOB text datetime datetime TIMESTAMP datetime long bigint NUMBER bigint decimal decimal NUMBER(20,2) decimal 4 数据导出 String[] tables = new String[1]; tables[0] = "es_auth_action"; DBSolutionFactory.dbExport(tables, false, "") 5 常用方法介绍 5.1 上下文获取 参见: http://www.javamall.com.cn/developer_help/index.php/常用方法 5.2 地区联动下拉框 1.如果是在jsp 中: <html:regionselect></html:regionselect> 2.如果是在Freemarker的html中: <#assign RegionSelect= "com.enation.app.base.component.widget.regions.RegionSelectDirective"?new()> <@RegionSelect /> 以上两种方式均支持以下参数: province_id:省id city_id:市id region_id:区id 如果指定上述参数,则默认选中 5.3 客户端校验 EOP自动为应用提供表单校验功能,通过指定form样式名和指定表单项特定属性的方式来完成。 5.3.1 示例 代码示例 [removed] function checkUserName(val){ if(val=='kingapex' ) return true; else return "用户名已存在"; } $.Validator.options={lang:{isrequired:'此项不能为空!'}}; [removed] <form class="validate"> 必须:<input type="text" name="test1" isrequired="true"> <br/> 整型:<input type="text" name="test2" isrequired="true" dataType="int"> <br/> 浮点型:<input type="text" name="test3" dataType="float"> <br/> 邮件:<input type="text" name="test4" isrequired="true" dataType="email"> <br/> 日期:<input type="text" name="test5" isrequired="true" dataType="date"> <br/> 电话号码:<input type="text" name="test6" isrequired="true" dataType="tel_num"> <br/> 手机:<input type="text" name="test7" isrequired="true" dataType="mobile"> <br/> 邮政编码:<input type="text" name="test8" isrequired="true" dataType="post_code"> <br/> 网址:<input type="text" name="test9" isrequired="true" dataType="url"> <br/> 自定义函数:<input type="text" name="test10" isrequired="true" fun="checkUserName"> <br/> <input type="submit" value="确定" /> </form> 5.3.2 参数说明: 5.3.2.1 isrequired 为true则为必填项,不指定或指为false则为非必填项。 5.3.2.2 dateType 指定校验特殊类型,支持的类型见下表: 类型 说明 int 整数 float 浮点数 email 邮件格式 date 日期格式 tel_num 电话格式 mobile 手机格式 post_code 邮编 url 网址 5.3.2.3 fun 自定义校验函数,返回真则通过校验,返回假或字串则校验失败,返回的字串会出现在失败提示框中。 5.3.2.4 动态绑定校验函数 通过 setValidator实现 $("#region_id").setValidator(function(){ var value = $("#region_id").val(); if( value=="" || value=="0" ) return "地区信息不完整"; else return true; }); 5.3.2.5 提示器 提示器用于显示校验的结果,如果不指定默认会在校验的控件后面创建一个span做为提示器。 可以通过两种方式指定提示器: 一、在控件中声明tiper属性: <input type="text" name="username" tiper="#name_tiper" /> 注:tiper指定的是jquery的一个选择器表达式。 二、动态指定: $("#region_id").setTiper($("#name_tiper")); 注:此时指定的是jquery对象,而不是一个表达式 5.3.2.6 手动调用: $("#siteForm").checkall(); 6 常用字典对照表 6.1 常用值 名称 值 说明 返回结果 0:失败 1:成功 适用于: 1. 客户端json返回值 2. 服务器端方法返回值 3. 数据库是否的标识值 性别 0:女 1:男 6.2 常用名称 名称 英文 商品 goods 货品 product 规格 spec 订单 order 会员 member 积分 point 购物车 cart 结算 checkout 订单 order 品牌 brand 分类 cat 优惠劵 coupon 支付 payment 团购 groupbuy 虚拟 virtual 发票 receipt 属性 prop 参数 param 标签 tag 地区 region 7 异常 1. 提供统一的异常处理机制 2. 底层级别的类不处理异常,只管向上抛出异常,且统一抛出RuntimeExcepton 8 事务处理 1. 数据库统一使用Srping AOP事务 2. 采用注解方式: 在接口处: @Transactional(propagation = Propagation.isrequired) 9 路径的规范 返回的路径变量最后不带 ‘/’ 如:String path =”user/1”; 使用者: path = path+”/”+myVar; 10 样式/HTML规范 10.1 前台分页 <div class="page"><span class="info">共41条记录</span> <span class="info">1/3</span> <ul><li><a class="selected">1</a></li> <li><a href="search-cat-4-page-2.html" class="unselected">2</a></li> <li><a href="search-cat-4-page-3.html" class="unselected">3</a></li> <li><a href="search-cat-4-page-2.html" class="unselected">>></a></li> <li><a href="search-cat-4-page-3.html" class="unselected">>|</a></li> </ul></div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kingapex1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值