Browse Source

对接拼团数据

master
wangfukang 3 weeks ago
parent
commit
9c2503feeb
  1. 14
      hiver-admin/test-output/test-report.html
  2. 4
      hiver-core/src/main/java/cc/hiver/core/entity/User.java
  3. 52
      hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/AuthController.java
  4. 62
      hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/UserController.java
  5. 4
      hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java
  6. 111
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java
  7. 189
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java
  8. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerController.java
  9. 18
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerRelaPriceController.java
  10. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/ShopDao.java
  11. 27
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java
  12. 25
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderGoodsMapper.java
  13. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderGroupMapper.java
  14. 23
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderMapper.java
  15. 12
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallRefundRecordMapper.java
  16. 1
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/WorkerRelaPriceMapper.java
  17. 35
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java
  18. 35
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java
  19. 11
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java
  20. 19
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGroup.java
  21. 11
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java
  22. 39
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallReturnOrderGoods.java
  23. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Product.java
  24. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/Shop.java
  25. 19
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/AreaRuleDTO.java
  26. 91
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java
  27. 50
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallDeliveryOrderPageQuery.java
  28. 38
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java
  29. 33
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/MallOrderVO.java
  30. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/WorkerRealPriceVo.java
  31. 57
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/GroupBuyExpireTask.java
  32. 2
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopService.java
  33. 43
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java
  34. 20
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderGroupService.java
  35. 72
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java
  36. 10
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallRefundRecordService.java
  37. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java
  38. 193
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java
  39. 204
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java
  40. 769
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java
  41. 15
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java
  42. 105
      hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml
  43. 34
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGoodsMapper.xml
  44. 23
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGroupMapper.xml
  45. 80
      hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderMapper.xml
  46. 17
      hiver-modules/hiver-mall/src/main/resources/mapper/MallRefundRecordMapper.xml
  47. 22
      hiver-modules/hiver-mall/src/main/resources/mapper/ShopMapper.xml
  48. 1
      hiver-modules/hiver-mall/src/main/resources/mapper/WorkerRelaPriceMapper.xml
  49. 1
      hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatController.java
  50. 12
      hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPayController.java

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

@ -35,7 +35,7 @@
<a href="#"><span class="badge badge-primary">Hiver</span></a>
</li>
<li class="m-r-10">
<a href="#"><span class="badge badge-primary">三月 06, 2026 18:12:41</span></a>
<a href="#"><span class="badge badge-primary">三月 22, 2026 16:23:04</span></a>
</li>
</ul>
</div>
@ -84,7 +84,7 @@
<div class="test-detail">
<span class="meta text-white badge badge-sm"></span>
<p class="name">passTest</p>
<p class="text-sm"><span>18:12:41 下午</span> / <span>0.019 secs</span></p>
<p class="text-sm"><span>16:23:04 下午</span> / <span>0.019 secs</span></p>
</div>
<div class="test-contents d-none">
<div class="detail-head">
@ -92,8 +92,8 @@
<div class="info">
<div class='float-right'><span class='badge badge-default'>#test-id=1</span></div>
<h5 class="test-status text-pass">passTest</h5>
<span class='badge badge-success'>03.06.2026 18:12:41</span>
<span class='badge badge-danger'>03.06.2026 18:12:41</span>
<span class='badge badge-success'>03.22.2026 16:23:04</span>
<span class='badge badge-danger'>03.22.2026 16:23:04</span>
<span class='badge badge-default'>0.019 secs</span>
</div>
<div class="m-t-10 m-l-5"></div>
@ -104,7 +104,7 @@
<tbody>
<tr class="event-row">
<td><span class="badge log pass-bg">Pass</span></td>
<td>18:12:41</td>
<td>16:23:04</td>
<td>
Test passed
</td>
@ -128,13 +128,13 @@
<div class="col-md-3">
<div class="card"><div class="card-body">
<p class="m-b-0">Started</p>
<h3>三月 06, 2026 18:12:41</h3>
<h3>三月 22, 2026 16:23:04</h3>
</div></div>
</div>
<div class="col-md-3">
<div class="card"><div class="card-body">
<p class="m-b-0">Ended</p>
<h3>三月 06, 2026 18:12:41</h3>
<h3>三月 22, 2026 16:23:04</h3>
</div></div>
</div>
<div class="col-md-3">

4
hiver-core/src/main/java/cc/hiver/core/entity/User.java

