springboot第72集:字节跳动全栈二面经,一文让你走出微服务迷雾架构周刊

科技   2024-05-20 00:24   广东  

架构师

组织类似于公司的部门,比如销售部、财务部、人力资源部。如果是集团公司,也可以是北京分公司、上海分公司、江西分公司等等。组织是一个树形结构,比如北京分公司下面还可以有各个部门。

用户必须属于某个组织,站点也必须属于某个组织。如果系统拥有多个站点,站点可属于不同组织,在切换到某个站点时,组织管理和用户管理只能看到站点所属组织的子组织和用户。从而实现多组织多站点的管理体系。

角色是系统管理用户权限的核心功能。用户所拥有的权限由用户所属的角色决定。一个用户拥有多个角色,则该用户拥有所有角色权限的总和。

文章状态

文章状态包括已发布、已归档、待发布、草稿、待审核、审核中、已删除、已下线和已退回。

  • 已发布:在前台页面可显示、可搜索的文章
  • 已归档:直接输入文章地址可以显示,但不在栏目列表中显示、也不在文章搜索中显示。
  • 待发布:可以发布但未到上线时间的文章,到上线时间后自动发布(约有10分钟所有误差)。
  • 草稿:录入人员尚未完成的文章,可以报错为草稿。
  • 待审核:已提交的审核的文章,但还没有人员进行审核,此时录入人员还可以对文章进行修改。
  • 审核中:文章已提交审核,且已至少被一个人员审核过,此时录入人员不可对文章进行修改。
  • 已删除:已被逻辑删除的文章,此时文章在前台不可显示。
  • 已下线:由于某些原因(如文章中发现错别字或某些措词不当等情况),需要暂时下线文章(前台不可访问);在进行适当修改或时机合适时,需要再次发布的文章。
  • 已退回:对于需要审核的文章(文章所属栏目设置了审核流程),在审核过程中,如被审核人员驳回,文章会变成已退回状态。

文章操作

  • 彻底删除:物理删除文章,不可恢复。
  • 删除:逻辑删除文章,预期以后将彻底删除的文章。文章在前台不可访问。可以通过提交功能恢复。
  • 提交:已归档、草稿、已删除、已下线、已退回的文章,都可以进行提交。如文章不需要审核,则直接发布;需要审核的,则会进入待审核状态。
  • 归档:将文章归档。不希望在栏目列表中显示文章和不希望被搜索的文章,可以进行归档。但直接输入文章的url地址,文章依然可访问。可以通过提交功能恢复为已发布状态。
  • 下线:将文章暂时下线,预期以后将再次上线的文章。文章在前台不可访问。可以通过提交功能恢复。
  • 复制:以当前文章为副本,录入一篇新的文章。对于相似的文章可减少一些录入工作。
  • 站内推送:即多栏目发布功能。一篇文章需要在多个栏目下发布的文章可以使用此功能。“引用”推送,则文章url地址和原文章地址完全相同,推送的文章只是一个url地址,点击该url地址访问的是原文章;“复制”推送,则相当于在新栏目下新建了一篇一样的文章,且可进行修改使其与原文章不同。
  • 站群推送:将文章推送到其它站点。
  • 置顶:前台文章列表标签可以使用orderBy='sticky_desc,order_desc'排序,将置顶文章排在最上面。
image.png

ID为1的是系统的默认的用户,类似于linux的root用户,不能被删除。但用户的权限机制和其他用户一样,由自己所属的角色控制,并非天然拥有所有权限。

另一个是ID为0的匿名用户,比如前台游客评论时,系统会使用匿名用户作为评论的用户,这个用户也不能被删除。匿名用户要处于锁定状态,以避免使用匿名用户登录系统。

image.png

全局用户和用户管理的区别

全局用户和用户管理中的数据是共享的,修改任何一个地方的数据,其他地方的数据也会相应变化。如果系统只有一个站点,全局用户和用户管理没有区别。

