Browse Source

对接拼团数据1

master
wangfukang 1 week ago
parent
commit
15d0c90edc
  1. 9
      hiver-admin/src/main/resources/application.yml
  2. 16
      hiver-admin/test-output/test-report.html
  3. 2
      hiver-core/src/main/java/cc/hiver/core/common/constant/DealingsRecordConstant.java
  4. 36
      hiver-core/src/main/java/cc/hiver/core/serviceimpl/JPushServiceImpl.java
  5. 46
      hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java
  6. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminDataStatisticsController.java
  7. 39
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopAreaController.java
  8. 71
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java
  9. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/WorkerRelaPriceMapper.java
  10. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java
  11. 15
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/ShopArea.java
  12. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/ShopTakeaway.java
  13. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeAdminController.java
  14. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeRealNameAuthController.java
  15. 14
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRealNameAuthService.java
  16. 13
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRealNameAuthServiceImpl.java
  17. 16
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java
  18. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/MerchantSettlementTask.java
  19. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/PlanetAdventureSettleTask.java
  20. 10
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopAreaService.java
  21. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderGroupService.java
  22. 37
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopAreaServiceImpl.java
  23. 53
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java
  24. 9
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  25. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
  26. 4
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallSettlementRecordServiceImpl.java
  27. 2
      hiver-modules/hiver-mall/src/main/resources/db/mall_order_is_virtual_group.sql
  28. 2
      hiver-modules/hiver-mall/src/main/resources/db/shop_area_area_kind.sql
  29. 4
      hiver-modules/hiver-mall/src/main/resources/db/shop_area_geo.sql
  30. 2
      hiver-modules/hiver-mall/src/main/resources/db/shop_takeaway_virtual_group_commission.sql
  31. 5
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml
  32. 10
      hiver-modules/hiver-mall/src/main/resources/mapper/ShopTakeawayMapper.xml
  33. 11
      hiver-modules/hiver-mall/src/main/resources/mapper/WorkerRelaPriceMapper.xml

9
hiver-admin/src/main/resources/application.yml

