单车第七天

用户上传头像,使用云存储。
我们这里使用七牛
七牛javasdk
https://developer.qiniu.com/kodo/sdk/1239/java#upload-file
1、首先导入pom
2、写个上传图片的工具类

/**
 * @Author ljs
 * @Description 七牛上传工具类
 * @Date 2018/10/4 10:31
 **/
public class QiniuFileUploadUtil {

    public static String uploadHeadImg(MultipartFile file) throws IOException {
        Configuration cfg = new Configuration(Zone.zone0());
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(Constants.QINIU_ACCESS_KEY, Constants.QINIU_SECRET_KEY);
        String upToken = auth.uploadToken(Constants.QINIU_HEAD_IMG_BUCKET_NAME);
        //key为null表示使用文件的哈希值表示
        //内存中的字节数组上传到空间中
        Response response = uploadManager.put(file.getBytes(), null, upToken);
        //解析上传成功的结果
        DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
        return putRet.key;
    }
}

3、接着只要更新数据库user的headimg把它更新为7牛上的url就行了,这样移动端拿到之后就可以直接访问。

/**
     * Author ljs
     * Description 更新数据库用户的head_img
     * Date 2018/10/4 11:10
     **/
    @Override
    public String uploadHeadImg(MultipartFile file, Long userId) throws MaMaBikeException {
        try {
            //获取用户
            User ue = userMapper.selectByPrimaryKey(userId);
            //七牛上传,返回哈希值
            String headImg = QiniuFileUploadUtil.uploadHeadImg(file);
            //更新用户头像url
            ue.setHeadImg(headImg);
            userMapper.updateByPrimaryKey(ue);
            //返回给移动端访问url的前缀,数据库是不存前缀的,存的是哈希值
            return Constants.QINIU_HEAD_IMG_BUCKET_URL + "/" + Constants.QINIU_HEAD_IMG_BUCKET_NAME;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new MaMaBikeException("头像上传失败");
        }
    }
/**
     * Author ljs
     * Description 用户更新头像
     * Date 2018/10/4 14:43
     **/
    @RequestMapping(value = "/uploadHeadImg", method = RequestMethod.POST)
    public ApiResult uploadHeadImg(HttpServletRequest request, MultipartFile file) throws MaMaBikeException{

        ApiResult resp = new ApiResult();
        try{
            UserElement ue = getCurrenUser();
            userService.uploadHeadImg(file, ue.getUserId());
            resp.setMessage("上传成功");
        }catch (MaMaBikeException e){
            resp.setCode(e.getStatusCode());
            resp.setMessage(e.getMessage());
        } catch (Exception e) {
            log.error("Fail to update user info", e);
            resp.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
            resp.setMessage("内部错误");
        }
        return resp;
    }

记住headers里不用设置content
不然会报错no multipart boundary was found
postman上传文件
https://blog.csdn.net/maowendi/article/details/80537304
关于文件上传遇到的问 no multipart boundary was found
https://blog.csdn.net/sanjay_f/article/details/47407063

image.png

image.png

最后7牛上也有文件,ok测试成功。

开始单车模块
功能列表

  • 用户打开app,就上传用户的经纬度到服务器,然后服务器就根据该经纬度查找附近的单车然后返回单车经纬度列表,然后安卓对接百度地图,把这些经纬度列表的每个坐标设置为我们自己的单车图标。
  • 用户开锁操作,及时推送,服务端主动告诉客户端,可以传播一个电流啥的,把单车上的锁打开,这是硬件的知识。
  • 用户开锁之后,改变单车状态,让其他用户搜素不到
  • 骑行历史记录,用户一边骑车,车子不间断上报坐标,服务器记录。

1、服务器查找附近单车怎么实现
geohash算法
2、如何存储这些地理坐标
mysql 最强是事物 acid 存储比较关键的数据
但是mysql对geohash支持不好,这些地理坐标我们需要频繁用到,
nosql对geohash比较好,nosql事物很弱甚至不支持,但是效率很高,例如redis。

一般操作地理坐标的 有两种方案一种是使用Mongodb,一种是elasticsearch,全文检索的中间件,可以理解为数据库。

3、首先不能全部交给mongodb,因为我们的单车还是需要事务的,订单啥的,所以mysql需要一个bike表,单车编号就交给mysql,经纬度就交给mongodb
这是mysql的bike表字段
number 单车编号 标识唯一一辆单车
type 1 码码单车 2 码码Little
enable_flag 1 可用 2 不可用

扫码上传编号就知道你骑那辆车了。
mongdb表,例子:bike_no单车编号,location包括单车类型和经纬度,status可用不可用