用户管理只能管理当前站点所属组织下的用户,并且也只能管理用户在当前站点的角色。而全局用户可以管理所有的用户,以及用户在所有站点的角色。

只有等级为1或者0的用户才能拥有全局用户的权限。

image.png
image.png
  • 名称:会员组的名称。
  • 描述:会员组的描述。
  • 浏览权限:有权限浏览的栏目。
  • 投稿权限:有权限投稿的栏目。
  • 评论权限:有权限评论的栏目。

角色是系统管理用户权限的核心功能。用户所拥有的权限由用户所属的角色决定。一个用户拥有多个角色,则该用户拥有所有角色权限的总和。

image.png
image.png
  • 名称:角色的名称。

  • 等级:角色的等级。决定哪些用户可以赋予该角色。用户不能赋予高于自身等级的角色。此功能可以用于控制拥有用户管理权限的管理员不能获取比自身等级更高级的角色。

  • 描述:角色的描述。无特殊意义。

  • 功能权限:角色拥有的功能权限。没有权限的功能不会出现的后台功能导航菜单里面。

  • 栏目权限:角色拥有的栏目权限。属于数据权限,用户不能管理没有权限的栏目。

  • 文档权限:角色拥有的文档权限。属于数据权限,用户不能在没有权限的栏目新增、修改、删除文章。

  • 权限范围:如果一个用户拥有多个角色,则哪个权限范围大以哪个为准。比如用户同时拥有A角色的自身范围和B角色的组织范围,则最终拥有组织范围的权限。

    • 所有范围:角色可以管理所有有文档权限的文章。
    • 组织范围:角色只能管理有文档权限且属于自己组织的用户发布的文章。
    • 自身范围:角色只能管理有文档权限且属于自己发布的文章,不能看到其他用户发表的文章。
  • 终审权限:如果用户拥有终审权限,即使没有文档终审权限,也可以管理有权限的文档。

image.png

表设计

