记得上下班打卡 | git大法好,push需谨慎

Commit 37ad6b75 authored by 姜秀龙's avatar 姜秀龙

Merge branch 'refs/heads/dev-1.6-shouqianba' into container-test

parents 929a7e41 7be3d282
...@@ -28,8 +28,8 @@ public class GoblinSqbPerfLinkedGoodsVo implements Serializable { ...@@ -28,8 +28,8 @@ public class GoblinSqbPerfLinkedGoodsVo implements Serializable {
@ApiModelProperty(value = "商品头图(本地)") @ApiModelProperty(value = "商品头图(本地)")
private String coverPic; private String coverPic;
@ApiModelProperty(value = "商品售价(分,本地)") @ApiModelProperty(value = "商品售价(元,与 goblin_goods_sku.price 一致)")
private Long price; private BigDecimal price;
@ApiModelProperty(value = "商品库存(本地)") @ApiModelProperty(value = "商品库存(本地)")
private Integer stock; private Integer stock;
......
...@@ -39,8 +39,9 @@ public interface IGoblinSqbService { ...@@ -39,8 +39,9 @@ public interface IGoblinSqbService {
/** /**
* 演出结束自动退款(定时任务调用) * 演出结束自动退款(定时任务调用)
* TODO * SQL 已联表过滤:收钱吧扩展单、主单已支付(2)、演出已结束、扩展未核销;循环内仅调 refund。
* @param performancesId 演出ID *
* @param performancesId 演出ID,传 null 或空则不按演出缩小(全库符合条件候选,慎用)
* @return 处理结果摘要(成功/失败笔数) * @return 处理结果摘要(成功/失败笔数)
*/ */
ResponseDto<String> autoRefundByPerformance(String performancesId); ResponseDto<String> autoRefundByPerformance(String performancesId);
......
...@@ -141,7 +141,8 @@ ...@@ -141,7 +141,8 @@
} }
var html = ''; var html = '';
linkedGoods.forEach(function (item, idx) { linkedGoods.forEach(function (item, idx) {
var priceYuan = item.price ? (item.price / 100).toFixed(2) : '-'; // 与接口一致:price 已为「元」(BigDecimal 序列化),不再按「分」/100
var priceYuan = (item.price != null && item.price !== '') ? Number(item.price).toFixed(2) : '-';
var settlementVal = item.settlementPrice || ''; var settlementVal = item.settlementPrice || '';
var imgHtml = item.coverPic var imgHtml = item.coverPic
? '<img src="' + item.coverPic + '" class="img-thumb"/>' ? '<img src="' + item.coverPic + '" class="img-thumb"/>'
......
...@@ -214,7 +214,7 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi ...@@ -214,7 +214,7 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
vo.setSkuName(sku.getName()); vo.setSkuName(sku.getName());
vo.setSpuName(sku.getName()); vo.setSpuName(sku.getName());
vo.setCoverPic(sku.getSkuPic()); vo.setCoverPic(sku.getSkuPic());
vo.setPrice(sku.getPrice() != null ? sku.getPrice().longValue() : null); vo.setPrice(sku.getPrice());
vo.setStock(sku.getSkuStock()); vo.setStock(sku.getSkuStock());
} }
goodsList.add(vo); goodsList.add(vo);
......
...@@ -234,6 +234,9 @@ public class SqbBiz { ...@@ -234,6 +234,9 @@ public class SqbBiz {
* 券码状态同步 * 券码状态同步
*/ */
public boolean syncCouponStatus(CouponStatusSyncRequest request) { public boolean syncCouponStatus(CouponStatusSyncRequest request) {
if (request != null && request.getSeller() == null) {
request.setSeller(cachedSeller);
}
CouponStatusSyncResponse response = executeRequestAndGetResponse("/optimus/core/voucher/modifyOrderVoucherList", "同步优惠券状态", request, CouponStatusSyncResponse.class); CouponStatusSyncResponse response = executeRequestAndGetResponse("/optimus/core/voucher/modifyOrderVoucherList", "同步优惠券状态", request, CouponStatusSyncResponse.class);
return response != null && Boolean.TRUE.equals(response.getSuccess()); return response != null && Boolean.TRUE.equals(response.getSuccess());
} }
...@@ -251,6 +254,7 @@ public class SqbBiz { ...@@ -251,6 +254,7 @@ public class SqbBiz {
public boolean syncCouponStatus(String redeemMerchantId, List<String> voucherNos, String redeemExternalOrderSn, String clientSn, Byte status) { public boolean syncCouponStatus(String redeemMerchantId, List<String> voucherNos, String redeemExternalOrderSn, String clientSn, Byte status) {
CouponStatusSyncRequest couponStatusSyncRequest = new CouponStatusSyncRequest(); CouponStatusSyncRequest couponStatusSyncRequest = new CouponStatusSyncRequest();
couponStatusSyncRequest.setAppid(sqbConfig.getAppId()); couponStatusSyncRequest.setAppid(sqbConfig.getAppId());
couponStatusSyncRequest.setSeller(cachedSeller);
couponStatusSyncRequest.setVoucherNos(voucherNos); couponStatusSyncRequest.setVoucherNos(voucherNos);
couponStatusSyncRequest.setRedeemSource("EXTERN"); couponStatusSyncRequest.setRedeemSource("EXTERN");
couponStatusSyncRequest.setRedeemExternalOrderSn(redeemExternalOrderSn); couponStatusSyncRequest.setRedeemExternalOrderSn(redeemExternalOrderSn);
......
...@@ -14,6 +14,9 @@ public class CouponStatusSyncRequest { ...@@ -14,6 +14,9 @@ public class CouponStatusSyncRequest {
/** 应用ID(聚合收单唯一ID,需申请) */ /** 应用ID(聚合收单唯一ID,需申请) */
private String appid; private String appid;
/** 卖家/商户信息(与 queryCoupon、refund 等接口一致,收钱吧侧必填) */
private CommonRequest.Seller seller;
/** 券号 */ /** 券号 */
private List<String> voucherNos; private List<String> voucherNos;
......
...@@ -58,8 +58,8 @@ info: ...@@ -58,8 +58,8 @@ info:
artifactId: '@project.artifactId@' artifactId: '@project.artifactId@'
version: '@project.version@' version: '@project.version@'
# ----------------------------------------------------------- # -----------------------------------------------------------
#mybatis-plus: mybatis-plus:
# mapper-locations: classpath*:com.liquidnet.service.*.mapper/*Mapper.xml mapper-locations: classpath*:com.liquidnet.service.*.mapper/*Mapper.xml
# ----------------------------------------------------------- # -----------------------------------------------------------
spring: spring:
application: application:
......
...@@ -2,6 +2,9 @@ package com.liquidnet.service.goblin.mapper; ...@@ -2,6 +2,9 @@ package com.liquidnet.service.goblin.mapper;
import com.liquidnet.service.goblin.entity.GoblinSqbOrder; import com.liquidnet.service.goblin.entity.GoblinSqbOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* <p> * <p>
...@@ -13,4 +16,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; ...@@ -13,4 +16,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/ */
public interface GoblinSqbOrderMapper extends BaseMapper<GoblinSqbOrder> { public interface GoblinSqbOrderMapper extends BaseMapper<GoblinSqbOrder> {
/**
* 联表过滤:主单已支付(2)、演出已结束、扩展未核销;可选按演出 id 缩小范围
*/
List<GoblinSqbOrder> selectAutoRefundCandidates(@Param("performancesId") String performancesId);
} }
<?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="com.liquidnet.service.goblin.mapper.GoblinSqbOrderMapper">
<!-- 收钱吧扩展单 + 主单已支付 + 演出已结束 + 未核销(定时自动退候选) -->
<select id="selectAutoRefundCandidates" resultType="com.liquidnet.service.goblin.entity.GoblinSqbOrder">
SELECT so.*
FROM goblin_sqb_order so
INNER JOIN goblin_store_order o ON o.order_id = so.order_id
INNER JOIN kylin_performances kp ON kp.performances_id = so.performances_id
WHERE (so.coupon_used_status = 0 OR so.coupon_used_status IS NULL)
AND o.status = 2
AND kp.time_end &lt; NOW()
AND so.performances_id IS NOT NULL
AND so.performances_id != ''
<if test="performancesId != null and performancesId != ''">
AND so.performances_id = #{performancesId}
</if>
</select>
</mapper>
...@@ -31,6 +31,6 @@ public interface FeignGoblinTaskClient { ...@@ -31,6 +31,6 @@ public interface FeignGoblinTaskClient {
ResponseDto<Boolean> refundRes(); ResponseDto<Boolean> refundRes();
@PostMapping("/goblin/job/sqb/autoRefund") @PostMapping("/goblin/job/sqb/autoRefund")
ResponseDto<String> sqbAutoRefund(@RequestParam("performancesId") String performancesId); ResponseDto<String> sqbAutoRefund(@RequestParam(value = "performancesId", required = false) String performancesId);
} }
...@@ -71,11 +71,11 @@ public class GoblinTaskHandler { ...@@ -71,11 +71,11 @@ public class GoblinTaskHandler {
/** /**
* 演出结束自动退款 * 演出结束自动退款
* xxl-job 配置:JobHandler = sev-goblin:sqbAutoRefund,任务参数传入 performancesId * xxl-job 配置:JobHandler = sev-goblin:sqbAutoRefund;参数为演出ID、或 performancesId=xxx;空则走 goblin 全库候选(慎用)
*/ */
@XxlJob(value = "sev-goblin:sqbAutoRefund") @XxlJob(value = "sev-goblin:sqbAutoRefund")
public void sqbAutoRefund() { public void sqbAutoRefund() {
String performancesId = XxlJobHelper.getJobParam(); String performancesId = parseSqbAutoRefundPerformancesId(XxlJobHelper.getJobParam());
try { try {
XxlJobHelper.handleSuccess("结果:" + feignGoblinTaskClient.sqbAutoRefund(performancesId).getData()); XxlJobHelper.handleSuccess("结果:" + feignGoblinTaskClient.sqbAutoRefund(performancesId).getData());
} catch (Exception e) { } catch (Exception e) {
...@@ -83,4 +83,27 @@ public class GoblinTaskHandler { ...@@ -83,4 +83,27 @@ public class GoblinTaskHandler {
XxlJobHelper.handleFail(); XxlJobHelper.handleFail();
} }
} }
/** 支持裸演出ID、或 performancesId=xxx(可与其他 & 参数混排) */
private static String parseSqbAutoRefundPerformancesId(String raw) {
if (raw == null) {
return null;
}
raw = raw.trim();
if (raw.isEmpty()) {
return null;
}
for (String part : raw.split("&")) {
part = part.trim();
int eq = part.indexOf('=');
if (eq > 0 && "performancesid".equalsIgnoreCase(part.substring(0, eq).trim())) {
String v = part.substring(eq + 1).trim();
return v.isEmpty() ? null : v;
}
}
if (!raw.contains("=")) {
return raw;
}
return null;
}
} }
...@@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -19,7 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
* - JobHandler:sev-goblin:sqbAutoRefund * - JobHandler:sev-goblin:sqbAutoRefund
* - CRON:按演出结束时间触发(如演出结束后 5 分钟:0 5 * * * ?,具体由运维根据演出时间动态配置) * - CRON:按演出结束时间触发(如演出结束后 5 分钟:0 5 * * * ?,具体由运维根据演出时间动态配置)
* - 执行器:liquidnet-service-executor * - 执行器:liquidnet-service-executor
* - 任务参数:performancesId=xxx(演出ID * - 任务参数:演出ID 或 performancesId=xxx;留空则处理全库「演出已结束 + 主单已支付 + 扩展未核销」候选(慎用
* - 路由策略:第一个 * - 路由策略:第一个
*/ */
@Slf4j @Slf4j
...@@ -33,7 +33,7 @@ public class GoblinSqbJobController { ...@@ -33,7 +33,7 @@ public class GoblinSqbJobController {
@PostMapping("/autoRefund") @PostMapping("/autoRefund")
@ApiOperation("演出结束自动退款") @ApiOperation("演出结束自动退款")
public ResponseDto<String> autoRefund(@RequestParam("performancesId") String performancesId) { public ResponseDto<String> autoRefund(@RequestParam(value = "performancesId", required = false) String performancesId) {
log.info("[收钱吧自动退款] 收到任务,performancesId={}", performancesId); log.info("[收钱吧自动退款] 收到任务,performancesId={}", performancesId);
return goblinSqbService.autoRefundByPerformance(performancesId); return goblinSqbService.autoRefundByPerformance(performancesId);
} }
......
...@@ -261,34 +261,52 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService { ...@@ -261,34 +261,52 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
@Override @Override
public ResponseDto<String> autoRefundByPerformance(String performancesId) { public ResponseDto<String> autoRefundByPerformance(String performancesId) {
log.info("[收钱吧自动退款] 开始处理演出 performancesId={}", performancesId); String pid = performancesId == null ? null : performancesId.trim();
if (pid != null && pid.isEmpty()) {
pid = null;
}
if (pid == null) {
log.warn("[收钱吧自动退款] performancesId 为空,将处理库内所有「演出已结束 + 主单已支付 + 扩展未核销」候选单");
} else {
log.info("[收钱吧自动退款] 开始 performancesId={}", pid);
}
// 查询该演出下 status=1(已支付)的订单(MySQL) List<GoblinSqbOrder> orders = goblinSqbOrderMapper.selectAutoRefundCandidates(pid);
// TODO: 通过 GoblinSqbOrderMapper 查询(需注入后解注释) if (CollectionUtils.isEmpty(orders)) {
// LambdaQueryWrapper<GoblinSqbOrder> query = new LambdaQueryWrapper<>(); String scope = pid != null ? ("演出 " + pid) : "全库(演出已结束)";
// query.eq(GoblinSqbOrder::getPerformancesId, performancesId).eq(GoblinSqbOrder::getCouponUsedStatus, 0); String summary = scope + " 无待自动退款的收钱吧扩展单";
// List<GoblinSqbOrder> orders = goblinSqbOrderMapper.selectList(query); log.info("[收钱吧自动退款] {}", summary);
return ResponseDto.success(summary);
}
int successCount = 0; int successCount = 0;
int failCount = 0; int failCount = 0;
// TODO: 取消注释并接入 GoblinSqbOrderMapper 后启用 for (GoblinSqbOrder order : orders) {
// for (GoblinSqbOrder order : orders) { String orderId = order.getOrderId();
// try { String userId = order.getUserId();
// ResponseDto<Boolean> result = refund(order.getUserId(), order.getOrderId()); if (orderId == null || orderId.trim().isEmpty() || userId == null || userId.trim().isEmpty()) {
// if (result != null && result.isSuccess()) { log.warn("[收钱吧自动退款] 扩展单缺少 orderId/userId,跳过 mid={}", order.getMid());
// successCount++; failCount++;
// } else { continue;
// log.warn("[收钱吧自动退款] 订单退款失败 orderId={}", order.getOrderId()); }
// failCount++; try {
// } ResponseDto<Boolean> result = refund(userId, orderId, "演出结束自动退款");
// } catch (Exception e) { if (result != null && result.isSuccess() && Boolean.TRUE.equals(result.getData())) {
// log.error("[收钱吧自动退款] 订单退款异常 orderId={}", order.getOrderId(), e); successCount++;
// failCount++; } else {
// } log.warn("[收钱吧自动退款] 退款失败 orderId={}, msg={}", orderId,
// } result != null ? result.getMessage() : "null");
failCount++;
String summary = String.format("演出 %s 自动退款完成:成功 %d 笔,失败 %d 笔", performancesId, successCount, failCount); }
} catch (Exception e) {
log.error("[收钱吧自动退款] 订单退款异常 orderId={}", orderId, e);
failCount++;
}
}
String scopeLabel = pid != null ? ("演出 " + pid) : "全库(演出已结束)";
String summary = String.format("%s 自动退款完成:成功 %d 笔,失败 %d 笔", scopeLabel, successCount, failCount);
log.info("[收钱吧自动退款] {}", summary); log.info("[收钱吧自动退款] {}", summary);
return ResponseDto.success(summary); return ResponseDto.success(summary);
} }
......
...@@ -541,14 +541,35 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService { ...@@ -541,14 +541,35 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
public ResponseDto<GoblinSqbOrderCreateVo> repay(String userId, String orderId) { public ResponseDto<GoblinSqbOrderCreateVo> repay(String userId, String orderId) {
log.info("[收钱吧再次付款] userId={}, orderId={}", userId, orderId); log.info("[收钱吧再次付款] userId={}, orderId={}", userId, orderId);
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) return ResponseDto.failure("订单不存在");
if (!userId.equals(orderVo.getUserId())) return ResponseDto.failure("无权限访问该订单");
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId); GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo == null if (storeOrderVo == null) {
|| !Integer.valueOf(GoblinStatusConst.Status.ORDER_STATUS_0.getValue()).equals(storeOrderVo.getStatus())) { return ResponseDto.failure("订单不存在");
}
if (!userId.equals(storeOrderVo.getUserId())) {
return ResponseDto.failure("无权限访问该订单");
}
if (!Integer.valueOf(GoblinStatusConst.Status.ORDER_STATUS_0.getValue()).equals(storeOrderVo.getStatus())) {
return ResponseDto.failure("订单状态不支持再次付款(仅待支付状态可重新拉起)"); return ResponseDto.failure("订单状态不支持再次付款(仅待支付状态可重新拉起)");
} }
List<String> orderSkuVoIds = storeOrderVo.getOrderSkuVoIds();
if (CollectionUtils.isEmpty(orderSkuVoIds)) {
return ResponseDto.failure("订单数据异常");
}
GoblinOrderSkuVo firstSkuVo = goblinRedisUtils.getGoblinOrderSkuVo(orderSkuVoIds.get(0));
if (firstSkuVo == null) {
return ResponseDto.failure("订单数据异常");
}
if (!Integer.valueOf(33).equals(firstSkuVo.getSkuType())) {
return ResponseDto.failure("该订单非收钱吧订单");
}
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) {
return ResponseDto.failure("收钱吧支付信息已失效,请取消订单后重新下单");
}
if (!userId.equals(orderVo.getUserId())) {
return ResponseDto.failure("无权限访问该订单");
}
if (orderVo.getSqbAcquiringSn() == null) { if (orderVo.getSqbAcquiringSn() == null) {
return ResponseDto.failure("订单数据异常,缺少收单号"); return ResponseDto.failure("订单数据异常,缺少收单号");
} }
......
...@@ -56,6 +56,22 @@ public class GoblinMongoUtils { ...@@ -56,6 +56,22 @@ public class GoblinMongoUtils {
object); object);
} }
public GoblinStoreOrderVo getGoblinStoreOrderVoByOrderId(String orderId) {
if (orderId == null || orderId.trim().isEmpty()) {
return null;
}
return mongoTemplate.findOne(Query.query(Criteria.where("orderId").is(orderId)),
GoblinStoreOrderVo.class, GoblinStoreOrderVo.class.getSimpleName());
}
public GoblinOrderSkuVo getGoblinOrderSkuVoByOrderSkuId(String orderSkuId) {
if (orderSkuId == null || orderSkuId.trim().isEmpty()) {
return null;
}
return mongoTemplate.findOne(Query.query(Criteria.where("orderSkuId").is(orderSkuId)),
GoblinOrderSkuVo.class, GoblinOrderSkuVo.class.getSimpleName());
}
//添加 订单SkuVo全量 //添加 订单SkuVo全量
public GoblinOrderSkuVo insertGoblinOrderSkuVo(GoblinOrderSkuVo vo) { public GoblinOrderSkuVo insertGoblinOrderSkuVo(GoblinOrderSkuVo vo) {
return mongoTemplate.insert(vo, GoblinOrderSkuVo.class.getSimpleName()); return mongoTemplate.insert(vo, GoblinOrderSkuVo.class.getSimpleName());
......
...@@ -396,7 +396,11 @@ public class GoblinRedisUtils { ...@@ -396,7 +396,11 @@ public class GoblinRedisUtils {
String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER.concat(orderId); String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER.concat(orderId);
Object obj = redisUtil.get(redisKey); Object obj = redisUtil.get(redisKey);
if (obj == null) { if (obj == null) {
return null; GoblinStoreOrderVo fromMongo = goblinMongoUtils.getGoblinStoreOrderVoByOrderId(orderId);
if (fromMongo != null) {
redisUtil.set(redisKey, fromMongo);
}
return fromMongo;
} else { } else {
return (GoblinStoreOrderVo) obj; return (GoblinStoreOrderVo) obj;
} }
...@@ -407,7 +411,11 @@ public class GoblinRedisUtils { ...@@ -407,7 +411,11 @@ public class GoblinRedisUtils {
String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_SKU.concat(orderSkuId); String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_SKU.concat(orderSkuId);
Object obj = redisUtil.get(redisKey); Object obj = redisUtil.get(redisKey);
if (obj == null) { if (obj == null) {
return null; GoblinOrderSkuVo fromMongo = goblinMongoUtils.getGoblinOrderSkuVoByOrderSkuId(orderSkuId);
if (fromMongo != null) {
redisUtil.set(redisKey, fromMongo);
}
return fromMongo;
} else { } else {
return (GoblinOrderSkuVo) obj; return (GoblinOrderSkuVo) obj;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment