首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >悬赏任务APP源码威客平台源码支持iOS/Android双端任务分发

悬赏任务APP源码威客平台源码支持iOS/Android双端任务分发

原创
作者头像
用户11761891
发布于 2025-07-25 11:24:04
发布于 2025-07-25 11:24:04
8000
举报

  在数字化时代,自由职业者与企业需求之间的桥梁变得越来越重要。悬赏任务APP和威客平台正是连接两者的理想解决方案,让雇主能够发布任务并悬赏,而自由职业者则可以接受任务并获得报酬。本文将介绍一个支持iOS和Android双端的悬赏任务APP与威客平台的核心实现方案,采用React Native作为跨平台框架,确保一套代码能够在两个系统上运行。

系统架构

源码及演示:casgams.top/xs

  前端:React Native+Redux

  后端:Node.js+Express

  数据库:MongoDB

  实时通讯:Socket.io

  核心功能模块

  用户认证系统(注册、登录、身份验证)

  任务发布与管理

  任务投标与接单

  消息通知系统

  支付与交易系统

  评价与信誉体系

  核心代码实现

  下面是平台的核心代码实现,包含主要功能模块:

  taskActions.js

代码语言:txt
复制
import axios from 'axios';
import { 
  FETCH_TASKS_REQUEST, 
  FETCH_TASKS_SUCCESS, 
  FETCH_TASKS_FAILURE,
  FETCH_TASK_DETAIL_REQUEST,
  FETCH_TASK_DETAIL_SUCCESS,
  FETCH_TASK_DETAIL_FAILURE,
  CREATE_TASK_REQUEST,
  CREATE_TASK_SUCCESS,
  CREATE_TASK_FAILURE,
  SUBMIT_BID_REQUEST,
  SUBMIT_BID_SUCCESS,
  SUBMIT_BID_FAILURE,
  ACCEPT_BID_REQUEST,
  ACCEPT_BID_SUCCESS,
  ACCEPT_BID_FAILURE,
  COMPLETE_TASK_REQUEST,
  COMPLETE_TASK_SUCCESS,
  COMPLETE_TASK_FAILURE
} from './types';

const API_URL = 'https://api.yourplatform.com';

// 获取任务列表
export const fetchTasks = () => {
  return async (dispatch) => {
    try {
      dispatch({ type: FETCH_TASKS_REQUEST });
      const response = await axios.get(`${API_URL}/tasks`);
      dispatch({ 
        type: FETCH_TASKS_SUCCESS, 
        payload: response.data 
      });
    } catch (error) {
      dispatch({ 
        type: FETCH_TASKS_FAILURE, 
        payload: error.response?.data?.message || '获取任务列表失败'
      });
    }
  };
};

// 获取任务详情
export const fetchTaskDetail = (taskId) => {
  return async (dispatch) => {
    try {
      dispatch({ type: FETCH_TASK_DETAIL_REQUEST });
      const response = await axios.get(`${API_URL}/tasks/${taskId}`);
      dispatch({ 
        type: FETCH_TASK_DETAIL_SUCCESS, 
        payload: response.data 
      });
    } catch (error) {
      dispatch({ 
        type: FETCH_TASK_DETAIL_FAILURE, 
        payload: error.response?.data?.message || '获取任务详情失败'
      });
    }
  };
};

// 创建任务
export const createTask = (taskData, navigation) => {
  return async (dispatch, getState) => {
    try {
      dispatch({ type: CREATE_TASK_REQUEST });
      const { auth } = getState();
      
      const response = await axios.post(
        `${API_URL}/tasks`,
        taskData,
        {
          headers: {
            Authorization: `Bearer ${auth.token}`
          }
        }
      );
      
      dispatch({ 
        type: CREATE_TASK_SUCCESS, 
        payload: response.data 
      });
      
      // 创建成功后返回任务列表
      navigation.navigate('Home');
    } catch (error) {
      dispatch({ 
        type: CREATE_TASK_FAILURE, 
        payload: error.response?.data?.message || '创建任务失败'
      });
    }
  };
};

