Skip to content

Commit 10aa1d8

Browse files
committed
feat: add Gaode Map search support with configurable API key and JWT authentication
1 parent e782a26 commit 10aa1d8

6 files changed

Lines changed: 263 additions & 64 deletions

File tree

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ RoadbookMaker是一个功能强大的基于网页的地图标记和路线规划
2929

3030
### 地图操作
3131
- **多地图源支持**: OpenStreetMap、高德地图、Google地图、ESRI卫星图等
32-
- **智能搜索**: 集成Photon、Nominatim、Overpass、百度地图、天地图等多种搜索服务
32+
- **智能搜索**: 集成Photon、Nominatim、Overpass、高德地图、百度地图、天地图等多种搜索服务
3333
- **坐标转换**: 自动处理中国地图坐标系(GCJ-02、BD-09)到标准GPS的转换
3434

3535
### 标记点管理
@@ -113,6 +113,29 @@ cd roadbook
113113
./roadbook-api
114114
```
115115

116+
d. **(可选) 配置高德地图搜索**
117+
本项目已支持高德地图搜索。如需启用,请在 `backend/configs/config.json` 文件中添加 `search` 配置块,并填入您自己的高德 API Key。
118+
119+
**配置示例:**
120+
```json
121+
{
122+
"port": 5436,
123+
"...": "...",
124+
"search": {
125+
"providers": {
126+
"gaode": {
127+
"key": "your_gaode_api_key_here",
128+
"login_required": false
129+
}
130+
}
131+
}
132+
}
133+
```
134+
- `key` (string): 您的高德Web服务API Key。
135+
- `login_required` (boolean, 可选): 设置为 `true` 时,调用高德搜索接口需要进行JWT登录认证。默认为 `false`(公开访问)。
136+
137+
如果`key`字段为空或没有此`search`配置块,高德搜索将不可用。
138+
116139
3. **配置Nginx** (可选但推荐)
117140
```bash
118141
sudo cp ./nginx.prod.conf /etc/nginx/sites-available/roadbook
@@ -310,6 +333,7 @@ roadbook/
310333
### 地图搜索服务
311334
- `GET /api/cnmap/search?q={query}` - 百度地图搜索
312335
- `GET /api/tianmap/search?q={query}` - 天地图搜索
336+
- `GET /api/gaode/search?q={query}` - 高德地图搜索
313337

314338
### 用户认证
315339
- `POST /api/v1/login` - 用户登录(限流保护)