[
    {
        "id": 6, // 唯一标识符
        "name""VIP角色", // 角色名称
        "description""查看VIP产品", // 角色描述
        "allPermission"false, // 是否拥有所有权限
        "allGrantPermission"true, // 是否拥有所有授权权限
        "globalPermission"true, // 是否拥有全局权限
        "allArticlePermission"true, // 是否拥有所有文章权限
        "allChannelPermission"true, // 是否拥有所有频道权限
        "dataScope": 1, // 数据范围
        "allStatusPermission"false, // 是否拥有所有状态权限
        "rank": 3, // 排名
        "type": 4, // 类型
        "scope": 2, // 作用域
        "order": 32767, // 顺序
        "permission""menu.personal,password:update,password:matches,homepage:systemInfo,homepage:generatedKey,article:page,article:list,channel:list,dict:list,model:list,block:list,tag:list,processInstance:task,article:create,article.submit,jodConvert:doc,jodConvert:library,article:show,article:update,article.submit,article:show,jodConvert:doc,jodConvert:library,article:externalPush,article:updateOrder,article:sticky,article:submit,article:archive,article:offline,article:delete,article:completelyDelete,menu.content", // 权限列表
        "site": {
            "id": 0, // 站点ID
            "name""", // 站点名称
            "url""" // 站点URL
        },
        "articlePermissions": [], // 文章权限
        "channelPermissions": [], // 频道权限
        "global"true // 是否为全局角色
    },
    {
        "id": 1, // 唯一标识符
        "siteId": 1, // 站点ID
        "name""管理角色", // 角色名称
        "allPermission"true, // 是否拥有所有权限
        "allGrantPermission"true, // 是否拥有所有授权权限
        "globalPermission"true, // 是否拥有全局权限
        "allArticlePermission"true, // 是否拥有所有文章权限
        "allChannelPermission"true, // 是否拥有所有频道权限
        "dataScope": 1, // 数据范围
        "allStatusPermission"false, // 是否拥有所有状态权限
        "rank": 1, // 排名
        "type": 4, // 类型
        "scope": 0, // 作用域
        "order": 1, // 顺序
        "site": {
            "id": 1, // 站点ID
            "name""企业网站", // 站点名称
            "url""http://localhost:8080" // 站点URL
        },
        "articlePermissions": [], // 文章权限
        "channelPermissions": [], // 频道权限
        "global"false // 是否为全局角色
    },
    {
        "id": 3, // 唯一标识符
        "siteId": 1, // 站点ID
        "name""私有产品", // 角色名称
        "allPermission"true, // 是否拥有所有权限
        "allGrantPermission"true, // 是否拥有所有授权权限
        "globalPermission"true, // 是否拥有全局权限
        "allArticlePermission"true, // 是否拥有所有文章权限
        "allChannelPermission"true, // 是否拥有所有频道权限
        "dataScope": 1, // 数据范围
        "allStatusPermission"false, // 是否拥有所有状态权限
        "rank": 2, // 排名
        "type": 4, // 类型
        "scope": 0, // 作用域
        "order": 32767, // 顺序
        "site": {
            "id": 1, // 站点ID
            "name""企业网站", // 站点名称
            "url""http://localhost:8080" // 站点URL
        },
        "articlePermissions": [], // 文章权限
        "channelPermissions": [], // 频道权限
        "global"false // 是否为全局角色
    },
    {
        "id": 5, // 唯一标识符
        "siteId": 1, // 站点ID
        "name""私有角色", // 角色名称
        "description""查看私有产品", // 角色描述
        "allPermission"true, // 是否拥有所有权限
        "allGrantPermission"true, // 是否拥有所有授权权限
        "globalPermission"true, // 是否拥有全局权限
        "allArticlePermission"true, // 是否拥有所有文章权限
        "allChannelPermission"true, // 是否拥有所有频道权限
        "dataScope": 1, // 数据范围
        "allStatusPermission"false, // 是否拥有所有状态权限
        "rank": 4, // 排名
        "type": 4, // 类型
        "scope": 0, // 作用域
        "order": 32767, // 顺序
        "site": {
            "id": 1, // 站点ID
            "name""企业网站", // 站点名称
            "url""http://localhost:8080" // 站点URL
        },
        "articlePermissions": [], // 文章权限
        "channelPermissions": [], // 频道权限
        "global"false // 是否为全局角色
    }
]