// 提交投标
export const submitBid = (taskId, bidData) => {
  return async (dispatch, getState) => {
    try {
      dispatch({ type: SUBMIT_BID_REQUEST });
      const { auth } = getState();
      
      const response = await axios.post(
        `${API_URL}/tasks/${taskId}/bids`,
        bidData,
        {
          headers: {
            Authorization: `Bearer ${auth.token}`
          }
        }
      );
      
      dispatch({ 
        type: SUBMIT_BID_SUCCESS, 
        payload: response.data 
      });
      
      // 刷新任务详情
      dispatch(fetchTaskDetail(taskId));
    } catch (error) {
      dispatch({ 
        type: SUBMIT_BID_FAILURE, 
        payload: error.response?.data?.message || '提交投标失败'
      });
    }
  };
};

// 接受投标
export const acceptBid = (taskId, bidId) => {
  return async (dispatch, getState) => {
    try {
      dispatch({ type: ACCEPT_BID_REQUEST });
      const { auth } = getState();
      
      const response = await axios.put(
        `${API_URL}/tasks/${taskId}/bids/${bidId}/accept`,
        {},
        {
          headers: {
            Authorization: `Bearer ${auth.token}`
          }
        }
      );
      
      dispatch({ 
        type: ACCEPT_BID_SUCCESS, 
        payload: response.data 
      });
      
      // 刷新任务详情
      dispatch(fetchTaskDetail(taskId));
    } catch (error) {
      dispatch({ 
        type: ACCEPT_BID_FAILURE, 
        payload: error.response?.data?.message || '接受投标失败'
      });
    }
  };
};

// 完成任务
export const completeTask = (taskId) => {
  return async (dispatch, getState) => {
    try {
      dispatch({ type: COMPLETE_TASK_REQUEST });
      const { auth } = getState();
      
      const response = await axios.put(
        `${API_URL}/tasks/${taskId}/complete`,
        {},
        {
          headers: {
            Authorization: `Bearer ${auth.token}`
          }
        }
      );
      
      dispatch({ 
        type: COMPLETE_TASK_SUCCESS, 
        payload: response.data 
      });
      
      // 刷新任务详情
      dispatch(fetchTaskDetail(taskId));
    } catch (error) {
      dispatch({ 
        type: COMPLETE_TASK_FAILURE, 
        payload: error.response?.data?.message || '完成任务失败'
      });
    }
  };
};

  HomeScreen.js

代码语言:txt
复制
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, TouchableOpacity, TextInput, StyleSheet, ActivityIndicator } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { fetchTasks } from '../redux/actions/taskActions';
import TaskCard from '../components/TaskCard';
import { Icon } from 'react-native-elements';

const HomeScreen = ({ navigation }) => {
  const dispatch = useDispatch();
  const { tasks, loading, error } = useSelector(state => state.tasks);
  const [searchTerm, setSearchTerm] = useState('');
  const [filteredTasks, setFilteredTasks] = useState([]);

  useEffect(() => {
    dispatch(fetchTasks());
  }, [dispatch]);

  useEffect(() => {
    if (tasks.length > 0) {
      const filtered = tasks.filter(task => 
        task.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
        task.description.toLowerCase().includes(searchTerm.toLowerCase())
      );
      setFilteredTasks(filtered);
    }
  }, [tasks, searchTerm]);

  const handleTaskPress = (taskId) => {
    navigation.navigate('TaskDetail', { taskId });
  };

  const handleCreateTask = () => {
    navigation.navigate('CreateTask');
  };

  if (loading) {
    return (
      <View style={styles.loadingContainer}>
        <ActivityIndicator size="large" color="#0066cc" />
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.errorContainer}>
        <Text style={styles.errorText}>{error}</Text>
        <TouchableOpacity 
          style={styles.retryButton}
          onPress={() => dispatch(fetchTasks())}
        >
          <Text style={styles.retryText}>重试</Text>
        </TouchableOpacity>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <View style={styles.searchContainer}>
        <TextInput
          style={styles.searchInput}
          placeholder="搜索任务..."
          value={searchTerm}
          onChangeText={setSearchTerm}
        />
        <TouchableOpacity 
          style={styles.createButton}
          onPress={handleCreateTask}
        >
          <Icon name="add" type="material" color="white" />
        </TouchableOpacity>
      </View>
      
      <FlatList
        data={filteredTasks}
        keyExtractor={item => item._id}
        renderItem={({ item }) => (
          <TaskCard 
            task={item} 
            onPress={() => handleTaskPress(item._id)} 
          />
        )}
        ListEmptyComponent={
          <View style={styles.emptyContainer}>
            <Text style={styles.emptyText}>没有找到匹配的任务</Text>
          </View>
        }
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  searchContainer: {
    flexDirection: 'row',
    padding: 10,
    backgroundColor: 'white',
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
  },
  searchInput: {
    flex: 1,
    height: 40,
    borderColor: '#ddd',
    borderWidth: 1,
    borderRadius: 20,
    paddingHorizontal: 15,
  },
  createButton: {
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: '#0066cc',
    justifyContent: 'center',
    alignItems: 'center',
    marginLeft: 10,
  },
  loadingContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  errorText: {
    color: 'red',
    fontSize: 16,
    marginBottom: 20,
    textAlign: 'center',
  },
  retryButton: {
    backgroundColor: '#0066cc',
    padding: 10,
    borderRadius: 5,
  },
  retryText: {
    color: 'white',
    fontSize: 16,
  },
  emptyContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  emptyText: {
    color: '#666',
    fontSize: 16,
  },
});

