国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频

SpringBoot實(shí)現(xiàn)抽獎(jiǎng)大轉(zhuǎn)盤(pán)

共 21206字,需瀏覽 43分鐘

 ·

2022-01-23 17:02

1、項(xiàng)目介紹

這是一個(gè)基于Spring boot + Mybatis Plus + Redis 的簡(jiǎn)單案例。

主要是將活動(dòng)內(nèi)容、獎(jiǎng)品信息、記錄信息等緩存到Redis中,然后所有的抽獎(jiǎng)過(guò)程全部從Redis中做數(shù)據(jù)的操作。

大致內(nèi)容很簡(jiǎn)單,具體操作下面慢慢分析。

2、項(xiàng)目演示

話不多說(shuō),首先上圖看看項(xiàng)目效果,如果覺(jué)得還行的話咱們就來(lái)看看他具體是怎么實(shí)現(xiàn)的。

3、表結(jié)構(gòu)

該項(xiàng)目包含以下四張表,分別是活動(dòng)表、獎(jiǎng)項(xiàng)表、獎(jiǎng)品表以及中獎(jiǎng)記錄表。具體的SQL會(huì)在文末給出。

4、項(xiàng)目搭建

咱們首先先搭建一個(gè)標(biāo)準(zhǔn)的Spring boot 項(xiàng)目,直接IDEA創(chuàng)建,然后選擇一些相關(guān)的依賴即可。

4.1 依賴

該項(xiàng)目主要用到了:Redis,thymeleaf,mybatis-plus等依賴。



????
????????org.springframework.boot
????????spring-boot-starter-data-redis
????


????
????????org.springframework.boot
????????spring-boot-starter-thymeleaf
????


????
????????org.springframework.boot
????????spring-boot-starter-web
????


????
????????mysql
????????mysql-connector-java
????????runtime
????


????
????????org.springframework.boot
????????spring-boot-starter-test
????????test
????


????
????????com.baomidou
????????mybatis-plus-boot-starter
????????3.4.3
????


????
????????com.baomidou
????????mybatis-plus-generator
????????3.4.1
????


????
????????com.alibaba
????????fastjson
????????1.2.72
????


????
????????com.alibaba
????????druid-spring-boot-starter
????????1.1.22
????


????
????????org.apache.commons
????????commons-lang3
????????3.9
????


????
????????org.projectlombok
????????lombok
????????1.18.12
????


????
????????org.apache.commons
????????commons-pool2
????????2.8.0
????


????
????????org.mapstruct
????????mapstruct
????????1.4.2.Final
????


????
????????org.mapstruct
????????mapstruct-jdk8
????????1.4.2.Final
????


????
????????org.mapstruct
????????mapstruct-processor
????????1.4.2.Final
????


????
????????joda-time
????????joda-time
????????2.10.6
????


4.2 YML配置

依賴引入之后,我們需要進(jìn)行相應(yīng)的配置:數(shù)據(jù)庫(kù)連接信息、Redis、mybatis-plus、線程池等。

server:
??port:?8080
??servlet:
????context-path:?/
spring:
??datasource:
????druid:
??????url:?jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
??????username:?root
??????password:?123456
??????driver-class-name:?com.mysql.cj.jdbc.Driver
??????initial-size:?30
??????max-active:?100
??????min-idle:?10
??????max-wait:?60000
??????time-between-eviction-runs-millis:?60000
??????min-evictable-idle-time-millis:?300000
??????validation-query:?SELECT?1?FROM?DUAL
??????test-while-idle:?true
??????test-on-borrow:?false
??????test-on-return:?false
??????filters:?stat,wall
??redis:
????port:?6379
????host:?127.0.0.1
????lettuce:
??????pool:
????????max-active:?-1
????????max-idle:?2000
????????max-wait:?-1
????????min-idle:?1
????????time-between-eviction-runs:?5000
??mvc:
????view:
??????prefix:?classpath:/templates/
??????suffix:?.html
#?mybatis-plus
mybatis-plus:
??configuration:
????map-underscore-to-camel-case:?true
????auto-mapping-behavior:?full
??mapper-locations:?classpath*:mapper/**/*Mapper.xml

#?線程池
async:
??executor:
????thread:
??????core-pool-size:?6
??????max-pool-size:?12
??????queue-capacity:?100000
??????name-prefix:?lottery-service-

4.3 代碼生成

這邊我們可以直接使用mybatis-plus的代碼生成器幫助我們生成一些基礎(chǔ)的業(yè)務(wù)代碼,避免這些重復(fù)的體力活。

這邊貼出相關(guān)代碼,直接修改數(shù)據(jù)庫(kù)連接信息、相關(guān)包名模塊名即可。

public?class?MybatisPlusGeneratorConfig?{
????public?static?void?main(String[]?args)?{
????????//?代碼生成器
????????AutoGenerator?mpg?=?new?AutoGenerator();

????????//?全局配置
????????GlobalConfig?gc?=?new?GlobalConfig();
????????String?projectPath?=?System.getProperty("user.dir");
????????gc.setOutputDir(projectPath?+?"/src/main/java");
????????gc.setAuthor("chen");
????????gc.setOpen(false);
????????//實(shí)體屬性?Swagger2?注解
????????gc.setSwagger2(false);
????????mpg.setGlobalConfig(gc);

????????//?數(shù)據(jù)源配置
????????DataSourceConfig?dsc?=?new?DataSourceConfig();
????????dsc.setUrl("jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true");
????????dsc.setDriverName("com.mysql.cj.jdbc.Driver");
????????dsc.setUsername("root");
????????dsc.setPassword("123456");
????????mpg.setDataSource(dsc);

????????//?包配置
????????PackageConfig?pc?=?new?PackageConfig();
//????????pc.setModuleName(scanner("模塊名"));
????????pc.setParent("com.example.lottery");
????????pc.setEntity("dal.model");
????????pc.setMapper("dal.mapper");
????????pc.setService("service");
????????pc.setServiceImpl("service.impl");
????????mpg.setPackageInfo(pc);


????????//?配置模板
????????TemplateConfig?templateConfig?=?new?TemplateConfig();

????????templateConfig.setXml(null);
????????mpg.setTemplate(templateConfig);

????????//?策略配置
????????StrategyConfig?strategy?=?new?StrategyConfig();
????????strategy.setNaming(NamingStrategy.underline_to_camel);
????????strategy.setColumnNaming(NamingStrategy.underline_to_camel);
????????strategy.setSuperEntityClass("com.baomidou.mybatisplus.extension.activerecord.Model");
????????strategy.setEntityLombokModel(true);
????????strategy.setRestControllerStyle(true);

????????strategy.setEntityLombokModel(true);
????????//?公共父類(lèi)
//????????strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
????????//?寫(xiě)于父類(lèi)中的公共字段
//????????strategy.setSuperEntityColumns("id");
????????strategy.setInclude(scanner("lottery,lottery_item,lottery_prize,lottery_record").split(","));
????????strategy.setControllerMappingHyphenStyle(true);
????????strategy.setTablePrefix(pc.getModuleName()?+?"_");
????????mpg.setStrategy(strategy);
????????mpg.setTemplateEngine(new?FreemarkerTemplateEngine());
????????mpg.execute();
????}

????public?static?String?scanner(String?tip)?{
????????Scanner?scanner?=?new?Scanner(System.in);
????????StringBuilder?help?=?new?StringBuilder();
????????help.append("請(qǐng)輸入"?+?tip?+?":");
????????System.out.println(help.toString());
????????if?(scanner.hasNext())?{
????????????String?ipt?=?scanner.next();
????????????if?(StringUtils.isNotEmpty(ipt))?{
????????????????return?ipt;
????????????}
????????}
????????throw?new?MybatisPlusException("請(qǐng)輸入正確的"?+?tip?+?"!");
????}
}

4.4 Redis 配置

我們?nèi)绻诖a中使用 RedisTemplate 的話,需要添加相關(guān)配置,將其注入到Spring容器中。