backend/internal/config/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ type Config struct {
2323

2424
// SearchProviderConfig holds configuration for a single search provider, like an API key.
2525
type SearchProviderConfig struct {
26-
Key string `json:"key"`
26+
Key string `json:"key"`
27+
LoginRequired bool `json:"login_required,omitempty"`
2728
}
2829

2930
// SearchProviders holds configurations for all supported search providers.

backend/internal/handler/search.go

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,29 @@ import (
1010
"github.com/gin-gonic/gin"
1111
)
1212

13-
func GaodeSearchHandler(apiKey string) gin.HandlerFunc {
14-
return func(c *gin.Context) {
15-
query := c.Query("q")
16-
results, err := gaode.Search(query, apiKey)
17-
if err != nil {
18-
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
19-
return
20-
}
21-
c.JSON(http.StatusOK, results)
13+
// SearchHandlers holds dependencies for search-related handlers.
14+
type SearchHandlers struct {
15+
cfg config.Config
16+
}
17+
18+
// NewSearchHandlers creates a new instance of SearchHandlers.
19+
func NewSearchHandlers(cfg config.Config) *SearchHandlers {
20+
return &SearchHandlers{cfg: cfg}
21+
}
22+
23+
// GaodeSearchHandler handles Gaode search requests.
24+
func (h *SearchHandlers) GaodeSearchHandler(c *gin.Context) {
25+
query := c.Query("q")
26+
results, err := gaode.Search(query, h.cfg.Search.Providers.Gaode.Key)
27+
if err != nil {
28+
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
29+
return
2230
}
31+
c.JSON(http.StatusOK, results)
2332
}
2433

25-
func BaiduSearchHandler(c *gin.Context) {
34+
// BaiduSearchHandler handles Baidu search requests.
35+
func (h *SearchHandlers) BaiduSearchHandler(c *gin.Context) {
2636
query := c.Query("q")
2737
results, err := baidu.Search(query)
2838
if err != nil {
@@ -32,7 +42,8 @@ func BaiduSearchHandler(c *gin.Context) {
3242
c.JSON(http.StatusOK, results)
3343
}
3444

35-
func TianmapSearchHandler(c *gin.Context) {
45+
// TianmapSearchHandler handles Tianmap search requests.
46+
func (h *SearchHandlers) TianmapSearchHandler(c *gin.Context) {
3647
query := c.Query("q")
3748
results, err := tianmap.Search(query)
3849
if err != nil {
@@ -43,18 +54,25 @@ func TianmapSearchHandler(c *gin.Context) {
4354
}
4455

4556
type ProviderStatus struct {
46-
Name string `json:"name"`
47-
Enabled bool `json:"enabled"`
48-
Label string `json:"label"`
57+
Name string `json:"name"`
58+
Enabled bool `json:"enabled"`
59+
Label string `json:"label"`
60+
LoginRequired bool `json:"login_required"`
4961
}
5062

51-
func GetSearchProvidersHandler(cfg config.Config) gin.HandlerFunc {
52-
return func(c *gin.Context) {
53-
providers := []ProviderStatus{
54-
{Name: "tianmap", Enabled: true, Label: "天地图"},
55-
{Name: "baidu", Enabled: true, Label: "百度"}, // baidu is hardcoded, so always "enabled" from backend pov
56-
{Name: "gaode", Enabled: cfg.Search.Providers.Gaode.Key != "", Label: "高德"},
57-
}
58-
c.JSON(http.StatusOK, providers)
63+
// GetSearchProvidersHandler handles requests for search provider status.
64+
func (h *SearchHandlers) GetSearchProvidersHandler(c *gin.Context) {
65+
// NOTE: For providers like baidu and tianmap that don't have a login_required config yet,
66+
// the default value of a boolean in Go is false, which is the desired behavior.
67+
providers := []ProviderStatus{
68+
{Name: "tianmap", Enabled: true, Label: "天地图", LoginRequired: false},
69+
{Name: "baidu", Enabled: true, Label: "百度", LoginRequired: false},
70+
{
71+
Name: "gaode",
72+
Enabled: h.cfg.Search.Providers.Gaode.Key != "",
73+
Label: "高德",
74+
LoginRequired: h.cfg.Search.Providers.Gaode.LoginRequired,
75+
},
5976
}
77+
c.JSON(http.StatusOK, providers)
6078
}

backend/internal/server/server.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func NewRouter(cfg config.Config) *gin.Engine {
6161

6262
authHandler := handler.NewAuthHandler(authService)
6363
planHandler := handler.NewPlanHandler(planRepo)
64+
searchHandlers := handler.NewSearchHandlers(cfg) // Create search handlers instance
6465

6566
// API v1 路由组
6667
v1 := r.Group("/api/v1")
@@ -100,13 +101,23 @@ func NewRouter(cfg config.Config) *gin.Engine {
100101
{
101102
api.GET("/ping", handler.Ping)
102103
api.HEAD("/ping", handler.Ping)
103-
api.GET("/cnmap/search", handler.BaiduSearchHandler)
104-
api.GET("/tianmap/search", handler.TianmapSearchHandler)
105-
api.GET("/gaode/search", handler.GaodeSearchHandler(cfg.Search.Providers.Gaode.Key))
106-
api.GET("/search/providers", handler.GetSearchProvidersHandler(cfg))
104+
api.GET("/cnmap/search", searchHandlers.BaiduSearchHandler)
105+
api.GET("/tianmap/search", searchHandlers.TianmapSearchHandler)
106+
api.GET("/gaode/search", applyAuthMiddleware(cfg.Search.Providers.Gaode.LoginRequired, authService, searchHandlers.GaodeSearchHandler)...)
107+
api.GET("/search/providers", searchHandlers.GetSearchProvidersHandler)
107108
// 新增trafficpos接口
108109
api.GET("/trafficpos", handler.GetTrafficPos)
109110
}
110111

111112
return r
112113
}
114+
115+
// applyAuthMiddleware conditionally applies JWTAuthMiddleware if loginRequired is true,
116+
// returning a HandlersChain suitable for gin.
117+
func applyAuthMiddleware(loginRequired bool, authService auth.Authenticator, handler gin.HandlerFunc) gin.HandlersChain {
118+
if loginRequired {
119+
return gin.HandlersChain{middleware.JWTAuthMiddleware(authService), handler}
120+
}
121+
return gin.HandlersChain{handler}
122+
}
123+

static/index.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ <h1 id="mainTitle">RoadbookMaker</h1>
2727
<input type="text" id="searchInput" placeholder="搜索地点...">
2828
<select id="searchMethodSelect" class="btn" style="background: linear-gradient(135deg, rgba(255,255,255,0.3), rgba(255,255,255,0.15)); color: #fff; border: 2px solid rgba(255,255,255,0.5); padding: 0.6rem 1.2rem; border-radius: 25px; font-size: 0.9rem; font-weight: 600; cursor: pointer; margin-left: 0.5rem;">
2929
<option value="auto">🗺️自动分配搜索</option>
30-
<option value="gaode">🤩高德搜索</option>
31-
<option value="nominatim">😓Nominatim搜索</option>
32-
<option value="overpass">😓Overpass搜索</option>
33-
<option value="mapsearch">😀MapSearch搜索</option>
34-
<option value="photon">😀Photon搜索</option>
30+
<option value="nominatim">Nominatim搜索</option>
31+
<option value="overpass">Overpass搜索</option>
32+
<option value="mapsearch">MapSearch搜索</option>
33+
<option value="photon">Photon搜索</option>
3534
<!-- fxxk you baidu map -->
36-
<option value="cnsearch" style="display:none;">🤩CNSearch搜索</option>
37-
<option value="tiansearch">🤩TianSearch搜索</option>
35+
<option value="cnsearch" style="display:none;">CNSearch搜索</option>
36+
<option value="gaode">高德搜索</option>
37+
<option value="tiansearch">TianSearch搜索</option>
3838
</select>
3939
<!-- 搜索结果下拉框 -->
4040
<div id="searchResults" class="search-results" style="display: none;">

0 commit comments

Comments
 (0)