[
    {
        "id": 1, // 唯一标识符
        "orgId": 1, // 组织ID
        "modelId": 13, // 模型ID
        "name""企业网站", // 网站名称
        "protocol""http", // 协议类型
        "domain""localhost", // 域名
        "theme""/1/default", // 主题路径
        "mobileTheme""/1/default", // 移动端主题路径
        "pageSize": 10, // 页面大小(分页)
        "staticFile""/index.html", // 静态文件路径
        "editorSettings""{\"typesetting\":{\"fontFamily\":\"\",\"fontSize\":\"\",\"lineHeight\":\"\",\"textIndent\":false,\"imageCenterAlign\":true,\"tableFullWidth\":true,\"emptyLine\":\"one\",\"halfWidthCharConversion\":true}}", // 编辑器设置
        "views": 627, // 总浏览量
        "selfViews": 465, // 自己的浏览量
        "todayViews": 149, // 今天的浏览量
        "yesterdayViews": 1, // 昨天的浏览量
        "maxViews": 200, // 最大浏览量
        "maxDate""2024-05-15T00:00:00+08:00", // 最大浏览量的日期
        "status": 0, // 状态
        "depth": 1, // 深度
        "order": 1, // 顺序
        "customs": {
            "icp""xx备xxx号", // ICP备案号
            "company""公司" // 公司名称
        },
        "watermark": {
            "enabled"false, // 是否启用水印
            "overlay": null, // 水印覆盖层
            "position": 9, // 水印位置
            "dissolve": 50, // 水印透明度
            "minWidth": 300, // 最小宽度
            "minHeight": 300 // 最小高度
        },
        "html": {
            "enabled"false, // 是否启用HTML生成
            "auto"true, // 是否自动生成
            "listPages": 1, // 列表页数
            "channel""/channels/{channel_alias}/index", // 频道路径模板
            "article""/articles/{article_id}", // 文章路径模板
            "enabledAndAuto"false // 是否启用并自动生成
        },
        "messageBoard": {
            "enabled"true, // 是否启用留言板
            "loginRequired"false // 是否需要登录
        },
        "editor": {
            "typesetting": {
                "fontFamily""", // 字体
                "fontSize""", // 字号
                "lineHeight""", // 行高
                "textIndent"false, // 首行缩进
                "imageCenterAlign"true, // 图片居中对齐
                "tableFullWidth"true, // 表格全宽
                "emptyLine""one", // 空行
                "halfWidthCharConversion"true // 半角字符转换
            }
        },
        "org": {
            "id": 1, // 组织ID
            "name""总部" // 组织名称
        },
        "model": {
            "id": 13, // 模型ID
            "name""站点设置模型" // 模型名称
        },
        "mobileStaticUrl""http://localhost:8080", // 移动端静态URL
        "normalStaticUrl""http://localhost:8080", // 普通静态URL
        "url""http://localhost:8080", // 网站URL
        "dy""", // 动态内容
        "api""/frontend", // API路径
        "staticUrl""http://localhost:8080", // 静态URL
        "dynamicUrl""http://localhost:8080", // 动态URL
        "filesPath""/templates/1/default/_files", // 文件路径
        "names": [
            "企业网站" // 名称列表
        ],
        "paths": [
            {
                "id": 1, // 路径ID
                "name""企业网站", // 路径名称
                "url""http://localhost:8080" // 路径URL
            }
        ],
        "title""企业网站", // 标题
        "staticBase""", // 静态基路径
        "targetBlank"false // 是否新窗口打开
    }
]

{
    "globalPermission"true, // 全局权限
    "loginDate""2024-05-19T10:53:14+08:00", // 登录日期
    "allStatusPermission"true, // 所有状态权限
    "avatar""/uploads/avatar/1/c53dc7ce57964a48a813430d2a0de8e1.jpg", // 头像路径
    "allArticlePermission"true, // 所有文章权限
    "allChannelPermission"true, // 所有频道权限
    "databaseType""mysql", // 数据库类型
    "permissions": [
        "backend", // 后端权限
        "*" // 全部权限
    ],
    "epDisplay"false, // 扩展权限显示
    "loginIp""127.0.0.1", // 登录IP
    "rank": 0, // 排名
    "epActivated"false, // 扩展权限激活
    "epExcludes": [
        "backupTemplates:page", // 排除项:备份模板页面
        "backupUploads:page", // 排除项:备份上传页面
        "incrementalUploads:page", // 排除项:增量上传页面
        "backupDatabase:page", // 排除项:备份数据库页面
        "processModel:page", // 排除项:流程模型页面
        "processInstance:page", // 排除项:流程实例页面
        "processHistory:page", // 排除项:流程历史页面
        "homepage:systemMonitor", // 排除项:系统监控主页
        "machine:code", // 排除项:机器代码
        "machine:license", // 排除项:机器许可证
        "generator:fulltext:reindexSite", // 排除项:全文索引站点生成器
        "articleReview:page", // 排除项:文章审核页面
        "article:internalPush", // 排除项:文章内部推送
        "article.externalPush", // 排除项:文章外部推送
        "survey:page", // 排除项:调查页面
        "sensitiveWord:page", // 排除项:敏感词页面
        "errorWord:page", // 排除项:错误词页面
        "siteSettings:editor:update", // 排除项:站点设置编辑器更新
        "menu.stat.articleStat", // 排除项:菜单统计文章统计
        "articleStatByUser:page", // 排除项:按用户统计文章页面
        "articleStatByOrg:page", // 排除项:按组织统计文章页面
        "articleStatByChannel:page", // 排除项:按频道统计文章页面
        "visitedPage:page", // 排除项:访问页面
        "entryPage:page", // 排除项:进入页面
        "visitSource:page", // 排除项:访问来源页面
        "visitEnv:page", // 排除项:访问环境页面
        "performanceType:page", // 排除项:性能类型页面
        "menu.stat.performanceStat", // 排除项:菜单统计性能统计
        "performanceStatByUser:page", // 排除项:按用户统计性能页面
        "performanceStatByOrg:page" // 排除项:按组织统计性能页面
    ],
    "epRank": 0, // 扩展权限排名
    "username""admin", // 用户名
    "grantPermissions": [
        "*" // 授予的权限(全部权限)
    ]
}

