卡包分为商户投放子系统和用户应用子系统组成:
商户投放子系统:
用户应用子系统:
缓存层设计:
缓存使用的是Redis,其中有两类信息,首先是优惠卷PassTemplate Token的存储方案,和商户Merchants存储方案。
优惠卷Token信息会有两个文件,商户上传的原始文件init和已使用的Token文件used,在系统中直接操作io文件会很慢,所以使用Redis的set结构保存优惠卷token信息。用户领取优惠卷时直接从redis中分配。
商户信息使用redis保存,使用hashMap存储,Field是商户id,key是Merchants
项目中常用的工具类:
Apache DigestUtils:编码解码工具类
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
/**
* 为什么不用jdk原生的MessageDigest:
* 只能调用一次,而且多线程下容易出错
*/
public static String md5Hex(String data){
return Hex.encodeHexString(md5(data));
}
Apache RandomStringUtils:生成随机数字,字母的工具
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
函数定义 | 功能 |
RandomStringUtils.random(5) | 产生2位长度的随机字符串 |
RandomStringUtils.random(5,new char[] {'a','b','2','4'}) | 使用指定的字符生成5位长度的随机字符串 |
RandomStringUtils.randomAlphanumeric(5) | 生成指定长度的字母和数字的随机组合字符串 |
RandomStringUtils.randomNumeric(5) | 生成随机数字字符串 |
RandomStringUtils.randomAlphabetic(5) | 生成随机[a-z]字符串,包含大小写 |
RandomStringUtils.randomAscii(4) | 生成从ASCII32到126组成的随机字符串 |
Apache DateUtils:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
函数定义 | 功能 |
Date parseDate(String str,String...parsePatterns) | 解析日期时间字符串日期时间Date对象 |
Date addYears(Date date,int amount) | 得到date日期时间后(前)amount年后的日期时间 |
Date addDays(Date date,int amount) | 同addYears相似,对天数进行加减 |
Date addHours(Date date,int amount) | 同addYears相似,对小时数进行加减 |
boolean isSameDay(Date date1,Date date2) | 判断两个日期是否是同一天 |
Apache Guava Enums:枚举实例的使用方法
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
static Field getField(Enum<?> enumValue) //返回enumValue定义的Field
static <T extends Enum<T>> Optional<T> getIfPresent(Class<T> enumClass,String value) //返回Enum类型的值,使用Enum.calueOf(java.lang.class<T>,java.lang.String)
static <T extends Enum<T>> Converter<String,T> stringConverter(Class<T> enumClass) //返回一个converter对象转换成string以及给定Enum类型的值,使用Enum.valueOf(Class,String) 与Enum.name()
starter-hbase
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>spring-boot-starter-hbase</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
表名 | 列族 | |
people | f | |
name | age |
/**
* 利用Hbase完成查询操作
*/
public class QueryService {
@Autowired
private HbaseTemplate hbaseTemplate;
//范围扫描查询
public List<PeopleDto> query(String startRow, String stopRow){
Scan scan = new Scan(Bytes.toBytes(startRow), Bytes.toBytes(stopRow));
scan.setCaching(5000); //设置scan缓存
List<PeopleDto> dtos = this.hbaseTemplate.find("people_table",scan,new PeopleRowMapper());
return dtos;
}
//确定行键查询
public PeopleDto query(String row){
PeopleDto dto = this.hbaseTemplate.get("people_table",row,new PeopleRowMapper());
return dto;
}
}
public class PeopleRowMapper implements RowMapper<PeopleDto>{
private static byte[] COLUMNFAMILY = "f".getBytes();
private static byte[] NAME = "name".getBytes();
private static byte[] AGE = "age".getBytes();
@Override
public PeopleDto mapRow(Result result,itn rowNum)throws Exception{
PeopleDto dto = new PeopleDto();
//TODO: 设置相关的属性值
String name = Bytes.toString(result.getValue(COLUMNFAMILY ,NAME));
int age = Bytes.toInt(result.getValue(COLUMNFAMILY ,AGE));
retrun fto.setName(name).setAge(age);
}
}
日志处理设计
为什么记录日志?
1.为了事件查询,问题查询 2.为了用户行为分析
日志分类?
记录日志;分析日志;
日志内容?
事件内容;action行为动作,userId用户id,timestamp时间戳,remoteIp用户Ip,info行为信息
异常处理
统一的异常处理
springboot实现了/error统一处理,但是需要我们自己实现自己的统一异常处理,并以Restful Api风格返回客户端:
@ControllerAdvice:定义统一的异常处理类
@ExceptionHandler:定义函数针对的异常类型
表结构设计
商户投放子系统
1.商户信息,保存在Mysql中,分别是id(商户id),name,logo_url(商户logo),business_license_url(商户营业执照),phone,address,is_audit(商户是否审核标志位,默认是未审核)。
2.优惠卷信息,保存在Hbase中,b列族表示基本信息,id表示商户id,title(用户卷标题),summary(摘要),o列族表示限制条件,limit(优惠卷最大数量),start(开始时间)
用户应用子系统
1.Pass,用户优惠卷表,保存在Hbase中,i列族表示用户基本信息,id(用户id),template_id(标识passtempalte在Hbase中的行键,用于映射具体的优惠卷信息),token(优惠卷token),assigned_date(优惠卷领取日期),con_date(消费日期)
2.Feedback,用户返回表,保存在Hbase中,i列族返回的基本信息,id(用户id),type(评论类型),template_id(优惠卷的rowkey)
id生成器:
1.user表,用户的基本信息,b列族表示基本信息,o列族表示额外信息。RowKey:用户表的行键,userId,生成策略:当前用户数+随机5位数字