Commit 548c125fc08f22c28c8ebb13f55de6f29cccbbb9
1 parent
5ca65d38
新增骑手到店上报功能,包含到店时间、经纬度及距离字段,完善订单处理逻辑以支持该功能
Showing
11 changed files
with
1273 additions
and
1118 deletions
src/main/java/com/diligrp/rider/controller/RiderOrderController.java
| ... | ... | @@ -23,6 +23,8 @@ public class RiderOrderController { |
| 23 | 23 | static class OrderCompleteReq { private Long orderId; private String thumbs; } |
| 24 | 24 | @Data |
| 25 | 25 | static class OrderStartReq { private Long orderId; private String code; } |
| 26 | + @Data | |
| 27 | + static class OrderArriveShopReq { private Long orderId; private String lng; private String lat; } | |
| 26 | 28 | |
| 27 | 29 | /** |
| 28 | 30 | * 订单列表 |
| ... | ... | @@ -70,6 +72,14 @@ public class RiderOrderController { |
| 70 | 72 | return Result.success(); |
| 71 | 73 | } |
| 72 | 74 | |
| 75 | + /** 到店上报 */ | |
| 76 | + @PostMapping("/arrive-shop") | |
| 77 | + public Result<Void> arriveShop(@RequestBody OrderArriveShopReq req, HttpServletRequest request) { | |
| 78 | + Long riderId = (Long) request.getAttribute("riderId"); | |
| 79 | + orderService.arriveShop(riderId, req.getOrderId(), req.getLng(), req.getLat()); | |
| 80 | + return Result.success(); | |
| 81 | + } | |
| 82 | + | |
| 73 | 83 | /** 完成订单,上传照片 */ |
| 74 | 84 | @PostMapping("/complete") |
| 75 | 85 | public Result<Void> complete(@RequestBody OrderCompleteReq req, HttpServletRequest request) { | ... | ... |
src/main/java/com/diligrp/rider/entity/Orders.java
| ... | ... | @@ -139,6 +139,18 @@ public class Orders { |
| 139 | 139 | /** 取件时间 */ |
| 140 | 140 | private Long pickTime; |
| 141 | 141 | |
| 142 | + /** 骑手到店时间 */ | |
| 143 | + private Long arriveShopTime; | |
| 144 | + | |
| 145 | + /** 骑手到店经度 */ | |
| 146 | + private String arriveShopLng; | |
| 147 | + | |
| 148 | + /** 骑手到店纬度 */ | |
| 149 | + private String arriveShopLat; | |
| 150 | + | |
| 151 | + /** 骑手到店距离门店距离,单位米 */ | |
| 152 | + private BigDecimal arriveShopDistance; | |
| 153 | + | |
| 142 | 154 | /** 完成时间 */ |
| 143 | 155 | private Long completeTime; |
| 144 | 156 | ... | ... |
src/main/java/com/diligrp/rider/service/RiderOrderService.java
| ... | ... | @@ -17,6 +17,8 @@ public interface RiderOrderService { |
| 17 | 17 | void grap(Long riderId, Long cityId, Long orderId); |
| 18 | 18 | /** 开始服务(取件),输入完成码 */ |
| 19 | 19 | void start(Long riderId, Long orderId, String code); |
| 20 | + /** 到店上报 */ | |
| 21 | + void arriveShop(Long riderId, Long orderId, String lng, String lat); | |
| 20 | 22 | /** 完成订单,上传照片 */ |
| 21 | 23 | void complete(Long riderId, Long orderId, String thumbsJson); |
| 22 | 24 | /** 骑手申请转单 */ | ... | ... |
src/main/java/com/diligrp/rider/service/impl/AdminRiderServiceImpl.java
| 1 | -package com.diligrp.rider.service.impl; | |
| 2 | - | |
| 3 | -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 5 | -import com.diligrp.rider.common.auth.AdminScopeGuard; | |
| 6 | -import com.diligrp.rider.common.exception.BizException; | |
| 7 | -import com.diligrp.rider.dto.AdminRiderAddDTO; | |
| 8 | -import com.diligrp.rider.entity.Orders; | |
| 9 | -import com.diligrp.rider.entity.Rider; | |
| 10 | -import com.diligrp.rider.entity.RiderLevel; | |
| 11 | -import com.diligrp.rider.mapper.OrdersMapper; | |
| 12 | -import com.diligrp.rider.mapper.RiderBalanceMapper; | |
| 13 | -import com.diligrp.rider.mapper.RiderLevelMapper; | |
| 14 | -import com.diligrp.rider.mapper.RiderMapper; | |
| 15 | -import com.diligrp.rider.entity.*; | |
| 16 | -import com.diligrp.rider.mapper.*; | |
| 17 | -import com.diligrp.rider.service.AdminRiderService; | |
| 18 | -import com.diligrp.rider.service.DeliveryOrderService; | |
| 19 | -import com.diligrp.rider.service.RiderHoldLimitService; | |
| 20 | -import com.diligrp.rider.service.WebhookService; | |
| 21 | -import com.diligrp.rider.vo.DeliveryOrderCreateVO; | |
| 22 | -import com.fasterxml.jackson.databind.ObjectMapper; | |
| 23 | -import lombok.RequiredArgsConstructor; | |
| 24 | -import lombok.extern.slf4j.Slf4j; | |
| 25 | -import org.springframework.stereotype.Service; | |
| 26 | -import org.springframework.transaction.annotation.Transactional; | |
| 27 | -import org.springframework.util.DigestUtils; | |
| 28 | - | |
| 29 | -import java.math.BigDecimal; | |
| 30 | -import java.nio.charset.StandardCharsets; | |
| 31 | -import java.util.HashMap; | |
| 32 | -import java.util.List; | |
| 33 | -import java.util.Map; | |
| 34 | -import java.util.Set; | |
| 35 | -import java.util.stream.Collectors; | |
| 36 | - | |
| 37 | -@Slf4j | |
| 38 | -@Service | |
| 39 | -@RequiredArgsConstructor | |
| 40 | -public class AdminRiderServiceImpl implements AdminRiderService { | |
| 41 | - | |
| 42 | - private final RiderMapper riderMapper; | |
| 43 | - private final RiderLevelMapper riderLevelMapper; | |
| 44 | - private final OrdersMapper ordersMapper; | |
| 45 | - private final RiderBalanceMapper balanceMapper; | |
| 46 | - private final AdminScopeGuard adminScopeGuard; | |
| 47 | - private final DeliveryOrderService deliveryOrderService; | |
| 48 | - private final RiderHoldLimitService riderHoldLimitService; | |
| 49 | - private final WebhookService webhookService; | |
| 50 | - private final ObjectMapper objectMapper; | |
| 51 | - | |
| 52 | - @Override | |
| 53 | - public void add(AdminRiderAddDTO dto, Long cityId) { | |
| 54 | - if (cityId == null || cityId < 1) { | |
| 55 | - throw new BizException("城市不能为空"); | |
| 56 | - } | |
| 57 | - Long exists = riderMapper.selectCount(new LambdaQueryWrapper<Rider>() | |
| 58 | - .eq(Rider::getMobile, dto.getMobile())); | |
| 59 | - if (exists > 0) { | |
| 60 | - throw new BizException("该手机号已存在"); | |
| 61 | - } | |
| 62 | - | |
| 63 | - Rider rider = new Rider(); | |
| 64 | - rider.setUserNickname(dto.getUserNickname()); | |
| 65 | - rider.setMobile(dto.getMobile()); | |
| 66 | - rider.setIdNo(dto.getIdNo()); | |
| 67 | - rider.setUserPass(encryptPass(dto.getPassword())); | |
| 68 | - rider.setCityId(cityId); | |
| 69 | - rider.setUserStatus(1); | |
| 70 | - rider.setStatus(1); | |
| 71 | - rider.setType(1); | |
| 72 | - rider.setIsRest(0); | |
| 73 | - rider.setBalance(BigDecimal.ZERO); | |
| 74 | - rider.setFrozenBalance(BigDecimal.ZERO); | |
| 75 | - rider.setUserLogin("phone_" + System.currentTimeMillis()); | |
| 76 | - rider.setCreateTime(System.currentTimeMillis() / 1000); | |
| 77 | - riderMapper.insert(rider); | |
| 78 | - } | |
| 79 | - | |
| 80 | - @Override | |
| 81 | - public List<Rider> list(String keyword, Integer userStatus, Long cityId) { | |
| 82 | - LambdaQueryWrapper<Rider> wrapper = new LambdaQueryWrapper<Rider>() | |
| 83 | - .orderByDesc(Rider::getId); | |
| 84 | - if (cityId != null) { | |
| 85 | - wrapper.eq(Rider::getCityId, cityId); | |
| 86 | - } | |
| 87 | - if (userStatus != null) { | |
| 88 | - wrapper.eq(Rider::getUserStatus, userStatus); | |
| 89 | - } | |
| 90 | - if (keyword != null && !keyword.isBlank()) { | |
| 91 | - wrapper.and(w -> w.like(Rider::getUserNickname, keyword) | |
| 92 | - .or() | |
| 93 | - .like(Rider::getMobile, keyword)); | |
| 94 | - } | |
| 95 | - List<Rider> riders = riderMapper.selectList(wrapper); | |
| 96 | - enrichLevelName(riders); | |
| 97 | - return riders; | |
| 98 | - } | |
| 99 | - | |
| 100 | - @Override | |
| 101 | - public List<Rider> designateCandidates(Long orderId, Long cityId) { | |
| 102 | - Orders order = ordersMapper.selectById(orderId); | |
| 103 | - if (order == null) throw new BizException("订单不存在"); | |
| 104 | - if (cityId != null && !cityId.equals(order.getCityId())) { | |
| 105 | - throw new BizException("只能查看当前租户订单的骑手"); | |
| 106 | - } | |
| 107 | - | |
| 108 | - LambdaQueryWrapper<Rider> wrapper = new LambdaQueryWrapper<Rider>() | |
| 109 | - .eq(Rider::getCityId, order.getCityId()) | |
| 110 | - .eq(Rider::getUserStatus, 1) | |
| 111 | - .eq(Rider::getStatus, 1) | |
| 112 | - .orderByAsc(Rider::getIsRest) | |
| 113 | - .orderByDesc(Rider::getId); | |
| 114 | - if (order.getIsTrans() != null && order.getIsTrans() == 1 | |
| 115 | - && order.getOldRiderId() != null && order.getOldRiderId() > 0) { | |
| 116 | - wrapper.ne(Rider::getId, order.getOldRiderId()); | |
| 117 | - } | |
| 118 | - | |
| 119 | - List<Rider> riders = riderMapper.selectList(wrapper); | |
| 120 | - enrichLevelName(riders); | |
| 121 | - return riders; | |
| 122 | - } | |
| 123 | - | |
| 124 | - @Override | |
| 125 | - public void setStatus(Long riderId, int status, Long cityId) { | |
| 126 | - Rider rider = riderMapper.selectById(riderId); | |
| 127 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 128 | - adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 129 | - riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 130 | - .eq(Rider::getId, riderId) | |
| 131 | - .set(Rider::getUserStatus, status)); | |
| 132 | - } | |
| 133 | - | |
| 134 | - @Override | |
| 135 | - public void setLevel(Long riderId, Long levelId, Long cityId) { | |
| 136 | - Rider rider = riderMapper.selectById(riderId); | |
| 137 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 138 | - if (cityId != null && !cityId.equals(rider.getCityId())) { | |
| 139 | - throw new BizException("只能操作当前城市骑手"); | |
| 140 | - } | |
| 141 | - if (levelId != null && levelId > 0) { | |
| 142 | - RiderLevel level = riderLevelMapper.selectById(levelId); | |
| 143 | - if (level == null) throw new BizException("等级不存在"); | |
| 144 | - if (!rider.getCityId().equals(level.getCityId())) { | |
| 145 | - throw new BizException("等级与骑手城市不匹配"); | |
| 146 | - } | |
| 147 | - } | |
| 148 | - riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 149 | - .eq(Rider::getId, riderId) | |
| 150 | - .set(Rider::getLevelId, levelId != null && levelId > 0 ? levelId : null)); | |
| 151 | - } | |
| 152 | - | |
| 153 | - @Override | |
| 154 | - public void setEnableStatus(Long riderId, int status, Long cityId) { | |
| 155 | - Rider rider = riderMapper.selectById(riderId); | |
| 156 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 157 | - adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 158 | - riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 159 | - .eq(Rider::getId, riderId) | |
| 160 | - .set(Rider::getStatus, status)); | |
| 161 | - } | |
| 162 | - | |
| 163 | - @Override | |
| 164 | - @Transactional | |
| 165 | - public void setType(Long riderId, int type, Long cityId) { | |
| 166 | - Rider rider = riderMapper.selectById(riderId); | |
| 167 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 168 | - adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 169 | - if (type == 2) { | |
| 170 | - if (rider.getBalance() != null && rider.getBalance().compareTo(BigDecimal.ZERO) > 0) { | |
| 171 | - throw new BizException("变更为全职前要保证余额为0"); | |
| 172 | - } | |
| 173 | - if (rider.getFrozenBalance() != null && rider.getFrozenBalance().compareTo(BigDecimal.ZERO) > 0) { | |
| 174 | - throw new BizException("变更为全职前要保证无提现冻结余额"); | |
| 175 | - } | |
| 176 | - } | |
| 177 | - LambdaUpdateWrapper<Rider> wrapper = new LambdaUpdateWrapper<Rider>() | |
| 178 | - .eq(Rider::getId, riderId) | |
| 179 | - .set(Rider::getType, type); | |
| 180 | - if (type == 1) { | |
| 181 | - wrapper.set(Rider::getBalance, BigDecimal.ZERO); | |
| 182 | - } | |
| 183 | - riderMapper.update(null, wrapper); | |
| 184 | - } | |
| 185 | - | |
| 186 | - @Override | |
| 187 | - public void setHoldOrderLimit(Long riderId, int holdOrderLimit, Long cityId) { | |
| 188 | - if (holdOrderLimit < 0) throw new BizException("个人持单上限不能小于0"); | |
| 189 | - Rider rider = riderMapper.selectById(riderId); | |
| 190 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 191 | - adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 192 | - riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 193 | - .eq(Rider::getId, riderId) | |
| 194 | - .set(Rider::getHoldOrderLimit, holdOrderLimit)); | |
| 195 | - } | |
| 196 | - | |
| 197 | - @Override | |
| 198 | - @Transactional | |
| 199 | - public void designate(Long orderId, Long riderId, Long cityId) { | |
| 200 | - Orders order = ordersMapper.selectById(orderId); | |
| 201 | - if (order == null) throw new BizException("订单不存在"); | |
| 202 | - adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 203 | - Rider rider = riderMapper.selectById(riderId); | |
| 204 | - if (rider == null) throw new BizException("骑手不存在"); | |
| 205 | - if (!order.getCityId().equals(rider.getCityId())) { | |
| 206 | - throw new BizException("骑手与订单城市不匹配"); | |
| 207 | - } | |
| 208 | - if (order.getStatus() == 1) throw new BizException("订单未支付,无法指派"); | |
| 209 | - if (order.getStatus() == 10) throw new BizException("订单已取消,无法指派"); | |
| 210 | - if (order.getStatus() != 2) throw new BizException("订单已服务中,无法指派"); | |
| 211 | - if (order.getIsTrans() == 1 && riderId.equals(order.getOldRiderId())) { | |
| 212 | - throw new BizException("此订单为该骑手转单订单,无法指派给该骑手"); | |
| 213 | - } | |
| 214 | - Integer holdOrderLimit = riderHoldLimitService.resolveHoldOrderLimit(rider); | |
| 215 | - riderHoldLimitService.assertWithinLimit(riderId, holdOrderLimit, "该骑手当前持单量已达个人上限,无法指派"); | |
| 216 | - | |
| 217 | - long now = System.currentTimeMillis() / 1000; | |
| 218 | - LambdaUpdateWrapper<Orders> wrapper = new LambdaUpdateWrapper<Orders>() | |
| 219 | - .eq(Orders::getId, orderId) | |
| 220 | - .eq(Orders::getStatus, 2) | |
| 221 | - .eq(Orders::getRiderId, 0) | |
| 222 | - .set(Orders::getRiderId, riderId) | |
| 223 | - .set(Orders::getStatus, 3) | |
| 224 | - .set(Orders::getGrapTime, now); | |
| 225 | - if (order.getOldRiderId() == null || order.getOldRiderId() == 0) { | |
| 226 | - wrapper.set(Orders::getOldRiderId, riderId); | |
| 227 | - } | |
| 228 | - int updated = ordersMapper.update(null, wrapper); | |
| 229 | - if (updated == 0) throw new BizException("指派失败,请重试"); | |
| 230 | - notifyOrderEvent(orderId, "order.dispatched"); | |
| 231 | - } | |
| 232 | - | |
| 233 | - @Override | |
| 234 | - @Transactional | |
| 235 | - public void setTrans(Long orderId, int trans, Long cityId) { | |
| 236 | - Orders order = ordersMapper.selectById(orderId); | |
| 237 | - if (order == null) throw new BizException("订单不存在"); | |
| 238 | - adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 239 | - if (order.getStatus() != 4) throw new BizException("订单状态错误,无法操作"); | |
| 240 | - if (order.getIsTrans() != 2) throw new BizException("订单未申请转单,无法操作"); | |
| 241 | - | |
| 242 | - LambdaUpdateWrapper<Orders> wrapper = new LambdaUpdateWrapper<Orders>() | |
| 243 | - .eq(Orders::getId, orderId) | |
| 244 | - .eq(Orders::getIsTrans, 2) | |
| 245 | - .set(Orders::getIsTrans, trans); | |
| 246 | - | |
| 247 | - if (trans == 1) { | |
| 248 | - wrapper.set(Orders::getStatus, 2) | |
| 249 | - .set(Orders::getGrapTime, 0L) | |
| 250 | - .set(Orders::getPickTime, 0L) | |
| 251 | - .set(Orders::getRiderId, 0L) | |
| 252 | - .set(Orders::getIsIncome, 0) | |
| 253 | - .set(Orders::getRiderIncome, BigDecimal.ZERO) | |
| 254 | - .set(Orders::getSubstationIncome, BigDecimal.ZERO); | |
| 255 | - } | |
| 256 | - int updated = ordersMapper.update(null, wrapper); | |
| 257 | - if (updated == 0) throw new BizException("操作失败,请重试"); | |
| 258 | - if (trans == 1) { | |
| 259 | - notifyOrderEvent(orderId, "order.transferred"); | |
| 260 | - } | |
| 261 | - } | |
| 262 | - | |
| 263 | - @Override | |
| 264 | - public DeliveryOrderCreateVO deliveryDetail(Long orderId, Long cityId) { | |
| 265 | - Orders order = ordersMapper.selectById(orderId); | |
| 266 | - if (order == null) throw new BizException("订单不存在"); | |
| 267 | - adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 268 | - return deliveryOrderService.getAdminDetail(order); | |
| 269 | - } | |
| 270 | - | |
| 271 | - @Override | |
| 272 | - public void cancelDeliveryOrder(Long orderId, Long cityId) { | |
| 273 | - Orders order = ordersMapper.selectById(orderId); | |
| 274 | - if (order == null) throw new BizException("订单不存在"); | |
| 275 | - adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 276 | - deliveryOrderService.cancelByAdmin(order); | |
| 277 | - } | |
| 278 | - | |
| 279 | - private void notifyOrderEvent(Long orderId, String event) { | |
| 280 | - try { | |
| 281 | - Orders order = ordersMapper.selectById(orderId); | |
| 282 | - if (order == null || order.getAppKey() == null || order.getAppKey().isBlank()) return; | |
| 283 | - Map<String, Object> payload = new HashMap<>(); | |
| 284 | - payload.put("event", event); | |
| 285 | - payload.put("outOrderNo", order.getOutOrderNo()); | |
| 286 | - payload.put("deliveryOrderId", order.getId()); | |
| 287 | - payload.put("orderNo", order.getOrderNo()); | |
| 288 | - payload.put("status", order.getStatus()); | |
| 289 | - payload.put("riderId", order.getRiderId()); | |
| 290 | - payload.put("timestamp", System.currentTimeMillis() / 1000); | |
| 291 | - webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); | |
| 292 | - } catch (Exception e) { | |
| 293 | - log.warn("后台订单事件通知失败 orderId={} event={}", orderId, event, e); | |
| 294 | - } | |
| 295 | - } | |
| 296 | - | |
| 297 | - private String encryptPass(String pass) { | |
| 298 | - return DigestUtils.md5DigestAsHex(pass.getBytes(StandardCharsets.UTF_8)); | |
| 299 | - } | |
| 300 | - | |
| 301 | - private void enrichLevelName(List<Rider> riders) { | |
| 302 | - if (riders == null || riders.isEmpty()) return; | |
| 303 | - | |
| 304 | - Set<Long> cityIds = riders.stream() | |
| 305 | - .map(Rider::getCityId) | |
| 306 | - .filter(id -> id != null && id > 0) | |
| 307 | - .collect(Collectors.toSet()); | |
| 308 | - if (cityIds.isEmpty()) return; | |
| 309 | - | |
| 310 | - List<RiderLevel> levels = riderLevelMapper.selectList(new LambdaQueryWrapper<RiderLevel>() | |
| 311 | - .in(RiderLevel::getCityId, cityIds)); | |
| 312 | - Map<Long, String> levelNameMap = new HashMap<>(); | |
| 313 | - Map<Long, String> defaultLevelNameMap = new HashMap<>(); | |
| 314 | - for (RiderLevel level : levels) { | |
| 315 | - levelNameMap.put(level.getId(), level.getName()); | |
| 316 | - if (level.getIsDefault() != null && level.getIsDefault() == 1) { | |
| 317 | - defaultLevelNameMap.put(level.getCityId(), level.getName()); | |
| 318 | - } | |
| 319 | - } | |
| 320 | - | |
| 321 | - for (Rider rider : riders) { | |
| 322 | - if (rider.getLevelId() != null && rider.getLevelId() > 0) { | |
| 323 | - rider.setLevelName(levelNameMap.getOrDefault(rider.getLevelId(), "未知等级")); | |
| 324 | - } else { | |
| 325 | - rider.setLevelName(defaultLevelNameMap.getOrDefault(rider.getCityId(), "默认等级")); | |
| 326 | - } | |
| 327 | - } | |
| 328 | - } | |
| 329 | -} | |
| 1 | +package com.diligrp.rider.service.impl; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 5 | +import com.diligrp.rider.common.auth.AdminScopeGuard; | |
| 6 | +import com.diligrp.rider.common.exception.BizException; | |
| 7 | +import com.diligrp.rider.dto.AdminRiderAddDTO; | |
| 8 | +import com.diligrp.rider.entity.Orders; | |
| 9 | +import com.diligrp.rider.entity.Rider; | |
| 10 | +import com.diligrp.rider.entity.RiderLevel; | |
| 11 | +import com.diligrp.rider.mapper.OrdersMapper; | |
| 12 | +import com.diligrp.rider.mapper.RiderBalanceMapper; | |
| 13 | +import com.diligrp.rider.mapper.RiderLevelMapper; | |
| 14 | +import com.diligrp.rider.mapper.RiderMapper; | |
| 15 | +import com.diligrp.rider.entity.*; | |
| 16 | +import com.diligrp.rider.mapper.*; | |
| 17 | +import com.diligrp.rider.service.AdminRiderService; | |
| 18 | +import com.diligrp.rider.service.DeliveryOrderService; | |
| 19 | +import com.diligrp.rider.service.RiderHoldLimitService; | |
| 20 | +import com.diligrp.rider.service.WebhookService; | |
| 21 | +import com.diligrp.rider.vo.DeliveryOrderCreateVO; | |
| 22 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
| 23 | +import lombok.RequiredArgsConstructor; | |
| 24 | +import lombok.extern.slf4j.Slf4j; | |
| 25 | +import org.springframework.stereotype.Service; | |
| 26 | +import org.springframework.transaction.annotation.Transactional; | |
| 27 | +import org.springframework.util.DigestUtils; | |
| 28 | + | |
| 29 | +import java.math.BigDecimal; | |
| 30 | +import java.nio.charset.StandardCharsets; | |
| 31 | +import java.util.HashMap; | |
| 32 | +import java.util.List; | |
| 33 | +import java.util.Map; | |
| 34 | +import java.util.Set; | |
| 35 | +import java.util.stream.Collectors; | |
| 36 | + | |
| 37 | +@Slf4j | |
| 38 | +@Service | |
| 39 | +@RequiredArgsConstructor | |
| 40 | +public class AdminRiderServiceImpl implements AdminRiderService { | |
| 41 | + | |
| 42 | + private final RiderMapper riderMapper; | |
| 43 | + private final RiderLevelMapper riderLevelMapper; | |
| 44 | + private final OrdersMapper ordersMapper; | |
| 45 | + private final RiderBalanceMapper balanceMapper; | |
| 46 | + private final AdminScopeGuard adminScopeGuard; | |
| 47 | + private final DeliveryOrderService deliveryOrderService; | |
| 48 | + private final RiderHoldLimitService riderHoldLimitService; | |
| 49 | + private final WebhookService webhookService; | |
| 50 | + private final ObjectMapper objectMapper; | |
| 51 | + | |
| 52 | + @Override | |
| 53 | + public void add(AdminRiderAddDTO dto, Long cityId) { | |
| 54 | + if (cityId == null || cityId < 1) { | |
| 55 | + throw new BizException("城市不能为空"); | |
| 56 | + } | |
| 57 | + Long exists = riderMapper.selectCount(new LambdaQueryWrapper<Rider>() | |
| 58 | + .eq(Rider::getMobile, dto.getMobile())); | |
| 59 | + if (exists > 0) { | |
| 60 | + throw new BizException("该手机号已存在"); | |
| 61 | + } | |
| 62 | + | |
| 63 | + Rider rider = new Rider(); | |
| 64 | + rider.setUserNickname(dto.getUserNickname()); | |
| 65 | + rider.setMobile(dto.getMobile()); | |
| 66 | + rider.setIdNo(dto.getIdNo()); | |
| 67 | + rider.setUserPass(encryptPass(dto.getPassword())); | |
| 68 | + rider.setCityId(cityId); | |
| 69 | + rider.setUserStatus(1); | |
| 70 | + rider.setStatus(1); | |
| 71 | + rider.setType(1); | |
| 72 | + rider.setIsRest(0); | |
| 73 | + rider.setBalance(BigDecimal.ZERO); | |
| 74 | + rider.setFrozenBalance(BigDecimal.ZERO); | |
| 75 | + rider.setUserLogin("phone_" + System.currentTimeMillis()); | |
| 76 | + rider.setCreateTime(System.currentTimeMillis() / 1000); | |
| 77 | + riderMapper.insert(rider); | |
| 78 | + } | |
| 79 | + | |
| 80 | + @Override | |
| 81 | + public List<Rider> list(String keyword, Integer userStatus, Long cityId) { | |
| 82 | + LambdaQueryWrapper<Rider> wrapper = new LambdaQueryWrapper<Rider>() | |
| 83 | + .orderByDesc(Rider::getId); | |
| 84 | + if (cityId != null) { | |
| 85 | + wrapper.eq(Rider::getCityId, cityId); | |
| 86 | + } | |
| 87 | + if (userStatus != null) { | |
| 88 | + wrapper.eq(Rider::getUserStatus, userStatus); | |
| 89 | + } | |
| 90 | + if (keyword != null && !keyword.isBlank()) { | |
| 91 | + wrapper.and(w -> w.like(Rider::getUserNickname, keyword) | |
| 92 | + .or() | |
| 93 | + .like(Rider::getMobile, keyword)); | |
| 94 | + } | |
| 95 | + List<Rider> riders = riderMapper.selectList(wrapper); | |
| 96 | + enrichLevelName(riders); | |
| 97 | + return riders; | |
| 98 | + } | |
| 99 | + | |
| 100 | + @Override | |
| 101 | + public List<Rider> designateCandidates(Long orderId, Long cityId) { | |
| 102 | + Orders order = ordersMapper.selectById(orderId); | |
| 103 | + if (order == null) throw new BizException("订单不存在"); | |
| 104 | + if (cityId != null && !cityId.equals(order.getCityId())) { | |
| 105 | + throw new BizException("只能查看当前租户订单的骑手"); | |
| 106 | + } | |
| 107 | + | |
| 108 | + LambdaQueryWrapper<Rider> wrapper = new LambdaQueryWrapper<Rider>() | |
| 109 | + .eq(Rider::getCityId, order.getCityId()) | |
| 110 | + .eq(Rider::getUserStatus, 1) | |
| 111 | + .eq(Rider::getStatus, 1) | |
| 112 | + .orderByAsc(Rider::getIsRest) | |
| 113 | + .orderByDesc(Rider::getId); | |
| 114 | + if (order.getIsTrans() != null && order.getIsTrans() == 1 | |
| 115 | + && order.getOldRiderId() != null && order.getOldRiderId() > 0) { | |
| 116 | + wrapper.ne(Rider::getId, order.getOldRiderId()); | |
| 117 | + } | |
| 118 | + | |
| 119 | + List<Rider> riders = riderMapper.selectList(wrapper); | |
| 120 | + enrichLevelName(riders); | |
| 121 | + return riders; | |
| 122 | + } | |
| 123 | + | |
| 124 | + @Override | |
| 125 | + public void setStatus(Long riderId, int status, Long cityId) { | |
| 126 | + Rider rider = riderMapper.selectById(riderId); | |
| 127 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 128 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 129 | + riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 130 | + .eq(Rider::getId, riderId) | |
| 131 | + .set(Rider::getUserStatus, status)); | |
| 132 | + } | |
| 133 | + | |
| 134 | + @Override | |
| 135 | + public void setLevel(Long riderId, Long levelId, Long cityId) { | |
| 136 | + Rider rider = riderMapper.selectById(riderId); | |
| 137 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 138 | + if (cityId != null && !cityId.equals(rider.getCityId())) { | |
| 139 | + throw new BizException("只能操作当前城市骑手"); | |
| 140 | + } | |
| 141 | + if (levelId != null && levelId > 0) { | |
| 142 | + RiderLevel level = riderLevelMapper.selectById(levelId); | |
| 143 | + if (level == null) throw new BizException("等级不存在"); | |
| 144 | + if (!rider.getCityId().equals(level.getCityId())) { | |
| 145 | + throw new BizException("等级与骑手城市不匹配"); | |
| 146 | + } | |
| 147 | + } | |
| 148 | + riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 149 | + .eq(Rider::getId, riderId) | |
| 150 | + .set(Rider::getLevelId, levelId != null && levelId > 0 ? levelId : null)); | |
| 151 | + } | |
| 152 | + | |
| 153 | + @Override | |
| 154 | + public void setEnableStatus(Long riderId, int status, Long cityId) { | |
| 155 | + Rider rider = riderMapper.selectById(riderId); | |
| 156 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 157 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 158 | + riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 159 | + .eq(Rider::getId, riderId) | |
| 160 | + .set(Rider::getStatus, status)); | |
| 161 | + } | |
| 162 | + | |
| 163 | + @Override | |
| 164 | + @Transactional | |
| 165 | + public void setType(Long riderId, int type, Long cityId) { | |
| 166 | + Rider rider = riderMapper.selectById(riderId); | |
| 167 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 168 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 169 | + if (type == 2) { | |
| 170 | + if (rider.getBalance() != null && rider.getBalance().compareTo(BigDecimal.ZERO) > 0) { | |
| 171 | + throw new BizException("变更为全职前要保证余额为0"); | |
| 172 | + } | |
| 173 | + if (rider.getFrozenBalance() != null && rider.getFrozenBalance().compareTo(BigDecimal.ZERO) > 0) { | |
| 174 | + throw new BizException("变更为全职前要保证无提现冻结余额"); | |
| 175 | + } | |
| 176 | + } | |
| 177 | + LambdaUpdateWrapper<Rider> wrapper = new LambdaUpdateWrapper<Rider>() | |
| 178 | + .eq(Rider::getId, riderId) | |
| 179 | + .set(Rider::getType, type); | |
| 180 | + if (type == 1) { | |
| 181 | + wrapper.set(Rider::getBalance, BigDecimal.ZERO); | |
| 182 | + } | |
| 183 | + riderMapper.update(null, wrapper); | |
| 184 | + } | |
| 185 | + | |
| 186 | + @Override | |
| 187 | + public void setHoldOrderLimit(Long riderId, int holdOrderLimit, Long cityId) { | |
| 188 | + if (holdOrderLimit < 0) throw new BizException("个人持单上限不能小于0"); | |
| 189 | + Rider rider = riderMapper.selectById(riderId); | |
| 190 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 191 | + adminScopeGuard.assertCityAccessible(cityId, rider.getCityId()); | |
| 192 | + riderMapper.update(null, new LambdaUpdateWrapper<Rider>() | |
| 193 | + .eq(Rider::getId, riderId) | |
| 194 | + .set(Rider::getHoldOrderLimit, holdOrderLimit)); | |
| 195 | + } | |
| 196 | + | |
| 197 | + @Override | |
| 198 | + @Transactional | |
| 199 | + public void designate(Long orderId, Long riderId, Long cityId) { | |
| 200 | + Orders order = ordersMapper.selectById(orderId); | |
| 201 | + if (order == null) throw new BizException("订单不存在"); | |
| 202 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 203 | + Rider rider = riderMapper.selectById(riderId); | |
| 204 | + if (rider == null) throw new BizException("骑手不存在"); | |
| 205 | + if (!order.getCityId().equals(rider.getCityId())) { | |
| 206 | + throw new BizException("骑手与订单城市不匹配"); | |
| 207 | + } | |
| 208 | + if (order.getStatus() == 1) throw new BizException("订单未支付,无法指派"); | |
| 209 | + if (order.getStatus() == 10) throw new BizException("订单已取消,无法指派"); | |
| 210 | + if (order.getStatus() != 2) throw new BizException("订单已服务中,无法指派"); | |
| 211 | + if (order.getIsTrans() == 1 && riderId.equals(order.getOldRiderId())) { | |
| 212 | + throw new BizException("此订单为该骑手转单订单,无法指派给该骑手"); | |
| 213 | + } | |
| 214 | + Integer holdOrderLimit = riderHoldLimitService.resolveHoldOrderLimit(rider); | |
| 215 | + riderHoldLimitService.assertWithinLimit(riderId, holdOrderLimit, "该骑手当前持单量已达个人上限,无法指派"); | |
| 216 | + | |
| 217 | + long now = System.currentTimeMillis() / 1000; | |
| 218 | + LambdaUpdateWrapper<Orders> wrapper = new LambdaUpdateWrapper<Orders>() | |
| 219 | + .eq(Orders::getId, orderId) | |
| 220 | + .eq(Orders::getStatus, 2) | |
| 221 | + .eq(Orders::getRiderId, 0) | |
| 222 | + .set(Orders::getRiderId, riderId) | |
| 223 | + .set(Orders::getStatus, 3) | |
| 224 | + .set(Orders::getGrapTime, now); | |
| 225 | + if (order.getOldRiderId() == null || order.getOldRiderId() == 0) { | |
| 226 | + wrapper.set(Orders::getOldRiderId, riderId); | |
| 227 | + } | |
| 228 | + int updated = ordersMapper.update(null, wrapper); | |
| 229 | + if (updated == 0) throw new BizException("指派失败,请重试"); | |
| 230 | + notifyOrderEvent(orderId, "order.dispatched"); | |
| 231 | + } | |
| 232 | + | |
| 233 | + @Override | |
| 234 | + @Transactional | |
| 235 | + public void setTrans(Long orderId, int trans, Long cityId) { | |
| 236 | + Orders order = ordersMapper.selectById(orderId); | |
| 237 | + if (order == null) throw new BizException("订单不存在"); | |
| 238 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 239 | + if (order.getStatus() != 4) throw new BizException("订单状态错误,无法操作"); | |
| 240 | + if (order.getIsTrans() != 2) throw new BizException("订单未申请转单,无法操作"); | |
| 241 | + | |
| 242 | + LambdaUpdateWrapper<Orders> wrapper = new LambdaUpdateWrapper<Orders>() | |
| 243 | + .eq(Orders::getId, orderId) | |
| 244 | + .eq(Orders::getIsTrans, 2) | |
| 245 | + .set(Orders::getIsTrans, trans); | |
| 246 | + | |
| 247 | + if (trans == 1) { | |
| 248 | + wrapper.set(Orders::getStatus, 2) | |
| 249 | + .set(Orders::getGrapTime, 0L) | |
| 250 | + .set(Orders::getPickTime, 0L) | |
| 251 | + .set(Orders::getArriveShopTime, 0L) | |
| 252 | + .set(Orders::getArriveShopLng, "") | |
| 253 | + .set(Orders::getArriveShopLat, "") | |
| 254 | + .set(Orders::getArriveShopDistance, BigDecimal.ZERO) | |
| 255 | + .set(Orders::getRiderId, 0L) | |
| 256 | + .set(Orders::getIsIncome, 0) | |
| 257 | + .set(Orders::getRiderIncome, BigDecimal.ZERO) | |
| 258 | + .set(Orders::getSubstationIncome, BigDecimal.ZERO); | |
| 259 | + } | |
| 260 | + int updated = ordersMapper.update(null, wrapper); | |
| 261 | + if (updated == 0) throw new BizException("操作失败,请重试"); | |
| 262 | + if (trans == 1) { | |
| 263 | + notifyOrderEvent(orderId, "order.transferred"); | |
| 264 | + } | |
| 265 | + } | |
| 266 | + | |
| 267 | + @Override | |
| 268 | + public DeliveryOrderCreateVO deliveryDetail(Long orderId, Long cityId) { | |
| 269 | + Orders order = ordersMapper.selectById(orderId); | |
| 270 | + if (order == null) throw new BizException("订单不存在"); | |
| 271 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 272 | + return deliveryOrderService.getAdminDetail(order); | |
| 273 | + } | |
| 274 | + | |
| 275 | + @Override | |
| 276 | + public void cancelDeliveryOrder(Long orderId, Long cityId) { | |
| 277 | + Orders order = ordersMapper.selectById(orderId); | |
| 278 | + if (order == null) throw new BizException("订单不存在"); | |
| 279 | + adminScopeGuard.assertCityAccessible(cityId, order.getCityId()); | |
| 280 | + deliveryOrderService.cancelByAdmin(order); | |
| 281 | + } | |
| 282 | + | |
| 283 | + private void notifyOrderEvent(Long orderId, String event) { | |
| 284 | + try { | |
| 285 | + Orders order = ordersMapper.selectById(orderId); | |
| 286 | + if (order == null || order.getAppKey() == null || order.getAppKey().isBlank()) return; | |
| 287 | + Map<String, Object> payload = new HashMap<>(); | |
| 288 | + payload.put("event", event); | |
| 289 | + payload.put("outOrderNo", order.getOutOrderNo()); | |
| 290 | + payload.put("deliveryOrderId", order.getId()); | |
| 291 | + payload.put("orderNo", order.getOrderNo()); | |
| 292 | + payload.put("status", order.getStatus()); | |
| 293 | + payload.put("riderId", order.getRiderId()); | |
| 294 | + payload.put("timestamp", System.currentTimeMillis() / 1000); | |
| 295 | + webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); | |
| 296 | + } catch (Exception e) { | |
| 297 | + log.warn("后台订单事件通知失败 orderId={} event={}", orderId, event, e); | |
| 298 | + } | |
| 299 | + } | |
| 300 | + | |
| 301 | + private String encryptPass(String pass) { | |
| 302 | + return DigestUtils.md5DigestAsHex(pass.getBytes(StandardCharsets.UTF_8)); | |
| 303 | + } | |
| 304 | + | |
| 305 | + private void enrichLevelName(List<Rider> riders) { | |
| 306 | + if (riders == null || riders.isEmpty()) return; | |
| 307 | + | |
| 308 | + Set<Long> cityIds = riders.stream() | |
| 309 | + .map(Rider::getCityId) | |
| 310 | + .filter(id -> id != null && id > 0) | |
| 311 | + .collect(Collectors.toSet()); | |
| 312 | + if (cityIds.isEmpty()) return; | |
| 313 | + | |
| 314 | + List<RiderLevel> levels = riderLevelMapper.selectList(new LambdaQueryWrapper<RiderLevel>() | |
| 315 | + .in(RiderLevel::getCityId, cityIds)); | |
| 316 | + Map<Long, String> levelNameMap = new HashMap<>(); | |
| 317 | + Map<Long, String> defaultLevelNameMap = new HashMap<>(); | |
| 318 | + for (RiderLevel level : levels) { | |
| 319 | + levelNameMap.put(level.getId(), level.getName()); | |
| 320 | + if (level.getIsDefault() != null && level.getIsDefault() == 1) { | |
| 321 | + defaultLevelNameMap.put(level.getCityId(), level.getName()); | |
| 322 | + } | |
| 323 | + } | |
| 324 | + | |
| 325 | + for (Rider rider : riders) { | |
| 326 | + if (rider.getLevelId() != null && rider.getLevelId() > 0) { | |
| 327 | + rider.setLevelName(levelNameMap.getOrDefault(rider.getLevelId(), "未知等级")); | |
| 328 | + } else { | |
| 329 | + rider.setLevelName(defaultLevelNameMap.getOrDefault(rider.getCityId(), "默认等级")); | |
| 330 | + } | |
| 331 | + } | |
| 332 | + } | |
| 333 | +} | ... | ... |
src/main/java/com/diligrp/rider/service/impl/DeliveryOrderServiceImpl.java
| ... | ... | @@ -387,6 +387,14 @@ public class DeliveryOrderServiceImpl implements DeliveryOrderService { |
| 387 | 387 | vo.setDistance(fee.getDistance()); |
| 388 | 388 | vo.setEstimatedMinutes(fee.getEstimatedMinutes()); |
| 389 | 389 | vo.setStatus(order.getStatus()); |
| 390 | + vo.setRiderId(order.getRiderId()); | |
| 391 | + vo.setGrapTime(order.getGrapTime()); | |
| 392 | + vo.setArriveShopTime(order.getArriveShopTime()); | |
| 393 | + vo.setArriveShopLng(order.getArriveShopLng()); | |
| 394 | + vo.setArriveShopLat(order.getArriveShopLat()); | |
| 395 | + vo.setArriveShopDistance(order.getArriveShopDistance()); | |
| 396 | + vo.setPickTime(order.getPickTime()); | |
| 397 | + vo.setCompleteTime(order.getCompleteTime()); | |
| 390 | 398 | return vo; |
| 391 | 399 | } |
| 392 | 400 | } | ... | ... |
src/main/java/com/diligrp/rider/service/impl/RiderOrderServiceImpl.java
| ... | ... | @@ -23,6 +23,7 @@ import org.springframework.stereotype.Service; |
| 23 | 23 | import org.springframework.transaction.annotation.Transactional; |
| 24 | 24 | |
| 25 | 25 | import java.math.BigDecimal; |
| 26 | +import java.math.RoundingMode; | |
| 26 | 27 | import java.time.LocalDate; |
| 27 | 28 | import java.time.ZoneId; |
| 28 | 29 | import java.time.format.DateTimeFormatter; |
| ... | ... | @@ -185,6 +186,9 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 185 | 186 | if (order == null) throw new BizException(1004, "订单信息错误"); |
| 186 | 187 | if (!riderId.equals(order.getRiderId())) throw new BizException(1005, "订单信息错误"); |
| 187 | 188 | if (order.getStatus() != 3) throw new BizException(1006, "订单状态错误"); |
| 189 | + if (order.getArriveShopTime() == null || order.getArriveShopTime() <= 0) { | |
| 190 | + throw new BizException(1007, "请先上报到店"); | |
| 191 | + } | |
| 188 | 192 | // @TODO 注释 |
| 189 | 193 | // if (!code.equals(order.getCode())) throw new BizException(1007, "完成码错误"); |
| 190 | 194 | |
| ... | ... | @@ -202,6 +206,39 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 202 | 206 | |
| 203 | 207 | @Override |
| 204 | 208 | @Transactional |
| 209 | + public void arriveShop(Long riderId, Long orderId, String lng, String lat) { | |
| 210 | + if (lng == null || lng.isBlank() || lat == null || lat.isBlank()) { | |
| 211 | + throw new BizException(1001, "请上传到店位置"); | |
| 212 | + } | |
| 213 | + | |
| 214 | + double arriveLng = parseCoordinate(lng, "经度格式错误"); | |
| 215 | + double arriveLat = parseCoordinate(lat, "纬度格式错误"); | |
| 216 | + Orders order = ordersMapper.selectById(orderId); | |
| 217 | + if (order == null) throw new BizException(1004, "订单信息错误"); | |
| 218 | + if (!riderId.equals(order.getRiderId())) throw new BizException(1005, "订单信息错误"); | |
| 219 | + if (order.getStatus() != 3) throw new BizException(1006, "订单状态错误"); | |
| 220 | + if (order.getArriveShopTime() != null && order.getArriveShopTime() > 0) { | |
| 221 | + throw new BizException(1007, "已上报到店,请勿重复操作"); | |
| 222 | + } | |
| 223 | + | |
| 224 | + BigDecimal distanceMeters = calcDistanceMeters(arriveLat, arriveLng, order.getFLat(), order.getFLng()); | |
| 225 | + long now = System.currentTimeMillis() / 1000; | |
| 226 | + int updated = ordersMapper.update(null, new LambdaUpdateWrapper<Orders>() | |
| 227 | + .eq(Orders::getId, orderId) | |
| 228 | + .eq(Orders::getRiderId, riderId) | |
| 229 | + .eq(Orders::getStatus, 3) | |
| 230 | + .eq(Orders::getArriveShopTime, 0L) | |
| 231 | + .set(Orders::getArriveShopTime, now) | |
| 232 | + .set(Orders::getArriveShopLng, lng) | |
| 233 | + .set(Orders::getArriveShopLat, lat) | |
| 234 | + .set(Orders::getArriveShopDistance, distanceMeters)); | |
| 235 | + if (updated == 0) throw new BizException(980, "操作失败"); | |
| 236 | + | |
| 237 | + notifyOrderEvent(orderId, "order.arrived_shop"); | |
| 238 | + } | |
| 239 | + | |
| 240 | + @Override | |
| 241 | + @Transactional | |
| 205 | 242 | public void complete(Long riderId, Long orderId, String thumbsJson) { |
| 206 | 243 | if (thumbsJson == null || thumbsJson.isBlank()) throw new BizException(1001, "请上传照片"); |
| 207 | 244 | |
| ... | ... | @@ -255,6 +292,12 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 255 | 292 | payload.put("orderNo", order.getOrderNo()); |
| 256 | 293 | payload.put("status", order.getStatus()); |
| 257 | 294 | payload.put("riderId", order.getRiderId()); |
| 295 | + payload.put("arriveShopTime", order.getArriveShopTime()); | |
| 296 | + payload.put("arriveShopLng", order.getArriveShopLng()); | |
| 297 | + payload.put("arriveShopLat", order.getArriveShopLat()); | |
| 298 | + payload.put("arriveShopDistance", order.getArriveShopDistance()); | |
| 299 | + payload.put("pickTime", order.getPickTime()); | |
| 300 | + payload.put("completeTime", order.getCompleteTime()); | |
| 258 | 301 | payload.put("timestamp", System.currentTimeMillis() / 1000); |
| 259 | 302 | webhookService.sendOrderEvent(order, event, objectMapper.writeValueAsString(payload)); |
| 260 | 303 | } catch (Exception e) { |
| ... | ... | @@ -324,6 +367,38 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 324 | 367 | return 0; |
| 325 | 368 | } |
| 326 | 369 | |
| 370 | + private double parseCoordinate(String value, String message) { | |
| 371 | + try { | |
| 372 | + return Double.parseDouble(value); | |
| 373 | + } catch (Exception e) { | |
| 374 | + throw new BizException(1002, message); | |
| 375 | + } | |
| 376 | + } | |
| 377 | + | |
| 378 | + private BigDecimal calcDistanceMeters(double arriveLat, double arriveLng, String shopLat, String shopLng) { | |
| 379 | + try { | |
| 380 | + if (shopLat == null || shopLat.isBlank() || shopLng == null || shopLng.isBlank()) { | |
| 381 | + return BigDecimal.ZERO; | |
| 382 | + } | |
| 383 | + double targetLat = Double.parseDouble(shopLat); | |
| 384 | + double targetLng = Double.parseDouble(shopLng); | |
| 385 | + double distance = haversineMeters(arriveLat, arriveLng, targetLat, targetLng); | |
| 386 | + return BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP); | |
| 387 | + } catch (Exception e) { | |
| 388 | + return BigDecimal.ZERO; | |
| 389 | + } | |
| 390 | + } | |
| 391 | + | |
| 392 | + private double haversineMeters(double lat1, double lng1, double lat2, double lng2) { | |
| 393 | + double earthRadiusMeters = 6371000.0; | |
| 394 | + double dLat = Math.toRadians(lat2 - lat1); | |
| 395 | + double dLng = Math.toRadians(lng2 - lng1); | |
| 396 | + double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) | |
| 397 | + + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) | |
| 398 | + * Math.sin(dLng / 2) * Math.sin(dLng / 2); | |
| 399 | + return earthRadiusMeters * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
| 400 | + } | |
| 401 | + | |
| 327 | 402 | private OrderVO toVO(Orders o, Long riderId) { |
| 328 | 403 | OrderVO vo = new OrderVO(); |
| 329 | 404 | vo.setId(o.getId()); |
| ... | ... | @@ -345,6 +420,10 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 345 | 420 | vo.setAddTime(o.getAddTime() != null ? formatTime(o.getAddTime()) : null); |
| 346 | 421 | vo.setGrapTime(o.getGrapTime() != null && o.getGrapTime() > 0 ? formatTime(o.getGrapTime()) : null); |
| 347 | 422 | vo.setPickTime(o.getPickTime() != null && o.getPickTime() > 0 ? formatTime(o.getPickTime()) : null); |
| 423 | + vo.setArriveShopTime(o.getArriveShopTime() != null && o.getArriveShopTime() > 0 ? formatTime(o.getArriveShopTime()) : null); | |
| 424 | + vo.setArriveShopLng(o.getArriveShopLng()); | |
| 425 | + vo.setArriveShopLat(o.getArriveShopLat()); | |
| 426 | + vo.setArriveShopDistance(o.getArriveShopDistance()); | |
| 348 | 427 | vo.setCompleteTime(o.getCompleteTime() != null && o.getCompleteTime() > 0 ? formatTime(o.getCompleteTime()) : null); |
| 349 | 428 | vo.setTransTime(o.getTransTime() != null && o.getTransTime() > 0 ? formatTime(o.getTransTime()) : null); |
| 350 | 429 | // 收入 |
| ... | ... | @@ -422,6 +501,10 @@ public class RiderOrderServiceImpl implements RiderOrderService { |
| 422 | 501 | .set(Orders::getStatus, 2) // 回到待接单 |
| 423 | 502 | .set(Orders::getRiderId, 0L) // 清空骑手 |
| 424 | 503 | .set(Orders::getGrapTime, 0L) |
| 504 | + .set(Orders::getArriveShopTime, 0L) | |
| 505 | + .set(Orders::getArriveShopLng, "") | |
| 506 | + .set(Orders::getArriveShopLat, "") | |
| 507 | + .set(Orders::getArriveShopDistance, BigDecimal.ZERO) | |
| 425 | 508 | .set(Orders::getIsIncome, 0) |
| 426 | 509 | .set(Orders::getRiderIncome, BigDecimal.ZERO) |
| 427 | 510 | .set(Orders::getSubstationIncome, BigDecimal.ZERO) | ... | ... |
src/main/java/com/diligrp/rider/vo/DeliveryOrderCreateVO.java
| ... | ... | @@ -29,4 +29,20 @@ public class DeliveryOrderCreateVO { |
| 29 | 29 | private Integer estimatedMinutes; |
| 30 | 30 | /** 订单状态:2=待接单 */ |
| 31 | 31 | private Integer status; |
| 32 | + /** 骑手ID */ | |
| 33 | + private Long riderId; | |
| 34 | + /** 接单时间 */ | |
| 35 | + private Long grapTime; | |
| 36 | + /** 骑手到店时间 */ | |
| 37 | + private Long arriveShopTime; | |
| 38 | + /** 骑手到店经度 */ | |
| 39 | + private String arriveShopLng; | |
| 40 | + /** 骑手到店纬度 */ | |
| 41 | + private String arriveShopLat; | |
| 42 | + /** 骑手到店距离门店距离,单位米 */ | |
| 43 | + private BigDecimal arriveShopDistance; | |
| 44 | + /** 取件时间 */ | |
| 45 | + private Long pickTime; | |
| 46 | + /** 完成时间 */ | |
| 47 | + private Long completeTime; | |
| 32 | 48 | } | ... | ... |
src/main/java/com/diligrp/rider/vo/OrderVO.java
| ... | ... | @@ -38,6 +38,10 @@ public class OrderVO { |
| 38 | 38 | private String addTime; |
| 39 | 39 | private String grapTime; |
| 40 | 40 | private String pickTime; |
| 41 | + private String arriveShopTime; | |
| 42 | + private String arriveShopLng; | |
| 43 | + private String arriveShopLat; | |
| 44 | + private BigDecimal arriveShopDistance; | |
| 41 | 45 | private String completeTime; |
| 42 | 46 | private String transTime; |
| 43 | 47 | private Object extra; | ... | ... |
src/main/resources/20260513-arrive-shop.sql
0 → 100644
| 1 | +-- 骑手到店节点字段 | |
| 2 | +ALTER TABLE `orders` | |
| 3 | + ADD COLUMN `arrive_shop_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手到店时间' AFTER `pick_time`, | |
| 4 | + ADD COLUMN `arrive_shop_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '骑手到店经度' AFTER `arrive_shop_time`, | |
| 5 | + ADD COLUMN `arrive_shop_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '骑手到店纬度' AFTER `arrive_shop_lng`, | |
| 6 | + ADD COLUMN `arrive_shop_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '骑手到店距离门店距离,单位米' AFTER `arrive_shop_lat`; | |
| 7 | + | |
| 8 | +-- 示例开放平台应用订阅新增到店事件。 | |
| 9 | +-- 如生产环境已有 open_app 数据,请按实际 app_key 更新 webhook_events。 | |
| 10 | +UPDATE `open_app` | |
| 11 | +SET `webhook_events` = '["order.created","order.accepted","order.arrived_shop","order.picking","order.completed","order.cancelled"]' | |
| 12 | +WHERE `app_key` = 'TESTAPPKEY00001'; | ... | ... |
src/main/resources/data-init.sql
| 1 | --- 外卖配送中台 初始化数据脚本 | |
| 2 | --- 执行前请确保已执行 schema.sql 建表 | |
| 3 | --- 执行方式:mysql -u root -p dili_rider < data-init.sql | |
| 4 | - | |
| 5 | -USE dili_rider; | |
| 6 | - | |
| 7 | --- ============================================================ | |
| 8 | --- 1. 城市数据(省+市两级) | |
| 9 | --- ============================================================ | |
| 10 | -INSERT INTO `city` (`id`, `pid`, `name`, `area_code`, `status`, `rate`, `list_order`, `config`) VALUES | |
| 11 | -(1, 0, '广东省', '44000000', 0, 0.00, 1, NULL), | |
| 12 | -(2, 1, '广州市', '44010000', 1, 10.00, 1, | |
| 13 | -'{ | |
| 14 | - "type": [6], | |
| 15 | - "type6": { | |
| 16 | - "feeMode": 2, | |
| 17 | - "fixMoney": 0, | |
| 18 | - "distanceSwitch": 1, | |
| 19 | - "distanceBasic": 3, | |
| 20 | - "distanceBasicMoney": 4.00, | |
| 21 | - "distanceMoreMoney": 1.50, | |
| 22 | - "distanceMode": 1, | |
| 23 | - "distanceType": 1, | |
| 24 | - "weightSwitch": 0, | |
| 25 | - "weightBasic": 0, | |
| 26 | - "weightBasicMoney": 0, | |
| 27 | - "weightMoreMoney": 0, | |
| 28 | - "weightType": 1, | |
| 29 | - "times": [ | |
| 30 | - {"start": 0, "end": 480, "isOpen": 0, "money": 0}, | |
| 31 | - {"start": 480, "end": 1320, "isOpen": 1, "money": 0}, | |
| 32 | - {"start": 1320, "end": 1440, "isOpen": 1, "money": 2} | |
| 33 | - ] | |
| 34 | - }, | |
| 35 | - "distanceBasic": 3, | |
| 36 | - "distanceBasicTime": 30, | |
| 37 | - "distanceMoreTime": 10 | |
| 38 | -}'), | |
| 39 | -(3, 1, '深圳市', '44030000', 1, 10.00, 2, | |
| 40 | -'{ | |
| 41 | - "type": [6], | |
| 42 | - "type6": { | |
| 43 | - "feeMode": 1, | |
| 44 | - "fixMoney": 5.00, | |
| 45 | - "distanceSwitch": 0, | |
| 46 | - "distanceBasic": 0, | |
| 47 | - "distanceBasicMoney": 0, | |
| 48 | - "distanceMoreMoney": 0, | |
| 49 | - "distanceMode": 1, | |
| 50 | - "distanceType": 1, | |
| 51 | - "weightSwitch": 0, | |
| 52 | - "weightBasic": 0, | |
| 53 | - "weightBasicMoney": 0, | |
| 54 | - "weightMoreMoney": 0, | |
| 55 | - "weightType": 1, | |
| 56 | - "times": [] | |
| 57 | - }, | |
| 58 | - "distanceBasic": 3, | |
| 59 | - "distanceBasicTime": 25, | |
| 60 | - "distanceMoreTime": 8 | |
| 61 | -}'); | |
| 62 | - | |
| 63 | --- ============================================================ | |
| 64 | --- 2. 分站管理员(每个已开通城市一个) | |
| 65 | --- 默认密码均为 admin123(MD5: 0192023a7bbd73250516f069df18b500) | |
| 66 | --- ============================================================ | |
| 67 | -INSERT INTO `sys_role` (`code`, `name`, `role_scope`, `status`, `create_time`) VALUES | |
| 68 | -('platform_admin', '平台管理员', 'PLATFORM', 1, UNIX_TIMESTAMP()), | |
| 69 | -('substation_admin', '分站管理员', 'SUBSTATION', 1, UNIX_TIMESTAMP()); | |
| 70 | - | |
| 71 | -INSERT INTO `sys_menu` (`code`, `name`, `type`, `path`, `icon`, `parent_id`, `menu_scope`, `list_order`, `visible`, `status`, `create_time`) VALUES | |
| 72 | -('dashboard', '工作台', 'MENU', '/dashboard', 'HomeOutlined', 0, 'BOTH', 10, 1, 1, UNIX_TIMESTAMP()), | |
| 73 | -('city.manage', '租户管理', 'MENU', '/city', 'GlobalOutlined', 0, 'PLATFORM', 20, 1, 1, UNIX_TIMESTAMP()), | |
| 74 | -('substation.manage', '分站管理', 'MENU', '/substation', 'ApartmentOutlined', 0, 'PLATFORM', 30, 1, 1, UNIX_TIMESTAMP()), | |
| 75 | -('merchant.root', '商家管理', 'DIR', '', 'ShopOutlined', 0, 'BOTH', 40, 1, 1, UNIX_TIMESTAMP()), | |
| 76 | -('merchant.enter', '入驻申请', 'MENU', '/merchant/enter', '', 4, 'BOTH', 41, 1, 1, UNIX_TIMESTAMP()), | |
| 77 | -('merchant.store', '店铺管理', 'MENU', '/merchant/store', '', 4, 'BOTH', 42, 1, 1, UNIX_TIMESTAMP()), | |
| 78 | -('rider.list', '骑手管理', 'MENU', '/rider', 'UserOutlined', 0, 'BOTH', 50, 1, 1, UNIX_TIMESTAMP()), | |
| 79 | -('rider.evaluate', '骑手评价', 'MENU', '/rider/evaluate', 'StarOutlined', 0, 'BOTH', 60, 1, 1, UNIX_TIMESTAMP()), | |
| 80 | -('order.root', '订单管理', 'DIR', '', 'UnorderedListOutlined', 0, 'BOTH', 70, 1, 1, UNIX_TIMESTAMP()), | |
| 81 | -('order.list', '订单列表', 'MENU', '/order', '', 9, 'BOTH', 71, 1, 1, UNIX_TIMESTAMP()), | |
| 82 | -('order.refund', '退款管理', 'MENU', '/refund', '', 9, 'BOTH', 72, 1, 1, UNIX_TIMESTAMP()), | |
| 83 | -('order.delivery', '配送订单', 'MENU', '/delivery/order', '', 9, 'BOTH', 73, 1, 1, UNIX_TIMESTAMP()), | |
| 84 | -('config.root', '配置中心', 'DIR', '', 'ControlOutlined', 0, 'BOTH', 80, 1, 1, UNIX_TIMESTAMP()), | |
| 85 | -('fee.plan', '配送费配置', 'MENU', '/config/fee-plan', '', 13, 'BOTH', 81, 1, 1, UNIX_TIMESTAMP()), | |
| 86 | -('dispatch.rule', '调度配置', 'MENU', '/dispatch/rule', '', 13, 'BOTH', 82, 1, 1, UNIX_TIMESTAMP()), | |
| 87 | -('open.root', '开放平台', 'DIR', '', 'ApiOutlined', 0, 'PLATFORM', 90, 1, 1, UNIX_TIMESTAMP()), | |
| 88 | -('open.app', '应用管理', 'MENU', '/open', '', 16, 'PLATFORM', 91, 1, 1, UNIX_TIMESTAMP()), | |
| 89 | -('open.mock_delivery', '模拟推单', 'MENU', '/open/mock-delivery', '', 16, 'PLATFORM', 92, 1, 1, UNIX_TIMESTAMP()), | |
| 90 | -('system.root', '系统管理', 'DIR', '', 'ControlOutlined', 0, 'PLATFORM', 100, 1, 1, UNIX_TIMESTAMP()), | |
| 91 | -('system.menu', '菜单管理', 'MENU', '/system/menu', '', 19, 'PLATFORM', 101, 1, 1, UNIX_TIMESTAMP()), | |
| 92 | -('system.role_menu', '角色菜单', 'MENU', '/system/role-menu', '', 19, 'PLATFORM', 102, 1, 1, UNIX_TIMESTAMP()), | |
| 93 | -('admin.user', '平台账号', 'MENU', '/admin-user', '', 19, 'PLATFORM', 103, 1, 1, UNIX_TIMESTAMP()), | |
| 94 | -('rider.withdraw', '骑手提现审核', 'MENU', '/rider/withdraw', 'WalletOutlined', 0, 'BOTH', 56, 1, 1, UNIX_TIMESTAMP()); | |
| 95 | - | |
| 96 | -INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 97 | -SELECT 1, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('PLATFORM', 'BOTH'); | |
| 98 | - | |
| 99 | -INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 100 | -SELECT 2, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('SUBSTATION', 'BOTH'); | |
| 101 | - | |
| 102 | -INSERT INTO `substation` (`city_id`, `user_login`, `user_nickname`, `user_pass`, `mobile`, `user_status`, `role_id`, `create_time`) VALUES | |
| 103 | -(2, 'gz_admin', '广州分站管理员', '0192023a7bbd73250516f069df18b500', '13800000001', 1, 2, UNIX_TIMESTAMP()), | |
| 104 | -(3, 'sz_admin', '深圳分站管理员', '0192023a7bbd73250516f069df18b500', '13800000002', 1, 2, UNIX_TIMESTAMP()); | |
| 105 | - | |
| 106 | --- ============================================================ | |
| 107 | --- 3. 骑手等级配置(广州) | |
| 108 | --- ============================================================ | |
| 109 | -INSERT INTO `rider_level` (`city_id`, `level_id`, `name`, `is_default`, `trans_nums`, | |
| 110 | - `run_fee_mode`, `run_fix_money`, `run_rate`, `distance_basic`, `distance_basic_money`, | |
| 111 | - `distance_more_money`, `distance_max_money`, `work_fee_mode`, `work_fix_money`, `work_rate`) VALUES | |
| 112 | --- 普通骑手:按比例拿配送费的70% | |
| 113 | -(2, 1, '普通骑手', 1, 3, | |
| 114 | - 2, 0.00, 70.00, 0, 0.00, 0.00, 0.00, | |
| 115 | - 1, 5.00, 0.00), | |
| 116 | --- 资深骑手:按比例拿配送费的80% | |
| 117 | -(2, 2, '资深骑手', 0, 5, | |
| 118 | - 2, 0.00, 80.00, 0, 0.00, 0.00, 0.00, | |
| 119 | - 1, 6.00, 0.00); | |
| 120 | - | |
| 121 | --- 深圳骑手等级 | |
| 122 | -INSERT INTO `rider_level` (`city_id`, `level_id`, `name`, `is_default`, `trans_nums`, | |
| 123 | - `run_fee_mode`, `run_fix_money`, `run_rate`, `distance_basic`, `distance_basic_money`, | |
| 124 | - `distance_more_money`, `distance_max_money`, `work_fee_mode`, `work_fix_money`, `work_rate`) VALUES | |
| 125 | -(3, 1, '普通骑手', 1, 3, | |
| 126 | - 2, 0.00, 70.00, 0, 0.00, 0.00, 0.00, | |
| 127 | - 1, 5.00, 0.00); | |
| 128 | - | |
| 129 | --- ============================================================ | |
| 130 | --- 4. 示例骑手账号 | |
| 131 | --- 默认密码均为 test1234(MD5: 16d7a4fca7442dda3ad93c9a726597e4) | |
| 132 | --- ============================================================ | |
| 133 | -INSERT INTO `rider` (`mobile`, `user_login`, `user_nickname`, `user_pass`, `city_id`, `level_id`, | |
| 134 | - `type`, `user_status`, `balance`, `is_rest`, `create_time`) VALUES | |
| 135 | -('13900000001', 'phone_rider001', '张骑手', '16d7a4fca7442dda3ad93c9a726597e4', 2, 1, 1, 1, 0.00, 0, UNIX_TIMESTAMP()), | |
| 136 | -('13900000002', 'phone_rider002', '李骑手', '16d7a4fca7442dda3ad93c9a726597e4', 2, 1, 2, 1, 0.00, 0, UNIX_TIMESTAMP()); | |
| 137 | - | |
| 138 | --- ============================================================ | |
| 139 | --- 5. 示例商家店铺 | |
| 140 | --- ============================================================ | |
| 141 | -INSERT INTO `merchant_store` (`name`, `thumb`, `city_id`, `address`, `lng`, `lat`, | |
| 142 | - `operating_state`, `automatic_order`, `shipping_type`, `free_shipping`, `up_to_send`, | |
| 143 | - `open_date`, `open_time`, `about`, `list_order`, `is_del`, `add_time`) VALUES | |
| 144 | -('测试餐厅', '', 2, '广州市天河区测试路1号', '113.330010', '23.132891', | |
| 145 | - 1, 1, 1, 30.00, 15.00, | |
| 146 | - '[1,2,3,4,5,6,7]', '["09:00","22:00"]', '测试店铺,仅供开发调试', 1, 0, UNIX_TIMESTAMP()); | |
| 147 | - | |
| 148 | --- 创建商家账号 | |
| 149 | -INSERT INTO `merchant_users` (`store_id`, `mobile`, `user_nickname`, `user_status`, `type`, `create_time`) | |
| 150 | -VALUES (LAST_INSERT_ID(), '13700000001', '测试餐厅老板', 1, 1, UNIX_TIMESTAMP()); | |
| 151 | - | |
| 152 | --- ============================================================ | |
| 153 | --- 6. 开放平台示例应用 | |
| 154 | --- ============================================================ | |
| 155 | -INSERT INTO `open_app` (`app_name`, `app_key`, `app_secret`, `store_id`, `status`, | |
| 156 | - `webhook_url`, `webhook_events`, `remark`, `create_time`) VALUES | |
| 157 | -('内部电商系统', 'TESTAPPKEY00001', 'testsecret0000000000000000000000000000000000000000000000000001', 0, 1, | |
| 158 | - '', '["order.paid","order.completed","order.cancelled"]', '用于测试的内部应用', UNIX_TIMESTAMP()); | |
| 159 | - | |
| 160 | --- ============================================================ | |
| 161 | --- 完成提示 | |
| 162 | --- ============================================================ | |
| 163 | -SELECT '初始化完成!' AS 提示; | |
| 164 | -SELECT '骑手登录账号: 13900000001 / 13900000002,密码: test1234' AS 骑手账号; | |
| 165 | -SELECT '分站管理员: gz_admin / sz_admin,密码: admin123' AS 分站账号; | |
| 166 | -SELECT '商家手机号: 13700000001' AS 商家账号; | |
| 167 | -SELECT '开放平台 AppKey: TESTAPPKEY00001' AS 开放平台; | |
| 168 | - | |
| 169 | --- ============================================================ | |
| 170 | --- 7. 超级管理员账号 | |
| 171 | --- 默认密码:admin123(MD5: 0192023a7bbd73250516f069df18b500) | |
| 172 | --- ============================================================ | |
| 173 | -INSERT INTO `admin_user` (`user_login`, `user_pass`, `user_nickname`, `user_status`, `role_id`, `create_time`) VALUES | |
| 174 | -('admin', '0192023a7bbd73250516f069df18b500', '超级管理员', 1, 1, UNIX_TIMESTAMP()); | |
| 175 | - | |
| 176 | -SELECT '超级管理员: admin / admin123(role=admin)' AS 超管账号; | |
| 177 | -SELECT '系统管理菜单: /system/menu /system/role-menu /admin-user' AS 系统管理; | |
| 1 | +-- 外卖配送中台 初始化数据脚本 | |
| 2 | +-- 执行前请确保已执行 schema.sql 建表 | |
| 3 | +-- 执行方式:mysql -u root -p dili_rider < data-init.sql | |
| 4 | + | |
| 5 | +USE dili_rider; | |
| 6 | + | |
| 7 | +-- ============================================================ | |
| 8 | +-- 1. 城市数据(省+市两级) | |
| 9 | +-- ============================================================ | |
| 10 | +INSERT INTO `city` (`id`, `pid`, `name`, `area_code`, `status`, `rate`, `list_order`, `config`) VALUES | |
| 11 | +(1, 0, '广东省', '44000000', 0, 0.00, 1, NULL), | |
| 12 | +(2, 1, '广州市', '44010000', 1, 10.00, 1, | |
| 13 | +'{ | |
| 14 | + "type": [6], | |
| 15 | + "type6": { | |
| 16 | + "feeMode": 2, | |
| 17 | + "fixMoney": 0, | |
| 18 | + "distanceSwitch": 1, | |
| 19 | + "distanceBasic": 3, | |
| 20 | + "distanceBasicMoney": 4.00, | |
| 21 | + "distanceMoreMoney": 1.50, | |
| 22 | + "distanceMode": 1, | |
| 23 | + "distanceType": 1, | |
| 24 | + "weightSwitch": 0, | |
| 25 | + "weightBasic": 0, | |
| 26 | + "weightBasicMoney": 0, | |
| 27 | + "weightMoreMoney": 0, | |
| 28 | + "weightType": 1, | |
| 29 | + "times": [ | |
| 30 | + {"start": 0, "end": 480, "isOpen": 0, "money": 0}, | |
| 31 | + {"start": 480, "end": 1320, "isOpen": 1, "money": 0}, | |
| 32 | + {"start": 1320, "end": 1440, "isOpen": 1, "money": 2} | |
| 33 | + ] | |
| 34 | + }, | |
| 35 | + "distanceBasic": 3, | |
| 36 | + "distanceBasicTime": 30, | |
| 37 | + "distanceMoreTime": 10 | |
| 38 | +}'), | |
| 39 | +(3, 1, '深圳市', '44030000', 1, 10.00, 2, | |
| 40 | +'{ | |
| 41 | + "type": [6], | |
| 42 | + "type6": { | |
| 43 | + "feeMode": 1, | |
| 44 | + "fixMoney": 5.00, | |
| 45 | + "distanceSwitch": 0, | |
| 46 | + "distanceBasic": 0, | |
| 47 | + "distanceBasicMoney": 0, | |
| 48 | + "distanceMoreMoney": 0, | |
| 49 | + "distanceMode": 1, | |
| 50 | + "distanceType": 1, | |
| 51 | + "weightSwitch": 0, | |
| 52 | + "weightBasic": 0, | |
| 53 | + "weightBasicMoney": 0, | |
| 54 | + "weightMoreMoney": 0, | |
| 55 | + "weightType": 1, | |
| 56 | + "times": [] | |
| 57 | + }, | |
| 58 | + "distanceBasic": 3, | |
| 59 | + "distanceBasicTime": 25, | |
| 60 | + "distanceMoreTime": 8 | |
| 61 | +}'); | |
| 62 | + | |
| 63 | +-- ============================================================ | |
| 64 | +-- 2. 分站管理员(每个已开通城市一个) | |
| 65 | +-- 默认密码均为 admin123(MD5: 0192023a7bbd73250516f069df18b500) | |
| 66 | +-- ============================================================ | |
| 67 | +INSERT INTO `sys_role` (`code`, `name`, `role_scope`, `status`, `create_time`) VALUES | |
| 68 | +('platform_admin', '平台管理员', 'PLATFORM', 1, UNIX_TIMESTAMP()), | |
| 69 | +('substation_admin', '分站管理员', 'SUBSTATION', 1, UNIX_TIMESTAMP()); | |
| 70 | + | |
| 71 | +INSERT INTO `sys_menu` (`code`, `name`, `type`, `path`, `icon`, `parent_id`, `menu_scope`, `list_order`, `visible`, `status`, `create_time`) VALUES | |
| 72 | +('dashboard', '工作台', 'MENU', '/dashboard', 'HomeOutlined', 0, 'BOTH', 10, 1, 1, UNIX_TIMESTAMP()), | |
| 73 | +('city.manage', '租户管理', 'MENU', '/city', 'GlobalOutlined', 0, 'PLATFORM', 20, 1, 1, UNIX_TIMESTAMP()), | |
| 74 | +('substation.manage', '分站管理', 'MENU', '/substation', 'ApartmentOutlined', 0, 'PLATFORM', 30, 1, 1, UNIX_TIMESTAMP()), | |
| 75 | +('merchant.root', '商家管理', 'DIR', '', 'ShopOutlined', 0, 'BOTH', 40, 1, 1, UNIX_TIMESTAMP()), | |
| 76 | +('merchant.enter', '入驻申请', 'MENU', '/merchant/enter', '', 4, 'BOTH', 41, 1, 1, UNIX_TIMESTAMP()), | |
| 77 | +('merchant.store', '店铺管理', 'MENU', '/merchant/store', '', 4, 'BOTH', 42, 1, 1, UNIX_TIMESTAMP()), | |
| 78 | +('rider.list', '骑手管理', 'MENU', '/rider', 'UserOutlined', 0, 'BOTH', 50, 1, 1, UNIX_TIMESTAMP()), | |
| 79 | +('rider.evaluate', '骑手评价', 'MENU', '/rider/evaluate', 'StarOutlined', 0, 'BOTH', 60, 1, 1, UNIX_TIMESTAMP()), | |
| 80 | +('order.root', '订单管理', 'DIR', '', 'UnorderedListOutlined', 0, 'BOTH', 70, 1, 1, UNIX_TIMESTAMP()), | |
| 81 | +('order.list', '订单列表', 'MENU', '/order', '', 9, 'BOTH', 71, 1, 1, UNIX_TIMESTAMP()), | |
| 82 | +('order.refund', '退款管理', 'MENU', '/refund', '', 9, 'BOTH', 72, 1, 1, UNIX_TIMESTAMP()), | |
| 83 | +('order.delivery', '配送订单', 'MENU', '/delivery/order', '', 9, 'BOTH', 73, 1, 1, UNIX_TIMESTAMP()), | |
| 84 | +('config.root', '配置中心', 'DIR', '', 'ControlOutlined', 0, 'BOTH', 80, 1, 1, UNIX_TIMESTAMP()), | |
| 85 | +('fee.plan', '配送费配置', 'MENU', '/config/fee-plan', '', 13, 'BOTH', 81, 1, 1, UNIX_TIMESTAMP()), | |
| 86 | +('dispatch.rule', '调度配置', 'MENU', '/dispatch/rule', '', 13, 'BOTH', 82, 1, 1, UNIX_TIMESTAMP()), | |
| 87 | +('open.root', '开放平台', 'DIR', '', 'ApiOutlined', 0, 'PLATFORM', 90, 1, 1, UNIX_TIMESTAMP()), | |
| 88 | +('open.app', '应用管理', 'MENU', '/open', '', 16, 'PLATFORM', 91, 1, 1, UNIX_TIMESTAMP()), | |
| 89 | +('open.mock_delivery', '模拟推单', 'MENU', '/open/mock-delivery', '', 16, 'PLATFORM', 92, 1, 1, UNIX_TIMESTAMP()), | |
| 90 | +('system.root', '系统管理', 'DIR', '', 'ControlOutlined', 0, 'PLATFORM', 100, 1, 1, UNIX_TIMESTAMP()), | |
| 91 | +('system.menu', '菜单管理', 'MENU', '/system/menu', '', 19, 'PLATFORM', 101, 1, 1, UNIX_TIMESTAMP()), | |
| 92 | +('system.role_menu', '角色菜单', 'MENU', '/system/role-menu', '', 19, 'PLATFORM', 102, 1, 1, UNIX_TIMESTAMP()), | |
| 93 | +('admin.user', '平台账号', 'MENU', '/admin-user', '', 19, 'PLATFORM', 103, 1, 1, UNIX_TIMESTAMP()), | |
| 94 | +('rider.withdraw', '骑手提现审核', 'MENU', '/rider/withdraw', 'WalletOutlined', 0, 'BOTH', 56, 1, 1, UNIX_TIMESTAMP()); | |
| 95 | + | |
| 96 | +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 97 | +SELECT 1, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('PLATFORM', 'BOTH'); | |
| 98 | + | |
| 99 | +INSERT INTO `sys_role_menu` (`role_id`, `menu_id`, `create_time`) | |
| 100 | +SELECT 2, id, UNIX_TIMESTAMP() FROM `sys_menu` WHERE `menu_scope` IN ('SUBSTATION', 'BOTH'); | |
| 101 | + | |
| 102 | +INSERT INTO `substation` (`city_id`, `user_login`, `user_nickname`, `user_pass`, `mobile`, `user_status`, `role_id`, `create_time`) VALUES | |
| 103 | +(2, 'gz_admin', '广州分站管理员', '0192023a7bbd73250516f069df18b500', '13800000001', 1, 2, UNIX_TIMESTAMP()), | |
| 104 | +(3, 'sz_admin', '深圳分站管理员', '0192023a7bbd73250516f069df18b500', '13800000002', 1, 2, UNIX_TIMESTAMP()); | |
| 105 | + | |
| 106 | +-- ============================================================ | |
| 107 | +-- 3. 骑手等级配置(广州) | |
| 108 | +-- ============================================================ | |
| 109 | +INSERT INTO `rider_level` (`city_id`, `level_id`, `name`, `is_default`, `trans_nums`, | |
| 110 | + `run_fee_mode`, `run_fix_money`, `run_rate`, `distance_basic`, `distance_basic_money`, | |
| 111 | + `distance_more_money`, `distance_max_money`, `work_fee_mode`, `work_fix_money`, `work_rate`) VALUES | |
| 112 | +-- 普通骑手:按比例拿配送费的70% | |
| 113 | +(2, 1, '普通骑手', 1, 3, | |
| 114 | + 2, 0.00, 70.00, 0, 0.00, 0.00, 0.00, | |
| 115 | + 1, 5.00, 0.00), | |
| 116 | +-- 资深骑手:按比例拿配送费的80% | |
| 117 | +(2, 2, '资深骑手', 0, 5, | |
| 118 | + 2, 0.00, 80.00, 0, 0.00, 0.00, 0.00, | |
| 119 | + 1, 6.00, 0.00); | |
| 120 | + | |
| 121 | +-- 深圳骑手等级 | |
| 122 | +INSERT INTO `rider_level` (`city_id`, `level_id`, `name`, `is_default`, `trans_nums`, | |
| 123 | + `run_fee_mode`, `run_fix_money`, `run_rate`, `distance_basic`, `distance_basic_money`, | |
| 124 | + `distance_more_money`, `distance_max_money`, `work_fee_mode`, `work_fix_money`, `work_rate`) VALUES | |
| 125 | +(3, 1, '普通骑手', 1, 3, | |
| 126 | + 2, 0.00, 70.00, 0, 0.00, 0.00, 0.00, | |
| 127 | + 1, 5.00, 0.00); | |
| 128 | + | |
| 129 | +-- ============================================================ | |
| 130 | +-- 4. 示例骑手账号 | |
| 131 | +-- 默认密码均为 test1234(MD5: 16d7a4fca7442dda3ad93c9a726597e4) | |
| 132 | +-- ============================================================ | |
| 133 | +INSERT INTO `rider` (`mobile`, `user_login`, `user_nickname`, `user_pass`, `city_id`, `level_id`, | |
| 134 | + `type`, `user_status`, `balance`, `is_rest`, `create_time`) VALUES | |
| 135 | +('13900000001', 'phone_rider001', '张骑手', '16d7a4fca7442dda3ad93c9a726597e4', 2, 1, 1, 1, 0.00, 0, UNIX_TIMESTAMP()), | |
| 136 | +('13900000002', 'phone_rider002', '李骑手', '16d7a4fca7442dda3ad93c9a726597e4', 2, 1, 2, 1, 0.00, 0, UNIX_TIMESTAMP()); | |
| 137 | + | |
| 138 | +-- ============================================================ | |
| 139 | +-- 5. 示例商家店铺 | |
| 140 | +-- ============================================================ | |
| 141 | +INSERT INTO `merchant_store` (`name`, `thumb`, `city_id`, `address`, `lng`, `lat`, | |
| 142 | + `operating_state`, `automatic_order`, `shipping_type`, `free_shipping`, `up_to_send`, | |
| 143 | + `open_date`, `open_time`, `about`, `list_order`, `is_del`, `add_time`) VALUES | |
| 144 | +('测试餐厅', '', 2, '广州市天河区测试路1号', '113.330010', '23.132891', | |
| 145 | + 1, 1, 1, 30.00, 15.00, | |
| 146 | + '[1,2,3,4,5,6,7]', '["09:00","22:00"]', '测试店铺,仅供开发调试', 1, 0, UNIX_TIMESTAMP()); | |
| 147 | + | |
| 148 | +-- 创建商家账号 | |
| 149 | +INSERT INTO `merchant_users` (`store_id`, `mobile`, `user_nickname`, `user_status`, `type`, `create_time`) | |
| 150 | +VALUES (LAST_INSERT_ID(), '13700000001', '测试餐厅老板', 1, 1, UNIX_TIMESTAMP()); | |
| 151 | + | |
| 152 | +-- ============================================================ | |
| 153 | +-- 6. 开放平台示例应用 | |
| 154 | +-- ============================================================ | |
| 155 | +INSERT INTO `open_app` (`app_name`, `app_key`, `app_secret`, `store_id`, `status`, | |
| 156 | + `webhook_url`, `webhook_events`, `remark`, `create_time`) VALUES | |
| 157 | +('内部电商系统', 'TESTAPPKEY00001', 'testsecret0000000000000000000000000000000000000000000000000001', 0, 1, | |
| 158 | + '', '["order.created","order.accepted","order.arrived_shop","order.picking","order.completed","order.cancelled"]', '用于测试的内部应用', UNIX_TIMESTAMP()); | |
| 159 | + | |
| 160 | +-- ============================================================ | |
| 161 | +-- 完成提示 | |
| 162 | +-- ============================================================ | |
| 163 | +SELECT '初始化完成!' AS 提示; | |
| 164 | +SELECT '骑手登录账号: 13900000001 / 13900000002,密码: test1234' AS 骑手账号; | |
| 165 | +SELECT '分站管理员: gz_admin / sz_admin,密码: admin123' AS 分站账号; | |
| 166 | +SELECT '商家手机号: 13700000001' AS 商家账号; | |
| 167 | +SELECT '开放平台 AppKey: TESTAPPKEY00001' AS 开放平台; | |
| 168 | + | |
| 169 | +-- ============================================================ | |
| 170 | +-- 7. 超级管理员账号 | |
| 171 | +-- 默认密码:admin123(MD5: 0192023a7bbd73250516f069df18b500) | |
| 172 | +-- ============================================================ | |
| 173 | +INSERT INTO `admin_user` (`user_login`, `user_pass`, `user_nickname`, `user_status`, `role_id`, `create_time`) VALUES | |
| 174 | +('admin', '0192023a7bbd73250516f069df18b500', '超级管理员', 1, 1, UNIX_TIMESTAMP()); | |
| 175 | + | |
| 176 | +SELECT '超级管理员: admin / admin123(role=admin)' AS 超管账号; | |
| 177 | +SELECT '系统管理菜单: /system/menu /system/role-menu /admin-user' AS 系统管理; | ... | ... |
src/main/resources/schema.sql
| 1 | --- 外卖骑手配送模块 数据库建表脚本 | |
| 2 | --- 数据库:dili_rider | |
| 3 | - | |
| 4 | -CREATE DATABASE IF NOT EXISTS dili_rider DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | |
| 5 | -USE dili_rider; | |
| 6 | - | |
| 7 | --- 骑手信息表 | |
| 8 | -CREATE TABLE `rider` ( | |
| 9 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '骑手ID', | |
| 10 | - `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 11 | - `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录名', | |
| 12 | - `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 13 | - `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 14 | - `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像', | |
| 15 | - `avatar_thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像缩略图', | |
| 16 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 17 | - `level_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '等级ID', | |
| 18 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=兼职 2=全职', | |
| 19 | - `user_status` TINYINT NOT NULL DEFAULT 2 COMMENT '审核状态:0=拒绝 1=通过 2=待审核', | |
| 20 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '账号状态:0=禁用 1=正常', | |
| 21 | - `balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '余额(兼职用)', | |
| 22 | - `frozen_balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额(提现审核中)', | |
| 23 | - `is_rest` TINYINT NOT NULL DEFAULT 0 COMMENT '是否休息:0=否 1=是', | |
| 24 | - `hold_order_limit` INT NOT NULL DEFAULT 0 COMMENT '个人持单上限,0=不限制', | |
| 25 | - `id_no` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '身份证号', | |
| 26 | - `thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '手持身份证照片', | |
| 27 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '注册时间', | |
| 28 | - `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0=正常 1=已删除', | |
| 29 | - PRIMARY KEY (`id`), | |
| 30 | - UNIQUE KEY `uk_mobile` (`mobile`), | |
| 31 | - KEY `idx_city_id` (`city_id`) | |
| 32 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手信息表'; | |
| 33 | - | |
| 34 | --- 骑手等级配置表 | |
| 35 | -CREATE TABLE `rider_level` ( | |
| 36 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 37 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 38 | - `level_id` INT NOT NULL DEFAULT 0 COMMENT '等级编号', | |
| 39 | - `name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '等级名称', | |
| 40 | - `is_default` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认', | |
| 41 | - `trans_nums` INT NOT NULL DEFAULT 0 COMMENT '每日转单次数上限', | |
| 42 | - `run_fee_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '跑腿收入模式:1=固定 2=比例 3=距离', | |
| 43 | - `run_fix_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '跑腿固定金额', | |
| 44 | - `run_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '跑腿比例(%)', | |
| 45 | - `distance_basic` INT NOT NULL DEFAULT 0 COMMENT '起始距离(米)', | |
| 46 | - `distance_basic_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基础配送费', | |
| 47 | - `distance_more_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '超出每公里费', | |
| 48 | - `distance_max_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '最高配送费上限', | |
| 49 | - `work_fee_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '办事收入模式:1=固定 2=比例', | |
| 50 | - `work_fix_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '办事固定金额', | |
| 51 | - `work_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '办事比例(%)', | |
| 52 | - PRIMARY KEY (`id`), | |
| 53 | - UNIQUE KEY `uk_city_level` (`city_id`, `level_id`), | |
| 54 | - KEY `idx_city_default` (`city_id`, `is_default`) | |
| 55 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手等级配置表'; | |
| 56 | - | |
| 57 | --- 骑手实时位置表 | |
| 58 | -CREATE TABLE `rider_location` ( | |
| 59 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 60 | - `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 61 | - `lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '经度', | |
| 62 | - `lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '纬度', | |
| 63 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间', | |
| 64 | - PRIMARY KEY (`id`), | |
| 65 | - UNIQUE KEY `uk_uid` (`uid`) | |
| 66 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手实时位置表'; | |
| 67 | - | |
| 68 | --- 骑手余额流水表 | |
| 69 | -CREATE TABLE `rider_balance` ( | |
| 70 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 71 | - `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 72 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=收入 2=提现', | |
| 73 | - `action` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '动作标识', | |
| 74 | - `action_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联ID', | |
| 75 | - `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 76 | - `nums` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '变动金额', | |
| 77 | - `total` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '变动后余额', | |
| 78 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '记录时间', | |
| 79 | - PRIMARY KEY (`id`), | |
| 80 | - KEY `idx_uid` (`uid`), | |
| 81 | - KEY `idx_action_id` (`action_id`) | |
| 82 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手余额流水表'; | |
| 83 | - | |
| 84 | --- 骑手提现申请表 | |
| 85 | -CREATE TABLE `rider_withdraw_apply` ( | |
| 86 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 87 | - `withdraw_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '提现单号', | |
| 88 | - `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID', | |
| 89 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 90 | - `amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '提现金额', | |
| 91 | - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=待审核 1=审核通过待打款 2=审核拒绝 3=已打款 4=打款失败', | |
| 92 | - `account_type` TINYINT NOT NULL DEFAULT 1 COMMENT '收款账户类型:1=银行卡 2=支付宝 3=微信', | |
| 93 | - `account_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收款人', | |
| 94 | - `bank_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '开户行', | |
| 95 | - `bank_branch` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '开户支行', | |
| 96 | - `account_no` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '收款账号', | |
| 97 | - `apply_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '申请备注', | |
| 98 | - `audit_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '审核/打款备注', | |
| 99 | - `auditor_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核人ID', | |
| 100 | - `auditor_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '审核人名称', | |
| 101 | - `apply_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请时间', | |
| 102 | - `audit_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核时间', | |
| 103 | - `pay_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '打款时间', | |
| 104 | - `transfer_no` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '打款流水号', | |
| 105 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 106 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 107 | - PRIMARY KEY (`id`), | |
| 108 | - UNIQUE KEY `uk_withdraw_no` (`withdraw_no`), | |
| 109 | - KEY `idx_rider_status` (`rider_id`, `status`), | |
| 110 | - KEY `idx_city_status_time` (`city_id`, `status`, `apply_time`) | |
| 111 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手提现申请表'; | |
| 112 | - | |
| 113 | --- 骑手订单统计表 | |
| 114 | -CREATE TABLE `rider_order_count` ( | |
| 115 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 116 | - `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 117 | - `count_date` INT NOT NULL COMMENT '统计日期yyyyMMdd', | |
| 118 | - `orders` INT NOT NULL DEFAULT 0 COMMENT '完成订单数', | |
| 119 | - `transfers` INT NOT NULL DEFAULT 0 COMMENT '转单数', | |
| 120 | - `distance` BIGINT NOT NULL DEFAULT 0 COMMENT '配送距离(米)', | |
| 121 | - PRIMARY KEY (`id`), | |
| 122 | - UNIQUE KEY `uk_uid_date` (`uid`, `count_date`) | |
| 123 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手订单统计表'; | |
| 124 | - | |
| 125 | --- 骑手拒单记录表 | |
| 126 | -CREATE TABLE `rider_orders_refuse` ( | |
| 127 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 128 | - `rider_id` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 129 | - `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 130 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '拒单时间', | |
| 131 | - PRIMARY KEY (`id`), | |
| 132 | - KEY `idx_rider_id` (`rider_id`), | |
| 133 | - KEY `idx_oid` (`oid`) | |
| 134 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手拒单记录表'; | |
| 135 | - | |
| 136 | --- 订单主表 | |
| 137 | -CREATE TABLE `orders` ( | |
| 138 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID', | |
| 139 | - `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 140 | - `uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户ID', | |
| 141 | - `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID', | |
| 142 | - `old_rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '原始骑手ID', | |
| 143 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 144 | - `type` TINYINT NOT NULL DEFAULT 6 COMMENT '订单类型:6=外卖配送', | |
| 145 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已接单 4服务中 6已完成 7退款申请 8退款成功 9退款拒绝 10已取消', | |
| 146 | - `pay_type` TINYINT NOT NULL DEFAULT 0 COMMENT '支付类型:1=支付宝 2=微信', | |
| 147 | - `money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额', | |
| 148 | - `money_delivery` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '配送费', | |
| 149 | - `money_total` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '实付总金额', | |
| 150 | - `rider_income` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '骑手收入', | |
| 151 | - `substation_income` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '站点收入', | |
| 152 | - `is_income` TINYINT NOT NULL DEFAULT 0 COMMENT '结算状态:0=未结算 1=待结算 2=已结算', | |
| 153 | - `is_trans` TINYINT NOT NULL DEFAULT 0 COMMENT '转单状态:0=未转 1=通过 2=申请中 3=拒绝', | |
| 154 | - `code` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '完成码', | |
| 155 | - `f_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '起点名称', | |
| 156 | - `f_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '起点地址', | |
| 157 | - `f_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '起点经度', | |
| 158 | - `f_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '起点纬度', | |
| 159 | - `t_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '终点名称', | |
| 160 | - `t_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '终点地址', | |
| 161 | - `t_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '终点经度', | |
| 162 | - `t_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '终点纬度', | |
| 163 | - `recip_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收件人姓名', | |
| 164 | - `recip_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '收件人电话', | |
| 165 | - `extra` TEXT COMMENT '附加信息JSON(距离、重量等)', | |
| 166 | - `thumbs` TEXT COMMENT '取件照片JSON数组', | |
| 167 | - `store_oid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺订单ID', | |
| 168 | - `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', | |
| 169 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '下单时间', | |
| 170 | - `pay_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付时间', | |
| 171 | - `grap_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '接单时间', | |
| 172 | - `pick_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '取件时间', | |
| 173 | - `complete_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '完成时间', | |
| 174 | - `trans_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '转单时间', | |
| 175 | - PRIMARY KEY (`id`), | |
| 176 | - UNIQUE KEY `uk_order_no` (`order_no`), | |
| 177 | - KEY `idx_rider_id` (`rider_id`), | |
| 178 | - KEY `idx_uid` (`uid`), | |
| 179 | - KEY `idx_city_status` (`city_id`, `status`), | |
| 180 | - KEY `idx_old_rider_trans` (`old_rider_id`, `is_trans`, `trans_time`) | |
| 181 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; | |
| 182 | - | |
| 183 | --- 城市表(配送中台核心配置) | |
| 184 | -CREATE TABLE `city` ( | |
| 185 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '城市ID', | |
| 186 | - `pid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父级ID,0=省级', | |
| 187 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '城市名称', | |
| 188 | - `area_code` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '行政区划码', | |
| 189 | - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未开通 1=已开通', | |
| 190 | - `rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '平台抽成比例(%)', | |
| 191 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 192 | - PRIMARY KEY (`id`), | |
| 193 | - KEY `idx_pid_order` (`pid`, `list_order`), | |
| 194 | - KEY `idx_area_code` (`area_code`) | |
| 195 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='城市表'; | |
| 196 | - | |
| 197 | --- 配送计价方案主表 | |
| 198 | -CREATE TABLE `delivery_fee_plan` ( | |
| 199 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 200 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '租户ID', | |
| 201 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '方案名称', | |
| 202 | - `is_default` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认方案', | |
| 203 | - `min_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '保底费用', | |
| 204 | - `distance_basic` DECIMAL(10,2) NOT NULL DEFAULT 3.00 COMMENT '预计送达基础距离(km)', | |
| 205 | - `distance_basic_time` INT NOT NULL DEFAULT 30 COMMENT '预计送达基础时间(分钟)', | |
| 206 | - `distance_more_time` INT NOT NULL DEFAULT 10 COMMENT '预计送达超出每km增加时间(分钟)', | |
| 207 | - `rider_distance` DECIMAL(10,2) NOT NULL DEFAULT 3.00 COMMENT '附近骑手展示范围(km)', | |
| 208 | - `rider_time` INT NOT NULL DEFAULT 0 COMMENT '预计接单时间(分钟)', | |
| 209 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=启用', | |
| 210 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 211 | - `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 212 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 213 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 214 | - PRIMARY KEY (`id`), | |
| 215 | - KEY `idx_city_default` (`city_id`, `is_default`), | |
| 216 | - KEY `idx_city_status` (`city_id`, `status`) | |
| 217 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价方案主表'; | |
| 218 | - | |
| 219 | --- 配送计价维度主配置表 | |
| 220 | -CREATE TABLE `delivery_fee_plan_dimension` ( | |
| 221 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 222 | - `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 223 | - `dimension_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '维度类型:base/distance/weight/piece/time', | |
| 224 | - `enabled` TINYINT NOT NULL DEFAULT 0 COMMENT '是否启用', | |
| 225 | - `base_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基础费', | |
| 226 | - `start_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起步里程(km)', | |
| 227 | - `start_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起步费用', | |
| 228 | - `first_weight` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '首重(kg)', | |
| 229 | - `first_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '首重费用', | |
| 230 | - `unit_weight_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '续重单价', | |
| 231 | - `cap_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '封顶费用', | |
| 232 | - `extra_json` TEXT COMMENT '扩展配置', | |
| 233 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 234 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 235 | - PRIMARY KEY (`id`), | |
| 236 | - UNIQUE KEY `uk_plan_dimension` (`plan_id`, `dimension_type`) | |
| 237 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价维度主配置表'; | |
| 238 | - | |
| 239 | --- 里程阶梯表 | |
| 240 | -CREATE TABLE `delivery_fee_plan_distance_step` ( | |
| 241 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 242 | - `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 243 | - `end_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '结束里程(km)', | |
| 244 | - `unit_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '每档里程(km)', | |
| 245 | - `unit_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '每档加价', | |
| 246 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 247 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 248 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 249 | - PRIMARY KEY (`id`), | |
| 250 | - KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 251 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价里程阶梯表'; | |
| 252 | - | |
| 253 | --- 件数区间表 | |
| 254 | -CREATE TABLE `delivery_fee_plan_piece_rule` ( | |
| 255 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 256 | - `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 257 | - `start_piece` INT NOT NULL DEFAULT 0 COMMENT '起始件数', | |
| 258 | - `end_piece` INT NOT NULL DEFAULT 0 COMMENT '结束件数', | |
| 259 | - `fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '费用', | |
| 260 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 261 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 262 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 263 | - PRIMARY KEY (`id`), | |
| 264 | - KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 265 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价件数区间表'; | |
| 266 | - | |
| 267 | --- 时段附加费表 | |
| 268 | -CREATE TABLE `delivery_fee_plan_time_rule` ( | |
| 269 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 270 | - `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 271 | - `start_minute` INT NOT NULL DEFAULT 0 COMMENT '开始分钟', | |
| 272 | - `end_minute` INT NOT NULL DEFAULT 0 COMMENT '结束分钟', | |
| 273 | - `fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '附加费', | |
| 274 | - `enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用', | |
| 275 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 276 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 277 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 278 | - PRIMARY KEY (`id`), | |
| 279 | - KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 280 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价时段附加费表'; | |
| 281 | - | |
| 282 | --- 分站管理员表(每城市一个,管理本城市骑手和订单) | |
| 283 | -CREATE TABLE `substation` ( | |
| 284 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分站ID', | |
| 285 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '管理城市ID', | |
| 286 | - `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录账号', | |
| 287 | - `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 288 | - `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 289 | - `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 290 | - `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像', | |
| 291 | - `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 292 | - `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 293 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 294 | - PRIMARY KEY (`id`), | |
| 295 | - UNIQUE KEY `uk_user_login` (`user_login`) | |
| 296 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分站管理员表(一个城市/租户下可有多个管理员)'; | |
| 297 | - | |
| 298 | --- 商家入驻申请表 | |
| 299 | -CREATE TABLE `merchant_enter` ( | |
| 300 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 301 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '联系人姓名', | |
| 302 | - `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 303 | - `store_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '店铺名称', | |
| 304 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=商家入驻 2=骑手入驻 3=商务合作', | |
| 305 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 306 | - `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 307 | - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未处理 1=已通过 -1=已拒绝', | |
| 308 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请时间', | |
| 309 | - PRIMARY KEY (`id`), | |
| 310 | - KEY `idx_status_type` (`status`, `type`), | |
| 311 | - KEY `idx_city_id` (`city_id`) | |
| 312 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家入驻申请表'; | |
| 313 | - | |
| 314 | --- 商家账号表 | |
| 315 | -CREATE TABLE `merchant_users` ( | |
| 316 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 317 | - `store_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺ID', | |
| 318 | - `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号(登录账号)', | |
| 319 | - `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 320 | - `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 321 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=商家', | |
| 322 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 323 | - PRIMARY KEY (`id`), | |
| 324 | - UNIQUE KEY `uk_mobile` (`mobile`), | |
| 325 | - KEY `idx_store_id` (`store_id`) | |
| 326 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家账号表'; | |
| 327 | - | |
| 328 | --- 商家店铺表 | |
| 329 | -CREATE TABLE `merchant_store` ( | |
| 330 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '店铺ID', | |
| 331 | - `name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '店铺名称', | |
| 332 | - `thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '封面图', | |
| 333 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '所属城市ID', | |
| 334 | - `address` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '店铺地址', | |
| 335 | - `lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '经度', | |
| 336 | - `lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '纬度', | |
| 337 | - `operating_state` TINYINT NOT NULL DEFAULT 1 COMMENT '营业状态:0=打烊 1=营业', | |
| 338 | - `automatic_order` TINYINT NOT NULL DEFAULT 0 COMMENT '自动接单:0=否 1=是', | |
| 339 | - `shipping_type` TINYINT NOT NULL DEFAULT 1 COMMENT '配送类型:1=外卖配送 2=到店自提', | |
| 340 | - `free_shipping` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '免运费门槛,0=不免', | |
| 341 | - `up_to_send` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起送金额,0=不限', | |
| 342 | - `open_date` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '营业日期JSON,如[1,2,3,4,5]', | |
| 343 | - `open_time` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '营业时间JSON,如["09:00","22:00"]', | |
| 344 | - `about` TEXT COMMENT '店铺简介', | |
| 345 | - `account_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联账号ID', | |
| 346 | - `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '接入方AppKey,为空=平台自建', | |
| 347 | - `out_store_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '接入方门店编号,用于推单时自动匹配', | |
| 348 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 349 | - `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', | |
| 350 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 351 | - `sync_app_key` VARCHAR(32) GENERATED ALWAYS AS (NULLIF(`app_key`, '')) STORED COMMENT '外部同步唯一键AppKey', | |
| 352 | - `sync_out_store_id` VARCHAR(64) GENERATED ALWAYS AS (NULLIF(`out_store_id`, '')) STORED COMMENT '外部同步唯一键门店ID', | |
| 353 | - PRIMARY KEY (`id`), | |
| 354 | - KEY `idx_city_id` (`city_id`), | |
| 355 | - UNIQUE KEY `uk_app_out_store` (`sync_app_key`, `sync_out_store_id`, `is_del`), | |
| 356 | - KEY `idx_order_del` (`list_order`, `is_del`) | |
| 357 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家店铺表'; | |
| 358 | - | |
| 359 | --- 开放平台应用表 | |
| 360 | -CREATE TABLE `open_app` ( | |
| 361 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 362 | - `app_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '应用名称', | |
| 363 | - `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'AppKey', | |
| 364 | - `app_secret` VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'AppSecret', | |
| 365 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联城市/租户ID(必填,租户隔离核心字段)', | |
| 366 | - `store_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺ID,0=不限制', | |
| 367 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 368 | - `webhook_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Webhook回调地址', | |
| 369 | - `webhook_events` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '订阅事件JSON数组', | |
| 370 | - `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 371 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 372 | - PRIMARY KEY (`id`), | |
| 373 | - UNIQUE KEY `uk_app_key` (`app_key`), | |
| 374 | - KEY `idx_city_id` (`city_id`) | |
| 375 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放平台应用表'; | |
| 376 | - | |
| 377 | --- Webhook 推送日志表 | |
| 378 | -CREATE TABLE `webhook_log` ( | |
| 379 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 380 | - `app_id` BIGINT UNSIGNED NOT NULL COMMENT '应用ID', | |
| 381 | - `event` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '事件类型', | |
| 382 | - `biz_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '业务ID', | |
| 383 | - `url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '推送URL', | |
| 384 | - `payload` TEXT COMMENT '推送内容JSON', | |
| 385 | - `response_code` INT NOT NULL DEFAULT 0 COMMENT 'HTTP响应码', | |
| 386 | - `response_body` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '响应内容', | |
| 387 | - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=失败 1=成功', | |
| 388 | - `retry_count` INT NOT NULL DEFAULT 0 COMMENT '重试次数', | |
| 389 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 390 | - PRIMARY KEY (`id`), | |
| 391 | - KEY `idx_app_event` (`app_id`, `event`), | |
| 392 | - KEY `idx_biz_id` (`biz_id`), | |
| 393 | - KEY `idx_status` (`status`) | |
| 394 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Webhook推送日志表'; | |
| 395 | - | |
| 396 | --- 超级管理员表 | |
| 397 | -CREATE TABLE `admin_user` ( | |
| 398 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 399 | - `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录账号', | |
| 400 | - `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 401 | - `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 402 | - `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 403 | - `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 404 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 405 | - PRIMARY KEY (`id`), | |
| 406 | - UNIQUE KEY `uk_user_login` (`user_login`) | |
| 407 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级管理员表'; | |
| 408 | - | |
| 409 | -CREATE TABLE `sys_role` ( | |
| 410 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 411 | - `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色编码', | |
| 412 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色名称', | |
| 413 | - `role_scope` VARCHAR(32) NOT NULL DEFAULT 'PLATFORM' COMMENT '角色归属:PLATFORM/SUBSTATION', | |
| 414 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '所属租户ID:0=平台全局角色,>0=分站租户专属角色', | |
| 415 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 416 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 417 | - PRIMARY KEY (`id`), | |
| 418 | - UNIQUE KEY `uk_role_code` (`code`, `city_id`), | |
| 419 | - KEY `idx_city_scope` (`city_id`, `role_scope`) | |
| 420 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单角色表'; | |
| 421 | - | |
| 422 | -CREATE TABLE `sys_menu` ( | |
| 423 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 424 | - `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单编码', | |
| 425 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单名称', | |
| 426 | - `type` VARCHAR(16) NOT NULL DEFAULT 'MENU' COMMENT '类型:DIR/MENU', | |
| 427 | - `path` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '前端路由路径', | |
| 428 | - `icon` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '前端图标名', | |
| 429 | - `parent_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父级菜单ID', | |
| 430 | - `menu_scope` VARCHAR(32) NOT NULL DEFAULT 'BOTH' COMMENT '菜单归属:PLATFORM/SUBSTATION/BOTH', | |
| 431 | - `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 432 | - `visible` TINYINT NOT NULL DEFAULT 1 COMMENT '是否显示:0=否 1=是', | |
| 433 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 434 | - KEY `idx_scope_parent` (`menu_scope`, `parent_id`, `list_order`), | |
| 435 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 436 | - PRIMARY KEY (`id`), | |
| 437 | - UNIQUE KEY `uk_menu_code` (`code`), | |
| 438 | - KEY `idx_parent_order` (`parent_id`, `list_order`) | |
| 439 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单表'; | |
| 440 | - | |
| 441 | -CREATE TABLE `sys_role_menu` ( | |
| 442 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 443 | - `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '角色ID', | |
| 444 | - `menu_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单ID', | |
| 445 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 446 | - PRIMARY KEY (`id`), | |
| 447 | - UNIQUE KEY `uk_role_menu` (`role_id`, `menu_id`) | |
| 448 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台角色菜单关系表'; | |
| 449 | - | |
| 450 | --- orders 表补充字段(如已有 orders 表,执行以下 ALTER) | |
| 451 | -ALTER TABLE `orders` ADD COLUMN `out_order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '外部系统订单号' AFTER `order_no`; | |
| 452 | -ALTER TABLE `orders` ADD COLUMN `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '接入方AppKey' AFTER `out_order_no`; | |
| 453 | -ALTER TABLE `orders` ADD COLUMN `callback_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '状态回调地址' AFTER `app_key`; | |
| 454 | -ALTER TABLE `orders` ADD INDEX `idx_app_out_order` (`app_key`, `out_order_no`); | |
| 455 | - | |
| 456 | --- 骑手评价表 | |
| 457 | -CREATE TABLE `rider_evaluate` ( | |
| 458 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 459 | - `uid` BIGINT UNSIGNED NOT NULL COMMENT '评价用户ID', | |
| 460 | - `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 461 | - `rid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 462 | - `content` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '评价内容', | |
| 463 | - `star` TINYINT NOT NULL DEFAULT 5 COMMENT '星级1-5', | |
| 464 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 465 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 466 | - PRIMARY KEY (`id`), | |
| 467 | - UNIQUE KEY `uk_uid_oid` (`uid`, `oid`), | |
| 468 | - KEY `idx_rid` (`rid`) | |
| 469 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手评价表'; | |
| 470 | - | |
| 471 | --- 退款原因配置表 | |
| 472 | -CREATE TABLE `orders_refund_reason` ( | |
| 473 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 474 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '原因描述', | |
| 475 | - `role` TINYINT NOT NULL DEFAULT 1 COMMENT '1=用户 2=骑手', | |
| 476 | - `list_order` INT NOT NULL DEFAULT 0, | |
| 477 | - PRIMARY KEY (`id`) | |
| 478 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款原因配置表'; | |
| 479 | - | |
| 480 | --- 退款申请记录表 | |
| 481 | -CREATE TABLE `orders_refund_record` ( | |
| 482 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 483 | - `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 484 | - `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 485 | - `uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请人ID', | |
| 486 | - `role` TINYINT NOT NULL DEFAULT 1 COMMENT '1=用户 2=骑手', | |
| 487 | - `reason_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 488 | - `reason` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '退款原因', | |
| 489 | - `money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '退款金额', | |
| 490 | - `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0=待处理 1=通过 2=拒绝', | |
| 491 | - `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '处理备注', | |
| 492 | - `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 493 | - `handle_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 494 | - PRIMARY KEY (`id`), | |
| 495 | - KEY `idx_oid` (`oid`), | |
| 496 | - KEY `idx_status` (`status`) | |
| 497 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款申请记录表'; | |
| 498 | - | |
| 499 | --- 退款原因初始数据 | |
| 500 | -INSERT INTO `orders_refund_reason` (`name`, `role`, `list_order`) VALUES | |
| 501 | -('骑手长时间未接单', 1, 1), | |
| 502 | -('骑手态度恶劣', 1, 2), | |
| 503 | -('物品损坏', 1, 3), | |
| 504 | -('其他原因', 1, 99), | |
| 505 | -('用户恶意单', 2, 1), | |
| 506 | -('无法完成配送', 2, 2), | |
| 507 | -('其他原因', 2, 99); | |
| 508 | - | |
| 509 | --- orders 表补充货物快照字段 | |
| 510 | -ALTER TABLE `orders` ADD COLUMN `items_json` TEXT COMMENT '货物清单快照JSON' AFTER `callback_url`; | |
| 511 | -ALTER TABLE `orders` ADD COLUMN `item_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '整单货物备注' AFTER `items_json`; | |
| 512 | - | |
| 513 | --- orders 表补充调度字段 | |
| 514 | -ALTER TABLE `orders` ADD COLUMN `dispatch_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '系统派单时间' AFTER `trans_time`; | |
| 515 | -ALTER TABLE `orders` ADD COLUMN `dispatch_rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '系统指派骑手ID' AFTER `dispatch_time`; | |
| 516 | - | |
| 517 | --- rider 表补充个人持单上限与评分统计字段 | |
| 518 | -ALTER TABLE `rider` ADD COLUMN `hold_order_limit` INT NOT NULL DEFAULT 0 COMMENT '个人持单上限,0=不限制' AFTER `is_rest`; | |
| 519 | -ALTER TABLE `rider` ADD COLUMN `star_total` INT NOT NULL DEFAULT 0 COMMENT '评分总分' AFTER `thumb`; | |
| 520 | -ALTER TABLE `rider` ADD COLUMN `star_count` INT NOT NULL DEFAULT 0 COMMENT '评分次数' AFTER `star_total`; | |
| 521 | - | |
| 522 | --- 调度规则模板表 | |
| 523 | -CREATE TABLE `dispatch_rule_template` ( | |
| 524 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 525 | - `city_id` BIGINT UNSIGNED NOT NULL COMMENT '城市ID', | |
| 526 | - `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '模板名称', | |
| 527 | - `is_active` TINYINT NOT NULL DEFAULT 0 COMMENT '是否当前生效:0=否 1=是', | |
| 528 | - `grab_enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '抢单模式启用', | |
| 529 | - `grab_timeout` INT NOT NULL DEFAULT 30 COMMENT '抢单超时分钟数,超时后转自动派单', | |
| 530 | - `grab_scope` TINYINT NOT NULL DEFAULT 1 COMMENT '抢单可见范围:1=订单所属区域骑手 2=全部自营骑手', | |
| 531 | - `grab_max_per_rider` INT NOT NULL DEFAULT 3 COMMENT '单人最大同时持单量', | |
| 532 | - `auto_dispatch` TINYINT NOT NULL DEFAULT 1 COMMENT '同步开启自动派单:0=否 1=是', | |
| 533 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 534 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 535 | - PRIMARY KEY (`id`), | |
| 536 | - KEY `idx_city_active` (`city_id`, `is_active`) | |
| 537 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='调度规则模板表'; | |
| 538 | - | |
| 539 | --- 派单优先级条件表 | |
| 540 | -CREATE TABLE `dispatch_rule_condition` ( | |
| 541 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 542 | - `template_id` BIGINT UNSIGNED NOT NULL COMMENT '所属模板ID', | |
| 543 | - `condition_type` VARCHAR(32) NOT NULL COMMENT '条件类型', | |
| 544 | - `enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用', | |
| 545 | - `threshold_value` DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '阈值', | |
| 546 | - `sort_order` INT NOT NULL DEFAULT 0 COMMENT '优先级排序(小的优先)', | |
| 547 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 548 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 549 | - PRIMARY KEY (`id`), | |
| 550 | - UNIQUE KEY `uk_template_type` (`template_id`, `condition_type`), | |
| 551 | - KEY `idx_template_order` (`template_id`, `sort_order`) | |
| 552 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='派单优先级条件表'; | |
| 553 | - | |
| 554 | --- 骑手消息表 | |
| 555 | -CREATE TABLE `rider_message` ( | |
| 556 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '消息ID', | |
| 557 | - `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID(多租户隔离)', | |
| 558 | - `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID,0表示全员消息', | |
| 559 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '消息类型:1=订单消息 2=系统通知', | |
| 560 | - `title` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '消息标题', | |
| 561 | - `content` TEXT NOT NULL COMMENT '消息内容', | |
| 562 | - `biz_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型:order_assigned/order_timeout/system_notice', | |
| 563 | - `biz_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联业务ID(如订单ID)', | |
| 564 | - `extra_data` TEXT COMMENT '扩展数据(JSON格式)', | |
| 565 | - `is_read` TINYINT NOT NULL DEFAULT 0 COMMENT '已读状态:0=未读 1=已读', | |
| 566 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 567 | - `read_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时间', | |
| 568 | - PRIMARY KEY (`id`), | |
| 569 | - KEY `idx_rider_type` (`rider_id`, `type`, `create_time`), | |
| 570 | - KEY `idx_city_create` (`city_id`, `create_time`), | |
| 571 | - KEY `idx_biz` (`biz_type`, `biz_id`) | |
| 572 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手消息表'; | |
| 573 | - | |
| 574 | --- 消息模板表 | |
| 575 | -CREATE TABLE `rider_message_template` ( | |
| 576 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 577 | - `biz_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型标识', | |
| 578 | - `type` TINYINT NOT NULL DEFAULT 1 COMMENT '消息类型:1=订单 2=系统', | |
| 579 | - `title_template` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '标题模板,支持 #{key} 占位符', | |
| 580 | - `content_template` TEXT NOT NULL COMMENT '内容模板,支持 #{key} 占位符', | |
| 581 | - `is_push` TINYINT NOT NULL DEFAULT 1 COMMENT '是否实时推送:0=否 1=是', | |
| 582 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=启用', | |
| 583 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 584 | - PRIMARY KEY (`id`), | |
| 585 | - UNIQUE KEY `uk_biz_type` (`biz_type`) | |
| 586 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息模板表'; | |
| 587 | - | |
| 588 | --- 初始化模板数据 | |
| 589 | -INSERT INTO `rider_message_template` (`biz_type`, `type`, `title_template`, `content_template`, `is_push`) VALUES | |
| 590 | -('order_assigned', 1, '新订单', '您有新的配送订单 #{orderNo},请及时处理', 1), | |
| 591 | -('order_timeout', 1, '订单超时提醒', '订单 #{orderNo} 即将超时,请尽快送达', 1), | |
| 592 | -('order_cancelled', 1, '订单取消', '订单 #{orderNo} 已被取消', 1), | |
| 593 | -('system_notice', 2, '系统通知', '#{content}', 1), | |
| 594 | -('level_upgrade', 2, '等级提升', '恭喜您,等级已提升至 #{levelName}', 1), | |
| 595 | -('balance_change', 2, '余额变动', '您的余额发生变动,当前余额:#{balance} 元', 0); | |
| 596 | - | |
| 597 | --- 骑手设备推送绑定表(极光推送) | |
| 598 | -CREATE TABLE `rider_device` ( | |
| 599 | - `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 600 | - `rider_id` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 601 | - `registration_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'JPush 设备注册ID', | |
| 602 | - `platform` TINYINT NOT NULL DEFAULT 0 COMMENT '平台:1=Android 2=iOS', | |
| 603 | - `device_info` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备信息(型号/系统版本)', | |
| 604 | - `app_version` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'App 版本', | |
| 605 | - `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=已解绑 1=正常', | |
| 606 | - `last_active_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '最近活跃时间', | |
| 607 | - `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 608 | - `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 609 | - PRIMARY KEY (`id`), | |
| 610 | - UNIQUE KEY `uk_registration_id` (`registration_id`), | |
| 611 | - KEY `idx_rider_status` (`rider_id`, `status`) | |
| 612 | -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手设备推送绑定表'; | |
| 1 | +-- 外卖骑手配送模块 数据库建表脚本 | |
| 2 | +-- 数据库:dili_rider | |
| 3 | + | |
| 4 | +CREATE DATABASE IF NOT EXISTS dili_rider DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; | |
| 5 | +USE dili_rider; | |
| 6 | + | |
| 7 | +-- 骑手信息表 | |
| 8 | +CREATE TABLE `rider` ( | |
| 9 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '骑手ID', | |
| 10 | + `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 11 | + `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录名', | |
| 12 | + `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 13 | + `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 14 | + `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像', | |
| 15 | + `avatar_thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像缩略图', | |
| 16 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 17 | + `level_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '等级ID', | |
| 18 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=兼职 2=全职', | |
| 19 | + `user_status` TINYINT NOT NULL DEFAULT 2 COMMENT '审核状态:0=拒绝 1=通过 2=待审核', | |
| 20 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '账号状态:0=禁用 1=正常', | |
| 21 | + `balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '余额(兼职用)', | |
| 22 | + `frozen_balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '冻结余额(提现审核中)', | |
| 23 | + `is_rest` TINYINT NOT NULL DEFAULT 0 COMMENT '是否休息:0=否 1=是', | |
| 24 | + `hold_order_limit` INT NOT NULL DEFAULT 0 COMMENT '个人持单上限,0=不限制', | |
| 25 | + `id_no` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '身份证号', | |
| 26 | + `thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '手持身份证照片', | |
| 27 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '注册时间', | |
| 28 | + `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0=正常 1=已删除', | |
| 29 | + PRIMARY KEY (`id`), | |
| 30 | + UNIQUE KEY `uk_mobile` (`mobile`), | |
| 31 | + KEY `idx_city_id` (`city_id`) | |
| 32 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手信息表'; | |
| 33 | + | |
| 34 | +-- 骑手等级配置表 | |
| 35 | +CREATE TABLE `rider_level` ( | |
| 36 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 37 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 38 | + `level_id` INT NOT NULL DEFAULT 0 COMMENT '等级编号', | |
| 39 | + `name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '等级名称', | |
| 40 | + `is_default` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认', | |
| 41 | + `trans_nums` INT NOT NULL DEFAULT 0 COMMENT '每日转单次数上限', | |
| 42 | + `run_fee_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '跑腿收入模式:1=固定 2=比例 3=距离', | |
| 43 | + `run_fix_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '跑腿固定金额', | |
| 44 | + `run_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '跑腿比例(%)', | |
| 45 | + `distance_basic` INT NOT NULL DEFAULT 0 COMMENT '起始距离(米)', | |
| 46 | + `distance_basic_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基础配送费', | |
| 47 | + `distance_more_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '超出每公里费', | |
| 48 | + `distance_max_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '最高配送费上限', | |
| 49 | + `work_fee_mode` TINYINT NOT NULL DEFAULT 1 COMMENT '办事收入模式:1=固定 2=比例', | |
| 50 | + `work_fix_money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '办事固定金额', | |
| 51 | + `work_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '办事比例(%)', | |
| 52 | + PRIMARY KEY (`id`), | |
| 53 | + UNIQUE KEY `uk_city_level` (`city_id`, `level_id`), | |
| 54 | + KEY `idx_city_default` (`city_id`, `is_default`) | |
| 55 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手等级配置表'; | |
| 56 | + | |
| 57 | +-- 骑手实时位置表 | |
| 58 | +CREATE TABLE `rider_location` ( | |
| 59 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 60 | + `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 61 | + `lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '经度', | |
| 62 | + `lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '纬度', | |
| 63 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间', | |
| 64 | + PRIMARY KEY (`id`), | |
| 65 | + UNIQUE KEY `uk_uid` (`uid`) | |
| 66 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手实时位置表'; | |
| 67 | + | |
| 68 | +-- 骑手余额流水表 | |
| 69 | +CREATE TABLE `rider_balance` ( | |
| 70 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 71 | + `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 72 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=收入 2=提现', | |
| 73 | + `action` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '动作标识', | |
| 74 | + `action_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联ID', | |
| 75 | + `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 76 | + `nums` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '变动金额', | |
| 77 | + `total` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '变动后余额', | |
| 78 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '记录时间', | |
| 79 | + PRIMARY KEY (`id`), | |
| 80 | + KEY `idx_uid` (`uid`), | |
| 81 | + KEY `idx_action_id` (`action_id`) | |
| 82 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手余额流水表'; | |
| 83 | + | |
| 84 | +-- 骑手提现申请表 | |
| 85 | +CREATE TABLE `rider_withdraw_apply` ( | |
| 86 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 87 | + `withdraw_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '提现单号', | |
| 88 | + `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID', | |
| 89 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 90 | + `amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '提现金额', | |
| 91 | + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=待审核 1=审核通过待打款 2=审核拒绝 3=已打款 4=打款失败', | |
| 92 | + `account_type` TINYINT NOT NULL DEFAULT 1 COMMENT '收款账户类型:1=银行卡 2=支付宝 3=微信', | |
| 93 | + `account_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收款人', | |
| 94 | + `bank_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '开户行', | |
| 95 | + `bank_branch` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '开户支行', | |
| 96 | + `account_no` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '收款账号', | |
| 97 | + `apply_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '申请备注', | |
| 98 | + `audit_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '审核/打款备注', | |
| 99 | + `auditor_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核人ID', | |
| 100 | + `auditor_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '审核人名称', | |
| 101 | + `apply_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请时间', | |
| 102 | + `audit_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核时间', | |
| 103 | + `pay_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '打款时间', | |
| 104 | + `transfer_no` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '打款流水号', | |
| 105 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 106 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 107 | + PRIMARY KEY (`id`), | |
| 108 | + UNIQUE KEY `uk_withdraw_no` (`withdraw_no`), | |
| 109 | + KEY `idx_rider_status` (`rider_id`, `status`), | |
| 110 | + KEY `idx_city_status_time` (`city_id`, `status`, `apply_time`) | |
| 111 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手提现申请表'; | |
| 112 | + | |
| 113 | +-- 骑手订单统计表 | |
| 114 | +CREATE TABLE `rider_order_count` ( | |
| 115 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 116 | + `uid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 117 | + `count_date` INT NOT NULL COMMENT '统计日期yyyyMMdd', | |
| 118 | + `orders` INT NOT NULL DEFAULT 0 COMMENT '完成订单数', | |
| 119 | + `transfers` INT NOT NULL DEFAULT 0 COMMENT '转单数', | |
| 120 | + `distance` BIGINT NOT NULL DEFAULT 0 COMMENT '配送距离(米)', | |
| 121 | + PRIMARY KEY (`id`), | |
| 122 | + UNIQUE KEY `uk_uid_date` (`uid`, `count_date`) | |
| 123 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手订单统计表'; | |
| 124 | + | |
| 125 | +-- 骑手拒单记录表 | |
| 126 | +CREATE TABLE `rider_orders_refuse` ( | |
| 127 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 128 | + `rider_id` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 129 | + `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 130 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '拒单时间', | |
| 131 | + PRIMARY KEY (`id`), | |
| 132 | + KEY `idx_rider_id` (`rider_id`), | |
| 133 | + KEY `idx_oid` (`oid`) | |
| 134 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手拒单记录表'; | |
| 135 | + | |
| 136 | +-- 订单主表 | |
| 137 | +CREATE TABLE `orders` ( | |
| 138 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID', | |
| 139 | + `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 140 | + `uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '用户ID', | |
| 141 | + `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID', | |
| 142 | + `old_rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '原始骑手ID', | |
| 143 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 144 | + `type` TINYINT NOT NULL DEFAULT 6 COMMENT '订单类型:6=外卖配送', | |
| 145 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2已支付 3已接单 4服务中 6已完成 7退款申请 8退款成功 9退款拒绝 10已取消', | |
| 146 | + `pay_type` TINYINT NOT NULL DEFAULT 0 COMMENT '支付类型:1=支付宝 2=微信', | |
| 147 | + `money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额', | |
| 148 | + `money_delivery` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '配送费', | |
| 149 | + `money_total` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '实付总金额', | |
| 150 | + `rider_income` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '骑手收入', | |
| 151 | + `substation_income` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '站点收入', | |
| 152 | + `is_income` TINYINT NOT NULL DEFAULT 0 COMMENT '结算状态:0=未结算 1=待结算 2=已结算', | |
| 153 | + `is_trans` TINYINT NOT NULL DEFAULT 0 COMMENT '转单状态:0=未转 1=通过 2=申请中 3=拒绝', | |
| 154 | + `code` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '完成码', | |
| 155 | + `f_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '起点名称', | |
| 156 | + `f_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '起点地址', | |
| 157 | + `f_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '起点经度', | |
| 158 | + `f_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '起点纬度', | |
| 159 | + `t_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '终点名称', | |
| 160 | + `t_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '终点地址', | |
| 161 | + `t_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '终点经度', | |
| 162 | + `t_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '终点纬度', | |
| 163 | + `recip_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收件人姓名', | |
| 164 | + `recip_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '收件人电话', | |
| 165 | + `extra` TEXT COMMENT '附加信息JSON(距离、重量等)', | |
| 166 | + `thumbs` TEXT COMMENT '取件照片JSON数组', | |
| 167 | + `store_oid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺订单ID', | |
| 168 | + `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', | |
| 169 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '下单时间', | |
| 170 | + `pay_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '支付时间', | |
| 171 | + `grap_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '接单时间', | |
| 172 | + `pick_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '取件时间', | |
| 173 | + `arrive_shop_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手到店时间', | |
| 174 | + `arrive_shop_lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '骑手到店经度', | |
| 175 | + `arrive_shop_lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '骑手到店纬度', | |
| 176 | + `arrive_shop_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '骑手到店距离门店距离,单位米', | |
| 177 | + `complete_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '完成时间', | |
| 178 | + `trans_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '转单时间', | |
| 179 | + PRIMARY KEY (`id`), | |
| 180 | + UNIQUE KEY `uk_order_no` (`order_no`), | |
| 181 | + KEY `idx_rider_id` (`rider_id`), | |
| 182 | + KEY `idx_uid` (`uid`), | |
| 183 | + KEY `idx_city_status` (`city_id`, `status`), | |
| 184 | + KEY `idx_old_rider_trans` (`old_rider_id`, `is_trans`, `trans_time`) | |
| 185 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; | |
| 186 | + | |
| 187 | +-- 城市表(配送中台核心配置) | |
| 188 | +CREATE TABLE `city` ( | |
| 189 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '城市ID', | |
| 190 | + `pid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父级ID,0=省级', | |
| 191 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '城市名称', | |
| 192 | + `area_code` VARCHAR(16) NOT NULL DEFAULT '' COMMENT '行政区划码', | |
| 193 | + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未开通 1=已开通', | |
| 194 | + `rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '平台抽成比例(%)', | |
| 195 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 196 | + PRIMARY KEY (`id`), | |
| 197 | + KEY `idx_pid_order` (`pid`, `list_order`), | |
| 198 | + KEY `idx_area_code` (`area_code`) | |
| 199 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='城市表'; | |
| 200 | + | |
| 201 | +-- 配送计价方案主表 | |
| 202 | +CREATE TABLE `delivery_fee_plan` ( | |
| 203 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 204 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '租户ID', | |
| 205 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '方案名称', | |
| 206 | + `is_default` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认方案', | |
| 207 | + `min_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '保底费用', | |
| 208 | + `distance_basic` DECIMAL(10,2) NOT NULL DEFAULT 3.00 COMMENT '预计送达基础距离(km)', | |
| 209 | + `distance_basic_time` INT NOT NULL DEFAULT 30 COMMENT '预计送达基础时间(分钟)', | |
| 210 | + `distance_more_time` INT NOT NULL DEFAULT 10 COMMENT '预计送达超出每km增加时间(分钟)', | |
| 211 | + `rider_distance` DECIMAL(10,2) NOT NULL DEFAULT 3.00 COMMENT '附近骑手展示范围(km)', | |
| 212 | + `rider_time` INT NOT NULL DEFAULT 0 COMMENT '预计接单时间(分钟)', | |
| 213 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=启用', | |
| 214 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 215 | + `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 216 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 217 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 218 | + PRIMARY KEY (`id`), | |
| 219 | + KEY `idx_city_default` (`city_id`, `is_default`), | |
| 220 | + KEY `idx_city_status` (`city_id`, `status`) | |
| 221 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价方案主表'; | |
| 222 | + | |
| 223 | +-- 配送计价维度主配置表 | |
| 224 | +CREATE TABLE `delivery_fee_plan_dimension` ( | |
| 225 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 226 | + `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 227 | + `dimension_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '维度类型:base/distance/weight/piece/time', | |
| 228 | + `enabled` TINYINT NOT NULL DEFAULT 0 COMMENT '是否启用', | |
| 229 | + `base_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '基础费', | |
| 230 | + `start_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起步里程(km)', | |
| 231 | + `start_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起步费用', | |
| 232 | + `first_weight` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '首重(kg)', | |
| 233 | + `first_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '首重费用', | |
| 234 | + `unit_weight_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '续重单价', | |
| 235 | + `cap_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '封顶费用', | |
| 236 | + `extra_json` TEXT COMMENT '扩展配置', | |
| 237 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 238 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 239 | + PRIMARY KEY (`id`), | |
| 240 | + UNIQUE KEY `uk_plan_dimension` (`plan_id`, `dimension_type`) | |
| 241 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价维度主配置表'; | |
| 242 | + | |
| 243 | +-- 里程阶梯表 | |
| 244 | +CREATE TABLE `delivery_fee_plan_distance_step` ( | |
| 245 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 246 | + `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 247 | + `end_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '结束里程(km)', | |
| 248 | + `unit_distance` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '每档里程(km)', | |
| 249 | + `unit_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '每档加价', | |
| 250 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 251 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 252 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 253 | + PRIMARY KEY (`id`), | |
| 254 | + KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 255 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价里程阶梯表'; | |
| 256 | + | |
| 257 | +-- 件数区间表 | |
| 258 | +CREATE TABLE `delivery_fee_plan_piece_rule` ( | |
| 259 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 260 | + `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 261 | + `start_piece` INT NOT NULL DEFAULT 0 COMMENT '起始件数', | |
| 262 | + `end_piece` INT NOT NULL DEFAULT 0 COMMENT '结束件数', | |
| 263 | + `fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '费用', | |
| 264 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 265 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 266 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 267 | + PRIMARY KEY (`id`), | |
| 268 | + KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 269 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价件数区间表'; | |
| 270 | + | |
| 271 | +-- 时段附加费表 | |
| 272 | +CREATE TABLE `delivery_fee_plan_time_rule` ( | |
| 273 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 274 | + `plan_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '方案ID', | |
| 275 | + `start_minute` INT NOT NULL DEFAULT 0 COMMENT '开始分钟', | |
| 276 | + `end_minute` INT NOT NULL DEFAULT 0 COMMENT '结束分钟', | |
| 277 | + `fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '附加费', | |
| 278 | + `enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用', | |
| 279 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 280 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 281 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 282 | + PRIMARY KEY (`id`), | |
| 283 | + KEY `idx_plan_order` (`plan_id`, `list_order`) | |
| 284 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配送计价时段附加费表'; | |
| 285 | + | |
| 286 | +-- 分站管理员表(每城市一个,管理本城市骑手和订单) | |
| 287 | +CREATE TABLE `substation` ( | |
| 288 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分站ID', | |
| 289 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '管理城市ID', | |
| 290 | + `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录账号', | |
| 291 | + `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 292 | + `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 293 | + `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 294 | + `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像', | |
| 295 | + `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 296 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 297 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 298 | + PRIMARY KEY (`id`), | |
| 299 | + UNIQUE KEY `uk_user_login` (`user_login`) | |
| 300 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分站管理员表(一个城市/租户下可有多个管理员)'; | |
| 301 | + | |
| 302 | +-- 商家入驻申请表 | |
| 303 | +CREATE TABLE `merchant_enter` ( | |
| 304 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 305 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '联系人姓名', | |
| 306 | + `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', | |
| 307 | + `store_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '店铺名称', | |
| 308 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=商家入驻 2=骑手入驻 3=商务合作', | |
| 309 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID', | |
| 310 | + `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 311 | + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未处理 1=已通过 -1=已拒绝', | |
| 312 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请时间', | |
| 313 | + PRIMARY KEY (`id`), | |
| 314 | + KEY `idx_status_type` (`status`, `type`), | |
| 315 | + KEY `idx_city_id` (`city_id`) | |
| 316 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家入驻申请表'; | |
| 317 | + | |
| 318 | +-- 商家账号表 | |
| 319 | +CREATE TABLE `merchant_users` ( | |
| 320 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 321 | + `store_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺ID', | |
| 322 | + `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号(登录账号)', | |
| 323 | + `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 324 | + `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 325 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '类型:1=商家', | |
| 326 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 327 | + PRIMARY KEY (`id`), | |
| 328 | + UNIQUE KEY `uk_mobile` (`mobile`), | |
| 329 | + KEY `idx_store_id` (`store_id`) | |
| 330 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家账号表'; | |
| 331 | + | |
| 332 | +-- 商家店铺表 | |
| 333 | +CREATE TABLE `merchant_store` ( | |
| 334 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '店铺ID', | |
| 335 | + `name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '店铺名称', | |
| 336 | + `thumb` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '封面图', | |
| 337 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '所属城市ID', | |
| 338 | + `address` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '店铺地址', | |
| 339 | + `lng` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '经度', | |
| 340 | + `lat` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '纬度', | |
| 341 | + `operating_state` TINYINT NOT NULL DEFAULT 1 COMMENT '营业状态:0=打烊 1=营业', | |
| 342 | + `automatic_order` TINYINT NOT NULL DEFAULT 0 COMMENT '自动接单:0=否 1=是', | |
| 343 | + `shipping_type` TINYINT NOT NULL DEFAULT 1 COMMENT '配送类型:1=外卖配送 2=到店自提', | |
| 344 | + `free_shipping` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '免运费门槛,0=不免', | |
| 345 | + `up_to_send` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '起送金额,0=不限', | |
| 346 | + `open_date` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '营业日期JSON,如[1,2,3,4,5]', | |
| 347 | + `open_time` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '营业时间JSON,如["09:00","22:00"]', | |
| 348 | + `about` TEXT COMMENT '店铺简介', | |
| 349 | + `account_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联账号ID', | |
| 350 | + `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '接入方AppKey,为空=平台自建', | |
| 351 | + `out_store_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '接入方门店编号,用于推单时自动匹配', | |
| 352 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 353 | + `is_del` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除', | |
| 354 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 355 | + `sync_app_key` VARCHAR(32) GENERATED ALWAYS AS (NULLIF(`app_key`, '')) STORED COMMENT '外部同步唯一键AppKey', | |
| 356 | + `sync_out_store_id` VARCHAR(64) GENERATED ALWAYS AS (NULLIF(`out_store_id`, '')) STORED COMMENT '外部同步唯一键门店ID', | |
| 357 | + PRIMARY KEY (`id`), | |
| 358 | + KEY `idx_city_id` (`city_id`), | |
| 359 | + UNIQUE KEY `uk_app_out_store` (`sync_app_key`, `sync_out_store_id`, `is_del`), | |
| 360 | + KEY `idx_order_del` (`list_order`, `is_del`) | |
| 361 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商家店铺表'; | |
| 362 | + | |
| 363 | +-- 开放平台应用表 | |
| 364 | +CREATE TABLE `open_app` ( | |
| 365 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 366 | + `app_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '应用名称', | |
| 367 | + `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'AppKey', | |
| 368 | + `app_secret` VARCHAR(128) NOT NULL DEFAULT '' COMMENT 'AppSecret', | |
| 369 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联城市/租户ID(必填,租户隔离核心字段)', | |
| 370 | + `store_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联店铺ID,0=不限制', | |
| 371 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 372 | + `webhook_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'Webhook回调地址', | |
| 373 | + `webhook_events` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '订阅事件JSON数组', | |
| 374 | + `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', | |
| 375 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 376 | + PRIMARY KEY (`id`), | |
| 377 | + UNIQUE KEY `uk_app_key` (`app_key`), | |
| 378 | + KEY `idx_city_id` (`city_id`) | |
| 379 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放平台应用表'; | |
| 380 | + | |
| 381 | +-- Webhook 推送日志表 | |
| 382 | +CREATE TABLE `webhook_log` ( | |
| 383 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 384 | + `app_id` BIGINT UNSIGNED NOT NULL COMMENT '应用ID', | |
| 385 | + `event` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '事件类型', | |
| 386 | + `biz_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '业务ID', | |
| 387 | + `url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '推送URL', | |
| 388 | + `payload` TEXT COMMENT '推送内容JSON', | |
| 389 | + `response_code` INT NOT NULL DEFAULT 0 COMMENT 'HTTP响应码', | |
| 390 | + `response_body` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '响应内容', | |
| 391 | + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=失败 1=成功', | |
| 392 | + `retry_count` INT NOT NULL DEFAULT 0 COMMENT '重试次数', | |
| 393 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 394 | + PRIMARY KEY (`id`), | |
| 395 | + KEY `idx_app_event` (`app_id`, `event`), | |
| 396 | + KEY `idx_biz_id` (`biz_id`), | |
| 397 | + KEY `idx_status` (`status`) | |
| 398 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Webhook推送日志表'; | |
| 399 | + | |
| 400 | +-- 超级管理员表 | |
| 401 | +CREATE TABLE `admin_user` ( | |
| 402 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 403 | + `user_login` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '登录账号', | |
| 404 | + `user_pass` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码(MD5)', | |
| 405 | + `user_nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', | |
| 406 | + `user_status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 407 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单角色ID', | |
| 408 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 409 | + PRIMARY KEY (`id`), | |
| 410 | + UNIQUE KEY `uk_user_login` (`user_login`) | |
| 411 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='超级管理员表'; | |
| 412 | + | |
| 413 | +CREATE TABLE `sys_role` ( | |
| 414 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 415 | + `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色编码', | |
| 416 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '角色名称', | |
| 417 | + `role_scope` VARCHAR(32) NOT NULL DEFAULT 'PLATFORM' COMMENT '角色归属:PLATFORM/SUBSTATION', | |
| 418 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '所属租户ID:0=平台全局角色,>0=分站租户专属角色', | |
| 419 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 420 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 421 | + PRIMARY KEY (`id`), | |
| 422 | + UNIQUE KEY `uk_role_code` (`code`, `city_id`), | |
| 423 | + KEY `idx_city_scope` (`city_id`, `role_scope`) | |
| 424 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单角色表'; | |
| 425 | + | |
| 426 | +CREATE TABLE `sys_menu` ( | |
| 427 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 428 | + `code` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单编码', | |
| 429 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '菜单名称', | |
| 430 | + `type` VARCHAR(16) NOT NULL DEFAULT 'MENU' COMMENT '类型:DIR/MENU', | |
| 431 | + `path` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '前端路由路径', | |
| 432 | + `icon` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '前端图标名', | |
| 433 | + `parent_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父级菜单ID', | |
| 434 | + `menu_scope` VARCHAR(32) NOT NULL DEFAULT 'BOTH' COMMENT '菜单归属:PLATFORM/SUBSTATION/BOTH', | |
| 435 | + `list_order` INT NOT NULL DEFAULT 0 COMMENT '排序', | |
| 436 | + `visible` TINYINT NOT NULL DEFAULT 1 COMMENT '是否显示:0=否 1=是', | |
| 437 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=正常', | |
| 438 | + KEY `idx_scope_parent` (`menu_scope`, `parent_id`, `list_order`), | |
| 439 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 440 | + PRIMARY KEY (`id`), | |
| 441 | + UNIQUE KEY `uk_menu_code` (`code`), | |
| 442 | + KEY `idx_parent_order` (`parent_id`, `list_order`) | |
| 443 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台菜单表'; | |
| 444 | + | |
| 445 | +CREATE TABLE `sys_role_menu` ( | |
| 446 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 447 | + `role_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '角色ID', | |
| 448 | + `menu_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '菜单ID', | |
| 449 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 450 | + PRIMARY KEY (`id`), | |
| 451 | + UNIQUE KEY `uk_role_menu` (`role_id`, `menu_id`) | |
| 452 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='后台角色菜单关系表'; | |
| 453 | + | |
| 454 | +-- orders 表补充字段(如已有 orders 表,执行以下 ALTER) | |
| 455 | +ALTER TABLE `orders` ADD COLUMN `out_order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '外部系统订单号' AFTER `order_no`; | |
| 456 | +ALTER TABLE `orders` ADD COLUMN `app_key` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '接入方AppKey' AFTER `out_order_no`; | |
| 457 | +ALTER TABLE `orders` ADD COLUMN `callback_url` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '状态回调地址' AFTER `app_key`; | |
| 458 | +ALTER TABLE `orders` ADD INDEX `idx_app_out_order` (`app_key`, `out_order_no`); | |
| 459 | + | |
| 460 | +-- 骑手评价表 | |
| 461 | +CREATE TABLE `rider_evaluate` ( | |
| 462 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 463 | + `uid` BIGINT UNSIGNED NOT NULL COMMENT '评价用户ID', | |
| 464 | + `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 465 | + `rid` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 466 | + `content` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '评价内容', | |
| 467 | + `star` TINYINT NOT NULL DEFAULT 5 COMMENT '星级1-5', | |
| 468 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 469 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 470 | + PRIMARY KEY (`id`), | |
| 471 | + UNIQUE KEY `uk_uid_oid` (`uid`, `oid`), | |
| 472 | + KEY `idx_rid` (`rid`) | |
| 473 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手评价表'; | |
| 474 | + | |
| 475 | +-- 退款原因配置表 | |
| 476 | +CREATE TABLE `orders_refund_reason` ( | |
| 477 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 478 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '原因描述', | |
| 479 | + `role` TINYINT NOT NULL DEFAULT 1 COMMENT '1=用户 2=骑手', | |
| 480 | + `list_order` INT NOT NULL DEFAULT 0, | |
| 481 | + PRIMARY KEY (`id`) | |
| 482 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款原因配置表'; | |
| 483 | + | |
| 484 | +-- 退款申请记录表 | |
| 485 | +CREATE TABLE `orders_refund_record` ( | |
| 486 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 487 | + `oid` BIGINT UNSIGNED NOT NULL COMMENT '订单ID', | |
| 488 | + `order_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单号', | |
| 489 | + `uid` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '申请人ID', | |
| 490 | + `role` TINYINT NOT NULL DEFAULT 1 COMMENT '1=用户 2=骑手', | |
| 491 | + `reason_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 492 | + `reason` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '退款原因', | |
| 493 | + `money` DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '退款金额', | |
| 494 | + `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0=待处理 1=通过 2=拒绝', | |
| 495 | + `remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '处理备注', | |
| 496 | + `add_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 497 | + `handle_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 498 | + PRIMARY KEY (`id`), | |
| 499 | + KEY `idx_oid` (`oid`), | |
| 500 | + KEY `idx_status` (`status`) | |
| 501 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款申请记录表'; | |
| 502 | + | |
| 503 | +-- 退款原因初始数据 | |
| 504 | +INSERT INTO `orders_refund_reason` (`name`, `role`, `list_order`) VALUES | |
| 505 | +('骑手长时间未接单', 1, 1), | |
| 506 | +('骑手态度恶劣', 1, 2), | |
| 507 | +('物品损坏', 1, 3), | |
| 508 | +('其他原因', 1, 99), | |
| 509 | +('用户恶意单', 2, 1), | |
| 510 | +('无法完成配送', 2, 2), | |
| 511 | +('其他原因', 2, 99); | |
| 512 | + | |
| 513 | +-- orders 表补充货物快照字段 | |
| 514 | +ALTER TABLE `orders` ADD COLUMN `items_json` TEXT COMMENT '货物清单快照JSON' AFTER `callback_url`; | |
| 515 | +ALTER TABLE `orders` ADD COLUMN `item_remark` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '整单货物备注' AFTER `items_json`; | |
| 516 | + | |
| 517 | +-- orders 表补充调度字段 | |
| 518 | +ALTER TABLE `orders` ADD COLUMN `dispatch_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '系统派单时间' AFTER `trans_time`; | |
| 519 | +ALTER TABLE `orders` ADD COLUMN `dispatch_rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '系统指派骑手ID' AFTER `dispatch_time`; | |
| 520 | + | |
| 521 | +-- rider 表补充个人持单上限与评分统计字段 | |
| 522 | +ALTER TABLE `rider` ADD COLUMN `hold_order_limit` INT NOT NULL DEFAULT 0 COMMENT '个人持单上限,0=不限制' AFTER `is_rest`; | |
| 523 | +ALTER TABLE `rider` ADD COLUMN `star_total` INT NOT NULL DEFAULT 0 COMMENT '评分总分' AFTER `thumb`; | |
| 524 | +ALTER TABLE `rider` ADD COLUMN `star_count` INT NOT NULL DEFAULT 0 COMMENT '评分次数' AFTER `star_total`; | |
| 525 | + | |
| 526 | +-- 调度规则模板表 | |
| 527 | +CREATE TABLE `dispatch_rule_template` ( | |
| 528 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 529 | + `city_id` BIGINT UNSIGNED NOT NULL COMMENT '城市ID', | |
| 530 | + `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '模板名称', | |
| 531 | + `is_active` TINYINT NOT NULL DEFAULT 0 COMMENT '是否当前生效:0=否 1=是', | |
| 532 | + `grab_enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '抢单模式启用', | |
| 533 | + `grab_timeout` INT NOT NULL DEFAULT 30 COMMENT '抢单超时分钟数,超时后转自动派单', | |
| 534 | + `grab_scope` TINYINT NOT NULL DEFAULT 1 COMMENT '抢单可见范围:1=订单所属区域骑手 2=全部自营骑手', | |
| 535 | + `grab_max_per_rider` INT NOT NULL DEFAULT 3 COMMENT '单人最大同时持单量', | |
| 536 | + `auto_dispatch` TINYINT NOT NULL DEFAULT 1 COMMENT '同步开启自动派单:0=否 1=是', | |
| 537 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 538 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 539 | + PRIMARY KEY (`id`), | |
| 540 | + KEY `idx_city_active` (`city_id`, `is_active`) | |
| 541 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='调度规则模板表'; | |
| 542 | + | |
| 543 | +-- 派单优先级条件表 | |
| 544 | +CREATE TABLE `dispatch_rule_condition` ( | |
| 545 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 546 | + `template_id` BIGINT UNSIGNED NOT NULL COMMENT '所属模板ID', | |
| 547 | + `condition_type` VARCHAR(32) NOT NULL COMMENT '条件类型', | |
| 548 | + `enabled` TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用', | |
| 549 | + `threshold_value` DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '阈值', | |
| 550 | + `sort_order` INT NOT NULL DEFAULT 0 COMMENT '优先级排序(小的优先)', | |
| 551 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 552 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 553 | + PRIMARY KEY (`id`), | |
| 554 | + UNIQUE KEY `uk_template_type` (`template_id`, `condition_type`), | |
| 555 | + KEY `idx_template_order` (`template_id`, `sort_order`) | |
| 556 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='派单优先级条件表'; | |
| 557 | + | |
| 558 | +-- 骑手消息表 | |
| 559 | +CREATE TABLE `rider_message` ( | |
| 560 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '消息ID', | |
| 561 | + `city_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '城市ID(多租户隔离)', | |
| 562 | + `rider_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '骑手ID,0表示全员消息', | |
| 563 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '消息类型:1=订单消息 2=系统通知', | |
| 564 | + `title` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '消息标题', | |
| 565 | + `content` TEXT NOT NULL COMMENT '消息内容', | |
| 566 | + `biz_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型:order_assigned/order_timeout/system_notice', | |
| 567 | + `biz_id` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联业务ID(如订单ID)', | |
| 568 | + `extra_data` TEXT COMMENT '扩展数据(JSON格式)', | |
| 569 | + `is_read` TINYINT NOT NULL DEFAULT 0 COMMENT '已读状态:0=未读 1=已读', | |
| 570 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | |
| 571 | + `read_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时间', | |
| 572 | + PRIMARY KEY (`id`), | |
| 573 | + KEY `idx_rider_type` (`rider_id`, `type`, `create_time`), | |
| 574 | + KEY `idx_city_create` (`city_id`, `create_time`), | |
| 575 | + KEY `idx_biz` (`biz_type`, `biz_id`) | |
| 576 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手消息表'; | |
| 577 | + | |
| 578 | +-- 消息模板表 | |
| 579 | +CREATE TABLE `rider_message_template` ( | |
| 580 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 581 | + `biz_type` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '业务类型标识', | |
| 582 | + `type` TINYINT NOT NULL DEFAULT 1 COMMENT '消息类型:1=订单 2=系统', | |
| 583 | + `title_template` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '标题模板,支持 #{key} 占位符', | |
| 584 | + `content_template` TEXT NOT NULL COMMENT '内容模板,支持 #{key} 占位符', | |
| 585 | + `is_push` TINYINT NOT NULL DEFAULT 1 COMMENT '是否实时推送:0=否 1=是', | |
| 586 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=禁用 1=启用', | |
| 587 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 588 | + PRIMARY KEY (`id`), | |
| 589 | + UNIQUE KEY `uk_biz_type` (`biz_type`) | |
| 590 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息模板表'; | |
| 591 | + | |
| 592 | +-- 初始化模板数据 | |
| 593 | +INSERT INTO `rider_message_template` (`biz_type`, `type`, `title_template`, `content_template`, `is_push`) VALUES | |
| 594 | +('order_assigned', 1, '新订单', '您有新的配送订单 #{orderNo},请及时处理', 1), | |
| 595 | +('order_timeout', 1, '订单超时提醒', '订单 #{orderNo} 即将超时,请尽快送达', 1), | |
| 596 | +('order_cancelled', 1, '订单取消', '订单 #{orderNo} 已被取消', 1), | |
| 597 | +('system_notice', 2, '系统通知', '#{content}', 1), | |
| 598 | +('level_upgrade', 2, '等级提升', '恭喜您,等级已提升至 #{levelName}', 1), | |
| 599 | +('balance_change', 2, '余额变动', '您的余额发生变动,当前余额:#{balance} 元', 0); | |
| 600 | + | |
| 601 | +-- 骑手设备推送绑定表(极光推送) | |
| 602 | +CREATE TABLE `rider_device` ( | |
| 603 | + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, | |
| 604 | + `rider_id` BIGINT UNSIGNED NOT NULL COMMENT '骑手ID', | |
| 605 | + `registration_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'JPush 设备注册ID', | |
| 606 | + `platform` TINYINT NOT NULL DEFAULT 0 COMMENT '平台:1=Android 2=iOS', | |
| 607 | + `device_info` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备信息(型号/系统版本)', | |
| 608 | + `app_version` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'App 版本', | |
| 609 | + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:0=已解绑 1=正常', | |
| 610 | + `last_active_time` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '最近活跃时间', | |
| 611 | + `create_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 612 | + `update_time` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| 613 | + PRIMARY KEY (`id`), | |
| 614 | + UNIQUE KEY `uk_registration_id` (`registration_id`), | |
| 615 | + KEY `idx_rider_status` (`rider_id`, `status`) | |
| 616 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='骑手设备推送绑定表'; | ... | ... |