夜雨聆风学习资料网

ARTICLE · 1351489

JAVA任务系统任务接单系统源码支持小程序

JAVA任务系统任务接单系统源码支持小程序

SOFTWARE / 源码交付 · 支持小程序

JAVA兼职任务接单系统源码

分类 · 区域 · 服务 · 需求 · 联系人 · 商铺一套能跑在小程序里的同城任务接单中台

SpringBoot + MyBatis-Plus | uni-app | Vue / ElementUI

一个任务从发出来到做完,中间其实只有六步:有人发布需求、有人分类、有人按区域找到它、有人看过服务介绍后决定接、双方交换联系方式、最后确认交付。麻烦的是这六步通常散在微信群、朋友圈、几张 Excel 和无数条私聊里,任何一环掉链子,事后都要花成倍的时间去核对。

这套 JAVA 兼职任务接单系统 把这六步收进同一个后端:用户端用 uni-app 一套代码出小程序,管理后台用 Vue + ElementUI 做审核与配置,服务端用 SpringBoot + MyBatis-Plus + MySQL 负责状态流转、赏金冻结与并发抢单。界面是干净的蓝白配色,列表、表单、状态标签都按手机端阅读习惯排布。

下面按「功能总览 → 链路拆解 → 三端架构 → 表结构与核心代码 → 合规清单 → 常见问题」的顺序写。代码给的是能跑通的骨架,字段与表结构一一对应,拿去二开比从零重写快得多。

一、功能总览:六个模块,一条主线

先把全景摊开。六个模块不是并列的功能清单,而是同一条链路上的六个卡口:前两个决定「能不能被找到」,中间两个决定「值不值得信任」,后两个决定「接得住、找得回」。每张卡片只用一句话说清它替谁解决了什么问题。

01 分类管理

任务挂错类目,筛选里就永远搜不到它;分类是一级筛选项,也是后台统计的第一维度。

02 区域管理

同城服务最怕跨区派单,接单方跑一趟的成本比报酬还高;区域把人、单、店锁在同一片范围里。

03 服务管理

把“我会做什么”做成明码标价的服务卡,避免每次接单都重新手写介绍、重新谈价。

04 发布需求

需求描述只有一句话,接单方就得反复追问;结构化表单把预算、周期、联系方式一次问全。

05 联系人管理

聊过的人在消息列表里翻不到,等于没聊过;联系人是会话索引,也是二次触达的入口。

06 商铺管理

资质、案例、评分、成交额散落各处,用户就没法判断该不该把单交给他;商铺页是信任的落点。

二、功能拆解:一条「发现—了解—发布—接单—交接—回访」的链路

这一节按业务链路走,不按菜单走。每个模块都写清三件事:输入是什么、状态怎么变、输出给谁看。读的时候可以对照第四节表结构,字段名是一致的。

01分类管理:让任务能被搜到

输入是后台维护的分类名称、排序与启用状态。分类不做多级树,只保留一级(如平面设计、电商设计、手游开发、APP 设计、品牌推广),再深一层就变成运营自己都记不住的迷宫。前台筛选默认只拉 status = 1 的分类,下架分类不影响已发布的历史单,只是不再出现在新建入口。

状态只有两个:启用与停用。停用是软删除,历史单仍然带着分类 id,报表统计不会断档。排序值支持拖拽,前台按 sort 升序渲染,运营调顺序不需要发版。

输出是首页的分类横滑入口、筛选面板的第一项,以及后台按分类聚合的任务量与成交额。结论:分类是内容运营的抓手,别让它变成开发每次都要改 SQL 的东西。

02区域管理:把人和单锁在同一片范围

输入是省市两级的区域字典,加一个可选的商圈标签。用户端授权定位后只用来做“默认筛选”,不落库精确坐标;落库的是 region_id,这样既有同城感,又不至于把位置数据变成负担。

状态是启用与停用,配合 parent_id 组成两级结构。任务发布时区域必填,接单时服务端的区域校验是“同一 region_id 或同一父级”,跨区接单需要走额外审核——这一条在后台是一个开关,默认关闭。

