毕业季--各应用场景案例使用技术

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
日志服务 SLS,月写入数据量 50GB 1个月
简介: 毕业季--各应用场景案例使用技术

当当网中的邮件发送功能。

本身邮件发送功能不难,就是借助了工具类JavaMail的工具类,我们调用工具类的发送邮件的方法即可完成,方法的参数有收件人,邮件标题和邮件内容。
我们在包装这个功能的时候可以把他应用到一个场景里面。
比如:我们定期给用户发送网站营销的一些电子邮件。在营销推广模块,我们可以写出这个功能。

当当网中的支付宝支付功能。

  1. 在当当网里面引入jar包。
<dependency>
  <groupId>com.alipay.sdk</groupId>
  <artifactId>alipay-sdk-java</artifactId>
  <version>4.13.50.ALL</version>
</dependency>
  1. 引入工具类。根据自己的支付宝沙箱环境修改工具类中的属性常量。
  2. 把工具类加入到spring中让,spring管理起来这个类。在spring配置文件中加入一个配置项
<!--让spring扫描工具中 的注解-->
<context:component-scan base-package="com.baizhi.util"/>
  1. 写控制器类
@Autowired
private AlipayTemplate at;
@RequestMapping("pay")
public void payMoney(HttpServletResponse response) throws Exception {
    PayVo pv=new PayVo();
    pv.setOut_trade_no("200");
    pv.setSubject("aaaa");
    pv.setTotal_amount("100");
    //告知浏览器响应的是一个html字符串
    response.setContentType("text/html");
    String pay = at.pay(pv);//支付的html源码
    response.getWriter().print(pay);
}
  1. 测试,输入控制器地址会到支付页面。

使用SpringAop和RabbitMQ实现日志的记录功能。

思路:

操作步骤:

  1. 建表,把日志表创建出来。
  2. 用mybatisplus生成log模块的增删改查基础代码。
  3. 把日志模块的springboot环境搭建起来。创建配置文件,创建入口类。
  4. 到商品模块中给业务类增加额外功能。LogAspect(springaop的额外增强类。)。
  5. LogAspect中准备log对象的各个属性值。封装到log对象里面。把log对象转换为一个json串,通过rabbitmq的生产者代码把json串作为一个消息发给rabbitmq的消息队列。
  6. 在日志模块中增加一个监听消息队列收到消息的监听器。这个监听器就是rabbitmq的消费者。消费者收到消息后,将json串转换为log对象,然后调用业务类的插入方法把日志对象存储到数据库里面。

Redis中秒杀的功能。

商城项目中有个秒杀功能,一元抢iphone手机。商城有十台iphone要促销,吸引顾客。现在要抢的人比较多。并发量大。

  1. 第一版代码
@GetMapping("/secKill")
public String secKill(int pid){
    String key="product:"+pid;
    //获取到操作string的工具类
    ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
    //获取到redis里面商品的数量
    Integer count = (Integer)stringObjectValueOperations.get(key);
    //判断商品数量是否大于0
    if(count>0){
        //大于0了就对商品数量减一
        stringObjectValueOperations.decrement(key);
        System.out.println("购买成功,还剩"+(count-1)+"个");
        return "购买成功,还剩"+count+"个";
    }
    System.out.println("购买失败");
    return "购买失败";
}
第一版代码在多线程的环境中运行是有问题的。
  1. 第二版使用redis的乐观锁
@GetMapping("/secKill2")
public String secKill2(int pid){
    //1.先获取redis里面的库存数量
    String key="product:"+pid;
    //开启一下事务
    redisTemplate.setEnableTransactionSupport(true);
    redisTemplate.watch(key);
    ValueOperations<String, Object> stringObjectValueOperations = redisTemplate.opsForValue();
    Integer count = (Integer) stringObjectValueOperations.get(key);
    redisTemplate.multi();
    //2.判断是否大于0
    if (count > 0) {
        //让库存减一,把库存减一放入到一个事务里面,然后监听product:1这个键是否被其他事务修改
        stringObjectValueOperations.decrement(key);
    }
    List<Object> execList = redisTemplate.exec();//真正执行事务
    if(execList!=null&&execList.size()!=0){
        System.out.println("购买成功!还有" + (count - 1) + "个");
        return "购买成功!还有" + (count - 1) + "个";
    }else{
        System.out.println("购买失败");
        return "购买失败";
    }
}
第二版的代码会出现很多人抢商品但是到最后商品还有留存的问题。
  1. 第三版使lua脚本实现原子操作
    lua脚本:
