|
|
@@ -0,0 +1,167 @@
|
|
|
+const axios = require('axios')
|
|
|
+const db = require('./plugin/DataBase/db')
|
|
|
+
|
|
|
+const API_URL = 'http://222.178.152.79:100/api_v1/getSportRecord'
|
|
|
+
|
|
|
+// ===== 账号组成 =====
|
|
|
+let part1 = '23'
|
|
|
+let part2 = 993 // 第二部分
|
|
|
+let part3 = 1 // 第三部分
|
|
|
+let part4 = 1 // 第四部分
|
|
|
+
|
|
|
+// ===== 最大范围(可自行调整)=====
|
|
|
+const MAX_P3 = 20
|
|
|
+const MAX_P4 = 90
|
|
|
+
|
|
|
+// ===== 南泉校区 =====
|
|
|
+const nq = [106.594871,29.421079]
|
|
|
+// ===== 双桥校区 =====
|
|
|
+const sq = [105.778790,29.512079]
|
|
|
+
|
|
|
+/**
|
|
|
+ * 计算两点经纬度之间的距离(单位:米)
|
|
|
+ * @param {[number, number]} p1 [经度, 纬度]
|
|
|
+ * @param {[number, number]} p2 [经度, 纬度]
|
|
|
+ * @returns {number} 距离(米,浮点数)
|
|
|
+ */
|
|
|
+function calcDistance(p1, p2) {
|
|
|
+ const [lng1, lat1] = p1
|
|
|
+ const [lng2, lat2] = p2
|
|
|
+
|
|
|
+ console.log(`计算距离,点1: (${lat1}, ${lng1}),点2: (${lat2}, ${lng2})`)
|
|
|
+
|
|
|
+ const toRad = d => d * Math.PI / 180
|
|
|
+
|
|
|
+ const R = 6378137 // 地球半径(米,WGS84)
|
|
|
+
|
|
|
+ const φ1 = toRad(lat1)
|
|
|
+ const φ2 = toRad(lat2)
|
|
|
+ const Δφ = toRad(lat2 - lat1)
|
|
|
+ const Δλ = toRad(lng2 - lng1)
|
|
|
+
|
|
|
+ const a =
|
|
|
+ Math.sin(Δφ / 2) ** 2 +
|
|
|
+ Math.cos(φ1) * Math.cos(φ2) *
|
|
|
+ Math.sin(Δλ / 2) ** 2
|
|
|
+
|
|
|
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
|
|
+
|
|
|
+ return R * c
|
|
|
+}
|
|
|
+
|
|
|
+// ===== 工具函数 =====
|
|
|
+const pad2 = n => String(n).padStart(2, '0')
|
|
|
+
|
|
|
+function genAccount() {
|
|
|
+ return `${part1}${part2}${pad2(part3)}${pad2(part4)}`
|
|
|
+}
|
|
|
+
|
|
|
+function parsePathLine(pathLine) {
|
|
|
+ return pathLine.split(';').map(p => {
|
|
|
+ const [lat, lng] = p.split(',').map(Number);
|
|
|
+ return [lng, lat];
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// ===== 进位逻辑 =====
|
|
|
+
|
|
|
+// 无数据:第三部分进位,必要时第二部分进位
|
|
|
+function advanceOnNoData() {
|
|
|
+ part3++
|
|
|
+
|
|
|
+ if (part3 > MAX_P3) {
|
|
|
+ part3 = 1
|
|
|
+ part2++
|
|
|
+ console.log('>>> 第三部分耗尽,第二部分进位为:', part2)
|
|
|
+ }
|
|
|
+
|
|
|
+ part4 = 1
|
|
|
+}
|
|
|
+
|
|
|
+// 有数据:第四部分进位
|
|
|
+function advanceOnHasData() {
|
|
|
+ part4++
|
|
|
+
|
|
|
+ if (part4 > MAX_P4) {
|
|
|
+ part4 = 1
|
|
|
+ part3++
|
|
|
+
|
|
|
+ if (part3 > MAX_P3) {
|
|
|
+ part3 = 1
|
|
|
+ part2++
|
|
|
+ console.log('>>> 第四、三部分耗尽,第二部分进位为:', part2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ===== 单次请求 =====
|
|
|
+async function fetchOnce() {
|
|
|
+ const account = genAccount()
|
|
|
+ console.log('\n请求 account:', account)
|
|
|
+
|
|
|
+ const res = await axios.post(
|
|
|
+ API_URL,
|
|
|
+ `account=${account}&startdate=2020-01-01&enddate=2025-12-31`,
|
|
|
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
|
|
|
+ )
|
|
|
+
|
|
|
+ const data = res.data
|
|
|
+
|
|
|
+ // ❌ 无数据
|
|
|
+ if (!data || !data.data || data.data.length === 0) {
|
|
|
+ console.log('无 data,执行进位')
|
|
|
+ advanceOnNoData()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // ✅ 有数据
|
|
|
+ for (let index = 0; index < data.data.length; index++) {
|
|
|
+ const item = data.data[index]
|
|
|
+
|
|
|
+ if (item.status !== 1) continue
|
|
|
+
|
|
|
+ const coords = parsePathLine(item.pathLine)
|
|
|
+ const pathLineJson = JSON.stringify(coords)
|
|
|
+
|
|
|
+ const sq_distance = calcDistance(sq, coords[0])
|
|
|
+ const nq_distance = calcDistance(nq, coords[0])
|
|
|
+ console.log(`双桥校区距离起点: ${sq_distance.toFixed(2)} 米,南泉校区距离起点: ${nq_distance.toFixed(2)} 米`)
|
|
|
+
|
|
|
+ const run_zone_name = sq_distance < nq_distance ? '重庆工程学院双桥校区' : '重庆工程学院南泉校区'
|
|
|
+
|
|
|
+ const sql = `
|
|
|
+ INSERT INTO path_data
|
|
|
+ (distance, time, calorie, speed, data, run_zone_name)
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?)
|
|
|
+ `
|
|
|
+
|
|
|
+ await db.query(sql, [
|
|
|
+ item.distance,
|
|
|
+ item.duration,
|
|
|
+ item.calorie,
|
|
|
+ item.distribution,
|
|
|
+ pathLineJson,
|
|
|
+ run_zone_name
|
|
|
+ ])
|
|
|
+
|
|
|
+ console.log(
|
|
|
+ `已保存到数据库。距离:${item.distance} 米,时长:${item.duration} 秒,路径点:${coords.length} 个`
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ advanceOnHasData()
|
|
|
+}
|
|
|
+
|
|
|
+// ===== 主循环 =====
|
|
|
+(async function start() {
|
|
|
+ while (true) {
|
|
|
+ try {
|
|
|
+ await fetchOnce()
|
|
|
+ } catch (e) {
|
|
|
+ console.error('请求异常:', e.message)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 控制请求频率
|
|
|
+ await new Promise(r => setTimeout(r, 200))
|
|
|
+ }
|
|
|
+})()
|