@ -126,6 +126,10 @@ public class User extends HiverBaseEntity {
@ApiModelProperty(value = "入库员标识:0:不是(数据库默认);1:是;")
private String warehouseKeeperFlag;
@Transient
@ApiModelProperty(value = "店铺id")
private String shopId;
@ApiModelProperty(value = "小程序id")
private String miniProgramOpenid;

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

@ -5,7 +5,10 @@ import cc.hiver.base.vo.QRStatusVo;
import cc.hiver.base.vo.RegisterShopVo;
import cc.hiver.core.common.annotation.RateLimiter;
import cc.hiver.core.common.annotation.SystemLog;
import cc.hiver.core.common.constant.*;
import cc.hiver.core.common.constant.AppToBConstant;
import cc.hiver.core.common.constant.SecurityConstant;
import cc.hiver.core.common.constant.ShopConstant;
import cc.hiver.core.common.constant.UserConstant;
import cc.hiver.core.common.enums.LogType;
import cc.hiver.core.common.exception.HiverException;
import cc.hiver.core.common.redis.RedisTemplateHelper;
@ -19,10 +22,8 @@ import cc.hiver.core.service.RoleService;
import cc.hiver.core.service.UserRoleService;
import cc.hiver.core.service.UserService;
import cc.hiver.mall.debt.service.DebtService;
import cc.hiver.mall.entity.Customer;
import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.entity.ShopUser;
import cc.hiver.mall.entity.Supplier;
import cc.hiver.mall.invitelog.constant.InviteLogConstant;
import cc.hiver.mall.invitelog.entity.InviteLog;
import cc.hiver.mall.invitelog.service.InviteLogService;
@ -360,17 +361,16 @@ public class AuthController {
@ApiOperation("注册店铺")
public Result register(@Valid RegisterShopVo registerShopVo) {
// 新增用户表信息
User user = new User();
/*User user = new User();
if (StringUtils.isEmpty(registerShopVo.getMobile())) {
return ResultUtil.error("手机号不能为空!");
}
if (StringUtils.isEmpty(registerShopVo.getPassword())) {
return ResultUtil.error("密码不能为空!");
}
}*/
// 一个用户可存在多个店铺
// 根据手机号及用户类型查询是否存在,如果存在,不新增,否则,新增用户
final User byMobileAndType = userService.findByMobile(registerShopVo.getMobile());
if (byMobileAndType != null) {
// 根据unionid及用户类型查询是否存在,如果存在,不新增,否则,新增用户
/*if (byMobileAndType != null) {
user = byMobileAndType;
if (!byMobileAndType.getType().contains(UserConstant.USER_TYPE_NORMAL)) {
// 如果不包含,赋予店铺角色
@ -401,9 +401,9 @@ public class AuthController {
user.setNickname(registerShopVo.getNickname());
}
user = userService.save(user);
}
}*/
// 默认角色
final List<Role> roleList = roleService.findByDefaultRole(true);
/* final List<Role> roleList = roleService.findByDefaultRole(true);
if (roleList != null && !roleList.isEmpty()) {
for (Role role : roleList) {
final UserRole ur = new UserRole().setUserId(user.getId()).setRoleId(role.getId());
@ -411,7 +411,11 @@ public class AuthController {
}
}
// 异步发送创建账号消息
addMessage.addSendMessage(user.getId());
addMessage.addSendMessage(user.getId());*/
final User byMobileAndType = userService.findByUnionid(registerShopVo.getUnionid());
byMobileAndType.setNickname(registerShopVo.getNickname());
userService.update(byMobileAndType);
// 新增店铺信息
final Shop shop = new Shop();
shop.setShopName(registerShopVo.getShopName());
@ -424,7 +428,7 @@ public class AuthController {
shop.setAliName(registerShopVo.getAliName());
shop.setRegion(registerShopVo.getRegion());
shop.setRegionId(registerShopVo.getRegionId());
shop.setIsStudent(registerShopVo.getIsStudent());
// 1. 获取当前日期 (LocalDate 只包含年月日,不包含时分秒)
final LocalDate today = LocalDate.now();
@ -442,8 +446,8 @@ public class AuthController {
shop.setEndTime(formattedFutureDate);
// 店铺店铺名称,店铺地址,与用户(默认店主)的关联
// 设置注册用户为店主
shop.setShopOwnerId(user.getId());
shop.setShopOwnerId(byMobileAndType.getId());
shop.setShopMangerId(byMobileAndType.getId());
// 判断是否传递了邀请人及邀请店铺,设置了的话,新增邀请返佣信息
if (StringUtils.isNotEmpty(registerShopVo.getInviteUserId()) && StringUtils.isNotEmpty(registerShopVo.getInviteShopId())) {
final InviteLog inviteLog = new InviteLog();
@ -462,21 +466,22 @@ public class AuthController {
shop.setAttrId(categoryId);
// 20241010 打印方式 字段默认给 0
shop.setPrintingMethod(ShopConstant.PRINTING_METHOD[0]);
shop.setContactPhone(registerShopVo.getMobile());
shopService.save(shop);
shopService.refreshShopCache(shop.getId(), shop.getRegionId());
// 增加店铺和员工关联表信息
final ShopUser shopUser = new ShopUser();
shopUser.setUser(user);
shopUser.setUserName(user.getUsername());
shopUser.setUser(byMobileAndType);
shopUser.setUserName(byMobileAndType.getUsername());
shopUser.setShop(shop);
shopUser.setShopName(shop.getShopName());
shopUser.setType(UserConstant.SHOP_USER_ADMIN);
shopUserService.save(shopUser);
// 20240625新增默认供应商
final Supplier supplier = new Supplier();
/*final Supplier supplier = new Supplier();
supplier.setShopId(shop.getId());
supplier.setConsigneeName("默认供应商");
supplier.setCreateBy(user.getId());
supplier.setCreateBy(byMobileAndType.getId());
supplier.setCreateTime(new Date());
supplier.setConsigneeMobile("13222222222");
supplier.setProvince("暂无");
@ -487,8 +492,8 @@ public class AuthController {
supplierService.addSupplier(supplier);
// 启用店铺的时候需要给该店铺新增一个散客的客户;
final Customer customer = new Customer();
customer.setCreateBy(user.getId());
customer.setCreateByName(user.getNickname());
customer.setCreateBy(byMobileAndType.getId());
customer.setCreateByName(byMobileAndType.getNickname());
customer.setName("散客");
customer.setPhone("13111111111");
customer.setProvince("暂无");
@ -496,8 +501,9 @@ public class AuthController {
customer.setArea("暂无");
customer.setUserName("sanke");
customer.setShopId(shop.getId());
customerService.addCustomer(customer);
return ResultUtil.data(user);
customerService.addCustomer(customer);*/
byMobileAndType.setShopId(shop.getId());
return ResultUtil.data(byMobileAndType);
}
@RequestMapping(value = "/chooseShop", method = RequestMethod.POST)

62
hiver-modules/hiver-base/src/main/java/cc/hiver/base/controller/manage/UserController.java

@ -18,11 +18,14 @@ import cc.hiver.core.service.*;
import cc.hiver.core.service.mybatis.IUserRoleService;
import cc.hiver.core.vo.RoleDTO;
import cc.hiver.core.vo.WechatVo;
import cc.hiver.mall.entity.Shop;
import cc.hiver.mall.entity.ShopArea;
import cc.hiver.mall.entity.WorkerRelaPrice;
import cc.hiver.mall.service.ShopAreaService;
import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.ShopUserService;
import cc.hiver.mall.service.mybatis.CustomerService;
import cc.hiver.mall.service.mybatis.WorkerRelaPriceService;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpUtil;
import com.google.gson.JsonObject;
@ -77,6 +80,9 @@ public class UserController {
@Autowired
private ShopAreaService shopAreaService;
@Autowired
private WorkerRelaPriceService workerRelaPriceService;
@Autowired
private DepartmentHeaderService departmentHeaderService;
@ -628,20 +634,17 @@ public class UserController {
wechatUser.setUsername(username);
final String encryptPass = new BCryptPasswordEncoder().encode("123456");
wechatUser.setPassword(encryptPass);
wechatUser.setType(UserConstant.USER_TYPE_CUSTOM);
wechatUser.setType(UserConstant.USER_TYPE_NORMAL);
wechatUser = userService.save(wechatUser);
// 设置用户角色
final UserRole userRole = new UserRole();
final PageVo pageVo = new PageVo();
pageVo.setPageNumber(1);
pageVo.setPageSize(1);
final Page<Role> roleCustomer = roleService.findByCondition("ROLE_CUSTOMER", PageUtil.initPage(pageVo));
final Role role = roleCustomer.getContent().get(0);
//客户角色
userRole.setRoleId(role.getId());
userRole.setRoleName("客户");
userRole.setUserId(wechatUser.getId());
userRoleService.save(userRole);
// 默认角色
final List<Role> roleList = roleService.findByDefaultRole(true);
if (roleList != null && !roleList.isEmpty()) {
for (Role role : roleList) {
final UserRole ur = new UserRole().setUserId(wechatUser.getId()).setRoleId(role.getId());
userRoleService.save(ur);
}
}
// 更新客户信息,绑定登录用户
customerService.updateUserId(wechatVo.getCustomId(), wechatUser.getId());
// 返回当前登录人的信息
@ -650,15 +653,6 @@ public class UserController {
} catch (Exception e) {
return ResultUtil.error(e.getMessage());
}
} else {
// 更新客户信息,绑定登录用户
customerService.updateUserId(wechatVo.getCustomId(), wechatUser.getId());
final String type = wechatUser.getType();
if (!type.contains(UserConstant.USER_TYPE_CUSTOM)) {
// 不包含,赋予下游客户身份
final String newUserType = StringUtils.isEmpty(type) ? UserConstant.USER_TYPE_CUSTOM : type + ',' + UserConstant.USER_TYPE_CUSTOM;
wechatUser.setType(newUserType);
}
}
wechatUser.setUnionid(unionid);
wechatUser.setWechatName(wechatVo.getWechatName());
@ -666,13 +660,37 @@ public class UserController {
// 统一更新小程序的一些信息
userService.save(wechatUser);
// 锁定当前登录用户的类型
wechatUser.setType(wechatVo.getUserType());
//wechatUser.setType(wechatVo.getUserType());
final String accessToken = securityUtil.getToken(userService.userToDTO(wechatUser), true);
resultMap.put("accessToken", accessToken);
// 返回当前登录人的信息
resultMap.put("user", wechatUser);
Shop shop = shopService.getShopByUserid(wechatUser.getId());
if(shop != null){
resultMap.put("shop", shop);
}
Worker worker = workerService.findByUserId(wechatUser.getId());
if(worker.getGetPushOrder() == 1){
final List<WorkerRelaPrice> workerRelaPriceList = workerRelaPriceService.selectByWorkerId(worker.getWorkerId());
List<WorkerRelaPrice> workerRelaPriceListWaimai = new ArrayList<>();
List<WorkerRelaPrice> workerRelaPriceListKuaidi = new ArrayList<>();
workerRelaPriceList.forEach(workerRelaPrice -> {
if(workerRelaPrice.getGetPushOrder() == 1){
if(workerRelaPrice.getOrderType() == 0 ){
workerRelaPriceListWaimai.add(workerRelaPrice);
}else{
workerRelaPriceListKuaidi.add(workerRelaPrice);
}
}
});
if(workerRelaPriceListWaimai.size() > 0){
resultMap.put("waimaiData", workerRelaPriceListWaimai);
}
if(workerRelaPriceListKuaidi.size() > 0){
resultMap.put("kuaidiData", workerRelaPriceListKuaidi);
}
}
resultMap.put("worker", worker);
return ResultUtil.data(resultMap);
}

4
hiver-modules/hiver-base/src/main/java/cc/hiver/base/vo/RegisterShopVo.java

@ -15,6 +15,10 @@ public class RegisterShopVo {
private String nickname;
private Integer isStudent;
private String unionid;
private String password;
private String inviteUserId;

111
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java

@ -0,0 +1,111 @@
package cc.hiver.mall.controller;
import cc.hiver.core.common.utils.ResultUtil;
import cc.hiver.core.common.vo.Result;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import cc.hiver.mall.service.mybatis.MallDeliveryOrderService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 配送订单接口
*/
@Slf4j
@RestController
@Api(tags = "配送订单接口")
@RequestMapping("/hiver/mall/delivery")
public class MallDeliveryOrderController {
@Autowired
private MallDeliveryOrderService mallDeliveryOrderService;
/**
* 分页查询配送单
* hallOnly=true 时查询抢单大厅未被接单的单
*/
@PostMapping("/page")
@ApiOperation(value = "分页查询配送单", notes = "hallOnly=true 查询抢单大厅")
public Result<Object> page(@RequestBody MallDeliveryOrderPageQuery q) {
if (Boolean.TRUE.equals(q.getHallOnly())) {
IPage<MallDeliveryOrder> page = mallDeliveryOrderService.pageDelivery(q);
java.util.Map<String, Object> result = new java.util.HashMap<>();
result.put("records", page.getRecords());
result.put("total", page.getTotal());
result.put("size", page.getSize());
result.put("current", page.getCurrent());
result.put("pages", page.getPages());
result.put("waimaiCount", mallDeliveryOrderService.countWaitGrabOrders(1));
result.put("kuaidiCount", mallDeliveryOrderService.countWaitGrabOrders(2));
return new ResultUtil<Object>().setData(result);
}
return new ResultUtil<Object>().setData(mallDeliveryOrderService.pageDelivery(q));
}
/**
* 配送员接单
*/
@PostMapping("/accept")
@ApiOperation(value = "配送员接单", notes = "抢单大厅单或指派单均通过此接口接单")
public Result accept(@RequestParam String deliveryId,
@RequestParam String workerId) {
try {
mallDeliveryOrderService.workerAccept(deliveryId, workerId);
return ResultUtil.success("接单成功");
} catch (Exception e) {
log.error("配送员接单失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 配送员取货
*/
@PostMapping("/pickup")
@ApiOperation("配送员取货(待取货 -> 配送中)")
public Result pickup(@RequestParam String deliveryId,
@RequestParam String workerId) {
try {
mallDeliveryOrderService.workerPickup(deliveryId, workerId);
return ResultUtil.success("已取货,配送中");
} catch (Exception e) {
log.error("取货失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 配送员送达
*/
@PostMapping("/complete")
@ApiOperation("配送员确认送达(配送中 -> 已送达)")
public Result complete(@RequestParam String deliveryId,
@RequestParam String workerId) {
try {
mallDeliveryOrderService.workerComplete(deliveryId, workerId);
return ResultUtil.success("已送达");
} catch (Exception e) {
log.error("送达操作失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 取消配送单
*/
@PostMapping("/cancel")
@ApiOperation("取消配送单")
public Result cancel(@RequestParam String deliveryId) {
try {
mallDeliveryOrderService.cancelDelivery(deliveryId);
return ResultUtil.success("已取消");
} catch (Exception e) {
log.error("取消配送失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
}

189
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java

@ -0,0 +1,189 @@
package cc.hiver.mall.controller;
import cc.hiver.core.common.utils.ResultUtil;
import cc.hiver.core.common.vo.Result;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.pojo.query.MallOrderPageQuery;
import cc.hiver.mall.pojo.vo.MallOrderVO;
import cc.hiver.mall.service.mybatis.MallOrderService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
/**
* 订单接口
*/
@Slf4j
@RestController
@Api(tags = "订单接口")
@RequestMapping("/hiver/mall/order")
public class MallOrderController {
@Autowired
private MallOrderService mallOrderService;
/**
* 下单普通购买 / 发起拼团 / 参团
*/
@PostMapping("/create")
@ApiOperation(value = "下单接口", notes = "支持普通购买、发起拼团、参加拼团三种模式")
public Result<MallOrderVO> createOrder(@RequestBody CreateOrderDTO dto) {
try {
MallOrderVO vo = mallOrderService.createOrder(dto);
return new ResultUtil<MallOrderVO>().setData(vo);
} catch (Exception e) {
log.error("下单失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 分页查询订单列表
*/
@PostMapping("/page")
@ApiOperation(value = "分页查询订单列表", notes = "用户端传userId,商家端传shopId")
public Result<IPage<MallOrderVO>> page(@RequestBody MallOrderPageQuery q) {
return new ResultUtil<IPage<MallOrderVO>>().setData(mallOrderService.pageOrder(q));
}
/**
* 根据拼团id查询团长订单配送费信息
*/
@GetMapping("/selectMallOrderByGroupId/{groupId}")
@ApiOperation("根据拼团id查询团长订单配送费信息")
public Result<MallOrder> selectMallOrderByGroupId(@ApiParam("订单ID") @PathVariable String groupId) {
MallOrder vo = mallOrderService.selectMallOrderByGroupId(groupId);
if (vo == null) return ResultUtil.error("订单不存在");
return new ResultUtil<MallOrder>().setData(vo);
}
/**
* 订单详情
*/
@GetMapping("/detail/{id}")
@ApiOperation("获取订单详情")
public Result<MallOrderVO> detail(@ApiParam("订单ID") @PathVariable String id) {
MallOrderVO vo = mallOrderService.getOrderDetail(id);
if (vo == null) return ResultUtil.error("订单不存在");
return new ResultUtil<MallOrderVO>().setData(vo);
}
/**
* 商家接单
*/
@PostMapping("/shopAccept")
@ApiOperation(value = "支付成功商家自动接单", notes = "将订单从「待商家接单」推进到下一状态")
public Result shopAccept(@ApiParam("订单ID") @RequestParam String orderId) {
try {
mallOrderService.shopAccept(orderId);
return ResultUtil.success("接单成功");
} catch (Exception e) {
log.error("接单失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 商家拒单
*/
@PostMapping("/shopReject")
@ApiOperation("商家拒单(自动触发退款)")
public Result shopReject(@RequestParam String orderId,
@RequestParam(required = false) String reason) {
try {
mallOrderService.shopReject(orderId, reason);
return ResultUtil.success("操作成功");
} catch (Exception e) {
log.error("拒单失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 用户取消订单
*/
@PostMapping("/cancel")
@ApiOperation(value = "用户取消订单", notes = "仅待支付/待成团状态可取消")
public Result cancelOrder(@RequestParam String orderId,
@RequestParam String userId) {
try {
mallOrderService.cancelOrder(orderId, userId);
return ResultUtil.success("已取消");
} catch (Exception e) {
log.error("取消失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 申请退款
*/
@PostMapping("/applyRefund")
@ApiOperation("用户申请退款")
public Result applyRefund(@RequestParam String orderId,
@RequestParam String userId,
@RequestParam(required = false) String reason,
@RequestParam BigDecimal amount) {
try {
mallOrderService.applyRefund(orderId, userId, reason, amount);
return ResultUtil.success("退款申请已提交");
} catch (Exception e) {
log.error("申请退款失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 商家同意退款
*/
@PostMapping("/agreeRefund")
@ApiOperation("商家同意退款")
public Result agreeRefund(@RequestParam String orderId) {
try {
mallOrderService.agreeRefund(orderId);
return ResultUtil.success("退款成功");
} catch (Exception e) {
log.error("同意退款失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 商家拒绝退款
*/
@PostMapping("/rejectRefund")
@ApiOperation("商家拒绝退款")
public Result rejectRefund(@RequestParam String orderId,
@RequestParam(required = false) String reason) {
try {
mallOrderService.rejectRefund(orderId, reason);
return ResultUtil.success("操作成功");
} catch (Exception e) {
log.error("拒绝退款失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
/**
* 用户确认完成
*/
@PostMapping("/complete")
@ApiOperation(value = "用户确认完成", notes = "外卖送达或自取后,用户手动点击确认完成")
public Result completeOrder(@RequestParam String orderId) {
try {
mallOrderService.completeOrder(orderId);
return ResultUtil.success("已完成");
} catch (Exception e) {
log.error("完成订单失败: {}", e.getMessage(), e);
return ResultUtil.error(e.getMessage());
}
}
}

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

@ -239,6 +239,8 @@ public class WorkerController {
public static class WorkerMatchDTO {
/** 取货区域id(商家所在区域) */
private String shopAreaId;
/** 接单类型 0外卖 1快递 */
private Integer orderType;
/** 送货区域id(用户收货区域) */
private String putAreaId;
/** 交易保障:true 只查 rebate_amount > 0 的配送员 */
@ -266,6 +268,7 @@ public class WorkerController {
}
List<WorkerMatchVO> list = workerRelaPriceMapper.getMatchingWorkerList(
dto.getShopAreaId(),
dto.getOrderType(),
dto.getPutAreaId(),
StrUtil.isBlank(dto.getKeyword()) ? null : dto.getKeyword().trim(),
dto.getBaozhang(),

18
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WorkerRelaPriceController.java

@ -84,6 +84,7 @@ public class WorkerRelaPriceController {
if(workerRealPriceVo.getWorkerRelaPriceList() != null){
workerRealPriceVo.getWorkerRelaPriceList().forEach(workerRelaPrice -> {
workerRelaPrice.setWorkerId(worker.getWorkerId());
workerRelaPrice.setGetPushOrder(0);
});
}
final boolean b = workerRelaPriceService.saveBatch(workerRealPriceVo.getWorkerRelaPriceList());
@ -99,14 +100,17 @@ public class WorkerRelaPriceController {
public Result batchUpdate(@RequestBody WorkerRealPriceVo workerRealPriceVo) {
Worker worker = workerService.findByUserId(workerRealPriceVo.getUserId());
//更新配送员信息 接受指派单
worker.setGetPushOrder(1);
worker.setGetPushOrder(workerRealPriceVo.getGetPushOrder());
workerService.update(worker);
// 批量更新配送规则
final boolean b = workerRelaPriceService.saveOrUpdateBatch(workerRealPriceVo.getWorkerRelaPriceList());
if (b) {
return ResultUtil.success("更新成功!");
} else {
return ResultUtil.error("保存失败!");
if(workerRealPriceVo.getGetPushOrder() != null && workerRealPriceVo.getGetPushOrder() == 1){
// 批量更新配送规则
final boolean b = workerRelaPriceService.saveOrUpdateBatch(workerRealPriceVo.getWorkerRelaPriceList());
if (b) {
return ResultUtil.success("更新成功!");
} else {
return ResultUtil.error("保存失败!");
}
}
return ResultUtil.success("更新成功!");
}
}

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/ShopDao.java

@ -15,10 +15,8 @@ limitations under the License.
*/
package cc.hiver.mall.dao;
import cc.hiver.mall.entity.Shop;
import cc.hiver.core.base.HiverBaseDao;
import cc.hiver.mall.pojo.dto.ShopRevenue;
import cc.hiver.mall.pojo.vo.QueryShopRevenueVO;
import cc.hiver.mall.entity.Shop;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@ -54,6 +52,14 @@ public interface ShopDao extends HiverBaseDao<Shop, String> {
@Query(value = "select * from t_shop where id in (select shop_id from t_shop_user where user_id = ?)",nativeQuery = true)
List<Shop> getShopInfoByUserid(String userId);
/**
* 根据用户id获取店铺信息
*
* @param userId
*/
@Query(value = "select * from t_shop where shop_owner_id = ?",nativeQuery = true)
Shop getShopByUserid(String userId);
@Query(value = "select * from t_shop where id in (select shop_id from t_customer where user_id = ?)",nativeQuery = true)
List<Shop> getAllShopByCustomer(String userId);
}

27
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java

@ -0,0 +1,27 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Map;
/**
* 配送订单 Mapper 接口
*/
@Repository
public interface MallDeliveryOrderMapper extends BaseMapper<MallDeliveryOrder> {
/**
* 分页查询配送单
*/
IPage<MallDeliveryOrder> selectPageVO(IPage<?> page, @Param("q") MallDeliveryOrderPageQuery q);
/**
* 统计配送员当前活跃单量待取货+配送中
*/
Map<String, Integer> countActiveOrdersByWorker(@Param("workerId") String workerId);
}

25
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderGoodsMapper.java

@ -0,0 +1,25 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallOrderGoods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 订单商品详情 Mapper 接口
*/
@Repository
public interface MallOrderGoodsMapper extends BaseMapper<MallOrderGoods> {
/**
* 按订单ID查询商品明细
*/
List<MallOrderGoods> selectByOrderId(@Param("orderId") String orderId);
/**
* 按订单ID集合批量查询
*/
List<MallOrderGoods> selectByOrderIds(@Param("orderIds") List<String> orderIds);
}

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderGroupMapper.java

@ -0,0 +1,12 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallOrderGroup;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* 拼团主表 Mapper 接口
*/
@Repository
public interface MallOrderGroupMapper extends BaseMapper<MallOrderGroup> {
}

23
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallOrderMapper.java

@ -0,0 +1,23 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.pojo.query.MallOrderPageQuery;
import cc.hiver.mall.pojo.vo.MallOrderVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* 核心订单 Mapper 接口
*/
@Repository
public interface MallOrderMapper extends BaseMapper<MallOrder> {
/**
* 分页查询订单含商品拼团配送信息
*/
IPage<MallOrderVO> selectPageVO(IPage<?> page, @Param("q") MallOrderPageQuery q);
MallOrder selectMallOrderByGroupId(@Param("groupId") String groupId);
}

12
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallRefundRecordMapper.java

@ -0,0 +1,12 @@
package cc.hiver.mall.dao.mapper;
import cc.hiver.mall.entity.MallRefundRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
/**
* 退款记录 Mapper 接口
*/
@Repository
public interface MallRefundRecordMapper extends BaseMapper<MallRefundRecord> {
}

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

@ -32,6 +32,7 @@ public interface WorkerRelaPriceMapper extends BaseMapper<WorkerRelaPrice> {
*/
List<WorkerMatchVO> getMatchingWorkerList(
@Param("shopAreaId") String shopAreaId,
@Param("orderType") Integer orderType,
@Param("putAreaId") String putAreaId,
@Param("keyword") String keyword,
@Param("baozhang") Boolean baozhang,

35
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java

@ -1,8 +1,10 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
@ -18,19 +20,50 @@ import java.util.Date;
@TableName("mall_delivery_order")
@ApiModel(value = "配送订单表")
public class MallDeliveryOrder implements Serializable {
@Id
@TableId
private String id;
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "关联核心订单ID")
private String orderId;
@ApiModelProperty(value = "关联拼团ID(如为拼团配送,对应多个子订单,如果是拼团 面对面团,代表所有人和团长共用一个地址,合并成一个配送订单配送)")
private String groupId;
@ApiModelProperty(value = "配送员ID")
private String workerId;
@ApiModelProperty(value = "店铺ID")
private String shopId;
@ApiModelProperty(value = "取货区域ID(订单商家的所在区域id)")
private String getAreaId;
@ApiModelProperty(value = "送货区域ID(用户收货地址区域id)")
private String putAreaId;
@ApiModelProperty(value = "配送费")
private BigDecimal deliveryFee;
@ApiModelProperty(value = "平台额外佣金(接收的指派单才有)")
private BigDecimal deliveryFeeMarketplace;
@ApiModelProperty(value = "配送状态 0:待接单 1:待取货 2:配送中 3:已送达 4:已取消")
private Integer status;
@ApiModelProperty(value = "收货人姓名(快照)")
private String receiverName;
@ApiModelProperty(value = "收货人电话(快照)")
private String receiverPhone;
@ApiModelProperty(value = "收货地址(快照,areaName+floor+roomNum合成)")
private String receiverAddress;
@ApiModelProperty(value = "商家名称(快照)")
private String shopName;
@ApiModelProperty(value = "商家联系电话(快照)")
private String shopPhone;
@ApiModelProperty(value = "商家地址(快照)")
private String shopAddress;
@ApiModelProperty(value = "加入大厅或指派时间")
private Date createTime;
@ApiModelProperty(value = "接单时间")
private Date acceptTime;
@ApiModelProperty(value = "取货时间")
private Date getTime;
@ApiModelProperty(value = "客户要求送达时间")
private Date mustFinishTime;
@ApiModelProperty(value = "送达时间")
private Date finishTime;
@ApiModelProperty(value = "订单类型 1:外卖 2:快递 3 跑腿")
private Integer deliveryType;
}

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

@ -1,8 +1,10 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
@ -18,20 +20,51 @@ import java.util.Date;
@TableName("mall_order")
@ApiModel(value = "核心订单表")
public class MallOrder implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@TableId
private String id;
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "用户ID")
private String userId;
@ApiModelProperty(value = "店铺ID")
private String shopId;
@ApiModelProperty(value = "订单类型 1:直接购买 2:拼团购买 3:面对面团")
private Integer orderType;
@ApiModelProperty(value = "配送方式 1:外卖配送 2:到店自取")
private Integer deliveryType;
@ApiModelProperty(value = "订单状态 0:待支付 1:待商家接单 01:待成团 2待配送员接单 3:待取货(配送)/待消费(自取) 4:配送中 5:已完成 6:已取消 7:待商家同意退款 8:已退款")
private Integer status;
@ApiModelProperty(value = "订单总金额(包含运费打包费等)")
private BigDecimal totalAmount;
@ApiModelProperty(value = "商品总金额")
private BigDecimal goodsAmount;
@ApiModelProperty(value = "个人实付配送费")
private BigDecimal deliveryFee;
@ApiModelProperty(value = "打包费")
private BigDecimal packageFee;
@ApiModelProperty(value = "收货地址ID")
private String addressId;
@ApiModelProperty(value = "取货区域ID(商户所在区域)")
private String getAreaId;
@ApiModelProperty(value = "送货区域ID(用户收货地址关联区域)")
private String putAreaId;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "收货人姓名(快照)")
private String receiverName;
@ApiModelProperty(value = "收货人电话(快照)")
private String receiverPhone;
@ApiModelProperty(value = "收货地址(快照,areaName+floor+roomNum合成)")
private String receiverAddress;
@ApiModelProperty(value = "商家名称(快照)")
private String shopName;
@ApiModelProperty(value = "商家联系电话(快照)")
private String shopPhone;
@ApiModelProperty(value = "商家地址(快照)")
private String shopAddress;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "支付时间")
private Date payTime;
}

11
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java

@ -1,8 +1,10 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
@ -19,12 +21,19 @@ import java.math.BigDecimal;
public class MallOrderGoods implements Serializable {
@Id
@TableId
private String id;
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "关联核心订单ID")
private String orderId;
@ApiModelProperty(value = "关联商品ID")
private String productId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "商品图片")
private String productPicture;
@ApiModelProperty(value = "商品规格json")
private String specs;
@ApiModelProperty(value = "商品单价")
private BigDecimal price;
@ApiModelProperty(value = "商品数量")
private Integer quantity;
}

19
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGroup.java

@ -1,8 +1,10 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
@ -20,18 +22,31 @@ import java.util.Date;
public class MallOrderGroup implements Serializable {
@Id
@TableId
private String id;
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "店铺ID")
private String shopId;
@ApiModelProperty(value = "团长用户ID")
private String headUserId;
@ApiModelProperty(value = "团长订单ID")
private String headOrderId;
private String groupUserIds;
@ApiModelProperty(value = "参团用户订单id集合")
private String groupOrderIds;
@ApiModelProperty(value = "拼团人数")
private Integer targetMembers;
@ApiModelProperty(value = "当前参团人数")
private Integer currentMembers;
@ApiModelProperty(value = "拼团状态 0:拼团中 1:拼团成功 2:拼团失败 3:面对面团(不公开)")
private Integer status;
@ApiModelProperty(value = "拼团价格")
private BigDecimal groupPrice;
@ApiModelProperty(value = "指定配送员ID(为空则进入抢单大厅)")
private String workerId;
@ApiModelProperty(value = "配送员佣金基数(不指定时为团长设置金额,指定时为配送员规则orderBkge)")
private BigDecimal workerCommission;
@ApiModelProperty(value = "总配送费")
private BigDecimal totalDeliveryFee;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "过期时间")
private Date expireTime;
}

11
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java

@ -1,8 +1,10 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
@ -20,12 +22,19 @@ import java.util.Date;
public class MallRefundRecord implements Serializable {
@Id
@TableId
private String id;
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "关联核心订单ID")
private String orderId;
@ApiModelProperty(value = "用户ID")
private String userId;
@ApiModelProperty(value = "退款金额")
private BigDecimal refundAmount;
@ApiModelProperty(value = "退款原因")
private String reason;
@ApiModelProperty(value = "退款状态 0:待商家同意 1:退款成功 2:商家拒绝退款")
private Integer status;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "退款成功时间")
private Date successTime;
}

39
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallReturnOrderGoods.java

@ -0,0 +1,39 @@
package cc.hiver.mall.entity;
import cc.hiver.core.common.utils.SnowFlakeUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Entity
@Table(name = "mall_return_order_goods")
@TableName("mall_return_order_goods")
@ApiModel(value = "退货订单商品详情表")
public class MallReturnOrderGoods implements Serializable {
@Id
@TableId
private String id = SnowFlakeUtil.nextId().toString();
@ApiModelProperty(value = "关联退货订单ID")
private String orderId;
@ApiModelProperty(value = "关联商品ID")
private String productId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "商品图片")
private String productPicture;
@ApiModelProperty(value = "商品规格json")
private String specs;
@ApiModelProperty(value = "商品单价")
private BigDecimal price;
@ApiModelProperty(value = "商品数量")
private Integer quantity;
}

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

@ -77,7 +77,7 @@ public class Product implements Serializable {
@ApiModelProperty(value = "商品属性列表")
private String attributeList;
@ApiModelProperty(value = "商品规格列表")
@ApiModelProperty(value = "商品规格列表 [{\"specPrice\":\"11\",\"specNum\":9999}]")
private String attributeListPrice;
@ApiModelProperty(value = "是否为推荐 0不是 1是")

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

@ -169,6 +169,9 @@ public class Shop extends HiverBaseEntity {
@ApiModelProperty(value = "副标题")
private String subtitle;
@ApiModelProperty(value = "是否为学生商家 0 不是 1是")
private Integer isStudent;
@Transient
@TableField(exist = false)
@ApiModelProperty(value = "店铺抽佣等设置")

19
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/AreaRuleDTO.java

@ -0,0 +1,19 @@
package cc.hiver.mall.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 区域匹配规则DTO
*/
@Data
@ApiModel("区域匹配规则DTO")
public class AreaRuleDTO {
@ApiModelProperty("取货区域ID")
private String getAreaId;
@ApiModelProperty("送货区域ID")
private String putAreaId;
}

91
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java

@ -0,0 +1,91 @@
package cc.hiver.mall.pojo.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 下单请求 DTO
*/
@ApiModel("下单请求DTO")
@Data
public class CreateOrderDTO {
@ApiModelProperty(value = "用户ID", required = true)
private String userId;
@ApiModelProperty(value = "店铺ID", required = true)
private String shopId;
@ApiModelProperty(value = "商品列表", required = true)
private List<OrderItemDTO> items;
@ApiModelProperty(value = "配送方式 1:外卖配送 2:到店自取", required = true)
private Integer deliveryType;
@ApiModelProperty(value = "收货地址ID(外卖配送时必填)")
private String addressId;
@ApiModelProperty(value = "取货区域ID(商户)")
private String getAreaId;
@ApiModelProperty(value = "送货区域ID(用户收货地址关联区域id)")
private String putAreaId;
@ApiModelProperty(value = "打包费(默认0)")
private BigDecimal packageFee;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "要求送达时间(预约配送时填写)")
private Date mustFinishTime;
@ApiModelProperty(value = "加入已有拼团时传入groupId")
private String groupId;
@ApiModelProperty(value = "发起拼团时传入(orderType=2且groupId为空时必填)")
private GroupParam groupParam;
@ApiModelProperty(value = "指定配送员参数(不指定则进入抢单大厅)")
private WorkerParam workerParam;
// ===================== 内嵌类 =====================
@Data
@ApiModel("订单商品条目")
public static class OrderItemDTO {
@ApiModelProperty(value = "商品ID", required = true)
private String productId;
@ApiModelProperty(value = "商品规格JSON,格式:{\"specPrice\":\"11\",\"specNum\":9999}", required = true)
private String specs;
@ApiModelProperty(value = "下单价格(以此为准,由前端传入规格对应价格)", required = true)
private BigDecimal price;
@ApiModelProperty(value = "购买数量", required = true)
private Integer quantity;
}
@Data
@ApiModel("发起拼团参数")
public static class GroupParam {
@ApiModelProperty(value = "目标拼团人数", required = true)
private Integer targetMembers;
@ApiModelProperty(value = "是否面对面团(不公开) 0否 1是", required = true)
private Integer isFaceToFace;
@ApiModelProperty(value = "团长自定义配送佣金(不指定配送员、面对面团时填写)")
private BigDecimal selfCommission;
}
@Data
@ApiModel("指定配送员参数")
public static class WorkerParam {
@ApiModelProperty(value = "配送员ID", required = true)
private String workerId;
@ApiModelProperty(value = "配送员规则佣金 orderBkge", required = true)
private BigDecimal orderBkge;
}
}

50
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallDeliveryOrderPageQuery.java

@ -0,0 +1,50 @@
package cc.hiver.mall.pojo.query;
import cc.hiver.core.base.HiverBasePageQuery;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 配送单分页查询对象
*/
@ApiModel("配送单分页查询对象")
@Data
public class MallDeliveryOrderPageQuery extends HiverBasePageQuery {
@ApiModelProperty("配送员ID")
private String workerId;
@ApiModelProperty("店铺ID")
private String shopId;
@ApiModelProperty("配送状态 0:待接单 1:待取货 2:配送中 3:已送达 4:已取消")
private Integer status;
@ApiModelProperty("取货区域ID")
private String getAreaId;
@ApiModelProperty("送货区域ID")
private String putAreaId;
@ApiModelProperty("开始时间")
private String startDate;
@ApiModelProperty("结束时间")
private String endDate;
@ApiModelProperty("是否只查询抢单大厅(workerId为空的)")
private Boolean hallOnly;
@ApiModelProperty("订单类型 1:外卖 2:快递")
private Integer deliveryType;
@ApiModelProperty("排序规则(如果有值且为deliveryFee,则按佣金降序)")
private String order;
@ApiModelProperty("外卖抢单匹配规则数组")
private java.util.List<cc.hiver.mall.pojo.dto.AreaRuleDTO> waimaiData;
@ApiModelProperty("快递抢单匹配规则数组")
private java.util.List<cc.hiver.mall.pojo.dto.AreaRuleDTO> kuaidiData;
}

38
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java

@ -0,0 +1,38 @@
package cc.hiver.mall.pojo.query;
import cc.hiver.core.base.HiverBasePageQuery;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 订单分页查询对象
*/
@ApiModel("订单分页查询对象")
@Data
public class MallOrderPageQuery extends HiverBasePageQuery {
@ApiModelProperty("用户ID")
private String userId;
@ApiModelProperty("店铺ID")
private String shopId;
@ApiModelProperty("订单状态 0:待支付 1:待商家接单 2:待配送员接单 3:待取货/待消费 4:配送中 5:已完成 6:已取消 7:待退款 8:已退款")
private Integer status;
@ApiModelProperty("订单类型 1:直接购买 2:拼团购买")
private Integer orderType;
@ApiModelProperty("配送方式 1:外卖配送 2:到店自取")
private Integer deliveryType;
@ApiModelProperty("拼团ID")
private String groupId;
@ApiModelProperty("开始时间(创建时间起)")
private String startDate;
@ApiModelProperty("结束时间(创建时间止)")
private String endDate;
}

33
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/MallOrderVO.java

@ -0,0 +1,33 @@
package cc.hiver.mall.pojo.vo;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.entity.MallOrderGoods;
import cc.hiver.mall.entity.MallOrderGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 订单详情/列表 响应 VO
*/
@ApiModel("订单响应VO")
@Data
@EqualsAndHashCode(callSuper = false)
public class MallOrderVO extends MallOrder {
@ApiModelProperty("订单商品列表")
private List<MallOrderGoods> goodsList;
@ApiModelProperty("拼团信息(拼团订单时返回)")
private MallOrderGroup groupInfo;
@ApiModelProperty("配送信息(外卖订单时返回)")
private MallDeliveryOrder deliveryInfo;
@ApiModelProperty("收货地址详情(冗余展示用)")
private String addressDetail;
}

3
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/WorkerRealPriceVo.java

@ -28,6 +28,9 @@ public class WorkerRealPriceVo implements Serializable {
@ApiModelProperty(value = " 配送规则")
private String cardPicture;
@ApiModelProperty(value = " 配送规则")
private Integer getPushOrder;
@ApiModelProperty(value = " 配送规则")
private BigDecimal highFloorFee;

57
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/quartz/GroupBuyExpireTask.java

@ -0,0 +1,57 @@
package cc.hiver.mall.quartz;
import cc.hiver.mall.entity.MallOrderGroup;
import cc.hiver.mall.service.mybatis.MallOrderGroupService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
/**
* 拼团超时自动失败定时任务
* 每30分钟扫描一次拼团中status=0/3且已超过 expireTime 的拼团
*/
@Slf4j
@Component
public class GroupBuyExpireTask {
@Autowired
private MallOrderGroupService mallOrderGroupService;
/**
* 每5分钟执行一次
*/
@Scheduled(cron = "0 0/30 * * * ?")
public void expireOverdueGroups() {
log.info("[拼团过期任务] 开始扫描超时拼团...");
try {
LambdaQueryWrapper<MallOrderGroup> qw = new LambdaQueryWrapper<>();
// 只处理「拼团中(0)」和「面对面团(3)」且已超时的
qw.in(MallOrderGroup::getStatus, 0, 3)
.lt(MallOrderGroup::getExpireTime, new Date());
List<MallOrderGroup> overdueGroups = mallOrderGroupService.list(qw);
if (overdueGroups.isEmpty()) {
log.info("[拼团过期任务] 无超时拼团");
return;
}
log.info("[拼团过期任务] 发现 {} 个超时拼团,开始处理...", overdueGroups.size());
for (MallOrderGroup group : overdueGroups) {
try {
mallOrderGroupService.expireGroup(group.getId());
log.info("[拼团过期任务] 拼团 {} 处理完成", group.getId());
} catch (Exception e) {
log.error("[拼团过期任务] 处理拼团 {} 失败: {}", group.getId(), e.getMessage(), e);
}
}
} catch (Exception e) {
log.error("[拼团过期任务] 任务执行异常: {}", e.getMessage(), e);
}
}
}

2
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/ShopService.java

@ -57,6 +57,8 @@ public interface ShopService extends HiverBaseService<Shop, String> {
*/
void updateShopAreaTitle(String shopArea, String shopAreaTitle);
Shop getShopByUserid(String userId);
/**
* 根据用户id获取店铺信息
*

43
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java

@ -0,0 +1,43 @@
package cc.hiver.mall.service.mybatis;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 配送订单 Service 接口
*/
public interface MallDeliveryOrderService extends IService<MallDeliveryOrder> {
/**
* 分页查询配送单可查抢单大厅
*/
IPage<MallDeliveryOrder> pageDelivery(MallDeliveryOrderPageQuery q);
/**
* 配送员接单抢单大厅或指派单
*/
void workerAccept(String deliveryId, String workerId);
/**
* 配送员取货
*/
void workerPickup(String deliveryId, String workerId);
/**
* 配送员送达完成
*/
void workerComplete(String deliveryId, String workerId);
/**
* 取消配送单
*/
void cancelDelivery(String deliveryId);
/**
* 统计抢单大厅未被接单的数量
* @param deliveryType 1:外卖 2:快递
*/
long countWaitGrabOrders(int deliveryType);
}

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

@ -0,0 +1,20 @@
package cc.hiver.mall.service.mybatis;
import cc.hiver.mall.entity.MallOrderGroup;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 拼团主表 Service 接口
*/
public interface MallOrderGroupService extends IService<MallOrderGroup> {
/**
* 检查拼团是否达到成团条件满足则自动激活
*/
void checkAndActivateGroup(String groupId);
/**
* 拼团过期处理发起退款所有子订单设为已取消
*/
void expireGroup(String groupId);
}

72
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java

@ -0,0 +1,72 @@
package cc.hiver.mall.service.mybatis;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.pojo.query.MallOrderPageQuery;
import cc.hiver.mall.pojo.vo.MallOrderVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.ibatis.annotations.Param;
/**
* 核心订单 Service 接口
*/
public interface MallOrderService extends IService<MallOrder> {
/**
* 创建订单支持普通购买/拼团发起/拼团参团
*/
MallOrderVO createOrder(CreateOrderDTO dto);
/**
* 分页查询订单列表
*/
IPage<MallOrderVO> pageOrder(MallOrderPageQuery q);
/**
* 查询订单详情
*/
MallOrderVO getOrderDetail(String orderId);
/**
* 商家接单待商家接单 -> 待配送员接单/待取货/待消费
*/
void shopAccept(String orderId);
/**
* 支付成功处理逻辑
*/
void paySuccess(String orderId);
/**
* 商家拒单触发自动退款
*/
void shopReject(String orderId, String reason);
/**
* 用户取消订单仅待支付/待成团状态可取消
*/
void cancelOrder(String orderId, String userId);
/**
* 申请退款
*/
void applyRefund(String orderId, String userId, String reason, java.math.BigDecimal amount);
/**
* 商家同意退款
*/
void agreeRefund(String orderId);
/**
* 商家拒绝退款
*/
void rejectRefund(String orderId, String reason);
/**
* 用户确认完成外卖送达/自取消费后确认
*/
void completeOrder(String orderId);
MallOrder selectMallOrderByGroupId(@Param("groupId") String groupId);
}

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

@ -0,0 +1,10 @@
package cc.hiver.mall.service.mybatis;
import cc.hiver.mall.entity.MallRefundRecord;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 退款记录 Service 接口
*/
public interface MallRefundRecordService extends IService<MallRefundRecord> {
}

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/ShopServiceImpl.java

@ -167,6 +167,11 @@ public class ShopServiceImpl implements ShopService {
shopDao.updateShopAreaTitle(shopArea, shopAreaTitle);
}
@Override
public Shop getShopByUserid(String userId) {
return shopDao.getShopByUserid(userId);
}
@Override
public List<Shop> getShopInfoByUserid(String userId) {
return shopDao.getShopInfoByUserid(userId);

193
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java

@ -0,0 +1,193 @@
package cc.hiver.mall.serviceimpl.mybatis;
import cc.hiver.mall.dao.mapper.MallDeliveryOrderMapper;
import cc.hiver.mall.entity.MallDeliveryOrder;
import cc.hiver.mall.entity.MallOrder;
import cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery;
import cc.hiver.mall.service.mybatis.MallDeliveryOrderService;
import cc.hiver.mall.service.mybatis.MallOrderService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
/**
* 配送订单 Service 实现
*/
@Slf4j
@Service
public class MallDeliveryOrderServiceImpl extends ServiceImpl<MallDeliveryOrderMapper, MallDeliveryOrder>
implements MallDeliveryOrderService {
// 配送状态常量
private static final int STATUS_WAIT_ACCEPT = 0; // 待接单
private static final int STATUS_WAIT_PICKUP = 1; // 待取货
private static final int STATUS_DELIVERING = 2; // 配送中
private static final int STATUS_DONE = 3; // 已送达
private static final int STATUS_CANCELLED = 4; // 已取消
// 订单状态
private static final int ORDER_STATUS_WAIT_DELIVERY = 2; // 待配送员接单
private static final int ORDER_STATUS_DELIVERING = 4; // 配送中
private static final int ORDER_STATUS_DONE = 5; // 已完成
@Autowired
@Lazy
private MallOrderService mallOrderService;
@Override
public IPage<MallDeliveryOrder> pageDelivery(MallDeliveryOrderPageQuery q) {
if (Boolean.TRUE.equals(q.getHallOnly())) {
q.setPageSize(20);
}
IPage<MallDeliveryOrder> page = new Page<>(q.getPageNum(), q.getPageSize());
return this.baseMapper.selectPageVO(page, q);
}
/**
* 配送员接单
* - 抢单大厅workerId 为空的单任何配送员均可接
* - 指派单workerId 与当前配送员相同才可接
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void workerAccept(String deliveryId, String workerId) {
MallDeliveryOrder delivery = this.getById(deliveryId);
if (delivery == null) throw new RuntimeException("配送单不存在");
if (delivery.getStatus() != STATUS_WAIT_ACCEPT) throw new RuntimeException("配送单已被接取或已取消");
// 指派单校验
if (StringUtils.isNotBlank(delivery.getWorkerId()) && !delivery.getWorkerId().equals(workerId)) {
throw new RuntimeException("该单已指定其他配送员,无法抢单");
}
// 更新配送单
LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getStatus, STATUS_WAIT_PICKUP)
.set(MallDeliveryOrder::getWorkerId, workerId)
.set(MallDeliveryOrder::getAcceptTime, new Date());
// 若为指派单(直接购买),计算平台额外佣金 = 商品总价 * 2%
if (StringUtils.isNotBlank(delivery.getOrderId())) {
MallOrder order = mallOrderService.getById(delivery.getOrderId());
if (order != null && order.getOrderType() == 1) {
BigDecimal bonus = order.getGoodsAmount()
.multiply(new BigDecimal("0.02"))
.setScale(2, RoundingMode.HALF_UP);
uw.set(MallDeliveryOrder::getDeliveryFeeMarketplace, bonus);
}
}
this.update(uw);
// 同步更新关联订单状态:待配送员接单 -> 配送中(或待取货)
if (StringUtils.isNotBlank(delivery.getOrderId())) {
MallOrder order = mallOrderService.getById(delivery.getOrderId());
if (order != null && order.getStatus() == ORDER_STATUS_WAIT_DELIVERY) {
LambdaUpdateWrapper<MallOrder> oUw = new LambdaUpdateWrapper<>();
oUw.eq(MallOrder::getId, delivery.getOrderId())
.set(MallOrder::getStatus, 3); // 待取货
mallOrderService.update(oUw);
}
}
log.info("配送员 {} 接单成功,deliveryId={}", workerId, deliveryId);
}
/**
* 配送员取货状态待取货 -> 配送中
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void workerPickup(String deliveryId, String workerId) {
MallDeliveryOrder delivery = checkWorkerAndStatus(deliveryId, workerId, STATUS_WAIT_PICKUP);
LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getStatus, STATUS_DELIVERING)
.set(MallDeliveryOrder::getGetTime, new Date());
this.update(uw);
// 同步订单状态 -> 配送中
if (StringUtils.isNotBlank(delivery.getOrderId())) {
LambdaUpdateWrapper<MallOrder> oUw = new LambdaUpdateWrapper<>();
oUw.eq(MallOrder::getId, delivery.getOrderId())
.set(MallOrder::getStatus, ORDER_STATUS_DELIVERING);
mallOrderService.update(oUw);
}
}
/**
* 配送员送达状态配送中 -> 已送达
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void workerComplete(String deliveryId, String workerId) {
MallDeliveryOrder delivery = checkWorkerAndStatus(deliveryId, workerId, STATUS_DELIVERING);
LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getStatus, STATUS_DONE)
.set(MallDeliveryOrder::getFinishTime, new Date());
this.update(uw);
// 同步订单状态 -> 已完成
if (StringUtils.isNotBlank(delivery.getOrderId())) {
LambdaUpdateWrapper<MallOrder> oUw = new LambdaUpdateWrapper<>();
oUw.eq(MallOrder::getId, delivery.getOrderId())
.set(MallOrder::getStatus, ORDER_STATUS_DONE);
mallOrderService.update(oUw);
}
}
/**
* 取消配送单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelDelivery(String deliveryId) {
MallDeliveryOrder delivery = this.getById(deliveryId);
if (delivery == null) throw new RuntimeException("配送单不存在");
LambdaUpdateWrapper<MallDeliveryOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallDeliveryOrder::getId, deliveryId)
.set(MallDeliveryOrder::getStatus, STATUS_CANCELLED);
this.update(uw);
}
// ================================================================
// 私有工具
// ================================================================
private MallDeliveryOrder checkWorkerAndStatus(String deliveryId, String workerId, int expectedStatus) {
MallDeliveryOrder delivery = this.getById(deliveryId);
if (delivery == null) throw new RuntimeException("配送单不存在");
if (delivery.getStatus() != expectedStatus) {
throw new RuntimeException("配送单状态不正确,当前状态: " + delivery.getStatus());
}
if (!workerId.equals(delivery.getWorkerId())) {
throw new RuntimeException("非本单配送员,无权操作");
}
return delivery;
}
@Override
public long countWaitGrabOrders(int deliveryType) {
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MallDeliveryOrder> query = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
query.isNull(MallDeliveryOrder::getWorkerId)
.eq(MallDeliveryOrder::getStatus, 0)
.eq(MallDeliveryOrder::getDeliveryType, deliveryType);
return this.count(query);
}
}

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

@ -0,0 +1,204 @@
package cc.hiver.mall.serviceimpl.mybatis;
import cc.hiver.core.entity.Worker;
import cc.hiver.core.service.WorkerService;
import cc.hiver.mall.dao.mapper.MallDeliveryOrderMapper;
import cc.hiver.mall.dao.mapper.MallOrderGroupMapper;
import cc.hiver.mall.dao.mapper.MallRefundRecordMapper;
import cc.hiver.mall.dao.mapper.WorkerRelaPriceMapper;
import cc.hiver.mall.entity.*;
import cc.hiver.mall.service.mybatis.MallOrderGroupService;
import cc.hiver.mall.service.mybatis.MallOrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* 拼团主表 Service 实现
*/
@Slf4j
@Service
public class MallOrderGroupServiceImpl extends ServiceImpl<MallOrderGroupMapper, MallOrderGroup>
implements MallOrderGroupService {
// 拼团状态
private static final int GROUP_STATUS_FORMING = 0;
private static final int GROUP_STATUS_SUCCESS = 1;
private static final int GROUP_STATUS_FAIL = 2;
private static final int GROUP_STATUS_FACE2FACE = 3;
// 订单状态
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_CANCELLED = 6;
@Autowired
@Lazy
private MallOrderService mallOrderService;
@Autowired
private MallRefundRecordMapper mallRefundRecordMapper;
@Autowired
private MallDeliveryOrderMapper mallDeliveryOrderMapper;
@Autowired
private WorkerService workerService;
@Autowired
private WorkerRelaPriceMapper workerRelaPriceMapper;
/**
* 检查是否成团满足则自动激活所有子订单
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void checkAndActivateGroup(String groupId) {
MallOrderGroup group = this.getById(groupId);
if (group == null) return;
// 已成团或已失败不重复处理
if (group.getStatus() == GROUP_STATUS_SUCCESS) return;
if (group.getCurrentMembers() < group.getTargetMembers()) return;
// 达到成团人数 -> 激活
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
guw.eq(MallOrderGroup::getId, groupId)
.set(MallOrderGroup::getStatus, GROUP_STATUS_SUCCESS);
this.update(guw);
// 将所有关联子订单从"待成团"改为"待商家接单"
// 通过 head_order_id 找到团长订单,再通过 groupUserIds 关联
// 策略:查所有 orderId 在 groupOrderIds 中、shopId 相同、status=待成团的订单
// 更简洁:直接查 mall_order 中 id in (groupOrderIds split) AND shop_id = ? AND status = 10
List<String> orderIdList = Arrays.asList(group.getGroupOrderIds().split(","));
LambdaQueryWrapper<MallOrder> oqw = new LambdaQueryWrapper<>();
oqw.eq(MallOrder::getShopId, group.getShopId())
.eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP)
.in(MallOrder::getId, orderIdList);
List<MallOrder> waitingOrders = mallOrderService.list(oqw);
boolean isFace2Face = (group.getStatus() == GROUP_STATUS_FACE2FACE
|| (group.getStatus() == GROUP_STATUS_SUCCESS
&& this.getById(groupId).getStatus() == GROUP_STATUS_FACE2FACE));
for (MallOrder order : waitingOrders) {
int targetStatus = (order.getDeliveryType() != null && order.getDeliveryType() == 1) ? 2 : 3;
LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>();
ouw.eq(MallOrder::getId, order.getId())
.set(MallOrder::getStatus, targetStatus);
mallOrderService.update(ouw);
if (order.getDeliveryType() != null && order.getDeliveryType() == 1) {
if (isFace2Face && !order.getId().equals(group.getHeadOrderId())) {
// 面对面团参团人共享团长配送单,因此参团人本身没有配送单,直接跳过
continue;
}
// 取出预创建的运单进行校验
LambdaQueryWrapper<MallDeliveryOrder> dqw = new LambdaQueryWrapper<>();
dqw.eq(MallDeliveryOrder::getOrderId, order.getId()).last("LIMIT 1");
MallDeliveryOrder delivery = mallDeliveryOrderMapper.selectOne(dqw);
if (delivery != null) {
if (StringUtils.isNotBlank(delivery.getWorkerId())) {
boolean isValid = false;
Worker worker = workerService.findById(delivery.getWorkerId());
if (worker != null && worker.getIsOnLine() != null && worker.getIsOnLine() == 1
&& worker.getGetPushOrder() != null && worker.getGetPushOrder() == 1) {
LambdaQueryWrapper<WorkerRelaPrice> ruleQuery = new LambdaQueryWrapper<>();
ruleQuery.eq(WorkerRelaPrice::getWorkerId, delivery.getWorkerId())
.eq(WorkerRelaPrice::getOrderType, 0)
.eq(WorkerRelaPrice::getGetPushOrder, 1)
.eq(WorkerRelaPrice::getGetAreaId, delivery.getGetAreaId())
.eq(WorkerRelaPrice::getPutAreaId, delivery.getPutAreaId())
.last("LIMIT 1");
WorkerRelaPrice rule = workerRelaPriceMapper.selectOne(ruleQuery);
if (rule != null) {
isValid = true;
}
}
if (!isValid) {
// 配送员下线或规则关闭 -> 去掉 workerId 进入抢单大厅,保留原有补贴金
delivery.setWorkerId("");
}
}
// 统一修改运单状态为0使其生效
delivery.setStatus(0);
mallDeliveryOrderMapper.updateById(delivery);
}
}
}
log.info("拼团 {} 成团成功,激活 {} 条子订单", groupId, waitingOrders.size());
}
/**
* 拼团过期所有子订单取消并生成退款记录
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void expireGroup(String groupId) {
MallOrderGroup group = this.getById(groupId);
if (group == null) return;
if (group.getStatus() != GROUP_STATUS_FORMING && group.getStatus() != GROUP_STATUS_FACE2FACE) return;
// 标记拼团失败
LambdaUpdateWrapper<MallOrderGroup> guw = new LambdaUpdateWrapper<>();
guw.eq(MallOrderGroup::getId, groupId)
.set(MallOrderGroup::getStatus, GROUP_STATUS_FAIL);
this.update(guw);
// 找出所有待成团子订单
List<String> orderIdList = Arrays.asList(group.getGroupOrderIds().split(","));
LambdaQueryWrapper<MallOrder> oqw = new LambdaQueryWrapper<>();
oqw.eq(MallOrder::getShopId, group.getShopId())
.eq(MallOrder::getStatus, ORDER_STATUS_WAIT_GROUP)
.in(MallOrder::getId, orderIdList);
List<MallOrder> waitingOrders = mallOrderService.list(oqw);
for (MallOrder order : waitingOrders) {
// 取消子订单
LambdaUpdateWrapper<MallOrder> ouw = new LambdaUpdateWrapper<>();
ouw.eq(MallOrder::getId, order.getId())
.set(MallOrder::getStatus, ORDER_STATUS_CANCELLED);
mallOrderService.update(ouw);
// 取消未生效的配送单
if (order.getDeliveryType() != null && order.getDeliveryType() == 1) {
LambdaUpdateWrapper<MallDeliveryOrder> duw = new LambdaUpdateWrapper<>();
duw.eq(MallDeliveryOrder::getOrderId, order.getId())
.in(MallDeliveryOrder::getStatus, java.util.Arrays.asList(-1, 0))
.set(MallDeliveryOrder::getStatus, 4);
mallDeliveryOrderMapper.update(null, duw);
}
// 生成退款记录
MallRefundRecord refund = new MallRefundRecord();
refund.setOrderId(order.getId());
refund.setUserId(order.getUserId());
refund.setRefundAmount(order.getTotalAmount());
refund.setReason("拼团超时未成团,系统自动退款");
refund.setStatus(0);
refund.setCreateTime(new Date());
mallRefundRecordMapper.insert(refund);
}
log.info("拼团 {} 已过期,取消 {} 条子订单并生成退款记录", groupId, waitingOrders.size());
}
}

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

@ -0,0 +1,769 @@
package cc.hiver.mall.serviceimpl.mybatis;
import cc.hiver.mall.dao.mapper.*;
import cc.hiver.mall.entity.*;
import cc.hiver.mall.pojo.dto.CreateOrderDTO;
import cc.hiver.mall.pojo.query.MallOrderPageQuery;
import cc.hiver.mall.pojo.vo.MallOrderVO;
import cc.hiver.mall.service.mybatis.MallDeliveryOrderService;
import cc.hiver.mall.service.mybatis.MallOrderGroupService;
import cc.hiver.mall.service.mybatis.MallOrderService;
import cc.hiver.mall.service.mybatis.MallRefundRecordService;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 核心订单 Service 实现
*/
@Slf4j
@Service
public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder> implements MallOrderService {
// 订单状态常量
private static final int STATUS_WAIT_PAY = 0;
private static final int STATUS_WAIT_GROUP = 10; // 数据库存储为 "01",用数字10代替
private static final int STATUS_WAIT_SHOP = 1;
private static final int STATUS_WAIT_DELIVERY = 2;
private static final int STATUS_WAIT_PICKUP = 3;
private static final int STATUS_DELIVERING = 4;
private static final int STATUS_DONE = 5;
private static final int STATUS_CANCELLED = 6;
private static final int STATUS_WAIT_REFUND = 7;
private static final int STATUS_REFUNDED = 8;
// 配送方式常量
private static final int DELIVERY_TYPE_EXPRESS = 1; // 外卖
private static final int DELIVERY_TYPE_SELF = 2; // 自取
// 订单类型常量
private static final int ORDER_TYPE_NORMAL = 1; // 直接购买
private static final int ORDER_TYPE_GROUP = 2; // 拼团购买
// 拼团状态常量
private static final int GROUP_STATUS_FORMING = 0; // 拼团中
private static final int GROUP_STATUS_SUCCESS = 1; // 拼团成功
private static final int GROUP_STATUS_FAIL = 2; // 拼团失败
private static final int GROUP_STATUS_FACE2FACE = 3; // 面对面团
@Autowired
private MallOrderGoodsMapper mallOrderGoodsMapper;
@Autowired
private MallOrderGroupMapper mallOrderGroupMapper;
@Autowired
private MallDeliveryOrderMapper mallDeliveryOrderMapper;
@Autowired
private MallRefundRecordMapper mallRefundRecordMapper;
@Autowired
private MallOrderGroupService mallOrderGroupService;
@Autowired
private MallDeliveryOrderService mallDeliveryOrderService;
@Autowired
private MallRefundRecordService mallRefundRecordService;
@Autowired
private ProductMapper productMapper;
@Autowired
private UserAddressMapper userAddressMapper;
@Autowired
private cc.hiver.mall.dao.mapper.ShopMapper shopMapper;
// ================================================================
// 核心下单逻辑
// ================================================================
@Override
@Transactional(rollbackFor = Exception.class)
public MallOrderVO createOrder(CreateOrderDTO dto) {
// 1. 基础校验
if (dto.getItems() == null || dto.getItems().isEmpty()) {
throw new RuntimeException("订单商品不能为空");
}
if(StringUtils.isBlank(dto.getGroupId())){
if (DELIVERY_TYPE_EXPRESS == dto.getDeliveryType() && StringUtils.isBlank(dto.getAddressId())) {
throw new RuntimeException("外卖配送时收货地址不能为空");
}
}
// 2. 扣库存 & 计算商品总金额
BigDecimal goodsAmount = BigDecimal.ZERO;
List<MallOrderGoods> goodsSnapshots = new ArrayList<>();
for (CreateOrderDTO.OrderItemDTO item : dto.getItems()) {
Product product = productMapper.selectById(item.getProductId());
if (product == null) {
throw new RuntimeException("商品不存在: " + item.getProductId());
}
// 扣减库存(更新 attributeListPrice 中的 specNum)
deductStock(product, item.getQuantity());
// 累计商品金额
goodsAmount = goodsAmount.add(item.getPrice().multiply(new BigDecimal(item.getQuantity())));
// 构建商品快照(orderId 后续填充)
MallOrderGoods snapshot = new MallOrderGoods();
snapshot.setProductId(item.getProductId());
snapshot.setProductName(product.getProductName());
snapshot.setProductPicture(product.getProductPicture());
snapshot.setSpecs(item.getSpecs());
snapshot.setPrice(item.getPrice());
snapshot.setQuantity(item.getQuantity());
goodsSnapshots.add(snapshot);
}
BigDecimal packageFee = dto.getPackageFee() != null ? dto.getPackageFee() : BigDecimal.ZERO;
// 3. 判断订单类型
boolean isGroupOrder = dto.getGroupParam() != null || StringUtils.isNotBlank(dto.getGroupId());
if (!isGroupOrder) {
// ---- 普通订单 ----
return createNormalOrder(dto, goodsAmount, packageFee, goodsSnapshots);
} else if (StringUtils.isNotBlank(dto.getGroupId())) {
// ---- 参团 ----
return joinGroup(dto, goodsAmount, packageFee, goodsSnapshots);
} else {
// ---- 发起拼团 ----
return startGroup(dto, goodsAmount, packageFee, goodsSnapshots);
}
}
/**
* 创建普通订单
*/
private MallOrderVO createNormalOrder(CreateOrderDTO dto, BigDecimal goodsAmount,
BigDecimal packageFee, List<MallOrderGoods> goodsSnapshots) {
BigDecimal deliveryFee = BigDecimal.ZERO;
if (DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
if (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) {
// 有指定配送员
deliveryFee = dto.getWorkerParam().getOrderBkge() != null ? dto.getWorkerParam().getOrderBkge() : BigDecimal.ZERO;
} else {
// 没有指定配送员,用户自设佣金
if (dto.getWorkerParam() != null && dto.getWorkerParam().getOrderBkge() != null) {
deliveryFee = dto.getWorkerParam().getOrderBkge();
} else if (dto.getGroupParam() != null && dto.getGroupParam().getSelfCommission() != null) {
deliveryFee = dto.getGroupParam().getSelfCommission();
}
}
}
MallOrder order = buildBaseOrder(dto, ORDER_TYPE_NORMAL, goodsAmount, deliveryFee, packageFee);
// 待支付
order.setStatus(STATUS_WAIT_PAY);
this.save(order);
// 保存商品快照
saveGoodsSnapshots(goodsSnapshots, order.getId());
// 若外卖,创建配送单(初始状态 -1:待支付/待成团)
if (DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
createDeliveryOrder(order, dto, null, -1, null);
}
return buildVO(order, goodsSnapshots, null, null);
}
/**
* 发起拼团
*/
private MallOrderVO startGroup(CreateOrderDTO dto, BigDecimal goodsAmount,
BigDecimal packageFee, List<MallOrderGoods> goodsSnapshots) {
CreateOrderDTO.GroupParam gp = dto.getGroupParam();
if (gp == null || gp.getTargetMembers() == null || gp.getTargetMembers() < 2) {
throw new RuntimeException("拼团人数不能少于2人");
}
boolean isFace2Face = gp.getIsFaceToFace() != null && gp.getIsFaceToFace() == 1;
// 计算面对面团团长个人配送费(当前1人)
BigDecimal deliveryFee = BigDecimal.ZERO;
if (isFace2Face && DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
BigDecimal selfComm = null;
if (dto.getWorkerParam() != null && StringUtils.isBlank(dto.getWorkerParam().getWorkerId()) && dto.getWorkerParam().getOrderBkge() != null) {
selfComm = dto.getWorkerParam().getOrderBkge();
} else if (gp.getSelfCommission() != null) {
selfComm = gp.getSelfCommission();
}
CreateOrderDTO.WorkerParam validWorker = (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) ? dto.getWorkerParam() : null;
Integer targetMembers = gp.getTargetMembers() != null ? gp.getTargetMembers() : 2;
deliveryFee = calcPersonalFee(
calcTotalDeliveryFee(selfComm, validWorker, targetMembers),
targetMembers
);
} else if (!isFace2Face && DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
// 普通拼团,团长有独立配送费计算
if (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) {
deliveryFee = dto.getWorkerParam().getOrderBkge() != null ? dto.getWorkerParam().getOrderBkge() : BigDecimal.ZERO;
} else {
if (dto.getWorkerParam() != null && dto.getWorkerParam().getOrderBkge() != null) {
deliveryFee = dto.getWorkerParam().getOrderBkge();
} else if (gp != null && gp.getSelfCommission() != null) {
deliveryFee = gp.getSelfCommission();
}
}
}
// 创建团长订单(初始状态:待支付)
MallOrder order = buildBaseOrder(dto, ORDER_TYPE_GROUP, goodsAmount, deliveryFee, packageFee);
order.setStatus(STATUS_WAIT_PAY);
this.save(order);
// 创建拼团主记录
MallOrderGroup group = new MallOrderGroup();
group.setShopId(dto.getShopId());
group.setHeadUserId(dto.getUserId());
group.setTargetMembers(gp.getTargetMembers());
group.setCurrentMembers(1);
if(goodsSnapshots != null && !goodsSnapshots.isEmpty()){
group.setGroupPrice(goodsSnapshots.get(0).getPrice());
}
group.setHeadOrderId(order.getId());
group.setStatus(isFace2Face ? GROUP_STATUS_FACE2FACE : GROUP_STATUS_FORMING);
group.setGroupOrderIds(order.getId());
group.setCreateTime(new Date());
// 拼团过期时间:24小时后
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, 24);
group.setExpireTime(cal.getTime());
// 设置配送员和佣金信息
if (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) {
group.setWorkerId(dto.getWorkerParam().getWorkerId());
group.setWorkerCommission(dto.getWorkerParam().getOrderBkge());
} else {
if (dto.getWorkerParam() != null && dto.getWorkerParam().getOrderBkge() != null) {
group.setWorkerCommission(dto.getWorkerParam().getOrderBkge());
} else if (gp.getSelfCommission() != null) {
group.setWorkerCommission(gp.getSelfCommission());
}
}
// 面对面团才有总配送费概念(非面对面团各自结算)
if (isFace2Face) {
CreateOrderDTO.WorkerParam validWorker = (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) ? dto.getWorkerParam() : null;
group.setTotalDeliveryFee(calcTotalDeliveryFee(group.getWorkerCommission(), validWorker, gp.getTargetMembers()));
}
mallOrderGroupMapper.insert(group);
// 保存商品快照
saveGoodsSnapshots(goodsSnapshots, order.getId());
// 若外卖,创建独立或共用配送单(初始状态 -1:待支付/待成团)
if (DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
createDeliveryOrder(order, dto, group.getId(), -1, isFace2Face ? group.getTotalDeliveryFee() : null);
}
// 将 groupId 关联到订单 (MallOrder 没有 groupId 字段,通过 MallOrderGroup 关联已足够)
return buildVO(order, goodsSnapshots, group, null);
}
/**
* 参团
*/
@Transactional(rollbackFor = Exception.class)
public MallOrderVO joinGroup(CreateOrderDTO dto, BigDecimal goodsAmount,
BigDecimal packageFee, List<MallOrderGoods> goodsSnapshots) {
MallOrderGroup group = mallOrderGroupMapper.selectById(dto.getGroupId());
if (group == null) {
throw new RuntimeException("拼团不存在");
}
if (group.getStatus() != GROUP_STATUS_FORMING && group.getStatus() != GROUP_STATUS_FACE2FACE) {
throw new RuntimeException("当前拼团状态不可参团");
}
if (group.getCurrentMembers() >= group.getTargetMembers()) {
throw new RuntimeException("拼团已满员");
}
boolean isFace2Face = (group.getStatus() == GROUP_STATUS_FACE2FACE);
// 面对面团:不再重新计算,直接使用根据 targetMembers 均摊的金额
BigDecimal deliveryFee = BigDecimal.ZERO;
if (isFace2Face && DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
deliveryFee = calcPersonalFee(group.getTotalDeliveryFee(), group.getTargetMembers());
} else if (!isFace2Face && DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
// 普通拼团参团时独立计算个人配送费
if (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) {
deliveryFee = dto.getWorkerParam().getOrderBkge() != null ? dto.getWorkerParam().getOrderBkge() : BigDecimal.ZERO;
} else {
if (dto.getWorkerParam() != null && dto.getWorkerParam().getOrderBkge() != null) {
deliveryFee = dto.getWorkerParam().getOrderBkge();
} else if (dto.getGroupParam() != null && dto.getGroupParam().getSelfCommission() != null) {
deliveryFee = dto.getGroupParam().getSelfCommission();
}
}
}
// 创建参团人订单(初始状态:待支付)
MallOrder order = buildBaseOrder(dto, ORDER_TYPE_GROUP, goodsAmount, deliveryFee, packageFee);
order.setStatus(STATUS_WAIT_PAY);
this.save(order);
saveGoodsSnapshots(goodsSnapshots, order.getId());
// 更新拼团人数和成员列表
int newMembers = group.getCurrentMembers() + 1;
String newOrderIds = group.getGroupOrderIds() + "," + order.getId();
group.setCurrentMembers(newMembers);
group.setGroupOrderIds(newOrderIds);
mallOrderGroupMapper.updateById(group);
// 普通拼团参团(非面对面),若外卖,为参团人创建独立配送单(初始状态 -1:待支付/待成团)
if (!isFace2Face && DELIVERY_TYPE_EXPRESS == dto.getDeliveryType()) {
createDeliveryOrder(order, dto, group.getId(), -1, null);
}
return buildVO(order, goodsSnapshots, group, null);
}
// ================================================================
// 订单生命周期
// ================================================================
@Override
@Transactional(rollbackFor = Exception.class)
public void shopAccept(String orderId) {
MallOrder order = getAndCheckOrder(orderId, STATUS_WAIT_PAY);
if (DELIVERY_TYPE_EXPRESS == order.getDeliveryType()) {
// 外卖:进入待配送员接单
updateOrderStatus(orderId, STATUS_WAIT_DELIVERY);
} else {
// 自取:进入待消费
updateOrderStatus(orderId, STATUS_WAIT_PICKUP);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void paySuccess(String orderId) {
MallOrder order = this.getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (order.getStatus() != STATUS_WAIT_PAY) {
return; // 已经处理过或状态不对
}
if (order.getOrderType() == ORDER_TYPE_NORMAL) {
// 直购支付成功 -> 跳过待商家接单,直接进入待配送或待消费
if (DELIVERY_TYPE_EXPRESS == order.getDeliveryType()) {
updateOrderStatus(orderId, STATUS_WAIT_DELIVERY);
// 激活配送单,状态改为待接单(0)
LambdaUpdateWrapper<MallDeliveryOrder> duw = new LambdaUpdateWrapper<>();
duw.eq(MallDeliveryOrder::getOrderId, orderId)
.set(MallDeliveryOrder::getStatus, 0);
mallDeliveryOrderMapper.update(null, duw);
} else {
updateOrderStatus(orderId, STATUS_WAIT_PICKUP);
}
} else if (order.getOrderType() == ORDER_TYPE_GROUP) {
// 拼团订单支付成功 -> 改为待成团
updateOrderStatus(orderId, STATUS_WAIT_GROUP);
// 查找属于该订单的拼团并检查成团条件
LambdaQueryWrapper<MallOrderGroup> gq = new LambdaQueryWrapper<>();
gq.eq(MallOrderGroup::getHeadOrderId, orderId);
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq);
if (group == null) {
// 如果不是团长,按参团人查找
LambdaQueryWrapper<MallOrderGroup> gq2 = new LambdaQueryWrapper<>();
gq2.eq(MallOrderGroup::getShopId, order.getShopId())
.like(MallOrderGroup::getGroupOrderIds, order.getId())
.last("LIMIT 1");
group = mallOrderGroupMapper.selectOne(gq2);
}
if (group != null) {
mallOrderGroupService.checkAndActivateGroup(group.getId());
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void shopReject(String orderId, String reason) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
updateOrderStatus(orderId, STATUS_CANCELLED);
// 先取消相关的配送单
LambdaUpdateWrapper<MallDeliveryOrder> duw = new LambdaUpdateWrapper<>();
duw.eq(MallDeliveryOrder::getOrderId, orderId)
.in(MallDeliveryOrder::getStatus, java.util.Arrays.asList(-1, 0))
.set(MallDeliveryOrder::getStatus, 4);
mallDeliveryOrderMapper.update(null, duw);
// 自动生成退款记录
createRefundRecord(order, order.getTotalAmount(), "商家拒单: " + reason);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelOrder(String orderId, String userId) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (!userId.equals(order.getUserId())) throw new RuntimeException("无权操作该订单");
if (order.getStatus() != STATUS_WAIT_PAY && order.getStatus() != STATUS_WAIT_GROUP) {
throw new RuntimeException("当前订单状态不允许取消");
}
updateOrderStatus(orderId, STATUS_CANCELLED);
// 先取消相关的配送单
LambdaUpdateWrapper<MallDeliveryOrder> duw = new LambdaUpdateWrapper<>();
duw.eq(MallDeliveryOrder::getOrderId, orderId)
.in(MallDeliveryOrder::getStatus, java.util.Arrays.asList(-1, 0))
.set(MallDeliveryOrder::getStatus, 4);
mallDeliveryOrderMapper.update(null, duw);
// 若已支付(待成团状态),生成退款记录
if (order.getStatus() == STATUS_WAIT_GROUP) {
createRefundRecord(order, order.getTotalAmount(), "用户取消拼团");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void applyRefund(String orderId, String userId, String reason, BigDecimal amount) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (!userId.equals(order.getUserId())) throw new RuntimeException("无权操作该订单");
updateOrderStatus(orderId, STATUS_WAIT_REFUND);
createRefundRecord(order, amount, reason);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void agreeRefund(String orderId) {
MallOrder order = getAndCheckOrder(orderId, STATUS_WAIT_REFUND);
updateOrderStatus(orderId, STATUS_REFUNDED);
// 更新退款记录为成功
LambdaUpdateWrapper<MallRefundRecord> uw = new LambdaUpdateWrapper<>();
uw.eq(MallRefundRecord::getOrderId, orderId)
.set(MallRefundRecord::getStatus, 1)
.set(MallRefundRecord::getSuccessTime, new Date());
mallRefundRecordMapper.update(null, uw);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void rejectRefund(String orderId, String reason) {
getAndCheckOrder(orderId, STATUS_WAIT_REFUND);
// 退款拒绝,恢复到已完成(或待商家处理状态)
updateOrderStatus(orderId, STATUS_DONE);
// 更新退款记录为拒绝
LambdaUpdateWrapper<MallRefundRecord> uw = new LambdaUpdateWrapper<>();
uw.eq(MallRefundRecord::getOrderId, orderId)
.set(MallRefundRecord::getStatus, 2);
mallRefundRecordMapper.update(null, uw);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void completeOrder(String orderId) {
MallOrder order = getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (order.getStatus() != STATUS_WAIT_PICKUP && order.getStatus() != STATUS_DELIVERING) {
throw new RuntimeException("当前状态不可完成");
}
updateOrderStatus(orderId, STATUS_DONE);
}
@Override
public MallOrder selectMallOrderByGroupId(String groupId) {
return this.baseMapper.selectMallOrderByGroupId(groupId);
}
// ================================================================
// 查询
// ================================================================
@Override
public IPage<MallOrderVO> pageOrder(MallOrderPageQuery q) {
IPage<MallOrderVO> page = new Page<>(q.getPageNum(), q.getPageSize());
return this.baseMapper.selectPageVO(page, q);
}
@Override
public MallOrderVO getOrderDetail(String orderId) {
MallOrder order = this.getById(orderId);
if (order == null) return null;
MallOrderVO vo = new MallOrderVO();
org.springframework.beans.BeanUtils.copyProperties(order, vo);
// 商品列表
vo.setGoodsList(mallOrderGoodsMapper.selectByOrderId(orderId));
// 配送信息
LambdaQueryWrapper<MallDeliveryOrder> dq = new LambdaQueryWrapper<>();
dq.eq(MallDeliveryOrder::getOrderId, orderId).last("LIMIT 1");
vo.setDeliveryInfo(mallDeliveryOrderMapper.selectOne(dq));
// 拼团信息(拼团订单才查)
if (ORDER_TYPE_GROUP == order.getOrderType()) {
// 通过 headOrderId 或 groupUserIds 中包含此 userId 查找拼团
LambdaQueryWrapper<MallOrderGroup> gq = new LambdaQueryWrapper<>();
// 团长订单匹配 headOrderId
gq.eq(MallOrderGroup::getHeadOrderId, orderId);
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq);
if (group == null) {
// 参团人:通过 userId + shopId 匹配
LambdaQueryWrapper<MallOrderGroup> gq2 = new LambdaQueryWrapper<>();
gq2.eq(MallOrderGroup::getShopId, order.getShopId())
.like(MallOrderGroup::getGroupOrderIds, order.getId())
.last("LIMIT 1");
group = mallOrderGroupMapper.selectOne(gq2);
}
vo.setGroupInfo(group);
}
return vo;
}
// ================================================================
// 私有工具方法
// ================================================================
/**
* 扣减商品库存更新 attributeListPrice 中的 specNum
*/
private void deductStock(Product product, int quantity) {
String attrJson = product.getAttributeListPrice();
if (StringUtils.isBlank(attrJson)) throw new RuntimeException("商品规格信息异常");
try {
JSONArray arr = JSONUtil.parseArray(attrJson);
if (arr.isEmpty()) throw new RuntimeException("商品规格信息异常");
JSONObject spec = arr.getJSONObject(0);
int current = spec.getInt("specNum");
if (current < quantity) throw new RuntimeException("商品库存不足: " + product.getProductName());
spec.set("specNum", current - quantity);
LambdaUpdateWrapper<Product> uw = new LambdaUpdateWrapper<>();
uw.eq(Product::getId, product.getId())
.set(Product::getAttributeListPrice, arr.toString());
productMapper.update(null, uw);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("库存扣减失败: " + e.getMessage());
}
}
/**
* 构建基础订单对象
*/
private MallOrder buildBaseOrder(CreateOrderDTO dto, int orderType, BigDecimal goodsAmount,
BigDecimal deliveryFee, BigDecimal packageFee) {
MallOrder order = new MallOrder();
order.setUserId(dto.getUserId());
order.setShopId(dto.getShopId());
order.setOrderType(orderType);
order.setDeliveryType(dto.getDeliveryType());
order.setAddressId(dto.getAddressId());
order.setGetAreaId(dto.getGetAreaId());
order.setPutAreaId(dto.getPutAreaId());
order.setGoodsAmount(goodsAmount);
order.setDeliveryFee(deliveryFee);
order.setPackageFee(packageFee);
order.setTotalAmount(goodsAmount.add(deliveryFee).add(packageFee));
order.setRemark(dto.getRemark());
order.setCreateTime(new Date());
// ---- 快照收货人信息 ----
if (StringUtils.isNotBlank(dto.getAddressId())) {
UserAddress addr = userAddressMapper.selectById(dto.getAddressId());
if (addr != null) {
order.setReceiverName(addr.getReceiverName());
order.setReceiverPhone(addr.getReceiverPhone());
// 地址合成:楼座+楼层+门牌
String addrStr = (addr.getAreaName() != null ? addr.getAreaName() : "")
+ (addr.getFloor() != null ? " " + addr.getFloor() + "层" : "")
+ (addr.getRoomNum() != null ? " " + addr.getRoomNum() : "");
order.setReceiverAddress(addrStr.trim());
}
}
// ---- 快照商家信息 ----
if (StringUtils.isNotBlank(dto.getShopId())) {
Shop shop = shopMapper.selectById(dto.getShopId());
if (shop != null) {
order.setShopName(shop.getShopName());
order.setShopPhone(shop.getContactPhone());
order.setShopAddress(shop.getShopAddress());
}
}
return order;
}
/**
* 批量保存商品快照
*/
private void saveGoodsSnapshots(List<MallOrderGoods> snapshots, String orderId) {
for (MallOrderGoods snap : snapshots) {
snap.setOrderId(orderId);
mallOrderGoodsMapper.insert(snap);
}
}
/**
* 创建配送单
*
* @param initialStatus 初始化状态-1: 待支付待成团, 0: 待接单
*/
private void createDeliveryOrder(MallOrder order, CreateOrderDTO dto, String groupId, int initialStatus, BigDecimal overrideDeliveryFee) {
MallDeliveryOrder delivery = new MallDeliveryOrder();
delivery.setOrderId(order.getId());
delivery.setGroupId(groupId);
delivery.setShopId(dto.getShopId());
delivery.setStatus(initialStatus);
delivery.setDeliveryType(1); // 外卖单
// 总配送费放入 delivery
delivery.setDeliveryFee(overrideDeliveryFee != null ? overrideDeliveryFee : order.getDeliveryFee());
delivery.setCreateTime(new Date());
if (dto.getMustFinishTime() != null) {
delivery.setMustFinishTime(dto.getMustFinishTime());
}
// 指定配送员
if (dto.getWorkerParam() != null && StringUtils.isNotBlank(dto.getWorkerParam().getWorkerId())) {
delivery.setWorkerId(dto.getWorkerParam().getWorkerId());
if (order.getOrderType() == ORDER_TYPE_NORMAL) {
BigDecimal bonus = order.getGoodsAmount()
.multiply(new BigDecimal("0.02"))
.setScale(2, RoundingMode.DOWN);
delivery.setDeliveryFeeMarketplace(bonus);
}
}
// 取货区域ID(商户所在区域,前端必传)
delivery.setGetAreaId(dto.getGetAreaId());
// 送货区域ID:优先使用前端传入值,兜底从地址表查询
if (StringUtils.isNotBlank(dto.getPutAreaId())) {
delivery.setPutAreaId(dto.getPutAreaId());
} else if (StringUtils.isNotBlank(dto.getAddressId())) {
UserAddress addr = userAddressMapper.selectById(dto.getAddressId());
if (addr != null) {
delivery.setPutAreaId(addr.getAreaId());
}
}
// ---- 快照字段:从订单复制收货人及商家信息 ----
delivery.setReceiverName(order.getReceiverName());
delivery.setReceiverPhone(order.getReceiverPhone());
delivery.setReceiverAddress(order.getReceiverAddress());
delivery.setShopName(order.getShopName());
delivery.setShopPhone(order.getShopPhone());
delivery.setShopAddress(order.getShopAddress());
mallDeliveryOrderMapper.insert(delivery);
}
/**
* 计算总配送费面对面团专用
*/
private BigDecimal calcTotalDeliveryFee(BigDecimal selfCommission,
CreateOrderDTO.WorkerParam workerParam, int members) {
if (workerParam != null && workerParam.getOrderBkge() != null) {
// 指定配送员:基础 + 额外(人数>2时每多一人+0.5)是用户承担的总配送费
BigDecimal extra = members > 2
? new BigDecimal(members - 2).multiply(new BigDecimal("0.5"))
: BigDecimal.ZERO;
return workerParam.getOrderBkge().add(extra);
} else if (selfCommission != null) {
// 不指定配送员:团长设定佣金
return selfCommission;
}
return BigDecimal.ZERO;
}
/**
* group 构建 WorkerParam已存团用
*/
private CreateOrderDTO.WorkerParam buildWorkerParam(MallOrderGroup group) {
CreateOrderDTO.WorkerParam wp = new CreateOrderDTO.WorkerParam();
wp.setWorkerId(group.getWorkerId());
wp.setOrderBkge(group.getWorkerCommission());
return wp;
}
/**
* 配送费分摊向上取整保留2位小数
*/
private BigDecimal calcPersonalFee(BigDecimal totalFee, int members) {
if (members <= 0) return totalFee;
BigDecimal result = totalFee
.divide(new BigDecimal(members), 2, RoundingMode.CEILING);
return result;
}
/**
* 更新订单状态
*/
private void updateOrderStatus(String orderId, int status) {
LambdaUpdateWrapper<MallOrder> uw = new LambdaUpdateWrapper<>();
uw.eq(MallOrder::getId, orderId).set(MallOrder::getStatus, status);
this.update(uw);
}
/**
* 获取订单并校验状态
*/
private MallOrder getAndCheckOrder(String orderId, int expectedStatus) {
MallOrder order = this.getById(orderId);
if (order == null) throw new RuntimeException("订单不存在");
if (order.getStatus() != expectedStatus) {
throw new RuntimeException("订单状态不正确,当前状态: " + order.getStatus());
}
return order;
}
/**
* 创建退款记录
*/
private void createRefundRecord(MallOrder order, BigDecimal amount, String reason) {
MallRefundRecord record = new MallRefundRecord();
record.setOrderId(order.getId());
record.setUserId(order.getUserId());
record.setRefundAmount(amount);
record.setReason(reason);
record.setStatus(0); // 待商家同意
record.setCreateTime(new Date());
mallRefundRecordMapper.insert(record);
}
/**
* 构建返回 VO
*/
private MallOrderVO buildVO(MallOrder order, List<MallOrderGoods> goods,
MallOrderGroup group, MallDeliveryOrder delivery) {
MallOrderVO vo = new MallOrderVO();
org.springframework.beans.BeanUtils.copyProperties(order, vo);
vo.setGoodsList(goods);
vo.setGroupInfo(group);
vo.setDeliveryInfo(delivery);
return vo;
}
}

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

@ -0,0 +1,15 @@
package cc.hiver.mall.serviceimpl.mybatis;
import cc.hiver.mall.dao.mapper.MallRefundRecordMapper;
import cc.hiver.mall.entity.MallRefundRecord;
import cc.hiver.mall.service.mybatis.MallRefundRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* 退款记录 Service 实现
*/
@Service
public class MallRefundRecordServiceImpl extends ServiceImpl<MallRefundRecordMapper, MallRefundRecord>
implements MallRefundRecordService {
}

105
hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.hiver.mall.dao.mapper.MallDeliveryOrderMapper">
<resultMap id="deliveryMap" type="cc.hiver.mall.entity.MallDeliveryOrder">
<id column="id" property="id"/>
<result column="order_id" property="orderId"/>
<result column="group_id" property="groupId"/>
<result column="worker_id" property="workerId"/>
<result column="shop_id" property="shopId"/>
<result column="get_area_id" property="getAreaId"/>
<result column="put_area_id" property="putAreaId"/>
<result column="delivery_fee" property="deliveryFee"/>
<result column="delivery_fee_marketplace" property="deliveryFeeMarketplace"/>
<result column="status" property="status"/>
<result column="receiver_name" property="receiverName"/>
<result column="receiver_phone" property="receiverPhone"/>
<result column="receiver_address" property="receiverAddress"/>
<result column="shop_name" property="shopName"/>
<result column="shop_phone" property="shopPhone"/>
<result column="shop_address" property="shopAddress"/>
<result column="create_time" property="createTime"/>
<result column="accept_time" property="acceptTime"/>
<result column="get_time" property="getTime"/>
<result column="must_finish_time" property="mustFinishTime"/>
<result column="finish_time" property="finishTime"/>
<result column="delivery_type" property="deliveryType"/>
</resultMap>
<!-- 分页查询配送单,支持多条件过滤(抢单大厅:hallOnly=true时只查workerId为空的) -->
<select id="selectPageVO" resultMap="deliveryMap">
SELECT
d.id, d.order_id, d.group_id, d.worker_id, d.shop_id,
d.get_area_id, d.put_area_id, d.delivery_fee, d.delivery_fee_marketplace,
d.status, d.create_time, d.accept_time, d.get_time, d.must_finish_time, d.finish_time,
d.receiver_name, d.receiver_phone, d.receiver_address,
d.shop_name, d.shop_phone, d.shop_address, d.delivery_type
FROM mall_delivery_order d
<where>
<if test="q.workerId != null and q.workerId != ''">
AND d.worker_id = #{q.workerId}
</if>
<if test="q.shopId != null and q.shopId != ''">
AND d.shop_id = #{q.shopId}
</if>
<if test="q.status != null">
AND d.status = #{q.status}
</if>
<if test="q.getAreaId != null and q.getAreaId != ''">
AND d.get_area_id = #{q.getAreaId}
</if>
<if test="q.putAreaId != null and q.putAreaId != ''">
AND d.put_area_id = #{q.putAreaId}
</if>
<if test="q.hallOnly != null and q.hallOnly == true">
AND (d.worker_id IS NULL OR d.worker_id = '')
AND d.status = 0
</if>
<if test="q.startDate != null and q.startDate != ''">
AND DATE(d.create_time) &gt;= #{q.startDate}
</if>
<if test="q.endDate != null and q.endDate != ''">
AND DATE(d.create_time) &lt;= #{q.endDate}
</if>
<if test="q.deliveryType != null">
AND d.delivery_type = #{q.deliveryType}
</if>
</where>
ORDER BY
<if test="(q.waimaiData != null and q.waimaiData.size() > 0) or (q.kuaidiData != null and q.kuaidiData.size() > 0)">
(CASE
<if test="q.waimaiData != null and q.waimaiData.size() > 0">
<foreach collection="q.waimaiData" item="rule">
WHEN d.get_area_id = #{rule.getAreaId} AND d.put_area_id = #{rule.putAreaId} AND d.delivery_type = 1 THEN 1
</foreach>
</if>
<if test="q.kuaidiData != null and q.kuaidiData.size() > 0">
<foreach collection="q.kuaidiData" item="rule">
WHEN d.get_area_id = #{rule.getAreaId} AND d.put_area_id = #{rule.putAreaId} AND d.delivery_type = 2 THEN 1
</foreach>
</if>
ELSE 0
END) DESC,
</if>
<if test="q.order != null and q.order == 'deliveryFee'">
d.delivery_fee DESC,
</if>
d.create_time DESC
</select>
<!--
统计配送员当前活跃单量
返回:waitPickup(待取货数)、delivering(配送中数)
-->
<select id="countActiveOrdersByWorker" resultType="map">
SELECT
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS waitPickup,
SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) AS delivering
FROM mall_delivery_order
WHERE worker_id = #{workerId}
AND status IN (1, 2)
</select>
</mapper>

34
hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGoodsMapper.xml

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.hiver.mall.dao.mapper.MallOrderGoodsMapper">
<resultMap id="goodsMap" type="cc.hiver.mall.entity.MallOrderGoods">
<id column="id" property="id"/>
<result column="order_id" property="orderId"/>
<result column="product_id" property="productId"/>
<result column="product_name" property="productName"/>
<result column="product_picture" property="productPicture"/>
<result column="specs" property="specs"/>
<result column="price" property="price"/>
<result column="quantity" property="quantity"/>
</resultMap>
<!-- 按单条订单ID查询商品明细 -->
<select id="selectByOrderId" resultMap="goodsMap">
SELECT id, order_id, product_id, product_name, product_picture, specs, price, quantity
FROM mall_order_goods
WHERE order_id = #{orderId}
</select>
<!-- 按多个订单ID批量查询 -->
<select id="selectByOrderIds" resultMap="goodsMap">
SELECT id, order_id, product_id, product_name, product_picture, specs, price, quantity
FROM mall_order_goods
WHERE order_id IN
<foreach collection="orderIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper>

23
hiver-modules/hiver-mall/src/main/resources/mapper/MallOrderGroupMapper.xml

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.hiver.mall.dao.mapper.MallOrderGroupMapper">
<resultMap id="groupMap" type="cc.hiver.mall.entity.MallOrderGroup">
<id column="id" property="id"/>
<result column="shop_id" property="shopId"/>
<result column="head_user_id" property="headUserId"/>
<result column="head_order_id" property="headOrderId"/>
<result column="group_Order_ids" property="groupOrderIds"/>
<result column="target_members" property="targetMembers"/>
<result column="current_members" property="currentMembers"/>
<result column="status" property="status"/>
<result column="group_price" property="groupPrice"/>
<result column="worker_id" property="workerId"/>
<result column="worker_commission" property="workerCommission"/>
<result column="total_delivery_fee" property="totalDeliveryFee"/>
<result column="create_time" property="createTime"/>
<result column="expire_time" property="expireTime"/>
</resultMap>
</mapper>

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

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.hiver.mall.dao.mapper.MallOrderMapper">
<!-- 通用结果映射 -->
<resultMap id="mallOrderVOMap" type="cc.hiver.mall.entity.MallOrder">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="shop_id" property="shopId"/>
<result column="order_type" property="orderType"/>
<result column="delivery_type" property="deliveryType"/>
<result column="status" property="status"/>
<result column="total_amount" property="totalAmount"/>
<result column="goods_amount" property="goodsAmount"/>
<result column="delivery_fee" property="deliveryFee"/>
<result column="package_fee" property="packageFee"/>
<result column="address_id" property="addressId"/>
<result column="remark" property="remark"/>
<result column="receiver_name" property="receiverName"/>
<result column="receiver_phone" property="receiverPhone"/>
<result column="receiver_address" property="receiverAddress"/>
<result column="shop_name" property="shopName"/>
<result column="shop_phone" property="shopPhone"/>
<result column="shop_address" property="shopAddress"/>
<result column="create_time" property="createTime"/>
<result column="pay_time" property="payTime"/>
</resultMap>
<!-- 分页查询订单(不挂载商品明细,由Service层补填) -->
<select id="selectPageVO" resultMap="mallOrderVOMap">
SELECT
o.id, o.user_id, o.shop_id, o.order_type, o.delivery_type,
o.status, o.total_amount, o.goods_amount, o.delivery_fee,
o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time,
o.receiver_name, o.receiver_phone, o.receiver_address,
o.shop_name, o.shop_phone, o.shop_address
FROM mall_order o
<where>
<if test="q.userId != null and q.userId != ''">
AND o.user_id = #{q.userId}
</if>
<if test="q.shopId != null and q.shopId != ''">
AND o.shop_id = #{q.shopId}
</if>
<if test="q.status != null">
AND o.status = #{q.status}
</if>
<if test="q.orderType != null">
AND o.order_type = #{q.orderType}
</if>
<if test="q.deliveryType != null">
AND o.delivery_type = #{q.deliveryType}
</if>
<if test="q.startDate != null and q.startDate != ''">
AND DATE(o.create_time) &gt;= #{q.startDate}
</if>
<if test="q.endDate != null and q.endDate != ''">
AND DATE(o.create_time) &lt;= #{q.endDate}
</if>
</where>
ORDER BY o.create_time DESC
</select>
<select id="selectMallOrderByGroupId" resultMap="mallOrderVOMap">
SELECT
o.id, o.user_id, o.shop_id, o.order_type, o.delivery_type,
o.status, o.total_amount, o.goods_amount, o.delivery_fee,
o.package_fee, o.address_id, o.remark, o.create_time, o.pay_time,
o.receiver_name, o.receiver_phone, o.receiver_address,
o.shop_name, o.shop_phone, o.shop_address
FROM mall_order o LEFT JOIN mall_order_group og ON o.id = og.head_order_id
<where>
<if test="groupId != null and groupId != ''">
AND og.id = #{groupId}
</if>
</where>
</select>
</mapper>

17
hiver-modules/hiver-mall/src/main/resources/mapper/MallRefundRecordMapper.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cc.hiver.mall.dao.mapper.MallRefundRecordMapper">
<resultMap id="refundMap" type="cc.hiver.mall.entity.MallRefundRecord">
<id column="id" property="id"/>
<result column="order_id" property="orderId"/>
<result column="user_id" property="userId"/>
<result column="refund_amount" property="refundAmount"/>
<result column="reason" property="reason"/>
<result column="status" property="status"/>
<result column="create_time" property="createTime"/>
<result column="success_time" property="successTime"/>
</resultMap>
</mapper>

22
hiver-modules/hiver-mall/src/main/resources/mapper/ShopMapper.xml

@ -41,6 +41,7 @@
<result column="subtitle" property="subtitle" jdbcType="VARCHAR" />
<result column="shoprank" property="shoprank" jdbcType="INTEGER" />
<result column="sale_count" property="saleCount" jdbcType="INTEGER" />
<result column="is_student" property="isStudent" jdbcType="INTEGER" />
</resultMap>
<resultMap id="ResultMapWithBLOBs" type="cc.hiver.mall.entity.Shop" extends="BaseResultMap" >
<result column="remark" property="remark" jdbcType="LONGVARCHAR" />
@ -49,7 +50,7 @@
id, create_by, create_time, del_flag, update_by, update_time, shop_name, shop_owner_id, shop_manger_id, region,
region_id, shop_area, shop_area_title, shop_type, shop_type_title, shop_address, year_fee, charge_time, start_time,
end_time, status, remark, business_district_level, contact_phone, shop_icon, defaulted, ali_account, ali_name,
rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop_score,isbrandflag,subtitle,shoprank,sale_count
rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop_score,isbrandflag,subtitle,shoprank,sale_count,is_student
</sql>
<sql id="Blob_Column_List" >
remark
@ -70,11 +71,11 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
insert into t_shop (id, create_by, create_time, del_flag, update_by, update_time, shop_name, shop_owner_id, shop_manger_id, region,
region_id, shop_area, shop_area_title, shop_type, shop_type_title, shop_address, year_fee, charge_time, start_time,
end_time, status, remark, business_district_level, contact_phone, shop_icon, defaulted, ali_account, ali_name,
rebate_amount, attr_id, printing_method, shop_images, shop_lename, shop_lecard, shop_score, isbrandflag, subtitle,shoprank, sale_count)
rebate_amount, attr_id, printing_method, shop_images, shop_lename, shop_lecard, shop_score, isbrandflag, subtitle,shoprank, sale_count,is_student)
values (#{id,jdbcType=VARCHAR}, #{createBy,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{delFlag,jdbcType=INTEGER}, #{updateBy,jdbcType=VARCHAR}, #{updateTime,jdbcType=TIMESTAMP}, #{shopName,jdbcType=VARCHAR}, #{shopOwnerId,jdbcType=VARCHAR}, #{shopMangerId,jdbcType=VARCHAR}, #{region,jdbcType=VARCHAR},
#{regionId,jdbcType=VARCHAR}, #{shopArea,jdbcType=VARCHAR}, #{shopAreaTitle,jdbcType=VARCHAR}, #{shopType,jdbcType=VARCHAR}, #{shopTypeTitle,jdbcType=VARCHAR}, #{shopAddress,jdbcType=VARCHAR}, #{yearFee,jdbcType=VARCHAR}, #{chargeTime,jdbcType=VARCHAR}, #{startTime,jdbcType=VARCHAR},
#{endTime,jdbcType=VARCHAR}, #{status,jdbcType=INTEGER}, #{remark,jdbcType=LONGVARCHAR}, #{businessDistrictLevel,jdbcType=VARCHAR}, #{contactPhone,jdbcType=VARCHAR}, #{shopIcon,jdbcType=VARCHAR}, #{defaulted,jdbcType=INTEGER}, #{aliAccount,jdbcType=VARCHAR}, #{aliName,jdbcType=VARCHAR},
#{rebateAmount,jdbcType=VARCHAR}, #{attrId,jdbcType=VARCHAR}, #{printingMethod,jdbcType=VARCHAR}, #{shopImages,jdbcType=VARCHAR}, #{shopLename,jdbcType=VARCHAR}, #{shopLecard,jdbcType=VARCHAR}, #{shopScore,jdbcType=DECIMAL}, #{isbrandflag,jdbcType=INTEGER}, #{subtitle,jdbcType=VARCHAR}, #{shoprank,jdbcType=INTEGER}, #{saleCount,jdbcType=INTEGER})
#{rebateAmount,jdbcType=VARCHAR}, #{attrId,jdbcType=VARCHAR}, #{printingMethod,jdbcType=VARCHAR}, #{shopImages,jdbcType=VARCHAR}, #{shopLename,jdbcType=VARCHAR}, #{shopLecard,jdbcType=VARCHAR}, #{shopScore,jdbcType=DECIMAL}, #{isbrandflag,jdbcType=INTEGER}, #{subtitle,jdbcType=VARCHAR}, #{shoprank,jdbcType=INTEGER}, #{saleCount,jdbcType=INTEGER}, #{isStudent,jdbcType=INTEGER})
</insert>
<insert id="insertSelective" parameterType="cc.hiver.mall.entity.Shop" >
insert into t_shop
@ -172,6 +173,9 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
<if test="saleCount != null" >
sale_count,
</if>
<if test="isStudent != null" >
is_student
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
@ -267,6 +271,9 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
<if test="saleCount != null" >
#{saleCount,jdbcType=INTEGER},
</if>
<if test="isStudent != null" >
#{isStudent,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="cc.hiver.mall.entity.Shop" >
@ -362,6 +369,9 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
<if test="saleCount != null" >
sale_count = #{saleCount,jdbcType=INTEGER},
</if>
<if test="isStudent != null" >
is_student = #{isStudent,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -396,7 +406,8 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
isbrandflag = #{isbrandflag,jdbcType=INTEGER},
subtitle = #{subtitle,jdbcType=VARCHAR},
shoprank = #{shoprank,jdbcType=INTEGER},
sale_count = #{saleCount,jdbcType=INTEGER}
sale_count = #{saleCount,jdbcType=INTEGER},
is_student = #{isStudent,jdbcType=INTEGER}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="cc.hiver.mall.entity.Shop" >
@ -438,7 +449,8 @@ rebate_amount, attr_id, printing_method,shop_images,shop_lename,shop_lecard,shop
isbrandflag = #{isbrandflag,jdbcType=INTEGER},
subtitle = #{subtitle,jdbcType=VARCHAR},
shoprank = #{shoprank,jdbcType=INTEGER},
sale_count = #{saleCount,jdbcType=INTEGER}
sale_count = #{saleCount,jdbcType=INTEGER},
is_student = #{isStudent,jdbcType=INTEGER}
where id = #{id,jdbcType=VARCHAR}
</update>
<select id="findByUserId" resultType="cc.hiver.mall.entity.Shop">

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

@ -91,6 +91,7 @@
ON p.worker_id = w.worker_id
AND p.get_push_order = 1
AND p.get_area_id = #{shopAreaId}
AND p.order_type = #{orderType}
AND p.put_area_id = #{putAreaId}
/* 关联配送订单表:LEFT JOIN 保留无任何订单的配送员 */
LEFT JOIN mall_delivery_order d

1
hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatController.java

@ -140,6 +140,7 @@ public class WechatController {
if (phoneNumber != null) {
final User u = securityUtil.getCurrUser();
u.setMobile(phoneNumber);
u.setUsername(phoneNumber);
userService.update(u);
// 删除缓存
//redisTemplate.delete(USER + u.getUsername());

12
hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPayController.java

@ -1,5 +1,6 @@
package cc.hiver.social.controller;
import cc.hiver.mall.service.mybatis.MallOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@ -15,8 +16,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/hiver/api/wechat/pay")
@ -27,6 +30,9 @@ public class WechatPayController {
@Autowired
private WechatPayConfig config;
@Autowired
private MallOrderService mallOrderService;
@Autowired
private ResourceLoader resourceLoader;
@ -61,7 +67,7 @@ public class WechatPayController {
@PostMapping("/unified-order")
public ResponseEntity<Map<String, String>> jsapiPay(@RequestBody Map<String, String> request) throws Exception {
String outTradeNo = "ORDER_" + System.currentTimeMillis();
String outTradeNo = "ORDER_" + request.get("outTradeNo");
String description = request.get("description");
String openid = request.get("openid");
long amount = Long.parseLong(request.get("amount")); // 单位:分
@ -75,6 +81,8 @@ public class WechatPayController {
payParams.put("message","调起微信支付失败");
return ResponseEntity.ok(payParams);
}else{
//订单支付成功逻辑
mallOrderService.paySuccess(request.get("outTradeNo"));
// 2. 生成前端支付参数
Map<String, String> payParams = buildPaySign(prepayId);
payParams.put("code","200");

Loading…
Cancel
Save