/**
     * @param lat 区域中心的纬度
     * @param lng 区域中心经度
     * @param distance 区域半径
     * @return 符合条件的数据
     */
    @Override
    public JsonResult fixedArea(Double lat, Double lng, Integer distance) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 以某点为中心,搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        distanceQueryBuilder
                .point(lat, lng)
                .distance(distance, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
 
        //查询封装
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        NativeSearchQuery build = nativeSearchQueryBuilder
                .withQuery(boolQueryBuilder)
                .build();
        return JsonResult.me().setResult(userRepository.search(build));
    }
package cn.nagisa.geo.doc;
 
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
 
/**
 * @author nagisa
 */
@Data
@Document(indexName = "test",type = "user")
public class UserDoc {
    private Long id;
 
    private String username;
 
    @GeoPointField
    private GeoPoint location;
}
PUT test 
{
  "mappings": {
    "user": {
      "properties": {
        "location": {
          "type""geo_point"
        }
      }
    }
  }
}
POST /map/_search
{
  "query": {
    "geo_distance":
    {
      "location":
      {
        "lon":116.433733
        ,"lat":39.908404
      },
      "distance":3000,
      "distance_type":"arc"
    }
  }
}
 

2、查找索引内距离北京站(116.433733,39.908404)3000米内的点
geo_distance涉及的参数如下

location:确定一个点;
distance:确定一个半径,单位米
distance_type:确定一个图形的类型;一般是圆形,arc

ES支持的地图检索方式有以下几种;

geo_distance geo_bounding_box geo_polygon

1、geo_distance:直线距离检索,如给定点A,要求返回地图上距离点A三千米的商家(点外卖场景)

代码拆分

这段代码是将所有的轨迹进行拆分纠偏,代码如下,grasp.driving是纠偏方法

var originPath = [  
    {"x":116.478928,"y":39.997761,"sp":19,"ag":0, "tm":1478031031},  
    // ... 其他点  
];  
  
function batchCorrect(path, callback) {  
    var batchSize = 100; // 批量大小,可以根据实际情况调整  
    var batches = [];  
    for (var i = 0; i < path.length; i += batchSize) {  
        batches.push(path.slice(i, i + batchSize));  
    }  
      
    var correctedPoints = [];  
    var batchIndex = 0;  
    function processBatch() {  
        if (batchIndex >= batches.length) {  
            callback(null, correctedPoints);  
            return;  
        }  
          
        var currentBatch = batches[batchIndex];  
        AMap.plugin('AMap.GraspRoad'function() {  
            var grasp = new AMap.GraspRoad();  
            grasp.driving(currentBatch, function(error, result) {  
                if (!error) {  
                    correctedPoints = correctedPoints.concat(result.data.points);  
                }  
                batchIndex++;  
                processBatch();  
            });  
        });  
    }  
      
    processBatch();  
}  
  
