package com.jieming.coupon.service.impl;
import com.alibaba.fastjson.JSON;
import com.jieming.coupon.constant.Constant;
import com.jieming.coupon.constant.CouponStatus;
import com.jieming.coupon.dao.CouponDao;
import com.jieming.coupon.entity.Coupon;
import com.jieming.coupon.exception.CouponException;
import com.jieming.coupon.feign.SettlementClient;
import com.jieming.coupon.feign.TemplateClient;
import com.jieming.coupon.service.IRedisService;
import com.jieming.coupon.service.IUserService;
import com.jieming.coupon.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* <h1>用户服务相关的接口实现</h1>
* 所有的操作过程, 状态都保存在 Redis 中, 并通过 Kafka 把消息传递到 MySQL 中
* 为什么使用 Kafka, 而不是直接使用 SpringBoot 中的异步处理 ? 为了保证数据一致性,因为SpringBoot异步任务有可能会失败。
* Created by Jieming.
*/
@Slf4j
@Service
public class UserServiceImpl implements IUserService {
/** Coupon Dao */
private final CouponDao couponDao;
/** Redis 服务 */
private final IRedisService redisService;
/** 模板微服务客户端 */
private final TemplateClient templateClient;
/** 结算微服务客户端 */
private final SettlementClient settlementClient;
/** Kafka 客户端 */
private final KafkaTemplate<String, String> kafkaTemplate;
@Autowired
public UserServiceImpl(CouponDao couponDao, IRedisService redisService,
TemplateClient templateClient,
SettlementClient settlementClient,
KafkaTemplate<String, String> kafkaTemplate) {
this.couponDao = couponDao;
this.redisService = redisService;
this.templateClient = templateClient;
this.settlementClient = settlementClient;
this.kafkaTemplate = kafkaTemplate;
}
/**
* <h2>根据用户 id 和状态查询优惠券记录</h2>
* @param userId 用户 id
* @param status 优惠券状态
* @return {@link Coupon}s
*/
@Override
public List<Coupon> findCouponsByStatus(Long userId, Integer status)
throws CouponException {
// 只要redisService 执行 get或者put操作,都不会为NULL
List<Coupon> curCached = redisService.getCachedCoupons(userId, status);
List<Coupon> preTarget;
if (CollectionUtils.isNotEmpty(curCached)) {
// 没有操作过或者缓存过期
log.debug("coupon cache is not empty: {}, {}", userId, status);
preTarget = curCached;
} else {
log.debug("coupon cache is empty, get coupon from db: {}, {}",
userId, status);
List<Coupon> dbCoupons = couponDao.findAllByUserIdAndStatus(
userId, CouponStatus.of(status)
);
// 如果数据库中没有记录, 直接返回就可以, Cache 中已经加入了一张无效的优惠券
if (CollectionUtils.isEmpty(dbCoupons)) {
log.debug("current user do not have coupon: {}, {}", userId, status);
return dbCoupons;
}
// 填充 dbCoupons的 templateSDK 字段
Map<Integer, CouponTemplateSDK> id2TemplateSDK =
templateClient.findIds2TemplateSDK(
dbCoupons.stream()
.map(Coupon::getTemplateId)
.collect(Collectors.toList())
).getData();
dbCoupons.forEach(
dc -> dc.setTemplateSDK(
id2TemplateSDK.get(dc.getTemplateId())
)
);
// 数据库中存在记录
preTarget = dbCoupons;
// 将记录写入 Cache
redisService.addCouponToCache(userId, preTarget, status);
}
// 将无效优惠券剔除
preTarget = preTarget.stream()
.filter(c -> c.getId() != -1)
.collect(Collectors.toList());
// 如果当前获取的是可用优惠券, 还需要做对已过期优惠券的延迟处理
if (CouponStatus.of(status) == CouponStatus.USABLE) {
CouponClassify classify = CouponClassify.classify(preTarget);
// 如果已过期状态不为空, 需要做延迟处理
if (CollectionUtils.isNotEmpty(classify.getExpired())) {
log.info("Add Expired Coupons To Cache From FindCouponsByStatus: " +
"{}, {}", userId, status);
redisService.addCouponToCache(
userId, classify.getExpired(),
CouponStatus.EXPIRED.getCode()
);
log.info("使用kafka");
// 发送到 kafka 中做异步处理
kafkaTemplate.send(
Constant.TOPIC,
JSON.toJSONString(new CouponKafkaMessage(
CouponStatus.EXPIRED.getCode(),
classify.getExpired().stream()
.map(Coupon::getId)
.collect(Collectors.toList())
))
);
}
return classify.getUsable();
}
return preTarget;
}
/**
* <h2>根据用户 id 查找当前可以领取的优惠券模板</h2>
* @param userId 用户 id
* @return {@link CouponTemplateSDK}s
*/
@Override
public List<CouponTemplateSDK> findAvailableTemplate(Long userId)
throws CouponException {
long curTime = new Date().getTime();
List<CouponTemplateSDK> templateSDKS =
templateClient.findAllUsableTemplate().getData();
log.debug("Find All Template(From TemplateClient) Count: {}",
templateSDKS.size());
// 过滤过期的优惠券模板
templateSDKS = templateSDKS.stream().filter(
t -> t.getRule().getExpiration().getDeadline() > curTime
).collect(Collectors.toList());
log.info("Find Usable Template Count: {}", templateSDKS.size());
// limit2Template:由 templateSDKS 转化为 对应的 map
// key 是 TemplateId
// value 中的 left 是 Template limitation, right 是优惠券模板
Map<Integer, Pair<Integer, CouponTemplateSDK>> limit2Template =
new HashMap<>(templateSDKS.size());
templateSDKS.forEach(
t -> limit2Template.put(
t.getId(),
Pair.of(t.getRule().getLimitation(), t)
)
);
List<CouponTemplateSDK> result =
new ArrayList<>(limit2Template.size());
List<Coupon> userUsableCoupons = findCouponsByStatus(
userId, CouponStatus.USABLE.getCode()
);
log.debug("Current User Has Usable Coupons: {}, {}", userId,
userUsableCoupons.size());
// templateId2Coupons :由userUserableCoupons 转化来的 map
// key 是 TemplateId
Map<Integer, List<Coupon>> templateId2Coupons = userUsableCoupons
.stream()
.collect(Collectors.groupingBy(Coupon::getTemplateId));
// 根据 Template 的 Rule 判断是否可以领取优惠券模板
limit2Template.forEach((k, v) -> {
int limita
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
2020优惠券分发系统-毕业设计.zip (587个子文件)
UserServiceImpl.class 18KB
RedisServiceImpl.class 15KB
ThyTemplateInfo.class 14KB
ThyCreateTemplate.class 13KB
ThyTemplateController.class 12KB
ThyDistributionController.class 11KB
CouponTemplate.class 11KB
ThyTemplateInfo.class 10KB
BuildTemplateServiceImpl.class 10KB
AsyncServiceImpl.class 9KB
TemplateBaseServiceImpl.class 8KB
ManJianZheKouExecutor.class 8KB
KafkaServiceImpl.class 7KB
ExecuteManagerTest.class 7KB
TemplateRequest.class 7KB
Coupon.class 6KB
BuildTemplateTest.class 6KB
CouponTemplateSDK.class 6KB
CouponClassify.class 6KB
UserServiceController.class 6KB
ExecuteManager.class 5KB
CouponTemplateController.class 5KB
ThyCouponInfo.class 5KB
SettlementInfo.class 5KB
TemplateRule.class 5KB
UserServiceTest.class 5KB
AbstractExecutor.class 4KB
TemplateBaseTest.class 4KB
User.class 4KB
HealthCheck.class 4KB
GoodsType.class 4KB
CouponStatus.class 4KB
CouponStatus.class 4KB
ScheduledTask.class 4KB
DistributeTarget.class 4KB
PeriodType.class 4KB
ProductLine.class 4KB
UseInfo.class 4KB
CouponCategory.class 4KB
CommonResponse.class 3KB
RibbonController$TemplateInfo.class 3KB
ManJianExecutor.class 3KB
TemplateRule$Expiration.class 3KB
CouponTemplateSerialize.class 3KB
CouponSerialize.class 3KB
CommonResponseDataAdvice.class 3KB
ZheKouExecutor.class 3KB
LiJianExecutor.class 3KB
RedisServiceImpl$1.class 3KB
TemplateRule$Usage.class 3KB
LogInfo.class 3KB
TemplateUpdateInfo.class 3KB
TemplateUpdateInfo.class 3KB
GoodsInfo.class 3KB
UserServiceImpl.class 3KB
RedisServiceImpl$3.class 3KB
RedisServiceImpl$2.class 3KB
CouponKafkaMessage.class 3KB
SettlementInfo$CouponAndTemplateInfo.class 2KB
TemplateRule$Discount.class 2KB
AcquireTemplateRequest.class 2KB
TemplateNumber.class 2KB
ObjectParams.class 2KB
AsyncPoolConfig.class 2KB
TemplateClientHystrix.class 2KB
TokenFilter.class 2KB
AbstractZuulFilter.class 2KB
SettlementClientHystrix.class 2KB
RateLimiterFilter.class 2KB
AccessLogFilter.class 2KB
SettlementController.class 2KB
RuleFlag.class 2KB
RibbonController.class 2KB
GlobalExceptionAdvice.class 2KB
RedisServiceImpl.class 2KB
AsyncPoolConfig$AsyncExceptionHandler.class 1KB
RuleConverter.class 1KB
DistributeTargetConverter.class 1KB
CouponTemplateDao.class 1KB
CouponCategoryConverter.class 1KB
CouponStatusConverter.class 1KB
ProductLineConverter.class 1KB
DistributionApplication.class 1KB
IRedisService.class 1KB
TemplateClient.class 1KB
RedisServiceImpl.class 1KB
IUserService.class 1KB
PreRequestFilter.class 1KB
WebConfiguration.class 1KB
SettlementClient.class 1023B
TemplateApplication.class 1020B
JacksonConfig.class 935B
ITemplateBaseService.class 898B
BuildTemplateServiceImpl$1.class 863B
RedisServiceImpl$4.class 839B
KafkaServiceImpl$1.class 839B
ExecuteManager$1.class 835B
SettlementApplication.class 823B
EurekaApplication.class 818B
ZuulGatewayApplication.class 816B
共 587 条
- 1
- 2
- 3
- 4
- 5
- 6
资源评论
马coder
- 粉丝: 1203
- 资源: 6602
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功