@ -403,8 +403,17 @@ ignored:
- /hiver/mall/order/getOrdersByUserId/** - /hiver/mall/order/getOrdersByUserId/**
- /hiver/mall/delivery/page - /hiver/mall/delivery/page
- /hiver/app/shopArea/getByParentId/** - /hiver/app/shopArea/getByParentId/**
- /hiver/app/shopArea/matchByLocation
- /hiver/mall/delivery/countOrderByStatus - /hiver/mall/delivery/countOrderByStatus
- /hiver/mall/admin/orderGroup/page
- /hiver/mall/order/countByShop/** - /hiver/mall/order/countByShop/**
- /hiver/app/shop/getByCondition
- /hiver/app/campus/categories
- /hiver/app/campus/posts/page
- /hiver/app/fishmarket/goods/page
- /hiver/app/fishmarket/categories
- /hiver/mall/admin/seckillGroup/baseDeliveryFee/get
- /hiver/app/ie/real-name/audit-setting
# 选择物流公司 # 选择物流公司
- /hiver/app/logitics/chooseCompany - /hiver/app/logitics/chooseCompany
# 根据客户id分页获取销售历史(按商品) # 根据客户id分页获取销售历史(按商品)

16
hiver-admin/test-output/test-report.html

@ -35,7 +35,7 @@
<a href="#"><span class="badge badge-primary">Hiver</span></a> <a href="#"><span class="badge badge-primary">Hiver</span></a>
</li> </li>
<li class="m-r-10"> <li class="m-r-10">
<a href="#"><span class="badge badge-primary">九月 02, 2026 15:11:22</span></a> <a href="#"><span class="badge badge-primary">九月 09, 2026 10:15:14</span></a>
</li> </li>
</ul> </ul>
</div> </div>
@ -84,7 +84,7 @@
<div class="test-detail"> <div class="test-detail">
<span class="meta text-white badge badge-sm"></span> <span class="meta text-white badge badge-sm"></span>
<p class="name">passTest</p> <p class="name">passTest</p>
<p class="text-sm"><span>15:11:23 下</span> / <span>0.028 secs</span></p> <p class="text-sm"><span>10:15:14 上</span> / <span>0.021 secs</span></p>
</div> </div>
<div class="test-contents d-none"> <div class="test-contents d-none">
<div class="detail-head"> <div class="detail-head">
@ -92,9 +92,9 @@
<div class="info"> <div class="info">
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div> <div class='float-right'><span class='badge badge-default'>#test-id=1</span></div>
<h5 class="test-status text-pass">passTest</h5> <h5 class="test-status text-pass">passTest</h5>
<span class='badge badge-success'>09.02.2026 15:11:23</span> <span class='badge badge-success'>09.09.2026 10:15:14</span>
<span class='badge badge-danger'>09.02.2026 15:11:23</span> <span class='badge badge-danger'>09.09.2026 10:15:14</span>
<span class='badge badge-default'>0.028 secs</span> <span class='badge badge-default'>0.021 secs</span>
</div> </div>
<div class="m-t-10 m-l-5"></div> <div class="m-t-10 m-l-5"></div>
</div> </div>
@ -104,7 +104,7 @@
<tbody> <tbody>
<tr class="event-row"> <tr class="event-row">
<td><span class="badge log pass-bg">Pass</span></td> <td><span class="badge log pass-bg">Pass</span></td>
<td>15:11:23</td> <td>10:15:14</td>
<td> <td>
Test passed Test passed
</td> </td>
@ -128,13 +128,13 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Started</p> <p class="m-b-0">Started</p>
<h3>九月 02, 2026 15:11:22</h3> <h3>九月 09, 2026 10:15:14</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="card"><div class="card-body"> <div class="card"><div class="card-body">
<p class="m-b-0">Ended</p> <p class="m-b-0">Ended</p>
<h3>九月 02, 2026 15:11:23</h3> <h3>九月 09, 2026 10:15:14</h3>
</div></div> </div></div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">

2
hiver-core/src/main/java/cc/hiver/core/common/constant/DealingsRecordConstant.java

@ -15,7 +15,7 @@ public interface DealingsRecordConstant {
/** /**
* *
*/ */
String[] TYPE_REMARK = {"配送订单佣金","售后退款","配送排名奖励","商家提现","配送员提现","免单奖励","商家结算","用户提现","白嫖中奖","学院排位赛排名奖励","配送员原因退款扣款","商家原因退款补发配送佣金","商家退款/售后扣款","二级代理返佣"}; String[] TYPE_REMARK = {"配送订单佣金","售后退款","配送排名奖励","商家提现","配送员提现","免单奖励","商家结算","用户提现","白嫖中奖","排位赛排名奖励","配送员原因退款扣款","商家原因退款补发配送佣金","商家退款/售后扣款","二级代理返佣"};
/** /**
*交易类型0:增加 1 提现 2消费 3售后 *交易类型0:增加 1 提现 2消费 3售后

36
hiver-core/src/main/java/cc/hiver/core/serviceimpl/JPushServiceImpl.java

@ -4,9 +4,15 @@ import cc.hiver.core.service.JPushService;
import cn.jpush.api.JPushClient; import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult; import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message; import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload; import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification; import cn.jpush.api.push.model.notification.Notification;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@ -22,6 +28,9 @@ public class JPushServiceImpl implements JPushService {
@Autowired @Autowired
private JPushClient jPushClient; private JPushClient jPushClient;
@Value("${jpush.apnsProduction:false}")
private Boolean apnsProduction;
private final ScheduledExecutorService pushScheduler = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService pushScheduler = Executors.newSingleThreadScheduledExecutor();
@Autowired @Autowired
@ -43,16 +52,35 @@ public class JPushServiceImpl implements JPushService {
private void doSendPushNotification(String registrationId, String message, String orderId) { private void doSendPushNotification(String registrationId, String message, String orderId) {
try { try {
String timestamp = String.valueOf(System.currentTimeMillis());
PushPayload payload = PushPayload.newBuilder() PushPayload payload = PushPayload.newBuilder()
.setPlatform(cn.jpush.api.push.model.Platform.all()) .setPlatform(Platform.all())
.setAudience(cn.jpush.api.push.model.audience.Audience.registrationId(registrationId)) .setAudience(Audience.registrationId(registrationId))
.setNotification(Notification.alert(message)) .setNotification(Notification.newBuilder()
.addPlatformNotification(AndroidNotification.newBuilder()
.setAlert(message)
.addExtra("type", "订单通知")
.addExtra("order_id", orderId)
.addExtra("timestamp", timestamp)
.build())
.addPlatformNotification(IosNotification.newBuilder()
.setAlert(message)
.setSound("default")
.incrBadge(1)
.addExtra("type", "订单通知")
.addExtra("order_id", orderId)
.addExtra("timestamp", timestamp)
.build())
.build())
.setMessage(Message.newBuilder() .setMessage(Message.newBuilder()
.setMsgContent("消息内容") .setMsgContent("消息内容")
.addExtra("custom_key", "custom_value") // 额外参数 .addExtra("custom_key", "custom_value") // 额外参数
.addExtra("type", "订单通知") .addExtra("type", "订单通知")
.addExtra("order_id",orderId) .addExtra("order_id",orderId)
.addExtra("timestamp", System.currentTimeMillis()) .addExtra("timestamp", timestamp)
.build())
.setOptions(Options.newBuilder()
.setApnsProduction(Boolean.TRUE.equals(apnsProduction))
.build()) .build())
.build(); .build();
PushResult result = jPushClient.sendPush(payload); PushResult result = jPushClient.sendPush(payload);

46
hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java

@ -335,6 +335,52 @@ public class AuthController {
return ResultUtil.data(result); return ResultUtil.data(result);
} }
@RequestMapping(value = "/updateClientId", method = RequestMethod.POST)
@ApiOperation("更新当前登录用户推送ID")
public Result updateClientId(@RequestParam(value = "clientId", required = false) String clientId) {
if (StringUtils.isEmpty(clientId)) {
return ResultUtil.error("clientId不能为空");
}
final User user = securityUtil.getCurrUser();
if (user == null || StringUtils.isEmpty(user.getId())) {
return ResultUtil.error("用户未登录");
}
updateClientIdByUserId(user.getId(), clientId);
return ResultUtil.success("修改成功");
}
private void updateClientIdByUserId(String userId, String clientId) {
if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(clientId)) {
return;
}
List<Shop> shop = shopService.getShopByUserid(userId);
if (shop != null && !shop.isEmpty()) {
for (Shop item : shop) {
item.setClientId(clientId);
shopService.update(item);
shopService.refreshShopCache(item.getId(), item.getRegionId());
}
}
Worker worker = workerService.findByUserId(userId);
if (worker != null) {
worker.setClientId(clientId);
workerService.update(worker);
String oldRegion = worker.getRegion();
WorkerRedisVo workerRedisVo = StrUtil.isNotBlank(oldRegion) ? workerRedisCacheUtil.get(oldRegion, worker.getWorkerId()) : null;
if (workerRedisVo != null) {
workerRedisVo.setWorker(worker);
if (Objects.equals(oldRegion, worker.getRegion())) {
workerRedisCacheUtil.update(worker.getRegion(), workerRedisVo);
} else {
workerRedisCacheUtil.remove(oldRegion, worker.getWorkerId());
if (StrUtil.isNotBlank(worker.getRegion())) {
workerRedisCacheUtil.put(worker.getRegion(), workerRedisVo);
}
}
}
}
}
@RequestMapping(value = "/getLoginQRCode", method = RequestMethod.GET) @RequestMapping(value = "/getLoginQRCode", method = RequestMethod.GET)
@ApiOperation("获取扫码登录二维码") @ApiOperation("获取扫码登录二维码")
@RateLimiter(rate = 1, ipLimit = true) @RateLimiter(rate = 1, ipLimit = true)

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/AdminDataStatisticsController.java

@ -46,7 +46,7 @@ public class AdminDataStatisticsController {
private static final BigDecimal CONTENT_AUDIT_PACKAGE_PRICE = new BigDecimal("0.05"); private static final BigDecimal CONTENT_AUDIT_PACKAGE_PRICE = new BigDecimal("0.05");
private static final BigDecimal REAL_NAME_UNIT_PRICE = BigDecimal.ONE; private static final BigDecimal REAL_NAME_UNIT_PRICE = BigDecimal.ONE;
private static final String REWARD_COLLEGE_RANK = "学院排位赛排名奖励"; private static final String REWARD_COLLEGE_RANK = "排位赛排名奖励";
private static final String REWARD_DELIVERY_RANK = "配送排名奖励"; private static final String REWARD_DELIVERY_RANK = "配送排名奖励";
private static final String REWARD_DRAW_POOL = "白嫖中奖"; private static final String REWARD_DRAW_POOL = "白嫖中奖";

39
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/ShopAreaController.java

@ -272,6 +272,20 @@ public class ShopAreaController {
return new ResultUtil<List<ShopArea>>().setData(list); return new ResultUtil<List<ShopArea>>().setData(list);
} }
@RequestMapping(value = "/matchByLocation", method = RequestMethod.GET)
@ApiOperation(value = "根据经纬度匹配一级区域")
public Result<ShopArea> matchByLocation(@ApiParam(value = "纬度", required = true) @RequestParam Double lat,
@ApiParam(value = "经度", required = true) @RequestParam Double lng) {
if (lat == null || lng == null) {
return ResultUtil.error("经纬度不能为空");
}
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {
return ResultUtil.error("经纬度不合法");
}
ShopArea matched = shopAreaService.matchByLocation(lat, lng);
return new ResultUtil<ShopArea>().setData(matched);
}
public void setInfo(List<ShopArea> list) { public void setInfo(List<ShopArea> list) {
// lambda表达式 // lambda表达式
list.forEach(item -> { list.forEach(item -> {
@ -290,16 +304,35 @@ public class ShopAreaController {
shopArea.setParentAgentId(null); shopArea.setParentAgentId(null);
shopArea.setParentAgentName(null); shopArea.setParentAgentName(null);
shopArea.setCommissionRatio(0); shopArea.setCommissionRatio(0);
shopArea.setCenterLng(null);
shopArea.setCenterLat(null);
shopArea.setRadiusMeters(null);
shopArea.setAreaKind(1);
return null; return null;
} }
Integer areaKind = shopArea.getAreaKind();
if (areaKind == null) {
shopArea.setAreaKind(1);
} else if (areaKind != 1 && areaKind != 2) {
return ResultUtil.error("区域类型仅支持:1校园区域,2社会区域");
}
Integer commissionRatio = shopArea.getCommissionRatio(); Integer commissionRatio = shopArea.getCommissionRatio();
if (commissionRatio == null) { if (commissionRatio == null) {
shopArea.setCommissionRatio(0); shopArea.setCommissionRatio(0);
return null; } else if (commissionRatio < 0 || commissionRatio > 100) {
}
if (commissionRatio < 0 || commissionRatio > 100) {
return ResultUtil.error("返佣比例需为0或1-100之间的整数"); return ResultUtil.error("返佣比例需为0或1-100之间的整数");
} }
boolean hasLng = shopArea.getCenterLng() != null;
boolean hasLat = shopArea.getCenterLat() != null;
boolean hasRadius = shopArea.getRadiusMeters() != null;
if (hasLng || hasLat || hasRadius) {
if (!hasLng || !hasLat) {
return ResultUtil.error("区域中心经纬度需同时设置");
}
if (!hasRadius || shopArea.getRadiusMeters() <= 0) {
return ResultUtil.error("匹配半径需为正整数(米)");
}
}
return null; return null;
} }
} }

71
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java

@ -242,7 +242,7 @@ public class WorkerController {
@ApiOperation(value = "修改配送员信息-APP", notes = "需要通过下单编号获取订单信息") @ApiOperation(value = "修改配送员信息-APP", notes = "需要通过下单编号获取订单信息")
public Result editApp(WorkerAppVO u) { public Result editApp(WorkerAppVO u) {
//修改配送员信息时如涉及到充值押金情况,需要更新余额且重置应缴 //修改配送员信息时如涉及到充值押金情况,需要更新余额且重置应缴
Worker worker = workerService.get(u.getWorkerId()); Worker worker = workerService.findByWorkerId(u.getWorkerId());
if (u.getDepoNum() != null && u.getDepoNum().compareTo(BigDecimal.ZERO) > 0) { if (u.getDepoNum() != null && u.getDepoNum().compareTo(BigDecimal.ZERO) > 0) {
Recharge recharge = new Recharge(); Recharge recharge = new Recharge();
@ -268,7 +268,7 @@ public class WorkerController {
@RequestMapping(value = "/admin/disable/{workerId}", method = RequestMethod.POST) @RequestMapping(value = "/admin/disable/{workerId}", method = RequestMethod.POST)
@ApiOperation(value = "后台人工禁用配送员") @ApiOperation(value = "后台人工禁用配送员")
public Result disable(@ApiParam("用户唯一id标识") @PathVariable String workerId) { public Result disable(@ApiParam("用户唯一id标识") @PathVariable String workerId) {
Worker worker = workerService.get(workerId); Worker worker = workerService.findByWorkerId(workerId);
if (worker == null) { if (worker == null) {
return ResultUtil.error("配送员不存在"); return ResultUtil.error("配送员不存在");
} }
@ -284,7 +284,7 @@ public class WorkerController {
@RequestMapping(value = "/admin/enable/{workerId}", method = RequestMethod.POST) @RequestMapping(value = "/admin/enable/{workerId}", method = RequestMethod.POST)
@ApiOperation(value = "后台人工启用配送员") @ApiOperation(value = "后台人工启用配送员")
public Result enable(@ApiParam("配送员唯一id标识") @PathVariable String workerId) { public Result enable(@ApiParam("配送员唯一id标识") @PathVariable String workerId) {
Worker worker = workerService.get(workerId); Worker worker = workerService.findByWorkerId(workerId);
if (worker == null) { if (worker == null) {
return ResultUtil.error("配送员不存在"); return ResultUtil.error("配送员不存在");
} }
@ -300,7 +300,8 @@ public class WorkerController {
@ApiOperation(value = "后台设置配送员全职状态") @ApiOperation(value = "后台设置配送员全职状态")
public Result changeFullTime(@ApiParam("配送员唯一id标识") @PathVariable String workerId, public Result changeFullTime(@ApiParam("配送员唯一id标识") @PathVariable String workerId,
@RequestParam("isFullTime") Integer isFullTime) { @RequestParam("isFullTime") Integer isFullTime) {
Worker worker = workerService.get(workerId); // 注意:不要用 workerService.get()(底层 getById 返回懒加载代理,仅有主键)
Worker worker = workerService.findByWorkerId(workerId);
if (worker == null) { if (worker == null) {
return ResultUtil.error("配送员不存在"); return ResultUtil.error("配送员不存在");
} }
@ -308,7 +309,12 @@ public class WorkerController {
return ResultUtil.error("全职状态参数错误"); return ResultUtil.error("全职状态参数错误");
} }
workerService.changeFullTime(workerId, isFullTime); workerService.changeFullTime(workerId, isFullTime);
// 批量更新后重新查询完整对象,避免把残缺数据写入配送员缓存
worker = workerService.findByWorkerId(workerId);
worker.setIsFullTime(isFullTime); worker.setIsFullTime(isFullTime);
if (worker == null) {
return ResultUtil.error("配送员不存在");
}
if (StrUtil.isNotBlank(worker.getRegion())) { if (StrUtil.isNotBlank(worker.getRegion())) {
WorkerRedisVo workerRedisVo = workerRedisCacheUtil.get(worker.getRegion(), worker.getWorkerId()); WorkerRedisVo workerRedisVo = workerRedisCacheUtil.get(worker.getRegion(), worker.getWorkerId());
if (workerRedisVo != null) { if (workerRedisVo != null) {
@ -331,7 +337,7 @@ public class WorkerController {
@ApiOperation(value = "配送员上线") @ApiOperation(value = "配送员上线")
public Result<Object> onLine(@RequestParam("id") String id) { public Result<Object> onLine(@RequestParam("id") String id) {
try { try {
Worker worker = workerService.get(id); Worker worker = workerService.findByWorkerId(id);
if (worker == null) { if (worker == null) {
return ResultUtil.error("配送员不存在"); return ResultUtil.error("配送员不存在");
} }
@ -405,6 +411,11 @@ public class WorkerController {
* orderBkge -> 配送佣金正序 * orderBkge -> 配送佣金正序
*/ */
private String sortField; private String sortField;
/**
* 是否后台查询仅后台管理系统传 true小程序不传
* true 优先按取收货区域规则匹配未匹配到则补充返回全职且上线的配送员
*/
private Boolean fromAdmin;
/** 页码(从1开始),默认1 */ /** 页码(从1开始),默认1 */
private Integer pageNum = 1; private Integer pageNum = 1;
/** 每页条数,默认10 */ /** 每页条数,默认10 */
@ -461,27 +472,34 @@ public class WorkerController {
} }
// ---- 配送规则匹配:shopAreaId + putAreaId + orderType + getPushOrder ---- // ---- 配送规则匹配:shopAreaId + putAreaId + orderType + getPushOrder ----
if (vo.getWorkerRelaPriceList() == null || vo.getWorkerRelaPriceList().isEmpty()) continue;
WorkerRelaPrice matchedRule = null; WorkerRelaPrice matchedRule = null;
for (WorkerRelaPrice rp : vo.getWorkerRelaPriceList()) { if (vo.getWorkerRelaPriceList() != null && !vo.getWorkerRelaPriceList().isEmpty()) {
// 缓存可能残留旧数据,规则也要限定当前校区 for (WorkerRelaPrice rp : vo.getWorkerRelaPriceList()) {
if (StrUtil.isNotBlank(dto.getRegionId()) && !dto.getRegionId().equals(rp.getRegionId())) continue; // 缓存可能残留旧数据,规则也要限定当前校区
// 规则必须接收指派 if (StrUtil.isNotBlank(dto.getRegionId()) && !dto.getRegionId().equals(rp.getRegionId())) continue;
if (rp.getGetPushOrder() == null || rp.getGetPushOrder() != 1) continue; // 规则必须接收指派
// 取货区域匹配 if (rp.getGetPushOrder() == null || rp.getGetPushOrder() != 1) continue;
if (StrUtil.isNotBlank(dto.getShopAreaId()) && !dto.getShopAreaId().equals(rp.getGetAreaId())) continue; // 取货区域匹配
// 送货区域匹配 if (StrUtil.isNotBlank(dto.getShopAreaId()) && !dto.getShopAreaId().equals(rp.getGetAreaId())) continue;
if (StrUtil.isNotBlank(dto.getPutAreaId()) && !dto.getPutAreaId().equals(rp.getPutAreaId())) continue; // 送货区域匹配
// 订单类型匹配 if (StrUtil.isNotBlank(dto.getPutAreaId()) && !dto.getPutAreaId().equals(rp.getPutAreaId())) continue;
if (dto.getOrderType() != null && !dto.getOrderType().equals(rp.getOrderType())) continue; // 订单类型匹配
// 改派时只允许选择接单佣金不超过当前配送单总佣金的规则 if (dto.getOrderType() != null && !dto.getOrderType().equals(rp.getOrderType())) continue;
if (dto.getCommissionAmount() != null // 改派时只允许选择接单佣金不超过当前配送单总佣金的规则
&& (rp.getOrderBkge() == null || rp.getOrderBkge().compareTo(dto.getCommissionAmount()) > 0)) continue; if (dto.getCommissionAmount() != null
matchedRule = rp; && (rp.getOrderBkge() == null || rp.getOrderBkge().compareTo(dto.getCommissionAmount()) > 0)) continue;
break; matchedRule = rp;
break;
}
}
// 后台查询:取收货区域未匹配时,全职且已上线的配送员也返回(上线已在上方过滤)
if (matchedRule == null) {
boolean allowFullTimeFallback = Boolean.TRUE.equals(dto.getFromAdmin())
&& w.getIsFullTime() != null && w.getIsFullTime() == 1;
if (!allowFullTimeFallback) {
continue;
}
} }
if (matchedRule == null) continue;
// ---- 从订单缓存获取该配送员的待完成订单,统计各状态数量 ---- // ---- 从订单缓存获取该配送员的待完成订单,统计各状态数量 ----
List<MallDeliveryOrder> workerOrders = workerOrderCacheUtil.getAllAsList(w.getWorkerId()); List<MallDeliveryOrder> workerOrders = workerOrderCacheUtil.getAllAsList(w.getWorkerId());
@ -524,8 +542,8 @@ public class WorkerController {
matchVO.setIsFullTime(w.getIsFullTime()); matchVO.setIsFullTime(w.getIsFullTime());
matchVO.setHighFloorFee(w.getHighFloorFee()); matchVO.setHighFloorFee(w.getHighFloorFee());
matchVO.setDeliveryLocation(w.getDeliveryLocation()); matchVO.setDeliveryLocation(w.getDeliveryLocation());
matchVO.setOrderBkge(matchedRule.getOrderBkge()); matchVO.setOrderBkge(matchedRule != null ? matchedRule.getOrderBkge() : null);
matchVO.setRemark(matchedRule.getRemark()); matchVO.setRemark(matchedRule != null ? matchedRule.getRemark() : null);
matchVO.setOrderWaitCount(orderWaitCount); matchVO.setOrderWaitCount(orderWaitCount);
matchVO.setOrderGetCount(orderGetCount); matchVO.setOrderGetCount(orderGetCount);
matchVO.setOrderPutCount(orderPutCount); matchVO.setOrderPutCount(orderPutCount);
@ -587,6 +605,7 @@ public class WorkerController {
StrUtil.isBlank(dto.getKeyword()) ? null : dto.getKeyword().trim(), StrUtil.isBlank(dto.getKeyword()) ? null : dto.getKeyword().trim(),
dto.getBaozhang(), dto.getBaozhang(),
dto.getXiangtong(), dto.getXiangtong(),
dto.getFromAdmin(),
dto.getSortField(), dto.getSortField(),
sortOrder sortOrder
); );

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/WorkerRelaPriceMapper.java