export default HomeScreen;

  taskController.js

代码语言:txt
复制
const Task = require('../models/Task');
const Bid = require('../models/Bid');
const User = require('../models/User');
const Notification = require('../models/Notification');
const mongoose = require('mongoose');

// 获取所有任务
exports.getTasks = async (req, res) => {
  try {
    const { 
      page = 1, 
      limit = 10, 
      category, 
      status = 'open', 
      minBudget, 
      maxBudget,
      search
    } = req.query;
    
    // 构建查询条件
    const query = {};
    
    if (status) {
      query.status = status;
    }
    
    if (category) {
      query.category = category;
    }
    
    if (minBudget || maxBudget) {
      query.budget = {};
      if (minBudget) query.budget.$gte = Number(minBudget);
      if (maxBudget) query.budget.$lte = Number(maxBudget);
    }
    
    if (search) {
      query.$text = { $search: search };
    }
    
    // 执行查询
    const tasks = await Task.find(query)
      .populate('employer', 'name avatar rating')
      .sort({ createdAt: -1 })
      .limit(limit * 1)
      .skip((page - 1) * limit)
      .exec();
      
    // 获取总数
    const count = await Task.countDocuments(query);
    
    res.status(200).json({
      tasks,
      totalPages: Math.ceil(count / limit),
      currentPage: page,
      totalTasks: count
    });
  } catch (error) {
    console.error('Error fetching tasks:', error);
    res.status(500).json({ message: '获取任务失败', error: error.message });
  }
};

// 获取单个任务详情
exports.getTaskById = async (req, res) => {
  try {
    const task = await Task.findById(req.params.id)
      .populate('employer', 'name avatar rating reviews')
      .populate('freelancer', 'name avatar rating reviews')
      .populate({
        path: 'bids',
        populate: {
          path: 'freelancer',
          select: 'name avatar rating'
        }
      })
      .populate('acceptedBid');
      
    if (!task) {
      return res.status(404).json({ message: '任务不存在' });
    }
    
    res.status(200).json(task);
  } catch (error) {
    console.error('Error fetching task:', error);
    res.status(500).json({ message: '获取任务详情失败', error: error.message });
  }
};

// 创建任务
exports.createTask = async (req, res) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  
  try {
    const { 
      title, 
      description, 
      category, 
      budget, 
      deadline,
      attachments 
    } = req.body;
    
    // 创建新任务
    const newTask = new Task({
      title,
      description,
      category,
      budget,
      deadline,
      employer: req.user.id,
      attachments: attachments || []
    });
    
    const savedTask = await newTask.save({ session });
    
    // 更新雇主的任务计数
    await User.findByIdAndUpdate(
      req.user.id,
      { $inc: { tasksPosted: 1 } },
      { session }
    );
    
    await session.commitTransaction();
    session.endSession();
    
    res.status(201).json({
      message: '任务创建成功',
      task: savedTask
    });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    
    console.error('Error creating task:', error);
    res.status(500).json({ message: '创建任务失败', error: error.message });
  }
};

