ソースを参照

Add 完善查询商家列表

enilu 6 年 前
コミット
06731a464d

+ 8 - 0
README.md

@@ -3,6 +3,14 @@
 - 基于spring boot和vue的前后端分离的外卖系统
 - 包含完整的手机端,后台管理功能
 
+## 技术选型
+- 核心框架:Spring Boot
+- 数据库层:Spring data jpa/Spring data mongodb
+- 数据库连接池:Druid
+- 缓存:Ehcache
+- 前端:Vue.js
+- 数据库:mysql5.5以上,Mongodb4.0(不要使用最新版4.2)
+
 ## 快速开始
 - 数据存储采用了mysql和mongodb,其中基础管理配置功能使用mysql,业务数据使用mongodb存储。
 - 创建mysql数据库

+ 29 - 11
flash-waimai-api/src/main/java/cn/enilu/flash/api/controller/business/ShopController.java

@@ -12,11 +12,15 @@ import cn.enilu.flash.dao.MongoRepository;
 import cn.enilu.flash.service.front.IdsService;
 import cn.enilu.flash.service.front.PositionService;
 import cn.enilu.flash.utils.BeanUtil;
+import cn.enilu.flash.utils.Lists;
 import cn.enilu.flash.utils.Maps;
 import cn.enilu.flash.utils.factory.Page;
+import cn.enilu.flash.utils.gps.Distance;
 import org.nutz.json.Json;
 import org.nutz.lang.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.geo.GeoResult;