@ -34,6 +34,7 @@ public interface WorkerRelaPriceMapper extends BaseMapper<WorkerRelaPrice> {
* @param keyword 模糊搜索姓名或手机号 null 时不过滤 * @param keyword 模糊搜索姓名或手机号 null 时不过滤
* @param baozhang 交易保障true 则只查 rebate_amount > 0 的配送员 * @param baozhang 交易保障true 则只查 rebate_amount > 0 的配送员
* @param xiangtong 相同地址true 则只查手上有相同 putAreaId 订单的配送员 * @param xiangtong 相同地址true 则只查手上有相同 putAreaId 订单的配送员
* @param fromAdmin 后台查询true 时未匹配取收货区域的全职上线配送员也返回
* @param sortField 排序字段score/avgTime/orderBkge默认 score * @param sortField 排序字段score/avgTime/orderBkge默认 score
* @param sortOrder 排序方向DESC/ASC * @param sortOrder 排序方向DESC/ASC
* @return 配送员匹配 VO 分页结果 * @return 配送员匹配 VO 分页结果
@ -48,6 +49,7 @@ public interface WorkerRelaPriceMapper extends BaseMapper<WorkerRelaPrice> {
@Param("keyword") String keyword, @Param("keyword") String keyword,
@Param("baozhang") Boolean baozhang, @Param("baozhang") Boolean baozhang,
@Param("xiangtong") Boolean xiangtong, @Param("xiangtong") Boolean xiangtong,
@Param("fromAdmin") Boolean fromAdmin,
@Param("sortField") String sortField, @Param("sortField") String sortField,
@Param("sortOrder") String sortOrder @Param("sortOrder") String sortOrder
); );

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java