@Configuration
public?class?RedisTemplateConfig?{
????@Bean
????public?RedisTemplate?redisTemplate(RedisConnectionFactory?redisConnectionFactory)?{
????????RedisTemplate?redisTemplate?=?new?RedisTemplate<>();
????????redisTemplate.setConnectionFactory(redisConnectionFactory);
????????//?使用Jackson2JsonRedisSerialize?替換默認(rèn)序列化
????????Jackson2JsonRedisSerializer?jackson2JsonRedisSerializer?=?new?Jackson2JsonRedisSerializer(Object.class);

????????ObjectMapper?objectMapper?=?new?ObjectMapper();
????????objectMapper.setVisibility(PropertyAccessor.ALL,?JsonAutoDetect.Visibility.ANY);
????????objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

????????SimpleModule?simpleModule?=?new?SimpleModule();
????????simpleModule.addSerializer(DateTime.class,?new?JodaDateTimeJsonSerializer());
????????simpleModule.addDeserializer(DateTime.class,?new?JodaDateTimeJsonDeserializer());
????????objectMapper.registerModule(simpleModule);

????????jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
????????//?設(shè)置value的序列化規(guī)則和?key的序列化規(guī)則
????????redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
????????redisTemplate.setKeySerializer(new?StringRedisSerializer());

????????redisTemplate.setHashKeySerializer(new?StringRedisSerializer());
????????redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

????????redisTemplate.afterPropertiesSet();
????????return?redisTemplate;
????}

}

class?JodaDateTimeJsonSerializer?extends?JsonSerializer?{
????@Override
????public?void?serialize(DateTime?dateTime,?JsonGenerator?jsonGenerator,?SerializerProvider?serializerProvider)?throws?IOException?{
????????jsonGenerator.writeString(dateTime.toString("yyyy-MM-dd?HH:mm:ss"));
????}
}

class?JodaDateTimeJsonDeserializer?extends?JsonDeserializer?{
????@Override
????public?DateTime?deserialize(JsonParser?jsonParser,?DeserializationContext?deserializationContext)?throws?IOException,?JsonProcessingException?{
????????String?dateString?=?jsonParser.readValueAs(String.class);
????????DateTimeFormatter?dateTimeFormatter?=?DateTimeFormat.forPattern("yyyy-MM-dd?HH:mm:ss");
????????return?dateTimeFormatter.parseDateTime(dateString);
????}
}

4.5 常量管理

由于代碼中會(huì)用到一些共有的常量,我們應(yīng)該將其抽離出來(lái)。

public?class?LotteryConstants?{

????/**
?????*?表示正在抽獎(jiǎng)的用戶標(biāo)記
?????*/
????public?final?static?String?DRAWING?=?"DRAWING";
????
????/**
?????*?活動(dòng)標(biāo)記?LOTTERY:lotteryID
?????*/
????public?final?static?String?LOTTERY?=?"LOTTERY";
????
????/**
?????*?獎(jiǎng)品數(shù)據(jù)??LOTTERY_PRIZE:lotteryID:PrizeId
?????*/
????public?final?static?String?LOTTERY_PRIZE?=?"LOTTERY_PRIZE";
????
????/**
?????*?默認(rèn)獎(jiǎng)品數(shù)據(jù)??DEFAULT_LOTTERY_PRIZE:lotteryID
?????*/
????public?final?static?String?DEFAULT_LOTTERY_PRIZE?=?"DEFAULT_LOTTERY_PRIZE";

????public?enum?PrizeTypeEnum?{
????????THANK(-1),?NORMAL(1),?UNIQUE(2);
????????private?int?value;

????????private?PrizeTypeEnum(int?value)?{
????????????this.value?=?value;
????????}

????????public?int?getValue()?{
????????????return?this.value;
????????}
????}

????/**
?????*?獎(jiǎng)項(xiàng)緩存:LOTTERY_ITEM:LOTTERY_ID
?????*/
????public?final?static?String?LOTTERY_ITEM?=?"LOTTERY_ITEM";
????
????/**
?????*?默認(rèn)獎(jiǎng)項(xiàng):DEFAULT_LOTTERY_ITEM:LOTTERY_ID
?????*/
????public?final?static?String?DEFAULT_LOTTERY_ITEM?=?"DEFAULT_LOTTERY_ITEM";

}
public?enum?ReturnCodeEnum?{

????SUCCESS("0000",?"成功"),

????LOTTER_NOT_EXIST("9001",?"指定抽獎(jiǎng)活動(dòng)不存在"),

????LOTTER_FINISH("9002",?"活動(dòng)已結(jié)束"),

????LOTTER_REPO_NOT_ENOUGHT("9003",?"當(dāng)前獎(jiǎng)品庫(kù)存不足"),

????LOTTER_ITEM_NOT_INITIAL("9004",?"獎(jiǎng)項(xiàng)數(shù)據(jù)未初始化"),

????LOTTER_DRAWING("9005",?"上一次抽獎(jiǎng)還未結(jié)束"),

????REQUEST_PARAM_NOT_VALID("9998",?"請(qǐng)求參數(shù)不正確"),

????SYSTEM_ERROR("9999",?"系統(tǒng)繁忙,請(qǐng)稍后重試");

????private?String?code;

????private?String?msg;

????private?ReturnCodeEnum(String?code,?String?msg)?{
????????this.code?=?code;
????????this.msg?=?msg;
????}

????public?String?getCode()?{
????????return?code;
????}

????public?String?getMsg()?{
????????return?msg;
????}

????public?String?getCodeString()?{
????????return?getCode()?+?"";
????}
}

對(duì)Redis中的key進(jìn)行統(tǒng)一的管理。

public?class?RedisKeyManager?{

????/**
?????*?正在抽獎(jiǎng)的key
?????*
?????*?@param?accountIp
?????*?@return
?????*/
????public?static?String?getDrawingRedisKey(String?accountIp)?{
????????return?new?StringBuilder(LotteryConstants.DRAWING).append(":").append(accountIp).toString();
????}

????/**
?????*?獲取抽獎(jiǎng)活動(dòng)的key
?????*
?????*?@param?id
?????*?@return
?????*/
????public?static?String?getLotteryRedisKey(Integer?id)?{
????????return?new?StringBuilder(LotteryConstants.LOTTERY).append(":").append(id).toString();
????}

????/**
?????*?獲取指定活動(dòng)下的所有獎(jiǎng)品數(shù)據(jù)
?????*
?????*?@param?lotteryId
?????*?@return
?????*/
????public?static?String?getLotteryPrizeRedisKey(Integer?lotteryId)?{
????????return?new?StringBuilder(LotteryConstants.LOTTERY_PRIZE).append(":").append(lotteryId).toString();
????}

????public?static?String?getLotteryPrizeRedisKey(Integer?lotteryId,?Integer?prizeId)?{
????????return?new?StringBuilder(LotteryConstants.LOTTERY_PRIZE).append(":").append(lotteryId).append(":").append(prizeId).toString();
????}

????public?static?String?getDefaultLotteryPrizeRedisKey(Integer?lotteryId)?{
????????return?new?StringBuilder(LotteryConstants.DEFAULT_LOTTERY_PRIZE).append(":").append(lotteryId).toString();
????}

????public?static?String?getLotteryItemRedisKey(Integer?lotteryId)?{
????????return?new?StringBuilder(LotteryConstants.LOTTERY_ITEM).append(":").append(lotteryId).toString();
????}

????public?static?String?getDefaultLotteryItemRedisKey(Integer?lotteryId)?{
????????return?new?StringBuilder(LotteryConstants.DEFAULT_LOTTERY_ITEM).append(":").append(lotteryId).toString();
????}
}

4.6 業(yè)務(wù)代碼

4.6.1 抽獎(jiǎng)接口

我們首先編寫(xiě)抽獎(jiǎng)接口,根據(jù)前臺(tái)傳的參數(shù)查詢到具體的活動(dòng),然后進(jìn)行相應(yīng)的操作。(當(dāng)然,前端直接是寫(xiě)死的/lottery/1)

@GetMapping("/{id}")
public?ResultResp?doDraw(@PathVariable("id")?Integer?id,?HttpServletRequest?request)?{
????String?accountIp?=?CusAccessObjectUtil.getIpAddress(request);
????log.info("begin?LotteryController.doDraw,access?user?{},?lotteryId,{}:",?accountIp,?id);
????ResultResp?resultResp?=?new?ResultResp<>();
????try?{
????????//判斷當(dāng)前用戶上一次抽獎(jiǎng)是否結(jié)束
????????checkDrawParams(id,?accountIp);

????????//抽獎(jiǎng)
????????DoDrawDto?dto?=?new?DoDrawDto();
????????dto.setAccountIp(accountIp);
????????dto.setLotteryId(id);
????????lotteryService.doDraw(dto);

????????//返回結(jié)果設(shè)置
????????resultResp.setCode(ReturnCodeEnum.SUCCESS.getCode());
????????resultResp.setMsg(ReturnCodeEnum.SUCCESS.getMsg());
????????//對(duì)象轉(zhuǎn)換
????????resultResp.setResult(lotteryConverter.dto2LotteryItemVo(dto));
????}?catch?(Exception?e)?{
????????return?ExceptionUtil.handlerException4biz(resultResp,?e);
????}?finally?{
????????//清除占位標(biāo)記
????????redisTemplate.delete(RedisKeyManager.getDrawingRedisKey(accountIp));
????}
????return?resultResp;
}

