diff --git a/admin-system/src/App.vue b/admin-system/src/App.vue index 94ac392..258e001 100644 --- a/admin-system/src/App.vue +++ b/admin-system/src/App.vue @@ -1,13 +1,19 @@ \ No newline at end of file diff --git a/admin-system/src/router/index.ts b/admin-system/src/router/index.ts index b16bd83..717e607 100644 --- a/admin-system/src/router/index.ts +++ b/admin-system/src/router/index.ts @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from 'vue-router' import type { RouteRecordRaw } from 'vue-router' +import { useAppStore } from '@/stores/app' // 基础路由 const routes: RouteRecordRaw[] = [ @@ -125,7 +126,14 @@ const router = createRouter({ // 路由守卫 router.beforeEach(async (to, from, next) => { const { meta } = to - const isAuthenticated = localStorage.getItem('admin_token') !== null + const appStore = useAppStore() + + // 确保应用已初始化 + if (!appStore.state.initialized) { + await appStore.initialize() + } + + const isAuthenticated = !!appStore.state.user // 检查是否需要认证 if (meta.requiresAuth && !isAuthenticated) { diff --git a/admin-system/src/stores/app.ts b/admin-system/src/stores/app.ts index dbfda1c..cb05211 100644 --- a/admin-system/src/stores/app.ts +++ b/admin-system/src/stores/app.ts @@ -32,16 +32,27 @@ export const useAppStore = defineStore('app', () => { if (token) { // 获取用户信息 const response = await authAPI.getCurrentUser() - // 修复数据结构访问问题 - if (response?.data?.admin) { + + // 统一处理接口响应格式 + if (!response || typeof response !== 'object') { + throw new Error('获取用户信息失败:接口返回格式异常') + } + + // 确保响应数据格式为 { data: { admin: object } } + if (response.data && typeof response.data === 'object' && response.data.admin) { state.user = response.data.admin - } else if (response?.data?.user) { - state.user = response.data.user + } else { + throw new Error('获取用户信息失败:响应数据格式不符合预期') } } } catch (error) { - console.error('初始化失败:', error) + console.error('初始化失败:', { + error: error, + timestamp: new Date().toISOString(), + token: localStorage.getItem('admin_token') + }) clearUser() + throw error // 抛出错误以便调用方处理 } finally { state.loading = false state.initialized = true @@ -54,13 +65,29 @@ export const useAppStore = defineStore('app', () => { try { const response = await authAPI.login(credentials) - // 保存token - localStorage.setItem('admin_token', response.data.token) + // 保存token - 修复数据结构访问问题 + if (response?.data?.token) { + localStorage.setItem('admin_token', response.data.token) + } else if (response?.token) { + localStorage.setItem('admin_token', response.token) + } else { + throw new Error('登录响应中缺少token') + } - // 设置用户信息 - state.user = response.data.admin + // 设置用户信息 - 修复数据结构访问问题 + if (response?.data?.admin) { + state.user = response.data.admin + } else if (response?.admin) { + state.user = response.admin + } else { + throw new Error('登录响应中缺少用户信息') + } return response + } catch (error) { + // 登录失败时清除可能存在的token + clearUser() + throw error } finally { state.loading = false } diff --git a/backend/src/app.js b/backend/src/app.js index 4045bda..fe9292b 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -92,6 +92,23 @@ app.get('/health', (req, res) => { }); }); +// 系统统计路由 +app.get('/system-stats', (req, res) => { + const stats = { + status: 'OK', + timestamp: new Date().toISOString(), + environment: process.env.NODE_ENV || 'development', + nodeVersion: process.version, + memoryUsage: process.memoryUsage(), + uptime: process.uptime(), + cpuCount: require('os').cpus().length, + platform: process.platform, + architecture: process.arch + }; + + res.status(200).json(stats); +}); + // API路由 app.use('/api/v1/auth', authRoutes); app.use('/api/v1/users', userRoutes); diff --git a/backend/src/controllers/admin/systemStats.js b/backend/src/controllers/admin/systemStats.js new file mode 100644 index 0000000..f349c32 --- /dev/null +++ b/backend/src/controllers/admin/systemStats.js @@ -0,0 +1,90 @@ +// 系统统计控制器 +const systemStatsService = require('../../services/admin/systemStats'); + +/** + * 获取系统统计数据 + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ +exports.getSystemStats = async (req, res, next) => { + try { + const stats = await systemStatsService.getSystemStats(); + + res.json({ + success: true, + code: 200, + message: '获取系统统计数据成功', + data: stats, + timestamp: new Date().toISOString() + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取用户统计数据 + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ +exports.getUserStats = async (req, res, next) => { + try { + const stats = await systemStatsService.getUserStats(); + + res.json({ + success: true, + code: 200, + message: '获取用户统计数据成功', + data: stats, + timestamp: new Date().toISOString() + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取订单统计数据 + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ +exports.getOrderStats = async (req, res, next) => { + try { + const stats = await systemStatsService.getOrderStats(); + + res.json({ + success: true, + code: 200, + message: '获取订单统计数据成功', + data: stats, + timestamp: new Date().toISOString() + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取系统信息 + * @param {import('express').Request} req + * @param {import('express').Response} res + * @param {import('express').NextFunction} next + */ +exports.getSystemInfo = async (req, res, next) => { + try { + const info = await systemStatsService.getSystemInfo(); + + res.json({ + success: true, + code: 200, + message: '获取系统信息成功', + data: info, + timestamp: new Date().toISOString() + }); + } catch (error) { + next(error); + } +}; \ No newline at end of file diff --git a/backend/src/docs/swagger.js b/backend/src/docs/swagger.js index 24ecec7..12fbce0 100644 --- a/backend/src/docs/swagger.js +++ b/backend/src/docs/swagger.js @@ -21,7 +21,7 @@ const options = { }, servers: [ { - url: 'http://localhost:3000/api/v1', + url: 'http://localhost:3100/api/v1', description: '开发环境' }, { @@ -91,235 +91,199 @@ const options = { } } }, - // 用户模型 - User: { - type: 'object', - properties: { - id: { - type: 'integer', - example: 1 - }, - username: { - type: 'string', - example: 'testuser' - }, - nickname: { - type: '极速版string', - example: '测试用户' - }, - email: { - type: 'string', - example: 'test@example.com' - }, - phone: { - type: 'string', - example: '13800138000' - }, - avatar: { - type: 'string', - example: 'https://example.com/avatar.jpg' - }, - gender: { - type: 'string', - enum: ['male', 'female', 'unknown'], - example: 'male' - }, - birthday: { - type: 'string', - format: 'date', - example: '1990-01-01' - }, - points: { - type: 'integer', - example: 1000 - }, - level: { - type: 'integer', - example: 3 - }, - status: { - type: 'string', - enum: ['active', 'inactive', 'banned'], - example: 'active' - }, - created_at: { - type: 'string', - format: 'date-time' - }, - updated_at: { - type: 'string', - format: 'date-time' - }, - last_login_at: { - type: 'string', - format: 'date-time', - description: '最后登录时间' - } - } - }, // 管理员模型 Admin: { type: 'object', properties: { id: { type: 'integer', - description: '管理员ID' + example: 1 }, username: { type: 'string', - description: '用户名' + example: 'admin' }, email: { type: 'string', - description: '邮箱' + example: 'admin@jiebanke.com' }, nickname: { type: 'string', - description: '昵称' + example: '管理员' }, avatar: { type: 'string', - description: '头像URL' + example: 'https://example.com/avatar.jpg' }, role: { type: 'string', - description: '角色' + example: 'super_admin' }, status: { - type: 'integer', - description: '状态 (1:启用, 0:禁用)' + type: 'string', + example: 'active' }, last_login: { type: 'string', format: 'date-time', - description: '最后登录时间' + example: '2023-01-01T00:00:00Z' }, created_at: { type: 'string', format: 'date-time', - description: '创建时间' + example: '2023-01-01T00:00:00Z' }, updated_at: { type: 'string', format: 'date-time', - description: '更新时间' + example: '2023-01-01T00:00:00Z' } } }, - // 分页模型 - Pagination: { + // 系统统计数据模型 + SystemStats: { + type: 'object', + properties: { + userCount: { + type: 'integer', + description: '用户总数' + }, + merchantCount: { + type: 'integer', + description: '商家数量' + }, + travelCount: { + type: 'integer', + description: '旅行计划数' + }, + animalCount: { + type: 'integer', + description: '动物数量' + }, + orderCount: { + type: 'integer', + description: '订单总数' + }, + todayUserCount: { + type: 'integer', + description: '今日新增用户数' + }, + todayOrderCount: { + type: 'integer', + description: '今日新增订单数' + } + } + }, + // 用户统计数据模型 + UserStats: { type: 'object', properties: { total: { type: 'integer', - example: 100 + description: '用户总数' }, - page: { - type: 'integer', - example: 1 + byType: { + type: 'array', + description: '按类型统计', + items: { + type: 'object', + properties: { + user_type: { + type: 'string' + }, + count: { + type: 'integer' + } + } + } }, - pageSize: { + byDate: { + type: 'array', + description: '按日期统计', + items: { + type: 'object', + properties: { + date: { + type: 'string' + }, + count: { + type: 'integer' + } + } + } + } + } + }, + // 订单统计数据模型 + OrderStats: { + type: 'object', + properties: { + total: { type: 'integer', - example: 20 + description: '订单总数' }, - totalPages: { + totalAmount: { + type: 'number', + description: '订单总金额' + }, + byStatus: { + type: 'array', + description: '按状态统计', + items: { + type: 'object', + properties: { + status: { + type: 'string' + }, + count: { + type: 'integer' + } + } + } + }, + byDate: { + type: 'array', + description: '按日期统计', + items: { + type: 'object', + properties: { + date: { + type: 'string' + }, + count: { + type: 'integer' + }, + amount: { + type: 'number' + } + } + } + } + } + }, + // 系统信息模型 + SystemInfo: { + type: 'object', + properties: { + nodeVersion: { + type: 'string', + description: 'Node.js版本' + }, + platform: { + type: 'string', + description: '运行平台' + }, + arch: { + type: 'string', + description: '系统架构' + }, + uptime: { type: 'integer', - example: 5 - } - } - } - }, - parameters: { - // 通用分页参数 - PageParam: { - in: 'query', - name: 'page', - schema: { - type: 'integer', - minimum: 1, - default: 1 - }, - description: '页码' - }, - PageSizeParam: { - in: 'query', - name: 'pageSize', - schema: { - type: 'integer', - minimum: 1, - maximum: 100, - default: 20 - }, - description: '每页数量' - } - }, - responses: { - // 通用响应 - UnauthorizedError: { - description: '未授权访问', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/ErrorResponse' - }, - example: { - success: false, - code: 401, - message: '未授权访问', - error: 'Token已过期或无效', - timestamp: '2025-01-01T00:00:00.000Z' - } - } - } - }, - ForbiddenError: { - description: '禁止访问', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/ErrorResponse' - }, - example: { - success: false, - code: 403, - message: '禁止访问', - error: '权限不足', - timestamp: '2025-01-01T00:00:00.000Z' - } - } - } - }, - NotFoundError: { - description: '资源不存在', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/ErrorResponse' - }, - example: { - success: false, - code: 404, - message: '资源不存在', - error: '请求的资源不存在', - timestamp: '2025-01-01T00:00:00.000Z' - } - } - } - }, - ValidationError: { - description: '参数验证错误', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/ErrorResponse' - }, - example: { - success: false, - code: 400, - message: '参数验证错误', - error: '用户名必须为4-20个字符', - timestamp: '2025-01-01T00:00:00.000Z' - } + description: '运行时间(秒)' + }, + databaseVersion: { + type: 'string', + description: '数据库版本' } } } @@ -333,20 +297,8 @@ const options = { }, apis: [ './src/routes/*.js', - './src/controllers/*.js', - './src/models/*.js' + './src/docs/*.js' ] }; -const specs = swaggerJsdoc(options); - -module.exports = { - swaggerUi, - specs, - serve: swaggerUi.serve, - setup: swaggerUi.setup(specs, { - explorer: true, - customCss: '.swagger-ui .topbar { display: none }', - customSiteTitle: '结伴客系统 API文档' - }) -}; \ No newline at end of file +module.exports = swaggerJsdoc(options); \ No newline at end of file diff --git a/backend/src/routes/admin.js b/backend/src/routes/admin.js index 8001d7b..dae3806 100644 --- a/backend/src/routes/admin.js +++ b/backend/src/routes/admin.js @@ -1,7 +1,143 @@ +/** + * 系统统计控制器 + * @module controllers/admin/systemStats + */ + +/** + * 获取系统统计数据 + * @function getSystemStats + * @param {Object} req - Express请求对象 + * @param {Object} res - Express响应对象 + * @param {Function} next - Express中间件next函数 + */ +exports.getSystemStats = async (req, res, next) => { + try { + // 这里应该从数据库获取真实数据 + const stats = { + userCount: 1000, + merchantCount: 50, + travelCount: 200, + animalCount: 300, + orderCount: 1500, + todayUserCount: 20, + todayOrderCount: 35 + }; + + res.json({ + success: true, + code: 200, + message: '获取成功', + data: stats + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取用户统计数据 + * @function getUserStats + * @param {Object} req - Express请求对象 + * @param {Object} res - Express响应对象 + * @param {Function} next - Express中间件next函数 + */ +exports.getUserStats = async (req, res, next) => { + try { + // 这里应该从数据库获取真实数据 + const userStats = { + total: 1000, + byType: [ + { user_type: '普通用户', count: 900 }, + { user_type: '商家', count: 50 }, + { user_type: '管理员', count: 50 } + ], + byDate: [ + { date: '2023-04-01', count: 10 }, + { date: '2023-04-02', count: 15 }, + { date: '2023-04-03', count: 20 } + ] + }; + + res.json({ + success: true, + code: 200, + message: '获取成功', + data: userStats + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取订单统计数据 + * @function getOrderStats + * @param {Object} req - Express请求对象 + * @param {Object} res - Express响应对象 + * @param {Function} next - Express中间件next函数 + */ +exports.getOrderStats = async (req, res, next) => { + try { + // 这里应该从数据库获取真实数据 + const orderStats = { + total: 1500, + totalAmount: 100000, + byStatus: [ + { status: '待支付', count: 100 }, + { status: '已支付', count: 1200 }, + { status: '已取消', count: 200 } + ], + byDate: [ + { date: '2023-04-01', count: 50, amount: 2500 }, + { date: '2023-04-02', count: 75, amount: 4000 }, + { date: '2023-04-03', count: 100, amount: 6000 } + ] + }; + + res.json({ + success: true, + code: 200, + message: '获取成功', + data: orderStats + }); + } catch (error) { + next(error); + } +}; + +/** + * 获取系统信息 + * @function getSystemInfo + * @param {Object} req - Express请求对象 + * @param {Object} res - Express响应对象 + * @param {Function} next - Express中间件next函数 + */ +exports.getSystemInfo = async (req, res, next) => { + try { + // 获取系统信息 + const systemInfo = { + nodeVersion: process.version, + platform: process.platform, + arch: process.arch, + uptime: Math.floor(process.uptime()), + databaseVersion: 'MySQL 8.0.28' // 这里应该从数据库获取真实版本信息 + }; + + res.json({ + success: true, + code: 200, + message: '获取成功', + data: systemInfo + }); + } catch (error) { + next(error); + } +}; // 管理员路由 const express = require('express'); const router = express.Router(); const adminController = require('../controllers/admin'); +const systemStatsController = require('../controllers/admin/systemStats'); const { authenticateAdmin } = require('../middleware/auth'); /** @@ -62,16 +198,14 @@ const { authenticateAdmin } = require('../middleware/auth'); router.post('/login', adminController.login); // 需要认证的接口 -router.use(authenticateAdmin); - /** * @swagger * /admin/profile: * get: - * summary: 获取当前管理员信息 + * summary: 获取管理员个人信息 * tags: [Admin] * security: - * - bearerAuth: [] + * - BearerAuth: [] * responses: * 200: * description: 获取成功 @@ -93,10 +227,12 @@ router.use(authenticateAdmin); * $ref: '#/components/schemas/Admin' * 401: * description: 未授权 - * 404: - * description: 管理员不存在 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' */ -router.get('/profile', adminController.getProfile); +router.get('/profile', authenticateAdmin, adminController.getProfile); /** * @swagger @@ -324,4 +460,227 @@ router.put('/:id', adminController.update); */ router.delete('/:id', adminController.delete); +/** + * @swagger + * /admin/system/stats: + * get: + * summary: 获取系统统计数据 + * tags: [Admin] + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * code: + * type: integer + * message: + * type: string + * data: + * type: object + * properties: + * userCount: + * type: integer + * description: 用户总数 + * merchantCount: + * type: integer + * description: 商家数量 + * travelCount: + * type: integer + * description: 旅行计划数 + * animalCount: + * type: integer + * description: 动物数量 + * orderCount: + * type: integer + * description: 订单总数 + * todayUserCount: + * type: integer + * description: 今日新增用户数 + * todayOrderCount: + * type: integer + * description: 今日新增订单数 + * 401: + * description: 未授权 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ +router.get('/system/stats', authenticateAdmin, systemStatsController.getSystemStats); + +/** + * @swagger + * /admin/system/user-stats: + * get: + * summary: 获取用户统计数据 + * tags: [Admin] + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * code: + * type: integer + * message: + * type: string + * data: + * type: object + * properties: + * total: + * type: integer + * description: 用户总数 + * byType: + * type: array + * description: 按类型统计 + * items: + * type: object + * properties: + * user_type: + * type: string + * count: + * type: integer + * byDate: + * type: array + * description: 按日期统计 + * items: + * type: object + * properties: + * date: + * type: string + * count: + * type: integer + * 401: + * description: 未授权 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ +router.get('/system/user-stats', authenticateAdmin, systemStatsController.getUserStats); + +/** + * @swagger + * /admin/system/order-stats: + * get: + * summary: 获取订单统计数据 + * tags: [Admin] + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * code: + * type: integer + * message: + * type: string + * data: + * type: object + * properties: + * total: + * type: integer + * description: 订单总数 + * totalAmount: + * type: number + * description: 订单总金额 + * byStatus: + * type: array + * description: 按状态统计 + * items: + * type: object + * properties: + * status: + * type: string + * count: + * type: integer + * byDate: + * type: array + * description: 按日期统计 + * items: + * type: object + * properties: + * date: + * type: string + * count: + * type: integer + * amount: + * type: number + * 401: + * description: 未授权 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ +router.get('/system/order-stats', authenticateAdmin, systemStatsController.getOrderStats); + +/** + * @swagger + * /admin/system/info: + * get: + * summary: 获取系统信息 + * tags: [Admin] + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: 获取成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * code: + * type: integer + * message: + * type: string + * data: + * type: object + * properties: + * nodeVersion: + * type: string + * description: Node.js版本 + * platform: + * type: string + * description: 运行平台 + * arch: + * type: string + * description: 系统架构 + * uptime: + * type: integer + * description: 运行时间(秒) + * databaseVersion: + * type: string + * description: 数据库版本 + * 401: + * description: 未授权 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ +router.get('/system/info', authenticateAdmin, systemStatsController.getSystemInfo); + module.exports = router; \ No newline at end of file diff --git a/backend/src/services/admin/systemStats.js b/backend/src/services/admin/systemStats.js new file mode 100644 index 0000000..49a5484 --- /dev/null +++ b/backend/src/services/admin/systemStats.js @@ -0,0 +1,164 @@ +// 系统统计服务 +const database = require('../../config/database'); + +class SystemStatsService { + /** + * 获取系统统计数据 + * @returns {Promise} 系统统计数据 + */ + async getSystemStats() { + try { + // 获取用户总数 + const userCountResult = await database.query('SELECT COUNT(*) as count FROM users'); + const userCount = userCountResult[0].count; + + // 获取商家总数 + const merchantCountResult = await database.query('SELECT COUNT(*) as count FROM merchants'); + const merchantCount = merchantCountResult[0].count; + + // 获取旅行计划总数 + const travelCountResult = await database.query('SELECT COUNT(*) as count FROM travel_plans'); + const travelCount = travelCountResult[0].count; + + // 获取动物总数 + const animalCountResult = await database.query('SELECT COUNT(*) as count FROM animals'); + const animalCount = animalCountResult[0].count; + + // 获取订单总数 + const orderCountResult = await database.query('SELECT COUNT(*) as count FROM orders'); + const orderCount = orderCountResult[0].count; + + // 获取今日新增用户数 + const todayUserCountResult = await database.query( + 'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = CURDATE()' + ); + const todayUserCount = todayUserCountResult[0].count; + + // 获取今日新增订单数 + const todayOrderCountResult = await database.query( + 'SELECT COUNT(*) as count FROM orders WHERE DATE(ordered_at) = CURDATE()' + ); + const todayOrderCount = todayOrderCountResult[0].count; + + return { + userCount, + merchantCount, + travelCount, + animalCount, + orderCount, + todayUserCount, + todayOrderCount + }; + } catch (error) { + throw new Error(`获取系统统计数据失败: ${error.message}`); + } + } + + /** + * 获取用户统计信息 + * @returns {Promise} 用户统计数据 + */ + async getUserStats() { + try { + // 用户总数 + const totalResult = await database.query('SELECT COUNT(*) as total FROM users'); + + // 按用户类型统计 + const byTypeResult = await database.query(` + SELECT + user_type, + COUNT(*) as count + FROM users + GROUP BY user_type + `); + + // 按注册时间统计(近7天) + const byDateResult = await database.query(` + SELECT + DATE(created_at) as date, + COUNT(*) as count + FROM users + WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) + GROUP BY DATE(created_at) + ORDER BY date ASC + `); + + return { + total: totalResult[0].total, + byType: byTypeResult, + byDate: byDateResult + }; + } catch (error) { + throw new Error(`获取用户统计数据失败: ${error.message}`); + } + } + + /** + * 获取订单统计信息 + * @returns {Promise} 订单统计数据 + */ + async getOrderStats() { + try { + // 订单总数和总金额 + const totalResult = await database.query(` + SELECT + COUNT(*) as total, + COALESCE(SUM(total_amount), 0) as totalAmount + FROM orders + `); + + // 按状态统计 + const byStatusResult = await database.query(` + SELECT + status, + COUNT(*) as count + FROM orders + GROUP BY status + `); + + // 按日期统计(近7天) + const byDateResult = await database.query(` + SELECT + DATE(ordered_at) as date, + COUNT(*) as count, + COALESCE(SUM(total_amount), 0) as amount + FROM orders + WHERE ordered_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) + GROUP BY DATE(ordered_at) + ORDER BY date ASC + `); + + return { + total: totalResult[0].total, + totalAmount: parseFloat(totalResult[0].totalAmount || 0), + byStatus: byStatusResult, + byDate: byDateResult + }; + } catch (error) { + throw new Error(`获取订单统计数据失败: ${error.message}`); + } + } + + /** + * 获取系统信息 + * @returns {Promise} 系统信息 + */ + async getSystemInfo() { + try { + // 数据库信息 + const dbInfo = await database.query('SELECT VERSION() as version'); + + return { + nodeVersion: process.version, + platform: process.platform, + arch: process.arch, + uptime: Math.floor(process.uptime()), + databaseVersion: dbInfo[0].version + }; + } catch (error) { + throw new Error(`获取系统信息失败: ${error.message}`); + } + } +} + +module.exports = new SystemStatsService(); \ No newline at end of file diff --git a/mini-program/api/config.js b/mini-program/api/config.js index 19379e3..859a6f2 100644 --- a/mini-program/api/config.js +++ b/mini-program/api/config.js @@ -34,7 +34,14 @@ const endpoints = { LIST: '/travel/list', DETAIL: '/travel/detail', CREATE: '/travel/create', + UPDATE: '/travel/update', + DELETE: '/travel/delete', JOIN: '/travel/join', + QUIT: '/travel/quit', + LIKE: '/travel/like', + UNLIKE: '/travel/unlike', + COMMENT: '/travel/comment', + COMMENTS: '/travel/comments', MY_PLANS: '/travel/my-plans', SEARCH: '/travel/search' }, diff --git a/mini-program/api/services.js b/mini-program/api/services.js index 429efa1..da8d66a 100644 --- a/mini-program/api/services.js +++ b/mini-program/api/services.js @@ -39,8 +39,29 @@ export const travelService = { // 创建旅行计划 create: (data) => request.post(endpoints.TRAVEL.CREATE, data), + // 更新旅行计划 + update: (id, data) => request.put(`${endpoints.TRAVEL.UPDATE}/${id}`, data), + + // 删除旅行计划 + deletePlan: (id) => request.delete(`${endpoints.TRAVEL.DELETE}/${id}`), + // 加入旅行计划 - join: (travelId) => request.post(`${endpoints.TRAVEL.JOIN}/${travelId}`), + joinPlan: (travelId) => request.post(`${endpoints.TRAVEL.JOIN}/${travelId}`), + + // 退出旅行计划 + quitPlan: (travelId) => request.post(`${endpoints.TRAVEL.QUIT}/${travelId}`), + + // 点赞旅行计划 + likePlan: (travelId) => request.post(`${endpoints.TRAVEL.LIKE}/${travelId}`), + + // 取消点赞旅行计划 + unlikePlan: (travelId) => request.post(`${endpoints.TRAVEL.UNLIKE}/${travelId}`), + + // 添加评论 + addComment: (travelId, data) => request.post(`${endpoints.TRAVEL.COMMENT}/${travelId}`, data), + + // 获取评论列表 + getComments: (travelId, params = {}) => request.get(`${endpoints.TRAVEL.COMMENTS}/${travelId}`, params), // 获取我的旅行计划 getMyPlans: (params = {}) => request.get(endpoints.TRAVEL.MY_PLANS, params), diff --git a/mini-program/package.json b/mini-program/package.json index 988a700..6e37d56 100644 --- a/mini-program/package.json +++ b/mini-program/package.json @@ -12,19 +12,19 @@ "build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build" }, "dependencies": { - "@dcloudio/uni-app": "^3.0.0", - "@dcloudio/uni-components": "^3.0.0", - "@dcloudio/uni-h5": "^3.0.0", - "@dcloudio/uni-mp-weixin": "^3.0.0", + "@dcloudio/uni-app": "^3.4.19", + "@dcloudio/uni-components": "^3.4.19", + "@dcloudio/uni-h5": "^3.4.19", + "@dcloudio/uni-mp-weixin": "^3.4.19", "vue": "^3.2.0", "vuex": "^4.0.0" }, "devDependencies": { - "@dcloudio/types": "^3.0.0", - "@dcloudio/uni-cli-shared": "^3.0.0", - "@dcloudio/uni-migration": "^3.0.0", - "@dcloudio/uni-template-compiler": "^3.0.0", - "@dcloudio/vue-cli-plugin-uni": "^3.0.0", + "@dcloudio/types": "^3.4.19", + "@dcloudio/uni-cli-shared": "^3.4.19", + "@dcloudio/uni-migration": "^3.4.19", + "@dcloudio/uni-template-compiler": "^3.4.19", + "@dcloudio/vue-cli-plugin-uni": "^3.4.19", "@vue/cli-service": "^5.0.0", "cross-env": "^7.0.0", "sass": "^1.32.0", diff --git a/mini-program/pages.json b/mini-program/pages.json index f42684c..54cc9e4 100644 --- a/mini-program/pages.json +++ b/mini-program/pages.json @@ -55,6 +55,22 @@ "navigationBarBackgroundColor": "#007aff", "navigationBarTextStyle": "white" } + }, + { + "path": "pages/travel/publish", + "style": { + "navigationBarTitleText": "发布旅行", + "navigationBarBackgroundColor": "#007aff", + "navigationBarTextStyle": "white" + } + }, + { + "path": "pages/travel/comments", + "style": { + "navigationBarTitleText": "评论详情", + "navigationBarBackgroundColor": "#007aff", + "navigationBarTextStyle": "white" + } } ], "globalStyle": { diff --git a/mini-program/pages/travel/comments.vue b/mini-program/pages/travel/comments.vue new file mode 100644 index 0000000..a7e3e91 --- /dev/null +++ b/mini-program/pages/travel/comments.vue @@ -0,0 +1,552 @@ + + + + + \ No newline at end of file diff --git a/mini-program/pages/travel/detail.vue b/mini-program/pages/travel/detail.vue index 0b326ec..44d6cfb 100644 --- a/mini-program/pages/travel/detail.vue +++ b/mini-program/pages/travel/detail.vue @@ -1,96 +1,410 @@ \ No newline at end of file diff --git a/mini-program/pages/travel/publish.vue b/mini-program/pages/travel/publish.vue new file mode 100644 index 0000000..eb2477a --- /dev/null +++ b/mini-program/pages/travel/publish.vue @@ -0,0 +1,566 @@ +