local prodid=KEYS[1];
local qtkey="product:"..prodid;
local num= redis.call("get" ,qtkey);
if tonumber(num)<=0 then 
  return 0; 
else 
  redis.call("decr",qtkey);
end
return tonumber(num);
@GetMapping("/secKill3")
public String secKill3(int pid){
    long result = 0;
    try {
        //调用lua脚本并执行
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(Long.class);//返回类型是Long
        //lua文件存放在resources目录下
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("test.lua")));
        result = redisTemplate.execute(redisScript, Arrays.asList(pid+""));
    } catch (Exception e) {
        e.printStackTrace();
    }
    if(result>0){
        System.out.println("购买成功,还剩"+(result-1)+"个");
        return "购买成功,还剩"+(result-1)+"个";
    }
    System.out.println("购买失败");
    return "购买失败";
}

Redis中验证码结合手机信息的验证功能。

要求:

  1. 收入手机号,点击发送后生成六位验证码,2分钟内有效
  2. 收入验证码点击验证,返回成功或者失败。
  3. 每个手机号每天只能输入三次。
//手机号
private static final String PHONE="15009876543";
//redis里面存储的两个key的名字,一个键叫15009876543:code,另一个叫15009876543:count
private static final String CODEKEY=PHONE+":code";
private static final String COUNTKEY=PHONE+":count";
//验证手机跟用户输入的验证码是否一致
@Test
public void verifyCode(){
    String userCode="555305";
    Jedis jedis = JedisUtil.getJedis();
    String realCode = jedis.get(CODEKEY);
    jedis.close();
    if(userCode.equalsIgnoreCase(realCode)){
        System.out.println("验证码一致!");
    }else{
        System.out.println("验证码有误!");
    }
}
@Test
//给手机发信息,保证两分钟有效,保证一天发送三次
public void testSendMsg(){
    //验证该手机号是否超过次数
    Jedis jedis= JedisUtil.getJedis();
    String countValue = jedis.get(COUNTKEY);//从redis里面获取到该手机号当天发了几次。
    if(countValue==null){
        //设置key和值的同时,给键一个超时时间expire
        jedis.setex(COUNTKEY,24*60*60,"1");
    }else{
        int count = Integer.parseInt(countValue);
        //一天还没有超过3次
        if(count<3){
            jedis.incr(COUNTKEY);//让redis的countkey自增一下
        }else{//超过三次
            System.out.println("一天不能超过三次");
            return;
        }
    }
    String code = generateCode();//生成验证码
    //调用工具类给手机发信息
    System.out.println("给手机发送了一个验证码:"+code);
    jedis.setex(CODEKEY,120,code);//把验证码放入到了redis里面。
    jedis.close();
}
public String generateCode(){
    Random random=new Random();
    StringBuilder result=new StringBuilder();
    for(int i=0;i<6;i++){
        int num = random.nextInt(10);
        result.append(num);
    }
    return result.toString();
}

用户模块认证和授权功能。

主要的表有三个:用户表(bz_admin),角色表(bz_role),菜单表(bz_menu).

用户表和角色表之间有多对多关系。关系表:bz_admin_role

角色表和菜单表有多对多关系。关系表:bz_role_resource

商品模块的商品属性表的设计