private?void?checkDrawParams(Integer?id,?String?accountIp)?{
????if?(null?==?id)?{
????????throw?new?RewardException(ReturnCodeEnum.REQUEST_PARAM_NOT_VALID.getCode(),?ReturnCodeEnum.REQUEST_PARAM_NOT_VALID.getMsg());
????}
????//采用setNx命令,判斷當(dāng)前用戶上一次抽獎(jiǎng)是否結(jié)束
????Boolean?result?=?redisTemplate.opsForValue().setIfAbsent(RedisKeyManager.getDrawingRedisKey(accountIp),?"1",?60,?TimeUnit.SECONDS);
????//如果為false,說(shuō)明上一次抽獎(jiǎng)還未結(jié)束
????if?(!result)?{
????????throw?new?RewardException(ReturnCodeEnum.LOTTER_DRAWING.getCode(),?ReturnCodeEnum.LOTTER_DRAWING.getMsg());
????}
}

為了避免用戶重復(fù)點(diǎn)擊抽獎(jiǎng),所以我們通過(guò)Redis來(lái)避免這種問(wèn)題,用戶每次抽獎(jiǎng)的時(shí)候,通過(guò)setNx給用戶排隊(duì)并設(shè)置過(guò)期時(shí)間;如果用戶點(diǎn)擊多次抽獎(jiǎng),Redis設(shè)置值的時(shí)候發(fā)現(xiàn)該用戶上次抽獎(jiǎng)還未結(jié)束則拋出異常。

最后用戶抽獎(jiǎng)成功的話,記得清除該標(biāo)記,從而用戶能夠繼續(xù)抽獎(jiǎng)。

4.6.2 初始化數(shù)據(jù)

從抽獎(jiǎng)入口進(jìn)來(lái),校驗(yàn)成功以后則開(kāi)始業(yè)務(wù)操作。

@Override
public?void?doDraw(DoDrawDto?drawDto)?throws?Exception?{
????RewardContext?context?=?new?RewardContext();
????LotteryItem?lotteryItem?=?null;
????try?{
????????//JUC工具?需要等待線程結(jié)束之后才能運(yùn)行
????????CountDownLatch?countDownLatch?=?new?CountDownLatch(1);
????????//判斷活動(dòng)有效性
????????Lottery?lottery?=?checkLottery(drawDto);
????????//發(fā)布事件,用來(lái)加載指定活動(dòng)的獎(jiǎng)品信息
????????applicationContext.publishEvent(new?InitPrizeToRedisEvent(this,?lottery.getId(),?countDownLatch));
????????//開(kāi)始抽獎(jiǎng)
????????lotteryItem?=?doPlay(lottery);
????????//記錄獎(jiǎng)品并扣減庫(kù)存
????????countDownLatch.await();?//等待獎(jiǎng)品初始化完成
????????String?key?=?RedisKeyManager.getLotteryPrizeRedisKey(lottery.getId(),?lotteryItem.getPrizeId());
????????int?prizeType?=?Integer.parseInt(redisTemplate.opsForHash().get(key,?"prizeType").toString());
????????context.setLottery(lottery);
????????context.setLotteryItem(lotteryItem);
????????context.setAccountIp(drawDto.getAccountIp());
????????context.setKey(key);
????????//調(diào)整庫(kù)存及記錄中獎(jiǎng)信息
????????AbstractRewardProcessor.rewardProcessorMap.get(prizeType).doReward(context);
????}?catch?(UnRewardException?u)?{?//表示因?yàn)槟承﹩?wèn)題未中獎(jiǎng),返回一個(gè)默認(rèn)獎(jiǎng)項(xiàng)
????????context.setKey(RedisKeyManager.getDefaultLotteryPrizeRedisKey(lotteryItem.getLotteryId()));
????????lotteryItem?=?(LotteryItem)?redisTemplate.opsForValue().get(RedisKeyManager.getDefaultLotteryItemRedisKey(lotteryItem.getLotteryId()));
????????context.setLotteryItem(lotteryItem);
????????AbstractRewardProcessor.rewardProcessorMap.get(LotteryConstants.PrizeTypeEnum.THANK.getValue()).doReward(context);
????}
????//拼接返回?cái)?shù)據(jù)
????drawDto.setLevel(lotteryItem.getLevel());
????drawDto.setPrizeName(context.getPrizeName());
????drawDto.setPrizeId(context.getPrizeId());
}

首先我們通過(guò)CountDownLatch來(lái)保證商品初始化的順序,關(guān)于CountDownLatch可以查看 JUC工具 該文章。

然后我們需要檢驗(yàn)一下活動(dòng)的有效性,確保活動(dòng)未結(jié)束。

檢驗(yàn)活動(dòng)通過(guò)后則通過(guò)ApplicationEvent 事件實(shí)現(xiàn)獎(jiǎng)品數(shù)據(jù)的加載,將其存入Redis中。或者通過(guò)ApplicationRunner在程序啟動(dòng)時(shí)獲取相關(guān)數(shù)據(jù)。我們這使用的是事件機(jī)制。ApplicationRunner 的相關(guān)代碼在下文我也順便貼出。

事件機(jī)制

public?class?InitPrizeToRedisEvent?extends?ApplicationEvent?{

????private?Integer?lotteryId;

????private?CountDownLatch?countDownLatch;

????public?InitPrizeToRedisEvent(Object?source,?Integer?lotteryId,?CountDownLatch?countDownLatch)?{
????????super(source);
????????this.lotteryId?=?lotteryId;
????????this.countDownLatch?=?countDownLatch;
????}

????public?Integer?getLotteryId()?{
????????return?lotteryId;
????}

????public?void?setLotteryId(Integer?lotteryId)?{
????????this.lotteryId?=?lotteryId;
????}

????public?CountDownLatch?getCountDownLatch()?{
????????return?countDownLatch;
????}

????public?void?setCountDownLatch(CountDownLatch?countDownLatch)?{
????????this.countDownLatch?=?countDownLatch;
????}

}

有了事件機(jī)制,我們還需要一個(gè)監(jiān)聽(tīng)事件,用來(lái)初始化相關(guān)數(shù)據(jù)信息。具體業(yè)務(wù)邏輯大家可以參考下代碼,有相關(guān)的注釋信息,主要就是將數(shù)據(jù)庫(kù)中的數(shù)據(jù)添加進(jìn)redis中,需要注意的是,我們?yōu)榱吮WC原子性,是通過(guò)HASH來(lái)存儲(chǔ)數(shù)據(jù)的,這樣之后庫(kù)存扣減的時(shí)候就可以通過(guò)opsForHash來(lái)保證其原子性。

當(dāng)初始化獎(jiǎng)品信息之后,則通過(guò)countDown()方法表名執(zhí)行完成,業(yè)務(wù)代碼中線程阻塞的地方可以繼續(xù)執(zhí)行了。