输出是首页顶部的城市切换、列表里的区域标签、以及按区域统计的供需缺口(发布多、接单少的区域会被标出来,作为地推依据)。

【兼职任务接单系统】实拍 01|首页分类与区域筛选、赏金排序列表、商铺案例墙、服务详情;截图数据为演示数据

03服务管理:把“我会做什么”变成明码标价

输入是商铺配置的服务项:标题、封面、起步价、计价单位、服务说明。一个商铺可以挂多条服务,前台按销量与上架时间混合排序,避免老服务一直霸屏、新服务永远没曝光。

状态是上架、下架、审核中。价格改动只影响新订单,已生成的订单金额以快照为准——这条如果做错了,用户会看到“昨天谈好的价格今天变了”。

输出是服务详情页的报价、服务说明与“联系商家 / 购买服务”两个动作,以及后台的服务热度表。结论:价格必须是服务端算、订单必须存快照,前端只负责把标签显示清楚。

04商铺管理:把信任落到一个可以核对的页面

输入是入驻资料:商铺名称、联系人、资质照片、擅长标签、案例图与简介。后台审核通过才能出现在列表里,审核驳回要带原因,否则用户只会反复提交同一份材料。

状态是待审核 → 已通过 → 已驳回,旁支是已冻结。审核通过时写入 audit_status = 1,同时记录操作人与时间;冻结用于处理投诉,冻结后商铺不能接新单,但历史订单仍可继续走完。

输出是商铺主页的四个页签:信息、服务、案例、评价,企业信息区展示名称、规模、地址与资质。这里有个细节值得坚持:成交额、完成率、评分都由服务端定时汇总,不让前端拿订单列表自己算,否则一个分页就能把数据算错。

【兼职任务接单系统】实拍 02|商铺详情的服务与案例页签、人才页服务列表、企业信息与资质;截图数据为演示数据

05发布需求:把一句话需求变成一张可接的单

输入是标题、需求内容、联系方式、赏金、截止周期、需求分类与增值服务(置顶 / 紧急)。赏金设下限,避免 1 元单污染列表;联系方式加密入库,列表与详情页统一脱敏展示。

状态是待接单 → 已接单 → 已完成,旁支是已关闭与已退款。发布时先冻结赏金再落单:冻结失败就不允许发布,这样接单方看到的需求都是有钱在账上的。

输出是一条带任务编号的需求单、一条站内通知、一条赏金冻结流水。到这里链路才走完一半:能不能被接住,取决于下一节的并发控制。

06联系人管理:接单之后的交接与回访

输入是接单动作本身——接单成功即建立一条联系人关系(user_id + target_id + 未读数 + 最近一条消息摘要),不需要用户手动加好友。消息中心按交易提醒、系统通知、服务通知三类分流,避免重要通知被聊天淹没。

状态是未读 → 已读,会话关系不随订单结束而删除;订单完成 7 天后才允许评价入口出现,防止刷评。这里的状态很简单,难点在于唯一索引:同一对 user 与 target 只能有一行,重复接单不会生成多条联系人。

输出是消息列表、联系人列表、以及“我发布 / 我服务”的订单看板(待接单、服务中、已完成、已结算)。结论:联系人不是通讯录,是订单的索引;把它挂在订单上,回访才有上下文。

【兼职任务接单系统】实拍 03|我的页面与订单看板、消息中心与联系人、发布需求表单;截图数据为演示数据

三、技术架构:三端分工与边界

这类系统的第一版几乎都长一个样:小程序、后台、服务端各写一套判断,上线三个月开始对不上账。这套拆法的核心不是选型,而是提前把“谁说了算”定死。

端
负责什么
用户端uni-appVue 语法
页面渲染、表单校验、筛选条件组装、状态文案映射、图片压缩上传、下拉刷新与分页
不该做:算赏金、判断余额够不够、决定订单能不能变成“已完成”
管理后台VueElementUI
分类与区域维护、商铺审核、需求巡检、订单对账、数据导出、操作日志
不该做:直连数据库、绕过接口改状态、把权限判断交给按钮显隐
服务端SpringBootMyBatis-PlusMySQL
赏金冻结与结算、接单并发控制、状态机、权限、幂等、事务、定时关单与对账
不该做:把业务规则散落在 Controller 里,每个接口各写一遍

