项目答辩怎么讲(P2项目答辩总结)
1.1 登录的时候做vip的判断;
1.2 使用JWT(Java Web token),验证登录 ,更加安全
2 、连续签到 2.1判断是否断签: 通过判断昨天是否登录 ,可以判断;
2.2判断连续签到多少天: 将每次签到的记录保存在redis中,判断保存的数量 ,有多少个 ,就连续签到多少天;
2.3 签到流水: 签到流水如果可以 ,直接保存在数据库;(考虑使用redis的bitMaps);
《Redis实战》之使用BitMap实现用户签到功能:https://blog.csdn.net/qq_31905135/article/details/124032880
Redis中是利用String类型数据结构实现BitMap ,因此最大上限是512M ,转换为bit则是2^32个bit位 。 bitmap的操作命令有: setbit:向指定位置offset存入一个0或1 getbit:获取指定位置offset的bit值 bitcount:统计BitMap中值为1的bit位的数量 bitfield: 操作(查询 ,修改 ,自增)BitMap中bit数组中的指定位置offset的值 bitfield_ro: 获取BitMap中bit数组 ,并以十进制形式返回 bittop: 将多个BitMap的结果做位运算(与 ,或,异或) bitpos: 查找bit数组中指定范围内第一个0或者1出现的位置 2.4 签到初始化(自我考虑) 每天0点的时候每个用户初始化一条没有登录的信息(例如:status=0) ,如果用户登录了就对用户的状态进行修改(status=1);
3、VIP 3.1 vip续约redis中存用户vip的唯一标识key ,设置ttl,并设置用户vip字段为vip;续约直接增加key值得时间;
3.2 判断用户是不是VIP会员状态 一些需要使用到vip的权限的时候,先对vip进行检查 ,如果vip失效 ,需要将用户的vip字段设置为 普通会员;(主要两个地方判断:1.用户登录;2.用户使用vip权限);这样可以大大节省时时判断用户vip状态的消耗;
4 、 关键词匹配考虑,模糊匹配 ,近义词 ,(俗名) ,ES分词,IK分词(ElasticSearch+Kibana)
5 、热词 5.1 热词库需要添加热词库 ,只有在热词库中的词语 ,命中才算热词;热词的管理权在商家手里;
5.2 添加热词(候选热词库) 通过商品的销量添加对应的热词; 对正常搜索到的词语进行搜索次数的记录 ,作为候选热词库;如果后期需要添加 ,可以从候选热词库中挑选; 可以使用老师的关键字的方式进行模仿实现命中; 6 、轮播图 6.1 轮播图的数量可以根据不同时段进行不同数量的展示 ,人越多 ,数量越多;
6.2 多商户下,轮播图的挑选 添加到轮播图需要一定的费用; 可以根据商品的销量和热卖品挑选轮播图; 6.3 轮播图排序 返回的前端的轮播图需要排序好; 6.4 轮播图每个人不一样 每个人的轮播图不一样; 6.5轮播图设计 1.1 :关于背景色:也可以不用 ,主要看你的轮播图占多大空间 ,如果是下图那样占全部空间,因为图片不可能将其全部占据 ,如果不用背景色 ,那么,轮播图片的两旁就是白色 ,会很难看 。所以看自己 1.2 商品id:有时候 ,需要客户点击轮播图 ,直接跳转到 商品详情页面 ,所以 商品id 是线索 1.3 商品分类id:有时 ,点击后 ,会跳转到某一分类下 ,不是具体的商品详情页面 1.4 轮播图类型(type):用于判断 ,可以根据商品id或者分类进行页面跳转 ,1:商品 2:分类 6 、首页展示 每个人展示的商品不一样,可以从用户收藏 ,用户购买 ,添加购物车,用户浏览记录 ,等等推荐; 用户的行为数据(用户收藏 ,用户购买,添加购物车 ,用户浏览记录 ,等等)不能物理删除 ,因为这个数据是经过用户的自主赛选后得到的数据 ,十分宝贵; 7 、优惠劵 优惠劵池(优惠劵有不同的分类可以领取); 优惠劵的领取情况; 智能优惠劵挑选 ,在使用的时候 ,给用户展示优惠劵 ,需要将金额大的放在第一个 ,用户可以自行挑选; 用户优惠劵挑选过一次 ,在下订单的时候还需要再次判断优惠劵是否使用过,是否过期(除非可以确定该优惠劵一定可以用); 8 、商品下单及自动签收 商品发货 需要考虑商品的自动签收 ,用户不主动签收 ,需要自动签收;考虑使用延时队列,(或者定时+普通消息队列 ,注意定时对性能消耗比较大); 9 、分享有礼 隐藏分享用户的信息(因为需要对分析的用户进行奖励 ,但是又不能随便暴露用户的信息) 可以将用户信息MD5加密后在加盐,(内部有永固加盐库,自己人可以知道) 需要动态生成用户相关信息 ,主要目的就是保护用户信息安全; 10 、某个商品比较冷清没有多少评论 将同一个商铺下 ,类似产品的评论移过来;(个人感觉不是太好); 11 、多负载的时候 ,防止定时任务重复执行 XXL-Job 分布式的redis加锁 ,执行完释放; 通过ip指定配置,指定执行定时任务的服务器;12、做项目中遇到的问题
12.1 日期格式问题
//1.返回给前端的日期格式 //实体参数上添加日期格式化注解 @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") //2.前端传给后端的日期格式 //实体参数上添加日期格式化注解 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")12.2 feign远程调用传输时间相差(快了)14个小时
1.先说结论:在feign接口调用时使用表单形式(@RequestParam)传date类型会发生时间精度错乱 ,在8时区会多14个小时 。
2.使用json格式(@RequestBody)传date类型
不会发生这种情况 。
表单形式我的解决办法是:在对外暴露的feign接口中使用字符串类型 。3.(String date)来接收日期 ,在真正feign调用的controller服务中使用@DateTimeFormat(pattern=“yyyy-MM-dd ”)Date date来转换字符串类型的日期 。
4.将接收到的时间减去14小时(强烈不建议 ,比较麻烦且冗余 ,多次远程调用可能会再相差时间) 。
5.扩展ZonedDateTime
java8 日期 时间 时区:https://blog.csdn.net/z69183787/article/details/87176339 ZonedDateTime时区日期时间类的运用:https://blog.csdn.net/qx020814/article/details/125580729 LocalDateTime 、OffsetDateTime 、ZonedDateTime互转:https://blog.csdn.net/f641385712/article/details/11273254612.3 swagger返回参数不显示实体属性注解
需要添加@ApiModel(value = "xxx")注解 ,@ApiModelProperty(value = "xxx")注解,由于我之前没有怎么使用这个注解,导致忘记了 ,特此记录一下;
12.4 一个消息队列出列多个业务
12.4.1 发消息将要处理的消息内容转换成JOSN字符对象 ,再放入到map集合中,监听到消息后 ,根据不同的消息内容进行不同的处理;
点击查看代码 //将订单信息封装成map集合 ,方便发送 Map<String, Object> orderMsgMapToQueue = new HashMap<>(); //父订单 orderMsgMapToQueue.put("ordersForm", JSON.toJSONString(ordersForm)); //子订单 orderMsgMapToQueue.put("orderItemListSize", orderItemListForm.size()); for (int i = 0; i < orderItemListForm.size(); i++) { orderMsgMapToQueue.put("orderItem"+i,JSON.toJSONString(orderItemListForm.get(i))); } //发送生成抢购订单的消息到消息队列,并在redis中添加此订单的记录 //0 正在生成 if(redisUtils.set("weddingshop:orders:"+ordersForm.getOid(),ordersForm)){ //发送生成的 订单信息 到 消息队列 rabbitTemplate.convertAndSend(OrderMQConstant.WEDDINGSHOP_SAVE_ORDER_EXCHANGE, OrderMQConstant.WEDDINGSHOP_SAVE_ORDER_ROUTING, orderMsgMapToQueue); } 12.4.2 接收消息监听到消息后 ,从map集合中取出不同的消息 ,根据不同的消息内容进行不同的处理;
点击查看代码 @Slf4j @Component //指定接听的 消息队列 名字 @RabbitListener(queues = OrderMQConstant.WEDDINGSHOP_SAVE_ORDER_QUEUE) public class SeckillOrderSaveListener { @Autowired private RedisUtils redisUtils; @Autowired(required = false) private OrdersMapper ordersMapper; @Autowired(required = false) private OrderItemMapper orderItemMapper; @Autowired private OrdersService ordersService; /** * @author : huayu * @date : 1/11/2022 * @param : [directMsgJson] * @return : void * @description : Direct 直连模式消费者One,消费信息 */ //指定消息队列中的消息 ,交给对应的方法处理 @RabbitHandler public void saveSeckillOrder(Map<String,Object> orderMsgMapToQueue){ //子订单消息 if(orderMsgMapToQueue.get("orderItemListSize") != null){ //插入子订单 int orderItemListSize = Integer.parseInt(orderMsgMapToQueue.get("orderItemListSize").toString()); for (int i = 0; i < orderItemListSize; i++) { OrderItem orderItem = JSON.parseObject(orderMsgMapToQueue.get("orderItem" + i).toString(), OrderItem.class); log.info("***** 秒杀抢购子订单:{} ,开始入库 ******",orderItem); if(orderItemMapper.insert(orderItem) > 0){ log.info("***** 秒杀抢购子订单:{} ,开始入库 成功 ******",orderItem); } } } //父订单消息 if(orderMsgMapToQueue.get("ordersForm")!= null){ Orders ordersForm = JSON.parseObject(orderMsgMapToQueue.get("ordersForm").toString(), Orders.class); log.info("***** 秒杀抢购父订单:{} ,开始入库 ******",ordersForm); //TODO 将消息中的订单实体对象 ,调入业务接口 ,插入到数据库,和redis中 //插入 父订单 if(ordersMapper.insert(ordersForm)>0){ log.info("***** 秒杀抢购父订单:{} ,入库 成功 ******",ordersForm); } } //父订单 付款后 ,操作 if(orderMsgMapToQueue.get("ordersPayOid") != null){ // 父订单付款后 对 父订单 修改状态 ,用户增加积分 和 子订单 修改状态 ,生成物流单号 String ordersPayOid = orderMsgMapToQueue.get("ordersPayOid").toString(); log.info("------ 父订单:{},支付成功,对 父订单 修改状态 ,用户增加积分 和 子订单 修改状态 ,生成物流单号------",ordersPayOid); if(ordersService.paySuccessOrders(ordersPayOid)){ log.info("------ 父订单:{},入库成功,对 父订单 修改状态 ,用户增加积分 和 子订单 修改状态 ,生成物流单号------",ordersPayOid); } } } }创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!