@Slf4j
@Component
public?class?InitPrizeToRedisListener?implements?ApplicationListener?{

????@Autowired
????RedisTemplate?redisTemplate;

????@Autowired
????LotteryPrizeMapper?lotteryPrizeMapper;

????@Autowired
????LotteryItemMapper?lotteryItemMapper;

????@Override
????public?void?onApplicationEvent(InitPrizeToRedisEvent?initPrizeToRedisEvent)?{
????????log.info("begin?InitPrizeToRedisListener,"?+?initPrizeToRedisEvent);
????????Boolean?result?=?redisTemplate.opsForValue().setIfAbsent(RedisKeyManager.getLotteryPrizeRedisKey(initPrizeToRedisEvent.getLotteryId()),?"1");
????????//已經(jīng)初始化到緩存中了,不需要再次緩存
????????if?(!result)?{
????????????log.info("already?initial");
????????????initPrizeToRedisEvent.getCountDownLatch().countDown();
????????????return;
????????}
????????QueryWrapper?lotteryItemQueryWrapper?=?new?QueryWrapper<>();
????????lotteryItemQueryWrapper.eq("lottery_id",?initPrizeToRedisEvent.getLotteryId());
????????List?lotteryItems?=?lotteryItemMapper.selectList(lotteryItemQueryWrapper);

????????//如果指定的獎(jiǎng)品沒(méi)有了,會(huì)生成一個(gè)默認(rèn)的獎(jiǎng)項(xiàng)
????????LotteryItem?defaultLotteryItem?=?lotteryItems.parallelStream().filter(o?->?o.getDefaultItem().intValue()?==?1).findFirst().orElse(null);

????????Map?lotteryItemMap?=?new?HashMap<>(16);
????????lotteryItemMap.put(RedisKeyManager.getLotteryItemRedisKey(initPrizeToRedisEvent.getLotteryId()),?lotteryItems);
????????lotteryItemMap.put(RedisKeyManager.getDefaultLotteryItemRedisKey(initPrizeToRedisEvent.getLotteryId()),?defaultLotteryItem);
????????redisTemplate.opsForValue().multiSet(lotteryItemMap);

????????QueryWrapper?queryWrapper?=?new?QueryWrapper();
????????queryWrapper.eq("lottery_id",?initPrizeToRedisEvent.getLotteryId());
????????List?lotteryPrizes?=?lotteryPrizeMapper.selectList(queryWrapper);

????????//保存一個(gè)默認(rèn)獎(jiǎng)項(xiàng)
????????AtomicReference?defaultPrize?=?new?AtomicReference<>();
????????lotteryPrizes.stream().forEach(lotteryPrize?->?{
????????????if?(lotteryPrize.getId().equals(defaultLotteryItem.getPrizeId()))?{
????????????????defaultPrize.set(lotteryPrize);
????????????}
????????????String?key?=?RedisKeyManager.getLotteryPrizeRedisKey(initPrizeToRedisEvent.getLotteryId(),?lotteryPrize.getId());
????????????setLotteryPrizeToRedis(key,?lotteryPrize);
????????});
????????String?key?=?RedisKeyManager.getDefaultLotteryPrizeRedisKey(initPrizeToRedisEvent.getLotteryId());
????????setLotteryPrizeToRedis(key,?defaultPrize.get());
????????initPrizeToRedisEvent.getCountDownLatch().countDown();?//表示初始化完成
????????log.info("finish?InitPrizeToRedisListener,"?+?initPrizeToRedisEvent);
????}

????private?void?setLotteryPrizeToRedis(String?key,?LotteryPrize?lotteryPrize)?{
????????redisTemplate.setHashValueSerializer(new?Jackson2JsonRedisSerializer<>(Object.class));
????????redisTemplate.opsForHash().put(key,?"id",?lotteryPrize.getId());
????????redisTemplate.opsForHash().put(key,?"lotteryId",?lotteryPrize.getLotteryId());
????????redisTemplate.opsForHash().put(key,?"prizeName",?lotteryPrize.getPrizeName());
????????redisTemplate.opsForHash().put(key,?"prizeType",?lotteryPrize.getPrizeType());
????????redisTemplate.opsForHash().put(key,?"totalStock",?lotteryPrize.getTotalStock());
????????redisTemplate.opsForHash().put(key,?"validStock",?lotteryPrize.getValidStock());
????}
}

上面部分是通過(guò)事件的方法來(lái)初始化數(shù)據(jù),下面我們說(shuō)下ApplicationRunner的方式:

這種方式很簡(jiǎn)單,在項(xiàng)目啟動(dòng)的時(shí)候?qū)?shù)據(jù)加載進(jìn)去即可。

我們只需要實(shí)現(xiàn)ApplicationRunner接口即可,然后在run方法中從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)加載到Redis中。

@Slf4j
@Component
public?class?LoadDataApplicationRunner?implements?ApplicationRunner?{


????@Autowired
????RedisTemplate?redisTemplate;

????@Autowired
????LotteryMapper?lotteryMapper;

????@Override
????public?void?run(ApplicationArguments?args)?throws?Exception?{
????????log.info("=========begin?load?lottery?data?to?Redis===========");
????????//加載當(dāng)前抽獎(jiǎng)活動(dòng)信息
????????Lottery?lottery?=?lotteryMapper.selectById(1);

????????log.info("=========finish?load?lottery?data?to?Redis===========");
????}
}

4.6.3 抽獎(jiǎng)

我們?cè)谑褂檬录M(jìn)行數(shù)據(jù)初始化的時(shí)候,可以同時(shí)進(jìn)行抽獎(jiǎng)操作,但是注意的是這個(gè)時(shí)候需要使用countDownLatch.await();來(lái)阻塞當(dāng)前線程,等待數(shù)據(jù)初始化完成。

在抽獎(jiǎng)的過(guò)程中,我們首先嘗試從Redis中獲取相關(guān)數(shù)據(jù),如果Redis中沒(méi)有則從數(shù)據(jù)庫(kù)中加載數(shù)據(jù),如果數(shù)據(jù)庫(kù)中也沒(méi)查詢到相關(guān)數(shù)據(jù),則表明相關(guān)的數(shù)據(jù)沒(méi)有配置完成。

獲取數(shù)據(jù)之后,我們就該開(kāi)始抽獎(jiǎng)了。抽獎(jiǎng)的核心在于隨機(jī)性以及概率性,咱們總不能隨便抽抽都能抽到一等獎(jiǎng)吧?所以我們需要在表中設(shè)置每個(gè)獎(jiǎng)項(xiàng)的概率性。如下所示:

在我們抽獎(jiǎng)的時(shí)候需要根據(jù)概率劃分處相關(guān)區(qū)間。我們可以通過(guò)Debug的方式來(lái)查看一下具體怎么劃分的:

獎(jiǎng)項(xiàng)的概率越大,區(qū)間越大;大家看到的順序是不同的,由于我們?cè)谏厦嫱ㄟ^(guò)Collections.shuffle(lotteryItems);將集合打亂了,所以這里看到的不是順序展示的。

在生成對(duì)應(yīng)區(qū)間后,我們通過(guò)生成隨機(jī)數(shù),看隨機(jī)數(shù)落在那個(gè)區(qū)間中,然后將對(duì)應(yīng)的獎(jiǎng)項(xiàng)返回。這就實(shí)現(xiàn)了我們的抽獎(jiǎng)過(guò)程。

private?LotteryItem?doPlay(Lottery?lottery)?{
????LotteryItem?lotteryItem?=?null;
????QueryWrapper?queryWrapper?=?new?QueryWrapper<>();
????queryWrapper.eq("lottery_id",?lottery.getId());
????Object?lotteryItemsObj?=?redisTemplate.opsForValue().get(RedisKeyManager.getLotteryItemRedisKey(lottery.getId()));
????List?lotteryItems;
????//說(shuō)明還未加載到緩存中,同步從數(shù)據(jù)庫(kù)加載,并且異步將數(shù)據(jù)緩存
????if?(lotteryItemsObj?==?null)?{
????????lotteryItems?=?lotteryItemMapper.selectList(queryWrapper);
????}?else?{
????????lotteryItems?=?(List)?lotteryItemsObj;
????}
????//獎(jiǎng)項(xiàng)數(shù)據(jù)未配置
????if?(lotteryItems.isEmpty())?{
????????throw?new?BizException(ReturnCodeEnum.LOTTER_ITEM_NOT_INITIAL.getCode(),?ReturnCodeEnum.LOTTER_ITEM_NOT_INITIAL.getMsg());
????}
????int?lastScope?=?0;
????Collections.shuffle(lotteryItems);
????Map?awardItemScope?=?new?HashMap<>();
????//item.getPercent=0.05?=?5%
????for?(LotteryItem?item?:?lotteryItems)?{
????????int?currentScope?=?lastScope?+?new?BigDecimal(item.getPercent().floatValue()).multiply(new?BigDecimal(mulriple)).intValue();
????????awardItemScope.put(item.getId(),?new?int[]{lastScope?+?1,?currentScope});
????????lastScope?=?currentScope;
????}
????int?luckyNumber?=?new?Random().nextInt(mulriple);
????int?luckyPrizeId?=?0;
????if?(!awardItemScope.isEmpty())?{
????????Set>?set?=?awardItemScope.entrySet();
????????for?(Map.Entry?entry?:?set)?{
????????????if?(luckyNumber?>=?entry.getValue()[0]?&&?luckyNumber?<=?entry.getValue()[1])?{
????????????????luckyPrizeId?=?entry.getKey();
????????????????break;
????????????}
????????}
????}
????for?(LotteryItem?item?:?lotteryItems)?{
????????if?(item.getId().intValue()?==?luckyPrizeId)?{
????????????lotteryItem?=?item;
????????????break;
????????}
????}
????return?lotteryItem;
}