{
    "_id" : ObjectId("5bb62a1a5dce96ed90adb50b"),
    "bike_no" : "28000010",
    "location" : {
        "type" : "Point",
        "corridinates" : [ 
            113.499912, 
            23.456307
        ]
    },
    "status" : 1
}

4、模拟数据生成单车
解决问题:
如何确定单车的编号是唯一并且是递增的(单车编号不是id)
解决方法:
我们可以使用mysql的主键自增的特性,也就是说我们创建另一个表并且有相对应的bean然后每次创建单车数据之前先new一个bean,然后把bean的id赋值给单车的编号就行。这表就两个字段


image.png

就只是获取个id,我们就没必要generater,我们自己创建bean,然后在bike的xml写generateBikeNo语句就行。

@Data
public class BikeNoGen {

    private Long autoIncNo;

    private byte whatEver;
}

配置了useGeneratedKeys="true",keyProperty="对象的属性而不是数据库里的字段",就能保证主键值自增并且把主键返回,这样就能在java里获得主键

 <insert id="generateBikeNo" parameterType="com.ljs.mamabike.bike.entity.BikeNoGen" useGeneratedKeys="true" keyProperty="autoIncNo">
    insert into auto_inc_no (what_ever)
    values (1)
  </insert>

记住使用insertselective,这样才可以使用默认值而不会报null错误了,不要使用insert。

/**
     * Author ljs
     * Description 生成单车
     * Date 2018/10/4 23:36
     **/
    @Override
    public void generateBike() throws MaMaBikeException {
        //生成单车编号
        BikeNoGen bikeNo = new BikeNoGen();
        bikeMapper.generateBikeNo(bikeNo);
        Long no = bikeNo.getAutoIncNo();
        //生成单车
        Bike bike = new Bike();
        bike.setNumber(no);
        bike.setType((byte) 2);
        bikeMapper.insertSelective(bike);
    }

最后也测试成功了,然后多运行几次就单车编号唯一且自增就解决了。
http://www.cnblogs.com/JoeyWong/p/9299282.html
https://www.cnblogs.com/caizhen/p/9170446.html
http://ysj5125094.iteye.com/blog/2185024
5、开始解决附近的单车问题还有单车离你多远的问题
查找附近单车,就是传递你的经纬度坐标给后台,然后我们去数据库里查询你附近50米(你自己定)有多少辆单车,然后移动端显示。
而我们的单车的经纬度是存在mongodb里的,mybatis是操作关系型的,所以我们不能使用mybatis来操作mongodb的crud。我们使用spring-data来操作。
加入pom

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

配置

#springdata
  data:
     # mongoDB    #mongodb note:mongo3.x will not use host and port,only use uri
    mongodb:
      uri: mongodb://localhost:27017/mama-bike

既然不能使用mybatis,我们就需要再创建一个service类来操作mongodb的crud啦,当然也要有对应的bean

@Data
public class BikeLocation {

    private String id;

    private Long bikeNumber;

    private int status;

    private Double[] coordinates;

    private Double distance;

}

参数:collection查哪个表,locationField查哪个字段,point用户上传的坐标,minDistance最近,maxDistance最远,limit查几条,querymongodb放查询条件例如单车类型,还有单车状态为1,filed是查询出来后你要显示哪几个字段你可以做一个限制但是这里没有限制因为我们所有字段都要
然后主要靠dbobject来拼接mongodb的语句。反正每一个大括号就加一个BasicObject就行。
mongodb 地理位置搜寻https://blog.csdn.net/fdipzone/article/details/46285521/

/**
 * @Author ljs
 * @Description 单车定位服务类,使用spring-data来操作mongodb
 * @Date 2018/10/5 13:14
 **/
@Component
@Slf4j
public class BikeGeoService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * Author ljs
     * Description 查找某经坐标点附近某范围内坐标点 由近到远
     * Date 2018/10/5 17:37
     **/
    public List<BikeLocation> findNearBike(String collection, String locationField, Point center,
                                           long minDistance, long maxDistance, DBObject query, DBObject fields, int limit) throws MaMaBikeException{
        try {
            if (query == null) {
                query = new BasicDBObject();
            }
            query.put(locationField,
                    new BasicDBObject("$nearSphere",
                            new BasicDBObject("$geometry",
                                    new BasicDBObject("type", "Point")
                                            .append("coordinates", new double[]{center.getLongitude(), center.getLatitude()}))
                                    .append("$minDistance", minDistance)
                                    .append("$maxDistance", maxDistance)
                    ));
            query.put("status", 1);
            List<DBObject> objList = mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
            List<BikeLocation> result = new ArrayList<>();
            for (DBObject obj : objList) {
                BikeLocation location = new BikeLocation();
                location.setBikeNumber(((Integer) obj.get("bike_no")).longValue());
                location.setStatus((Integer) obj.get("status"));
                BasicDBList coordinates = (BasicDBList) ((BasicDBObject) obj.get("location")).get("coordinates");
                Double[] temp = new Double[2];
                coordinates.toArray(temp);
                location.setCoordinates(temp);
                result.add(location);
            }
            return result;
        } catch (Exception e) {
            log.error("fail to find around bike", e);
            throw new MaMaBikeException("查找附近单车失败");
        }
    }
}