三条硬规则,写进团队规范比写进注释管用:

第一,钱和状态只允许服务端改。小程序看到的价格、余额、完成率都来自接口,前端不参与计算,也不缓存写结果。

第二,接单只能靠条件更新抢。先查再改在并发下必然超卖,判断条件必须写进 UPDATE 的 where 里,影响行数就是唯一的裁判。

第三,前端的隐藏按钮不算权限。按钮藏起来只是体验,接口上必须再校验角色、商铺归属与审核状态三层。

请求链路很朴素:小程序 → Nginx → SpringBoot → MySQL / Redis。分类、区域这类读多写少的数据进 Redis 做短缓存(5 分钟过期,后台改动主动删 key),写操作一律走库,缓存只做加速不做真相。

四、数据库与核心代码

下面七段代码构成一套最小可跑的骨架,字段与表结构严格对应。每段先说清它解决什么问题,再给代码,最后注明生产环境还要补什么。

4.1 表结构:七张表撑起一条链路

分类、区域、商铺、服务、需求单、接单记录、幂等表,外加一张联系人索引。设计上有两个刻意选择:需求单与接单记录都带 version 与唯一索引,是为了让“抢单”在数据库层就有裁判;联系方式一律存密文,是为了让脱敏展示变成默认行为而不是例外。

SQL

-- 任务接单平台核心表(MySQL 8.0)CREATE TABLE t_task_category (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  name VARCHAR(32) NOT NULL,  sort INT NOT NULL DEFAULT 0,  status TINYINT NOT NULL DEFAULT 1,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP) COMMENT '任务分类';CREATE TABLE t_task_region (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  parent_id BIGINT NOT NULL DEFAULT 0,  name VARCHAR(32) NOT NULL,  sort INT NOT NULL DEFAULT 0,  status TINYINT NOT NULL DEFAULT 1) COMMENT '区域';CREATE TABLE t_shop (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  user_id BIGINT NOT NULL,  shop_name VARCHAR(64) NOT NULL,  contact_phone VARCHAR(64) COMMENT '密文',  category_id BIGINT NOT NULL DEFAULT 0,  region_id BIGINT NOT NULL DEFAULT 0,  audit_status TINYINT NOT NULL DEFAULT 0,  audit_remark VARCHAR(128) DEFAULT '',  balance DECIMAL(12,2) NOT NULL DEFAULT 0.00,  version INT NOT NULL DEFAULT 0,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  UNIQUE KEY uk_shop_user (user_id),  KEY idx_audit (audit_status)) COMMENT '商铺 0待审 1通过 2驳回';CREATE TABLE t_shop_service (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  shop_id BIGINT NOT NULL,  title VARCHAR(64) NOT NULL,  price DECIMAL(10,2) NOT NULL,  order_count INT NOT NULL DEFAULT 0,  status TINYINT NOT NULL DEFAULT 1,  KEY idx_shop (shop_id)) COMMENT '服务';CREATE TABLE t_task (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  task_no VARCHAR(32) NOT NULL,  publisher_id BIGINT NOT NULL,  category_id BIGINT NOT NULL,  region_id BIGINT NOT NULL,  title VARCHAR(64) NOT NULL,  budget DECIMAL(10,2) NOT NULL,  deadline_days INT NOT NULL DEFAULT 7,  contact_phone VARCHAR(64) NOT NULL,  status TINYINT NOT NULL DEFAULT 0,  accept_shop_id BIGINT DEFAULT NULL,  version INT NOT NULL DEFAULT 0,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  UNIQUE KEY uk_task_no (task_no),  KEY idx_list (status, category_id, region_id)) COMMENT '需求单 0待接 1已接 2完成';CREATE TABLE t_task_order (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  task_id BIGINT NOT NULL,  shop_id BIGINT NOT NULL,  request_no VARCHAR(40) NOT NULL,  amount DECIMAL(10,2) NOT NULL,  status TINYINT NOT NULL DEFAULT 0,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  UNIQUE KEY uk_task_shop (task_id, shop_id),  UNIQUE KEY uk_request_no (request_no)) COMMENT '接单记录';CREATE TABLE t_idempotent (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  request_no VARCHAR(40) NOT NULL,  biz_type VARCHAR(24) NOT NULL,  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  UNIQUE KEY uk_req (request_no)) COMMENT '幂等表';CREATE TABLE t_contact (  id BIGINT PRIMARY KEY AUTO_INCREMENT,  user_id BIGINT NOT NULL,  target_id BIGINT NOT NULL,  last_msg VARCHAR(64) DEFAULT '',  unread INT NOT NULL DEFAULT 0,  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,  UNIQUE KEY uk_tgt (user_id, target_id)) COMMENT '联系人';

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;金额统一用 DECIMAL,不要用 float。

