JAVA校园跑腿代买代拿社区-校园跑腿小程序的后端代码示例
·
🏃 JAVA校园跑腿系统 - 后端完整代码示例
校园跑腿代买代拿 | Spring Boot + MyBatis Plus + MySQL + Redis
📦 一、项目依赖 pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<groupId>com.campus</groupId>
<artifactId>campus-errand</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<jwt.version>0.12.3</jwt.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.23</version>
</dependency>
<!-- 微信小程序SDK (可选) -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.5.0</version>
</dependency>
</dependencies>
</project>
📁 二、项目结构
src/main/java/com/campus/errand/
├── CampusErrandApplication.java # 启动类
├── config/
│ ├── CorsConfig.java # 跨域配置
│ ├── MyBatisPlusConfig.java # MP配置
│ ├── RedisConfig.java # Redis配置
│ └── JwtConfig.java # JWT配置
├── common/
│ ├── Result.java # 统一返回
│ ├── PageResult.java # 分页返回
│ ├── BaseEntity.java # 基础实体
│ └── BusinessException.java # 业务异常
├── entity/
│ ├── User.java # 用户
│ ├── ErrandOrder.java # 跑腿订单
│ ├── Runner.java # 跑腿员
│ └── Category.java # 分类
├── mapper/
│ ├── UserMapper.java
│ ├── ErrandOrderMapper.java
│ └── RunnerMapper.java
├── service/
│ ├── UserService.java
│ ├── ErrandService.java
│ └── RunnerService.java
├── controller/
│ ├── UserController.java
│ ├── ErrandController.java
│ └── RunnerController.java
└── utils/
└── JwtUtils.java # JWT工具
🔧 三、核心代码
1️⃣ 统一返回 Result.java
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}
2️⃣ 实体类
👤 User.java - 用户
java
@Data
@TableName("user")
public class User extends BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
private String openid; // 微信openid
private String nickname; // 昵称
private String avatar; // 头像
private String phone; // 手机号
private Integer role; // 0-普通用户 1-跑腿员 2-管理员
private BigDecimal balance; // 余额
private Integer status; // 0-禁用 1-正常
}
📦 ErrandOrder.java - 跑腿订单 ⭐
java
@Data
@TableName("errand_order")
public class ErrandOrder extends BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo; // 订单号
private Long userId; // 发布者ID
private Long runnerId; // 接单跑腿员ID
private Integer type; // 1-代买 2-代拿 3-代送 4-其他
private String title; // 标题
private String description; // 详细描述
private String pickupAddress; // 取货地址
private String deliveryAddress; // 送货地址
private String contactPhone; // 联系电话
private BigDecimal reward; // 跑腿费
private Integer status; // 0-待接单 1-进行中 2-已完成 3-已取消 4-已退款
private LocalDateTime acceptTime; // 接单时间
private LocalDateTime finishTime; // 完成时间
private String remark; // 备注
}
🏃 Runner.java - 跑腿员
java
@Data
@TableName("runner")
public class Runner extends BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId; // 关联用户ID
private String realName; // 真实姓名
private String studentId; // 学号
private String idCard; // 身份证
private Integer status; // 0-待审核 1-已认证 2-已拒绝
private Integer orderCount; // 接单总数
private BigDecimal totalEarning; // 总收入
private Double rating; // 评分 4.5-5.0
}
📂 Category.java - 分类
java
@Data
@TableName("category")
public class Category {
@TableId(type = IdType.AUTO)
private Long id;
private String name; // 分类名
private String icon; // 图标
private Integer sort; // 排序
private Integer status; // 0-禁用 1-启用
}
3️⃣ Controller层 ⭐
👤 UserController.java - 用户模块
java
@RestController
@RequestMapping("/api/user")
@CrossOrigin
public class UserController {
@Autowired
private UserService userService;
// 微信登录
@PostMapping("/wxLogin")
public Result<?> wxLogin(@RequestBody WxLoginDTO dto) {
String token = userService.wxLogin(dto.getCode());
return Result.success(Map.of("token", token));
}
// 获取用户信息
@GetMapping("/info")
public Result<?> getUserInfo(@RequestAttribute Long userId) {
User user = userService.getById(userId);
return Result.success(user);
}
// 申请成为跑腿员
@PostMapping("/applyRunner")
public Result<?> applyRunner(@RequestAttribute Long userId,
@RequestBody RunnerApplyDTO dto) {
userService.applyRunner(userId, dto);
return Result.success();
}
// 充值
@PostMapping("/recharge")
public Result<?> recharge(@RequestAttribute Long userId,
@RequestParam BigDecimal amount) {
userService.recharge(userId, amount);
return Result.success();
}
}
📦 ErrandController.java - 跑腿订单 ⭐⭐⭐
java
@RestController
@RequestMapping("/api/errand")
@CrossOrigin
public class ErrandController {
@Autowired
private ErrandService errandService;
// 发布跑腿订单
@PostMapping("/publish")
public Result<?> publish(@RequestAttribute Long userId,
@RequestBody ErrandOrderDTO dto) {
String orderNo = errandService.publish(userId, dto);
return Result.success(Map.of("orderNo", orderNo));
}
// 首页 - 订单列表(待接单)
@GetMapping("/list")
public Result<?> list(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) Integer type,
@RequestParam(required = false) String keyword
) {
Page<ErrandOrderVO> result = errandService.getOrderList(page, size, type, keyword);
return Result.success(result);
}
// 订单详情
@GetMapping("/detail/{id}")
public Result<?> detail(@PathVariable Long id) {
ErrandOrderVO order = errandService.getOrderDetail(id);
return Result.success(order);
}
// 接单
@PostMapping("/accept/{orderId}")
public Result<?> accept(@PathVariable Long orderId,
@RequestAttribute Long runnerId) {
errandService.acceptOrder(orderId, runnerId);
return Result.success();
}
// 更新订单状态
@PostMapping("/updateStatus")
public Result<?> updateStatus(@RequestBody StatusUpdateDTO dto) {
errandService.updateStatus(dto);
return Result.success();
}
// 我的订单(发布者)
@GetMapping("/myOrders")
public Result<?> myOrders(@RequestAttribute Long userId,
@RequestParam Integer status) {
List<ErrandOrderVO> orders = errandService.getUserOrders(userId, status);
return Result.success(orders);
}
// 取消订单
@PostMapping("/cancel/{orderId}")
public Result<?> cancel(@PathVariable Long orderId,
@RequestAttribute Long userId) {
errandService.cancelOrder(orderId, userId);
return Result.success();
}
// 评价
@PostMapping("/rate/{orderId}")
public Result<?> rate(@PathVariable Long orderId,
@RequestAttribute Long userId,
@RequestParam Integer score,
@RequestParam String content) {
errandService.rateOrder(orderId, userId, score, content);
return Result.success();
}
}
🏃 RunnerController.java - 跑腿员模块
java
@RestController
@RequestMapping("/api/runner")
@CrossOrigin
public class RunnerController {
@Autowired
private RunnerService runnerService;
// 附近跑腿员列表
@GetMapping("/nearby")
public Result<?> nearby(@RequestParam Double latitude,
@RequestParam Double longitude) {
List<RunnerVO> runners = runnerService.getNearbyRunners(latitude, longitude);
return Result.success(runners);
}
// 跑腿员接单大厅
@GetMapping("/orders")
public Result<?> availableOrders(@RequestAttribute Long runnerId) {
List<ErrandOrderVO> orders = runnerService.getAvailableOrders(runnerId);
return Result.success(orders);
}
// 跑腿员统计
@GetMapping("/stats")
public Result<?> stats(@RequestAttribute Long runnerId) {
RunnerStatsVO stats = runnerService.getStats(runnerId);
return Result.success(stats);
}
}
4️⃣ Service层 ⭐⭐⭐
📦 ErrandService.java - 跑腿订单服务(核心)
java
@Service
@Slf4j
public class ErrandServiceImpl implements ErrandService {
@Autowired
private ErrandOrderMapper orderMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 发布跑腿订单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String publish(Long userId, ErrandOrderDTO dto) {
// 1. 生成订单号
String orderNo = "ER" + System.currentTimeMillis() +
RandomUtil.randomNumbers(4);
// 2. 保存订单
ErrandOrder order = new ErrandOrder();
BeanUtils.copyProperties(dto, order);
order.setOrderNo(orderNo);
order.setUserId(userId);
order.setStatus(0); // 待接单
order.setReward(dto.getReward());
orderMapper.insert(order);
// 3. 推送到Redis实时队列 (通知附近跑腿员)
String key = "errand:waiting:" + dto.getType();
redisTemplate.opsForList().rightPush(key, orderNo);
// 设置过期时间 30分钟
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
log.info("新订单发布: {}", orderNo);
return orderNo;
}
/**
* 接单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void acceptOrder(Long orderId, Long runnerId) {
// 1. 查询订单
ErrandOrder order = orderMapper.selectById(orderId);
if (order == null) throw new BusinessException("订单不存在");
if (order.getStatus() != 0) throw new BusinessException("订单已被接单");
// 2. 分布式锁防止超卖
String lockKey = "errand:lock:" + orderId;
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (!Boolean.TRUE.equals(locked)) {
throw new BusinessException("订单正在被处理,请稍后重试");
}
try {
// 3. 更新订单
order.setRunnerId(runnerId);
order.setStatus(1); // 进行中
order.setAcceptTime(LocalDateTime.now());
orderMapper.updateById(order);
// 4. 从等待队列移除
redisTemplate.opsForList().remove("errand:waiting:" + order.getType(), orderId);
log.info("跑腿员{}接单: {}", runnerId, orderId);
} finally {
redisTemplate.delete(lockKey);
}
}
/**
* 订单列表(首页)
*/
@Override
public Page<ErrandOrderVO> getOrderList(Integer page, Integer size,
Integer type, String keyword) {
Page<ErrandOrder> pageParam = new Page<>(page, size);
LambdaQueryWrapper<ErrandOrder> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ErrandOrder::getStatus, 0) // 只查待接单
.like(StringUtils.isNotBlank(keyword),
ErrandOrder::getTitle, keyword)
.eq(type != null, ErrandOrder::getType, type)
.orderByDesc(ErrandOrder::getCreateTime);
Page<ErrandOrder> result = orderMapper.selectPage(pageParam, wrapper);
// 转换VO
Page<ErrandOrderVO> voPage = new Page<>();
BeanUtils.copyProperties(result, voPage, "records");
voPage.setRecords(result.getRecords().stream()
.map(this::convertToVO)
.collect(Collectors.toList()));
return voPage;
}
/**
* 取消订单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(Long orderId, Long userId) {
ErrandOrder order = orderMapper.selectById(orderId);
if (order == null) throw new BusinessException("订单不存在");
if (!order.getUserId().equals(userId)) {
throw new BusinessException("无权操作此订单");
}
if (order.getStatus() != 0) {
throw new BusinessException("只能取消待接单订单");
}
order.setStatus(3); // 已取消
orderMapper.updateById(order);
// 退还跑腿费(如果已支付)
if (order.getReward() != null && order.getReward().compareTo(BigDecimal.ZERO) > 0) {
// 调用支付退款接口...
}
}
/**
* 评价订单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void rateOrder(Long orderId, Long userId, Integer score, String content) {
ErrandOrder order = orderMapper.selectById(orderId);
if (order == null || !order.getUserId().equals(userId)) {
throw new BusinessException("订单不存在");
}
if (order.getStatus() != 2) {
throw new BusinessException("只能评价已完成订单");
}
// 更新评分(这里简化,实际应关联评价表)
order.setRemark(content);
orderMapper.updateById(order);
// 更新跑腿员评分
Runner runner = runnerMapper.selectById(order.getRunnerId());
if (runner != null) {
Double newRating = (runner.getRating() * runner.getOrderCount() + score)
/ (runner.getOrderCount() + 1);
runner.setRating(newRating);
runner.setOrderCount(runner.getOrderCount() + 1);
runnerMapper.updateById(runner);
}
}
private ErrandOrderVO convertToVO(ErrandOrder order) {
ErrandOrderVO vo = new ErrandOrderVO();
BeanUtils.copyProperties(order, vo);
// 查询发布者信息
User user = userMapper.selectById(order.getUserId());
if (user != null) {
vo.setUserNickname(user.getNickname());
vo.setUserAvatar(user.getAvatar());
}
return vo;
}
}
🏃 RunnerService.java - 跑腿员服务
java
@Service
public class RunnerServiceImpl implements RunnerService {
@Autowired
private RunnerMapper runnerMapper;
@Override
public List<RunnerVO> getNearbyRunners(Double lat, Double lon) {
// 实际项目应使用MySQL空间函数或Redis GEO
// 这里简化为查询所有已认证跑腿员
LambdaQueryWrapper<Runner> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Runner::getStatus, 1); // 已认证
List<Runner> runners = runnerMapper.selectList(wrapper);
return runners.stream()
.map(r -> {
RunnerVO vo = new RunnerVO();
BeanUtils.copyProperties(r, vo);
// 计算距离(简化)
vo.setDistance(calculateDistance(lat, lon, r.getLatitude(), r.getLongitude()));
return vo;
})
.sorted(Comparator.comparing(RunnerVO::getDistance))
.collect(Collectors.toList());
}
@Override
public List<ErrandOrderVO> getAvailableOrders(Long runnerId) {
// 查询待接单订单(排除自己已接的)
LambdaQueryWrapper<ErrandOrder> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ErrandOrder::getStatus, 0)
.ne(ErrandOrder::getRunnerId, runnerId)
.orderByDesc(ErrandOrder::getCreateTime);
List<ErrandOrder> orders = orderMapper.selectList(wrapper);
return orders.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
}
@Override
public RunnerStatsVO getStats(Long runnerId) {
RunnerStatsVO stats = new RunnerStatsVO();
// 今日接单数
stats.setTodayOrders(orderMapper.countTodayOrders(runnerId));
// 总收入
stats.setTotalEarning(runnerMapper.sumEarning(runnerId));
// 评分
Runner runner = runnerMapper.selectById(runnerId);
stats.setRating(runner != null ? runner.getRating() : 0.0);
return stats;
}
private Double calculateDistance(Double lat1, Double lon1, Double lat2, Double lon2) {
// Haversine公式计算距离(km)
double R = 6371;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
}
5️⃣ JWT工具类 JwtUtils.java
java
@Component
public class JwtUtils {
@Value("${jwt.secret:campus-errand-secret-key-2024}")
private String secret;
private Key getSignKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
/**
* 生成Token
*/
public String generateToken(Long userId) {
return Jwts.builder()
.subject(String.valueOf(userId))
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600 * 1000))
.signWith(getSignKey())
.compact();
}
/**
* 解析Token
*/
public Long parseToken(String token) {
return Long.parseLong(Jwts.parser()
.verifyWith(getSignKey())
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject());
}
/**
* 验证Token
*/
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(getSignKey()).build().parseSignedClaims(token);
return true;
} catch (JwtException e) {
return false;
}
}
}
6️⃣ 拦截器 JwtInterceptor.java
java
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtils jwtUtils;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
if (!(handler instanceof HandlerMethod)) return true;
String token = request.getHeader("Authorization");
if (StringUtils.isBlank(token) || !token.startsWith("Bearer ")) {
throw new BusinessException("请先登录");
}
token = token.substring(7);
if (!jwtUtils.validateToken(token)) {
throw new BusinessException("Token已过期");
}
Long userId = jwtUtils.parseToken(token);
request.setAttribute("userId", userId);
return true;
}
}
🗄️ 四、数据库SQL
sql
CREATE DATABASE campus_errand DEFAULT CHARSET utf8mb4;
USE campus_errand;
-- 用户表
CREATE TABLE `user` (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
openid VARCHAR(100) UNIQUE COMMENT '微信openid',
nickname VARCHAR(100) COMMENT '昵称',
avatar VARCHAR(500) COMMENT '头像',
phone VARCHAR(20) COMMENT '手机号',
role TINYINT DEFAULT 0 COMMENT '0-用户 1-跑腿员 2-管理员',
balance DECIMAL(10,2) DEFAULT 0 COMMENT '余额',
status TINYINT DEFAULT 1 COMMENT '0-禁用 1-正常',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) COMMENT '用户表';
-- 跑腿订单表
CREATE TABLE `errand_order` (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(64) UNIQUE NOT NULL COMMENT '订单号',
user_id BIGINT NOT NULL COMMENT '发布者ID',
runner_id BIGINT COMMENT '跑腿员ID',
type TINYINT COMMENT '1-代买 2-代拿 3-代送 4-其他',
title VARCHAR(200) COMMENT '标题',
description TEXT COMMENT '详细描述',
pickup_address VARCHAR(500) COMMENT '取货地址',
delivery_address VARCHAR(500) COMMENT '送货地址',
contact_phone VARCHAR(20) COMMENT '联系电话',
reward DECIMAL(10,2) COMMENT '跑腿费',
status TINYINT DEFAULT 0 COMMENT '0-待接单 1-进行中 2-已完成 3-已取消 4-已退款',
accept_time DATETIME COMMENT '接单时间',
finish_time DATETIME COMMENT '完成时间',
remark VARCHAR(500) COMMENT '备注',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_status (status),
INDEX idx_user (user_id),
INDEX idx_runner (runner_id)
) COMMENT '跑腿订单表';
-- 跑腿员表
CREATE TABLE `runner` (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT UNIQUE NOT NULL COMMENT '用户ID',
real_name VARCHAR(50) COMMENT '真实姓名',
student_id VARCHAR(50) COMMENT '学号',
id_card VARCHAR(18) COMMENT '身份证',
status TINYINT DEFAULT 0 COMMENT '0-待审核 1-已认证 2-已拒绝',
order_count INT DEFAULT 0 COMMENT '接单总数',
total_earning DECIMAL(10,2) DEFAULT 0 COMMENT '总收入',
rating DOUBLE DEFAULT 5.0 COMMENT '评分',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) COMMENT '跑腿员表';
-- 分类表
CREATE TABLE `category` (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL COMMENT '分类名',
icon VARCHAR(200) COMMENT '图标',
sort INT DEFAULT 0 COMMENT '排序',
status TINYINT DEFAULT 1 COMMENT '0-禁用 1-启用'
) COMMENT '分类表';
-- 插入测试分类
INSERT INTO category (name, icon, sort) VALUES
('代买饭', '/static/icons/food.png', 1),
('代拿快递', '/static/icons/express.png', 2),
('代送文件', '/static/icons/file.png', 3),
('代打印', '/static/icons/print.png', 4),
('其他', '/static/icons/other.png', 5);
🚀 五、配置文件 application.yml
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/campus_errand?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
data:
redis:
host: localhost
port: 6379
password:
database: 0
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
jwt:
secret: campus-errand-secret-key-2024-very-long-secure-key
# 微信小程序配置(可选)
wx:
miniapp:
appid: your_appid
secret: your_secret
📊 六、API接口总览
| 方法 | 接口 | 说明 | 认证 |
|---|---|---|---|
| POST | /api/user/wxLogin |
微信登录 | ❌ |
| GET | /api/user/info |
获取用户信息 | ✅ |
| POST | /api/user/applyRunner |
申请跑腿员 | ✅ |
| POST | /api/errand/publish |
发布跑腿订单 | ✅ |
| GET | /api/errand/list |
订单列表(首页) | ❌ |
| GET | /api/errand/detail/{id} |
订单详情 | ❌ |
| POST | /api/errand/accept/{id} |
接单 | ✅(跑腿员) |
| POST | /api/errand/cancel/{id} |
取消订单 | ✅ |
| POST | /api/errand/rate/{id} |
评价订单 | ✅ |
| GET | /api/runner/nearby |
附近跑腿员 | ❌ |
| GET | /api/runner/orders |
接单大厅 | ✅(跑腿员) |
🎯 七、核心业务流程
用户发布订单 → Redis实时推送 → 跑腿员抢单(分布式锁) → 接单 → 完成 → 评价
↓
[待接单] → [进行中] → [已完成] → [已评价]
↓
30分钟无人接单 → 自动取消 → 退款
💡 关键技术点
| 技术 | 用途 |
|---|---|
| ✅ Redis List | 实时订单队列 |
| ✅ 分布式锁 | 防止并发接单 |
| ✅ JWT | 用户认证 |
| ✅ MyBatis Plus | ORM操作 |
| ✅ 微信登录 | 小程序授权 |
| ✅ 地理位置计算 | 附近跑腿员 |
📱 UniApp前端代码需要吗?我可以继续提供小程序端完整代码! 🚀
更多推荐


所有评论(0)