@ -36,6 +36,8 @@ public class MallOrder implements Serializable {
private Integer orderType; private Integer orderType;
@ApiModelProperty(value = "订单类型 null:外卖 1:快递跑腿 2:团购 3:二手") @ApiModelProperty(value = "订单类型 null:外卖 1:快递跑腿 2:团购 3:二手")
private Integer otherOrder; private Integer otherOrder;
@ApiModelProperty(value = "是否虚拟成团 0否 1是")
private Integer isVirtualGroup;
@ApiModelProperty(value = "是否秒杀订单 0否 1是") @ApiModelProperty(value = "是否秒杀订单 0否 1是")
private Integer seckillOrder; private Integer seckillOrder;
@ApiModelProperty(value = "配送方式 1:外卖配送 2:到店消费") @ApiModelProperty(value = "配送方式 1:外卖配送 2:到店消费")

15
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/ShopArea.java

@ -64,6 +64,21 @@ public class ShopArea extends HiverBaseEntity {
@ApiModelProperty(value = "返佣比例,按百分比计算") @ApiModelProperty(value = "返佣比例,按百分比计算")
private Integer commissionRatio = 0; private Integer commissionRatio = 0;
@ApiModelProperty(value = "区域中心经度(仅一级区域,用于定位自动匹配)")
@Column(precision = 12, scale = 6)
private BigDecimal centerLng;
@ApiModelProperty(value = "区域中心纬度(仅一级区域,用于定位自动匹配)")
@Column(precision = 12, scale = 6)
private BigDecimal centerLat;
@ApiModelProperty(value = "自动匹配半径(米,仅一级区域)")
private Integer radiusMeters;
@ApiModelProperty(value = "区域类型:1校园区域 2社会区域(仅一级区域)")
@Column(nullable = false, columnDefinition = "int default 1")
private Integer areaKind = 1;
@Transient @Transient
@TableField(exist = false) @TableField(exist = false)
@ApiModelProperty(value = "父节点名称") @ApiModelProperty(value = "父节点名称")

3
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/ShopTakeaway.java

@ -55,6 +55,9 @@ public class ShopTakeaway extends HiverBaseEntity {
@ApiModelProperty(value = "拼团合作佣金比例") @ApiModelProperty(value = "拼团合作佣金比例")
private BigDecimal commissionRateMore; private BigDecimal commissionRateMore;
@ApiModelProperty(value = "虚拟成团合作佣金比例")
private BigDecimal virtualGroupCommissionRate;
@ApiModelProperty(value = "单独购买合作佣金比例") @ApiModelProperty(value = "单独购买合作佣金比例")
private BigDecimal commissionRateOne; private BigDecimal commissionRateOne;

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeAdminController.java

@ -91,13 +91,16 @@ public class IeAdminController {
public Result<java.util.Map<String, Boolean>> auditSetting() { public Result<java.util.Map<String, Boolean>> auditSetting() {
java.util.Map<String, Boolean> result = new java.util.HashMap<>(); java.util.Map<String, Boolean> result = new java.util.HashMap<>();
result.put("realNameAuditEnabled", realNameAuthService.isRealNameAuditEnabled()); result.put("realNameAuditEnabled", realNameAuthService.isRealNameAuditEnabled());
result.put("ieListingAuditEnabled", realNameAuthService.isIeListingAuditEnabled());
return new ResultUtil<java.util.Map<String, Boolean>>().setData(result); return new ResultUtil<java.util.Map<String, Boolean>>().setData(result);
} }
@RequestMapping(value = "/audit-setting/save", method = RequestMethod.POST) @RequestMapping(value = "/audit-setting/save", method = RequestMethod.POST)
@ApiOperation("保存i/e审核管理配置") @ApiOperation("保存i/e审核管理配置")
public Result<Object> saveAuditSetting(@RequestParam(required = false) Boolean realNameAuditEnabled) { public Result<Object> saveAuditSetting(@RequestParam(required = false) Boolean realNameAuditEnabled,
@RequestParam(required = false) Boolean ieListingAuditEnabled) {
realNameAuthService.setRealNameAuditEnabled(realNameAuditEnabled); realNameAuthService.setRealNameAuditEnabled(realNameAuditEnabled);
realNameAuthService.setIeListingAuditEnabled(ieListingAuditEnabled);
return ResultUtil.success("保存成功"); return ResultUtil.success("保存成功");
} }
} }

1
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/controller/IeRealNameAuthController.java

@ -50,6 +50,7 @@ public class IeRealNameAuthController {
public Result<java.util.Map<String, Boolean>> auditSetting() { public Result<java.util.Map<String, Boolean>> auditSetting() {
java.util.Map<String, Boolean> result = new java.util.HashMap<>(); java.util.Map<String, Boolean> result = new java.util.HashMap<>();
result.put("realNameAuditEnabled", realNameAuthService.isRealNameAuditEnabled()); result.put("realNameAuditEnabled", realNameAuthService.isRealNameAuditEnabled());
result.put("ieListingAuditEnabled", realNameAuthService.isIeListingAuditEnabled());
return new ResultUtil<java.util.Map<String, Boolean>>().setData(result); return new ResultUtil<java.util.Map<String, Boolean>>().setData(result);
} }

14
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRealNameAuthService.java

@ -39,6 +39,20 @@ public interface IeRealNameAuthService {
*/ */
void setRealNameAuditEnabled(Boolean enabled); void setRealNameAuditEnabled(Boolean enabled);
/**
* 查询i/e上架审核开关
*
* @return true=开启上架审核入口false=保持正常i/e入口
*/
boolean isIeListingAuditEnabled();
/**
* 后台保存i/e上架审核开关
*
* @param enabled true=开启上架审核入口
*/
void setIeListingAuditEnabled(Boolean enabled);
/** /**
* 匹配/建档前的服务端兜底校验防止用户绕过前端直接请求匹配接口 * 匹配/建档前的服务端兜底校验防止用户绕过前端直接请求匹配接口
* *

13
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRealNameAuthServiceImpl.java

@ -61,6 +61,7 @@ public class IeRealNameAuthServiceImpl implements IeRealNameAuthService {
private static final DateTimeFormatter BIRTHDAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter BIRTHDAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final int MAX_CERT_PICTURE_BASE64_BYTES = 50 * 1024; private static final int MAX_CERT_PICTURE_BASE64_BYTES = 50 * 1024;
private static final String REAL_NAME_AUDIT_ENABLED_KEY = "IE:REVIEW:REAL_NAME_AUDIT_ENABLED"; private static final String REAL_NAME_AUDIT_ENABLED_KEY = "IE:REVIEW:REAL_NAME_AUDIT_ENABLED";
private static final String IE_LISTING_AUDIT_ENABLED_KEY = "IE:REVIEW:LISTING_AUDIT_ENABLED";
private static final String ALIYUN_CONSISTENT_PASS = "1"; private static final String ALIYUN_CONSISTENT_PASS = "1";
private static final String ALIYUN_CONSISTENT_PHOTO_MISMATCH = "0"; private static final String ALIYUN_CONSISTENT_PHOTO_MISMATCH = "0";
private static final String REPEATED_PHOTO_MISMATCH_AUTO_PASS_REASON = "连续两次照片识别为非同一人,系统默认通过实名认证"; private static final String REPEATED_PHOTO_MISMATCH_AUTO_PASS_REASON = "连续两次照片识别为非同一人,系统默认通过实名认证";
@ -281,6 +282,18 @@ public class IeRealNameAuthServiceImpl implements IeRealNameAuthService {
redisTemplateHelper.set(REAL_NAME_AUDIT_ENABLED_KEY, Boolean.FALSE.equals(enabled) ? "0" : "1"); redisTemplateHelper.set(REAL_NAME_AUDIT_ENABLED_KEY, Boolean.FALSE.equals(enabled) ? "0" : "1");
} }
@Override
public boolean isIeListingAuditEnabled() {
String value = redisTemplateHelper == null ? null : redisTemplateHelper.get(IE_LISTING_AUDIT_ENABLED_KEY);
// 默认关闭,不影响正常i/e入口;后台开启后用于小程序上架审核入口。
return "1".equals(value) || "true".equalsIgnoreCase(value);
}
@Override
public void setIeListingAuditEnabled(Boolean enabled) {
redisTemplateHelper.set(IE_LISTING_AUDIT_ENABLED_KEY, Boolean.TRUE.equals(enabled) ? "1" : "0");
}
private AliyunThreeElementResult callAliyunThreeElement(String certName, String certNo, String certPicture, private AliyunThreeElementResult callAliyunThreeElement(String certName, String certNo, String certPicture,
String regionId, Long userId) { String regionId, Long userId) {
if (isBlank(aliyunAccessKeyId) || isBlank(aliyunAccessKeySecret) || isBlank(aliyunAuthCode)) { if (isBlank(aliyunAccessKeyId) || isBlank(aliyunAccessKeySecret) || isBlank(aliyunAuthCode)) {

16
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderDelayConsumer.java

@ -11,6 +11,7 @@ import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.entity.MallOrder; import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.entity.MallRefundRecord; import cc.hiver.mall.entity.MallRefundRecord;
import cc.hiver.mall.entity.Shop; import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.service.mybatis.MallOrderGroupService;
import cc.hiver.mall.service.mybatis.MallOrderService; import cc.hiver.mall.service.mybatis.MallOrderService;
import cc.hiver.mall.service.mybatis.MallRefundRecordService; import cc.hiver.mall.service.mybatis.MallRefundRecordService;
import cc.hiver.mall.utils.WaitOrderCacheUtil; import cc.hiver.mall.utils.WaitOrderCacheUtil;
@ -44,6 +45,9 @@ public class OrderDelayConsumer {
@Autowired @Autowired
private MallOrderService mallOrderService; private MallOrderService mallOrderService;
@Autowired
private MallOrderGroupService mallOrderGroupService;
@Autowired @Autowired
private ShopMapper shopMapper; private ShopMapper shopMapper;
@ -75,6 +79,7 @@ public class OrderDelayConsumer {
String orderId = msg.getString("orderId"); String orderId = msg.getString("orderId");
String delayType = msg.getString("delayType"); String delayType = msg.getString("delayType");
String assignedWorkerId = msg.getString("assignedWorkerId"); String assignedWorkerId = msg.getString("assignedWorkerId");
String groupId = msg.getString("groupId");
switch (delayType) { switch (delayType) {
case "Worker_Timeout_5m": case "Worker_Timeout_5m":
@ -105,6 +110,9 @@ public class OrderDelayConsumer {
case "Order_Pay_Timeout_1h": case "Order_Pay_Timeout_1h":
handleOrderPayTimeout(orderId); handleOrderPayTimeout(orderId);
break; break;
case "Group_Virtual_Form_5m":
handleGroupVirtualForm(groupId);
break;
default: default:
log.warn("【订单延时处理】未知的延迟类型: {}", delayType); log.warn("【订单延时处理】未知的延迟类型: {}", delayType);
} }
@ -175,6 +183,14 @@ public class OrderDelayConsumer {
mallOrderService.autoCancelUnpaidOrder(orderId); mallOrderService.autoCancelUnpaidOrder(orderId);
} }
private void handleGroupVirtualForm(String groupId) {
if (StringUtils.isBlank(groupId)) {
log.warn("【虚拟成团处理】groupId 为空,跳过");
return;
}
mallOrderGroupService.virtualActivateGroup(groupId);
}
private void moveAssignedOrderBackToWaitCache(String workerId, MallDeliveryOrder delivery) { private void moveAssignedOrderBackToWaitCache(String workerId, MallDeliveryOrder delivery) {
if (delivery == null || StringUtils.isBlank(delivery.getId())) { if (delivery == null || StringUtils.isBlank(delivery.getId())) {
return; return;

4
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/MerchantSettlementTask.java

@ -395,6 +395,10 @@ public class MerchantSettlementTask {
if (takeaway != null) { if (takeaway != null) {
if (order.getOrderType() != null && order.getOrderType() == 1) { if (order.getOrderType() != null && order.getOrderType() == 1) {
commissionRate = takeaway.getCommissionRateOne(); commissionRate = takeaway.getCommissionRateOne();
} else if (Integer.valueOf(1).equals(order.getIsVirtualGroup())) {
commissionRate = takeaway.getVirtualGroupCommissionRate() != null
? takeaway.getVirtualGroupCommissionRate()
: takeaway.getCommissionRateMore();
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { } else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) {
commissionRate = takeaway.getCommissionRateMore(); commissionRate = takeaway.getCommissionRateMore();
} }

4
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/PlanetAdventureSettleTask.java

@ -167,10 +167,10 @@ public class PlanetAdventureSettleTask {
dealingsRecord.setLinkType(DealingsRecordConstant.TYPE[0]); dealingsRecord.setLinkType(DealingsRecordConstant.TYPE[0]);
dealingsRecord.setDealingsType(DealingsRecordConstant.DEALINGS_TYPE[0]); dealingsRecord.setDealingsType(DealingsRecordConstant.DEALINGS_TYPE[0]);
dealingsRecord.setCreateBy(session.getUserId()); dealingsRecord.setCreateBy(session.getUserId());
dealingsRecord.setRemark("学院助推赛荣耀榜第" + rankNo + "名现金奖励"); dealingsRecord.setRemark("区域排位赛荣耀榜第" + rankNo + "名现金奖励");
dealingsRecordService.save(dealingsRecord); dealingsRecordService.save(dealingsRecord);
homeReminderService.addPlanetReward(String.valueOf(session.getUserId()), session.getRegionId(), "rankTop", homeReminderService.addPlanetReward(String.valueOf(session.getUserId()), session.getRegionId(), "rankTop",
"学院助推赛第" + rankNo + "名奖励 ¥" + amount, linkId); "区域排位赛第" + rankNo + "名奖励 ¥" + amount, linkId);
return true; return true;
} }
} }

10
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopAreaService.java

@ -58,6 +58,16 @@ public interface ShopAreaService extends HiverBaseService<ShopArea, String> {
*/ */
ShopArea findTopShopAreaById(String id); ShopArea findTopShopAreaById(String id);
/**
* 根据经纬度匹配一级区域+半径
* 仅返回半径内命中的校区都不在半径内则返回 null
*
* @param lat 纬度
* @param lng 经度
* @return 命中的一级区域未命中返回 null
*/
ShopArea matchByLocation(double lat, double lng);
void removeShopAreaCache(String parentId,String shopAreaId); void removeShopAreaCache(String parentId,String shopAreaId);
void refreshShopAreaCache(String parentId,String shopAreaId); void refreshShopAreaCache(String parentId,String shopAreaId);

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderGroupService.java

@ -20,5 +20,10 @@ public interface MallOrderGroupService extends IService<MallOrderGroup> {
*/ */
void expireGroup(String groupId); void expireGroup(String groupId);
/**
* 普通拼团长时间未满员时按虚拟成团激活当前已支付待成团订单
*/
void virtualActivateGroup(String groupId);
List<MallOrderGroup> selectMallGroup(MallOrderGroup group); List<MallOrderGroup> selectMallGroup(MallOrderGroup group);
} }

37
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopAreaServiceImpl.java

@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
@ -24,6 +25,7 @@ import java.util.List;
@Service @Service
@Transactional @Transactional
public class ShopAreaServiceImpl implements ShopAreaService { public class ShopAreaServiceImpl implements ShopAreaService {
private static final double EARTH_RADIUS_METERS = 6371000D;
@Autowired @Autowired
private ShopAreaDao shopAreaDao; private ShopAreaDao shopAreaDao;
@ -104,6 +106,41 @@ public class ShopAreaServiceImpl implements ShopAreaService {
} }
} }
@Override
public ShopArea matchByLocation(double lat, double lng) {
List<ShopArea> rootAreas = shopAreaDao.findByParentIdAndStatusOrderBySortOrder(
CommonConstant.PARENT_ID, CommonConstant.STATUS_NORMAL);
if (rootAreas == null || rootAreas.isEmpty()) {
return null;
}
ShopArea best = null;
double bestDistance = Double.MAX_VALUE;
for (ShopArea area : rootAreas) {
BigDecimal centerLat = area.getCenterLat();
BigDecimal centerLng = area.getCenterLng();
Integer radiusMeters = area.getRadiusMeters();
if (centerLat == null || centerLng == null || radiusMeters == null || radiusMeters <= 0) {
continue;
}
double distance = distanceMeters(lat, lng, centerLat.doubleValue(), centerLng.doubleValue());
if (distance <= radiusMeters && distance < bestDistance) {
best = area;
bestDistance = distance;
}
}
return best;
}
private double distanceMeters(double lat1, double lng1, double lat2, double lng2) {
double dLat = Math.toRadians(lat2 - lat1);
double dLng = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
* Math.sin(dLng / 2) * Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS_METERS * c;
}
@Override @Override
public void removeShopAreaCache(String parentId,String shopAreaId) { public void removeShopAreaCache(String parentId,String shopAreaId) {
if (CharSequenceUtil.isBlank(parentId)) { if (CharSequenceUtil.isBlank(parentId)) {

53
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java

@ -52,9 +52,11 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
private static final int GROUP_STATUS_FACE2FACE = 3; private static final int GROUP_STATUS_FACE2FACE = 3;
// 订单状态 // 订单状态
private static final int ORDER_STATUS_WAIT_PAY = 0;
private static final int ORDER_STATUS_WAIT_GROUP = 10; // 待成团(01) private static final int ORDER_STATUS_WAIT_GROUP = 10; // 待成团(01)
private static final int ORDER_STATUS_WAIT_SHOP = 1; private static final int ORDER_STATUS_WAIT_SHOP = 1;
private static final int ORDER_STATUS_CANCELLED = 6; private static final int ORDER_STATUS_CANCELLED = 6;
private static final int ORDER_TYPE_GROUP = 2;
@Autowired @Autowired
@Lazy @Lazy
@ -140,7 +142,7 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
if (group.getCurrentMembers() < group.getTargetMembers()) return; if (group.getCurrentMembers() < group.getTargetMembers()) return;
//成团缓存去掉 //成团缓存去掉
shopGroupOrderCacheUtil.remove(group.getShopId(), groupId); shopGroupOrderCacheUtil.remove(group.getShopId(), groupId);
// 达到成团人数 -> 激活 (超出拼团人数也可以参与) // 达到成团人数 -> 激活 (超出拼团人数也可以参与)
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
@ -289,6 +291,55 @@ public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper,
log.info("拼团 {} 成团成功,激活 {} 条子订单", groupId, waitingOrders.size()); log.info("拼团 {} 成团成功,激活 {} 条子订单", groupId, waitingOrders.size());
} }
@Override
@Transactional(rollbackFor = Exception.class)
public void virtualActivateGroup(String groupId) {
MallOrderGroup group = this.getById(groupId);
if (group == null) return;
if (group.getStatus() == null || group.getStatus() != GROUP_STATUS_FORMING) return;
if (group.getIsFace() != null && group.getIsFace() == 1) return;
if (group.getTargetMembers() == null || group.getTargetMembers() <= 0) return;
if (group.getCurrentMembers() != null
&& group.getCurrentMembers() >= group.getTargetMembers()) {
checkAndActivateGroup(groupId);
return;
}
List<String> orderIdList = parseGroupOrderIds(group);
if (orderIdList.isEmpty()) return;
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
guw.eq(MallOrderGroup::getId, groupId)
.eq(MallOrderGroup::getStatus, GROUP_STATUS_FORMING)
.lt(MallOrderGroup::getCurrentMembers, group.getTargetMembers())
.set(MallOrderGroup::getCurrentMembers, group.getTargetMembers());
if (!this.update(guw)) {
return;
}
LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>();
ouw.in(MallOrder::getId, orderIdList)
.eq(MallOrder::getOrderType, ORDER_TYPE_GROUP)
.in(MallOrder::getStatus, Arrays.asList(ORDER_STATUS_WAIT_PAY, ORDER_STATUS_WAIT_GROUP))
.set(MallOrder::getIsVirtualGroup, 1);
mallOrderService.update(ouw);
group.setCurrentMembers(group.getTargetMembers());
shopGroupOrderCacheUtil.update(group.getShopId(), group);
checkAndActivateGroup(groupId);
log.info("普通拼团 {} 触发虚拟成团,已标记 {} 条订单", groupId, orderIdList.size());
}
private List<String> parseGroupOrderIds(MallOrderGroup group) {
if (group == null || StringUtils.isBlank(group.getGroupOrderIds())) {
return Collections.emptyList();
}
return Arrays.stream(group.getGroupOrderIds().split(","))
.map(String::trim)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
}
private void notifyShopDeliveryOrder(MallOrder order) { private void notifyShopDeliveryOrder(MallOrder order) {
if (order == null || order.getShopDelivery() == null || order.getShopDelivery() != 1) { if (order == null || order.getShopDelivery() == null || order.getShopDelivery() != 1) {
return; return;

9
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java

@ -80,7 +80,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
private static final int STATUS__RETURNED = 12;//已售后 private static final int STATUS__RETURNED = 12;//已售后
private static final int REFUND_STATUS_AFTER_SALE_AGREED = 4;//同意售后 private static final int REFUND_STATUS_AFTER_SALE_AGREED = 4;//同意售后
private static final String DELAY_TYPE_ORDER_PAY_TIMEOUT = "Order_Pay_Timeout_1h"; private static final String DELAY_TYPE_ORDER_PAY_TIMEOUT = "Order_Pay_Timeout_1h";
private static final String DELAY_TYPE_GROUP_VIRTUAL_FORM = "Group_Virtual_Form_5m";
private static final long ORDER_PAY_TIMEOUT_DELAY_MILLIS = 60 * 60 * 1000L; private static final long ORDER_PAY_TIMEOUT_DELAY_MILLIS = 60 * 60 * 1000L;
private static final long GROUP_VIRTUAL_FORM_DELAY_MILLIS = 5 * 60 * 1000L;
// 配送方式常量 // 配送方式常量
private static final int DELIVERY_TYPE_EXPRESS = 1; // 外卖 private static final int DELIVERY_TYPE_EXPRESS = 1; // 外卖
@ -816,6 +818,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
group.setStatus(GROUP_STATUS_FORMING); group.setStatus(GROUP_STATUS_FORMING);
group.setIsFace(0); group.setIsFace(0);
shopGroupOrderCacheUtil.put(order.getShopId(), group); shopGroupOrderCacheUtil.put(order.getShopId(), group);
Map<String, Object> extraParams = new HashMap<>();
extraParams.put("groupId", group.getId());
orderAsyncProducer.sendDelayMessage(order.getId(), DELAY_TYPE_GROUP_VIRTUAL_FORM, GROUP_VIRTUAL_FORM_DELAY_MILLIS, extraParams);
} }
}else{ }else{
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
@ -2515,6 +2520,10 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder
if (takeaway != null) { if (takeaway != null) {
if (Integer.valueOf(ORDER_TYPE_NORMAL).equals(order.getOrderType())) { if (Integer.valueOf(ORDER_TYPE_NORMAL).equals(order.getOrderType())) {
rate = takeaway.getCommissionRateOne(); rate = takeaway.getCommissionRateOne();
} else if (Integer.valueOf(1).equals(order.getIsVirtualGroup())) {
rate = takeaway.getVirtualGroupCommissionRate() != null
? takeaway.getVirtualGroupCommissionRate()
: takeaway.getCommissionRateMore();
} else if (Integer.valueOf(ORDER_TYPE_GROUP).equals(order.getOrderType()) } else if (Integer.valueOf(ORDER_TYPE_GROUP).equals(order.getOrderType())
|| Integer.valueOf(ORDER_TYPE_FACETOFACE).equals(order.getOrderType())) { || Integer.valueOf(ORDER_TYPE_FACETOFACE).equals(order.getOrderType())) {
rate = takeaway.getCommissionRateMore(); rate = takeaway.getCommissionRateMore();

4
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java

@ -224,6 +224,10 @@ public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMap
if (takeaway != null) { if (takeaway != null) {
if (order.getOrderType() != null && order.getOrderType() == 1) { if (order.getOrderType() != null && order.getOrderType() == 1) {
rate = takeaway.getCommissionRateOne(); rate = takeaway.getCommissionRateOne();
} else if (Integer.valueOf(1).equals(order.getIsVirtualGroup())) {
rate = takeaway.getVirtualGroupCommissionRate() != null
? takeaway.getVirtualGroupCommissionRate()
: takeaway.getCommissionRateMore();
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { } else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) {
rate = takeaway.getCommissionRateMore(); rate = takeaway.getCommissionRateMore();
} }

4
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallSettlementRecordServiceImpl.java

@ -118,6 +118,10 @@ public class MallSettlementRecordServiceImpl extends ServiceImpl<MallSettlementR
if (takeaway != null) { if (takeaway != null) {
if (order.getOrderType() != null && order.getOrderType() == 1) { if (order.getOrderType() != null && order.getOrderType() == 1) {
commissionRate = takeaway.getCommissionRateOne(); commissionRate = takeaway.getCommissionRateOne();
} else if (Integer.valueOf(1).equals(order.getIsVirtualGroup())) {
commissionRate = takeaway.getVirtualGroupCommissionRate() != null
? takeaway.getVirtualGroupCommissionRate()
: takeaway.getCommissionRateMore();
} else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) { } else if (order.getOrderType() != null && (order.getOrderType() == 2 || order.getOrderType() == 3)) {
commissionRate = takeaway.getCommissionRateMore(); commissionRate = takeaway.getCommissionRateMore();
} }

2
hiver-modules/hiver-mall/src/main/resources/db/mall_order_is_virtual_group.sql

@ -0,0 +1,2 @@
ALTER TABLE `mall_order`
ADD COLUMN `is_virtual_group` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否虚拟成团 0否 1是' AFTER `other_order`;

2
hiver-modules/hiver-mall/src/main/resources/db/shop_area_area_kind.sql

@ -0,0 +1,2 @@
ALTER TABLE `t_shop_area`
ADD COLUMN `area_kind` int(11) NOT NULL DEFAULT 1 COMMENT '区域类型:1校园区域 2社会区域(仅一级区域)';

4
hiver-modules/hiver-mall/src/main/resources/db/shop_area_geo.sql

@ -0,0 +1,4 @@
ALTER TABLE `t_shop_area`
ADD COLUMN `center_lng` decimal(12,6) DEFAULT NULL COMMENT '区域中心经度(仅一级区域)',
ADD COLUMN `center_lat` decimal(12,6) DEFAULT NULL COMMENT '区域中心纬度(仅一级区域)',
ADD COLUMN `radius_meters` int(11) DEFAULT NULL COMMENT '自动匹配半径米(仅一级区域)';

2
hiver-modules/hiver-mall/src/main/resources/db/shop_takeaway_virtual_group_commission.sql

@ -0,0 +1,2 @@
ALTER TABLE `t_shop_takeaway`
ADD COLUMN `virtual_group_commission_rate` decimal(10,2) DEFAULT NULL COMMENT '虚拟成团合作佣金比例' AFTER `commission_rate_more`;

5
hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml

@ -30,6 +30,7 @@
<result column="settlement_status" property="settlementStatus"/> <result column="settlement_status" property="settlementStatus"/>
<result column="shop_make_time" property="shopMakeTime"/> <result column="shop_make_time" property="shopMakeTime"/>
<result column="other_order" property="otherOrder"/> <result column="other_order" property="otherOrder"/>
<result column="is_virtual_group" property="isVirtualGroup"/>
<result column="seckill_order" property="seckillOrder"/> <result column="seckill_order" property="seckillOrder"/>
<result column="user_require_make" property="userRequireMake"/> <result column="user_require_make" property="userRequireMake"/>
<result column="is_free_order" property="isFreeOrder"/> <result column="is_free_order" property="isFreeOrder"/>
@ -135,7 +136,7 @@
o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time, o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time,
o.receiver_name, o.receiver_phone, o.receiver_address, o.receiver_name, o.receiver_phone, o.receiver_address,
o.shop_name, o.shop_phone, o.shop_address,o.number_code,o.region_id, o.shop_name, o.shop_phone, o.shop_address,o.number_code,o.region_id,
o.settlement_status,o.shop_make_time,o.other_order,o.seckill_order,o.user_require_make o.settlement_status,o.shop_make_time,o.other_order,o.is_virtual_group,o.seckill_order,o.user_require_make
,o.is_free_order,o.free_amount,o.saved_amount,o.is_pack,o.shop_delivery, ,o.is_free_order,o.free_amount,o.saved_amount,o.is_pack,o.shop_delivery,
o.transfer_delivery,o.transfer_address_id,o.transfer_address_name o.transfer_delivery,o.transfer_address_id,o.transfer_address_name
FROM mall_order o FROM mall_order o
@ -257,7 +258,7 @@
o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time, o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time,
o.receiver_name, o.receiver_phone, o.receiver_address, o.receiver_name, o.receiver_phone, o.receiver_address,
o.shop_name, o.shop_phone, o.shop_address,o.number_code,o.region_id, o.shop_name, o.shop_phone, o.shop_address,o.number_code,o.region_id,
o.settlement_status,o.shop_make_time,o.other_order,o.seckill_order,o.user_require_make o.settlement_status,o.shop_make_time,o.other_order,o.is_virtual_group,o.seckill_order,o.user_require_make
,o.is_free_order,o.free_amount,o.is_pack,o.shop_delivery, ,o.is_free_order,o.free_amount,o.is_pack,o.shop_delivery,
o.transfer_delivery,o.transfer_address_id,o.transfer_address_name o.transfer_delivery,o.transfer_address_id,o.transfer_address_name
FROM mall_order o LEFT JOIN mall_order_group og ON o.id = og.head_order_id FROM mall_order o LEFT JOIN mall_order_group og ON o.id = og.head_order_id

10
hiver-modules/hiver-mall/src/main/resources/mapper/ShopTakeawayMapper.xml

@ -12,6 +12,7 @@
<result column="shop_name" property="shopName" jdbcType="VARCHAR" /> <result column="shop_name" property="shopName" jdbcType="VARCHAR" />
<result column="region_id" property="regionId" jdbcType="VARCHAR" /> <result column="region_id" property="regionId" jdbcType="VARCHAR" />
<result column="commission_rate_more" property="commissionRateMore" jdbcType="VARCHAR" /> <result column="commission_rate_more" property="commissionRateMore" jdbcType="VARCHAR" />
<result column="virtual_group_commission_rate" property="virtualGroupCommissionRate" jdbcType="DECIMAL" />
<result column="commission_rate_one" property="commissionRateOne" jdbcType="VARCHAR" /> <result column="commission_rate_one" property="commissionRateOne" jdbcType="VARCHAR" />
<result column="business_hour_begin" property="businessHourBegin" jdbcType="VARCHAR" /> <result column="business_hour_begin" property="businessHourBegin" jdbcType="VARCHAR" />
<result column="business_hour_end" property="businessHourEnd" jdbcType="VARCHAR" /> <result column="business_hour_end" property="businessHourEnd" jdbcType="VARCHAR" />
@ -25,7 +26,7 @@
</resultMap> </resultMap>
<sql id="Base_Column_List" > <sql id="Base_Column_List" >
id,create_by, create_time, update_time, update_by, id,create_by, create_time, update_time, update_by,
del_flag,shop_id,shop_name,region_id,commission_rate_more,commission_rate_one,business_hour_begin,business_hour_end,status,type,is_delivery,cooking_time,shoprank,is_price_lock,join_time del_flag,shop_id,shop_name,region_id,commission_rate_more,virtual_group_commission_rate,commission_rate_one,business_hour_begin,business_hour_end,status,type,is_delivery,cooking_time,shoprank,is_price_lock,join_time
</sql> </sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" > <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
select select
@ -57,10 +58,10 @@
</delete> </delete>
<insert id="insert" parameterType="cc.hiver.mall.entity.ShopTakeaway" > <insert id="insert" parameterType="cc.hiver.mall.entity.ShopTakeaway" >
insert into t_shop_takeaway (id,create_by, create_time, update_time, update_by, insert into t_shop_takeaway (id,create_by, create_time, update_time, update_by,
del_flag,shop_id, shop_name,region_id,commission_rate_more, commission_rate_one, business_hour_begin, business_hour_end, status, type, is_delivery, cooking_time, shoprank, is_price_lock, join_time) del_flag,shop_id, shop_name,region_id,commission_rate_more, virtual_group_commission_rate, commission_rate_one, business_hour_begin, business_hour_end, status, type, is_delivery, cooking_time, shoprank, is_price_lock, join_time)
values (#{id,jdbcType=VARCHAR},#{createBy,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP}, values (#{id,jdbcType=VARCHAR},#{createBy,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP},
#{updateTime,jdbcType=TIMESTAMP},#{updateBy,jdbcType=VARCHAR},#{delFlag,jdbcType=INTEGER} #{updateTime,jdbcType=TIMESTAMP},#{updateBy,jdbcType=VARCHAR},#{delFlag,jdbcType=INTEGER}
,#{shopId,jdbcType=VARCHAR},#{shopName,jdbcType=VARCHAR},#{regionId,jdbcType=VARCHAR}, #{commissionRateMore,jdbcType=DECIMAL}, #{commissionRateOne,jdbcType=DECIMAL}, #{businessHourBegin,jdbcType=VARCHAR}, #{businessHourEnd,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, #{type,jdbcType=VARCHAR}, #{isDelivery,jdbcType=INTEGER}, #{cookingTime,jdbcType=INTEGER}, #{shoprank,jdbcType=INTEGER}, #{isPriceLock,jdbcType=INTEGER}, #{joinTime,jdbcType=VARCHAR}) ,#{shopId,jdbcType=VARCHAR},#{shopName,jdbcType=VARCHAR},#{regionId,jdbcType=VARCHAR}, #{commissionRateMore,jdbcType=DECIMAL}, #{virtualGroupCommissionRate,jdbcType=DECIMAL}, #{commissionRateOne,jdbcType=DECIMAL}, #{businessHourBegin,jdbcType=VARCHAR}, #{businessHourEnd,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, #{type,jdbcType=VARCHAR}, #{isDelivery,jdbcType=INTEGER}, #{cookingTime,jdbcType=INTEGER}, #{shoprank,jdbcType=INTEGER}, #{isPriceLock,jdbcType=INTEGER}, #{joinTime,jdbcType=VARCHAR})
</insert> </insert>
<update id="updateByPrimaryKeySelective" parameterType="cc.hiver.mall.entity.ShopTakeaway" > <update id="updateByPrimaryKeySelective" parameterType="cc.hiver.mall.entity.ShopTakeaway" >
@ -78,6 +79,9 @@
<if test="commissionRateMore != null" > <if test="commissionRateMore != null" >
commission_rate_more = #{commissionRateMore,jdbcType=DECIMAL}, commission_rate_more = #{commissionRateMore,jdbcType=DECIMAL},
</if> </if>
<if test="virtualGroupCommissionRate != null" >
virtual_group_commission_rate = #{virtualGroupCommissionRate,jdbcType=DECIMAL},
</if>
<if test="commissionRateOne != null" > <if test="commissionRateOne != null" >
commission_rate_one = #{commissionRateOne,jdbcType=DECIMAL}, commission_rate_one = #{commissionRateOne,jdbcType=DECIMAL},
</if> </if>

11
hiver-modules/hiver-mall/src/main/resources/mapper/WorkerRelaPriceMapper.xml

@ -108,8 +108,8 @@
/* 待送达:status = 2 */ /* 待送达:status = 2 */
COUNT(CASE WHEN d.status = 2 THEN 1 END) AS order_put_count COUNT(CASE WHEN d.status = 2 THEN 1 END) AS order_put_count
FROM t_worker w FROM t_worker w
/* 关联配送规则表:只取满足区域匹配和接单条件的规则 */ /* 关联配送规则表:优先取满足区域匹配和接单条件的规则;后台可补充全职上线配送员 */
INNER JOIN t_worker_rela_price p LEFT JOIN t_worker_rela_price p
ON p.worker_id = w.worker_id ON p.worker_id = w.worker_id
AND p.get_push_order = 1 AND p.get_push_order = 1
<if test="regionId != null and regionId != ''"> <if test="regionId != null and regionId != ''">
@ -139,6 +139,13 @@
<if test="regionId != null and regionId != ''"> <if test="regionId != null and regionId != ''">
AND w.region = #{regionId} AND w.region = #{regionId}
</if> </if>
/* 规则匹配,或后台查询时补充全职配送员 */
AND (
p.worker_id IS NOT NULL
<if test="fromAdmin != null and fromAdmin == true">
OR w.is_full_time = 1
</if>
)
/* 交易保障:rebate_amount > 0 */ /* 交易保障:rebate_amount > 0 */
<if test="baozhang != null and baozhang == true"> <if test="baozhang != null and baozhang == true">
AND w.rebate_amount &gt; 0 AND w.rebate_amount &gt; 0

Loading…
Cancel
Save