#!/usr/bin/env python # -*- coding: utf-8 -*- print('Starting backend...') try: from flask import Flask, request, jsonify from flask_cors import CORS import sqlite3 import os import uuid import datetime import secrets print('Imports successful') app = Flask(__name__) CORS(app, resources={ r"/*": { "origins": ["http://localhost:8080", "http://127.0.0.1:8080"], "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization"], "supports_credentials": True } }) DATABASE_PATH = os.path.join(os.path.dirname(__file__), '../database/app.db') SCHEMA_PATH = os.path.join(os.path.dirname(__file__), '../database/schema.sql') print(f'Database path: {DATABASE_PATH}') print(f'Schema path: {SCHEMA_PATH}') @app.before_request def handle_options(): if request.method == 'OPTIONS': response = app.make_default_options_response() headers = response.headers headers['Access-Control-Allow-Origin'] = 'http://localhost:8080' headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' headers['Access-Control-Max-Age'] = '3600' return response def get_db(): conn = sqlite3.connect(DATABASE_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): print('Initializing database...') db_dir = os.path.dirname(DATABASE_PATH) if not os.path.exists(db_dir): os.makedirs(db_dir) print(f'Created directory: {db_dir}') if not os.path.exists(DATABASE_PATH): print('数据库不存在,创建新数据库') conn = get_db() with open(SCHEMA_PATH, 'r', encoding='utf-8') as f: schema = f.read() conn.executescript(schema) conn.commit() conn.close() print('数据库初始化成功') else: print('数据库已存在,跳过初始化') def generate_token(): return secrets.token_urlsafe(32) def get_user_from_token(token): if not token: return None conn = get_db() cursor = conn.cursor() cursor.execute(''' SELECT u.* FROM users u JOIN sessions s ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > datetime('now') ''', (token,)) row = cursor.fetchone() conn.close() return dict(row) if row else None def get_config_value(config_key, default_value): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT config_value FROM system_config WHERE config_key = ?', (config_key,)) row = cursor.fetchone() conn.close() if row: return row['config_value'] return default_value def get_user_pending_tasks_count(user_id): conn = get_db() cursor = conn.cursor() cursor.execute(''' SELECT COUNT(*) FROM tasks WHERE publisher_id = ? AND status IN ('pending', 'in_progress') ''', (user_id,)) count = cursor.fetchone()[0] conn.close() return count def get_user_accepted_tasks_count(user_id): conn = get_db() cursor = conn.cursor() cursor.execute(''' SELECT COUNT(*) FROM tasks WHERE acceptor_id = ? AND status = 'in_progress' ''', (user_id,)) count = cursor.fetchone()[0] conn.close() return count @app.route('/api/tasks', methods=['GET']) def get_tasks(): status = request.args.get('status') type_param = request.args.get('type') search = request.args.get('search') conn = get_db() cursor = conn.cursor() query = 'SELECT * FROM tasks WHERE 1=1' params = [] if status and status != 'all': query += ' AND status = ?' params.append(status) auth_header = request.headers.get('Authorization') if type_param and auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] cursor.execute(''' SELECT u.id FROM users u JOIN sessions s ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > datetime('now') ''', (token,)) user = cursor.fetchone() if user: current_user_id = user[0] if type_param == 'published': query += ' AND publisher_id = ?' params.append(current_user_id) elif type_param == 'accepted': query += ' AND acceptor_id = ?' params.append(current_user_id) query += ' AND status IN (?, ?)' params.extend(['in_progress', 'completed']) if search: query += ' AND (title LIKE ? OR description LIKE ? OR location LIKE ?)' search_term = f'%{search}%' params.extend([search_term, search_term, search_term]) query += ' ORDER BY created_at DESC' print(f'=== 查询任务 ===') print(f'查询参数: status={status}, type={type_param}, search={search}') print(f'最终查询SQL: {query}') print(f'参数: {params}') cursor.execute(query, params) rows = cursor.fetchall() conn.close() print(f'返回任务数量: {len(rows)}') return jsonify([dict(row) for row in rows]) @app.route('/api/tasks/', methods=['GET']) def get_task(task_id): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM tasks WHERE id = ?', (task_id,)) row = cursor.fetchone() conn.close() if not row: return jsonify({'error': '任务不存在'}), 404 return jsonify(dict(row)) @app.route('/api/tasks', methods=['POST']) def create_task(): data = request.json conn = get_db() cursor = conn.cursor() print('=== 创建任务请求开始 ===') print(f'收到的数据: {data}') auth_header = request.headers.get('Authorization') print(f'Authorization header: {auth_header}') publisher_id = None if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] print(f'解析出token: {token[:20]}...') cursor.execute(''' SELECT u.id FROM users u JOIN sessions s ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > datetime('now') ''', (token,)) user = cursor.fetchone() print(f'查询到的用户: {user}') if user: publisher_id = user[0] print(f'设置publisher_id: {publisher_id}') max_publish = int(get_config_value('max_publish_tasks', '3')) current_count = get_user_pending_tasks_count(publisher_id) print(f'用户已发布任务数: {current_count}, 最大限制: {max_publish}') if current_count >= max_publish: conn.close() return jsonify({'error': f'已发布的任务数量过多,暂时不能发布。您目前有{current_count}个待处理或进行中的任务,最多可发布{max_publish}个任务。'}), 400 print(f'最终publisher_id: {publisher_id}') cursor.execute(''' INSERT INTO tasks (title, description, location, time, reward, publisher_id, publisher_name, contact, status, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( data.get('title'), data.get('description'), data.get('location'), data.get('time'), data.get('reward'), publisher_id, data.get('publisher_name'), data.get('contact'), data.get('status', 'pending'), data.get('type', 'published') )) task_id = cursor.lastrowid conn.commit() conn.close() print(f'任务创建成功,ID: {task_id}') print('=== 创建任务请求完成 ===') return jsonify({'id': task_id, **data}) @app.route('/api/tasks/', methods=['PUT']) def update_task(task_id): data = request.json conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM tasks WHERE id = ?', (task_id,)) existing_task = cursor.fetchone() if not existing_task: conn.close() return jsonify({'error': '任务不存在'}), 404 update_fields = [] update_values = [] if 'title' in data: update_fields.append('title=?') update_values.append(data['title']) if 'description' in data: update_fields.append('description=?') update_values.append(data['description']) if 'location' in data: update_fields.append('location=?') update_values.append(data['location']) if 'time' in data: update_fields.append('time=?') update_values.append(data['time']) if 'reward' in data: update_fields.append('reward=?') update_values.append(data['reward']) if 'publisher_name' in data: update_fields.append('publisher_name=?') update_values.append(data['publisher_name']) if 'contact' in data: update_fields.append('contact=?') update_values.append(data['contact']) if 'status' in data: update_fields.append('status=?') update_values.append(data['status']) if data['status'] == 'in_progress': auth_header = request.headers.get('Authorization') print(f'=== 更新任务状态为in_progress,开始设置acceptor_id ===') if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] print(f'Token: {token[:20]}...') cursor.execute(''' SELECT u.id FROM users u JOIN sessions s ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > datetime('now') ''', (token,)) user = cursor.fetchone() print(f'查询到的用户: {user}') if user: acceptor_id = user[0] max_accept = int(get_config_value('max_accept_tasks', '3')) current_count = get_user_accepted_tasks_count(acceptor_id) print(f'用户已承接任务数: {current_count}, 最大限制: {max_accept}') if current_count >= max_accept: conn.close() return jsonify({'error': f'已承接的任务数量过多,暂时无法承接。您目前有{current_count}个进行中的任务,最多可承接{max_accept}个任务。'}), 400 update_fields.append('acceptor_id=?') update_values.append(acceptor_id) print(f'设置acceptor_id为: {acceptor_id}') if 'type' in data: update_fields.append('type=?') update_values.append(data['type']) if update_fields: update_values.append(task_id) query = f'UPDATE tasks SET {", ".join(update_fields)} WHERE id=?' cursor.execute(query, update_values) conn.commit() cursor.execute('SELECT * FROM tasks WHERE id = ?', (task_id,)) updated_task = cursor.fetchone() conn.close() return jsonify(dict(updated_task)) @app.route('/api/tasks/', methods=['DELETE']) def delete_task(task_id): conn = get_db() cursor = conn.cursor() cursor.execute('DELETE FROM tasks WHERE id = ?', (task_id,)) conn.commit() conn.close() if cursor.rowcount == 0: return jsonify({'error': '任务不存在'}), 404 return jsonify({'message': '删除成功'}) @app.route('/api/carousels/', methods=['GET']) def get_carousels(page_type): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM carousels WHERE page_type = ? ORDER BY sort_order', (page_type,)) rows = cursor.fetchall() conn.close() return jsonify([dict(row) for row in rows]) @app.route('/api/carousels', methods=['POST']) def create_carousel(): data = request.json conn = get_db() cursor = conn.cursor() cursor.execute(''' INSERT INTO carousels (page_type, image_url, image_color, sort_order) VALUES (?, ?, ?, ?) ''', ( data.get('page_type'), data.get('image_url'), data.get('image_color'), data.get('sort_order', 0) )) carousel_id = cursor.lastrowid conn.commit() conn.close() return jsonify({'id': carousel_id, **data}) @app.route('/api/carousels/', methods=['PUT']) def update_carousel(carousel_id): data = request.json conn = get_db() cursor = conn.cursor() cursor.execute(''' UPDATE carousels SET page_type=?, image_url=?, image_color=?, sort_order=? WHERE id=? ''', ( data.get('page_type'), data.get('image_url'), data.get('image_color'), data.get('sort_order'), carousel_id )) conn.commit() conn.close() if cursor.rowcount == 0: return jsonify({'error': '轮播图不存在'}), 404 return jsonify({'id': carousel_id, **data}) @app.route('/api/carousels/', methods=['DELETE']) def delete_carousel(carousel_id): conn = get_db() cursor = conn.cursor() cursor.execute('DELETE FROM carousels WHERE id = ?', (carousel_id,)) conn.commit() conn.close() if cursor.rowcount == 0: return jsonify({'error': '轮播图不存在'}), 404 return jsonify({'message': '删除成功'}) @app.route('/api/users', methods=['GET']) def get_users(): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM users') rows = cursor.fetchall() conn.close() return jsonify([dict(row) for row in rows]) @app.route('/api/users/', methods=['GET']) def get_user(user_id): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,)) row = cursor.fetchone() conn.close() if not row: return jsonify({'error': '用户不存在'}), 404 return jsonify(dict(row)) @app.route('/api/users/', methods=['PUT']) def update_user(user_id): data = request.json conn = get_db() cursor = conn.cursor() # 构建更新语句 update_fields = [] update_values = [] if 'nickname' in data: update_fields.append('nickname=?') update_values.append(data['nickname']) if 'gender' in data: update_fields.append('gender=?') update_values.append(data['gender']) if 'location' in data: update_fields.append('location=?') update_values.append(data['location']) if 'phone' in data: update_fields.append('phone=?') update_values.append(data['phone']) if 'enabled' in data: update_fields.append('enabled=?') update_values.append(data['enabled']) if not update_fields: conn.close() return jsonify({'error': '没有需要更新的字段'}), 400 query = f"UPDATE users SET {', '.join(update_fields)} WHERE id=?" update_values.append(user_id) cursor.execute(query, update_values) conn.commit() conn.close() if cursor.rowcount == 0: return jsonify({'error': '用户不存在'}), 404 return jsonify({'id': user_id, **data}) @app.route('/api/auth/login', methods=['POST']) def login(): print('=== 登录请求开始 ===') conn = None try: data = request.json print(f'收到的数据: {data}') phone = data.get('phone') code = data.get('code') print(f'手机号: {phone}, 验证码: {code}') if not phone or not code: return jsonify({'error': '手机号和验证码不能为空'}), 400 if not phone.isdigit() or len(phone) != 11: return jsonify({'error': '请输入正确的11位手机号'}), 400 if not code.isdigit() or len(code) != 4: return jsonify({'error': '请输入4位验证码'}), 400 conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM users WHERE phone = ?', (phone,)) user = cursor.fetchone() print(f'找到用户: {user}') if not user: print('创建新用户') try: cursor.execute('INSERT INTO users (phone, enabled) VALUES (?, ?)', (phone, 1)) user_id = cursor.lastrowid conn.commit() is_new_user = True print(f'新用户ID: {user_id}') except sqlite3.IntegrityError: print('手机号已存在,重新查询') conn.rollback() cursor.execute('SELECT * FROM users WHERE phone = ?', (phone,)) user = cursor.fetchone() user_id = user['id'] is_new_user = False print(f'现有用户ID: {user_id}') else: user_id = user['id'] is_new_user = False print(f'现有用户ID: {user_id}') # 检查用户是否被禁用 cursor.execute('SELECT enabled FROM users WHERE id = ?', (user_id,)) user_enabled = cursor.fetchone()['enabled'] if user_enabled != 1: conn.close() return jsonify({'error': '账户已被禁用,无法登录'}), 403 token = generate_token() expires_at = (datetime.datetime.now() + datetime.timedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S') print(f'生成token: {token[:20]}...') cursor.execute('DELETE FROM sessions WHERE user_id = ?', (user_id,)) cursor.execute('INSERT INTO sessions (user_id, token, expires_at) VALUES (?, ?, ?)', (user_id, token, expires_at)) conn.commit() cursor.execute('SELECT * FROM users WHERE id = ?', (user_id,)) user = cursor.fetchone() print(f'最终用户数据: {dict(user)}') result = { 'token': token, 'user': dict(user), 'is_new_user': is_new_user, 'need_profile': not (user['nickname'] and user['location']) } print(f'返回结果: {result}') print('=== 登录请求完成 ===') return jsonify(result) except Exception as e: print(f'登录错误: {e}') import traceback traceback.print_exc() return jsonify({'error': '服务器错误,请稍后重试'}), 500 finally: if conn: try: conn.close() except: pass @app.route('/api/auth/logout', methods=['POST']) def logout(): auth_header = request.headers.get('Authorization') if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] conn = get_db() cursor = conn.cursor() cursor.execute('DELETE FROM sessions WHERE token = ?', (token,)) conn.commit() conn.close() return jsonify({'message': '登出成功'}) @app.route('/api/auth/me', methods=['GET']) def get_current_user(): auth_header = request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return jsonify({'error': '未登录'}), 401 token = auth_header.split(' ')[1] user = get_user_from_token(token) if not user: return jsonify({'error': '登录已过期,请重新登录'}), 401 return jsonify(user) @app.route('/api/auth/profile', methods=['PUT']) def update_profile(): auth_header = request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return jsonify({'error': '未登录'}), 401 token = auth_header.split(' ')[1] user = get_user_from_token(token) if not user: return jsonify({'error': '登录已过期,请重新登录'}), 401 data = request.json # 验证昵称不能为空 if not data.get('nickname'): return jsonify({'error': '昵称不能为空'}), 400 conn = get_db() cursor = conn.cursor() cursor.execute(''' UPDATE users SET nickname=?, avatar=?, gender=?, location=? WHERE id=? ''', ( data.get('nickname'), data.get('avatar'), data.get('gender'), data.get('location'), user['id'] )) conn.commit() conn.close() cursor = get_db().cursor() cursor.execute('SELECT * FROM users WHERE id = ?', (user['id'],)) updated_user = cursor.fetchone() get_db().close() return jsonify(dict(updated_user)) @app.route('/api/config', methods=['GET']) def get_configs(): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM system_config ORDER BY id') rows = cursor.fetchall() conn.close() return jsonify([dict(row) for row in rows]) @app.route('/api/config/', methods=['GET']) def get_config(config_key): conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM system_config WHERE config_key = ?', (config_key,)) row = cursor.fetchone() conn.close() if not row: return jsonify({'error': '配置不存在'}), 404 return jsonify(dict(row)) @app.route('/api/config/', methods=['PUT']) def update_config(config_key): data = request.json conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM system_config WHERE config_key = ?', (config_key,)) existing = cursor.fetchone() if not existing: conn.close() return jsonify({'error': '配置不存在'}), 404 cursor.execute(''' UPDATE system_config SET config_value = ?, updated_at = datetime('now') WHERE config_key = ? ''', ( data.get('config_value'), config_key )) conn.commit() cursor.execute('SELECT * FROM system_config WHERE config_key = ?', (config_key,)) updated_config = cursor.fetchone() conn.close() return jsonify(dict(updated_config)) if __name__ == '__main__': print('Initializing database...') init_db() print('Starting Flask server on port 3000...') app.run(debug=True, port=3000, host='0.0.0.0', use_reloader=False) except Exception as e: print(f'Error: {e}') import traceback traceback.print_exc() input('Press Enter to exit...')