这是我遇到的写完登录以及验证码功能后,进入首页时而显示验证码时而不显示,报错维护
分析问题:为什么验证码会一会出来一会没出来?
看前端请求,9527是网关,然后通过manager路由到微服务实例sl-ms-web-manger
看一眼Nacos(负载均衡)中对应几个实例
很明显是两个微服务实例,那我们到底用的哪个呢?要知道,这是在两个服务器上,代码不一致自然也就会出现负载均衡问题
测试:
docker中停掉部署到jekins中的微服务实例
问题解决
最后,在没完善代码之前不用提前部署微服务实例到jkins,如果部署了停掉jkins上代码不一致的,另一个服务器的微服务才能正常使用
或者每次写完代码提交重新部署一下(比较麻烦)
2025/6/30
业务问题?
使用token登录,token被盗
解决方案?
双Token三验证方案
具体业务实现?
第一步:先做双Token 和access_token的验证(之前做过,在这基础上多了refresh_token)
小点是通过refresh_token存入redis中时用MD5加密一下
第二步:access_token过期就会执行refresh方法
refresh方法的目的就是重新生成一遍access_token和refresh_token
//面试中如果问 双token三验证? /** 双Token: access_token(短5min) 和 refresh_token(长24H) 三验证: access_token refresh_token redis中的refresh_token **/
第三步删除旧的token
思考token被盗的情况,如何解决?
把24小时过期的token存到redis中,无论谁携带token来访问,
不仅要校验token是否失效还要校验token是否在我的redis中存着,如何发现token被盗直接删除redis的token让它马上失效
双token三验证总结:
面试题!!!
说下你们双 token 三验证的设计逻辑?
1.登录发 token:
用户登录时,系统生成短期 access_token(日常接口用)和长期 refresh_token(续期用),把 refresh_token 存 Redis,双 token 给前端。
2.业务访问校验:
前端带 access_token 调接口,业务系统先一次校验(检查 access_token 合法性),通过则正常返回,不通过返回 401 触发续期。
3.token 失效续期:
前端拿 refresh_token 调刷新接口,系统先二次校验(检查 refresh_token 合法性),通过后再三次校验(查 Redis 里的 refresh_token 是否已用,防止重复续期 )。三次都过,就生成新双 token,更新 Redis 并返回前端;校验失败就要求重新登录。
这样设计既保障了接口安全(多层校验防攻击),又能让用户无感续期(不用频繁登录),Redis 存 refresh_token 还能解决续期凭证的复用风险~
2025/7/1
运费模板
后面这部分有时间再补上
2025/7/6
路线管理
1、需求分析:
2、具体实现
准备机构坐标
实现思路
根据上图总体实现分成以下六步:
起点和终点机构id是否一样
路线类型是否符合规定
判断起点和终点的路线是否存在
判断起点和终点的机构是否为空
封装数据获取两个节点之间的距离开车时间总成本
新增操作
两个Neo4j语句比较重要,贴这了
具体实现的业务逻辑如下:
/** * 新增路线 * * @param transportLine 路线数据 * @return 是否成功 */ @Override public Boolean createLine(TransportLine transportLine) { Long startOrganId = transportLine.getStartOrganId(); Long endOrganId = transportLine.getEndOrganId(); //1、起点和终点机构id是否一样 if (startOrganId.equals(endOrganId)) { throw new SLException(ExceptionEnum.TRANSPORT_LINE_ORGAN_CANNOT_SAME); } //2、路线类型是否符合规定 干线 支线 接驳路线 //根据type获取枚举类 TransportLineEnum transportLineEnum = TransportLineEnum.codeOf(transportLine.getType()); BaseEntity firstNode = null; BaseEntity secondNode = null; //3、判断起点和终点的路线是否存在 switch (transportLineEnum) { case TRUNK_LINE: firstNode = OLTEntity.builder().bid(startOrganId).build(); secondNode = OLTEntity.builder().bid(endOrganId).build(); break; case BRANCH_LINE: firstNode = OLTEntity.builder().bid(startOrganId).build(); secondNode = TLTEntity.builder().bid(endOrganId).build(); break; case CONNECT_LINE: firstNode = OLTEntity.builder().bid(startOrganId).build(); secondNode = AgencyEntity.builder().bid(endOrganId).build(); break; default: throw new SLException(ExceptionEnum.TRANSPORT_LINE_TYPE_ERROR); } //4、判断起点和终点的机构是否为空 Long count = transportLineRepository.queryCount(firstNode, secondNode); if (count > 0) { throw new SLException(ExceptionEnum.TRANSPORT_LINE_ALREADY_EXISTS); } //5、封装数据获取两个节点之间的距离开车时间总成本 initDataFormMap(firstNode, secondNode, transportLine); //6、新增操作 Long lineId = transportLineRepository.create(firstNode, secondNode, transportLine); return lineId > 0; } //抽取方法:封装数据获取两个节点之间的距离开车时间总成本 private void initDataFormMap(BaseEntity firstNode, BaseEntity secondNode, TransportLine transportLine) { //查询两个节点 OrganDTO startOrgan = organReepository.findByBid(firstNode.getBid()); OrganDTO endOrgan = organReepository.findByBid(secondNode.getBid()); if (startOrgan == null || endOrgan == null) { throw new SLException(ExceptionEnum.ORGAN_NOT_FOUND); } //根据坐标调用高德地图 if (startOrgan.getLatitude() != null && startOrgan.getLatitude() != null && endOrgan.getLongitude() != null && endOrgan.getLongitude() != null ) { Map<String, Object> param = new HashMap<>(); param.put("show_fields", "cost"); String driving = eagleMapTemplate.opsForDirection().driving(ProviderEnum.AMAP, new Coordinate(startOrgan.getLongitude(), startOrgan.getLatitude()), new Coordinate(endOrgan.getLongitude(), endOrgan.getLatitude()), param); System.out.println(driving); JSONObject jsonObject = JSONUtil.parseObj(driving); //获取总时间和距离 Long time = jsonObject.getByPath("route.paths[0].cost.duration", Long.class); Long distance = jsonObject.getByPath("route.paths[0].distance", Long.class); transportLine.setTime(time); transportLine.setDistance(distance.doubleValue()); //暂时给一个假的每公里的成本 0.5 TODO: 获取成本 transportLine.setCost(distance/1000 * 0.5); //0.5元/公里 } }
具体两个对应Neo4j语句的方法:
/** * 查询数据节点之间的关系数量 * * @param firstNode 第一个节点 * @param secondNode 第二个节点 * @return 数量 */ @Override public Long queryCount(BaseEntity firstNode, BaseEntity secondNode) { //MATCH (m:OLT) -[r]- (n:OLT) WHERE m.bid =10001 AND n.bid = 1002 RETURN count(r) AS c String firstType = firstNode.getClass().getAnnotation(Node.class).value()[0]; String secondType = secondNode.getClass().getAnnotation(Node.class).value()[0]; String cypher = StrUtil.format("MATCH (m:{}) -[r]- (n:{}) WHERE m.bid =$startId AND n.bid = $endId RETURN count(r) AS c", firstType, secondType); //执行查询 return neo4jClient.query(cypher) .bind(firstNode.getBid()).to("startId") .bind(secondNode.getBid()).to("endId") .fetchAs(Long.class) .mappedBy((typeSystem, record) -> record.get("c").asLong() ).one().orElse(0L); } /** * 新增路线 * * @param firstNode 第一个节点 * @param secondNode 第二个节点 * @param transportLine 路线数据 * @return 新增关系的数量 */ @Override public Long create(BaseEntity firstNode, BaseEntity secondNode, TransportLine transportLine) { //MATCH (m:OLT {bid : 10001}) //WITH m MATCH (n:OLT {bid : 1002}) WITH m,n //CREATE //(m) -[r:IN_LINE {cost:999.0, number:'LX001', type:1, name:'京广线', distance:4000, time:60, extra:'', startOrganId:1001, endOrganId:1002}]-> (n), //(m) <-[:OUT_LINE {cost:999.0, number:'LX002', type:1, name:'京广线', distance:4000, time:60, extra:'', startOrganId:1002, endOrganId:1001}]- (n) //RETURN count(r) AS c String firstType = firstNode.getClass().getAnnotation(Node.class).value()[0]; String secondType = secondNode.getClass().getAnnotation(Node.class).value()[0]; String cypher = StrUtil.format(" MATCH (m:{} {bid : $startId})n" + " WITH m MATCH (n:{} {bid : $endId}) WITH m,nn" + " CREATEn" + " (m) -[r:IN_LINE {cost:$cost, number:'$number', type:$type, name:'$name', distance:$distance, time:$time, extra:'', startOrganId:$startOrganId, endOrganId:$endOrganId}]-> (n),n" + " (m) <-[:OUT_LINE {cost:$cost, number:'$number', type:$type, name:'$name', distance:$distance, time:$time, extra:'', startOrganId:$endOrganId, endOrganId:$startOrganId}]- (n)n" + " RETURN count(r) AS c", firstType, secondType); return neo4jClient.query(cypher) .bind(firstNode.getBid()).to("startId") .bind(secondNode.getBid()).to("endId") .bindAll(BeanUtil.beanToMap(transportLine)) .fetchAs(Long.class) .mappedBy((typeSystem, record) -> record.get("c").asLong() ).one().orElse(0L); }
ok以后就记住每天业务实现的思路即可
2025/7/7
1、面试问路线规划是怎么做的?(机构管理+线路管理)
路线规划也就是机构与机构之间的路线,一开始机构是在权限管家存储,机构数据通过MQ会同步存储到Neo4j(Neo4j就是图数据库方便存储机构与机构之间的路线关系),然后在后台添加机构与机构之间的路线(路线分成干线、支线、接驳路线),通过转运次数最少和成本最低两个方法选择机构之间的最佳路线。
推荐阅读:
文章有(0)条网友点评