商品模块复杂的是表设计,最初设计是只有一个张商品表。但是有一个问题就是不同类型的商品属性是不一样的。比如手机有cpu和屏幕这些属性,但是衣服没有,衣服有颜色,尺寸,布料这些属性。一个大型商城系统需要很多类型的商品。所以不适合建到一张表里面。
我们为了能够灵活存储商品属性我们又设计了一个属性表,来存储各个类型的商品都有哪些不同的属性,一个属性就对应属性表里面的一行数据。
然后我们需要存储每个商品的商品属性值,这时候我们又创建了一个属性值表。来存储每个属性对应的属性值。
这样就巧妙地把商品表中的列转换成了另外两张表的行数据。

修改后一张表被改为下面四张表

ES的站内分词高亮查询功能。

seata实现分布式事务,下订单时库存和订单的事务问题。

  • 一阶段:执行本地事务,并返回执行结果
  • 二阶段:根据一阶段的结果,判断二阶段做法:提交或回滚

Seata在第一极端先做用户的sql,在做用户的sql前会记录数据日志信息。当需要回滚的的时候seata会将数据进行自动恢复,不需要程序写补偿代码。

使用分布式锁解决缓存击穿的功能。

redis分布式锁代码

/**
 * 分布式锁
 */
public List<Node> getDataByRedisLock(){
    List<Node> nodeList = null;
    //生成UUID
    String uuid = UUID.randomUUID().toString();
    //1.获取锁  执行业务代码 set nx (setIfAbsent)
    //setIfAbsent 该方法如果设置了过期时间 底层就是 set Ex nx 加锁和过期时间设置是原子性的
    Boolean lock = redisTemplate.opsForValue().setIfAbsent("redis-lock", uuid,100,TimeUnit.SECONDS);
    if (lock){
        System.out.println("获取锁");
        //加锁成功查询数据库
        try {
            nodeList = getNodesByMysql();
        }finally {
            //查询完成解锁
            String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
            /**
             * 参数1 脚本
             * 参数2 要删除的key
             * 参数3 uuid
             */
            redisTemplate.execute(
                new DefaultRedisScript<Long>(script,Long.class),
                Arrays.asList("redis-lock"),
                uuid
            );
        }
    }else {
        try {
            System.out.println("没有获取锁重试");
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //重试
        getDataByRedisLock();
    }
    return nodeList;
}

EasyPoi的excel导入导出功能。

在项目中使用excel导入导出是一个很常用的功能,比如到导出商品类别信息或者导出用户信息到excel里面,都可以作为项目开发经验写到简历里面。

使用步骤:

  1. 实体类加注解
@Data
public class BzBrand implements Serializable {
    @Excel(name = "品牌id")
    private Long brandId;
    @Excel(name = "品牌名字")
    private String name;
    @Excel(name = "首字母")
    private String firstLetter;
    @Excel(name = "显示状态")
    private Integer showStatus;
}
  1. 直接完成导入导出
    导出:
/**
 * 导出
 */
@Test
public void test1() throws Exception {
    /**
     * 准备数据
     */
    List<BzBrand> BzBrands = brandMapper.selectAll();
    /**
     * 导出参数对象
     * 参数1 标题
     * 参数2 表名
     */
    ExportParams params = new ExportParams("所有的品牌数据","brands");
    Workbook workbook = ExcelExportUtil.exportExcel(params, BzBrand.class, BzBrands);
    workbook.write(new FileOutputStream("E://easyBrands.xls"));
}
导入:
/**
  * 导入
  */
 @Test
 public void test2() throws Exception {
     FileInputStream inputStream = new FileInputStream("E://easyBrands.xls");
     /**
      * 导入参数对象
      * setTitleRows 声明标题占有的行数
      * setHeadRows 声明表头占有的行数
      */
     ImportParams importParams = new ImportParams();
     importParams.setTitleRows(1);
     importParams.setHeadRows(1);
     /**
      * 导入方法
      * 参数1 流
      * 参数2 类对象
      * 参数3 导入参数对象
      */
     List<BzBrand> BzBrands = ExcelImportUtil.importExcel(inputStream, BzBrand.class, importParams);
     for (BzBrand brand : BzBrands) {
         System.out.println(brand);
     }
 }
  1. 值的替换
    当导出数据时,需要对数据进行转换,比如显示状态 (0->不显示,1->显示)。就需要在导出时,对值进行替换。
@Data
public class BzBrand implements Serializable {
    @Excel(name = "品牌id")
    private Long brandId;
    @Excel(name = "品牌名字")
    private String name;
    @Excel(name = "首字母")
    private String firstLetter;
    @Excel(name = "显示状态",replace={"不显示_0","显示_1"})
    private Integer showStatus;
}

拍卖系统的拍卖功能

拍卖系统的主要表的设计:

create table t_auction(
  auctionId int primary key auto_increment, -- 主键 编号
  auctionName varchar(500), -- 拍卖品名称
  auctionStartPrice double, -- 拍卖品起拍价
  auctionUpset double,   -- 拍卖品底价
  auctionStartTime date, -- 开始时间
  auctionEndTime date , -- 结束时间
  auctionPic  varchar(500), --  拍卖品老图片
  auctionDesc varchar(200)  -- 拍卖品描述
);
create table auction_user(
  userId int primary key auto_increment, -- 主键
  userName varchar(50),-- 用户名
  userPassword varchar(50),-- 用户密码
  userIsadmin int -- 是否为管理员 0普通用户 1管理员
);
create table auction_record(
  record_id int primary key auto_increment, -- 主键
  record_time timestamp,-- 竞拍时间
  record_price double,  -- 竞拍出价
  user_id int ,      -- 用户编号 外键 (用户表)
  auction_id int,      -- 拍卖品编号 外键(拍卖品表)
);

学校的评论功能

评论系统的主要表设计:

create table t_school(
  id int primary key auto_increment,
  name varchar(30) not null,
  description varchar(200) not null
);
-- 学校和评论外键 用户和评论外键
create table t_user(
  id int primary key auto_increment,
  username varchar(30) not null,
  password varchar(30) not null
);
-- 点赞方法redis,评论下的评论用一级类别二级类别
create table t_comment(
  id int primary key auto_increment,
  pid int,
  content varchar(255) not null,
  sid int not null,
  uid int not null,
  create_time date not null
);
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
程序员 项目管理 监控
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(3)
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)
89 0
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(3)
|
敏捷开发 中间件 数据处理
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(1)
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(1)
117 0
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(1)
|
测试技术 Java 数据库
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(2)
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)
54 0
【软件设计师-从小白到大牛】上午题基础篇:第六章 软件工程基础(重点中的重点)(2)
|
7月前
|
消息中间件 JSON NoSQL
毕业季--各应用场景案例使用技术
毕业季--各应用场景案例使用技术
|
敏捷开发 设计模式 缓存
华为资深架构师耗时十年精心整理:Java高级开发需要的分布式技术
分布式、微服务几乎是现在的技术人员必须要了解的架构方向,从理论上来讲确实解耦了很多结构,但另一方面,又会带来更多衍生的复杂度及难点。 如何保证事物的最终一致性?如何进行性能及容量预估?如何处理分布式系统的日志?如何进行线上应急?如果你 曾有和我一样的困惑,那么相信你一定能从本文中得到非常宝贵的解答。 面对越来越复杂的系统和业务,分布式技术早已成为互联网时代的必学技术 ,然而, 如果没有经历过大公司背景的实践和历练,则我们很难接触到分布式服务的设计和架构 。
28个项目实战典型案例总结收获
28个项目实战典型案例总结收获
150 0
|
运维 监控 算法
实战案例——分众传媒 | 学习笔记
快速学习实战案例——分众传媒
实战案例——分众传媒 | 学习笔记
|
SQL 分布式计算 算法
面试心经01--大数据开发工程师
面试心经01--大数据开发工程师
|
数据挖掘 Linux 程序员
手把手教你---猿如意之八大高效利器使用(一)
手把手教你---猿如意之八大高效利器使用
647 0
手把手教你---猿如意之八大高效利器使用(一)