4.6.4 調(diào)整庫(kù)存及記錄

在調(diào)整庫(kù)存的時(shí)候,我們需要考慮到每個(gè)獎(jiǎng)品類(lèi)型的不同,根據(jù)不同類(lèi)型的獎(jiǎng)品采取不同的措施。比如如果是一些價(jià)值高昂的獎(jiǎng)品,我們需要通過(guò)分布式鎖來(lái)確保安全性;或者比如有些商品我們需要發(fā)送相應(yīng)的短信;所以我們需要采取一種具有擴(kuò)展性的實(shí)現(xiàn)機(jī)制。

具體的實(shí)現(xiàn)機(jī)制可以看下方的類(lèi)圖,我首先定義一個(gè)獎(jiǎng)品方法的接口(RewardProcessor),然后定義一個(gè)抽象類(lèi)(AbstractRewardProcessor),抽象類(lèi)中定義了模板方法,然后我們就可以根據(jù)不同的類(lèi)型創(chuàng)建不同的處理器即可,這大大加強(qiáng)了我們的擴(kuò)展性。

比如我們這邊就創(chuàng)建了庫(kù)存充足處理器及庫(kù)存不足處理器。

接口:

public?interface?RewardProcessor?{

????void?doReward(RewardContext?context);

}

抽象類(lèi):

@Slf4j
public?abstract?class?AbstractRewardProcessor?implements?RewardProcessor,?ApplicationContextAware?{

????public?static?Map?rewardProcessorMap?=?new?ConcurrentHashMap();

????@Autowired
????protected?RedisTemplate?redisTemplate;

????private?void?beforeProcessor(RewardContext?context)?{
????}

????@Override
????public?void?doReward(RewardContext?context)?{
????????beforeProcessor(context);
????????processor(context);
????????afterProcessor(context);
????}

????protected?abstract?void?afterProcessor(RewardContext?context);


????/**
?????*?發(fā)放對(duì)應(yīng)的獎(jiǎng)品
?????*
?????*?@param?context
?????*/
????protected?abstract?void?processor(RewardContext?context);

????/**
?????*?返回當(dāng)前獎(jiǎng)品類(lèi)型
?????*
?????*?@return
?????*/
????protected?abstract?int?getAwardType();

????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????rewardProcessorMap.put(LotteryConstants.PrizeTypeEnum.THANK.getValue(),?(RewardProcessor)?applicationContext.getBean(NoneStockRewardProcessor.class));
????????rewardProcessorMap.put(LotteryConstants.PrizeTypeEnum.NORMAL.getValue(),?(RewardProcessor)?applicationContext.getBean(HasStockRewardProcessor.class));
????}
}

我們可以從抽象類(lèi)中的doReward方法處開(kāi)始查看,比如我們這邊先查看庫(kù)存充足處理器中的代碼:

庫(kù)存處理器執(zhí)行的時(shí)候首相將Redis中對(duì)應(yīng)的獎(jiǎng)項(xiàng)庫(kù)存減1,這時(shí)候是不需要加鎖的,因?yàn)檫@個(gè)操作是原子性的。

當(dāng)扣減后,我們根據(jù)返回的值判斷商品庫(kù)存是否充足,這個(gè)時(shí)候庫(kù)存不足則提示未中獎(jiǎng)或者返回一個(gè)默認(rèn)商品。

最后我們還需要記得更新下數(shù)據(jù)庫(kù)中的相關(guān)數(shù)據(jù)。

@Override
protected?void?processor(RewardContext?context)?{
????//扣減庫(kù)存(redis的更新)
????Long?result?=?redisTemplate.opsForHash().increment(context.getKey(),?"validStock",?-1);
????//當(dāng)前獎(jiǎng)品庫(kù)存不足,提示未中獎(jiǎng),或者返回一個(gè)兜底的獎(jiǎng)品
????if?(result.intValue()?????????throw?new?UnRewardException(ReturnCodeEnum.LOTTER_REPO_NOT_ENOUGHT.getCode(),?ReturnCodeEnum.LOTTER_REPO_NOT_ENOUGHT.getMsg());
????}
????List?propertys?=?Arrays.asList("id",?"prizeName");
????List?prizes?=?redisTemplate.opsForHash().multiGet(context.getKey(),?propertys);
????context.setPrizeId(Integer.parseInt(prizes.get(0).toString()));
????context.setPrizeName(prizes.get(1).toString());
????//更新庫(kù)存(數(shù)據(jù)庫(kù)的更新)
????lotteryPrizeMapper.updateValidStock(context.getPrizeId());
}

方法執(zhí)行完成之后,我們需要執(zhí)行afterProcessor方法:

這個(gè)地方我們是通過(guò)異步任務(wù)異步存入抽獎(jiǎng)記錄信息。

@Override
protected?void?afterProcessor(RewardContext?context)?{
????asyncLotteryRecordTask.saveLotteryRecord(context.getAccountIp(),?context.getLotteryItem(),?context.getPrizeName());
}

在這邊我們可以發(fā)現(xiàn)是通過(guò)Async注解,指定一個(gè)線程池,開(kāi)啟一個(gè)異步執(zhí)行的方法。

@Slf4j
@Component
public?class?AsyncLotteryRecordTask?{

????@Autowired
????LotteryRecordMapper?lotteryRecordMapper;

????@Async("lotteryServiceExecutor")
????public?void?saveLotteryRecord(String?accountIp,?LotteryItem?lotteryItem,?String?prizeName)?{
????????log.info(Thread.currentThread().getName()?+?"---saveLotteryRecord");
????????//存儲(chǔ)中獎(jiǎng)信息
????????LotteryRecord?record?=?new?LotteryRecord();
????????record.setAccountIp(accountIp);
????????record.setItemId(lotteryItem.getId());
????????record.setPrizeName(prizeName);
????????record.setCreateTime(LocalDateTime.now());
????????lotteryRecordMapper.insert(record);
????}
}

創(chuàng)建一個(gè)線程池:相關(guān)的配置信息是我們定義在YML文件中的數(shù)據(jù)。

@Configuration
@EnableAsync
@EnableConfigurationProperties(ThreadPoolExecutorProperties.class)
public?class?ThreadPoolExecutorConfig?{

????@Bean(name?=?"lotteryServiceExecutor")
????public?Executor?lotteryServiceExecutor(ThreadPoolExecutorProperties?poolExecutorProperties)?{
????????ThreadPoolTaskExecutor?executor?=?new?ThreadPoolTaskExecutor();
????????executor.setCorePoolSize(poolExecutorProperties.getCorePoolSize());
????????executor.setMaxPoolSize(poolExecutorProperties.getMaxPoolSize());
????????executor.setQueueCapacity(poolExecutorProperties.getQueueCapacity());
????????executor.setThreadNamePrefix(poolExecutorProperties.getNamePrefix());
????????executor.setRejectedExecutionHandler(new?ThreadPoolExecutor.CallerRunsPolicy());
????????return?executor;
????}
}
@Data
@ConfigurationProperties(prefix?=?"async.executor.thread")
public?class?ThreadPoolExecutorProperties?{
????private?int?corePoolSize;
????private?int?maxPoolSize;
????private?int?queueCapacity;
????private?String?namePrefix;
}

4.7 總結(jié)

以上便是整個(gè)項(xiàng)目的搭建,關(guān)于前端界面無(wú)非就是向后端發(fā)起請(qǐng)求,根據(jù)返回的獎(jiǎng)品信息,將指針落在對(duì)應(yīng)的轉(zhuǎn)盤(pán)位置處,具體代碼可以前往項(xiàng)目地址查看。希望大家可以動(dòng)個(gè)小手點(diǎn)點(diǎn)贊,嘻嘻。

5. 項(xiàng)目地址

https://gitee.com/cl1429745331/redis-demo

如果直接使用項(xiàng)目的話,記得修改數(shù)據(jù)庫(kù)中活動(dòng)的結(jié)束時(shí)間。

具體的實(shí)戰(zhàn)項(xiàng)目在lottery工程中。

瀏覽 46
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)
評(píng)論
圖片
表情
推薦
點(diǎn)贊
評(píng)論
收藏
分享

手機(jī)掃一掃分享

分享
舉報(bào)

