这是我遇到的写完登录以及验证码功能后,进入首页时而显示验证码时而不显示,报错维护
分析问题:为什么验证码会一会出来一会没出来?
看前端请求,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)条网友点评