前言
因最近給的需求要在微信公眾號完成考勤打卡,剛開始說根據員工連接公司的wifi去判斷,網上查了下java好像沒得這個功能,所以只能選擇在地圖來完成。
本人也是第一次接觸微信公眾號,所以剛開始動手比較困難,好在經過一番摸索還是完成了。這里記錄下自己的地圖方案。
准備工作
既然是微信公眾號肯定是基於微信接口的,也不用想的那么復雜,其實就是基於前台weui樣式+微信js接口。下面詳細說明步驟。
首先需要在公眾號設置功能設置中配置微信公眾號js接口安全域名
按流程把文件放在項目工程靜態資源下
另外在基本配置中查看開發者密碼后設置IP白名單,否則后續調用jssdk會報自身IP不在白名單內的錯誤
獲取微信接口回調的數據
public class GetWeChatUtil { public static final String wechat_token_url = "https://api.weixin.qq.com/cgi-bin/token" public static final String wechat_appid = "xxx"; public static final String wechat_secret = "xxx"; public static final String wechat_token_grant_type = "client_credential"; public static final String wechat_jsapi_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; public static final String wechat_jsapi_ticket_type = "jsapi"; public static Map<String,Object> getWeChatToken(){ Map<String,Object> map = new HashMap<>(); try { Map<String,String> params = new HashMap<String, String>(); String ss = HttpConnectionUtil.httpsSendPost(wechat_token_url+"?grant_type="+wechat_token_grant_type
+"&appid="+wechat_appid+"&secret="+wechat_secret, null, "", "UTF-8", null); System.out.println("----獲取token返回信息:"+ss); JSONObject json = JSONObject.parseObject(ss); if(json.containsKey("access_token")){ map.put("token", json.get("access_token")); map.put("expires", json.get("expires_in")); } } catch (Exception e) { e.printStackTrace(); } return map; } public static Map<String,Object> getJsapiTicket(String token) { Map<String,Object> map = new HashMap<String, Object>(); try { Map<String,String> params = new HashMap<>(); params.put("access_token", token); params.put("type", "jsapi"); String ss = HttpConnectionUtil.httpsSendPost(wechat_jsapi_ticket_url + "?access_token=" + token + "&type=" + wechat_jsapi_ticket_type, null, "", "UTF-8", null); System.out.println("----獲取JsapiTicket返回信息:"+ss); JSONObject json = JSONObject.parseObject(ss); if (json.getIntValue("errcode") == 0) { map.put("ticket", json.get("ticket")); map.put("expires", json.get("expires_in")); } }catch (Exception e) { e.printStackTrace(); } return map; } public static Map<String, String> sign(String jsapi_ticket, String url) { Map<String, String> ret = new HashMap<>(); String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String string1; String signature = ""; string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("url", url); ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; } private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } private static String create_nonce_str() { return UUID.randomUUID().toString(); } private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } }
返回給前端調用微信接口必要參數
@Resource private RedisTemplate<String,String> redisTemplate; @RequestMapping("getwechatparams") @ResponseBody public Map<String,String> getParams(@RequestParam String url) { if (redisTemplate.opsForValue().get("token") == null) { Map<String,Object> map = GetWeChatUtil.getWeChatToken(); if (map.get("token") != null) { redisTemplate .opsForValue() .set("token", map.get("token").toString(), Long.parseLong(map.get("expires").toString()), TimeUnit.SECONDS); } } System.out.println("-----getWeToken:"+redisTemplate.opsForValue().get("token")); if (redisTemplate.opsForValue().get("ticket") == null) { Map<String,Object> mm = GetWeChatUtil.getJsapiTicket(redisTemplate.opsForValue().get("token")); if (mm.get("ticket") != null) { redisTemplate .opsForValue() .set("ticket", mm.get("ticket").toString(), Long.parseLong(mm.get("expires").toString()), TimeUnit.SECONDS); } } System.out.println("-----getWeTicket:"+redisTemplate.opsForValue().get("ticket")); String weTicket = redisTemplate.opsForValue().get("ticket"); Map<String, String> ret = GetWeChatUtil.sign(weTicket, url); ret.put("appId", GetWeChatUtil.wechat_appid); return ret; }
前端百度地圖顯示
這里就只貼顯示地圖的關鍵性代碼
1 <script type="text/javascript" src="https://api.map.baidu.com/api?v=2.0&ak=ak密匙"></script> 2 <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 3 4 <div id="allmap" style="width: 100%; height: 410px;"></div>
這里需要調用wx.getLocation()回調成功后的參數res中存放的是當前自身的GPS經緯度坐標,需要將GPS坐標系通過百度接口轉換成百度坐標系
$(function () { $.ajax({ async: false, url: "/getwechatparams", type: "POST", data: { "url": window.location.href }, dataType: "json", success: function (bal) { wx.config({ debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來 appId: bal.appId, // 必填,公眾號的唯一標識 timestamp: bal.timestamp, // 必填,生成簽名的時間戳 nonceStr: bal.nonceStr, // 必填,生成簽名的隨機串 signature: bal.signature,// 必填,簽名,見附錄1 jsApiList: ['openLocation', 'getLocation'] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 }); wx.ready(function () { wx.getLocation({ // 獲取微信接口中的當前坐標經緯度 type: 'wgs84', // GPS坐標 success: function (res) { //alert("gps轉換前:" + res.longitude + "," + res.latitude) var lnggg = ''; // 經度 var lattt = ''; // 緯度 var coordinate = ''; // 簽到地點坐標 $.ajax({ async: false, url: "/get/location", data: { "longitude": res.longitude, "latitude": res.latitude }, dataType: 'json', success: function (responseData) { lnggg = responseData.obj.lng; lattt = responseData.obj.lat; }, error: function (responseData) { alert(responseData.msg); } }); // 百度地圖API功能 var map = new BMap.Map("allmap"); map.enableScrollWheelZoom(true); //alert("gps轉換后" + lnggg + ',' + lattt); var point = new BMap.Point(lnggg, lattt); var geo = new BMap.Geocoder(); geo.getLocation(point, function (rs) { var addComp = rs.addressComponents; var address = addComp.city + addComp.district + addComp.street; // 當前自身詳情街道地址 //alert(address) }); var geolocation = new BMap.Geolocation(); geolocation.getCurrentPosition(function (r) { r.point.lng = lnggg; r.point.lat = lattt; //alert("r.point.lng:" + r.point.lng + ",r.point.lat:" + r.point.lat); if (this.getStatus() == BMAP_STATUS_SUCCESS) { var mk = new BMap.Marker(r.point); // 創建標注 map.addOverlay(mk); // 將標注小紅點添加到地圖中 map.centerAndZoom(r.point, 16); // 縮放級別16 map.panTo(r.point); coordinate = "116.40213223,40.10213223"; // 簽到地點坐標 var arr = coordinate.split(","); var lon = arr[0]; var latt = arr[1]; var pointAttendance = new BMap.Point(lon, latt); r.point.lng = lon; r.point.lat = latt; var mk2 = new BMap.Marker(r.point); var label = new BMap.Label("顯示的文字", {offset: new BMap.Size(20, -10)}); mk2.setLabel(label); map.addOverlay(mk2); map.addOverlay(pointAttendance); mk2.setAnimation(BMAP_ANIMATION_BOUNCE); // 點跳動,沒反應 circle = new BMap.Circle(pointAttendance, 200, { fillColor: "blue", strokeWeight: 1, fillOpacity: 0.2, strokeOpacity: 0.2 });// 顯示簽到點的位置(半徑為200米的一個圓) map.addOverlay(circle); //計算當前位置與考勤點距離 var distance = map.getDistance(pointAttendance, point).toFixed(2); //alert("距離為" + distance); } else { switch (this.getStatus()) { case 2: $.alert("位置結果未知 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 3: $.alert("導航結果未知 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 4: $.alert("非法密鑰 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 5: $.alert("非法請求位置 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 6: $.alert("sorry 當前沒有權限 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 7: $.alert("sorry 服務不可用 獲取位置失敗...", "加載地圖失敗", function () {}); break; case 8: $.alert("sorry 請求超時 獲取位置失敗...", "加載地圖失敗", function () {}); break; } } }, { enableHighAccuracy: true }) }, error: function () { $.alert("請檢查GPS網絡是否正常", "加載地圖失敗", function () {}); } } ); }); } }); });
GPS坐標系轉百度坐標系
@RequestMapping("get/location") @ResponseBody public AjaxResponse getLocation(String longitude, String latitude) { try { Map<String, String> mapLocation = ScHttpRequestUtil.getWpsToBaiduLocation(longitude, latitude); return new AjaxResponse().setObj(mapLocation); } catch (IOException e) { log.error("{[]}", e); return new AjaxResponse().setMsg("地址解析錯誤請重試!"); } } public static synchronized Map<String, String> getWpsToBaiduLocation(String longitude, String latitude) throws IOException { BufferedReader br = null; StringBuffer sb = new StringBuffer(); URL url = new URL("http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=" + longitude + "&y=" + latitude); br = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); String str = null; while ((str = br.readLine()) != null) { sb.append(str); } Map<String,String> map = (Map<String, String>) JSON.parse(sb.toString()); Base64.Decoder decoder = Base64.getDecoder(); String lng = new String(decoder.decode(map.get("x")), "utf-8"); String lat = new String(decoder.decode(map.get("y")), "utf-8"); Map<String, String> mapLocation = new HashMap<>(); map.put("lng", lng); map.put("lat", lat); return map; }
至此,基於微信接口實現百度地圖的功能就完成了