然后测试,这里写成了position然后查不到害我浪费了一个晚上找原因,主要还是不熟悉mongodb。

@Test
    public void findNearBike() throws MaMaBikeException {
        bikeGeoService.geoNearSphere("bike-poistion", "location"
                , new Point( 113.500298, 23.457256), 0, 50, null, null, 10);
    }

查找附件单车并且计算距离

/**
     * Author ljs
     * Description 查找某经坐标点附近某范围内坐标点 由近到远 并且计算距离
     * Date 2018/10/6 14:34
     **/
    public List<BikeLocation> findNearBikeAndDis(String collection, DBObject query, Point point, int limit, long maxDistance) throws MaMaBikeException {

        try {
            if (query == null) {
                query = new BasicDBObject();
            }
            List<DBObject> pipeLine = new ArrayList<>();
            BasicDBObject aggregate = new BasicDBObject("$geoNear",
                    new BasicDBObject("near", new BasicDBObject("type", "Point").append("coordinates", new double[]{point.getLongitude(), point.getLatitude()}))
                            .append("distanceField", "distance")
                            .append("num", limit)
                            .append("maxDistance", maxDistance)
                            .append("spherical", true)
                            .append("query", new BasicDBObject("status", 1))
            );
            pipeLine.add(aggregate);
            Cursor cursor = mongoTemplate.getCollection(collection).aggregate(pipeLine, AggregationOptions.builder().build());
            List<BikeLocation> result = new ArrayList<>();
            while (cursor.hasNext()) {
                DBObject obj = cursor.next();
                BikeLocation location = new BikeLocation();
                location.setBikeNumber(Long.valueOf((String) obj.get("bike_no")));
                BasicDBList coordinates = (BasicDBList) ((BasicDBObject) obj.get("location")).get("coordinates");
                Double[] temp = new Double[2];
                coordinates.toArray(temp);
                location.setCoordinates(temp);
                location.setDistance((Double) obj.get("distance"));
                result.add(location);
            }

            return result;
        } catch (Exception e) {
            log.error("fail to find around bike", e);
            throw new MaMaBikeException("查找附近单车失败");
        }
    }

@Test
    public void findNearBikeAndDis() throws MaMaBikeException {
        bikeGeoService.findNearBikeAndDis("bike-poistion", null
                , new Point( 113.500298, 23.457256), 10, 50);
    }

都测试成功后,controller,我们还是把距离和limit和collection参数动态起来,放到parameter文件里。这里明明成功进来后但是却返回404原因是我们@RestController写错了@Controller

 @RequestMapping(value = "/findAroundBike", method = RequestMethod.POST)
    public ApiResult<String> findAroundBike(@RequestBody Point point){
        ApiResult result = new ApiResult();
        try {
            List<BikeLocation> bikeList = bikeGeoService.findNearBikeAndDis(parameters.getCollection(), null, point, parameters.getLimit(), parameters.getMaxDistance());
            result.setData(bikeList);
            result.setMessage("查询单车成功");
        } catch (MaMaBikeException e) {
            result.setCode(e.getStatusCode());
            result.setMessage(e.getMessage());
        } catch (Exception e) {
            log.error("Fail to find around bike info", e);
            result.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
            result.setMessage("内部错误");
        }
        return result;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,929评论 2 89
  • 1.场景 随着智能手机和传感器技术的发展,LBS(Location based service)类的应用也逐渐多了...
    Daniel_adu阅读 11,464评论 3 13
  • 1. 引言 GeoHash本质上是空间索引的一种方式,其基本原理是将地球理解为一个二维平面,将平面递归分解成更小的...
    renzehello阅读 38,440评论 1 17
  • 1. 前言   在我们平时使用美团,饿了么等app进行订餐,或者使用猫眼进行订电影票的时候,都有一个距离的排序,表...
    骑着乌龟去看海阅读 15,281评论 0 25
  • 一、老王的思绪 老王蹲在医护楼的高墙根上,吧嗒吧嗒一根又一根抽着旱烟,蓝色的烟雾断断续续遮住老王皱皱巴巴、酱黑色的...
    MrWolf_阅读 385评论 8 1