用户上传头像,使用云存储。
我们这里使用七牛
七牛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
最后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赋值给单车的编号就行。这表就两个字段
就只是获取个id,我们就没必要generater,我们自己创建bean,然后在bike的xml写generateBikeNo语句就行。
@Data
public class BikeNoGen {
private Long autoIncNo;
private byte whatEver;
}
配置了useGeneratedKeys="true",keyProperty="对象的属性而不是数据库里的字段",就能保证主键值自增并且把主键返回,这样就能在java里获得主键
insert into auto_inc_no (what_ever)
values (1)
记住使用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
org.springframework.boot
spring-boot-starter-data-mongodb
配置
#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 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 objList = mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
List 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 findNearBikeAndDis(String collection, DBObject query, Point point, int limit, long maxDistance) throws MaMaBikeException {
try {
if (query == null) {
query = new BasicDBObject();
}
List 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 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 findAroundBike(@RequestBody Point point){
ApiResult result = new ApiResult();
try {
List 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;
}