diff --git a/hiver-core/src/main/java/cc/hiver/core/serviceimpl/WorkerServiceImpl.java b/hiver-core/src/main/java/cc/hiver/core/serviceimpl/WorkerServiceImpl.java index d0efc19d..9520c456 100644 --- a/hiver-core/src/main/java/cc/hiver/core/serviceimpl/WorkerServiceImpl.java +++ b/hiver-core/src/main/java/cc/hiver/core/serviceimpl/WorkerServiceImpl.java @@ -65,7 +65,7 @@ public class WorkerServiceImpl implements WorkerService { final Path workerStatusField = root.get("workerStatus"); final Path mobileField = root.get("mobile"); final Path createTimeField = root.get("createTime"); - final Path signPersonField = root.get("signPerson"); + //final Path signPersonField = root.get("signPerson"); final Path isOnLineField = root.get("isOnLine"); final Path userIdField = root.get("userId"); @@ -80,9 +80,9 @@ public class WorkerServiceImpl implements WorkerService { list.add(cb.equal(userIdField, worker.getUserId())); } - if (CharSequenceUtil.isNotBlank(worker.getSignPerson())) { + /*if (CharSequenceUtil.isNotBlank(worker.getSignPerson())) { list.add(cb.equal(signPersonField, worker.getSignPerson())); - } + }*/ // 模糊搜素 if (CharSequenceUtil.isNotBlank(worker.getWorkerName())) { list.add(cb.like(workerNameField, '%' + worker.getWorkerName() + '%')); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java index f7b3712a..5e14f4ef 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallDeliveryOrderController.java @@ -19,6 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; +import java.util.List; +import java.util.Map; /** * 配送订单接口 @@ -101,13 +103,25 @@ public class MallDeliveryOrderController { return new ResultUtil().setData(mallDeliveryOrderService.pageDelivery(q)); } + @PostMapping("/countOrderByStatus") + @ApiOperation(value = "查询当前配送员指派单和已接单数量") + public Result>> countOrderByStatus(@RequestParam String workerId) { + return new ResultUtil>>().setData(mallDeliveryOrderService.countOrdersByStatus(workerId)); + } + + @PostMapping("/pagebyworker") + @ApiOperation(value = "分页查询配送员配送单") + public Result pageByWorker(@RequestBody MallDeliveryOrderPageQuery q) { + return new ResultUtil().setData(mallDeliveryOrderService.pageDeliveryByStatus(q)); + } + /** * 配送员接单 */ @PostMapping("/accept") @ApiOperation(value = "配送员接单", notes = "抢单大厅单或指派单均通过此接口接单") public Result accept(@RequestParam String deliveryId, - @RequestParam String workerId,@RequestParam String regionId,@RequestParam String groupId) { + @RequestParam String workerId,@RequestParam String workerName,@RequestParam String regionId,@RequestParam String groupId) { try { if(StrUtil.isBlank(workerId)){ //当前人没有注册兼职,先赋予兼职身份 @@ -123,7 +137,7 @@ public class MallDeliveryOrderController { worker.setScore(BigDecimal.valueOf(5)); workerService.save(worker); } - Integer result = mallDeliveryOrderService.workerAccept(deliveryId, workerId,groupId); + Integer result = mallDeliveryOrderService.workerAccept(deliveryId, workerId,workerName,groupId); if(result == 0){ return ResultUtil.success("配送单不存在"); }else if (result == 1){ @@ -134,7 +148,7 @@ public class MallDeliveryOrderController { return ResultUtil.success("接单成功"); } }else{ - Integer result = mallDeliveryOrderService.workerAccept(deliveryId, workerId,groupId); + Integer result = mallDeliveryOrderService.workerAccept(deliveryId, workerId,workerName,groupId); if(result == 0){ return ResultUtil.success("配送单不存在"); }else if (result == 1){ @@ -151,6 +165,22 @@ public class MallDeliveryOrderController { } } + /** + * 配送员到店 + */ + @PostMapping("/arriveShop") + @ApiOperation("配送员到店)") + public Result arriveShop(@RequestParam String deliveryId, + @RequestParam String workerId) { + try { + mallDeliveryOrderService.arriveShop(deliveryId, workerId); + return ResultUtil.success("到店成功"); + } catch (Exception e) { + log.error("到店失败: {}", e.getMessage(), e); + return ResultUtil.error(e.getMessage()); + } + } + /** * 配送员取货 */ @@ -160,7 +190,7 @@ public class MallDeliveryOrderController { @RequestParam String workerId) { try { mallDeliveryOrderService.workerPickup(deliveryId, workerId); - return ResultUtil.success("已取货,配送中"); + return ResultUtil.success("取货成功"); } catch (Exception e) { log.error("取货失败: {}", e.getMessage(), e); return ResultUtil.error(e.getMessage()); @@ -176,7 +206,7 @@ public class MallDeliveryOrderController { @RequestParam String workerId) { try { mallDeliveryOrderService.workerComplete(deliveryId, workerId); - return ResultUtil.success("已送达"); + return ResultUtil.success("送达成功"); } catch (Exception e) { log.error("送达操作失败: {}", e.getMessage(), e); return ResultUtil.error(e.getMessage()); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java index b24e5d50..3c2625be 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallOrderController.java @@ -93,14 +93,14 @@ public class MallOrderController { /** * 商家接单 */ - @PostMapping("/shopAccept") - @ApiOperation(value = "支付成功商家自动接单", notes = "将订单从「待商家接单」推进到下一状态") - public Result shopAccept(@ApiParam("订单ID") @RequestParam String orderId) { + @PostMapping("/shopMakeTime") + @ApiOperation(value = "商家出餐") + public Result shopMakeTime(@ApiParam("订单ID") @RequestParam String orderId) { try { - mallOrderService.shopAccept(orderId); - return ResultUtil.success("接单成功"); + mallOrderService.shopMakeTime(orderId); + return ResultUtil.success("出餐成功"); } catch (Exception e) { - log.error("接单失败: {}", e.getMessage(), e); + log.error("出餐失败: {}", e.getMessage(), e); return ResultUtil.error(e.getMessage()); } } @@ -125,7 +125,7 @@ public class MallOrderController { * 用户取消订单 */ @PostMapping("/cancel") - @ApiOperation(value = "用户取消订单", notes = "仅待支付/待成团状态可取消") + @ApiOperation(value = "用户取消订单") public Result cancelOrder(@RequestParam String orderId, @RequestParam String userId) { try { diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallRefundRecordController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallRefundRecordController.java new file mode 100644 index 00000000..302fa69b --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/MallRefundRecordController.java @@ -0,0 +1,43 @@ +package cc.hiver.mall.controller; + +import cc.hiver.core.common.utils.ResultUtil; +import cc.hiver.core.common.vo.Result; +import cc.hiver.mall.entity.MallRefundRecord; +import cc.hiver.mall.service.mybatis.MallRefundRecordService; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 订单接口 + */ +@Slf4j +@RestController +@Api(tags = "订单售后接口") +@RequestMapping("/hiver/mall/refund") +public class MallRefundRecordController { + + @Autowired + private MallRefundRecordService mallRefundRecordService; + + /** + * 下单(普通购买 / 发起拼团 / 参团) + */ + @PostMapping("/create") + @ApiOperation(value = "售后提交接口") + public Result create(@RequestBody MallRefundRecord mallRefundRecord) { + try { + mallRefundRecordService.create(mallRefundRecord); + return ResultUtil.success("提交售后成功"); + } catch (Exception e) { + log.error("下单失败: {}", e.getMessage(), e); + return ResultUtil.error(e.getMessage()); + } + } + +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java new file mode 100644 index 00000000..f00058db --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/controller/WechatPayController.java @@ -0,0 +1,247 @@ +package cc.hiver.mall.controller; + +import cc.hiver.mall.entity.MallOrder; +import cc.hiver.mall.service.mybatis.MallOrderService; +import cc.hiver.mall.serviceimpl.UnifiedOrderService; +import cc.hiver.mall.utils.KeyUtils; +import cc.hiver.mall.utils.WechatPayConfig; +import cc.hiver.mall.utils.WechatPaySigner; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@RestController +@RequestMapping("/hiver/api/wechat/pay") +public class WechatPayController { + + private static final Logger log = LoggerFactory.getLogger(WechatPayController.class); + + @Autowired + private UnifiedOrderService orderService; + + @Autowired + private WechatPayConfig config; + + @Autowired + private MallOrderService mallOrderService; + + @Autowired + private WechatPaySigner signer; + + @Autowired + private ResourceLoader resourceLoader; + + private PrivateKey privateKey; + + private final OkHttpClient httpClient = new OkHttpClient(); + private final ObjectMapper objectMapper = new ObjectMapper(); + + @PostConstruct + public void init() throws Exception { + Resource resource = resourceLoader.getResource(config.getPrivateKeyPath()); + byte[] keyBytes; + try (InputStream is = resource.getInputStream()) { + keyBytes = readAllBytes(is); + } + + String key = new String(keyBytes, StandardCharsets.UTF_8) + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + + byte[] decodedKey = Base64.getDecoder().decode(key); + privateKey = KeyUtils.loadPrivateKey(decodedKey); + } + + private byte[] readAllBytes(InputStream inputStream) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] data = new byte[1024]; + int nRead; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + return buffer.toByteArray(); + } + + @PostMapping("/unified-order") + public ResponseEntity> jsapiPay(@RequestBody Map request) throws Exception { + String outTradeNo = "ORDER_" + request.get("outTradeNo"); + String description = request.get("description"); + String openid = request.get("openid"); + long amount = Long.parseLong(request.get("amount")); // 单位:分 + + Map orderResult = orderService.createJsapiOrder(outTradeNo, description, openid, amount); + String prepayId = (String) orderResult.get("prepay_id"); + + if (prepayId == null) { + Map payParams = new HashMap<>(); + payParams.put("code","101"); + payParams.put("message","调起微信支付失败"); + return ResponseEntity.ok(payParams); + }else{ + // 2. 生成前端支付参数 + Map payParams = buildPaySign(prepayId); + payParams.put("code","200"); + return ResponseEntity.ok(payParams); + } + } + + @PostMapping("/paySuccess") + public ResponseEntity> paySuccess(@RequestBody Map request) throws Exception { + //订单支付成功逻辑 + mallOrderService.paySuccess(request.get("outTradeNo")); + Map payParams = new HashMap<>(); + payParams.put("code","200"); + payParams.put("message","支付成功订单更新"); + return ResponseEntity.ok(payParams); + } + + public Map buildPaySign(String prepayId) throws Exception { + // 1. 生成参数 + String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); // 秒 + String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 32); + String packageValue = "prepay_id=" + prepayId; + String signType = "RSA"; + + // 2. 构建签名字符串 + String message = String.join("\n", + config.getAppId(), + timeStamp, + nonceStr, + packageValue, + "" // 最后一个 \n + ); + + // 3. 使用商户私钥签名(和 API v3 同一套私钥) + String paySign = KeyUtils.signWithSha256Rsa(message, privateKey); // ← 复用之前的签名方法 + + // 4. 返回给前端 + Map result = new HashMap<>(); + result.put("timeStamp", timeStamp); + result.put("nonceStr", nonceStr); + result.put("package", packageValue); + result.put("signType", signType); + result.put("paySign", paySign); + return result; + } + + /** + * 微信退款通用方法 + * 传入 orderId,根据订单信息发起微信支付退款(全额退款) + */ + @PostMapping("/refund") + public ResponseEntity> refund(@RequestBody Map request) { + Map result = new HashMap<>(); + String orderId = request.get("orderId"); + + try { + // 1. 查询订单信息 + MallOrder order = mallOrderService.getById(orderId); + if (order == null) { + result.put("code", "101"); + result.put("message", "订单不存在"); + return ResponseEntity.ok(result); + } + + // 2. 构建退款请求参数 + // 原支付交易号(与下单时一致,带 ORDER_ 前缀) + String outTradeNo = "ORDER_" + orderId; + // 退款单号(唯一,使用 REFUND_ + orderId + 时间戳避免重复) + String outRefundNo = "REFUND_" + orderId + "_" + System.currentTimeMillis(); + // 订单总金额(元 → 分) + long totalFee = order.getTotalAmount() + .multiply(new java.math.BigDecimal("100")) + .longValue(); + // 退款金额(默认全额退款,如果前端传了 refundAmount 则使用前端的值) + long refundFee = totalFee; + if (request.get("refundAmount") != null) { + refundFee = new java.math.BigDecimal(request.get("refundAmount")) + .multiply(new java.math.BigDecimal("100")) + .longValue(); + } + + // 退款原因 + String reason = request.get("reason") != null ? request.get("reason") : "订单退款"; + + // 3. 构建请求 Body + Map reqBody = new HashMap<>(); + reqBody.put("out_trade_no", outTradeNo); + reqBody.put("out_refund_no", outRefundNo); + reqBody.put("reason", reason); + + Map amountMap = new HashMap<>(); + amountMap.put("refund", refundFee); + amountMap.put("total", totalFee); + amountMap.put("currency", "CNY"); + reqBody.put("amount", amountMap); + + String jsonBody = objectMapper.writeValueAsString(reqBody); + + // 4. 签名并调用微信退款 API + String refundUrl = WechatPayConfig.REFUND_URL; + String authorization = signer.sign("POST", refundUrl, jsonBody); + + // 【修改点】使用标准的 create 方法,参数顺序是 (MediaType, String) + okhttp3.RequestBody body = okhttp3.RequestBody.create( + MediaType.parse("application/json; charset=utf-8"), + jsonBody + ); + + Request httpRequest = new Request.Builder() + .url(refundUrl) + .post(body) + .addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorization) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .addHeader("User-Agent", "MyApp/1.0") + .build(); + + try (Response response = httpClient.newCall(httpRequest).execute()) { + String responseBody = response.body() != null ? response.body().string() : ""; + + if (response.isSuccessful()) { + // 退款申请成功(注意:微信退款是异步的,此处只表示申请提交成功) + log.info("微信退款申请成功, orderId={}, outRefundNo={}, response={}", orderId, outRefundNo, responseBody); + result.put("code", "200"); + result.put("message", "退款申请成功"); + result.put("outRefundNo", outRefundNo); + result.put("refundResponse", responseBody); + } else { + log.error("微信退款申请失败, orderId={}, httpCode={}, response={}", orderId, response.code(), responseBody); + result.put("code", "102"); + result.put("message", "微信退款申请失败: " + responseBody); + } + } + + } catch (Exception e) { + log.error("微信退款异常, orderId={}", orderId, e); + result.put("code", "500"); + result.put("message", "退款异常: " + e.getMessage()); + } + + return ResponseEntity.ok(result); + } +} \ No newline at end of file diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java index 1d572f86..9a5a8426 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallDeliveryOrderMapper.java @@ -21,10 +21,18 @@ public interface MallDeliveryOrderMapper extends BaseMapper { */ IPage selectPageVO(IPage page, @Param("q") MallDeliveryOrderPageQuery q); + /** + * 分页查询配送单 + */ + IPage selectPageVOByStatus(IPage page, @Param("q") MallDeliveryOrderPageQuery q); + /** * 统计配送员当前活跃单量(待取货+配送中) */ Map countActiveOrdersByWorker(@Param("workerId") String workerId); List> countOrdersByType(@Param("regionId") String regionId); + + List> countOrdersByStatus(@Param("workerId") String workerId); + } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallReturnOrderGoodsMapper.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallReturnOrderGoodsMapper.java new file mode 100644 index 00000000..e00e4ced --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/dao/mapper/MallReturnOrderGoodsMapper.java @@ -0,0 +1,13 @@ +package cc.hiver.mall.dao.mapper; + +import cc.hiver.mall.entity.MallReturnOrderGoods; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springframework.stereotype.Repository; + +/** + * 订单商品详情 Mapper 接口 + */ +@Repository +public interface MallReturnOrderGoodsMapper extends BaseMapper { + +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java index 19139681..9005bb1a 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallDeliveryOrder.java @@ -1,6 +1,7 @@ package cc.hiver.mall.entity; import cc.hiver.core.common.utils.SnowFlakeUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; @@ -10,9 +11,11 @@ import lombok.Data; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; +import java.util.List; @Data @Entity @@ -30,6 +33,8 @@ public class MallDeliveryOrder implements Serializable { private String groupId; @ApiModelProperty(value = "配送员ID") private String workerId; + @ApiModelProperty(value = "配送员名称") + private String workerName; @ApiModelProperty(value = "店铺ID") private String shopId; @ApiModelProperty(value = "取货区域ID(订单商家的所在区域id)") @@ -40,7 +45,7 @@ public class MallDeliveryOrder implements Serializable { private BigDecimal deliveryFee; @ApiModelProperty(value = "平台额外佣金(接收的指派单才有)") private BigDecimal deliveryFeeMarketplace; - @ApiModelProperty(value = "配送状态 0:待接单 1:待取货 2:配送中 3:已送达 4:已取消") + @ApiModelProperty(value = "配送状态 -1:待激活 0:待接单 1:待取货 2:配送中 3:已送达 4:已取消 5:待售后") private Integer status; @ApiModelProperty(value = "收货人姓名(快照)") private String receiverName; @@ -66,10 +71,29 @@ public class MallDeliveryOrder implements Serializable { private Date getTime; @ApiModelProperty(value = "客户要求送达时间") private Date mustFinishTime; + @ApiModelProperty(value = "到店时间") + private Date arriveTime; @ApiModelProperty(value = "送达时间") private Date finishTime; @ApiModelProperty(value = "订单类型 1:外卖 2:快递 3 跑腿") private Integer deliveryType; @ApiModelProperty(value = "商家-订单序号") private String numberCode; + @ApiModelProperty(value = "总件数") + private Integer allCount; + @ApiModelProperty(value = "是否超重/超大 0不是 1是") + private Integer isBig; + @ApiModelProperty(value = "是否退款中 0不是 1是") + private Integer isReturn; + @ApiModelProperty(value = "手机号尾号") + private String phoneNumber; + @ApiModelProperty(value = "取件码 , 分割") + private String getCodes; + @ApiModelProperty(value = "取件码图片 , 分割") + private String getPictures; + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "商品信息") + private List goodsList; + } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java index 3ccf90bb..92fed091 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrder.java @@ -33,8 +33,10 @@ public class MallOrder implements Serializable { private Integer orderType; @ApiModelProperty(value = "配送方式 1:外卖配送 2:到店自取") private Integer deliveryType; - @ApiModelProperty(value = "订单状态 0:待支付 1:待商家接单 10:待成团 2待配送员接单 3:待取货(配送)/待消费(自取) 4:配送中 5:已完成 6:已取消 7:待商家同意退款 8:已退款") + @ApiModelProperty(value = "订单状态 0:待支付 1:待商家接单 10:待成团 2待配送员接单 3:待取货(配送)/待消费(自取) 4:配送中 5:已完成 6:已取消 7:待商家同意退款 8:已退款 11: 售后中 售后处理后还变成4") private Integer status; + @ApiModelProperty(value = "结算状态 0 未结算 1已结算") + private Integer settlementStatus; @ApiModelProperty(value = "商家-订单序号") private String numberCode; @ApiModelProperty(value = "订单总金额(包含运费打包费等)") @@ -69,6 +71,8 @@ public class MallOrder implements Serializable { private Date createTime; @ApiModelProperty(value = "支付时间") private Date payTime; + @ApiModelProperty(value = "商家出餐时间") + private Date shopMakeTime; @ApiModelProperty(value = "学校id") private String regionId; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java index 05ad9282..3d970afe 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallOrderGoods.java @@ -36,6 +36,8 @@ public class MallOrderGoods implements Serializable { private String specs; @ApiModelProperty(value = "商品单价") private BigDecimal price; + @ApiModelProperty(value = "商品餐盒费") + private BigDecimal packageFee; @ApiModelProperty(value = "商品数量") private Integer quantity; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java index 5dd4dc0b..6afe6479 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/entity/MallRefundRecord.java @@ -10,9 +10,11 @@ import lombok.Data; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; +import java.util.List; @Data @Entity @@ -31,10 +33,19 @@ public class MallRefundRecord implements Serializable { private BigDecimal refundAmount; @ApiModelProperty(value = "退款原因") private String reason; - @ApiModelProperty(value = "退款状态 0:待商家同意 1:退款成功 2:商家拒绝退款") + @ApiModelProperty(value = "退款图片") + private String pictures; + @ApiModelProperty(value = "退款状态 0:退款处理中 1:退款成功 2:商家拒绝退款 退款失败 3售后中 4已售后 5 拒绝售后 6 商家拒绝售后 7 配送员拒绝售后") private Integer status; + @ApiModelProperty(value = "退款类型 1 退商品 2退配送费 3 全额退款") + private Integer refundType; + @ApiModelProperty(value = "全额退款类型 1 商家原因 2 配送原因 3 两者都有 4 平台退款") + private Integer refundTypeStatus; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "退款成功时间") private Date successTime; + @ApiModelProperty("售后商品列表") + @Transient + private List items; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java index 93a6c3b9..0c9098ed 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/dto/CreateOrderDTO.java @@ -72,6 +72,8 @@ public class CreateOrderDTO { private String specs; @ApiModelProperty(value = "下单价格(以此为准,由前端传入规格对应价格)", required = true) private BigDecimal price; + @ApiModelProperty(value = "餐盒费(以此为准,由前端传入规格对应价格)") + private BigDecimal packageFee; @ApiModelProperty(value = "购买数量", required = true) private Integer quantity; @ApiModelProperty(value = "是否是拼团商品", required = false) @@ -94,6 +96,8 @@ public class CreateOrderDTO { public static class WorkerParam { @ApiModelProperty(value = "配送员ID", required = true) private String workerId; + @ApiModelProperty(value = "配送员名称", required = true) + private String workerName; @ApiModelProperty(value = "配送员规则佣金 orderBkge", required = true) private BigDecimal orderBkge; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java index b863e909..b98004e2 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/query/MallOrderPageQuery.java @@ -18,6 +18,9 @@ public class MallOrderPageQuery extends HiverBasePageQuery { @ApiModelProperty("店铺ID") private String shopId; + @ApiModelProperty("店铺名称") + private String shopName; + @ApiModelProperty("学校ID") private String regionId; @@ -33,6 +36,9 @@ public class MallOrderPageQuery extends HiverBasePageQuery { @ApiModelProperty("订单类型 0:不区分 1:饭团 2:跑腿/快递 3:二手") private Integer searchType; + @ApiModelProperty("订单状态 0:待支付 1:待成团 2:待消费 3:待接单4:待取货 5:待送达 6:已完成 7:待商家同意退款 8:已退款 9:已取消") + private Integer searchStatus; + @ApiModelProperty("拼团ID") private String groupId; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java index 36d8016d..28f8ce99 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallDeliveryOrderService.java @@ -18,16 +18,26 @@ public interface MallDeliveryOrderService extends IService { */ IPage pageDelivery(MallDeliveryOrderPageQuery q); + /** + * 分页查询配送员的配送单 + */ + IPage pageDeliveryByStatus(MallDeliveryOrderPageQuery q); + /** * 配送员接单(抢单大厅或指派单) */ - Integer workerAccept(String deliveryId, String workerId,String groupId); + Integer workerAccept(String deliveryId, String workerId, String workerName,String groupId); /** * 配送员取货 */ void workerPickup(String deliveryId, String workerId); + /** + * 配送员到带你 + */ + void arriveShop(String deliveryId, String workerId); + /** * 配送员送达完成 */ @@ -45,4 +55,6 @@ public interface MallDeliveryOrderService extends IService { long countWaitGrabOrders(int deliveryType); List> countOrdersByType(String regionId); + + List> countOrdersByStatus(String workerId); } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java index 831fc19a..aaeb2f3e 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallOrderService.java @@ -58,6 +58,11 @@ public interface MallOrderService extends IService { */ void agreeRefund(String orderId); + /** + * 商家出餐 + */ + void shopMakeTime(String orderId); + /** * 商家拒绝退款 */ diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallRefundRecordService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallRefundRecordService.java index fe0ca477..92bc1ba6 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallRefundRecordService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/service/mybatis/MallRefundRecordService.java @@ -7,4 +7,6 @@ import com.baomidou.mybatisplus.extension.service.IService; * 退款记录 Service 接口 */ public interface MallRefundRecordService extends IService { + + void create(MallRefundRecord mallRefundRecord); } diff --git a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/UnifiedOrderService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/UnifiedOrderService.java similarity index 95% rename from hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/UnifiedOrderService.java rename to hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/UnifiedOrderService.java index 2a208549..981705e5 100644 --- a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/UnifiedOrderService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/UnifiedOrderService.java @@ -1,5 +1,7 @@ -package cc.hiver.social.controller; +package cc.hiver.mall.serviceimpl; +import cc.hiver.mall.utils.WechatPayConfig; +import cc.hiver.mall.utils.WechatPaySigner; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.*; import org.springframework.beans.factory.annotation.Autowired; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java index 516e0b06..10bd3b06 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallDeliveryOrderServiceImpl.java @@ -1,9 +1,11 @@ package cc.hiver.mall.serviceimpl.mybatis; import cc.hiver.mall.dao.mapper.MallDeliveryOrderMapper; +import cc.hiver.mall.dao.mapper.MallOrderGoodsMapper; import cc.hiver.mall.dao.mapper.MallOrderGroupMapper; 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 cc.hiver.mall.pojo.query.MallDeliveryOrderPageQuery; import cc.hiver.mall.service.mybatis.MallDeliveryOrderService; @@ -45,6 +47,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl records = page.getRecords(); + if (records != null && !records.isEmpty()) { + // 1. 收集所有订单ID + List orderIds = records.stream() + .map(MallDeliveryOrder::getOrderId) + .collect(java.util.stream.Collectors.toList()); + + // 2. 一次 IN 查询所有商品明细 + List allGoods = mallOrderGoodsMapper.selectByOrderIds(orderIds); + + // 3. 按 orderId 分组 + java.util.Map> goodsMap = allGoods.stream() + .collect(java.util.stream.Collectors.groupingBy(MallOrderGoods::getOrderId)); + + // 4. 组装到每个 VO + for (MallDeliveryOrder vo : records) { + vo.setGoodsList(goodsMap.getOrDefault(vo.getOrderId(), java.util.Collections.emptyList())); + } + } return page; } + @Override + public IPage pageDeliveryByStatus(MallDeliveryOrderPageQuery q) { + IPage page = new Page<>(q.getPageNum(), q.getPageSize()); + page = this.baseMapper.selectPageVOByStatus(page,q); + // 批量查询订单商品明细,避免 N+1 问题 + List records = page.getRecords(); + if (records != null && !records.isEmpty()) { + // 1. 收集所有订单ID + List orderIds = records.stream() + .map(MallDeliveryOrder::getOrderId) + .collect(java.util.stream.Collectors.toList()); + + // 2. 一次 IN 查询所有商品明细 + List allGoods = mallOrderGoodsMapper.selectByOrderIds(orderIds); + + // 3. 按 orderId 分组 + java.util.Map> goodsMap = allGoods.stream() + .collect(java.util.stream.Collectors.groupingBy(MallOrderGoods::getOrderId)); + + // 4. 组装到每个 + for (MallDeliveryOrder vo : records) { + vo.setGoodsList(goodsMap.getOrDefault(vo.getOrderId(), java.util.Collections.emptyList())); + } + } + return page; + } + /** * 配送员接单 * - 抢单大厅:workerId 为空的单,任何配送员均可接 @@ -81,7 +133,7 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl uw = new LambdaUpdateWrapper<>(); uw.eq(MallDeliveryOrder::getId, deliveryId) .set(MallDeliveryOrder::getStatus, STATUS_WAIT_PICKUP) - .set(MallDeliveryOrder::getWorkerId, workerId) + .set(MallDeliveryOrder::getWorkerId, workerId).set(MallDeliveryOrder::getWorkerName, workerName) .set(MallDeliveryOrder::getAcceptTime, new Date()); MallOrder order = mallOrderService.getById(delivery.getOrderId()); @@ -176,6 +228,14 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl uw = new LambdaUpdateWrapper<>(); + uw.eq(MallDeliveryOrder::getId, deliveryId) + .set(MallDeliveryOrder::getArriveTime, new Date()); + this.update(uw); + } + /** * 配送员送达(状态:配送中 -> 已送达) */ @@ -227,6 +287,12 @@ public class MallDeliveryOrderServiceImpl extends ServiceImpl> countOrdersByType(String regionId) { return this.baseMapper.countOrdersByType(regionId); } + + @Override + public List> countOrdersByStatus(String workerId) { + return this.baseMapper.countOrdersByStatus(workerId); + } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java index 7c9f179d..387a62ed 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderGroupServiceImpl.java @@ -2,14 +2,12 @@ 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.dao.mapper.*; import cc.hiver.mall.entity.*; import cc.hiver.mall.service.mybatis.MallOrderGroupService; import cc.hiver.mall.service.mybatis.MallOrderService; import cc.hiver.mall.utils.MerchantOrderSeqUtil; +import cc.hiver.mall.utils.WechatPayUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -20,9 +18,12 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.sql.Timestamp; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; /** * 拼团主表 Service 实现 @@ -59,9 +60,21 @@ public class MallOrderGroupServiceImpl extends ServiceImpl goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId()); + if (goodsList == null || goodsList.isEmpty()) return; + List returnList = goodsList.stream() + .map(goods -> { + // 在这里为每个 MallOrderGoods 对象创建并填充对应的 MallOrderReturnGoods 对象 + MallReturnOrderGoods returnGoods = new MallReturnOrderGoods(); + returnGoods.setOrderId(refund.getId()); + returnGoods.setProductId(goods.getProductId()); + returnGoods.setProductName(goods.getProductName()); + returnGoods.setProductPicture(goods.getProductPicture()); + returnGoods.setSpecs(goods.getSpecs()); + returnGoods.setPrice(goods.getPrice()); + returnGoods.setQuantity(goods.getQuantity()); + mallReturnOrderGoodsMapper.insert(returnGoods); + return returnGoods; + }) + .collect(Collectors.toList()); } log.info("拼团 {} 已过期,取消 {} 条子订单并生成退款记录", groupId, waitingOrders.size()); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index a2d11ce6..ed22636a 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -5,11 +5,10 @@ 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 cc.hiver.mall.utils.MerchantOrderSeqUtil; +import cc.hiver.mall.utils.WechatPayUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -26,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; +import java.sql.Timestamp; import java.util.*; import java.util.stream.Collectors; @@ -51,6 +51,7 @@ public class MallOrderServiceImpl extends ServiceImpl duw = new LambdaUpdateWrapper<>(); duw.eq(MallDeliveryOrder::getOrderId, orderId) - .set(MallDeliveryOrder::getStatus, 0); + .set(MallDeliveryOrder::getStatus, 0).set(MallDeliveryOrder::getCreateTime, createTime); mallDeliveryOrderMapper.update(null, duw); } else { String latestSeq = merchantOrderSeqUtil.generateOrderSequence(order.getShopId(), 2); @@ -793,12 +796,28 @@ public class MallOrderServiceImpl extends ServiceImpl uw = new LambdaUpdateWrapper<>(); + uw.eq(MallOrder::getId, orderId).set(MallOrder::getShopMakeTime,new Date()); + this.update(uw); + } + @Override @Transactional(rollbackFor = Exception.class) public void rejectRefund(String orderId, String reason) { - getAndCheckOrder(orderId, STATUS_WAIT_REFUND); - // 退款拒绝,恢复到已完成(或待商家处理状态) - updateOrderStatus(orderId, STATUS_DONE); + MallOrder order = getAndCheckOrder(orderId, STATUS_WAIT_REFUND); + //如果是自取订单,则恢复待消费状态 + if(order.getDeliveryType() == DELIVERY_TYPE_SELF){ + updateOrderStatusAndReturn(orderId, STATUS_WAIT_PICKUP); + }else{ + MallDeliveryOrder delivery = findDeliveryByOrderId(orderId); + if(delivery.getStatus() == STATUS_WAIT_SHOP){ + updateOrderStatusAndReturn(orderId, STATUS_WAIT_PICKUP); + }else if(delivery.getStatus() == DELIVERY_TYPE_SELF){ + updateOrderStatusAndReturn(orderId, STATUS_DELIVERING); + } + } // 更新退款记录为拒绝 LambdaUpdateWrapper uw = new LambdaUpdateWrapper<>(); uw.eq(MallRefundRecord::getOrderId, orderId) @@ -999,6 +1018,7 @@ public class MallOrderServiceImpl extends ServiceImpl uw = new LambdaUpdateWrapper<>(); + uw.eq(MallOrder::getId, orderId).set(MallOrder::getStatus, status); + this.update(uw); + } + /** * 获取订单并校验状态 */ @@ -1101,6 +1130,8 @@ public class MallOrderServiceImpl extends ServiceImpl goodsList = mallOrderGoodsMapper.selectByOrderId(order.getId()); + if (goodsList == null || goodsList.isEmpty()) return; + List returnList = goodsList.stream() + .map(goods -> { + // 在这里为每个 MallOrderGoods 对象创建并填充对应的 MallReturnOrderGoods 对象 + MallReturnOrderGoods returnGoods = new MallReturnOrderGoods(); + returnGoods.setOrderId(record.getId()); + returnGoods.setProductId(goods.getProductId()); + returnGoods.setProductName(goods.getProductName()); + returnGoods.setProductPicture(goods.getProductPicture()); + returnGoods.setSpecs(goods.getSpecs()); + returnGoods.setPrice(goods.getPrice()); + returnGoods.setQuantity(goods.getQuantity()); + mallReturnOrderGoodsMapper.insert(returnGoods); + return returnGoods; + }) + .collect(Collectors.toList()); } /** @@ -1209,6 +1260,13 @@ public class MallOrderServiceImpl extends ServiceImpl duw = new LambdaUpdateWrapper<>(); + duw.eq(MallDeliveryOrder::getOrderId, order.getId()) + .set(MallDeliveryOrder::getIsReturn, IS_RETURN_EXPRESS); + mallDeliveryOrderMapper.update(null, duw); + } createRefundRecord(order, order.getTotalAmount(), reason); } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java index 79aec7bc..1f4a269c 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallRefundRecordServiceImpl.java @@ -1,15 +1,50 @@ package cc.hiver.mall.serviceimpl.mybatis; import cc.hiver.mall.dao.mapper.MallRefundRecordMapper; +import cc.hiver.mall.dao.mapper.MallReturnOrderGoodsMapper; +import cc.hiver.mall.entity.MallOrder; import cc.hiver.mall.entity.MallRefundRecord; +import cc.hiver.mall.service.mybatis.MallOrderService; import cc.hiver.mall.service.mybatis.MallRefundRecordService; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Date; + /** * 退款记录 Service 实现 */ @Service public class MallRefundRecordServiceImpl extends ServiceImpl implements MallRefundRecordService { + + @Autowired + private MallRefundRecordMapper mallRefundRecordMapper; + + @Autowired + private MallReturnOrderGoodsMapper mallReturnOrderGoodsMapper; + + @Autowired + private MallOrderService mallOrderService; + + @Override + public void create(MallRefundRecord mallRefundRecord) { + //更新订单为 待售后 + LambdaUpdateWrapper oUw = new LambdaUpdateWrapper<>(); + oUw.eq(MallOrder::getId, mallRefundRecord.getOrderId()) + .set(MallOrder::getStatus, 11); + mallOrderService.update(oUw); + mallRefundRecord.setStatus(3); // 申请售后 + mallRefundRecord.setCreateTime(new Date()); + mallRefundRecordMapper.insert(mallRefundRecord); + + if(!mallRefundRecord.getItems().isEmpty()){ + mallRefundRecord.getItems().forEach(item -> { + mallReturnOrderGoodsMapper.insert(item); + }); + } + + } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java new file mode 100644 index 00000000..eaf17107 --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/KeyUtils.java @@ -0,0 +1,25 @@ +package cc.hiver.mall.utils; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; + +import java.security.PrivateKey; +import java.security.Signature; +import java.util.Base64; + +public class KeyUtils { + + public static PrivateKey loadPrivateKey(byte[] keyBytes) throws Exception { + PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(keyBytes); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + return converter.getPrivateKey(pkInfo); + } + + public static String signWithSha256Rsa(String message, PrivateKey privateKey) throws Exception { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(privateKey); + sign.update(message.getBytes("utf-8")); + byte[] signed = sign.sign(); + return Base64.getEncoder().encodeToString(signed); + } +} diff --git a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPayConfig.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayConfig.java similarity index 98% rename from hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPayConfig.java rename to hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayConfig.java index 9288ff6b..b888ed80 100644 --- a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPayConfig.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayConfig.java @@ -1,4 +1,4 @@ -package cc.hiver.social.controller; +package cc.hiver.mall.utils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; diff --git a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPaySigner.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPaySigner.java similarity index 96% rename from hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPaySigner.java rename to hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPaySigner.java index 375e66a5..bc8c71c6 100644 --- a/hiver-modules/hiver-social/src/main/java/cc/hiver/social/controller/WechatPaySigner.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPaySigner.java @@ -1,6 +1,5 @@ -package cc.hiver.social.controller; +package cc.hiver.mall.utils; -import com.alipay.api.internal.util.file.IOUtils; import okhttp3.HttpUrl; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +14,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.Security; -import java.security.cert.X509Certificate; import java.util.Base64; import java.util.UUID; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java new file mode 100644 index 00000000..10fce1ed --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/WechatPayUtil.java @@ -0,0 +1,92 @@ +package cc.hiver.mall.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 微信支付工具类 + * + * @author Yazhi Li + */ +@Component +public class WechatPayUtil { + @Autowired + private WechatPaySigner signer; + + private static final Logger log = LoggerFactory.getLogger(WechatPayUtil.class); + + private final OkHttpClient httpClient = new OkHttpClient(); + private final ObjectMapper objectMapper = new ObjectMapper(); + public Boolean refund(String orderId,long totalFee) { + try { + // 2. 构建退款请求参数 + // 原支付交易号(与下单时一致,带 ORDER_ 前缀) + String outTradeNo = "ORDER_" + orderId; + // 退款单号(唯一,使用 REFUND_ + orderId + 时间戳避免重复) + String outRefundNo = "REFUND_" + orderId + "_" + System.currentTimeMillis(); + + // 退款原因 + String reason = "订单退款"; + // 3. 构建请求 Body + Map reqBody = new HashMap<>(); + reqBody.put("out_trade_no", outTradeNo); + reqBody.put("out_refund_no", outRefundNo); + reqBody.put("reason", reason); + + Map amountMap = new HashMap<>(); + amountMap.put("refund", totalFee); + amountMap.put("total", totalFee); + amountMap.put("currency", "CNY"); + reqBody.put("amount", amountMap); + + String jsonBody = objectMapper.writeValueAsString(reqBody); + + // 4. 签名并调用微信退款 API + String refundUrl = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds"; + String authorization = signer.sign("POST", refundUrl, jsonBody); + + // 【修改点】使用标准的 create 方法,参数顺序是 (MediaType, String) + okhttp3.RequestBody body = okhttp3.RequestBody.create( + MediaType.parse("application/json; charset=utf-8"), + jsonBody + ); + + Request httpRequest = new Request.Builder() + .url(refundUrl) + .post(body) + .addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorization) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .addHeader("User-Agent", "MyApp/1.0") + .build(); + + try (Response response = httpClient.newCall(httpRequest).execute()) { + String responseBody = response.body() != null ? response.body().string() : ""; + + if (response.isSuccessful()) { + // 退款申请成功(注意:微信退款是异步的,此处只表示申请提交成功) + log.info("微信退款申请成功, orderId={}, outRefundNo={}, response={}", orderId, outRefundNo, responseBody); + return true; + } else { + log.error("微信退款申请失败, orderId={}, httpCode={}, response={}", orderId, response.code(), responseBody); + return false; + } + } + + } catch (Exception e) { + log.error("微信退款异常, orderId={}", orderId, e); + return false; + } + } + +} diff --git a/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml b/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml index bdb047a7..43622523 100644 --- a/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml +++ b/hiver-modules/hiver-mall/src/main/resources/mapper/MallDeliveryOrderMapper.xml @@ -29,6 +29,14 @@ + + + + + + + + @@ -39,7 +47,8 @@ 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, d.number_code, - d.region_id,d.remark + d.region_id,d.remark, d.all_count, d.phone_number, d.get_codes, d.get_pictures, d.is_big, + d.is_return,d.worker_name, d.arrive_time FROM mall_delivery_order d @@ -97,6 +106,52 @@ d.must_finish_time ASC,d.delivery_fee DESC + +