感谢您访问我们的网站,您可能还对以下资源感兴趣:

国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 久久色资源| 国产精品自拍视频| 天天爽视频| 久久女女| 激情成人五月天| www.97av| 国产免费AV在线观看| 亚洲一级Av无码毛片久久精品| 五月婷婷六月丁香综合| 免费观看一级毛一片| 怕怕怕视频| 熟女导航| 国产伦子伦一级A片在线| 亚洲精品无码更新| 女女女女女女BBBBBB手| 亚洲男女av| 亚洲小电影| 成人免费毛片AAAAAA片| 在线观看黄色av| 亚洲精品国产成人综合久久久久久久久 | 日本親子亂子倫XXXX50路| 日产精品久久久久| 久久黄色的| 黄色av免费网站| 大地资源中文第二页导读内容| 亚洲AV综合色区无码国产播放| 欧美国产日韩欧美亚洲国产| 精品视频一区二区三区四区| 豆花视频| 国产日韩精品无码去免费专区国产 | 人妻精品| 国产毛片视频| 国产在线a| 性日韩| 日本高清无码视频| 欧一美一色一伦一A片| 美女乱伦视频| 国产无遮挡又黄又爽又色视频| 青青久视频| 亚洲成人视频网| 婷婷好色五月天| 你懂的久久| 鸭子AV| 天天影视综合网免费观看电视剧国产| 一本在线| 亚洲av免费| 毛片一区二区三区| 中文av字幕| 亚洲高清无码一区| 亚洲无码专区在线| 精品无码电影| 日韩一区二区免费视频| AV一区二区三区四区| 丰满人妻无码| 欧美XXX视频| 超碰免费观看| 欧美日韩一级A片| 亚洲v在线观看| av无码免费| 久久中文字幕视频| 久操免费观看| 91香蕉视频免费在线观看| 成人毛片AV无码| 久热激情| 无码人妻精品一区二区蜜桃漫画| 亚洲天堂无码AV| 日韩欧美国产综合| 另类老妇奶性BBWBBwBBw| 一区二区三区www污污污网站| 欧美亚洲国产一区二区三区| www黄色片| 日本国产在线视频| 国产在线一区二区三区四区| 天天日少妇| 美女一级A片| 黄色A级视频| 九九亚洲精品| 亚洲综合色网站| 蜜臀久久99精品久久久| 中文字幕在线免费观看电影| 一插菊花综合| 青娱乐Av| 亚洲人成色777777无码| 99ri精品| 黄色AV免费在线观看| 午夜天堂精品久久| 在线观看视频黄| 国产熟妇婬乱A片免费看牛牛| 国产一级a毛一级a做免费高清视频| 蝌蚪窝视频在线| 就要草| 你懂的在线观看| 一区二区三区无码在线| 成人免费观看视频| 自拍偷拍综合| 久久免费黄色视频| 青青草原av| 亚洲激情黑人| 91无码一区二区三区在线| 91白浆肆意四溢456| 国产三级AV在线观看| 久久六月天| 无码人妻精品一区二区三区温州 | 亚洲AV综合色区无码国产播放| 人人澡人人爽欧一区| 亚洲aaaaaa| 亚洲精品久久久久久久蜜桃| 一级黄色电影免费在线观看| 一个人看的www日本高清视频| 日韩欧美在线中文字幕| 337P人体美鮑高清| 精品视频91| 人人综合| 97久久久| 成人无码在线播放| 午夜性爽视频男人的天堂| 蝌蚪AV| 牛牛精品一区二区AV| 四川BBB搡BBB爽爽爽欧美| 高潮91PORN蝌蚪九色| 国产无码操逼视频| 中文字幕乱码无码人妻系列蜜桃 | 中文字幕乱码中文乱码91| 亚洲精品成人无码AV在线| 欧美日韩国产成人在线| 人妻精品一区二区| 男人的天堂黄色| 69精品视频| 色色天堂成人电影| 久久九九免费视频| 大色鬼在线天堂精品| 99热精品免费| 91亚洲精品国偷拍自产在线观看 | 亚洲日韩在线播放| 婷婷九月色| 国产婬片一级A片AAA毛片AⅤ| 一本久道无码| 激情综合网站| 黄片网站在线看| 色婷婷狠狠| 日韩人妻无码专区| 韩国高清无码60.70.80| AV无码资源| 免费高清无码视频在线观看| 91丨国产丨白浆| 噜噜噜色| 成人国产精品视频| 好叼操| 肏少妇女情人大骚逼直播一区二区| 777欧美| 樱桃Av| www.99在线| 丁香五月婷婷啪啪| 国产在线无码视频| 黄片视频免费在线观看| 91成人情欲影视网| 一级视频免费观看| 色综合久久88色综合| 九一国产| 欧美特黄AAAAAAAAA片| 国产一级婬片A片| 欧美高清在线综合| 国产一级A片视频| 9I成人免费版视频| 五月婷婷色色色| 日韩国产一区二区| 久久爆乳一区二区三区| 欧美性爱精品一区| 91视频免费在线看| 刘玥精品国产一区二区三区| 黄色视频小说| 亚洲精品国产AV| 欧洲黑种人日P视频| 亚洲在线视频网站| 51妺嘿嘿午夜福利视频| 亚洲自拍网站| 中文字幕av无码| 天天干夜夜操| 国产午夜福利视频在线观看 | 国产日B| 色欲91| 丁香五月影院| 日韩综合在线| 日逼小视频| 日欧内射| 人妻体内射精一区二区三区| 亚洲精品AⅤ一区二| 脓肿是什么原因引起的,该怎么治疗| 中文字幕亚洲观看| 桃花岛tⅴ+亚洲品质| 国产一二区| 山东wBBBB搡wBBBB| 小泽玛利亚一区二区免费| 精品久久无码中文字幕| 亚洲三级无码视频| 色色网的五月天| 亚洲ww| 中文字幕网址在线| 亚洲133| 大BBBw大BBBW另类| 亚欧洲精品在线视频| 亚洲成人网在线观看| 爽好紧别夹喷水无码| 少妇搡BBBB搡BBB搡打电话| 日本高清久久| 无码日韩视频| 亚洲特黄| 人妻熟女视频| AV无码一区二区三区| 国产黄色免费观看| 亚洲中文字幕第一页| 玖玖热在线视频| 中文字幕少妇| 亚洲无码精品久久| 狠狠干综合| 无码视频日韩| 操鸡巴网站| 99免费精品视频| 淫乱人妻| 色婷婷狠| 欧美性性生交XXXXX无码| 悠悠色影院| 日韩无码高清免费视频| 欧美午夜性爱视频| www.熟女| 久操免费视频| 国产成人V在线精品一区| 97亚洲综合| 色婷婷视频在线| 成人免费区一区二区三区| 骚逼日本| 中文字幕无码亚| 乱伦五月天| 粉嫩99精品99久久久久久特污兔| 99热这里只有精| 免费在线观看黄色| 亚洲天堂在线观看网站| 久久婷婷婬片A片AAA| 在线无码视频播放| 男女操逼视频网站免费观看 | 五月天婷婷导航| 国产高清不卡| 在线免费AV片| 日本成人电影在线观看| 另类在线| 亚洲无码一卡| 69式荫蒂被添全过程| 国产久久在线| 国产精品成人69| 四川BBB搡BBB爽爽爽电影| 中文字幕日韩在线视频| 先锋影音资源站av每日资源在线| 亚洲AV成人无码一区二区三区 | 亚洲最大三级片| 国产亚洲精品久久久久久桃色| 中文字幕第12页| 精品无码一| 國產精品777777777| 成人午夜毛片| 精品无码久久久久久久久app| 嫩BBB槡BBBB槡BBBB百度 | 国产天堂在线观看| 猛男大粗猛爽H男人味| 人人操碰成人网| 91视频网站免费观看| 免费看的毛片| 午夜亚洲福利视频| 午夜在线免费视频| 成人免费视频18| 91麻豆一区二区| 麻豆md0049免费| 欧美在线天堂| 成人在线18| 欧美一区不卡| 怡春院综合成人社区| 久久人人操人人| 三级片无码在线播放| 欧美日本一区二区三区| 一本色道久久综合无码| 日韩视频在线免费观看| 一级黄片免费视频| 牛牛影视av| 日韩黄色免费视频| ww毛片| 无码一道本| 嘿嘿午夜影院| 国产黄片免费在线观看| 黄频美女日本免费| 欧美成年人视频| 亚洲一级黄| www.毛片| 一级日逼片| 看毛片视频| 亚洲一级视频在线观看| 无码人妻一区二区三区免费九色| 日韩精品毛片| 亚洲AV无码成人精品涩涩麻豆| 中文原创麻豆传媒md0052| 豆花天天吃最新视频| 国产精品久久777777| 亚洲成年人在线| 91色噜噜狠狠色婷婷| 亚洲色婷婷久久精品AV蜜桃| 嫰BBB槡BBBB槡BBBB| 欧美一区| 欧美亚洲成人电影| 青青草视频在线观看| 蜜桃91精品| 人人摸人人操人人| 青青草综合网| 日韩三级片网址| 日批网站视频| 亚洲国产精品成人综合色五月| 中文字幕日本欧美| 天码人妻一区二区三区在线看| 欧美成人综合| 日韩美女毛片| 2025无码视频| 久久99精品视频| 日韩无码黄色电影| 中文无码一区二区三区| 91香蕉在线观看| 欧美爱爱免费看| 97免费在线观看视频| 成人亚洲在线| 亚洲精品国产精品乱码视99| 中文字幕黄色片| 玩弄小怮女在线观看| 无码人妻AⅤ一区二区三区| 91天天射| 青青伊人网| 超碰97人人爱| 国产一区二区在线视频| 翔田千里无码免费播放| 成人性爱视频在线观看| 人人舔人人爱| 西西444WWW大胆无视频软件亮点| 91就去干| 日韩午夜精品| 91久久人澡人妻人人澡人人爽| 国产精品91视频| 在线中文字幕第一页| 色五月天婷婷| 97av视频| 亚洲免费网| 国产福利在线导航| 人人射人人操| 无码欧精品亚洲日韩一区| 欧美性爱无码| 日韩欧美高清无码| 亚洲免费成人| 日韩欧美在线一区| 三级片在线看| 国内精品久久久久久久久98| 亚洲三级无码| www.久久久| 懂色中国闺密偷情懂色AV| 中文字幕在线观看日本| 中文字幕一区二区蜜桃| 亚洲成人五月天| 欧美激情性爱网站| 亚洲专区中文字幕| 亚洲欧美高清视频| 欧美熟妇精品黑人巨大一二三区 | 51成人精品午夜福利| 成人乱无码AV在线观看| 秋霞午夜久久| 亚洲视频一区二区| 欧美日韩中文字幕视频| 色老板在线观看| 人人妻人人澡人人爽人人爽| 国产探花视频在线免费观看| 免费在线观看黄色片| 欧美精品在线免费| 伊人视频在线观看| 操女人的网站| 人妻免费视频| 影音先锋av在线资源| 黄色免费网站| 国产网址| 日韩vA| 免费看黄片,在线观看| 亚洲精品在线观看免费| 黄色亚洲| 欧美成人无码一区二区三区| 亚洲免费观看| 日本高清无码在线观看| 日韩黄色电影在线| 日韩人妻精品无码制服| 国产一级网站| 久久精品人妻| 国产成人高清视频| 麻豆91久久久| 久久久久久久亚洲| 日韩国产传媒| 中文字幕免费高清| 欧美va在线| 精品乱子伦一区二区三区毛| 影音先锋日韩| 成人做爰100片免费视频| 99热这里只有精品9| 大香蕉在线看| 欧洲毛片基地c区| 久久AV影院| 天天爽夜夜爽夜夜爽| 插进去综合网| 亚洲黄色视频网站在线观看| 91探花精品偷拍在线播放| 成人肏逼视频| 欧美日韩国产免费观看成人片| 色一区二区| 91抽插| 久久久久亚洲AV成人片乱码| 操b在线观看| 极品少妇av| 骚白虎一区| 国产夫妻精品| 五月天操逼网站| 久热伊人| 国产无码中文| 国产男女无套免费| 成人在线观看网站| 亚洲v在线观看| 9l视频自拍蝌蚪9l视频成人| 欧美国产综合| 亚欧洲精品在线视频| 午夜精品久久久久久久久无码99热 | 91精品午夜少妇| 国产成人高清| 青榴社区| 午夜视频18| 国产激情AV| 欧美嗯啊| 大鸡巴久久久久| 九九99久久| 久久极品| 偷拍亚洲综合| www.天天干| 我要操网站| 99久久人妻无码中文字幕系列| 国产日韩欧美| 人人澡人人澡人人| 干老女人逼| 日韩操逼视频| www.91国产| 国产精品久久久久无码AV| 成人播放视频| 91人妻在线视频| 极品少妇久久久| 四虎激情影院| 成人视频免费网站| 97亚洲综合| www.无码视频| 亚洲一区二区无码| 色欲综合网| 亚洲无码在线精品| 土耳其电影《爱与罚》| 亚洲小视频在线| 午夜乱伦福利| 91成人免费电影片| 日韩无码电影网站| 乱子伦国产精品www| 性性性性性XXXXX| 免费A片视频| 亚洲二级片| 成人综合在线观看| 天堂91| 青青草99| 黄片51| 中文字幕无码一区二区三区一本久 | 久久中文网| 大肉大捧一出免费观看| 阿v视频在线观看| 美日韩一区二区| 亚洲高清在线观看视频| 色婷婷五月天在线观看| 91伊人久热精品| 日韩综合| 少妇嫩搡BBBB搡BBBB| 亚洲成人二区| AV-ThePorn| 色眯眯久久爱| 国产黄色在线播放| 成人免费视频国产在线观看| 成年人AV| 色色天堂成人电影| 亚洲操片| 综合久久中文字幕| 日本在线免费视频| 国产狂喷水潮免费网站www| 尤物AV| 日本中文字幕免费| 99色国产| 999成人电影| 国产中文字幕在线播放| 艳妇乳肉豪妇荡乳AV无码福利 | 丁香五月天在线视频| yjizz视频网| 北条麻妃一区二区三区在线| 成人午夜免费视频| 大香蕉精品在线视频| 毛片网站视频| 久久久精品999| 在线观看高清无码| 国产精品V| 国产做受91电影| 欧美AAA黄片| 五月激情婷婷基地| 国产精品色哟哟| 亚洲综合社区| 在线视频99| 大鸡吧草逼| 精品一区二区三区蜜桃臀www| 91成人视频18| 操碰视频在线| 久热免费视频在线观看| 夜夜bb| 激情麻豆论坛| 久热精品视频在线观看| 最新国产视频| 欧美日韩一区二区三区四区| 日逼老女人| 全部视频午夜寂寞| 大地影院在线资源观看| 久久久久久久91| 久久久高清无码| 91亚洲精品国产成人| 女人高潮天天躁夜夜躁| 午夜婷婷| av不卡在线| 色五月婷婷在线| 久久香蕉网站| 中文字幕北条麻妃| 成人免费毛片果冻日本| 波多野结衣视频网站| 无码精品人妻一区二区三区漫画| 操人视频在线观看| 午夜福利电影AV| 日韩性生活| 丁香五月天在线播放| 亚洲热热| 91精品国产综合久久久蜜臀九色 | 欧美成人免费A级在线观看| 无码蜜桃吴梦梦| 无码视频日韩| 成人三级AV在线| 激情五月综合| 大香伊人久久| 成人首页| 日韩无码精品一区二区三区| 无码电影在线播放| 日韩AV资源网| 妖精视频黄色| 乱伦无码| 成人网站视频在线观看| 日本久久网站| 一道本视频| 日本一级婬片免费放| 黄色视频网站在线播放| AV女优天堂| 欧美性视频网站| 亚洲一区av| 亚州AV无码| 欧美一级婬片AAAA毛片| 久久只有精品| 成人小说视频| 好男人一区二区三区在线观看| 四川BBB搡BBB爽爽爽欧美| 欧美性爱香蕉视频| 91啪啪| 人妻免费视频| 内射极品美女| 国产精品99久久久久久成人| 97久久一区二区| 日韩欧美成人片| 爱搞视频| 伊人啪啪| 日韩成人AV在线播放| 91最新在线播放| 中文字幕在线观看一区| EEUSS| 人妻一区| 日韩一区在线视频| 国产a片免费看| 翔田千里91| 日韩一| 奇米av在线| 一区二区中文| 91久久久精品| 中文字幕免费久久| 精品一区二区三区三区| 中文字幕乱妇无码Av在线| 亚洲无码网| 七十路の高齡熟妇无码| 高清无码波多野结衣| 一级性爽A√毛片| 91骚| 少妇av| 黄色中文字幕| 在线观看中文字幕一区| 国产女人高潮的AV毛片| 精品色片| 色天堂色男人| 精品无码一区二区人妻久久蜜桃| 国产无套进入免费| 亚洲无码免费看| 午夜探花| 欧美在线黄色| 亚洲欧美一区二区三区在线| 国产黄| 免费看v片| 国产精品va| 午夜久久久久久久久久久久91| 中文字幕高清无码在线观看| 欧美精品福利| 国产成人精品无码| 小明成人免费视频| 亚洲男同Gay一区二区| 仓井空一区二区三区| 日韩不卡av| 久久久久成人视频| 日韩av成人| 9l视频自拍蝌蚪9l视频成人| 日韩毛片在线视频x| 91天天综合在线| 免费在线性爱视频| 亚洲国产操逼| 波多野结衣无码流出| 丝瓜视频污APP| 国产免费视频69| 国产AV高清| 国产视频久久| 国产婷婷| 青娱乐AV| 青春草视频| 苍井空亚洲精品AA片在线播放| 男女爱爱动态图| 激情丁香五月| 成人性生活视频| 国产精品无码一区二区三区免费| 亚洲影音| 日本在线观看| 欧美视频在线观看| 天天日天天干天天射| 伊人97| 插菊花综合| 成人片成人片| 日欧美美女逼| 九九草影院| 高清无码1区| 日韩美在线视频| 久久久www| 日韩欧美日本| 亚洲wwwwww| 国产精品午夜在线观看| 超碰a片| 一级a一级a爱片兔兔软件| 成人网站视频| 日韩AV无码高清| 国产免费啪啪视频| 337p粉嫩噜噜噜| 人人妻日日摸狠狠躁视频| 成人综合娱乐网| 中文字幕在线成人| 亚洲AV成人无码一区二区三区 | 天天av天天av天天爽| 日本一区二区三区免费视频| 丰滿老婦BBwBBwBBw| 国产久久久久| 国产a区| 亚洲AV日韩AV永久无码网站| 亚洲AV无码成人精品区天堂小说| 亚洲中文字幕av天堂| 五月伊人婷婷| 乱子伦一区二区三区视频在线观看| 亚洲精品天堂无码| 欧美精品操逼| 黄色一级电影网| 北京熟妇搡BBBB搡BBBB电影| 欧美激情一级| 免看一级a毛片一片成人不卡| 成人黄色免费看| 欧美内射网站| 亚洲高清无码一区二区三区| 国产色五月| Av大香蕉| 搡BBBB搡BBB搡五十| 日韩一区二区视频| 蜜臀久久99精品久久久巴士| 无码av无码AV| 国产精品色婷婷99久久精品| 欧美无遮挡| 日本wwwwww| 荫蒂添到高潮免费视频| 久久91精品| 成人无码电影在线观看| 丁香婷婷五月色成人网站| 一级日逼片| 热久久久| 一区二区三区无码视频| 2015中文字幕黄色视频| 蜜桃导航-精品导航| 欧洲无码一区二区三区| 特爽特黄特级特色视频| 黄色视频免费网站| 人人做人人做人人做,人人做全句下一 | 刘玥91精一区二区三区| 天天干天天撸| 国产麻豆视频| 国产成人综合自拍| 靠逼国产| 三级片AV在线| 亚洲综合激情网| 无码一级二级| 日韩一区二区在线视频| eeuss一区| 超碰在线观看97| 亚洲欧美日韩黑料吃瓜在线观看| 求毛片网址| 国产精品免费一区二区三区四区视频| 五月婷婷综合激情| 亚洲AV无码成人专区| 在线免费观看亚洲| 一级黄色片在线观看| 中文字幕网站| 国产av资源| 日韩无码精品AV| 狠狠搞狠狠操| 2025中文字幕在线| 日韩视频――中文字幕| 久操福利| 国产激情网站| 国产免费黄色| 国内自拍青青| 无码免费毛片| 中字无码制服| 成人做爰A片免费看网站| 色色9999| 日本a视频| 人人妻人人操人人爱| 亚洲一区视频在线| 99久久久无码国产精品性波多| 国产视频第一页| 北京熟妇槡BBBB槡BBBB| 亚洲人妻AV| 色高清无码免费视频| 精品AV国产| 久久精品人人| 91肏屄视频| 日本韩国无码| 精品操逼| 精品久久免费| AV电影在线免费观看| 天堂a√在线8| 色婷婷AV一区二区三区之e本道 | 国产不卡在线视频| 欧美疯狂做受XXXXX高潮| 韩国无码免费| 黄色A片约| 91国视频| 麻豆国产在线| 天天日天天拍| 老熟女痒到不行-ThePorn | 国产免费一区二区三区| 51成人免费| 久久久久久久久久久久高清毛片一级 | 亚洲人妻性爱| 蜜桃Av噜噜| 久久久成人网站| 97人妻视频| 欧美日韩成人| 操逼网视频| 91久久久久久久18| 西西444WWW无码精品| www.黄色在线观看| 欧美中文字幕| 中文字幕第一页亚洲| 亚洲五区| 欧美日韩视频在线播放| 在线大香蕉| 国产又大又粗又爽| 高清无码视频免费看| 久久99国产精品| 国产三级片在线免费观看| 无码乱伦| 免费AV网站在线| 天天舔天天干| 亚洲国产精品成人久久蜜臀| 五月丁香六月婷| 先锋影音资源站av每日资源在线| 超碰人人爱人人操| 大香蕉1024| 日本中文字幕电影| 无套影院| 嫩草国产在线| 国产理论片在线观看| 九九热热| 91麻豆大奶巨乳一区白虎| 亚洲日本中文字幕在线观看| 国产操逼小视频| 2017天天干| 五月丁香啪啪啪| 国产波霸爆乳一区二区| 午夜麻豆| 老司机无码视频| 人妻丝袜中出北条麻妃| 亚洲二页| 亚洲第一黄片| 大香蕉日韩| 麻豆精品传媒国产剧的特点| 一区二区三区无码精品| 精品视频91| 操逼欧美| 亚洲欧美激情小说另类| 免费无码蜜臀在线观看| 操逼视频免费播放| 青青草原av| 国产乱子伦一区二区三区视频| 中文不卡在线| 无码专区视频| 木牛AV| 69成人在线| 巜痴漢電車~凌脔版2| 国产一级a毛一级a| 亲子乱婬-一级A片| 乱视频| 日韩精品成人电影| 久久精品国产亚洲AV麻豆痴男| 国产精品免费网站| 国产一级片视频| 丁香五月婷婷五月| 日韩免费性爱视频| 久久99久久99| 三级乱伦| 色老板在线精品免费观看| 亚洲性无码| 永久免费视频| 99精品视频播放| 99免费小视频| 妻子互换被高潮了三次| 国产特級黃色大片| 99爱在线| 国产欧美一区二区三区视频在线观看| 成人免费黄片| 婷婷日韩在线| 亚洲69p| 9999久久久久| 日韩成人性爱网站| 亚洲AV性爱| 精品三区| 东北骚妇大战黑人视频| 日韩AV一二三| 亚洲欧洲在线视频| a片在线免费| 91丨露脸丨熟女抽搐| 加勒比无码在线| 亚洲无码一区二区三区妃光| 久久一区二区三区四区五区| 西西888WWW大胆无码| 夜夜骑免费视频| 人人澡超碰碰| 成人无码中文字幕| 亚洲无码你懂的| 高清无码三级片在线观看| 久久久婷| 一本大道久久久久| 韩国三级av| 青春草在线观看| 精品国产一| 亚洲AV秘无码苍井空| 91人妻人人澡人人爽人人精品 | 欧美日韩亚洲另类| Chinese搡老女人| 伊人网站| av乱伦小说| 在线观看黄色AV| 麻豆午夜福利| 99热这里只有精品7| 日韩福利在线观看| 日韩AV电影网| 99热在线观看免费精品| 国产激情在线视频| 国产最新福利| 99久在线精品99re8热| 91麻豆福利在线观看| 刘玥精品国产一区二区三区| 亚洲黄色免费网站| 美女少妇激情BBBB| 亚洲熟妇视频| 亚洲AV无码乱码| 超碰综合| 亚洲清高毛无码毛片| 国产三级片网| 亚洲高清成人| 99久久99| 91av在线电影| 日韩欧美精品在线观看| 日韩黄在线| 熟妇槡BBBB槡BBBB图| 大香蕉久草|