batchCorrect(originPath, function(error, correctedPath) {  
    if (!error) {  
        // 在这里使用纠偏后的轨迹  
        var newPath = correctedPath;  
        var distance = result.data.distance; // 里程  
    }  
});
$(function () {
            var orderLoadList = [[${model.orderLoad}]];
            //初始化地图对象,加载地图
            //         var position = [orderLoadList[i].varMapX, orderLoadList[i].varMapY];
 
            var map = new AMap.Map("container", {
                resizeEnable: true,
                center: [orderLoadList[0].varMapX, orderLoadList[0].varMapY],
                zoom: 13,
 
            });
            for (var i = 0; i < orderLoadList.length; i++) {
                var icon = "";
                if (i == 0) {
                    icon = "/content/images/load.png"
                } else {
                    icon = "/content/images/unLoad.png"
                }
                var marker = new AMap.Marker({
                    position: [orderLoadList[i].varMapX, orderLoadList[i].varMapY], // 基点位置
                    title: orderLoadList[i].varName,
                    icon: icon,
                    offset: new AMap.Pixel(0, 0), // 设置点标记偏移量
                    anchor: 'bottom-left', // 设置锚点方位,
                });
                map.add(marker);
            }
            var isGps=[[${model.order.isGps}]];
            var path=[(${model.order.varWheelPath==null?'[]':model.order.varWheelPath})];
            console.log('原始path长度=='+path.length+',内容==='+JSON.stringify(path));
            var isHaveCorrection=$("#isHaveCorrection").val();
            var pathAfterCorrection = [(${model.order.varWheelPathAfterCorrection==null?'[]':model.order.varWheelPathAfterCorrection})];
            //如果该线路已经纠偏,直接调用数据库里的纠偏后的数组进行画图
            if(isHaveCorrection=='true'){
                if(Array.isArray(pathAfterCorrection)){
                    if(pathAfterCorrection[0] && pathAfterCorrection[0] instanceof Object){
                        console.log('该路线已经纠偏完成,显示数据库中已经纠偏好的路线,数据库中已经纠偏的线路长度=='+pathAfterCorrection.length+',内容==='+JSON.stringify(pathAfterCorrection));
                        var path2=[];
                        for(var i =0;i<pathAfterCorrection.length;i+=1){
                            path2.push([pathAfterCorrection[i].x,pathAfterCorrection[i].y])
                        }
                        console.log('纠偏后保存到数据库中的路线画图:');
                        console.log(path2);
                        var polyline = new AMap.Polyline({
                            path: path2,
                            strokeWeight: 4, // 线条宽度,默认为 1
                            strokeOpacity: 1,
                            strokeColor: '#3780e0', // 线条颜色
                            lineJoin: 'round', // 折线拐点连接处样式
                            strokeStyle: 'dashed'
                        });
                        map.add(polyline);
                        map.setFitView();
                    }
                }
            }else{
                if (Array.isArray(path)) {
                    if (path[0]?.constructor === Object) {
                        var pathGPS=[];
                        var path
                        for(var j=0,lengt=path.length;j<lengt;j++){
                            pathGPS.push([path[j].x,path[j].y])
                        }
                        var pathGD=[];
                        function batchCorrect(path, callback) {
                            /*
                            var batchSize = 400; // 批量大小,可以根据实际情况调整
                            var batches = [];
                            for (var i = 0; i < path.length; i += batchSize) {
                                batches.push(path.slice(i, i + batchSize));
                            }*/
                            var zhengNum = Math.floor(path.length / 250);
                            var batches = splitArray(path, zhengNum);
                            function splitArray(array, n) {
                                var result = [];
                                if(n==0) {n=1;}
                                var step = Math.ceil(array.length / n);
                                for (var i = 0; i < n; i++) {
                                    result.push(array.slice(i * step, (i + 1) * step));
                                }
                                return result;
                            }
                            var correctedPoints = [];
                            var batchIndex = 0;
                            function processBatch() {
                                if (batchIndex >= batches.length) {
                                    console.log('批量纠偏完成');
                                    callback(null, correctedPoints);
                                    return;
                                }
                                var currentBatch = batches[batchIndex];
                                var path2 = [];
                                AMap.plugin('AMap.GraspRoad'function() {
                                    var grasp = new AMap.GraspRoad();
                                    grasp.driving(currentBatch, function(error, result) {
                                        if (!error) {
                                            console.log("正确入参长度=="+currentBatch.length);
                                            correctedPoints = correctedPoints.concat(result.data.points);
                                            console.log("纠偏后正确出参长度=="+correctedPoints.length+",内容==="+JSON.stringify(correctedPoints));
                                        }
                                        if(error){
                                            console.log("错误入参长度=="+currentBatch.length+",内容==="+JSON.stringify(currentBatch));
                                            console.log('错误编码=='+error.errcode);
                                            console.log('错误原因=='+error.errdetail);
                                            console.log('错误日志=='+error.errmsg);
                                            var newPath = currentBatch;
                                            for(var i =0;i<newPath.length;i+=1){
                                                path2.push([newPath[i].x,newPath[i].y])
                                            }
                                            console.log('将无法纠偏的线路直接用黑色线条画图');
                                            console.log(path2)
                                            var oldLine = new AMap.Polyline({
                                                path:path2,
                                                strokeWeight: 4, // 线条宽度,默认为 1
                                                strokeOpacity: 1,
                                                strokeColor: '#3780e0' // 线条颜色
                                            })
                                            map.add(oldLine);
                                            map.setFitView();
                                        }
                                        batchIndex++;
                                        processBatch();
                                    });
                                });
                            }
                            processBatch();
                        }
                        if(isGps==true){
                            console.log('该线路的值是gps格式');
                            AMap.convertFrom(pathGPS, 'gps'function (status, result) {
                                if (result.info === 'ok') {
                                    pathGD = result.locations; // Array.<LngLat>
                                    for(var t=0,leng=path.length;t<leng;t++){
                                        path[t].x=pathGD[t].lng;
                                        path[t].y=pathGD[t].lat;
                                    }
                                    var path2 = [];
                                    batchCorrect(path, function(error, correctedPath) {
                                        if (!error) {
                                            console.log("gps格式纠偏成功,纠偏后的路线长度=="+correctedPath.length+",内容==="+JSON.stringify(correctedPath));
                                            // 在这里使用纠偏后的轨迹
                                            for(var i =0;i<correctedPath.length;i+=1){
                                                path2.push([correctedPath[i].x,correctedPath[i].y]);
                                            }
                                            console.log('gps格式纠偏成功,即将画图');
                                            console.log(path2);
                                            var polyline = new AMap.Polyline({
                                                path: path2,
                                                strokeWeight: 4, // 线条宽度,默认为 1
                                                strokeOpacity: 1,
                                                strokeColor: '#3780e0', // 线条颜色
                                                lineJoin: 'round', // 折线拐点连接处样式
                                                strokeStyle: 'dashed'
                                            });
                                            map.add(polyline);
                                            map.setFitView();
                                            savaLineAfterCorrection(correctedPath);
                                        }
                                    });
                                }
                            });
                        }else{
                            console.log('该线路的值不是gps格式');
                            var path2 = [];
                            batchCorrect(path, function(error, correctedPath) {
                                if (!error) {
                                    console.log("非gps格式纠偏成功,纠偏后的路线长度=="+correctedPath.length+",内容==="+JSON.stringify(correctedPath));
                                    // 在这里使用纠偏后的轨迹
                                    for(var i =0;i<correctedPath.length;i+=1){
                                        path2.push([correctedPath[i].x,correctedPath[i].y]);
                                    }
                                    console.log('非gps格式纠偏成功,即将画图');
                                    console.log(path2);
                                    var polyline = new AMap.Polyline({
                                        path: path2,
                                        strokeWeight: 4, // 线条宽度,默认为 1
                                        strokeOpacity: 1,
                                        strokeColor: '#3780e0', // 线条颜色
                                        lineJoin: 'round', // 折线拐点连接处样式
                                        strokeStyle: 'dashed'
                                    });
                                    map.add(polyline);
                                    map.setFitView();
                                    savaLineAfterCorrection(correctedPath);
                                }
                            });
                        }
                    }else{
                        console.log('该线路数组中的内容不是对象,原始path长度=='+path.length+',内容==='+JSON.stringify(path));
                        AMap.convertFrom(path, 'gps'function (status, result) {
                            if (result.info === 'ok') {
                                var lnglats = result.locations; // Array.<LngLat>
                                console.log('该线路数组中的内容不是对象,直接画图');
                                console.log(lnglats);
                                var polyline = new AMap.Polyline({
                                    path: lnglats,
                                    strokeWeight: 4, // 线条宽度,默认为 1
                                    strokeOpacity: 1,
                                    strokeColor: '#3780e0', // 线条颜色
                                    lineJoin: 'round', // 折线拐点连接处样式
                                    strokeStyle: 'dashed'
                                });
                                map.add(polyline);
                            }
                        });
                    }
                }
            }
        })
