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

Commit 52399664 authored by 姜秀龙's avatar 姜秀龙

收钱吧 ai1

parent 1350071c
CREATE TABLE `goblin_sqb_mall_info` (
`mid` BIGINT NOT NULL AUTO_INCREMENT,
`mall_id` VARCHAR(64) NOT NULL COMMENT '商城唯一ID',
`mall_sn` VARCHAR(64) NOT NULL COMMENT '收钱吧商城编号',
`mall_name` VARCHAR(128) NOT NULL COMMENT '商城名称',
`signature` VARCHAR(256) NOT NULL COMMENT '商城密钥',
`store_id` VARCHAR(64) NOT NULL COMMENT '关联我方店铺ID',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_mall_sn` (`mall_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收钱吧商城信息';
CREATE TABLE `goblin_sqb_goods_ext` (
`mid` BIGINT NOT NULL AUTO_INCREMENT,
`spu_id` VARCHAR(64) NOT NULL COMMENT '关联 goblin_goods.spu_id',
`sku_id` VARCHAR(64) NOT NULL COMMENT '关联 goblin_goods_sku.sku_id',
`mall_sn` VARCHAR(64) NOT NULL COMMENT '所属收钱吧商城编号',
`sqb_product_id` VARCHAR(64) NOT NULL COMMENT '收钱吧商品ID',
`sqb_product_sn` VARCHAR(64) NOT NULL COMMENT '收钱吧商品编号',
`sqb_sku_id` VARCHAR(64) COMMENT '收钱吧SKU ID',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_spu_sku` (`spu_id`, `sku_id`),
KEY `idx_mall_sn` (`mall_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收钱吧商品扩展信息';
CREATE TABLE `goblin_sqb_performance_goods` (
`mid` BIGINT NOT NULL AUTO_INCREMENT,
`performances_id` VARCHAR(64) NOT NULL COMMENT '演出ID',
`spu_id` VARCHAR(64) NOT NULL COMMENT '商品ID',
`sku_id` VARCHAR(64) NOT NULL COMMENT 'SKU ID',
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序权重',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_perf_sku` (`performances_id`, `sku_id`),
KEY `idx_performances_id` (`performances_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='演出-收钱吧商品关联';
CREATE TABLE `goblin_sqb_order` (
`mid` BIGINT NOT NULL AUTO_INCREMENT,
`order_id` VARCHAR(64) NOT NULL COMMENT '本地订单ID',
`user_id` VARCHAR(64) NOT NULL COMMENT '用户ID',
`performances_id` VARCHAR(64) NOT NULL COMMENT '关联演出ID',
`spu_id` VARCHAR(64) NOT NULL COMMENT '商品ID',
`sku_id` VARCHAR(64) NOT NULL COMMENT 'SKU ID',
`quantity` INT NOT NULL DEFAULT 1,
`amount` BIGINT NOT NULL COMMENT '支付金额(分)',
`sqb_order_sn` VARCHAR(64) COMMENT '收钱吧订单号',
`sqb_order_signature` VARCHAR(256) COMMENT '收钱吧订单签名',
`sqb_acquiring_sn` VARCHAR(64) COMMENT '收钱吧收单号',
`sqb_checkout_items_id` VARCHAR(64) COMMENT '结算明细ID',
`coupon_sn` VARCHAR(64) COMMENT '券码编号',
`coupon_qr_code` VARCHAR(512) COMMENT '核销二维码',
`coupon_expire_time` DATETIME COMMENT '券码过期时间',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败',
`refund_reason` VARCHAR(256) COMMENT '退款原因',
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_order_id` (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_performances_id` (`performances_id`),
KEY `idx_sqb_acquiring_sn` (`sqb_acquiring_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收钱吧订单';
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class GoblinSqbCouponVo implements Serializable {
private static final long serialVersionUID = 1L;
private String couponSn;
private String couponQrCode;
private String couponExpireTime;
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class GoblinSqbOrderCreateVo implements Serializable {
private static final long serialVersionUID = 1L;
private String orderId;
private String acquiringSn;
// paymentVoucher fields
private String timeStamp;
private String packageStr;
private String paySign;
private String appId;
private String signType;
private String nonceStr;
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 收钱吧订单列表/详情展示 VO
* 融合:GoblinStoreOrderVo(基础信息)+ GoblinOrderSkuVo(商品信息)+ GoblinSqbOrderVo(收钱吧扩展)
*/
@Data
public class GoblinSqbOrderDetailVo implements Serializable {
private static final long serialVersionUID = 1L;
// ========== 来自 GoblinStoreOrderVo(现有订单体系) ==========
@ApiModelProperty(value = "本地订单ID")
private String orderId;
@ApiModelProperty(value = "订单号")
private String orderCode;
@ApiModelProperty(value = "goblin订单状态[0-待付款|2-已付款|5-取消]")
private Integer status;
@ApiModelProperty(value = "实付金额")
private BigDecimal priceActual;
@ApiModelProperty(value = "创建时间")
private String createdAt;
@ApiModelProperty(value = "支付时间")
private String payTime;
// ========== 来自 GoblinOrderSkuVo(sku商品信息) ==========
@ApiModelProperty(value = "商品SPU ID")
private String spuId;
@ApiModelProperty(value = "商品名称")
private String spuName;
@ApiModelProperty(value = "商品SKU ID")
private String skuId;
@ApiModelProperty(value = "款式名称")
private String skuName;
@ApiModelProperty(value = "款式图片")
private String skuImage;
@ApiModelProperty(value = "购买数量")
private Integer quantity;
// ========== 来自 GoblinSqbOrderVo(收钱吧扩展字段) ==========
@ApiModelProperty(value = "收钱吧订单状态:0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败")
private Integer sqbStatus;
@ApiModelProperty(value = "关联演出ID")
private String performancesId;
@ApiModelProperty(value = "收钱吧收单号")
private String sqbAcquiringSn;
@ApiModelProperty(value = "券码编号")
private String couponSn;
@ApiModelProperty(value = "核销二维码")
private String couponQrCode;
@ApiModelProperty(value = "券码过期时间")
private String couponExpireTime;
@ApiModelProperty(value = "核销状态:0-未核销 1-已核销")
private Integer couponUsedStatus;
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class GoblinSqbOrderVo implements Serializable {
private static final long serialVersionUID = 1L;
private String orderId;
private String userId;
private String performancesId;
private String spuId;
private String skuId;
private Integer quantity;
private Long amount;
private String sqbOrderSn;
private String sqbOrderSignature;
private String sqbAcquiringSn;
private String sqbCheckoutItemsId;
private String couponSn;
private String couponQrCode;
private String couponExpireTime;
private Integer status; // 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败
private Integer couponUsedStatus; // 0-未核销 1-已核销
private String refundReason;
private String createdAt;
private String updatedAt;
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class GoblinSqbPerfGoodsVo implements Serializable {
private static final long serialVersionUID = 1L;
private String spuId;
private String spuName;
private String skuId;
private String skuName;
private Long price;
private String coverPic;
private Integer sort;
}
package com.liquidnet.service.goblin.service;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import java.util.List;
import java.util.Map;
/**
* 收钱吧商品同步服务接口
*/
public interface IGoblinSqbGoodsService {
/**
* 获取所有商城及商品列表
* 自动拉取所有商城,循环获取每个商城的商品列表,聚合返回
*
* @return 商城商品列表
*/
ResponseDto<List<GoblinSqbPerfGoodsVo>> getAllMallProducts();
/**
* 批量同步商品到 goblin 系统
* 已存在则更新,不存在则新增(spuType=33)
*
* @param items 待同步商品列表,每项包含 mallSn、sqbProductId、sqbProductSn
* @return 同步结果
*/
ResponseDto<String> syncGoods(List<Map<String, String>> items);
/**
* 查询演出关联商品列表(先查 Redis 缓存,未命中则查 MySQL 并写入缓存)
*
* @param performancesId 演出ID
* @return 演出关联商品列表
*/
ResponseDto<List<GoblinSqbPerfGoodsVo>> getPerfGoods(String performancesId);
}
package com.liquidnet.service.goblin.service;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbCouponVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderDetailVo;
import java.util.List;
import java.util.Map;
/**
* 收钱吧订单服务接口
*/
public interface IGoblinSqbOrderService {
/**
* 创建收钱吧订单
*
* @param userId 用户ID(从 token 获取)
* @param spuId 商品 SPU ID
* @param skuId 商品 SKU ID
* @param quantity 购买数量
* @param performancesId 关联演出 ID
* @return 订单创建结果(orderId、acquiringSn、paymentVoucher)
*/
ResponseDto<GoblinSqbOrderCreateVo> createOrder(String userId, String spuId, String skuId,
Integer quantity, String performancesId);
/**
* 查询支付状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 支付状态(0-待支付 1-已支付 9-失败)
*/
ResponseDto<Integer> queryPayStatus(String userId, String orderId);
/**
* 获取核销二维码(券码)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 券码信息(couponSn、couponQrCode、couponExpireTime)
*/
ResponseDto<GoblinSqbCouponVo> queryCoupon(String userId, String orderId);
/**
* 申请退款
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 退款结果
*/
ResponseDto<Boolean> refund(String userId, String orderId);
/**
* 主动同步核销状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 同步结果
*/
ResponseDto<Boolean> syncCouponStatus(String userId, String orderId);
/**
* 支付成功回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto<String> handlePayCallback(Map<String, Object> params);
/**
* 退款成功回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto<String> handleRefundCallback(Map<String, Object> params);
/**
* 券状态变更回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto<String> handleCouponCallback(Map<String, Object> params);
/**
* 演出结束自动退款(定时任务调用)
*
* @param performancesId 演出ID
* @return 处理结果摘要(成功/失败笔数)
*/
ResponseDto<String> autoRefundByPerformance(String performancesId);
/**
* 查询用户收钱吧订单列表
*
* @param userId 用户ID
* @return 收钱吧订单列表(仅 skuType=33 的订单,按下单时间倒序)
*/
ResponseDto<List<GoblinSqbOrderDetailVo>> getOrderList(String userId);
/**
* 查询收钱吧订单详情
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 订单详情(基础信息 + 收钱吧扩展信息)
*/
ResponseDto<GoblinSqbOrderDetailVo> getOrderDetail(String userId, String orderId);
/**
* 再次付款(待支付状态下重新拉起微信支付)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 新的 paymentVoucher(复用原 orderId)
*/
ResponseDto<GoblinSqbOrderCreateVo> repay(String userId, String orderId);
}
package com.liquidnet.client.admin.web.controller.zhengzai.goblin;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsService;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 演出-收钱吧商品关联管理接口
*/
@Slf4j
@RestController
@Api(tags = "收钱吧-演出商品关联管理")
@RequestMapping("sqb/performance/goods")
public class SqbPerformanceGoodsController extends BaseController {
@Autowired
private ISqbPerformanceGoodsService sqbPerformanceGoodsService;
@PostMapping("bind")
@ApiOperation("关联演出与商品")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "skuIds", value = "SKU ID列表(多个逗号分隔)"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "sort", value = "排序权重", example = "0"),
})
public ResponseDto<Boolean> bind(@RequestParam("performancesId") String performancesId,
@RequestParam("skuIds") List<String> skuIds,
@RequestParam(value = "sort", required = false, defaultValue = "0") Integer sort) {
return sqbPerformanceGoodsService.bind(performancesId, skuIds, sort);
}
@DeleteMapping("unbind")
@ApiOperation("解除演出与商品关联")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "skuId", value = "SKU ID"),
})
public ResponseDto<Boolean> unbind(@RequestParam("performancesId") String performancesId,
@RequestParam("skuId") String skuId) {
return sqbPerformanceGoodsService.unbind(performancesId, skuId);
}
@GetMapping("list")
@ApiOperation("查询演出关联商品列表(管理后台)")
@ApiResponse(code = 200, message = "接口返回对象参数")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
})
public ResponseDto<List<GoblinSqbPerfGoodsVo>> list(@RequestParam("performancesId") String performancesId) {
return sqbPerformanceGoodsService.list(performancesId);
}
}
package com.liquidnet.client.admin.zhengzai.goblin.service;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import java.util.List;
/**
* 演出-收钱吧商品关联 服务接口
*/
public interface ISqbPerformanceGoodsService {
/**
* 关联演出与商品(批量)
*
* @param performancesId 演出ID
* @param skuIds SKU ID 列表
* @param sort 排序权重
* @return 操作结果
*/
ResponseDto<Boolean> bind(String performancesId, List<String> skuIds, Integer sort);
/**
* 解除演出与商品关联
*
* @param performancesId 演出ID
* @param skuId SKU ID
* @return 操作结果
*/
ResponseDto<Boolean> unbind(String performancesId, String skuId);
/**
* 查询演出关联的收钱吧商品列表
*
* @param performancesId 演出ID
* @return 商品列表
*/
ResponseDto<List<GoblinSqbPerfGoodsVo>> list(String performancesId);
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 收钱吧商品扩展信息
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("goblin_sqb_goods_ext")
public class GoblinSqbGoodsExt implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
/**
* 关联 goblin_goods.spu_id
*/
private String spuId;
/**
* 关联 goblin_goods_sku.sku_id
*/
private String skuId;
/**
* 所属收钱吧商城编号
*/
private String mallSn;
/**
* 收钱吧商品ID
*/
private String sqbProductId;
/**
* 收钱吧商品编号
*/
private String sqbProductSn;
/**
* 收钱吧SKU ID
*/
private String sqbSkuId;
private String createdAt;
private String updatedAt;
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 收钱吧商城信息
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("goblin_sqb_mall_info")
public class GoblinSqbMallInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
/**
* 商城唯一ID
*/
private String mallId;
/**
* 收钱吧商城编号
*/
private String mallSn;
/**
* 商城名称
*/
private String mallName;
/**
* 商城密钥
*/
private String signature;
/**
* 关联我方店铺ID
*/
private String storeId;
/**
* 状态 0-禁用 1-启用
*/
private Integer status;
private String createdAt;
private String updatedAt;
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 收钱吧订单
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("goblin_sqb_order")
public class GoblinSqbOrder implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
/**
* 本地订单ID
*/
private String orderId;
/**
* 用户ID
*/
private String userId;
/**
* 关联演出ID
*/
private String performancesId;
/**
* 商品ID
*/
private String spuId;
/**
* SKU ID
*/
private String skuId;
/**
* 购买数量
*/
private Integer quantity;
/**
* 支付金额(分)
*/
private Long amount;
/**
* 收钱吧订单号
*/
private String sqbOrderSn;
/**
* 收钱吧订单签名
*/
private String sqbOrderSignature;
/**
* 收钱吧收单号
*/
private String sqbAcquiringSn;
/**
* 结算明细ID
*/
private String sqbCheckoutItemsId;
/**
* 券码编号
*/
private String couponSn;
/**
* 核销二维码
*/
private String couponQrCode;
/**
* 券码过期时间
*/
private String couponExpireTime;
/**
* 状态 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败
*/
private Integer status;
/**
* 退款原因
*/
private String refundReason;
private String createdAt;
private String updatedAt;
}
package com.liquidnet.service.goblin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 演出-收钱吧商品关联
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("goblin_sqb_performance_goods")
public class GoblinSqbPerformanceGoods implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
/**
* 演出ID
*/
private String performancesId;
/**
* 商品ID
*/
private String spuId;
/**
* SKU ID
*/
private String skuId;
/**
* 排序权重
*/
private Integer sort;
/**
* 状态 0-禁用 1-启用
*/
private Integer status;
private String createdAt;
private String updatedAt;
}
package com.liquidnet.service.goblin.mapper;
import com.liquidnet.service.goblin.entity.GoblinSqbGoodsExt;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 收钱吧商品扩展信息 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public interface GoblinSqbGoodsExtMapper extends BaseMapper<GoblinSqbGoodsExt> {
}
package com.liquidnet.service.goblin.mapper;
import com.liquidnet.service.goblin.entity.GoblinSqbMallInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 收钱吧商城信息 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public interface GoblinSqbMallInfoMapper extends BaseMapper<GoblinSqbMallInfo> {
}
package com.liquidnet.service.goblin.mapper;
import com.liquidnet.service.goblin.entity.GoblinSqbOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 收钱吧订单 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public interface GoblinSqbOrderMapper extends BaseMapper<GoblinSqbOrder> {
}
package com.liquidnet.service.goblin.mapper;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 演出-收钱吧商品关联 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public interface GoblinSqbPerformanceGoodsMapper extends BaseMapper<GoblinSqbPerformanceGoods> {
}
...@@ -5,6 +5,8 @@ import feign.hystrix.FallbackFactory; ...@@ -5,6 +5,8 @@ import feign.hystrix.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Component @Component
@FeignClient(name = "liquidnet-service-goblin", @FeignClient(name = "liquidnet-service-goblin",
...@@ -28,4 +30,7 @@ public interface FeignGoblinTaskClient { ...@@ -28,4 +30,7 @@ public interface FeignGoblinTaskClient {
@GetMapping("/rsc/maizhi/job/refundRes") @GetMapping("/rsc/maizhi/job/refundRes")
ResponseDto<Boolean> refundRes(); ResponseDto<Boolean> refundRes();
@PostMapping("/goblin/job/sqb/autoRefund")
ResponseDto<String> sqbAutoRefund(@RequestParam("performancesId") String performancesId);
} }
...@@ -68,4 +68,19 @@ public class GoblinTaskHandler { ...@@ -68,4 +68,19 @@ public class GoblinTaskHandler {
XxlJobHelper.handleFail(); XxlJobHelper.handleFail();
} }
} }
/**
* 演出结束自动退款
* xxl-job 配置:JobHandler = sev-goblin:sqbAutoRefund,任务参数传入 performancesId
*/
@XxlJob(value = "sev-goblin:sqbAutoRefund")
public void sqbAutoRefund() {
String performancesId = XxlJobHelper.getJobParam();
try {
XxlJobHelper.handleSuccess("结果:" + feignGoblinTaskClient.sqbAutoRefund(performancesId).getData());
} catch (Exception e) {
XxlJobHelper.log(e);
XxlJobHelper.handleFail();
}
}
} }
...@@ -60,6 +60,12 @@ ...@@ -60,6 +60,12 @@
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-goblin-do</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>com.liquidnet</groupId> <groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-zxlnft</artifactId> <artifactId>liquidnet-common-third-zxlnft</artifactId>
......
package com.liquidnet.service.goblin.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.service.IGoblinSqbOrderService;
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.RestController;
import java.util.Map;
/**
* 收钱吧回调 Controller(收钱吧主动推送,无需鉴权)
*/
@Slf4j
@Api(tags = "收钱吧回调")
@RestController
public class GoblinSqbCallbackController {
@Autowired
private IGoblinSqbOrderService goblinSqbOrderService;
/**
* 支付成功回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/order/callback
*/
@PostMapping("/goblin/sqb/order/callback")
@ApiOperation("支付成功回调")
public ResponseDto<String> payCallback(@RequestBody Map<String, Object> params) {
log.info("[收钱吧支付回调] 收到推送");
return goblinSqbOrderService.handlePayCallback(params);
}
/**
* 退款成功回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/refund/callback
*/
@PostMapping("/goblin/sqb/refund/callback")
@ApiOperation("退款成功回调")
public ResponseDto<String> refundCallback(@RequestBody Map<String, Object> params) {
log.info("[收钱吧退款回调] 收到推送");
return goblinSqbOrderService.handleRefundCallback(params);
}
/**
* 券状态变更回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/coupon/callback
*/
@PostMapping("/goblin/sqb/coupon/callback")
@ApiOperation("券状态变更回调")
public ResponseDto<String> couponCallback(@RequestBody Map<String, Object> params) {
log.info("[收钱吧券状态回调] 收到推送");
return goblinSqbOrderService.handleCouponCallback(params);
}
}
package com.liquidnet.service.goblin.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import com.liquidnet.service.goblin.service.IGoblinSqbGoodsService;
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.*;
import java.util.List;
import java.util.Map;
/**
* 收钱吧商品同步 Controller
*/
@Slf4j
@Api(tags = "收钱吧商品同步")
@RestController
@RequestMapping("/goblin/sqb/goods")
public class GoblinSqbGoodsController {
@Autowired
IGoblinSqbGoodsService goblinSqbGoodsService;
/**
* 查询所有商城及商品列表
* 后端自动拉取所有商城,循环获取每个商城的商品列表,聚合返回前端
*/
@GetMapping("/list")
@ApiOperation("查询收钱吧所有商城及商品列表")
public ResponseDto<List<GoblinSqbPerfGoodsVo>> list() {
return goblinSqbGoodsService.getAllMallProducts();
}
/**
* 批量同步商品到 goblin 系统
* 已存在则更新,不存在则新增(spuType=33)
*/
@PostMapping("/sync")
@ApiOperation("批量同步收钱吧商品")
public ResponseDto<String> sync(@RequestBody List<Map<String, String>> items) {
return goblinSqbGoodsService.syncGoods(items);
}
/**
* 查询演出关联商品列表(C端)
* 先查 Redis 缓存(TTL 5min),未命中则查 MySQL 并写入缓存
*/
@GetMapping("/performance/goods/{performancesId}")
@ApiOperation("查询演出关联商品列表")
public ResponseDto<List<GoblinSqbPerfGoodsVo>> getPerfGoods(@PathVariable String performancesId) {
return goblinSqbGoodsService.getPerfGoods(performancesId);
}
}
package com.liquidnet.service.goblin.controller;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbCouponVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderDetailVo;
import com.liquidnet.service.goblin.service.IGoblinSqbOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 收钱吧订单 Controller
*/
@Slf4j
@Api(tags = "收钱吧订单")
@RestController
@RequestMapping("/goblin/sqb/order")
public class GoblinSqbOrderController {
@Autowired
private IGoblinSqbOrderService goblinSqbOrderService;
/**
* 创建收钱吧订单
*/
@PostMapping("/create")
@ApiOperation("创建收钱吧订单")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "spuId", value = "商品 SPU ID"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "skuId", value = "商品 SKU ID"),
@ApiImplicitParam(type = "form", required = true, dataType = "Integer", name = "quantity", value = "购买数量", example = "1"),
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "performancesId", value = "关联演出 ID"),
})
public ResponseDto<GoblinSqbOrderCreateVo> createOrder(
@NotBlank(message = "spuId 不能为空") @RequestParam("spuId") String spuId,
@NotBlank(message = "skuId 不能为空") @RequestParam("skuId") String skuId,
@NotNull(message = "quantity 不能为空")
@Min(value = 1, message = "购买数量至少为 1") @RequestParam("quantity") Integer quantity,
@NotBlank(message = "performancesId 不能为空") @RequestParam("performancesId") String performancesId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧下单] 接收请求 userId={}, spuId={}, skuId={}, quantity={}, performancesId={}",
userId, spuId, skuId, quantity, performancesId);
return goblinSqbOrderService.createOrder(userId, spuId, skuId, quantity, performancesId);
}
/**
* 查询支付状态
*/
@GetMapping("/pay/status")
@ApiOperation("查询支付状态")
@ApiImplicitParam(type = "query", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<Integer> queryPayStatus(
@NotBlank(message = "orderId 不能为空") @RequestParam("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧支付状态] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.queryPayStatus(userId, orderId);
}
/**
* 获取核销二维码(券码)
*/
@GetMapping("/coupon/{orderId}")
@ApiOperation("获取核销二维码")
@ApiImplicitParam(type = "path", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<GoblinSqbCouponVo> queryCoupon(
@NotBlank(message = "orderId 不能为空") @PathVariable("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧券码] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.queryCoupon(userId, orderId);
}
/**
* 申请退款
*/
@PostMapping("/refund")
@ApiOperation("申请退款")
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<Boolean> refund(
@NotBlank(message = "orderId 不能为空") @RequestParam("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧退款] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.refund(userId, orderId);
}
/**
* 主动同步核销状态
*/
@PostMapping("/coupon/sync")
@ApiOperation("主动同步核销状态")
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<Boolean> syncCouponStatus(
@NotBlank(message = "orderId 不能为空") @RequestParam("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧核销同步] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.syncCouponStatus(userId, orderId);
}
/**
* 用户收钱吧订单列表
*/
@GetMapping("/list")
@ApiOperation("用户收钱吧订单列表")
public ResponseDto<java.util.List<GoblinSqbOrderDetailVo>> getOrderList() {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧订单列表] 接收请求 userId={}", userId);
return goblinSqbOrderService.getOrderList(userId);
}
/**
* 订单详情
*/
@GetMapping("/detail/{orderId}")
@ApiOperation("收钱吧订单详情")
@ApiImplicitParam(type = "path", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<GoblinSqbOrderDetailVo> getOrderDetail(
@NotBlank(message = "orderId 不能为空") @PathVariable("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧订单详情] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.getOrderDetail(userId, orderId);
}
/**
* 再次付款(待支付状态下重新拉起微信支付)
*/
@PostMapping("/repay")
@ApiOperation("再次付款")
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "本地订单ID")
public ResponseDto<GoblinSqbOrderCreateVo> repay(
@NotBlank(message = "orderId 不能为空") @RequestParam("orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧再次付款] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbOrderService.repay(userId, orderId);
}
}
package com.liquidnet.service.goblin.controller.Inner;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.service.IGoblinSqbOrderService;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 收钱吧定时任务入口(供 executor 服务通过 Feign 调用)
*
* xxl-job 配置说明(Task 11.4 运维配置):
* - 在 xxl-job 平台新增定时任务
* - JobHandler:sev-goblin:sqbAutoRefund
* - CRON:按演出结束时间触发(如演出结束后 5 分钟:0 5 * * * ?,具体由运维根据演出时间动态配置)
* - 执行器:liquidnet-service-executor
* - 任务参数:performancesId=xxx(演出ID)
* - 路由策略:第一个
*/
@Slf4j
@Api(tags = "收钱吧定时任务")
@RestController
@RequestMapping("/goblin/job/sqb")
public class GoblinSqbJobController {
@Autowired
private IGoblinSqbOrderService goblinSqbOrderService;
@PostMapping("/autoRefund")
@ApiOperation("演出结束自动退款")
public ResponseDto<String> autoRefund(@RequestParam("performancesId") String performancesId) {
log.info("[收钱吧自动退款] 收到任务,performancesId={}", performancesId);
return goblinSqbOrderService.autoRefundByPerformance(performancesId);
}
}
...@@ -1697,6 +1697,11 @@ public class GoblinRedisUtils { ...@@ -1697,6 +1697,11 @@ public class GoblinRedisUtils {
redisUtil.set(redisKey, list); redisUtil.set(redisKey, list);
} }
//主订单下包含的子订单
public void setMasterCode(String masterCode, String orderIds) {
String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_MASTER.concat(masterCode);
redisUtil.set(redisKey, orderIds);
}
public String[] getMasterCode(String masterCode) { public String[] getMasterCode(String masterCode) {
String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_MASTER.concat(masterCode); String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_MASTER.concat(masterCode);
......
package com.liquidnet.service.goblin.util;
import com.fasterxml.jackson.core.type.TypeReference;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* 收钱吧相关 Redis 操作封装
*/
@Slf4j
@Component
public class GoblinSqbRedisUtils {
// Redis Key 前缀
private static final String KEY_SQB_ORDER = "goblin:sqb:order:";
private static final String KEY_SQB_PERF_GOODS = "goblin:sqb:perf:goods:";
private static final String KEY_SQB_ORDER_LOCK = "goblin:sqb:order:lock:";
// TTL 常量(秒)
private static final long TTL_ORDER = 2 * 60 * 60; // 2h
private static final long TTL_PERF_GOODS = 5 * 60; // 5min
private static final long TTL_ORDER_LOCK = 10; // 10s
@Autowired
private RedisUtil redisUtil;
/* ---------------------------------------- 订单操作(TTL 2h) ---------------------------------------- */
public void setSqbOrder(String orderId, GoblinSqbOrderVo vo) {
redisUtil.set(KEY_SQB_ORDER.concat(orderId), JsonUtils.toJson(vo), TTL_ORDER);
}
public GoblinSqbOrderVo getSqbOrder(String orderId) {
String valStr = (String) redisUtil.get(KEY_SQB_ORDER.concat(orderId));
if (StringUtils.isEmpty(valStr)) {
return null;
}
return JsonUtils.fromJson(valStr, GoblinSqbOrderVo.class);
}
public void delSqbOrder(String orderId) {
redisUtil.del(KEY_SQB_ORDER.concat(orderId));
}
/* ---------------------------------------- 演出关联商品缓存(TTL 5min) ---------------------------------------- */
public void setPerfGoods(String performancesId, List<GoblinSqbPerfGoodsVo> list) {
redisUtil.set(KEY_SQB_PERF_GOODS.concat(performancesId), JsonUtils.toJson(list), TTL_PERF_GOODS);
}
public List<GoblinSqbPerfGoodsVo> getPerfGoods(String performancesId) {
String valStr = (String) redisUtil.get(KEY_SQB_PERF_GOODS.concat(performancesId));
if (StringUtils.isEmpty(valStr)) {
return null;
}
return JsonUtils.fromJson(valStr, new TypeReference<List<GoblinSqbPerfGoodsVo>>() {});
}
public void delPerfGoods(String performancesId) {
redisUtil.del(KEY_SQB_PERF_GOODS.concat(performancesId));
}
/* ---------------------------------------- 下单防重锁(TTL 10s) ---------------------------------------- */
/**
* 尝试获取下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
* @return true 获取成功,false 已被锁定
*/
public boolean tryOrderLock(String userId, String skuId) {
String key = KEY_SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
return redisUtil.lock(key, 1, TTL_ORDER_LOCK);
}
/**
* 释放下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
*/
public void releaseOrderLock(String userId, String skuId) {
String key = KEY_SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
redisUtil.uLock(key);
}
}
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