feat(移动端优化): 实现安卓原生应用的全屏布局和安全区域处理
添加安全区域插件和样式处理,优化移动端视图布局 重构路由和导航结构,改进底部导航栏 增强原生功能集成,包括状态栏和导航栏控制 优化位置服务和后台任务处理 更新语言包和样式以适应移动端体验
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
-- OPTIMIZATION: Simplify location_updates table schema
|
||||
-- Remove redundant and unnecessary fields for better performance
|
||||
|
||||
-- Step 1: Create new optimized table structure
|
||||
CREATE TABLE `location_updates_new` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`longitude` decimal(11,8) NOT NULL COMMENT 'Longitude first for geographic convention',
|
||||
`latitude` decimal(10,8) NOT NULL COMMENT 'Latitude second for geographic convention',
|
||||
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Single timestamp field',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_user_id` (`user_id`),
|
||||
KEY `idx_created_at` (`created_at`),
|
||||
KEY `idx_user_created` (`user_id`, `created_at`) COMMENT 'Composite index for user location history'
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Optimized location updates - essential fields only';
|
||||
|
||||
-- Step 2: Migrate existing data (longitude, latitude order)
|
||||
INSERT INTO `location_updates_new` (`user_id`, `longitude`, `latitude`, `created_at`)
|
||||
SELECT `user_id`, `longitude`, `latitude`, `created_at`
|
||||
FROM `location_updates`
|
||||
ORDER BY `created_at` ASC;
|
||||
|
||||
-- Step 3: Backup old table and replace with new one
|
||||
RENAME TABLE `location_updates` TO `location_updates_backup`;
|
||||
RENAME TABLE `location_updates_new` TO `location_updates`;
|
||||
|
||||
-- Step 4: Update the view to work with new schema
|
||||
DROP VIEW IF EXISTS `recent_location_updates`;
|
||||
CREATE VIEW `recent_location_updates` AS
|
||||
SELECT
|
||||
`lu`.`id` AS `id`,
|
||||
`lu`.`user_id` AS `user_id`,
|
||||
`lu`.`longitude` AS `longitude`,
|
||||
`lu`.`latitude` AS `latitude`,
|
||||
`lu`.`created_at` AS `created_at`,
|
||||
`w`.`username` AS `username`,
|
||||
`w`.`full_name` AS `full_name`,
|
||||
TIMESTAMPDIFF(MINUTE, `lu`.`created_at`, NOW()) AS `minutes_ago`
|
||||
FROM (`location_updates` `lu`
|
||||
JOIN `workers` `w` ON((`lu`.`user_id` = `w`.`id`)))
|
||||
WHERE (`lu`.`created_at` > (NOW() - INTERVAL 24 HOUR))
|
||||
ORDER BY `lu`.`created_at` DESC;
|
||||
|
||||
-- Step 5: Add comment about optimization
|
||||
ALTER TABLE `location_updates` COMMENT = 'Optimized for 30-minute updates - essential fields only (longitude, latitude, created_at)';
|
||||
|
||||
-- Verification queries (run these to verify the migration)
|
||||
-- SELECT COUNT(*) as old_count FROM location_updates_backup;
|
||||
-- SELECT COUNT(*) as new_count FROM location_updates;
|
||||
-- SELECT * FROM location_updates ORDER BY created_at DESC LIMIT 5;
|
||||
-- SELECT * FROM recent_location_updates LIMIT 5;
|
||||
|
||||
-- Note: After verifying the migration is successful, you can drop the backup table:
|
||||
-- DROP TABLE `location_updates_backup`;
|
||||
+57
-33
@@ -189,18 +189,28 @@ async function startServer() {
|
||||
}
|
||||
|
||||
// Define the geofence polygon by calling the 'polygon' function directly
|
||||
const geofence = polygon([
|
||||
[
|
||||
[101.80827335908509, 2.8350045747358337],
|
||||
[101.80822799653066, 2.8340134829130363],
|
||||
[101.80827902940462, 2.8335264317641418],
|
||||
[101.80941309326164, 2.8332772427247335],
|
||||
[101.81144873788423, 2.834596811345506],
|
||||
[101.81166988033686, 2.8345911479647157],
|
||||
[101.81199875885511, 2.83593336858695],
|
||||
[101.80827335908509, 2.8350045747358337],
|
||||
],
|
||||
])
|
||||
// const geofence = polygon([
|
||||
// [
|
||||
// [101.80827335908509, 2.8350045747358337],
|
||||
// [101.80822799653066, 2.8340134829130363],
|
||||
// [101.80827902940462, 2.8335264317641418],
|
||||
// [101.80941309326164, 2.8332772427247335],
|
||||
// [101.81144873788423, 2.834596811345506],
|
||||
// [101.81166988033686, 2.8345911479647157],
|
||||
// [101.81199875885511, 2.83593336858695],
|
||||
// [101.80827335908509, 2.8350045747358337],
|
||||
// ],
|
||||
// ])
|
||||
|
||||
const geofence = polygon([
|
||||
[
|
||||
[113.310, 23.120],
|
||||
[113.330, 23.120],
|
||||
[113.310, 23.140],
|
||||
[113.330, 23.140],
|
||||
[113.310, 23.120]
|
||||
]
|
||||
])
|
||||
|
||||
|
||||
// Enhanced CORS configuration for HTTPS and mobile development
|
||||
@@ -897,38 +907,52 @@ async function startServer() {
|
||||
|
||||
// --- NEW NATIVE FEATURES API ENDPOINTS ---
|
||||
|
||||
// Location Update Endpoint
|
||||
// Location Update Endpoint - OPTIMIZED: Removed continuous geofence checking
|
||||
app.post('/api/location/update', authenticateJWT, async (req, res) => {
|
||||
try {
|
||||
const { userId, latitude, longitude, accuracy, timestamp, speed, heading, altitude } = req.body
|
||||
const { userId, latitude, longitude, checkGeofence } = req.body
|
||||
|
||||
if (!userId || !latitude || !longitude) {
|
||||
return res.status(400).json({ message: 'User ID, latitude, and longitude are required.' })
|
||||
}
|
||||
|
||||
// Convert timestamp to MySQL-compatible format
|
||||
let mysqlTimestamp
|
||||
if (timestamp) {
|
||||
if (typeof timestamp === 'string' && timestamp.includes('T')) {
|
||||
// Handle ISO 8601 format (e.g., '2025-07-04T09:00:49.192Z')
|
||||
// Convert to MySQL DATETIME format by replacing 'T' with ' ' and removing 'Z'
|
||||
mysqlTimestamp = timestamp.replace('T', ' ').replace('Z', '')
|
||||
} else if (timestamp instanceof Date) {
|
||||
// Handle Date object
|
||||
mysqlTimestamp = timestamp.toISOString().replace('T', ' ').replace('Z', '')
|
||||
} else {
|
||||
// Fallback to current time for invalid formats
|
||||
mysqlTimestamp = new Date().toISOString().replace('T', ' ').replace('Z', '')
|
||||
// OPTIMIZATION: Only check geofence when explicitly requested
|
||||
// This reduces unnecessary processing on every location update
|
||||
if (checkGeofence === true) {
|
||||
try {
|
||||
const userLocation = point([longitude, latitude]);
|
||||
const isWithinGeofence = booleanPointInPolygon(userLocation, geofence);
|
||||
|
||||
if (!isWithinGeofence) {
|
||||
// User is outside the geofence - log security alert silently
|
||||
const distance = pointToLineDistance(userLocation, geofence.geometry.coordinates[0], { units: 'meters' });
|
||||
|
||||
const alertData = {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
timestamp: new Date().toISOString(),
|
||||
distance_from_geofence: distance.toFixed(2),
|
||||
check_type: 'scheduled_update' // Indicate this was a scheduled check
|
||||
};
|
||||
|
||||
// Log geofence violation to security_alerts table
|
||||
await logSecurityAlert(userId, 'geofence_violation', alertData, db);
|
||||
|
||||
console.log(`OPTIMIZED: Geofence violation detected for user ${userId}: ${distance.toFixed(2)} meters outside boundary`);
|
||||
} else {
|
||||
console.log(`OPTIMIZED: User ${userId} within geofence during scheduled check`);
|
||||
}
|
||||
} catch (geofenceError) {
|
||||
console.error('OPTIMIZED: Error checking geofence:', geofenceError);
|
||||
// Continue with location update even if geofence check fails
|
||||
}
|
||||
} else {
|
||||
// Use current time if no timestamp provided
|
||||
mysqlTimestamp = new Date().toISOString().replace('T', ' ').replace('Z', '')
|
||||
}
|
||||
|
||||
// Insert location update
|
||||
// OPTIMIZED: Simplified location update - only essential fields
|
||||
// No need for timestamp conversion as we use created_at with NOW()
|
||||
await db.execute(
|
||||
'INSERT INTO location_updates (user_id, latitude, longitude, accuracy, timestamp, speed, heading, altitude, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())',
|
||||
[userId, latitude, longitude, accuracy || null, mysqlTimestamp, speed || null, heading || null, altitude || null]
|
||||
'INSERT INTO location_updates (user_id, longitude, latitude, created_at) VALUES (?, ?, ?, NOW())',
|
||||
[userId, longitude, latitude]
|
||||
)
|
||||
|
||||
res.json({ message: 'Location updated successfully' })
|
||||
|
||||
Reference in New Issue
Block a user