4.2 发布需求:一个事务里冻结赏金并落单

这段代码解决“需求发出来了,钱却没冻住”。顺序是:先过幂等、再校验分类与区域、然后扣冻结额度、最后写单与流水,四步在同一个事务里,任一步失败整体回滚。扣减用 CAS 写法(WHERE balance >= 金额),并发下也不会扣成负数。

JAVA

@Transactional(rollbackFor = Exception.class)public String publish(PublishDTO dto, Long uid) {    // 1) 幂等:请求号先过 Redis,再落唯一索引    String rq = dto.getRequestNo();    idempotentService.check(rq, "TASK");    // 2) 分类、区域必须启用,否则不进列表    Category c = categoryMapper        .selectById(dto.getCategoryId());    if (c == null || c.getStatus() != 1) {        throw new BizException("分类不可用");    }    Region r = regionMapper        .selectById(dto.getRegionId());    if (r == null || r.getStatus() != 1) {        throw new BizException("区域不可用");    }    BigDecimal amt = dto.getBudget();    BigDecimal min = new BigDecimal("300");    if (amt.compareTo(min) < 0) {        throw new BizException("赏金不足 300");    }    // 3) 冻结赏金:余额不足时影响 0 行    int rows = shopMapper.freeze(uid, amt);    if (rows == 0) {        throw new BizException("账户余额不足");    }    // 4) 写需求单,status = 0 表示待接单    Task t = new Task();    t.setTaskNo(snowflake.nextIdStr());    t.setPublisherId(uid);    t.setCategoryId(dto.getCategoryId());    t.setRegionId(dto.getRegionId());    t.setTitle(dto.getTitle());    t.setBudget(amt);    t.setDeadlineDays(dto.getDeadlineDays());    // 联系方式入库前加密,列表只回显脱敏值    String tel = dto.getContactPhone();    t.setContactPhone(aes.encrypt(tel));    t.setStatus(0);    taskMapper.insert(t);    // 5) 流水与需求单同事务,失败整体回滚    Flow f = Flow.freeze(uid, t.getId(), amt);    flowMapper.insert(f);    return t.getTaskNo();}

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;冻结额度与可提现余额建议分两个字段,避免结算时混账。

4.3 抢单:条件更新在前,唯一索引在后

这段代码解决“十个服务者同时点抢单,结果三个人都抢到了”。思路是把判断写进 where:状态必须是 0,更新成功才有 1 行受影响,其余请求全部拿到 0 行,直接抛业务异常。写完接单记录后,唯一索引 uk_task_shop 再兜一层,保证同一需求同一商铺不可能出现两条记录。

JAVA

@Transactional(rollbackFor = Exception.class)public void accept(Long taskId, Long shopId) {    // 条件更新:只有状态 0(待接单)才抢得到,    // 并发下只有一条更新能返回 1,其余全为 0    int rows = taskMapper.accept(taskId, shopId);    if (rows == 0) {        throw new BizException("该需求已被接走");    }    Task t = taskMapper.selectById(taskId);    TaskOrder o = new TaskOrder();    o.setTaskId(taskId);    o.setShopId(shopId);    o.setAmount(t.getBudget());    o.setStatus(0);    // uk_task_shop 保证同一需求同一商铺只有一条    orderMapper.insert(o);    // 接单后给发布方留一条会话索引,不写正文    contactMapper.touch(t.getPublisherId(),        shopId, "需求已被接单");}

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;被接走的需求要有超时释放定时任务,否则发布方关单后额度会一直冻着。

4.4 幂等:Redis 抢号 + 唯一索引兜底

这段代码解决“网络抖动导致用户点了两次,发了两张单”。两层防护:Redis 用 setIfAbsent 抢请求号,挡住绝大多数重复提交;数据库唯一索引兜底,Redis 挂了、服务多实例了,也不会真的落两条数据。

JAVA

public void check(String rq, String bizType) {    if (!StringUtils.hasText(rq)) {        throw new BizException("缺少请求号");    }    String key = "idem:" + bizType + ":" + rq;    // 第一层:Redis 抢号,挡住重复点击    Duration ttl = Duration.ofMinutes(10);    Boolean ok = redis.opsForValue()        .setIfAbsent(key, "1", ttl);    if (Boolean.FALSE.equals(ok)) {        throw new BizException("请勿重复提交");    }    try {        // 第二层:唯一索引兜底,Redis 挂了        //         也不会落两条数据        Idempotent i = new Idempotent();        i.setRequestNo(rq);        i.setBizType(bizType);        idempotentMapper.insert(i);    } catch (DuplicateKeyException e) {        throw new BizException("该请求已处理");    }}

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;request_no 建议由客户端生成并在重试时复用,服务端只校验不重造。

4.5 权限:注解管功能,商铺管数据

这段代码解决“没通过审核的商铺也能接单”和“自己能接自己发的单”。功能权限用注解声明式校验,数据权限在方法里判:商铺必须审核通过、需求必须是待接单、发布人不能是自己。审核动作单独落日志,保留操作人与前后状态。

JAVA

@PreAuthorize("hasAnyRole('SHOP','ADMIN')")@PostMapping("/task/{id}/accept")public R<TaskOrderVO> accept(        @PathVariable Long id,        @RequestHeader("X-Request-No")        String rq) {    Long uid = UserContext.getUserId();    Long shopId = UserContext.getShopId();    // 功能权限由注解拦,数据权限在这里判    Shop shop = shopMapper.selectById(shopId);    if (shop == null            || shop.getAuditStatus() != 1) {        throw new BizException("商铺未通过审核");    }    Task task = taskMapper.selectById(id);    if (task == null || task.getStatus() != 0) {        throw new BizException("需求已被接走");    }    if (task.getPublisherId().equals(uid)) {        throw new BizException("不能接自己的单");    }    // 审核动作落日志,保留操作人与前后状态    opLogService.log("ACCEPT", id,        task.getStatus(), 1);    return R.ok(taskService.accept(id,        shopId, rq));}

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;角色与商铺的绑定关系要能在后台解绑,否则员工离职就得改库。

4.6 多端请求:一个封装管住 token 与请求号

这段代码解决“每个页面各写一遍登录过期处理”。uni-app 里把请求收进一个函数:统一拼域名、统一带 token、统一在 401 时回登录页、统一给写请求挂上 X-Request-No。页面只调 api.xxx,不关心这些。

JAVASCRIPT

const BASE = 'https://api.example.com';const LOGIN = '/pages/login';function uuid() {  return 'rq-' + Date.now() + '-' + Math.random()    .toString(16).slice(2, 8);}export function request(path, data = {},                        method = 'GET') {  return new Promise((resolve, reject) => {    uni.request({      url: BASE + path,      method,      data,      header: {        'Content-Type': 'application/json',        'Authorization': 'Bearer ' +          (uni.getStorageSync('token') || ''),        // 写请求统一带请求号,服务端据此幂等        'X-Request-No': data.requestNo || uuid()      },      success(res) {        // 401 统一回登录页,页面不再各自处理        if (res.statusCode === 401) {          uni.removeStorageSync('token');          uni.reLaunch({ url: LOGIN });          return reject(new Error('401'));        }        const body = res.data || {};        if (res.statusCode !== 200            || body.code !== 0) {          const msg = body.msg || '请求失败';          uni.showToast({ title: msg,            icon: 'none' });          return reject(new Error(msg));        }        resolve(body.data);      },      fail(err) {        uni.showToast({ title: '网络异常',          icon: 'none' });        reject(err);      }    });  });}

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;小程序还需要在后台配置 request 合法域名,否则请求会被直接拦掉。

4.7 后台表格:分页、状态标签、审核

这是管理后台最常见的一屏:筛选 + 表格 + 分页 + 审核。状态用 el-tag 区分颜色,只有待审核的行才出现操作按钮,处理过的显示“已处理”,从交互上避免重复审核。

VUE

<template>  <div class="shop-page">    <el-form :inline="true" :model="query">      <el-form-item label="审核状态">        <el-select v-model="query.auditStatus"          clearable placeholder="全部">          <el-option label="待审核" :value="0" />          <el-option label="已通过" :value="1" />          <el-option label="已驳回" :value="2" />        </el-select>      </el-form-item>      <el-form-item label="商铺名">        <el-input v-model="query.keyword"          placeholder="模糊搜索" />      </el-form-item>      <el-button type="primary"        @click="reload">查询</el-button>    </el-form>    <el-table v-loading="loading"      :data="rows" border>      <el-table-column prop="shopName"        label="商铺" min-width="150" />      <el-table-column prop="shopId"        label="ID" width="70" />      <el-table-column label="状态" width="90">        <template slot-scope="s">          <el-tag size="small"            :type="tagOf(s.row.auditStatus)">            {{ textOf(s.row.auditStatus) }}          </el-tag>        </template>      </el-table-column>      <el-table-column prop="createdAt"        label="提交时间" width="150" />      <el-table-column label="操作" width="110">        <template slot-scope="s">          <el-button v-if="isPending(s.row)"            type="text"            @click="openAudit(s.row)">            审核          </el-button>          <span v-else class="done">已处理</span>        </template>      </el-table-column>    </el-table>    <el-pagination background class="pager"      layout="total, prev, pager, next"      :total="total" :page-size="query.size"      :current-page.sync="query.page"      @current-change="load" />  </div></template><script>export default {  data() {    return {      loading: false,      rows: [],      total: 0,      query: { page: 1, size: 10,        auditStatus: 0, keyword: '' }    };  },  created() { this.load(); },  methods: {    textOf(v) {      const s = ['待审核', '已通过', '已驳回'];      return s[v] || '未知';    },    isPending(row) {      return row.auditStatus === 0;    },    tagOf(v) {      const t = ['warning','success','danger'];      return t[v] || 'info';    },    reload() {      this.query.page = 1; this.load();    },    async load() {      this.loading = true;      try {        const res = await this.$api          .shopPage(this.query);        this.rows = res.list;        this.total = res.total;      } finally {        this.loading = false;      }    },    openAudit(row) {      this.$confirm('确认通过该商铺?', '审核', {        type: 'warning'      }).then(async () => {        await this.$api.auditShop(row.shopId, 1);        this.$message.success('已通过');        this.load();      }).catch(() => {});    }  }};</script>

示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;数据量大时分页参数要下沉到 SQL,不要前端全量过滤。

五、合规与上线检查

任务接单平台踩的线比普通工具类产品多:个人信息、资金结算、用工关系、内容审核。技术能做的部分有限,但做与不做差别很大。下面几条建议在开发阶段就写进需求,不要等审核被拒再补。

个人信息。手机号、真实姓名、位置按最小必要收集。手机号加密存储、列表与详情页统一脱敏(138****0000),完整号码只在必要环节解密并留痕。隐私政策里要写清收集范围、用途与保存期限,注销入口不能藏在三级菜单。

实名与资质。接单方建议做实名或企业资质认证,商铺要留存营业执照。认证信息只做校验与展示“已认证”状态,不在前端传输证件原图;证件照存储加密,访问走带时效的签名链接。

资金结算。赏金属于交易款,建议走持牌支付机构的商户号与分账能力,平台不做资金池、不做二次清算。提现要实名一致、有额度与频次限制,结算流水与订单一一对应,方便对账与开票。涉及个人所得税的代扣代缴,按灵活用工的合规路径处理。

内容审核。需求标题、正文、服务描述、案例图都要过机审 + 人审,重点拦刷单炒信、代写代考、虚假招聘、涉赌涉诈。关键词库要能后台维护,命中后进人工队列,而不是直接发布。

用工与未成年人。平台不是用人单位,这一点要在协议里写清楚;不得发布招用未满十六周岁未成年人的需求,涉及未成年人的服务类目建议直接禁止。用工性质、社保、工伤责任由发布方与接单方按法律约定,平台只提供信息撮合与履约工具。

数据与留痕。审核、冻结、退款、改价这些动作都要落操作日志;订单与资金流水保存期限按当地要求执行。导出功能要限权、限频、留痕,导出文件带水印。

上线前建议逐项打勾

       · 小程序类目与资质已提交并通过审核       · 隐私政策、用户协议、服务商入驻协议已上线       · 手机号、证件照已加密,列表页已脱敏       · 抢单、发布、退款三处均有幂等与条件更新       · 超时未接单、未确认的需求有自动关单任务       · 资金走持牌机构分账,平台不经手资金池    

六、这套系统适合谁

同城技能服务平台。设计、摄影、家政、维修这类“人在本地、活在本地”的生意,最缺的不是流量而是履约记录。分类与区域两个模块一搭,供需就能在同一片范围里对上。

兼职与众包平台。需求方发单、服务方抢单、平台审核,这条主线几乎不用改结构;把赏金规则、结算周期、评价维度换成自己行业的说法即可。

垂直行业外包撮合。装修、活动执行、软件开发这类单价高、环节多的场景,可以把“服务”当成标准商品库,用商铺页承载案例与资质,用联系人模块承接长周期沟通。

做行业 SaaS 或接二开的技术团队。表结构与接口分层是按“可扩展”写的:换一套 UI 就是另一个客户,核心链路不用重写;多租户改造主要动 shop 表与数据权限那一层。

七、常见问题

Q1:只做小程序,不要管理后台行不行?   不建议。分类区域维护、商铺审核、退款处理都需要后台,没有后台就只能人工改库,出问题的概率比省下的开发量高得多。后台第一版可以只做五六个页面:审核、分类、区域、需求巡检、订单对账。

Q2:抢单会不会出现两个人同时接到同一单?   不会。接单走条件更新(WHERE status = 0),影响行数为 1 才算抢到,其余请求直接失败;接单记录上再加“同一需求同一商铺唯一”的索引兜底。真正要盯的是超时关单,别让被占住的需求一直不释放。

Q3:赏金怎么保证安全?   发布时先冻结、完工确认后解冻结算,中间平台不碰资金本体:冻结的是账户可用额度,结算走持牌支付机构的分账能力。系统只做记账与状态流转,资金存管与结算资质需按当地要求由支付机构承接。

Q4:支持二次开发吗?改得多不多?   支持。代码按 Controller / Service / Mapper 分层,表结构与接口文档随源码提供。常见定制是加分类、改赏金规则、换 UI 配色、接入自家支付,这四类都不需要动核心链路。

Q5:上线需要准备什么?   小程序主体与类目资质、已备案的域名与 HTTPS 证书、服务器与数据库、支付商户号(涉及线上收款时)、以及隐私政策与服务协议文本。技术侧还要配置 request 合法域名、上传域名与隐私授权弹窗。

八、写在最后

任务接单系统说到底只做三件事:把需求说清楚、把单接稳、把钱结明白。这三件事做扎实,比堆十个营销玩法更有用;反过来,接单靠人工确认、结算靠 Excel 对账,用户量一上来就会散架。

如果你正在做同城服务、兼职众包或行业外包撮合,或者想基于 SpringBoot + uni-app 做一套可二开的任务接单系统,欢迎找我领取演示资料和部署说明。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料