function savaLineAfterCorrection(pathAfterCorrection) {
            var pathAfterCorrectionStr = JSON.stringify(pathAfterCorrection);
            if(null!=pathAfterCorrectionStr && pathAfterCorrectionStr!=''){
                $.ajax({
                    type'post',
                    url: '/transaction/updateOrderAfterCorrection',
                    dataType: 'Json',
                    data: {"id":$("#orderId").val(),
                        "isHaveCorrect":1,
                        "pathAfterCorrectionStr":pathAfterCorrectionStr},
                    async: false,
                    success: function (data) {
                    }
                });
            }
        }

由于高德地图的纠偏接口一次只支持500个点位,并且有访问次数限制,于是进行批量纠偏操作,并把纠偏的结果保存到数据库,下次再打开网页时显示已经纠偏过的轨迹

/**
     * @param lat 区域中心的纬度
     * @param lng 区域中心经度
     * @param distance 区域半径
     * @return 符合条件的数据
     */
    @Override
    public JsonResult fixedArea(Double lat, Double lng, Integer distance) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        // 以某点为中心,搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        distanceQueryBuilder
                .point(lat, lng)
                .distance(distance, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
 
        //查询封装
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        NativeSearchQuery build = nativeSearchQueryBuilder
                .withQuery(boolQueryBuilder)
                .build();
        return JsonResult.me().setResult(userRepository.search(build));
    }