// 提交投标
exports.submitBid = async (req, res) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  
  try {
    const { taskId } = req.params;
    const { amount, proposal, deliveryTime } = req.body;
    
    // 检查任务是否存在且状态为开放
    const task = await Task.findById(taskId).session(session);
    if (!task) {
      await session.abortTransaction();
      session.endSession();
      return res.status(404).json({ message: '任务不存在' });
    }
    
    if (task.status !== 'open') {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '该任务不接受投标' });
    }
    
    // 检查是否是自己发布的任务
    if (task.employer.toString() === req.user.id) {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '不能对自己发布的任务投标' });
    }
    
    // 检查是否已经投标
    const existingBid = await Bid.findOne({
      task: taskId,
      freelancer: req.user.id
    }).session(session);
    
    if (existingBid) {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '您已经对该任务投过标' });
    }
    
    // 创建新投标
    const newBid = new Bid({
      task: taskId,
      freelancer: req.user.id,
      amount,
      proposal,
      deliveryTime
    });
    
    const savedBid = await newBid.save({ session });
    
    // 将投标添加到任务中
    task.bids.push(savedBid._id);
    await task.save({ session });
    
    // 更新 freelancer 的投标计数
    await User.findByIdAndUpdate(
      req.user.id,
      { $inc: { bidsSubmitted: 1 } },
      { session }
    );
    
    // 创建通知
    const notification = new Notification({
      user: task.employer,
      type: 'new_bid',
      message: `您的任务"${task.title}"收到了新的投标`,
      relatedId: savedBid._id
    });
    
    await notification.save({ session });
    
    await session.commitTransaction();
    session.endSession();
    
    // 发送实时通知
    req.io.to(`user_${task.employer}`).emit('notification', {
      type: 'new_bid',
      message: `您的任务"${task.title}"收到了新的投标`,
      relatedId: savedBid._id,
      createdAt: new Date()
    });
    
    res.status(201).json({
      message: '投标提交成功',
      bid: savedBid
    });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    
    console.error('Error submitting bid:', error);
    res.status(500).json({ message: '提交投标失败', error: error.message });
  }
};

// 接受投标
exports.acceptBid = async (req, res) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  
  try {
    const { taskId, bidId } = req.params;
    
    // 查找任务和投标
    const task = await Task.findById(taskId)
      .populate('bids')
      .session(session);
      
    if (!task) {
      await session.abortTransaction();
      session.endSession();
      return res.status(404).json({ message: '任务不存在' });
    }
    
    // 检查是否是任务发布者
    if (task.employer.toString() !== req.user.id) {
      await session.abortTransaction();
      session.endSession();
      return res.status(403).json({ message: '没有权限操作此任务' });
    }
    
    // 检查任务状态
    if (task.status !== 'open') {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '该任务已不再接受投标' });
    }
    
    // 查找投标
    const bid = await Bid.findById(bidId).session(session);
    if (!bid) {
      await session.abortTransaction();
      session.endSession();
      return res.status(404).json({ message: '投标不存在' });
    }
    
    // 检查投标是否属于该任务
    if (bid.task.toString() !== taskId) {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '该投标不属于此任务' });
    }
    
    // 更新任务状态和接受的投标
    task.status = 'in_progress';
    task.acceptedBid = bidId;
    task.freelancer = bid.freelancer;
    await task.save({ session });
    
    // 更新投标状态
    bid.status = 'accepted';
    await bid.save({ session });
    
    // 更新用户信息
    await User.findByIdAndUpdate(
      bid.freelancer,
      { $inc: { tasksAccepted: 1 } },
      { session }
    );
    
    // 创建通知
    const notification = new Notification({
      user: bid.freelancer,
      type: 'bid_accepted',
      message: `您对任务"${task.title}"的投标已被接受`,
      relatedId: taskId
    });
    
    await notification.save({ session });
    
    await session.commitTransaction();
    session.endSession();
    
    // 发送实时通知
    req.io.to(`user_${bid.freelancer}`).emit('notification', {
      type: 'bid_accepted',
      message: `您对任务"${task.title}"的投标已被接受`,
      relatedId: taskId,
      createdAt: new Date()
    });
    
    res.status(200).json({
      message: '投标接受成功',
      task
    });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    
    console.error('Error accepting bid:', error);
    res.status(500).json({ message: '接受投标失败', error: error.message });
  }
};