+import org.springframework.data.geo.GeoResults;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
@@ -46,23 +50,37 @@ public class ShopController extends BaseController {
 
     @RequestMapping(value = "restaurants", method = RequestMethod.GET)
     public Object listShop(@RequestParam(value = "latitude", required = false) String latitude,
-                           @RequestParam(value = "longitude", required = false) String longitude) {
+                           @RequestParam(value = "longitude", required = false) String longitude,
+                           @RequestParam(value = "restaurant_category_ids[]",required = false) Long[] categoryIds) {
         if (com.google.common.base.Strings.isNullOrEmpty(latitude) || "undefined".equals(latitude)
                 || com.google.common.base.Strings.isNullOrEmpty(longitude) || "undefined".equals(longitude)) {
             Page<Shop> page = new PageFactory<Shop>().defaultPage();
             return Rets.success(mongoRepository.queryPage(page, Shop.class));
         } else {
             //查询指定经纬度范围内的餐厅
-            //todo mongo4.2之后不支持geoNear command,暂时先用下面方法返回测试数据
-//            GeoResults<Map> geoResults = mongoRepository.near(Double.valueOf(longitude), Double.valueOf(latitude), "shops");
-//            List<GeoResult<Map>> geoResultList = geoResults.getContent();
-//            List list = Lists.newArrayList();
-//            for (int i = 0; i < geoResultList.size(); i++) {
-//                list.add(geoResultList.get(i).getContent());
-//            }
-//            return Rets.success(list);
-            Page<Shop> page = new PageFactory<Shop>().defaultPage();
-            return Rets.success(mongoRepository.queryPage(page, Shop.class));
+            Map<String,Object> params = Maps.newHashMap();
+            if(categoryIds!=null&&categoryIds.length>0){
+                Map map = (Map) mongoRepository.findOne(categoryIds[0],"categories");
+                if(map!=null) {
+                    params.put("category",map.get("name").toString());
+                }
+            }
+            GeoResults<Map> geoResults = mongoRepository.near(Double.valueOf(longitude), Double.valueOf(latitude), "shops",params);
+            List<GeoResult<Map>> geoResultList = geoResults.getContent();
+            List list = Lists.newArrayList();
+            for (int i = 0; i < geoResultList.size(); i++) {
+                Map map = geoResultList.get(i).getContent();
+                Distance distance = new Distance(Double.valueOf(longitude),Double.valueOf(latitude),
+                        Double.valueOf(map.get("longitude").toString()),Double.valueOf(map.get("latitude").toString()));
+                map.put("distance",distance.getDistance());
+
+                map.put("order_lead_time","30分钟");
+                list.add(map);
+            }
+            Page<Map> page = new PageFactory<Map>().defaultPage();
+            page.setTotal(list.size());
+            page.setRecords(list);
+            return Rets.success(page);
         }
     }
 

+ 1 - 1
flash-waimai-core/src/main/java/cn/enilu/flash/bean/entity/front/Shop.java

@@ -44,7 +44,7 @@ public class Shop extends BaseMongoEntity {
     private String float_minimum_order_amount;
     private String float_delivery_fee;
     private String distance;
-    private String order_lead_time;
+    private String order_lead_time="30分钟";
     private String description;
     private Map delivery_mode;
     private List activities;

+ 11 - 2
flash-waimai-core/src/main/java/cn/enilu/flash/dao/MongoRepository.java

@@ -7,7 +7,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
+import org.springframework.data.geo.Distance;
 import org.springframework.data.geo.GeoResults;
+import org.springframework.data.geo.Metrics;
+import org.springframework.data.geo.Point;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.NearQuery;
@@ -151,8 +154,14 @@ public class MongoRepository {
         return mongoTemplate.find(Query.query(criteria), Map.class, collectionName);
     }
 
-    public GeoResults<Map> near(double x, double y, String collectionName) {
-        return mongoTemplate.geoNear(NearQuery.near(x, y), Map.class, collectionName);
+    public GeoResults<Map> near(double x, double y, String collectionName,Map<String,Object> params) {
+        Point location = new Point(x,y);
+        NearQuery nearQuery = NearQuery.near(location).maxDistance(new Distance(5, Metrics.MILES));
+        if(params!=null && !params.isEmpty()){
+            Query query = Query.query(criteria(params));
+            nearQuery.query(query);
+        }
+        return mongoTemplate.geoNear(nearQuery,Map.class,collectionName);
     }
 
     public long count(Class klass) {

+ 2 - 2
flash-waimai-core/src/main/java/cn/enilu/flash/service/BaseService.java

@@ -120,13 +120,13 @@ public abstract  class BaseService<T, ID extends Serializable, R extends BaseRep
         }
 
     }
-    @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#record.id")
+    @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#p0.id")
     @Override
     public T update(T record) {
         return dao.save(record);
     }
     @Override
-    @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#record.id")
+    @CacheEvict(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#p0.id")
     public T saveOrUpdate(T record) {
         return dao.save(record);
     }

+ 9 - 1
flash-waimai-core/src/main/java/cn/enilu/flash/service/front/PositionService.java

@@ -1,6 +1,7 @@
 package cn.enilu.flash.service.front;
 
 import cn.enilu.flash.bean.AppConfiguration;
+import cn.enilu.flash.bean.constant.cache.Cache;
 import cn.enilu.flash.bean.vo.business.CityInfo;
 import cn.enilu.flash.dao.MongoRepository;
 import cn.enilu.flash.utils.HttpClients;
@@ -10,6 +11,7 @@ import org.nutz.mapl.Mapl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import java.net.URLEncoder;
@@ -76,8 +78,9 @@ public class PositionService {
         }
         return null;
     }
-
+    @Cacheable(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#cityName+'-'+#keyword")
     public List searchPlace(String cityName, String keyword) {
+        logger.info("获取地址信息:{},{}",cityName,keyword);
         Map<String, String> params = Maps.newHashMap();
         params.put("key", appConfiguration.getTencentKey());
         params.put("keyword", URLEncoder.encode(keyword));
@@ -136,7 +139,10 @@ public class PositionService {
      * @param geohash
      * @return
      */
+
+    @Cacheable(value = Cache.APPLICATION ,key = "#root.targetClass.simpleName+':'+#geohash")
     public Map pois(String geohash) {
+        logger.info("获取地址信息:{}",geohash);
         Map<String, String> map = Maps.newHashMap();
         map.put("location", geohash);
         map.put("key", appConfiguration.getTencentKey());
@@ -150,6 +156,8 @@ public class PositionService {
                 result.put("name", Mapl.cell(response, "result.formatted_addresses.recommend"));
                 result.put("latitude", Mapl.cell(response, "result.location.lat"));
                 result.put("longitude", Mapl.cell(response, "result.location.lng"));
+            }else{
+                logger.error("获取地理位置信息失败:{}",str);
             }
 
         } catch (Exception e) {

+ 1 - 16
flash-waimai-core/src/main/java/cn/enilu/flash/service/system/FileService.java

@@ -127,23 +127,8 @@ public class FileService extends BaseService<FileInfo,Long,FileInfoRepository> {
         fileInfo.setAblatePath(configCache.get(ConfigKeyEnum.SYSTEM_FILE_UPLOAD_PATH.getValue()) + File.separator+fileInfo.getRealFileName());
         return fileInfo;
     }
-
+    @Cacheable(value = Cache.APPLICATION, key = "'" + CacheKey.FILE_INFO + "'+#fileName")
     public FileInfo getByName(String fileName) {
-//        File file = new File("E:\\flash-waimai\\img\\foods");
-//        File[] imglist = file.listFiles();
-//        List<FileInfo> fileInfoList = Lists.newArrayList();
-//        for(int i=0;i<imglist.length;i++){
-//            String name = imglist[i].getName();
-//            FileInfo fileInfo = fileInfoRepository.findByRealFileName(name);
-//            if(fileInfo!=null){
-//                continue;
-//            }
-//            fileInfo = new FileInfo();
-//            fileInfo.setRealFileName(name);
-//            fileInfo.setOriginalFileName("食品图片");
-//            fileInfoList.add(fileInfo);
-//        }
-//        fileInfoRepository.saveAll(fileInfoList);
         FileInfo fileInfo = fileInfoRepository.findByRealFileName(fileName);
         if(fileInfo!=null) {
             fileInfo.setAblatePath(configCache.get(ConfigKeyEnum.SYSTEM_FILE_UPLOAD_PATH.getValue()) + File.separator + fileInfo.getRealFileName());

+ 72 - 0
flash-waimai-core/src/main/java/cn/enilu/flash/utils/gps/Distance.java

@@ -0,0 +1,72 @@
+package cn.enilu.flash.utils.gps;
+
+/**
+ * 根据gps坐标计算距离
+ * @author :enilu
+ * @date :Created in 2019/9/16 19:38
+ */
+
+public class Distance {
+    private Double[] start;
+    private Double[] end;
+    public Distance(Double srcLng,Double srcLat,Double destLng,Double destLat){
+        this.start = new Double[]{srcLat,srcLng};
+        this.end = new Double[]{destLat,destLng};
+    }
+    public Distance(Double[] start, Double[] end) {
+        this.start = start;
+        this.end = end;
+    }
+
+    public Double[] getStart() {
+        return start;
+    }
+
+    public void setStart(Double[] start) {
+        this.start = start;
+    }
+
+    public Double[] getEnd() {
+        return end;
+    }
+
+    public void setEnd(Double[] end) {
+        this.end = end;
+    }
+
+    /**
+     * 返回两点间的距离,单位:米
+     * @return
+     */
+    public Double getDistance() {
+        if (start == null || end == null) {
+            return null;
+        }
+        double lon1 = (Math.PI / 180) * start[0];
+        double lon2 = (Math.PI / 180) * end[0];
+        double lat1 = (Math.PI / 180) * start[1];
+        double lat2 = (Math.PI / 180) * end[1];
+
+        // 地球半径
+        double R = 6371;
+        // 两点间距离 km,如果想要米的话,结果*1000就可以了
+        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * R;
+        return d*1000;
+
+    }
+
+    /*
+     * 计算两点之间距离
+     *
+     * @param start
+     *
+     * @param end
+     *
+     * @return 米
+     */
+    public Double getDistance(Double[] start, Double[] end) {
+        this.start = start;
+        this.end = end;
+        return getDistance();
+    }
+}

+ 11 - 7
flash-waimai-mobile/src/components/common/shoplist.vue

@@ -30,15 +30,17 @@
 					<h5 class="fee_distance">
 						<p class="fee">
 							¥{{item.float_minimum_order_amount}}起送
-							<span class="segmentation">/</span>
+							<span class="segmentation">|</span>
 							{{item.piecewise_agent_fee.tips}}
 						</p>
 						<p class="distance_time">
-							<span v-if="Number(item.distance)">{{item.distance > 1000? (item.distance/1000).toFixed(2) + 'km': item.distance + 'm'}}
-								<span class="segmentation">/</span>
-							</span>
-							<span v-else>{{item.distance}}</span>
-							<span class="segmentation">/</span>
+							<template v-if="Number(item.distance)">{{item.distance > 1000? (item.distance/1000).toFixed(2) + 'km': item.distance + 'm'}}
+								<span class="segmentation">|</span>
+							</template>
+							<template v-else>
+                {{item.distance}}
+							<span class="segmentation">|</span>
+              </template>
 							<span class="order_time">{{item.order_lead_time}}</span>
 						</p>
 					</h5>
@@ -151,7 +153,8 @@ export default {
 		async listenPropChange(){
 			this.showLoading = true;
 			this.offset = 0;
-			let res = await shopList(this.latitude, this.longitude, this.offset, '', this.restaurantCategoryIds, this.sortByType, this.deliveryMode, this.supportIds);
+			let resResponse = await shopList(this.latitude, this.longitude, this.offset, '' , this.restaurantCategoryIds, this.sortByType, this.deliveryMode, this.supportIds);
+			let res = resResponse.records
 			this.hideLoading();
 			//考虑到本地模拟数据是引用类型,所以返回一个新的数组
 			this.shopListArr = [...res];
@@ -177,6 +180,7 @@ export default {
 	watch: {
 		//监听父级传来的restaurantCategoryIds,当值发生变化的时候重新获取餐馆数据,作用于排序和筛选
 		restaurantCategoryIds: function (value){
+		  console.log('watchids',value)
 			this.listenPropChange();
 		},
 		//监听父级传来的排序方式

+ 2 - 1
flash-waimai-mobile/src/page/food/food.vue

@@ -295,8 +295,9 @@ export default {
     },
     //选中Category右侧列表的某个选项时,进行筛选,重新获取数据并渲染
     getCategoryIds(id, name) {
-	  console.log(id, name)
+	    console.log(id, name)
       this.restaurant_category_ids = id;
+      this.restaurant_category_id = id;
       this.sortBy = "";
       this.foodTitle = this.headTitle = name;
     },