package cn.nagisa.geo.doc;
 
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
 
/**
 * @author nagisa
 */
@Data
@Document(indexName = "test",type = "user")
public class UserDoc {
    private Long id;
 
    private String username;
 
    @GeoPointField
    private GeoPoint location;
}

优化根据经纬度,搜索制定距离范围内的数据

  1. **实例化 ElasticSResultSet**:

  • 这个 ElasticSResultSet 实例没有在代码段中使用。你需要确保这个类的用途,并在后面的查询结果处理中使用它。
  • 设置索引

    • 确保 index 变量已经正确定义且包含了你想查询的索引名。
  • 地理位置查询

    • 你的 GeoDistanceQueryBuilder 使用了 "location" 字段和从 siteInfoBO 获取的经纬度。确保这个字段已经被映射为geo-point类型,并且 siteInfoBO.getLatd()siteInfoBO.getLngd() 正确返回经纬度。
  • 分页设置

    • 分页设置看起来是正确的。你通过 siteInfoBO.getPageIndex()siteInfoBO.getPageSize() 来计算 from 参数,这是标准的分页实现方式。
  • 查询构建和执行

    • 代码中缺少执行查询的部分。通常,你会需要一个Elasticsearch客户端来执行这个 searchRequest

    加群联系作者vx:xiaoda0423

    仓库地址:https://github.com/webVueBlog/JavaGuideInterview

    算法猫叔
    程序员:进一寸有一寸的欢喜
     最新文章