// 完成任务
exports.completeTask = async (req, res) => {
  const session = await mongoose.startSession();
  session.startTransaction();
  
  try {
    const { taskId } = req.params;
    
    // 查找任务
    const task = await Task.findById(taskId)
      .populate('employer')
      .populate('freelancer')
      .session(session);
      
    if (!task) {
      await session.abortTransaction();
      session.endSession();
      return res.status(404).json({ message: '任务不存在' });
    }
    
    // 检查是否是任务发布者
    if (task.employer._id.toString() !== req.user.id) {
      await session.abortTransaction();
      session.endSession();
      return res.status(403).json({ message: '没有权限完成此任务' });
    }
    
    // 检查任务状态
    if (task.status !== 'in_progress') {
      await session.abortTransaction();
      session.endSession();
      return res.status(400).json({ message: '该任务不能被标记为完成' });
    }
    
    // 更新任务状态
    task.status = 'completed';
    await task.save({ session });
    
    // 更新用户信息
    await User.findByIdAndUpdate(
      task.freelancer._id,
      { 
        $inc: { 
          tasksCompleted: 1,
          earnings: task.acceptedBid.amount 
        } 
      },
      { session }
    );
    
    // 创建通知
    const notification = new Notification({
      user: task.freelancer._id,
      type: 'task_completed',
      message: `任务"${task.title}"已被标记为完成`,
      relatedId: taskId
    });
    
    await notification.save({ session });
    
    await session.commitTransaction();
    session.endSession();
    
    // 发送实时通知
    req.io.to(`user_${task.freelancer._id}`).emit('notification', {
      type: 'task_completed',
      message: `任务"${task.title}"已被标记为完成`,
      relatedId: taskId,
      createdAt: new Date()
    });
    
    res.status(200).json({
      message: '任务已标记为完成',
      task
    });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    
    console.error('Error completing task:', error);
    res.status(500).json({ message: '标记任务完成失败', error: error.message });
  }
};

App.js

代码语言:txt
复制
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Provider } from 'react-redux';
import store from './src/store';
import SocketProvider from './src/contexts/SocketContext';

// 页面导入
import SplashScreen from './src/screens/SplashScreen';
import LoginScreen from './src/screens/LoginScreen';
import RegisterScreen from './src/screens/RegisterScreen';
import HomeScreen from './src/screens/HomeScreen';
import TaskDetailScreen from './src/screens/TaskDetailScreen';
import CreateTaskScreen from './src/screens/CreateTaskScreen';
import MyTasksScreen from './src/screens/MyTasksScreen';
import MessagesScreen from './src/screens/MessagesScreen';
import ProfileScreen from './src/screens/ProfileScreen';
import PaymentScreen from './src/screens/PaymentScreen';

const Stack = createStackNavigator();

const App = () => {
  return (
    <Provider store={store}>
      <SocketProvider>
        <NavigationContainer>
          <Stack.Navigator initialRouteName="Splash">
            <Stack.Screen name="Splash" component={SplashScreen} options={{ headerShown: false }} />
            <Stack.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
            <Stack.Screen name="Register" component={RegisterScreen} options={{ headerShown: false }} />
            <Stack.Screen name="Home" component={HomeScreen} options={{ title: '任务广场' }} />
            <Stack.Screen name="TaskDetail" component={TaskDetailScreen} options={{ title: '任务详情' }} />
            <Stack.Screen name="CreateTask" component={CreateTaskScreen} options={{ title: '发布任务' }} />
            <Stack.Screen name="MyTasks" component={MyTasksScreen} options={{ title: '我的任务' }} />
            <Stack.Screen name="Messages" component={MessagesScreen} options={{ title: '消息中心' }} />
            <Stack.Screen name="Profile" component={ProfileScreen} options={{ title: '个人中心' }} />
            <Stack.Screen name="Payment" component={PaymentScreen} options={{ title: '支付中心' }} />
          </Stack.Navigator>
        </NavigationContainer>
      </SocketProvider>
    </Provider>
  );
};

export default App;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档