feat(hjc): 阶段2 订单支付闭环 - 下单后发起微信Native支付并回推一站式

- HjcOrderController: POST /api/hjc/order/pay(复用 payment.createPayment 取codeUrl)、PUT /api/hjc/order/mark-paid(幂等置已付+触发一站式推送)
- HjcBizService.markPaid: 置 payStatus=1/orderStatus=1/payTime,支付成功即 pushOrderToOneStop
- 出向推送在 base-url 未配置时不发起HTTP,仅记日志待重试
This commit is contained in:
2026-09-08 20:54:01 +08:00
parent b2f310b81d
commit 4cbd925544
3 changed files with 110 additions and 0 deletions
@@ -11,8 +11,13 @@ import com.gxwebsoft.hjc.entity.HjcEnterprise;
import com.gxwebsoft.hjc.entity.HjcOrder; import com.gxwebsoft.hjc.entity.HjcOrder;
import com.gxwebsoft.hjc.param.HjcOrderParam; import com.gxwebsoft.hjc.param.HjcOrderParam;
import com.gxwebsoft.hjc.service.HjcBidProjectService; import com.gxwebsoft.hjc.service.HjcBidProjectService;
import com.gxwebsoft.hjc.service.HjcBizService;
import com.gxwebsoft.hjc.service.HjcEnterpriseService; import com.gxwebsoft.hjc.service.HjcEnterpriseService;
import com.gxwebsoft.hjc.service.HjcOrderService; import com.gxwebsoft.hjc.service.HjcOrderService;
import com.gxwebsoft.payment.dto.PaymentRequest;
import com.gxwebsoft.payment.dto.PaymentResponse;
import com.gxwebsoft.payment.enums.PaymentType;
import com.gxwebsoft.payment.service.PaymentService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -22,6 +27,7 @@ import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 汇吉采标书订单(下单 + 我的订单 + 后台订单) * 汇吉采标书订单(下单 + 我的订单 + 后台订单)
@@ -39,6 +45,10 @@ public class HjcOrderController extends BaseController {
private HjcBidProjectService hjcBidProjectService; private HjcBidProjectService hjcBidProjectService;
@Resource @Resource
private HjcEnterpriseService hjcEnterpriseService; private HjcEnterpriseService hjcEnterpriseService;
@Resource
private PaymentService paymentService;
@Resource
private HjcBizService hjcBizService;
@Operation(summary = "下单(创建待支付订单)") @Operation(summary = "下单(创建待支付订单)")
@PostMapping("/create") @PostMapping("/create")
@@ -99,6 +109,65 @@ public class HjcOrderController extends BaseController {
return success("下单成功", order); return success("下单成功", order);
} }
@Operation(summary = "发起支付(微信Native扫码,返回codeUrl)")
@PostMapping("/pay")
public ApiResult<?> pay(@RequestBody Map<String, Object> body) {
Integer userId = getLoginUserId();
if (userId == null) {
return fail("用户未登录");
}
String orderNo = body.get("orderNo") == null ? null : String.valueOf(body.get("orderNo"));
if (orderNo == null) {
return fail("订单号不能为空");
}
HjcOrder order = hjcOrderService.getByOrderNo(orderNo);
if (order == null) {
return fail("订单不存在");
}
if (order.getPayStatus() != null && order.getPayStatus() == 1) {
return fail("订单已支付");
}
HjcEnterprise enterprise = hjcEnterpriseService.getByUserId(userId);
if (enterprise == null || !enterprise.getId().equals(order.getEnterpriseId())) {
return fail("无权操作该订单");
}
PaymentRequest request = new PaymentRequest();
request.setTenantId(order.getTenantId());
request.setUserId(userId);
request.setPaymentType(PaymentType.WECHAT_NATIVE);
request.setAmount(order.getTotalAmount());
request.setSubject(truncate(order.getProjectName(), 127));
request.setOrderNo(order.getOrderNo());
try {
PaymentResponse resp = paymentService.createPayment(request);
return success("发起支付成功", resp);
} catch (com.gxwebsoft.payment.exception.PaymentException e) {
return fail("发起支付失败:" + e.getMessage());
}
}
@Operation(summary = "标记订单已支付(支付成功后调用,幂等),并触发一站式推送")
@PutMapping("/mark-paid")
public ApiResult<?> markPaid(@RequestBody Map<String, Object> body) {
Integer userId = getLoginUserId();
if (userId == null) {
return fail("用户未登录");
}
String orderNo = body.get("orderNo") == null ? null : String.valueOf(body.get("orderNo"));
if (orderNo == null) {
return fail("订单号不能为空");
}
HjcOrder order = hjcOrderService.getByOrderNo(orderNo);
if (order == null) {
return fail("订单不存在");
}
HjcEnterprise enterprise = hjcEnterpriseService.getByUserId(userId);
if (enterprise == null || !enterprise.getId().equals(order.getEnterpriseId())) {
return fail("无权操作该订单");
}
return success("已支付", hjcBizService.markPaid(orderNo));
}
@Operation(summary = "我的订单") @Operation(summary = "我的订单")
@GetMapping("/my") @GetMapping("/my")
public ApiResult<?> my() { public ApiResult<?> my() {
@@ -136,4 +205,11 @@ public class HjcOrderController extends BaseController {
private String firstNotBlank(String a, String b) { private String firstNotBlank(String a, String b) {
return a != null && !a.trim().isEmpty() ? a : b; return a != null && !a.trim().isEmpty() ? a : b;
} }
private String truncate(String s, int max) {
if (s == null) {
return "";
}
return s.length() > max ? s.substring(0, max) : s;
}
} }
@@ -16,6 +16,11 @@ public interface HjcBizService {
*/ */
void pushOrderToOneStop(HjcOrder order); void pushOrderToOneStop(HjcOrder order);
/**
* 支付成功后标记订单已支付(幂等),并触发一站式推送
*/
HjcOrder markPaid(String orderNo);
/** /**
* 重试失败的推送(幂等) * 重试失败的推送(幂等)
*/ */
@@ -109,6 +109,7 @@ public class HjcBizServiceImpl implements HjcBizService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void retryPendingPush() { public void retryPendingPush() {
List<HjcOrderPushLog> pending = hjcOrderPushLogService.list(new LambdaQueryWrapper<HjcOrderPushLog>() List<HjcOrderPushLog> pending = hjcOrderPushLogService.list(new LambdaQueryWrapper<HjcOrderPushLog>()
.in(HjcOrderPushLog::getPushStatus, 0, 2) .in(HjcOrderPushLog::getPushStatus, 0, 2)
@@ -123,10 +124,38 @@ public class HjcBizServiceImpl implements HjcBizService {
} }
} }
@Override
@Transactional(rollbackFor = Exception.class)
public HjcOrder markPaid(String orderNo) {
HjcOrder order = hjcOrderService.getByOrderNo(orderNo);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getPayStatus() != null && order.getPayStatus() == 1) {
return order;
}
order.setPayStatus(1);
order.setOrderStatus(1);
order.setPayTime(LocalDateTime.now());
hjcOrderService.updateById(order);
// 支付成功后触发一站式推送
pushOrderToOneStop(order);
return order;
}
private void doPush(HjcOrder order, HjcOrderPushLog logEntity) { private void doPush(HjcOrder order, HjcOrderPushLog logEntity) {
try { try {
CreatePurchaseDetails body = buildCreatePurchaseDetails(order); CreatePurchaseDetails body = buildCreatePurchaseDetails(order);
String payload = objectMapper.writeValueAsString(body); String payload = objectMapper.writeValueAsString(body);
logEntity.setPayload(payload);
if (StrUtil.isBlank(oneStopBaseUrl)) {
logEntity.setAttemptCount(logEntity.getAttemptCount() + 1);
logEntity.setPushStatus(2);
logEntity.setErrorMsg("一站式base-url未配置");
logEntity.setNextRetryTime(LocalDateTime.now().plusMinutes(30));
hjcOrderPushLogService.updateById(logEntity);
return;
}
String ts = HjcOneStopAuthUtil.timestamp(); String ts = HjcOneStopAuthUtil.timestamp();
String sign = HjcOneStopAuthUtil.sign(ts); String sign = HjcOneStopAuthUtil.sign(ts);