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

Commit 2e9b36ae authored by 姜秀龙's avatar 姜秀龙

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

parents e531a918 bdd81e6e
-- CREATE TABLE `goblin_sqb_mall_info` (
-- `mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
-- `mall_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城唯一ID',
-- `mall_sn` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧商城编号',
-- `mall_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城名称',
-- `signature` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城密钥',
-- `store_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联我方店铺ID',
-- `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
-- `created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
-- `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
-- PRIMARY KEY (`mid`),
-- UNIQUE KEY `uk_mall_sn` (`mall_sn`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧商城信息';
--
-- CREATE TABLE `goblin_sqb_goods_ext` (
-- `mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
-- `spu_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联 goblin_goods.spu_id',
-- `sku_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联 goblin_goods_sku.sku_id',
-- `mall_sn` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '所属收钱吧商城编号',
-- `signature` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城密钥',
-- `sqb_spu_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧SPU ID',
-- `sqb_sku_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧SKU ID',
-- `created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
-- `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
-- PRIMARY KEY (`mid`),
-- UNIQUE KEY `uk_spu_sku` (`spu_id`, `sku_id`),
-- KEY `idx_mall_sn` (`mall_sn`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧商品扩展信息';
--
-- CREATE TABLE `goblin_sqb_performance_goods` (
-- `mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
-- `performances_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
-- `spu_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品ID',
-- `sku_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU ID',
-- `sort` INT NOT NULL DEFAULT 0 COMMENT '排序权重',
-- `settlement_price` DECIMAL(10,2) DEFAULT NULL COMMENT '换购价格(不设置则按商品售价售卖)',
-- `status` TINYINT NOT NULL DEFAULT 1 COMMENT '0-禁用 1-启用',
-- `created_at` timestamp NULL DEFAULT NULL,
-- `updated_at` timestamp NULL DEFAULT NULL,
-- PRIMARY KEY (`mid`),
-- UNIQUE KEY `uk_perf_sku` (`performances_id`, `sku_id`),
-- KEY `idx_performances_id` (`performances_id`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='演出-收钱吧商品关联';
--
-- CREATE TABLE `goblin_sqb_order` (
-- `mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
-- `order_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本地订单ID',
-- `user_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID',
-- `performances_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联演出ID',
-- `spu_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品ID',
-- `sku_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU ID',
-- `quantity` INT NOT NULL DEFAULT 1 COMMENT '购买数量',
-- `amount` BIGINT(20) NOT NULL COMMENT '支付金额(分)',
-- `sqb_order_sn` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧订单号',
-- `sqb_order_signature` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧订单签名',
-- `sqb_acquiring_sn` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧收单号',
-- `sqb_acquiring_sign` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧收单密钥',
-- `sqb_checkout_items_id` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '结算明细ID',
-- `coupon_sn` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci default '' COMMENT '券码编号',
-- `coupon_qr_code` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci default '' COMMENT '核销二维码',
-- `coupon_expire_time` DATETIME DEFAULT NULL COMMENT '券码过期时间',
-- `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败',
-- `refund_reason` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci default '' COMMENT '退款原因',
-- `sqb_refund_sn` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci default '' COMMENT '收钱吧-退款号',
-- `sqb_refund_signature` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci default '' COMMENT '收钱吧-退款号密码',
-- `created_at` timestamp NULL DEFAULT NULL,
-- `updated_at` timestamp NULL DEFAULT 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 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧订单';
--
--
-- CREATE TABLE `goblin_sqb_performance_config` (
-- `mid` bigint(20) NOT NULL AUTO_INCREMENT,
-- `performances_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
-- `auto_offline` tinyint(4) DEFAULT '0' COMMENT '演出结束自动下架 0-否 1-是',
-- `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
-- `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- PRIMARY KEY (`mid`),
-- UNIQUE KEY `uk_perf_id` (`performances_id`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧演出级全局配置';
-- -- 2026-03-30 增加逻辑删除及审计建议字段
-- ALTER TABLE `goblin_sqb_goods_ext` ADD COLUMN `updated_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人' AFTER `updated_at`;
-- ALTER TABLE `goblin_sqb_goods_ext` ADD COLUMN `del_flg` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '0' COMMENT '删除标记[0-未删除|1-删除]' AFTER `updated_by`;
-- ALTER TABLE `goblin_sqb_goods_ext` ADD COLUMN `deleted_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '删除人' AFTER `del_flg`;
-- ALTER TABLE `goblin_sqb_goods_ext` ADD COLUMN `deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间' AFTER `deleted_by`;
---------------------------------- !!! 2026-4-3 设置字段排序规则-最新表结构 --------------------------------
CREATE TABLE `goblin_sqb_goods_ext` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT,
`spu_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联 goblin_goods.spu_id',
`sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联 goblin_goods_sku.sku_id',
`mall_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '所属收钱吧商城编号',
`signature` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城密钥',
`sqb_spu_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧SPU ID',
`sqb_sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收钱吧SKU ID',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`updated_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人',
`del_flg` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '0' COMMENT '删除标记[0-未删除|1-删除]',
`deleted_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '删除人',
`deleted_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_spu_sku` (`spu_id`,`sku_id`),
KEY `idx_mall_sn` (`mall_sn`)
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧商品扩展信息';
CREATE TABLE `goblin_sqb_performance_goods` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`performances_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
`spu_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品ID',
`sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU ID',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序权重',
`settlement_price` decimal(10,2) DEFAULT NULL COMMENT '换购价格(不设置则按商品售价售卖)',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0-禁用 1-启用',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_perf_sku` (`performances_id`,`sku_id`),
KEY `idx_performances_id` (`performances_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='演出-收钱吧商品关联';
CREATE TABLE `goblin_sqb_performance_config` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT,
`performances_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '演出ID',
`auto_offline` tinyint(4) DEFAULT '0' COMMENT '演出结束自动下架 0-否 1-是',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_perf_id` (`performances_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧演出级全局配置';
CREATE TABLE `goblin_sqb_mall_info` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`mall_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城唯一ID',
`mall_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧商城编号',
`mall_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城名称',
`signature` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商城密钥',
`store_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联我方店铺ID',
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0-禁用 1-启用',
`created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_mall_sn` (`mall_sn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧商城信息';
CREATE TABLE `goblin_sqb_order` (
`mid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '本地订单ID',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户ID',
`performances_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联演出ID',
`spu_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '商品ID',
`sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SKU ID',
`quantity` int(11) NOT NULL DEFAULT '1' COMMENT '购买数量',
`amount` bigint(20) NOT NULL COMMENT '支付金额(分)',
`sqb_order_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧订单号',
`sqb_order_signature` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧订单签名',
`sqb_acquiring_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧收单号',
`sqb_acquiring_sign` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收钱吧收单密钥',
`sqb_checkout_items_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '结算明细ID',
`coupon_sn` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '券码编号',
`coupon_qr_code` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '核销二维码',
`coupon_expire_time` datetime DEFAULT NULL COMMENT '券码过期时间',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败',
`refund_reason` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '退款原因',
`sqb_refund_sn` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '收钱吧-退款号',
`sqb_refund_signature` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '收钱吧-退款号密码',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT 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 COLLATE=utf8mb4_unicode_ci COMMENT='收钱吧订单';
\ No newline at end of file
......@@ -27,6 +27,12 @@
<artifactId>liquidnet-service-dragon-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-sqb</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -430,4 +430,39 @@ public class GoblinRedisConst {
/* ----------------------------------------------------------------- */
/* ----------------------------------------------------------------- */
/* ----------------------------------------------------------------- */
/**
* 收钱吧订单详情
*/
public static final String SQB_ORDER = PREFIX.concat("sqb:order:");
/**
* 演出关联收钱吧商品缓存 performancesId
*/
public static final String SQB_PERFORMANCE_GOODS = PREFIX.concat("sqb:perf:goods:");
/**
* 收钱吧下单防重锁
*/
public static final String SQB_ORDER_LOCK = PREFIX.concat("sqb:order:lock:");
/**
* 收钱吧订单扩展 key
*/
public static final String SQB_GOBLIN_GOODS_EXT_KEY = PREFIX.concat("sqb:goods:ext:");
/**
* 收钱吧 orderSn与正在orderId 对应关系 key
*/
public static final String SQB_GOBLIN_ORDER_SN_KEY = PREFIX.concat("sqb:orderSn:");
/**
* 收钱吧 用户订单列表
*/
public static final String SQB_GOBLIN_ORDER_LIST = PREFIX.concat("sqb:order:id:list:");//用户订单id列表 key:$uid
/**
* 演出关联收钱吧商品换购价 缓存 performancesId:skuId
*/
public static final String SQB_SKU_PRICE = PREFIX.concat("sqb:sku:price:");
}
package com.liquidnet.service.goblin.dto.manage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@ApiModel(value = "GoblinSqbOrderParam")
@Data
public class GoblinSqbOrderParam {
@ApiModelProperty(required = true, value = "apuId")
@NotBlank(message = "商品不能为空")
private String spuId;
@ApiModelProperty(required = true, value = "skuId")
@NotBlank(message = "商品规格不能为空")
private String skuId;
@ApiModelProperty(required = true, value = "购买数量")
@NotNull(message = "购买数量不能为空")
private Integer quantity;
@ApiModelProperty(required = true, value = "关联演出ID")
@NotBlank(message = "关联演出ID不能为空")
private String performancesId;
@ApiModelProperty(required = true, value = "微信 openId(对应支付小程序 appid 下的用户唯一 openid)")
@NotBlank(message = "openId不能为空")
private String openId;
}
package com.liquidnet.service.goblin.dto.manage;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.goblin.dto.GoblinGoodsSpecDto;
import com.liquidnet.service.goblin.dto.vo.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ApiModel(value = "GoblinStoreMgtGoodsSqbAddParam", description = "商品管理:添加收钱吧商品入参")
@Data
public class GoblinStoreMgtGoodsSqbAddParam implements Serializable {
private static final long serialVersionUID = -6261151635859590265L;
@ApiModelProperty(position = 10, required = true, value = "店铺ID")
@NotNull(message = "店铺ID不能为空")
private String storeId;
@ApiModelProperty(position = 11, required = false, value = "商品ID[编辑时必传]")
private String spuId;
/**
* ---------------------------- 基本信息 ----------------------------
**/
@ApiModelProperty(position = 12, required = true, value = "商品名称[36]")
@NotBlank(message = "商品名称不能为空")
@Size(max = 36, message = "商品名称长度超限")
private String name;
@ApiModelProperty(position = 15, required = true, value = "商品一级分类ID[30]")
@NotBlank(message = "商品分类ID不能为空")
private String cateFid;
@ApiModelProperty(position = 16, required = true, value = "商品二级分类ID[30]")
private String cateSid;
@ApiModelProperty(position = 17, required = true, value = "商品三级分类ID[30]")
private String cateTid;
@ApiModelProperty(position = 18, required = false, value = "商品简介[256]", example = "商品简介...")
@Size(max = 256, message = "商品简介内容过长")
private String intro;
@ApiModelProperty(position = 19, required = false, value = "商品标签列表")
private List<String> tagList;
@ApiModelProperty(position = 20, required = true, value = "商品图片列表")
private List<String> imageList;
@ApiModelProperty(position = 21, required = false, value = "商品图片封面")
@Size(max = 256, message = "商品图片封面URL过长")
private String coverPic;
@ApiModelProperty(position = 22, required = false, value = "商品视频地址")
@Size(max = 256, message = "商品视频地址URL过长")
private String video;
/**
* ---------------------------- 价格库存 ----------------------------
**/
@ApiModelProperty(position = 24, required = false, value = "SKU规格信息[初次添加商品必填]")
@Valid
private GoblinStoreMgtGoodsSqbAddSkuParam skuParam;
@ApiModelProperty(position = 26, required = false, value = "商品编码[默认为系统编码,也可手动输入商家自己的编码]")
@Pattern(regexp = LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, message = "商品编码格式或长度有误")
private String spuNo;
/**
* ---------------------------- 商品介绍 ----------------------------
**/
@ApiModelProperty(position = 27, required = true, value = "商品详情", example = "商品详情...")
@Size(max = 10000, message = "商品详情内容过长")
private String details;
/**
* ---------------------------- 销售属性 ----------------------------
**/
@ApiModelProperty(position = 28, required = true, value = "上架处理方式[1-等待手动上架|2-直接上架售卖|3-预约定时上架]", example = "1")
@NotNull(message = "上架处理方式不能为空")
@Pattern(regexp = "\\b(1|2|3)\\b", message = "规格展现方式参数无效")
private String shelvesHandle;
@ApiModelProperty(position = 29, required = false, value = "预约上架时间[yyyy-MM-dd HH:mm:ss][上架处理方式为3-预约定时上架时需要指定]")
@Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "预约上架时间格式有误")
private String shelvesTime;
/**
* ---------------------------- 服务保障 ----------------------------
**/
@ApiModelProperty(position = 32, required = false, value = "商品服务支持ID列表")
private List<String> ssidList;
/**
* ---------------------------- 专属标签 ----------------------------
**/
@ApiModelProperty(position = 33, required = false, value = "商品关联音乐人、艺术家、品牌方、厂牌列表")
private List<String> extagList;
/**
* ---------------------------- AR标签 ----------------------------
**/
@ApiModelProperty(position = 33, required = false, value = "商品关联音乐人、艺术家、品牌方、厂牌、AR列表")
private List<String> artagList;
/**
* 生成券类SPU
*
* @return GoblinGoodsInfoVo
*/
public GoblinGoodsInfoVo initGoodsInfoVo(String storeId, GoblinSqbPerfGoodsVo sqbGoods, String currentUid, LocalDateTime nowTime) {
GoblinGoodsInfoVo vo = GoblinGoodsInfoVo.getNew();
vo.setStoreId(storeId);
// 主表使用系统ID,收钱吧ID仅通过`goblin_sqb_goods_ext`映射
vo.setSpuId(IDGenerator.nextMilliId2());
vo.setSpuNo(vo.getSpuId());
vo.setSpuType(33);
vo.setName(sqbGoods.getTitle());
vo.setIntro("");
vo.setDetails(sqbGoods.getProductIntroduction());
vo.setCoverPic(CollectionUtil.isEmpty(sqbGoods.getConverImages()) ? "" : sqbGoods.getConverImages().get(0));
vo.setVideo("");
vo.setSpecMode("1");
vo.setCateFid("2690132621531");
vo.setCateSid("2690132639617");
vo.setCateTid("2690132656988");
vo.setShelvesHandle("1");
vo.setShelvesTime(null);
vo.setVirtualFlg("1");
vo.setStatus("3");
vo.setShelvesStatus("0");
vo.setSpuAppear("0");
vo.setDelFlg("0");
vo.setImageList(sqbGoods.getConverImages());
vo.setLogisticsTemplate("");
vo.setCreatedBy(currentUid);
vo.setCreatedAt(nowTime);
return vo;
}
/**
* 编辑SPU参数整理
*
* @return GoblinGoodsInfoVo
*/
public GoblinGoodsInfoVo initEditGoodsInfoVo(GoblinSqbPerfGoodsVo sqbGoods,
GoblinGoodsInfoVo mgtGoodsInfoVo) {
GoblinGoodsInfoVo vo = mgtGoodsInfoVo;
vo.setName(sqbGoods.getTitle());
vo.setDetails(sqbGoods.getProductIntroduction());
vo.setCoverPic(CollectionUtil.isEmpty(sqbGoods.getConverImages()) ? "" : sqbGoods.getConverImages().get(0));
// vo.setImageList(sqbGoods.getConverImages());
return vo;
}
/**
* 生成SKU列表以及SPU规格等信息
*
* @param goodsInfoVo GoblinGoodsInfoVo
* @param skuInfoVos List<GoblinGoodsSkuInfoVo>
* @param goodsSpecVos List<GoblinGoodsSpecVo>
*/
public void initGoodsSkuInfoVo(GoblinGoodsInfoVo goodsInfoVo, List<GoblinGoodsSkuInfoVo> skuInfoVos,
List<GoblinGoodsSpecVo> goodsSpecVos, GoblinSqbPerfGoodsVo sqbGoods) {
List<MallProductsQueryData.Sku> addSkuParamSqb = sqbGoods.getSkuResults();
if (CollectionUtil.isEmpty(addSkuParamSqb)) {
addSkuParamSqb = new ArrayList<>();
}
List<String> skuIdList = CollectionUtil.arrayListString();
for (MallProductsQueryData.Sku sku : addSkuParamSqb) {
GoblinGoodsSkuInfoVo skuInfoVo = GoblinGoodsSkuInfoVo.getNew();
String systemSkuId = IDGenerator.nextSnowId();
skuInfoVo.setSpuId(goodsInfoVo.getSpuId());
// 主表使用系统SKU_ID,收钱吧SKU_ID仅通过`goblin_sqb_goods_ext`映射
skuInfoVo.setSkuId(systemSkuId);
skuInfoVo.setSkuNo(systemSkuId);
skuInfoVo.setSkuBarCode(systemSkuId);
skuInfoVo.setSkuType(33);
skuInfoVo.setName(sku.getSkuName());
skuInfoVo.setSubtitle("");
skuInfoVo.setSkuPic("");
skuInfoVo.setSkuIsbn("");
skuInfoVo.setStock(999);
skuInfoVo.setSkuStock(999);
skuInfoVo.setPrice(BigDecimal.valueOf(sku.getPrice()));
skuInfoVo.setPriceMember(BigDecimal.valueOf(sku.getPrice()));
skuInfoVo.setBuyFactor("0");
skuInfoVo.setBuyLimit(0);
skuInfoVo.setStoreId(goodsInfoVo.getStoreId());
skuInfoVo.setVirtualFlg("1");
skuInfoVo.setStatus("3");// 没有审核流程,默认通过
skuInfoVo.setShelvesStatus(goodsInfoVo.getShelvesStatus());
skuInfoVo.setSkuAppear("0");
skuInfoVo.setDelFlg("0");
skuInfoVo.setCreatedBy(goodsInfoVo.getCreatedBy());
skuInfoVo.setCreatedAt(goodsInfoVo.getCreatedAt());
skuInfoVo.setLogisticsTemplate("");
skuInfoVo.setErpType(goodsInfoVo.getErpType());
skuInfoVo.setErpHosting(0);
{// 规格信息处理
// 收钱吧商品固定规格
GoblinGoodsSpecDto goblinGoodsSpecDto = GoblinGoodsSpecDto.getNew();
goblinGoodsSpecDto.setSpecName("规格");
goblinGoodsSpecDto.setSpecVname("默认");
// SKU规格设置
List<GoblinGoodsSpecDto> skuSpecList = new ArrayList<>();
skuSpecList.add(goblinGoodsSpecDto);
skuInfoVo.setSkuSpecList(skuSpecList);
}
skuIdList.add(systemSkuId);
skuInfoVos.add(skuInfoVo);
}
// SPU规格集合
GoblinGoodsSpecValueVo specValueVo = GoblinGoodsSpecValueVo.getNew().setSpecVname("规格").setSpecVsort(0);
ArrayList<GoblinGoodsSpecValueVo> specValueVos = new ArrayList<>();
specValueVos.add(specValueVo);
goodsSpecVos.add(GoblinGoodsSpecVo.getNew().setSpecName("默认").setSpecSort(0).setSpecValues(specValueVos));
goodsInfoVo.setPriceGe(BigDecimal.ZERO);
goodsInfoVo.setPriceLe(BigDecimal.ZERO);
goodsInfoVo.setSkuIdList(skuIdList);
goodsInfoVo.setSpecVoList(goodsSpecVos);
}
}
package com.liquidnet.service.goblin.dto.manage;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.service.goblin.dto.GoblinGoodsSpecDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@ApiModel(value = "GoblinStoreMgtGoodsSqbAddSkuParam", description = "商品管理:添加收钱吧商品:添加SKU入参")
@Data
public class GoblinStoreMgtGoodsSqbAddSkuParam implements Serializable {
private static final long serialVersionUID = 7886534346305369761L;
@ApiModelProperty(position = 10, required = false, value = "单品ID[编辑时必传]")
private String skuId;
// @ApiModelProperty(position = 11, required = false, value = "单品编码[默认为系统编码,也可手动输入商家自己的编码]")
// @Pattern(regexp = LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, message = "单品编码格式或长度有误")
// private String skuNo;
// @ApiModelProperty(position = 12, required = false, value = "单品条码")
// @Pattern(regexp = LnsRegex.Valid.ALPHABET_NUMBER_32, message = "单品条码格式或长度有误")
// private String skuBarCode;
// @ApiModelProperty(position = 13, required = false, value = "ERP商家编码")
// @Size(max = 40, message = "ERP商家编码长度限制40")
// private String skuErpCode;//-
// @ApiModelProperty(position = 14, required = false, value = "ERP托管[0-否|1-是],默认0")
// @Pattern(regexp = "\\b(0|1)\\b", message = "ERP托管参数无效")
// private String erpHosting;//-
// @ApiModelProperty(position = 15, required = false, value = "ERP仓库编号")
// @Size(max = 40, message = "ERP仓库编号长度限制40")
// private String erpWarehouseNo;//-
// @ApiModelProperty(position = 16, required = false, value = "单品默认图片的url[256]")
// @NotBlank(message = "单品图片不能为空")
// private String skuPic;
@ApiModelProperty(position = 12, required = false, value = "单品规格信息")
// @NotNull(message = "规格信息不能为空")
// @Valid// 初始化写死['规格':'张']
private List<GoblinGoodsSpecDto> skuSpecList;
// @ApiModelProperty(position = 18, required = false, value = "单品销售价-原价[20,2]")
// private BigDecimal sellPrice;
@ApiModelProperty(position = 19, required = true, value = "单品现价[20,2]")
@Digits(integer = 6, fraction = 2, message = "参数'单品现价'无效")
@DecimalMin(value = "0.01", message = "参数'单品现价'必须为大于0")
private BigDecimal price;
// @ApiModelProperty(position = 20, required = false, value = "单品会员价格[20,2]")
// @NotNull(message = "单品会员价格不能为空")
// @Min(value = 0, message = "单品会员价格不能小于0")
// private BigDecimal priceMember;
// @ApiModelProperty(position = 21, required = false, value = "单品的重量[20,2]")
// private BigDecimal weight;//-
@ApiModelProperty(position = 20, required = true, value = "总库存")
@Min(value = 0, message = "总库存不能小于0")
private Integer stock;
// @ApiModelProperty(position = 21, required = false, value = "预警库存")
// private Integer warningStock;
// @ApiModelProperty(position = 22, required = false, value = "ISBN,针对CD/图书等[100]")
// private String skuIsbn;//-
// @ApiModelProperty(position = 23, required = false, value = "购买限制[0-全部用户|1-仅会员|2-指定用户]")
// @NotNull(message = "购买限制不能为空")
// private String buyFactor;
// @ApiModelProperty(position = 24, required = false, value = "购买限制人员名单[购买限制为2-指定用户时必填]")
// private String buyRoster;
@ApiModelProperty(position = 25, required = false, value = "限量[0-无限制|X:限购数量]")
private Integer buyLimit;
// @ApiModelProperty(position = 26, required = false, value = "单品有效期[yyyy-MM-dd HH:mm:ss]")
// @Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "单品有效期格式有误")
// private String skuValidity;
// @ApiModelProperty(position = 27, required = false, value = "自定义展示[0-默认展示|1-隐藏不可购买]")
// private String skuAppear;
/**
* ---------------------------- 收钱吧商品-代金券属性 ----------------------------
*/
@ApiModelProperty(position = 28, required = true, value = "是否实名[0-否|1-是,表示该商品需要实名关联]", allowableValues = "0,1", example = "1")
@Pattern(regexp = "\\b(0|1)\\b", message = "参数'是否实名'无效")
private String isTrueName;
@ApiModelProperty(position = 29, required = true, value = "适用范围[101-音乐节|102-小型演出(livehouse演出)|103-巡演]", allowableValues = "101,102,103", example = "101")
@Pattern(regexp = "\\b(101|102|103)\\b", message = "参数'适用范围'无效")
private String useScope;
@ApiModelProperty(position = 30, required = true, value = "面值", example = "99.00")
@Digits(integer = 3, fraction = 2, message = "参数'面值'无效")
@DecimalMin(value = "0.01", message = "参数'面值'必须为大于0")
private BigDecimal valFace;
@ApiModelProperty(position = 31, required = true, value = "开始时间[yyyy-MM-dd HH:mm:ss]", example = "2024-01-01 00:00:00")
@Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "开始时间格式有误")
@NotNull(message = "参数'开始时间'不可为空")
private String effectAt;
@ApiModelProperty(position = 32, required = true, value = "结束时间[yyyy-MM-dd HH:mm:ss]", example = "2024-12-31 00:00:00")
@Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "结束时间格式有误")
@NotNull(message = "参数'结束时间'不可为空")
private String expireAt;
}
package com.liquidnet.service.goblin.dto.manage;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.math.BigDecimal;
@ApiModel(value = "GoblinStoreMgtGoodsSqbEditSkuParam", description = "商品管理:编辑收钱吧商品:编辑SKU入参")
@Data
public class GoblinStoreMgtGoodsSqbEditSkuParam implements Serializable {
private static final long serialVersionUID = 8174428924922310702L;
@ApiModelProperty(position = 10, required = true, value = "店铺ID[64]")
@NotBlank(message = "店铺ID不能为空")
private String storeId;
@ApiModelProperty(position = 11, required = true, value = "商品ID[64]")
@NotNull(message = "商品ID不能为空")
private String spuId;
@ApiModelProperty(position = 12, required = true, value = "单品ID[编辑时必传]")
@NotNull(message = "商品SKU_ID不能为空")
private String skuId;
@ApiModelProperty(position = 13, required = true, value = "单品现价[20,2]")
@Digits(integer = 3, fraction = 2, message = "参数'单品现价'无效")
@DecimalMin(value = "0.01", message = "参数'单品现价'必须为大于0")
private BigDecimal price;
@ApiModelProperty(position = 14, required = true, value = "限量[0-无限制|X:限购数量]")
@Min(value = 0, message = "参数'限购数量'不能小于0")
@NotNull(message = "参数'限购数量'不可为空")
private Integer buyLimit;
@ApiModelProperty(position = 15, required = false, value = "总库存")
private Integer stock;
@ApiModelProperty(position = 16, required = false, value = "总库存")
private Integer skuStock;
@ApiModelProperty(position = 17, required = false, value = "加减库存")
private Integer operStock;
/**
* ---------------------------- 收钱吧商品-代金券属性 ----------------------------
*/
@ApiModelProperty(position = 18, required = true, value = "是否实名[0-否|1-是,表示该商品需要实名关联]", example = "1")
@Pattern(regexp = "\\b(0|1)\\b", message = "参数'是否实名'无效")
private String isTrueName;
@ApiModelProperty(position = 19, required = true, value = "适用范围[101-音乐节|102-小型演出(livehouse演出)|103-巡演]")
@Pattern(regexp = "\\b(101|102|103)\\b", message = "参数'适用范围'无效")
private String useScope;
@ApiModelProperty(position = 20, required = true, value = "面值", example = "99.00")
@Digits(integer = 3, fraction = 2, message = "参数'面值'无效")
@DecimalMin(value = "0.01", message = "参数'面值'必须为大于0")
private BigDecimal valFace;
@ApiModelProperty(position = 21, required = true, value = "结束时间[yyyy-MM-dd HH:mm:ss]", example = "2024-01-01 00:00:00")
@Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "开始时间格式有误")
@NotNull(message = "参数'开始时间'不可为空")
private String effectAt;
@ApiModelProperty(position = 22, required = true, value = "结束时间[yyyy-MM-dd HH:mm:ss]", example = "2024-12-31 00:00:00")
@Pattern(regexp = LnsRegex.Valid.DATETIME_FULL, message = "结束时间格式有误")
@NotNull(message = "参数'结束时间'不可为空")
private String expireAt;
public GoblinGoodsSkuInfoVo initEditGoodsSkuInfoVo() {
GoblinGoodsSkuInfoVo goodsSkuInfoVo = GoblinGoodsSkuInfoVo.getNew();
goodsSkuInfoVo.setSkuType(2);
goodsSkuInfoVo.setSkuId(this.getSkuId());
goodsSkuInfoVo.setPrice(this.getPrice());
goodsSkuInfoVo.setPriceMember(this.getPrice());
goodsSkuInfoVo.setBuyLimit(this.getBuyLimit());
goodsSkuInfoVo.setStock(this.getStock());
goodsSkuInfoVo.setSkuStock(this.getSkuStock());
goodsSkuInfoVo.setIsTrueName(Integer.valueOf(this.getIsTrueName()));
goodsSkuInfoVo.setUseScope(Integer.valueOf(this.getUseScope()));
goodsSkuInfoVo.setValFace(this.getValFace());
goodsSkuInfoVo.setEffectAt(DateUtil.Formatter.yyyyMMddHHmmss.parse(this.getEffectAt()));
goodsSkuInfoVo.setExpireAt(DateUtil.Formatter.yyyyMMddHHmmss.parse(this.getExpireAt()));
return goodsSkuInfoVo;
}
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
public class GoblinAdminSqbGoodsVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "商品skuId")
private String skuId;
@ApiModelProperty(value = "单品默认图片的url")
private String skuPic;
@ApiModelProperty(value = "商品描述")
private String productIntroduction;
@ApiModelProperty(value = "商品标题")
private String title;
@ApiModelProperty(value = "单品现价")
private BigDecimal price;
@ApiModelProperty(value = "单品库存")
private Integer skuStock;
}
......@@ -21,16 +21,20 @@ import java.util.List;
public class GoblinFrontGoodDetailVo implements Serializable {
//spu
GoblinGoodsInfoDetailVo goblinGoodsInfoVo;
//sku
List<GoblinGoodsSkuInfoDetailVo> goblinGoodsSkuInfoVolist;
@ApiModelProperty(value = "商铺名称")
String storeName;
@ApiModelProperty(value = "商品可参与券类型")
ArrayList<String> getSpuType;
@ApiModelProperty(value = "条码识别到的SKUID列表", notes = "仅当条码识别时有效")
private List<String> hitSkuIdList;
private static final long serialVersionUID = 1L;
private static final GoblinFrontGoodDetailVo obj = new GoblinFrontGoodDetailVo();
......
......@@ -146,6 +146,11 @@ public class GoblinGoodsInfoVo implements Serializable, Cloneable {
@ApiModelProperty(position = 63, value = "skuList")
private List<GoblinGoodsSkuInfoVo> goblinOrderSkuVos;
@ApiModelProperty(position = 70, value = "收钱吧SPU ID")
private String sqbSpuId;
@ApiModelProperty(position = 71, value = "收钱吧商城编号")
private String mallSn;
public String getMarketType() {
if (marketId == null) {
return "";
......
......@@ -48,6 +48,9 @@ public class GoblinGoodsSkuInfoDetailVo implements Serializable, Cloneable {
@ApiModelProperty(position = 51, value = "是否实名[0-否|1-是,表示该商品需要实名关联]")
private Integer isTrueName;
@ApiModelProperty(position = 52, value = "关联演出ID")
private String performancesId;
private static final GoblinGoodsSkuInfoDetailVo obj = new GoblinGoodsSkuInfoDetailVo();
public static GoblinGoodsSkuInfoDetailVo getNew() {
......
......@@ -78,7 +78,7 @@ public class GoblinOrderSkuVo implements Serializable, Cloneable {
* ---------------------------- 以下为券类商品-代金券属性 ----------------------------
*/
@ApiModelProperty(value = "商品类型[0-常规|1-数字藏品|2-券类商品]")
@ApiModelProperty(value = "商品类型[0-常规|1-数字藏品|2-券类商品] 33-收钱吧商品")
private Integer skuType;
@ApiModelProperty(value = "是否实名[0-否|1-是,表示该商品需要实名关联],这里默认0")
private Integer isTrueName;
......
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "用户端-查询券码-VO")
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 io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "收钱吧商品扩展-VO-redis")
public class GoblinSqbGoodsExtVo implements Serializable {
@ApiModelProperty(value = " goblin_goods.spu_id")
private String spuId;
@ApiModelProperty(value = "关联 goblin_goods_sku.sku_id")
private String skuId;
@ApiModelProperty(value = "所属收钱吧商城编号")
private String mallSn;
@ApiModelProperty(value = "商城签名")
private String signature;
@ApiModelProperty(value = "收钱吧商品ID")
private String sqbSpuId;
@ApiModelProperty(value = "收钱吧规格ID (skuId)")
private String sqbSkuId;
public static GoblinSqbGoodsExtVo of(String spuId,
String skuId,
String mallSn,
String signature,
String sqbSpuId,
String sqbSkuId) {
GoblinSqbGoodsExtVo extVo = new GoblinSqbGoodsExtVo();
extVo.setSpuId(spuId);
extVo.setSkuId(skuId);
extVo.setMallSn(mallSn);
extVo.setSignature(signature);
extVo.setSqbSpuId(sqbSpuId);
extVo.setSqbSkuId(sqbSkuId);
return extVo;
}
}
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 com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.io.Serializable;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class GoblinSqbOrderVo implements Serializable {
private static final long serialVersionUID = 1L;
private String orderId;
private String userId;
private String performancesId;
/**
* 微信 openId(对应支付小程序 appid 下的用户唯一 openid)
* 用于收钱吧创建预支付时的 identity,以及待支付状态下的再次拉起支付。
*/
private String openId;
// 正在spuId
private String spuId;
// 正在skuId
private String skuId;
// 数量
private Integer quantity;
// 金额(分为单位)
private Long amount;
// 订单编号
private String sqbOrderSn;
// 订单密钥
private String sqbOrderSignature;
// 收单号
private String sqbAcquiringSn;
// 收单密钥
private String sqbAcquiringSign;
// 结算项ID
private String sqbCheckoutItemsId;
// 收钱吧-券码编号
private String couponSn;
// 收钱吧-券二维码url
private String couponQrCode;
// 券核销时间?
private String couponExpireTime;
// 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 5-已取消 9-失败
private Integer status;
// 退款备注
private String refundReason;
// 退款单号
private String refundSn;
// 退款单号密码
private String refundSignature;
// 创建时间
private String createdAt;
// 修改时间
private String updatedAt;
// 扩展属性
// 券核销状态 0-未核销 1-已核销
private Integer couponUsedStatus;
}
package com.liquidnet.service.goblin.dto.vo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
public class GoblinSqbPerfGoodsVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商城编号")
private String mallSn;
@ApiModelProperty("商城名称")
private String mallName;
@ApiModelProperty("商城签名")
private String signature;
@ApiModelProperty(value = "商品spuId")
private String spuId;
@ApiModelProperty(value = "商品图片")
private List<String> converImages;
@ApiModelProperty(value = "商品描述")
private String productIntroduction;
@ApiModelProperty(value = "商品标题")
private String title;
@ApiModelProperty(value = "商品规格")
private List<MallProductsQueryData.Sku> skuResults;
@ApiModelProperty("是否已同步")
private Boolean synced;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(value = "商品规格")
public static class Sku {
@ApiModelProperty(value = "库存类型")
private Integer stockType;
@ApiModelProperty(value = "商品skuId")
private String skuId;
@ApiModelProperty(value = "库存值")
private BigDecimal quantity;
@ApiModelProperty(value = "规格标题")
private String skuTitle;
@ApiModelProperty(value = "规格名称")
private String skuName;
@ApiModelProperty(value = "价格")
private Long price;
}
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 演出关联收钱吧商品列表项(本地关联视图)
*/
@Data
public class GoblinSqbPerfLinkedGoodsVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "商品skuId(本地)")
private String skuId;
@ApiModelProperty(value = "商品spuId(本地)")
private String spuId;
@ApiModelProperty(value = "商品spu名称(本地)")
private String spuName;
@ApiModelProperty(value = "商品sku名称(本地)")
private String skuName;
@ApiModelProperty(value = "商品头图(本地)")
private String coverPic;
@ApiModelProperty(value = "商品售价(分,本地)")
private Long price;
@ApiModelProperty(value = "商品库存(本地)")
private Integer stock;
@ApiModelProperty(value = "排序权重")
private Integer sort;
@ApiModelProperty(value = "换购价格")
private BigDecimal settlementPrice;
@ApiModelProperty(value = "状态 0-禁用 1-启用")
private Integer status;
}
package com.liquidnet.service.goblin.dto.vo;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class GoblinSqbPerfListRespVo implements Serializable {
private static final long serialVersionUID = 1L;
/** 演出结束自动下架 0-否 1-是(全局配置) */
private Integer autoOffline;
/** 关联商品列表 */
private List<GoblinSqbPerfLinkedGoodsVo> goodsList;
}
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/**
* 收钱吧-演出关联商品前端展示VO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "GoblinSqbPerformanceGoodsInfoVo", description = "收钱吧-演出关联商品前端展示VO")
public class GoblinSqbPerformanceGoodsInfoVo extends GoblinGoodsInfoVo {
@ApiModelProperty(value = "正常售价")
private BigDecimal price;
@ApiModelProperty(value = "换购价(不设置则按正常价)")
private BigDecimal settlementPrice;
}
......@@ -42,6 +42,11 @@ public interface GoblinFrontService {
*/
GoblinFrontSelectGoodVo getSelectGoods(int page, int pageSize);
/**
* 按演出ID查询已关联商品列表
*/
GoblinFrontSelectGoodVo getPerformanceSelectGoods(String performancesId, int page, int pageSize);
//根据艺人标签和演出id查询商品列表
List<GoblinGoodsInfoVo> getGoodByMusicTagP(String musicTag, String performanceId);
......
package com.liquidnet.service.goblin.service;
import com.liquidnet.common.third.sqb.param.callback.CouponCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.OrderCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.RefundCallbackContent;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.manage.GoblinSqbOrderParam;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo;
/**
* 收钱吧订单服务接口
*/
public interface IGoblinSqbOrderService {
/**
* 创建收钱吧订单
*
* @param userId 用户ID(从 token 获取)
* @param orderParam
* @return 订单创建结果(orderId、acquiringSn、paymentVoucher)
*/
ResponseDto<GoblinSqbOrderCreateVo> createOrder(String userId, GoblinSqbOrderParam orderParam);
/**
* 再次付款(待支付状态下重新拉起微信支付)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 新的 paymentVoucher(复用原 orderId)
*/
ResponseDto<GoblinSqbOrderCreateVo> repay(String userId, String orderId);
/**
* 查询支付状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 支付状态(0-待支付 1-已支付 9-失败)
*/
ResponseDto<Integer> queryPayStatus(String userId, String orderId);
/**
* 支付成功回调(收钱吧主动推送)
*
* @param orderCallbackContent@return "success"
*/
ResponseDto<String> handlePayCallback(OrderCallbackContent orderCallbackContent);
/**
* 退款成功回调(收钱吧主动推送)
*
* @param refundCallbackContent@return "success"
*/
ResponseDto<String> handleRefundCallback(RefundCallbackContent refundCallbackContent);
/**
* 券状态变更回调(收钱吧主动推送)
*
* @param callbackContent@return "success"
*/
ResponseDto<String> handleCouponCallback(CouponCallbackContent callbackContent);
}
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.GoblinSqbOrderDetailVo;
import java.util.List;
public interface IGoblinSqbService {
/**
* 获取核销二维码(券码)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 券码信息(couponSn、couponQrCode、couponExpireTime)
*/
ResponseDto<GoblinSqbCouponVo> queryCoupon(String userId, String orderId);
/**
* 申请退款
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @param reason 退款原因
* @return 退款结果
*/
ResponseDto<Boolean> refund(String userId, String orderId, String reason);
/**
* 主动同步核销状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 同步结果
*/
ResponseDto<Boolean> syncCouponStatus(String userId, String orderId);
/**
* 演出结束自动退款(定时任务调用)
* TODO
* @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);
}
package com.liquidnet.service.goblin.service.manage;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import java.util.List;
/**
* 商品管理:收钱吧商品(独立接口)
*/
public interface IGoblinStoreMgtSqbGoodsService {
/**
* 根据搜索条件拉取商城商品列表
*
* @return 过滤后的商品列表
*/
ResponseDto<List<GoblinSqbPerfGoodsVo>> getProductList();
/**
* 商品管理:SPU添加-收钱吧商品
*/
void sqbGoodsAdd(GoblinGoodsInfoVo goodsInfoVo, List<GoblinGoodsSkuInfoVo> goodsSkuInfoVoList);
/**
* 商品管理:商品编辑:SPU编辑-收钱吧商品
*/
boolean sqbGoodsEditSpu(String uid, GoblinSqbPerfGoodsVo mgtGoodsSqbParam, GoblinGoodsInfoVo goodsInfoVo);
}
package com.liquidnet.client.admin.web.controller.zhengzai.goblin;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.zhengzai.goblin.dto.SqbPerfGoodsBindItemParam;
import com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsService;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinAdminSqbGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfListRespVo;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 演出-收钱吧商品关联管理
*/
@Slf4j
@Controller
@Api(tags = "收钱吧-演出商品关联管理")
@RequestMapping("sqb/performance/goods")
public class SqbPerformanceGoodsController extends BaseController {
private final String prefix = "zhengzai/goblin/sqbGoods";
@Autowired
private ISqbPerformanceGoodsService sqbPerformanceGoodsService;
/**
* 关联商品管理页面(iframe 内嵌)
*/
@GetMapping("page/{performancesId}")
public String sqbGoodsPage(@PathVariable("performancesId") String performancesId, ModelMap mmap) {
mmap.put("performancesId", performancesId);
return prefix + "/index";
}
/**
* 搜索可选商品(skuType=33 的收钱吧商品)
*/
@GetMapping("search")
@ResponseBody
@ApiOperation("搜索收钱吧候选商品")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "keyword", value = "关键词", required = false),
})
public AjaxResult searchGoods(@RequestParam(value = "keyword", required = false) String keyword) {
ResponseDto<List<GoblinAdminSqbGoodsVo>> resp = sqbPerformanceGoodsService.searchGoods(keyword);
if (resp.isSuccess()) {
return AjaxResult.success(resp.getData());
}
return AjaxResult.error(resp.getMessage());
}
/**
* 批量绑定/保存关联商品配置
*/
@PostMapping("bind")
@ResponseBody
@ApiOperation("批量绑定演出与商品(含换购价、演出级自动下架全局配置)")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
@ApiImplicitParam(type = "query", required = false, dataType = "int", name = "autoOffline", value = "自动下架开关 0-否 1-是"),
})
public AjaxResult bind(@RequestParam("performancesId") String performancesId,
@RequestParam(value = "autoOffline", required = false, defaultValue = "0") Integer autoOffline,
@RequestBody List<SqbPerfGoodsBindItemParam> items) {
ResponseDto<Boolean> resp = sqbPerformanceGoodsService.bind(performancesId, items, autoOffline);
if (resp.isSuccess()) {
return AjaxResult.success("保存成功");
}
return AjaxResult.error(resp.getMessage());
}
/**
* 解除关联
*/
@PostMapping("unbind")
@ResponseBody
@ApiOperation("解除演出与商品关联")
@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 AjaxResult unbind(@RequestParam("performancesId") String performancesId,
@RequestParam("skuId") String skuId) {
ResponseDto<Boolean> resp = sqbPerformanceGoodsService.unbind(performancesId, skuId);
if (resp.isSuccess()) {
return AjaxResult.success("已取消关联");
}
return AjaxResult.error(resp.getMessage());
}
/**
* 查询演出已关联商品列表
*/
@GetMapping("list")
@ResponseBody
@ApiOperation("查询演出关联商品列表(含全局配置)")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
})
public AjaxResult list(@RequestParam("performancesId") String performancesId) {
ResponseDto<GoblinSqbPerfListRespVo> resp = sqbPerformanceGoodsService.list(performancesId);
if (resp.isSuccess()) {
return AjaxResult.success(resp.getData());
}
return AjaxResult.error(resp.getMessage());
}
}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('关联推荐商品')"/>
<link rel="stylesheet" th:href="@{/ajax/libs/select2/select2.min.css}"/>
<style>
body { background: #fff; }
.page-wrap { padding: 16px; }
.tip { color: #e8501a; font-size: 12px; margin-left: 8px; }
.section-title { font-weight: bold; margin: 16px 0 8px; }
.auto-offline-bar { margin-bottom: 10px; font-size: 13px; }
.settlement-input { width: 100px; }
.img-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; }
.select2-container { min-width: 380px; }
.add-row { margin-bottom: 16px; display: flex; align-items: center; gap: 10px; }
</style>
</head>
<body class="white-bg">
<div class="page-wrap">
<div class="section-title">
关联推荐商品
<span class="tip">如未关联商品,则APP和小程序前端推荐模块隐藏</span>
</div>
<!-- Select2 选择框 -->
<div class="add-row">
<select id="goodsSelect" style="width:400px;">
<option value="">请输入商品名称搜索...</option>
</select>
<span style="color:#999;font-size:12px;">选中商品后自动添加到列表</span>
</div>
<!-- 已关联商品 -->
<div class="section-title">
已关联商品
<span class="tip">⚠ 设置换购价格,则商品参与演出票换购活动,不设置则按售价售卖,换购价须小于商品售价</span>
</div>
<div class="auto-offline-bar">
<label>
<input type="checkbox" id="globalAutoOffline" onchange="toggleGlobalAutoOffline(this)"/>
演出结束商品自动下架
</label>
</div>
<div class="table-responsive">
<table class="table table-bordered" id="linkedGoodsTable">
<thead>
<tr>
<th>商品ID</th>
<th>商品头图</th>
<th>商品名称</th>
<th>商品售价(元)</th>
<th>换购价(元)</th>
<th>商品库存</th>
<th>操作</th>
</tr>
</thead>
<tbody id="linkedGoodsBody">
<tr id="emptyRow">
<td colspan="7" style="text-align:center;color:#999;">暂无关联商品</td>
</tr>
</tbody>
</table>
</div>
<button type="button" class="btn btn-primary" onclick="saveConfig()">保存配置</button>
</div>
<th:block th:include="include :: footer"/>
<script th:src="@{/ajax/libs/select2/select2.min.js}"></script>
<script th:inline="javascript">
var performancesId = /*[[${performancesId}]]*/ '';
var prefix = ctx + 'sqb/performance/goods';
// 内存中维护已关联商品列表
var linkedGoods = [];
$(function () {
initSelect2();
loadLinkedGoods();
});
// 初始化 Select2(AJAX 搜索)
function initSelect2() {
$('#goodsSelect').select2({
allowClear: true,
placeholder: '请输入商品名称搜索...',
minimumInputLength: 0,
language: { inputTooShort: function () { return '请输入关键词'; } },
ajax: {
url: prefix + '/search',
dataType: 'json',
delay: 300,
data: function (params) { return { keyword: params.term || '' }; },
processResults: function (resp) {
var list = (resp && resp.data) ? resp.data : [];
return { results: list.map(function (item) {
return { id: item.skuId, text: (item.title || item.spuName || item.skuName), _raw: item };
})};
}
}
});
// 选中即添加
$('#goodsSelect').on('select2:select', function (e) {
var raw = e.params.data._raw;
if (raw) { addLinked(raw); }
// 清空选择框
$(this).val(null).trigger('change');
});
}
// 加载已关联商品
function loadLinkedGoods() {
$.get(prefix + '/list', { performancesId: performancesId }, function (resp) {
if (resp.code === 0 && resp.data) {
// 设置全局开关状态
var globalAuto = resp.data.autoOffline === 1;
$('#globalAutoOffline').prop('checked', globalAuto);
var list = resp.data.goodsList || [];
linkedGoods = list.map(function (item) {
return {
skuId: item.skuId, spuId: item.spuId,
spuName: item.spuName || item.skuName, skuName: item.skuName,
coverPic: item.coverPic || '', price: item.price, stock: item.stock,
settlementPrice: (item.settlementPrice != null ? item.settlementPrice : ''),
persisted: true
};
});
renderLinkedGoods();
}
});
}
function renderLinkedGoods() {
var tbody = document.getElementById('linkedGoodsBody');
if (linkedGoods.length === 0) {
tbody.innerHTML = '<tr id="emptyRow"><td colspan="7" style="text-align:center;color:#999;">暂无关联商品</td></tr>';
return;
}
var html = '';
linkedGoods.forEach(function (item, idx) {
var priceYuan = item.price ? (item.price / 100).toFixed(2) : '-';
var settlementVal = item.settlementPrice || '';
var imgHtml = item.coverPic
? '<img src="' + item.coverPic + '" class="img-thumb"/>'
: '<span style="color:#ccc;font-size:12px;">无图</span>';
html += '<tr data-idx="' + idx + '">';
html += '<td><small>' + item.skuId + '</small></td>';
html += '<td>' + imgHtml + '</td>';
html += '<td>' + (item.spuName || item.skuName || '-') + '</td>';
html += '<td>' + priceYuan + '</td>';
html += '<td><input type="number" class="form-control settlement-input" value="' + settlementVal
+ '" placeholder="不填按售价" onchange="updateSettlement(' + idx + ', this.value)" min="0" step="0.01"/></td>';
html += '<td>' + (item.stock != null ? item.stock : '-') + '</td>';
html += '<td><button type="button" class="btn btn-xs btn-danger" onclick="removeLinked(' + idx + ')">取消关联</button></td>';
html += '</tr>';
});
tbody.innerHTML = html;
}
function addLinked(item) {
for (var i = 0; i < linkedGoods.length; i++) {
if (linkedGoods[i].skuId === item.skuId) {
layer.msg('该商品已在关联列表中');
return;
}
}
linkedGoods.push({
skuId: item.skuId, spuId: item.spuId || '',
spuName: item.title || item.spuName || item.skuName,
skuName: item.title || item.skuName || item.spuName,
coverPic: item.skuPic || item.coverPic || '',
price: item.price,
stock: (item.skuStock != null ? item.skuStock : item.stock),
settlementPrice: null, autoOffline: 0, persisted: false
});
renderLinkedGoods();
layer.msg('已添加,可配置换购价后保存');
}
function updateSettlement(idx, val) {
linkedGoods[idx].settlementPrice = val ? parseFloat(val) : null;
}
function updateAutoOffline(idx, checkbox) {
linkedGoods[idx].autoOffline = checkbox.checked ? 1 : 0;
}
function toggleGlobalAutoOffline(checkbox) {
// 全局开关,无需在 linkedGoods 中逐个维护 autoOffline
// 只需在保存时通过 checkbox.checked 获取
}
function removeLinked(idx) {
var item = linkedGoods[idx];
if (!item) {
return;
}
// 未入库(本次新增、未保存)直接从页面移除
if (!item.persisted) {
linkedGoods.splice(idx, 1);
renderLinkedGoods();
layer.msg('已从待保存列表移除');
return;
}
$.modal.confirm('确认要取消关联该商品吗?', function () {
$.post(prefix + '/unbind', { performancesId: performancesId, skuId: item.skuId }, function (resp) {
if (resp.code === 0) {
linkedGoods.splice(idx, 1);
renderLinkedGoods();
layer.msg('已取消关联');
} else {
layer.msg(resp.msg || resp.message || '操作失败', { icon: 2 });
}
});
});
}
function saveConfig() {
var autoOffline = $('#globalAutoOffline').is(':checked') ? 1 : 0;
var items = linkedGoods.map(function (item) {
return {
skuId: item.skuId, sort: 0,
settlementPrice: (item.settlementPrice === '' || item.settlementPrice == null) ? null : item.settlementPrice
};
});
$.ajax({
url: prefix + '/bind?performancesId=' + encodeURIComponent(performancesId) + '&autoOffline=' + autoOffline,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(items),
success: function (resp) {
if (resp.code === 0) {
layer.msg('保存成功');
loadLinkedGoods();
} else {
layer.msg(resp.msg || resp.message || '保存失败', { icon: 2 });
}
},
error: function () {
layer.msg('请求失败,请重试', { icon: 2 });
}
});
}
</script>
</body>
</html>
......@@ -45,6 +45,8 @@
</li>
<li id="li-tab-12"><a data-toggle="tab" href="#tab-12" aria-expanded="false" onclick="artistLineupInfo()">演出阵容</a>
</li>
<li id="li-tab-13"><a data-toggle="tab" href="#tab-13" aria-expanded="false" onclick="sqbGoodsInfo()">关联推荐商品</a>
</li>
</ul>
<div class="tab-content">
<div id="tab-1" class="tab-pane">
......@@ -373,6 +375,13 @@
height=800px frameborder=0></iframe>
</div>
</div>
<div id="tab-13" class="tab-pane">
<div class="panel-body">
<iframe id="sqb_goods_iframe" name="sqb_goods_iframe" marginwidth=0 marginheight=0
width=100%
height=800px frameborder=0></iframe>
</div>
</div>
</div>
</div>
</div>
......@@ -578,6 +587,11 @@
document.getElementById("artist_lineup_iframe").src = "../artistLineup/" + '[[${kylinPerformanceMisVo.performancesId}]]'.replaceAll("\"", "");
}
//关联推荐商品
function sqbGoodsInfo() {
document.getElementById("sqb_goods_iframe").src = ctx + "sqb/performance/goods/page/" + '[[${kylinPerformanceMisVo.performancesId}]]'.replaceAll("\"", "");
}
$("#tab-nav-1").bind("click", function () {
$("#tab_iframe_1").attr("src", prefix + "/performanceStatic/" + '[[${kylinPerformanceMisVo.performancesId}]]'.replaceAll("\"", ""));
});
......
package com.liquidnet.client.admin.zhengzai.goblin.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 演出-商品关联绑定请求 DTO
*/
@Data
public class SqbPerfGoodsBindItemParam implements Serializable {
private static final long serialVersionUID = 1L;
/** SKU ID */
private String skuId;
/** 排序权重 */
private Integer sort;
/** 换购价格(null 表示不设置换购价,按原价售卖) */
private BigDecimal settlementPrice;
}
package com.liquidnet.client.admin.zhengzai.goblin.service;
import com.liquidnet.client.admin.zhengzai.goblin.dto.SqbPerfGoodsBindItemParam;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinAdminSqbGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfListRespVo;
import java.util.List;
/**
* 演出-收钱吧商品关联 服务接口
*/
public interface ISqbPerformanceGoodsService {
/**
* 关联演出与商品(批量,支持换购价/演出级自动下架全局配置)
*
* @param performancesId 演出ID
* @param items 绑定项列表(仅含 SKU ID、排序、换购价)
* @param autoOffline 演出结束自动下架 0-否 1-是(全局配置)
* @return 操作结果
*/
ResponseDto<Boolean> bind(String performancesId, List<SqbPerfGoodsBindItemParam> items, Integer autoOffline);
/**
* 解除演出与商品关联
*
* @param performancesId 演出ID
* @param skuId SKU ID
* @return 操作结果
*/
ResponseDto<Boolean> unbind(String performancesId, String skuId);
/**
* 查询演出关联的收钱吧商品列表(包含全局配置)
*
* @param performancesId 演出ID
* @return 商品列表及配置
*/
ResponseDto<GoblinSqbPerfListRespVo> list(String performancesId);
/**
* 搜索收钱吧商品(skuType=33,供关联候选)
*
* @param keyword 商品名称关键词
* @return 商品列表
*/
ResponseDto<List<GoblinAdminSqbGoodsVo>> searchGoods(String keyword);
}
package com.liquidnet.client.admin.zhengzai.goblin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.liquidnet.client.admin.zhengzai.goblin.dto.SqbPerfGoodsBindItemParam;
import com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsService;
import com.liquidnet.client.admin.zhengzai.goblin.utils.GoblinRedisUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinAdminSqbGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfLinkedGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfListRespVo;
import com.liquidnet.service.goblin.entity.GoblinGoodsSku;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceConfig;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import com.liquidnet.service.goblin.mapper.GoblinGoodsMapper;
import com.liquidnet.service.goblin.mapper.GoblinGoodsSkuMapper;
import com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceConfigMapper;
import com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 演出-收钱吧商品关联 服务实现
* 直连 MySQL,写入/删除时同步删除 Redis 缓存
*/
@Slf4j
@Service
public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsService {
/** skuType=33 代表收钱吧商品 */
private static final int SQB_SKU_TYPE = 33;
@Autowired
private GoblinSqbPerformanceGoodsMapper performanceGoodsMapper;
@Autowired
private GoblinSqbPerformanceConfigMapper performanceConfigMapper;
@Autowired
private GoblinGoodsMapper goblinGoodsMapper;
@Autowired
private GoblinGoodsSkuMapper goblinGoodsSkuMapper;
@Autowired
private GoblinRedisUtils goblinRedisUtils;
@Override
public ResponseDto<Boolean> bind(String performancesId, List<SqbPerfGoodsBindItemParam> items, Integer autoOffline) {
if (!StringUtils.hasText(performancesId)) {
return ResponseDto.failure("演出ID不能为空");
}
if (items == null || items.isEmpty()) {
return ResponseDto.failure("商品列表不能为空");
}
LocalDateTime nowTime = LocalDateTime.now();
String nowStr = nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
try {
// 1. 保存全局配置
GoblinSqbPerformanceConfig config = performanceConfigMapper.selectById(performancesId);
if (config == null) {
config = new GoblinSqbPerformanceConfig();
config.setPerformancesId(performancesId);
config.setAutoOffline(autoOffline != null ? autoOffline : 0);
config.setCreatedAt(nowStr);
config.setUpdatedAt(nowStr);
performanceConfigMapper.insert(config);
} else {
config.setAutoOffline(autoOffline != null ? autoOffline : 0);
config.setUpdatedAt(nowStr);
performanceConfigMapper.updateById(config);
}
// 2. 批量处理商品绑定
for (SqbPerfGoodsBindItemParam item : items) {
String skuId = item.getSkuId();
if (!StringUtils.hasText(skuId)) {
continue;
}
LambdaQueryWrapper<GoblinGoodsSku> skuQuery = new LambdaQueryWrapper<>();
skuQuery.eq(GoblinGoodsSku::getSkuId, skuId).last("LIMIT 1");
GoblinGoodsSku sku = goblinGoodsSkuMapper.selectOne(skuQuery);
if (sku == null) {
log.warn("[演出商品关联] SKU不存在,skuId={}", skuId);
continue;
}
int sortVal = item.getSort() != null ? item.getSort() : 0;
LambdaQueryWrapper<GoblinSqbPerformanceGoods> existQuery = new LambdaQueryWrapper<>();
existQuery.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getSkuId, skuId)
.last("LIMIT 1");
GoblinSqbPerformanceGoods existing = performanceGoodsMapper.selectOne(existQuery);
if (existing != null) {
LambdaUpdateWrapper<GoblinSqbPerformanceGoods> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getSkuId, skuId)
.set(GoblinSqbPerformanceGoods::getSort, sortVal)
.set(GoblinSqbPerformanceGoods::getStatus, 1)
.set(GoblinSqbPerformanceGoods::getSettlementPrice, item.getSettlementPrice())
.set(GoblinSqbPerformanceGoods::getUpdatedAt, nowStr);
performanceGoodsMapper.update(null, updateWrapper);
} else {
GoblinSqbPerformanceGoods entity = new GoblinSqbPerformanceGoods();
entity.setPerformancesId(performancesId);
entity.setSpuId(sku.getSpuId());
entity.setSkuId(skuId);
entity.setSort(sortVal);
entity.setStatus(1);
entity.setSettlementPrice(item.getSettlementPrice());
entity.setCreatedAt(nowStr);
entity.setUpdatedAt(nowStr);
performanceGoodsMapper.insert(entity);
}
}
goblinRedisUtils.delPerformanceGoodsCache(performancesId);
return ResponseDto.success(Boolean.TRUE);
} catch (Exception e) {
log.error("[演出商品关联] bind 异常,performancesId={}", performancesId, e);
return ResponseDto.failure("关联操作失败:" + e.getMessage());
}
}
@Override
public ResponseDto<Boolean> unbind(String performancesId, String skuId) {
if (!StringUtils.hasText(performancesId)) {
return ResponseDto.failure("演出ID不能为空");
}
if (!StringUtils.hasText(skuId)) {
return ResponseDto.failure("SKU ID不能为空");
}
try {
LambdaUpdateWrapper<GoblinSqbPerformanceGoods> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getSkuId, skuId)
.set(GoblinSqbPerformanceGoods::getStatus, 0)
.set(GoblinSqbPerformanceGoods::getUpdatedAt,
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
int rows = performanceGoodsMapper.update(null, updateWrapper);
if (rows == 0) {
return ResponseDto.failure("关联记录不存在");
}
goblinRedisUtils.delPerformanceGoodsCache(performancesId);
return ResponseDto.success(Boolean.TRUE);
} catch (Exception e) {
log.error("[演出商品关联] unbind 异常,performancesId={}, skuId={}", performancesId, skuId, e);
return ResponseDto.failure("解除关联失败:" + e.getMessage());
}
}
@Override
public ResponseDto<GoblinSqbPerfListRespVo> list(String performancesId) {
if (!StringUtils.hasText(performancesId)) {
return ResponseDto.failure("演出ID不能为空");
}
try {
GoblinSqbPerfListRespVo resp = new GoblinSqbPerfListRespVo();
// 1. 查询全局配置
GoblinSqbPerformanceConfig config = performanceConfigMapper.selectById(performancesId);
resp.setAutoOffline(config != null ? config.getAutoOffline() : 0);
// 2. 查询关联商品
LambdaQueryWrapper<GoblinSqbPerformanceGoods> query = new LambdaQueryWrapper<>();
query.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getStatus, 1)
.orderByAsc(GoblinSqbPerformanceGoods::getSort);
List<GoblinSqbPerformanceGoods> relations = performanceGoodsMapper.selectList(query);
if (relations == null || relations.isEmpty()) {
resp.setGoodsList(new ArrayList<>());
return ResponseDto.success(resp);
}
List<String> skuIds = relations.stream()
.map(GoblinSqbPerformanceGoods::getSkuId).collect(Collectors.toList());
LambdaQueryWrapper<GoblinGoodsSku> skuQuery = new LambdaQueryWrapper<>();
skuQuery.in(GoblinGoodsSku::getSkuId, skuIds);
Map<String, GoblinGoodsSku> skuMap = goblinGoodsSkuMapper.selectList(skuQuery)
.stream().collect(Collectors.toMap(GoblinGoodsSku::getSkuId, Function.identity(), (a, b) -> a));
List<GoblinSqbPerfLinkedGoodsVo> goodsList = new ArrayList<>();
for (GoblinSqbPerformanceGoods rel : relations) {
GoblinSqbPerfLinkedGoodsVo vo = new GoblinSqbPerfLinkedGoodsVo();
vo.setSkuId(rel.getSkuId());
vo.setSpuId(rel.getSpuId());
vo.setSort(rel.getSort());
vo.setSettlementPrice(rel.getSettlementPrice());
vo.setStatus(rel.getStatus());
GoblinGoodsSku sku = skuMap.get(rel.getSkuId());
if (sku != null) {
vo.setSkuName(sku.getName());
vo.setSpuName(sku.getName());
vo.setCoverPic(sku.getSkuPic());
vo.setPrice(sku.getPrice() != null ? sku.getPrice().longValue() : null);
vo.setStock(sku.getSkuStock());
}
goodsList.add(vo);
}
resp.setGoodsList(goodsList);
return ResponseDto.success(resp);
} catch (Exception e) {
log.error("[演出商品关联] list 异常,performancesId={}", performancesId, e);
return ResponseDto.failure("查询失败:" + e.getMessage());
}
}
@Override
public ResponseDto<List<GoblinAdminSqbGoodsVo>> searchGoods(String keyword) {
try {
// 收钱吧商品的所有核心展示信息均已同步到 SKU(Type=33)表中,直接一次查询避免二次查 SPU
LambdaQueryWrapper<GoblinGoodsSku> skuQuery = new LambdaQueryWrapper<>();
skuQuery.eq(GoblinGoodsSku::getSkuType, SQB_SKU_TYPE);
if (StringUtils.hasText(keyword)) {
skuQuery.like(GoblinGoodsSku::getName, keyword);
}
skuQuery.last("LIMIT 50");
List<GoblinGoodsSku> skuList = goblinGoodsSkuMapper.selectList(skuQuery);
if (skuList.isEmpty()) {
return ResponseDto.success(new ArrayList<>());
}
List<GoblinAdminSqbGoodsVo> result = new ArrayList<>();
for (GoblinGoodsSku sku : skuList) {
GoblinAdminSqbGoodsVo vo = new GoblinAdminSqbGoodsVo();
vo.setSkuId(sku.getSkuId());
vo.setTitle(sku.getName());
vo.setSkuPic(sku.getSkuPic());
vo.setPrice(sku.getPrice());
vo.setSkuStock(sku.getSkuStock());
result.add(vo);
}
return ResponseDto.success(result);
} catch (Exception e) {
log.error("[演出商品关联] searchGoods 异常,keyword={}", keyword, e);
return ResponseDto.failure("搜索失败:" + e.getMessage());
}
}
}
......@@ -249,4 +249,14 @@ public class GoblinRedisUtils {
rk = rk.concat(skuId);
return (int) redisDataSourceUtil.getRedisGoblinUtil().incr(rk, stock);
}
/**
* 清除演出关联收钱吧商品缓存
*
* @param performancesId 演出ID
*/
public void delPerformanceGoodsCache(String performancesId) {
String redisKey = GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId);
redisDataSourceUtil.getRedisGoblinUtil().del(redisKey);
}
}
......@@ -56,4 +56,19 @@ public class RedisKeyExpireConst {
// 演出关联阵容缓存过期时间
public static final long PERFORMANCES_ARTISTS_EXPIRE = 30 * 24 * 60 * 60;
/**
* 演出关联收钱吧商品缓存过期时间 (5分钟)
*/
public static final long SQB_PERFORMANCE_GOODS_EXPIRE = 5 * 60;
/**
* 收钱吧订单详情过期时间 (2小时)
*/
public static final long SQB_ORDER_EXPIRE = 2 * 60 * 60;
/**
* 收钱吧下单防重锁过期时间 (10秒)
*/
public static final long SQB_ORDER_LOCK_EXPIRE = 10;
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liquidnet-common-third-sqb</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-base</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.liquidnet.common.third.sqb.biz;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.liquidnet.common.third.sqb.config.SqbConfig;
import com.liquidnet.common.third.sqb.param.callback.CallbackParams;
import com.liquidnet.common.third.sqb.param.request.*;
import com.liquidnet.common.third.sqb.param.response.*;
import com.liquidnet.common.third.sqb.param.response.data.*;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.MD5Utils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 收钱吧对接公共工具类
*
* @author liquidnet
*/
@Slf4j
@Component
public class SqbBiz {
@Getter
private final SqbConfig sqbConfig;
private final CommonRequest.Seller cachedSeller;
public SqbBiz(SqbConfig properties) {
this.sqbConfig = properties;
if (this.sqbConfig == null) {
throw new IllegalArgumentException("找不到收钱吧应用配置");
}
// 商户信息来自配置,整个生命周期内不变,提前构建并缓存
this.cachedSeller = new CommonRequest.Seller();
this.cachedSeller.setMerchantId(properties.getMerchantId());
this.cachedSeller.setMerchantUserId(properties.getMerchantUserId());
this.cachedSeller.setRole(properties.getRole());
}
private static final ObjectMapper objectMapper;
private static final RestTemplate restTemplate;
static {
// 1. 初始化 RestTemplate 并强制设置 String 转换器为 UTF-8
restTemplate = new RestTemplate();
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 2. 初始化紧凑型 ObjectMapper (签名专用:无空格、无换行、不含空字段)
objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, false);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
/**
* 创建结算明细
*
* @param request 创建结算明细请求参数
* @return 收钱吧响应结果;调用失败时返回 null
*/
public SettlementCreateData createSettlement(SettlementCreateRequest request) {
return executeShouQianBaRequest("/optimus/core/order/createCheckoutItems", "创建结算明细", request, SettlementCreateResponse.class);
}
/**
* 创建结算明细
*
* @param mallSn 商城ID
* @param signature 商城密钥
* @param userId 正在用户ID
* @param checkoutItems 商品
* @return
*/
public SettlementCreateData createSettlement(String mallSn, String signature, String userId, List<SettlementCreateRequest.CheckoutItem> checkoutItems) {
SettlementCreateRequest request = new SettlementCreateRequest();
request.setAppid(sqbConfig.getAppId());
request.setMallID(buildMall(mallSn, signature));
request.setSeller(cachedSeller);
request.setBuyer(buildBuyer(userId));
request.setCheckoutItems(checkoutItems);
return createSettlement(request);
}
/**
* 创建订单
*/
public OrderCreateData createOrder(OrderCreateRequest request) {
return executeShouQianBaRequest("/optimus/core/order/placeOrder", "创建订单", request, OrderCreateResponse.class);
}
/**
* 创建订单
*
* @param mallSn 商城ID
* @param signature 商城密钥
* @param checkoutItemsId 创建结算明细 返回的 结算项ID
* @param userId 正在用户ID
* @param requestId 请求ID
* @param subject 标题
* @return
*/
public OrderCreateData createOrder(String mallSn, String signature, String checkoutItemsId, String userId, String requestId, String subject) {
OrderCreateRequest orderCreateRequest = new OrderCreateRequest();
orderCreateRequest.setAppid(sqbConfig.getAppId());
orderCreateRequest.setMallID(buildMall(mallSn, signature));
orderCreateRequest.setSeller(cachedSeller);
orderCreateRequest.setCheckoutItemsId(checkoutItemsId);
orderCreateRequest.setBuyer(buildBuyer(userId));
orderCreateRequest.setRequestId(requestId);
orderCreateRequest.setSubject(subject);
return createOrder(orderCreateRequest);
}
/**
* 创建收单
*/
public AcquiringCreateData createAcquiring(AcquiringCreateRequest request) {
return executeShouQianBaRequest("/optimus/core/order/createAcquiring", "创建收单环节", request, AcquiringCreateResponse.class);
}
/**
* 查询收银台
*/
public CashierQueryData queryCashier(CashierQueryRequest request) {
return executeShouQianBaRequest("/optimus/core/cashier/queryCashierPage", "查询收银台", request, CashierQueryResponse.class);
}
/**
* 查询收银台
*
* @param acquiringSn 收单号
* @param acquiringSignature 收单密钥
* @param userId 正在用户ID
* @return
*/
public CashierQueryData queryCashier(String acquiringSn, String acquiringSignature, String userId) {
CashierQueryRequest.PaymentEnv paymentEnv = new CashierQueryRequest.PaymentEnv();
paymentEnv.setClient("wechat");
CashierQueryRequest cashierQueryRequest = new CashierQueryRequest();
cashierQueryRequest.setAppid(sqbConfig.getAppId());
cashierQueryRequest.setSeller(cachedSeller);
cashierQueryRequest.setPaymentMode(4);
cashierQueryRequest.setPaymentEnv(paymentEnv);
cashierQueryRequest.setPayer(new CommonRequest.Payer(userId));
cashierQueryRequest.setAcquiringInfo(new CommonRequest.Acquiring(acquiringSn, acquiringSignature));
return queryCashier(cashierQueryRequest);
}
/**
* 创建微信预支付订单
*/
public CreateWechatPrepayOrderData createWechatPrepayOrder(CreateWechatPrepayOrderRequest request) {
return executeShouQianBaRequest("/optimus/core/cashier/proxyPreCreatedPay", "创建微信预支付订单", request, CreateWechatPrepayOrderResponse.class);
}
/**
* 创建微信预支付订单
*
* @param acquiringSn 收单号
* @param signature 收单签名
* @param userId 正在用户ID
* @param amount 支付金额
* @param requestSn 支付请求号
* @param payTool 支付工具代码
* @param channelExt 通道扩展参数 {"sub_appid":"wx36e68952a6"}
* @param selectedSignature 支付工具签名
* @param seq 序列号 上一步seq(查询收银台)
* @return
*/
public CreateWechatPrepayOrderData createWechatPrepayOrder(String acquiringSn,
String signature,
String userId,
String identity,
String amount,
String requestSn,
CashierQueryData.PayTool payTool,
Map<String, Object> channelExt,
String selectedSignature,
String seq) {
CreateWechatPrepayOrderRequest orderRequest = new CreateWechatPrepayOrderRequest();
orderRequest.setAppid(sqbConfig.getAppId());
orderRequest.setSeller(cachedSeller);
orderRequest.setAcquiringSn(acquiringSn);
orderRequest.setSignature(signature);
orderRequest.setUsingPayTools(buildUsingPayTools(requestSn, payTool, channelExt, amount, identity));
orderRequest.setSelectedSignature(selectedSignature);
orderRequest.setSeq(seq);
return createWechatPrepayOrder(orderRequest);
}
/**
* 查询券码
*/
public CouponQueryData queryCoupon(CouponQueryRequest request) {
return executeShouQianBaRequest("/optimus/core/voucher/queryOrderVoucherList", "查询优惠券", request, CouponQueryResponse.class);
}
/**
* 查询券码
*
* @param sn 订单ID
* @param signature 订单密码
* @return
*/
public CouponQueryData queryCoupon(String sn, String signature) {
CouponQueryRequest couponQueryRequest = new CouponQueryRequest();
couponQueryRequest.setAppid(sqbConfig.getAppId());
couponQueryRequest.setSeller(cachedSeller);
couponQueryRequest.setOrderID(new CommonRequest.OrderInfo(sn, signature));
return queryCoupon(couponQueryRequest);
}
/**
* 券码状态同步
*/
public boolean syncCouponStatus(CouponStatusSyncRequest request) {
CouponStatusSyncResponse response = executeRequestAndGetResponse("/optimus/core/voucher/modifyOrderVoucherList", "同步优惠券状态", request, CouponStatusSyncResponse.class);
return response != null && Boolean.TRUE.equals(response.getSuccess());
}
/**
* 券码状态同步
*
* @param redeemMerchantId 用户ID(自定义,核销用户id)
* @param voucherNos 券号
* @param redeemExternalOrderSn 外部单号(${AppCode}+id,AppCode需要分配)
* @param clientSn 核销终端号(自定义)
* @param status 券使用状态(0未核销,1已核销)
* @return
*/
public boolean syncCouponStatus(String redeemMerchantId, List<String> voucherNos, String redeemExternalOrderSn, String clientSn, Byte status) {
CouponStatusSyncRequest couponStatusSyncRequest = new CouponStatusSyncRequest();
couponStatusSyncRequest.setAppid(sqbConfig.getAppId());
couponStatusSyncRequest.setVoucherNos(voucherNos);
couponStatusSyncRequest.setRedeemSource("EXTERN");
couponStatusSyncRequest.setRedeemExternalOrderSn(redeemExternalOrderSn);
couponStatusSyncRequest.setRedeemMerchantId(redeemMerchantId);
couponStatusSyncRequest.setClientSn(clientSn);
couponStatusSyncRequest.setStatus(status);
return syncCouponStatus(couponStatusSyncRequest);
}
/**
* 券退款
*/
public CouponRefundData refundCoupon(CouponRefundRequest request) {
return executeShouQianBaRequest("/optimus/core/aftersale/applyRefund", "退款优惠券", request, CouponRefundResponse.class);
}
/**
* @param sn 单号(前置操作后拿到)
* @param signature 密码(前置操作后拿到)
* @param applyAmount 金额
* @param type 退款类型(1商品 2金额)
* @param item
* @param refundReason 退款原因
* @param requestId 退款请求id(${AppCode}+id)
* @return
*/
public CouponRefundData refundCoupon(String sn,
String signature,
Long applyAmount,
Byte type,
List<CouponRefundRequest.RefundItem> item,
String refundReason,
String requestId) {
CouponRefundRequest couponRefundRequest = new CouponRefundRequest();
couponRefundRequest.setAppid(sqbConfig.getAppId());
couponRefundRequest.setSeller(cachedSeller);
couponRefundRequest.setOrderID(new CommonRequest.OrderInfo(sn, signature));
couponRefundRequest.setRefundInfo(new CouponRefundRequest.RefundInfo(applyAmount, type, item, refundReason));
couponRefundRequest.setRequestId(requestId);
couponRefundRequest.setRequestSource("EXTERN");
return refundCoupon(couponRefundRequest);
}
/**
* 商城列表接口
*/
public List<MallListQueryData> queryMallList(MallListQueryRequest request) {
return executeShouQianBaRequest("/optimus/core/open/mall/queryMallList", "查询门店列表", request, MallListQueryResponse.class);
}
/**
* 商城列表接口
*
* @param endCursor 结束游标 上一页的结束游标(首次查询传 null)
* @param count 查询数量 每页返回的最大订单数
* @return
*/
public List<MallListQueryData> queryMallList(String endCursor, Integer count) {
MallListQueryRequest.Filter filter = new MallListQueryRequest.Filter();
filter.setSeller(cachedSeller);
MallListQueryRequest.Cursor cursor = new MallListQueryRequest.Cursor();
cursor.setCursorField("id");
cursor.setEndCursor(endCursor);
cursor.setCount(count);
MallListQueryRequest.Sort sort = new MallListQueryRequest.Sort();
sort.setSortField("id");
sort.setSort("DESC");
MallListQueryRequest mallListQueryRequest = new MallListQueryRequest();
mallListQueryRequest.setAppid(sqbConfig.getAppId());
mallListQueryRequest.setFilter(filter);
mallListQueryRequest.setCursor(cursor);
mallListQueryRequest.setSort(sort);
return queryMallList(mallListQueryRequest);
}
/**
* 商城商品接口
*/
public List<MallProductsQueryData> queryMallProducts(MallProductsQueryRequest request) {
return executeShouQianBaRequest("/optimus/core/open/mall/queryMallProducts", "查询门店商品", request, MallProductsQueryResponse.class);
}
/**
* 商城商品接口
*
* @param mallSn 商城ID
* @param signature 商城密码
* @return
*/
public List<MallProductsQueryData> queryMallProducts(String mallSn, String signature) {
MallProductsQueryRequest request = new MallProductsQueryRequest();
request.setAppid(sqbConfig.getAppId());
request.setSeller(cachedSeller);
request.setMallID(new CommonRequest.Mall(mallSn, signature));
return queryMallProducts(request);
}
/**
* 发送HTTP请求并返回Response对象
*/
private <T extends BaseResponse<?>> T executeRequestAndGetResponse(
String apiPath, String businessName, Object request, Class<T> responseClass) {
final String url = sqbConfig.getBaseApi() + apiPath;
log.info("[收钱吧] {}, 请求URL: {}, 请求参数: {}", businessName, url, JsonUtils.toJson(request));
try {
String requestBody = objectMapper.writeValueAsString(request);
// log.info("request body: {}", requestBody);
// 构建请求头(添加签名参数)
final String sign = getSign(requestBody);
log.info("request header: {}", sign);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", sign);
headers.add("Content-Type", "application/json");
headers.add("X-env-flag", "41508");
// 4. 发送请求
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
String responseStr = responseEntity.getBody();
log.info("[收钱吧] {}, 响应体: {}", businessName, responseStr);
// 转换响应报文
T response = objectMapper.readValue(responseStr, responseClass);
if (response != null && !Boolean.TRUE.equals(response.getSuccess())) {
log.error("[收钱吧] {}业务失败, code: {}, msg: {}", businessName,
response.getCode(), response.getMsg());
}
return response;
} catch (Exception e) {
log.error("[收钱吧] {}发生异常, 请求参数: {}", businessName, request, e);
return null;
}
}
/**
* 封装收钱吧接口调用,提取公共逻辑并发起请求,默认返回Data
*/
private <D, T extends BaseResponse<D>> D executeShouQianBaRequest(
String apiPath, String businessName, Object request, Class<T> responseClass) {
T response = executeRequestAndGetResponse(apiPath, businessName, request, responseClass);
if (response != null && Boolean.TRUE.equals(response.getSuccess())) {
return response.getData();
}
return null;
}
private String getSign(String bodyJsonStr) {
try {
// 签名算法: sign = MD5( CONCAT( body + appkey ) )
String rawStr = bodyJsonStr + sqbConfig.getAppKey();
String sign = MD5Utils.md5(rawStr);
// 签名首部: Authorization: appid + " " + sign
return sqbConfig.getAppId() + " " + sign;
} catch (Exception e) {
log.error("[收钱吧] 生成签名发生异常", e);
return "";
}
}
/**
* 收钱吧回调推送验签
*/
public boolean verifySignature(CallbackParams callbackParams) {
// 签名原串 = eventId + timestamp + nonce + content
String plaintext = callbackParams.getEventId() + callbackParams.getTimestamp() + callbackParams.getNonce() + callbackParams.getContent();
return verifySignatureSHA256WithRSA(plaintext, callbackParams.getSignature(), sqbConfig.getPublicKey());
}
private CommonRequest.Mall buildMall(String mallSn, String signature) {
CommonRequest.Mall mall = new CommonRequest.Mall();
mall.setMallSn(mallSn);
mall.setSignature(signature);
return mall;
}
private CommonRequest.Buyer buildBuyer(String userId) {
CommonRequest.Buyer buyer = new CommonRequest.Buyer();
buyer.setBuyerId(userId);
return buyer;
}
private List<CreateWechatPrepayOrderRequest.UsingPayTool> buildUsingPayTools(String requestSn,
CashierQueryData.PayTool payTool,
Map<String, Object> channelExt,
String amount,
String identity) {
CreateWechatPrepayOrderRequest.UsingPayTool tool = new CreateWechatPrepayOrderRequest.UsingPayTool();
tool.setId(payTool.getId());
tool.setPayTool(Integer.valueOf(payTool.getCode()));
tool.setPayMode(4); // TODO 需要确认
tool.setAmount(amount);
tool.setIdentity(identity);
tool.setAmountComposition(payTool.getAmountComposition());
tool.setRequestSn(requestSn);
tool.setChannelExt(channelExt);
return Collections.singletonList(tool);
}
private boolean verifySignatureSHA256WithRSA(String plaintext, String inputSignature, String pubKey) {
return verifySignatureSHA256WithRSA(plaintext.getBytes(StandardCharsets.UTF_8), inputSignature, pubKey);
}
private boolean verifySignatureSHA256WithRSA(byte[] plaintextByte, String inputSignature, String pubKey) {
try {
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
byte[] signed = Base64.getDecoder().decode(inputSignature);
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initVerify(publicKey);
signature.update(plaintextByte);
return signature.verify(signed);
} catch (Exception e) {
log.error("[收钱吧] 验签异常, signature={}, pubKey={}", inputSignature, pubKey, e);
return false;
}
}
}
package com.liquidnet.common.third.sqb.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "liquidnet.sqb")
@Component
@Setter
@Getter
public class SqbConfig {
private String appId;
private String appKey;
private String appCode;
/**
* 商户ID
*/
private String merchantId;
/**
* 商户UserID
*/
private String merchantUserId;
/**
* 角色
*/
private String role;
/**
* 公钥
*/
private String publicKey;
private String baseApi;
}
package com.liquidnet.common.third.sqb.param.callback;
import lombok.Data;
@Data
/**
* CallbackParams
*/
public class CallbackParams {
/** 推送事件ID,全局唯一 */
private Long eventId;
/** 推送时间戳,当前时间毫秒值 */
private Long timestamp;
/** 随机字串,用于签名 */
private String nonce;
/** 订单数据 JSON字符串 (详见 SqbOrderContentDTO) */
private String content;
/** 报文签名 */
private String signature;
}
package com.liquidnet.common.third.sqb.param.callback;
import lombok.Data;
import java.util.List;
@Data
/**
* SqbVoucherStatusDTO
*/
public class CouponCallbackContent {
/** 订单Sn */
private String orderSn;
/** 商城Sn */
private String mallSn;
/** 商户Sn */
private String merchantSn;
/** 门店id */
private String storeId;
/** 券列表信息 */
private List<VoucherItem> voucherList;
@Data
/**
* VoucherItem
*/
public static class VoucherItem {
/** 券号 */
private String voucherNo;
/** 类型 */
private String type;
/** 券状态 (0未核销, 1已核销, 2未核销退款, 3已核销退款中, 4未核销退款完成, 5已核销退款完成, 6已失效) */
private Byte targetState;
/** 券原状态 */
private Byte sourceState;
/** 券名称 */
private String voucherName;
/** 操作时间 (yyyy-MM-dd HH:mm:ss) */
private String operationTime;
/** 券对应的商品信息 */
private VoucherProductInfo item;
/** 券二维码地址 */
private String qrCodeUrl;
/** 券二维码内容 */
private String qrCodeContent;
/** 原金额 (单位:分) */
private Long oriAmount;
/** 实收金额 (单位:分) */
private Long receivedAmount;
/** 核销信息 (已核销会有这部分) */
private RedeemInfo redeemInfo;
}
@Data
/**
* VoucherProductInfo
*/
public static class VoucherProductInfo {
/** spuId */
private Long spuId;
/** skuId */
private Long skuId;
/** 商品标题 */
private String title;
/** 商品图片地址 */
private String img;
}
@Data
/**
* RedeemInfo
*/
public static class RedeemInfo {
/** 操作来源 (如: EXTERN) */
private String redeemSource;
/** 操作appId */
private String redeemAppId;
/** 客户端编码 */
private String redeemClientSn;
/** 外部编码 (appId + 请求号) */
private String redeemExternalOrderSn;
/** 操作商户 (userId) */
private String operatorMerchantId;
}
}
package com.liquidnet.common.third.sqb.param.callback;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
/**
* OrderCallbackContent
*/
public class OrderCallbackContent {
/**
* 订单号
*/
private String orderSn;
/**
* 支付流水号列表
*/
private List<String> payTsnList;
/**
* 终端号
*/
private String terminalSn;
/**
* 门店号
*/
private String storeSn;
/**
* 商城名称
*/
private String mallName;
/**
* 订单金额
*/
private BigDecimal orderAmount;
/**
* 创建时间
*/
private String createdAt;
/**
* 订单状态码 0:已创建 10:已支付 35:已完成 40:已取消
*/
private Integer orderStateCode;
/**
* 商品明细列表
*/
private List<Item> items;
/**
* 收集的信息 JSON字符串
*/
private String collectedInfo;
/**
* 预订单编码
*/
private List<String> preOrderSns;
/**
* 订单签名
*/
private String orderSignature;
/**
* 支付的代客下单数据
*/
private List<PreOrder> preOrderList;
/**
* 商品明细
*/
@Data
public static class Item {
/**
* 商品标题
*/
private String title;
/**
* 规格描述
*/
private String skuDesc;
/**
* 数量
*/
private String quantity;
/**
* 单位
*/
private String unit;
}
/**
* 采集数据
*/
@Data
public static class CollectedInfo {
/**
* 控件类型
*/
private String type;
/**
* 控件格式
*/
private String form;
/**
* 字段名
*/
private String fieldName;
/**
* 采集数据内容
*/
private String content;
}
/**
* 支付的代客下单数据
*/
@Data
public static class PreOrder {
/**
* 预订单id
*/
private String preOrderId;
/**
* 预订单编号
*/
private String preOrderSn;
/**
* 请求号
*/
private String requestId;
}
}
package com.liquidnet.common.third.sqb.param.callback;
import lombok.Data;
import java.util.List;
@Data
/**
* SqbRefundContentDTO
*/
public class RefundCallbackContent {
/** 售后单号 (全局唯一) */
private String ticketSn;
/** 订单号 */
private String orderSn;
/** 原状态 */
private Integer sourceState;
/** 目标状态 (20:完成, 15:退款完成, 0:创建, 10:退款中, 30:取消退款) */
private Integer targetState;
/** 下单金额 (单位:分) */
private Long amount;
/** 申请金额 (单位:分) */
private Long applyAmount;
/** 请求号 */
private String requestId;
/** 来源 (如: EXTERN) */
private String requestSource;
/** 退款的商品信息 */
private SqbRefundItemContainer item;
@Data
/**
* SqbRefundItemContainer
*/
public static class SqbRefundItemContainer {
/** 退款类型 (1:商品, 2:金额) */
private Byte type;
/** 商品明细列表 */
private List<SqbRefundItemDetail> items;
}
@Data
/**
* SqbRefundItemDetail
*/
public static class SqbRefundItemDetail {
/** spu id */
private String spuId;
/** sku id */
private String skuId;
/** 标题 */
private String title;
/** 数量 */
private String quantity;
/** 退款类型 (0:正常商品, 1:自定义商品) */
private Byte type;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
/**
* 创建收单请求
*/
@Data
public class AcquiringCreateRequest {
/**
* 应用id
*/
private String appid;
/**
* 卖家信息
*/
private CommonRequest.Seller seller;
/**
* 卖家信息
*/
private Order orderID;
/**
* 订单信息
*/
@Data
public static class Order {
/**
* 订单ID
*/
private String sn;
/**
* 订单密码
*/
private String signature;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
/**
* 收银台查询请求
*/
@Data
public class CashierQueryRequest {
/** 应用id */
private String appid;
/** 商户信息 */
private CommonRequest.Seller seller;
/** 支付模式代码 */
private Integer paymentMode;
/** 支付环境 */
private PaymentEnv paymentEnv;
/** 付款人信息 */
private CommonRequest.Payer payer;
/** 收单信息 */
private CommonRequest.Acquiring acquiringInfo;
@Data
/**
* 支付环境信息
*/
public static class PaymentEnv {
/** ip地址 最大40位 */
private String ip;
/** 支付客户端 最大32位 */
private String client;
/** 设备号 最大64位 */
private String device;
/** 版本号 */
private String version;
}
}
package com.liquidnet.common.third.sqb.param.request;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
public class CommonRequest {
/**
* 商城信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@NoArgsConstructor
public static class Mall {
/**
* 商城ID
*/
private String mallSn;
/**
* 商城密码
*/
private String signature;
}
/**
* 卖家信息/商户信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@NoArgsConstructor
public static class Seller {
/**
* 商户ID
*/
private String merchantId;
/**
* 商户userID
*/
private String merchantUserId;
/**
* 角色 固定值(super_admin)
*/
private String role;
}
/**
* 买家信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Buyer {
/**
* 买家ID(用户唯一标识)
*/
private String buyerId;
}
/**
* 付款人信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Payer {
/**
* 付款人ID
*/
private String userId;
}
/**
* 收单信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@NoArgsConstructor
public static class Acquiring {
/**
* 收单号
*/
private String acquiringSn;
/**
* 收单密钥
*/
private String signature;
}
/**
* 金额构成信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class AmountComposition {
/**
* 金额构成项列表
*/
private List<CompositionItem> compositionItems;
}
/**
* 金额构成项详情
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class CompositionItem {
/**
* 构成项类目
*/
private String category;
/**
* 构成项金额
*/
private Long amount;
}
/**
* 订单信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class OrderInfo {
/**
* 订单ID
*/
private String sn;
/**
* 订单密码
*/
private String signature;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
/**
* 查询券码请求
*/
@Data
public class CouponQueryRequest {
/** 应用id */
private String appid;
/** 订单信息 */
private CommonRequest.OrderInfo orderID;
/** 卖家信息 */
private CommonRequest.Seller seller;
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 券退款请求参数
*/
@Data
public class CouponRefundRequest {
/**
* 应用ID(收钱吧分配的应用id)
*/
private String appid;
/**
* 卖家信息
*/
private CommonRequest.Seller seller;
/**
* 订单信息
*/
private CommonRequest.OrderInfo orderID;
/**
* 申请退款信息不能为空
*/
private RefundInfo refundInfo;
/**
* 退款请求id(${AppCode}+id)
*/
private String requestId;
/**
* 退款来源(固定值:EXTERN)
*/
private String requestSource;
/**
* 退款信息
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RefundInfo {
/**
* 金额
*/
private Long applyAmount;
/**
* 退款类型(1商品 2金额)
*/
private Byte type;
/**
* 退款明细(商品必填)
*/
private List<RefundItem> items;
/**
* 退款原因
*/
private String refundReason;
}
/**
* 退款明细商品
*/
@Data
public static class RefundItem {
/**
* spu
*/
private String spuId;
/**
* sku
*/
private String skuId;
/**
* 标题
*/
private String title;
/**
* 商品图片
*/
private String img;
/**
* 数量
*/
private String quantity;
/**
* 退款类型(0正常商品 1自定义商品)
*/
private Byte type;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
import java.util.List;
/**
* 券码状态同步
*/
@Data
public class CouponStatusSyncRequest {
/** 应用ID(聚合收单唯一ID,需申请) */
private String appid;
/** 券号 */
private List<String> voucherNos;
/** 用户ID(自定义,核销用户id) */
private String redeemMerchantId;
/** 外部单号(${AppCode}+id,AppCode需要分配) */
private String redeemExternalOrderSn;
/** 来源(固定值:EXTERN) */
private String redeemSource;
/** 核销终端号(自定义) */
private String clientSn;
/** 券使用状态(0未核销,1已核销) */
private Byte status;
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 创建微信预支付订单请求
*/
@Data
public class CreateWechatPrepayOrderRequest {
/** 应用id */
private String appid;
/** 商户信息 */
private CommonRequest.Seller seller;
/** 收单号 */
private String acquiringSn;
/** 收单签名 */
private String signature;
/** 支付工具 */
private List<UsingPayTool> usingPayTools;
/** 支付环境 */
private PaymentEnv paymentEnv;
/** 支付工具签名 */
private String selectedSignature;
/** 序列号 */
private String seq;
@Data
/**
* 支付工具使用详情
*/
public static class UsingPayTool {
/** 支付工具ID */
private Long id;
/** 支付工具代码 */
private Integer payTool;
/** 支付模式 */
private Integer payMode;
/** 支付金额 */
private String amount;
/** 金额构成 */
private CommonRequest.AmountComposition amountComposition;
/** 用户身份信息 */
private String identity;
/** 支付请求号 */
private String requestSn;
/** 通道扩展参数 */
private Map<String, Object> channelExt;
}
@Data
/**
* 支付环境信息
*/
public static class PaymentEnv {
/** 付款终端ip */
private String clientIp;
/** 付款经纬度 */
private ClientPoi clientPoi;
/** 支付入口 */
private String paymentEntry;
}
@Data
/**
* 地理位置信息
*/
public static class ClientPoi {
/** 经度信息 */
private String longitude;
/** 纬度信息 */
private String latitude;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 商城列表接口
*/
@Data
public class MallListQueryRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 应用ID */
private String appid;
/** 过滤条件 */
private Filter filter;
/** 分页游标 */
private Cursor cursor;
/** 排序条件 */
private Sort sort;
@Data
/**
* 查询过滤条件
*/
public static class Filter {
/** 商户信息 */
private CommonRequest.Seller seller;
/** 商城行业筛选 */
private List<String> industryCodes;
/** 商城状态筛选 */
private List<Byte> states;
/** 商城状态筛选 */
private Byte state;
/** 创建时间起始 */
private String beginDateTime;
/** 创建时间截止 */
private String endDateTime;
}
@Data
/**
* 排序规则
*/
public static class Sort {
/** 排序方式: DESC/ASC */
private String sort;
/** 排序字段 */
private String sortField;
}
@Data
/**
* 分页游标
*/
public static class Cursor {
/** 游标字段 分页依据的字段(固定为id) */
private String cursorField;
/** 结束游标 上一页的结束游标(首次查询传 null) */
private String endCursor;
/** 查询数量 每页返回的最大订单数 */
private Integer count;
}
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
/**
* 商城列表查询响应数据
*/
@Data
public class MallProductsQueryRequest {
/** 应用ID */
private String appid;
/** 商户信息 */
private CommonRequest.Seller seller;
/** 商城标识 */
private CommonRequest.Mall mallID;
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.List;
/**
* 收钱吧 - 订单创建 请求参数
*
* @author liquidnet
* @since 2026-03-16
*/
@Data
public class OrderCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 应用ID(聚合收单唯一ID,需申请) */
private String appid;
/** 商城信息 */
private CommonRequest.Mall mallID;
/** 商户信息 */
private CommonRequest.Seller seller;
/** 结算项ID */
private String checkoutItemsId;
/** 买家信息 */
private CommonRequest.Buyer buyer;
/** 请求ID 不超过40位 */
private String requestId;
/** 标题 不超过32 */
private String subject;
}
package com.liquidnet.common.third.sqb.param.request;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 收钱吧 - 创建结算明细 请求参数
*
* @author liquidnet
* @since 2026-03-16
*/
@Data
public class SettlementCreateRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 应用ID(聚合收单唯一ID,需申请)
*/
private String appid;
/**
* 商城信息
*/
private CommonRequest.Mall mallID;
/**
* 商户信息
*/
private CommonRequest.Seller seller;
/**
* 买家信息
*/
private CommonRequest.Buyer buyer;
/**
* 结算明细组(结算条目列表)
*/
private List<CheckoutItem> checkoutItems;
/**
* 结算总金额(单位:分)
*/
private Long amount;
/**
* 结算明细条目
*/
@Data
public static class CheckoutItem {
/**
* sqb 商品识别号(商品唯一标识,如 spuId)
*/
private String spuId;
/**
* sqb 规格识别号(SKU唯一标识,如 skuId)
*/
private String skuId;
/**
* 单价(单位:分)
*/
private Long price;
/**
* 购买数量
*/
private String quantity;
/**
* 商品类型(0 正常商品 1 自定义商品)
*/
private Byte type;
/**
* 商品标题
*/
private String title;
/**
* 商品图片链接
*/
private String image;
}
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.AcquiringCreateData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 创建收单响应参数
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AcquiringCreateResponse extends BaseResponse<AcquiringCreateData> {
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 收钱吧基础响应参数
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaseResponse<T> {
/** 是否成功(true-成功,false-失败) */
private Boolean success;
/** 业务码(200-成功,4001等-失败码) */
private String code;
/** 失败信息(success为false时返回失败原因描述) */
private String msg;
/** data */
private T data;
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.CashierQueryData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 自助收银查询响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CashierQueryResponse extends BaseResponse<CashierQueryData>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.CouponQueryData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 券详情查询响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CouponQueryResponse extends BaseResponse<CouponQueryData>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.CouponRefundData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 券退款响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CouponRefundResponse extends BaseResponse<CouponRefundData>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 券状态变更响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CouponStatusSyncResponse extends BaseResponse<String>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.CreateWechatPrepayOrderData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 创建微信预支付订单响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreateWechatPrepayOrderResponse extends BaseResponse<CreateWechatPrepayOrderData>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.MallListQueryData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 商城列表查询响应参数
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class MallListQueryResponse extends BaseResponse<List<MallListQueryData>> {
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 商城商品接口响应
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class MallProductsQueryResponse extends BaseResponse<List<MallProductsQueryData>>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.OrderCreateData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 收钱吧 - 创建订单 响应参数
*
* @author liquidnet
* @since 2026-03-16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class OrderCreateResponse extends BaseResponse<OrderCreateData>{
}
package com.liquidnet.common.third.sqb.param.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.response.data.SettlementCreateData;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 收钱吧 - 创建结算明细 响应参数
*
* @author liquidnet
* @since 2026-03-16
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SettlementCreateResponse extends BaseResponse<SettlementCreateData> {
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 创建收单Data
*/
@EqualsAndHashCode(callSuper = true)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AcquiringCreateData extends CommonRequest.Acquiring {
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import lombok.Data;
import java.util.List;
/**
* 收银台查询Data
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CashierQueryData {
/**
* 支付工具列表
*/
private List<PayTool> payTools;
/**
* 已选中的支付工具签名
*/
private String selectedSignature;
/**
* 全局提示
*/
private List<String> globalTips;
/**
* 响应唯一序列号
*/
private String seq;
/**
* 收单金额
*/
private Long amount;
/**
* 收款方信息
*/
private Payee payee;
/**
* 业务信息
*/
private BizInfo bizInfo;
/**
* 金额模型
*/
private CashierAmount cashierAmount;
/**
* 支付工具详情
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class PayTool {
/**
* 支付工具id
*/
private Long id;
/**
* 支付工具代码
*/
private String code;
/**
* 支付工具名称
*/
private String name;
/**
* 支付工具类型 (0表示微信)
*/
private Integer type;
/**
* 是否可选
*/
private Boolean selectable;
/**
* 是否选中
*/
private Boolean selected;
/**
* 展示金额
*/
private Long showAmount;
/**
* 提示列表
*/
private List<String> tips;
/**
* 金额构成
*/
private CommonRequest.AmountComposition amountComposition;
}
/**
* 收款方信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Payee {
/**
* 商户id
*/
private String merchantId;
/**
* 商户名称
*/
private String merchantName;
/**
* 门店id
*/
private String storeId;
/**
* 门店名称
*/
private String storeName;
/**
* 终端号
*/
private String terminalSn;
}
/**
* 业务信息
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class BizInfo {
/**
* 业务方
*/
private String bizParty;
/**
* 交易应用
*/
private String tradeApp;
}
/**
* 金额明细模型
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class CashierAmount {
/**
* 原始支付金额
*/
private Long oriPaymentAmount;
/**
* 实际支付金额
*/
private Long discPaymentAmount;
/**
* 优惠金额
*/
private Long discAmount;
}
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
/**
* 查询券码响应Data
*/
public class CouponQueryData {
/** 券名称 */
private String voucherName;
/** 商品SPU */
private String spuId;
/** 商品SKU */
private String skuId;
/** 商品地址 */
private String itemImageUrl;
/** 券二维码 */
private String url;
/** 券号 */
private String voucherNo;
/** 券类型 0金额券 1商品券 */
private Byte type;
/** 券状态 0未核销 1已核销 2未核销退款 3已核销退款中 4未核销退款完成 5已核销退款完成 6已失效 */
private Byte status;
/** 操作时间 */
private String operationTime;
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 券退款响应结果
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CouponRefundData {
/** 退款单号 */
private String ticketsSn;
/** 单号密码 */
private String ticketSignature;
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 创建微信预支付订单响应
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreateWechatPrepayOrderData {
/**
* 收单号
*/
private String acquiringSn;
/**
* 收单签名
*/
private String signature;
/**
* 收单状态
*/
private Byte acquiringState;
/**
* 金额明细
*/
private AmountDetails amount;
/**
* 支付凭证
*/
private PaymentVoucher paymentVoucher;
/**
* 通道信息
*/
private ChannelInfo channelInfo;
/**
* 订单金额明细
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class AmountDetails {
/**
* 收单总金额
*/
private Long totalAmount;
/**
* 已付总金额
*/
private Long paidAmount;
/**
* 已退总金额
*/
private Long refundedAmount;
}
/**
* 支付凭证详情
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class PaymentVoucher {
/**
* 时间戳
*/
private String timeStamp;
/**
* 订单详情扩展字符串
*/
private String packageStr;
/**
* 签名
*/
private String paySign;
/**
* Appid
*/
private String appId;
/**
* 签名方式
*/
private String signType;
/**
* 随机字符串
*/
private String nonceStr;
}
/**
* 通道信息详情
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class ChannelInfo {
/**
* 收钱吧通道流水号
*/
private String payTsn;
}
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.util.List;
/**
* 商城列表查询响应数据
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class MallListQueryData {
/** 商城编号 */
private String mallSn;
/** 商城签名 */
private String signature;
/** 商城名称 */
private String mallName;
/** 商城状态 0:待上线 1:已上线 2:已下线 */
private Byte state;
/** 商城状态描述 */
private String stateDesc;
/** 内部状态 1启用 0禁用 */
private Byte internalState;
/** 内部状态描述 */
private String internalStateDesc;
/** 删除状态 0未删除 1已删除 */
private Byte isDelete;
/** 删除时间 */
private String deleteTime;
/** 大额开通状态 */
private Byte crossCityPaymentStatus;
/** 大额开通状态描述 */
private String crossCityPaymentStatusDesc;
/** 应用类型 0拼塔商城 目前都是0,无需关注 */
private Integer appType;
/** 行业code */
private String industryCode;
/** 商户信息 */
private Seller seller;
/** 订单统计 */
private OrderStatistics orderStatistics;
/** 商户统计 */
private SellerStatisticsModel sellerStatisticsModel;
/** 创建时间 */
private String ctime;
/** 跨城状态明细 */
private List<CrossCityPayment> crossCityPayments;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
/**
* 商户信息
*/
public static class Seller {
/** 商户号 */
private String merchantSn;
/** 商户id */
private String merchantId;
/** 商户名称 */
private String merchantName;
/** 门店号 */
private String storeSn;
/** 门店id */
private String storeId;
/** 门店名称 */
private String storeName;
/** 终端号 */
private String terminalSn;
/** 大额终端编号 */
private String crossCityTerminalSn;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
/**
* 订单统计
*/
public static class OrderStatistics {
/** 订单数量 */
private Long totalNum;
/** 商品数量 */
private String totalQuantity;
/** 订单总额(分) */
private Long totalAmount;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
/**
* 商户统计
*/
public static class SellerStatisticsModel {
/** 异地交易失败订单数量 */
private Long orderFailNum;
}
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
/**
* 跨城支付明细
*/
public static class CrossCityPayment {
/** 支付工具code 2:支付宝 3:微信 */
private Byte payToolCode;
/** 支付工具描述 */
private String payToolCodeDesc;
/** 大额开通状态 */
private Integer status;
/** 大额开通状态描述 */
private String statusDesc;
}
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 商城商品接口响应数据
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class MallProductsQueryData {
/**
* 商品spuId
*/
private String spuId;
/**
* 商品图片
*/
private List<String> converImages;
/**
* 商品描述
*/
private String productIntroduction;
/**
* 商品标题
*/
private String title;
/**
* 商品规格
*/
private List<Sku> skuResults;
/**
* 商品规格
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Sku {
/**
* 库存类型
*/
private Integer stockType;
/**
* 商品skuId
*/
private String skuId;
/**
* 库存值
*/
private BigDecimal quantity;
/**
* 规格标题
*/
private String skuTitle;
/**
* 规格名称
*/
private String skuName;
/**
* 价格
*/
private Long price;
}
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import lombok.Data;
/**
* 收钱吧创建订单响应参数Data
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class OrderCreateData {
/** 订单编号) */
private String orderSn;
/** 订单密钥) */
private String orderSignature;
/** 收单信息) */
private CommonRequest.Acquiring acquiring;
}
package com.liquidnet.common.third.sqb.param.response.data;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 收钱吧创建结算明细响应参数Data
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SettlementCreateData {
/** 结算项ID) */
private String checkoutItemsId;
}
......@@ -16,5 +16,6 @@
<module>liquidnet-common-third-zxlnft</module>
<module>liquidnet-common-third-antchain</module>
<module>liquidnet-common-third-xuper</module>
<module>liquidnet-common-third-sqb</module>
</modules>
</project>
......@@ -251,4 +251,13 @@ liquidnet:
pubKey: pubKey
privateKey: xxxx
#application-dev-end
\ No newline at end of file
#application-dev-end
sqb:
base-api: 'https://open-api.shouqianba.com'
app-id: '2026040900011060'
app-key: '5cd1e48eba44a5264914115cd31df494'
app-code: 'MDTEST'
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin'
\ No newline at end of file
......@@ -250,4 +250,13 @@ liquidnet:
appId: 2021002131608679
pubKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAph6mXcV8gPoEE6ZJJiaSk6x6jwWThLuzHDpZ9CMreZrMRUkGrV0WC920Ktp0tGlpo+BNfF+yBrapTAM8Y0Ztz5XcZWnx7gsfcsV48GHJ09qWbkJfXaBY30iX6O6q59jqWJMITQKz6OLL6HL3wxhoXooKHjXamQ983RTsI6wT4nWsTtBp8mTXCY+8XOQ4rw87AeHHetIoAtogk8H2etKgu1nDaQGXaA+ng+khab+b42pZSBX5g6jWlNCZviAoiy7e3upyu/6lqOhuLDEYzxD0i0oZ/46oIsILvEBCYQvXpbEz8KAM8dD5RBylNSpbu2edrrhytCq+0HFA4f1yp2D7WQIDAQAB
privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCR3u0lL7LvLHK0VG8/FXcA6hjLC+oY4ra/zqs7QRg4EVR8GGQ1G3hg5av6LJxBN5lR07ONYojAOgzmxkoPdM4+aTn9XMfk2sAWen7RMMNcdpyx1eNdCFlm1GzT419CXkIS0Z+gUsxF+PasGamjzfB/Lg66DNZ1zHh1YPa/hwt4KKO1YHiReBCO0hLo7/uGTFUdfzB9pCUIzOTQwaYJPqRc0pz0/9MSF65K/Qtc9lqFpsVXmcWQOZZjoDJGIH6XEbyWXzMRPy5MLryvF3s6ZNURuU7j8t16DVkUTx3p+bEm2U3GmL5btltiouduPUBXHZjRUWAAyFVzc9EMQDQDA89rAgMBAAECggEAQPEHkQuoVPTr6D7C/EnZHk4aVaNrSEL+62veLdYwKx4EB+9wBXjCYhk+NHXR5vMSziw6/tIEYdg2UDOtWy48d+qvB45b7BY7eIZ2mTllcG/aGQ5JV+zUqIQgI0FR8qE2N1yd/Wl/ShOp9jrCnIud63Ec21XF0NIEOvW9RM2hnI+G6YLT9j3NbvJvB4zS7U7tejEXfDP1Oxtjk0ba7Qq04yYdeX16TOrs8VTpAzrj+xW/VJDLBoDfgLvE2G/PqhpFOFfH5V6zKTYfk9VQLh1H9dTj1CkFgRHEJfrFc7XcIIb9nxjnV5JXZmcPjmErQUg1t8JLzjJuQIvzCda5Ba9JEQKBgQDL+iSWlSfyJQyx283VIjhIJ3w9ZRAIWsm/Z1205tdnBeFAzsiItLamASf7Y1zMIMYxFyUD41npFUqetjzdxVHiWlOg2/cE7t2SQWbfrbgqwG0z23If4jUdIj3CJq9yLVWAXvvvm/I/Zrj4oGycYkWV0eGZwoAqIIHIOmTPslacUwKBgQC3Eve/jAHkcgCRppwvDvrw9AYKd6rrs9dyCcsj7Zz2sT7CTw89JcjmjwsLx5+v+zSGgkceG21uIusAfWoAbAYadIGP7zCOBWGVdFvt5hqqKtwL7sLdWrauKw5NId8SlVu0Jvy2dVjzyJ3jfYj1tXt/kCNLgf0zL4yZ+q9G+KBdiQKBgQCkNcSu1XVLIziNFv8lzl6w99i1NF8r2qsARB7UO+K9NaaZnd8i7xj7m4Kshtl2HAxyCMfr0WPYmSNxkhR+FRROvZkFrw+2EPaff7dp61iQUkmXrdq6gElyItbFLo+fw49JwS3hQBJNqEzRG5VUcGjErCqKtmKnh3Pz1c7CxjejsQKBgEbJ6cxCGdU4k6m+D7ROiY+z+8X+YbPEFXF+AfOBhGkLPiYqJc1SF+22r+G9La0BaFz+cPteRaEJlW7aD6vcGTwPgq2iIlc4E3STypwhlnvoGK/wgZ7P3cVY1q3ShAwOfqgZTyxKEbwp/YsiVlwT8Y3wsQUYXUx2fVpoyW+a4X9pAoGBAIQSQ/Dig/6kU7yb9F4uFYVGnrArQk9bFC3tXe1rgyw6nUq5txAmc6ovvrmCVMilQ2nfYuzAtuKy6ouf71u+nTDaTV9LxGOxKzSDu+58R8qZrzcnSerfPivocWmlpN5mse45RVRnlw3EvObCwKE3zkDYKk5PgxKsmLrLAgaF4CGg
#application-test-end
\ No newline at end of file
#application-test-end
sqb:
base-api: 'https://open-api.shouqianba.com'
app-id: '2026040900011060'
app-key: '5cd1e48eba44a5264914115cd31df494'
app-code: 'MDTEST'
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin'
\ No newline at end of file
......@@ -26,3 +26,5 @@ liquidnet:
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArR8pqWsRMqiunn8uEZGF9AeizJK0vuWjlcNnTbw9Sb96dMVuYu3SRj+Dx4E4SgyEL4CYROou1xwY57kAKEqHdH7o1W41O9jYjXZG38BrtBR+D9Qh9OqGxCZ+e4Gi38XHGg6fn67iXefOqp1kWGd4qc8tIZO1lIDXS19R09D/mESNBMulQdVPyZF7gvd11A+7EEOfRlSOjrtqIoUWV0GIqhLPUtGJk8Uq/d9NLitJyvK3tgz8cvJ4RyK6UpGtRDrqiBiQxbvK9EqMd1sw3zkvM03szSWon4LHFNqvDr6RYfFyFUCvX9UPYmeritENnroEuTBlTFLLb68ed4HZEZDPTQIDAQAB"
private-key: "MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDJAOaA1ikJzDL0vUuTyl3/vlHyuSod6/gFLLrSTD6EJkma5Ld34HHu82/5pEojEvbcU113L9j3fUJlpyjX6CFk6j2KjMIuyFxhgrVFi5WT5m74wYohoWNifkQrgwsO3oxI7cewWFu/w7/yCK9dzI4QxasGUKH9iPweI+26IR0DBbOfC9GVudOy2b2xLrGAevEEHdVTNqrQNdlrTzqAH7r3uk8s2vaBZX+O4gyf7eKdHdC4CVSWfYPLO1sA48MxNwI7OExxfGeV+0wmBMGRSoZ5FhWsqZs+f9jGcmfF+uEfAO71PqHjezXYxq7+oWDfDBPCTc5fo9w5v1HV0aZaYOe1AgMBAAECggEBAI4yR98fInse7XF8NOpBwIv6/QhEfAoc9CHdCfFaJOPiHjIo2a5BpvhPWYj288eqU998TmPSAqDbCUzWm6taOb2lhJHukDT+Y3RMPqcLX275Fsp+SJUQEjoMb3eExh7ny8CQDrOvXoDkH3c/M6ic3Gf7Hslh46dz8D/2VOhXIqoObPlSLzniwiMTDBEwB7IRc3Q+r4V6ZnKt8wjKQZpotBA3TlJlEBBj/h5SbWokwMQbTqFkjl7gVe0ase2WfV+cD4qhPZx6CWphPVyWelg+wpDqXOIQdnE8pgri5a9ZkzgPTOrKyCm+EOa9lZAp81tnb2iFhrlkKPSWUW8zLtZzxMECgYEA+sNyF0U9anyxeKxXtlGKKuMHJSnBpZeU6FSvZjTewFH2Sxh3QwZjg6h5BfvTLH1XfNerx3gdpAPJ+EyAZuEibDr47bp+j4CtT27dVolz5XQ5ugOadwzdNZkq6vhuq1aGATmS/mlNE1/pdMEP9F6hi2HYncER6BFOy0xSwMKCnRECgYEAzTNxhvZ0pb2hPKylxHUydkm3Uznq5Zkquv6II6W5aiKvceETHwdRZLoKc+I0kd0/4fBfJI2Jsjexy51ERiG+8y4wVrcrky6NLw6mnXSvnTSQCftbexheJTg9c5dpfKIj+rxtuBeZ3Sj1MJQ6OSBUYu3iTqstO0Rgp/1ofWQJ8GUCgYEAspxzr0+KJ0cZwbI/54S8vT9n33iWjbQiRDnNlScjYij/HQ4YJI1wZF6jlTeBerbskeesWy+bLS/ltA4Jhz3knuKCXBHyA5TL3UBCN1lAS7c1RuE6LIHlLkAi6ap6aV//ou+3W671T0+JobfB/XVJ61WOTQ8wCfQKA5QhfVsOXYECgYEAvzbm3Ysfm6qfazi+p9lGErASov1fhGA8T1AMcJtnsh1sO8Qu20UodaJfRylNL3dqphIltpwl6eq4RTLhgjDEDTvHU6cQdfB1I5qVbDhlxSpL5uFRl91XLXvA18wKQledC3M3Esr7V/loscIOl1knCaD+t6wPVCEdqK0dB2uHT3kCgYEA3p3rlmCmWzkZ/U8jE4087YEkJWV+r86YC63r4YZEqZtfHk4hNchAYke4jYPqkTtmRVZi2C6KuVr5M3ASHmGWorBY0VA9Abd3daniNocZCeMOt4Z7U6MIbqW7KYSrjx8V8HIsdH7HF97ofRuMH6oaz9bFMM6XwrEAMY+zTdH9A4Y="
notify-url: 'https://testgoblin.zhengzai.tv/goblin/bracelet/callback'
......@@ -126,6 +126,10 @@ global-auth:
- ${liquidnet.info.context}/pay/**
# 健康检查
- ${liquidnet.info.context}/health
# 收钱吧回调
- ${liquidnet.info.context}/goblin/sqb/order/callback
- ${liquidnet.info.context}/goblin/sqb/refund/callback
- ${liquidnet.info.context}/goblin/sqb/coupon/callback
# -----------------------------------------------------------
# -----------------------------------------------------------
......
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;
import java.time.LocalDateTime;
/**
* <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;
/**
* 商城签名
*/
private String signature;
/**
* 收钱吧商品ID
*/
private String sqbSpuId;
/**
* 收钱吧规格ID (skuId)
*/
private String sqbSkuId;
private LocalDateTime createdAt;
private String updatedBy;
private LocalDateTime updatedAt;
/**
* 删除标记[0-未删除|1-删除]
*/
private String delFlg;
private String deletedBy;
private LocalDateTime deletedAt;
}
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;
/**
* 收钱吧收单号密码
*/
private String sqbAcquiringSign;
/**
* 结算明细ID
*/
private String sqbCheckoutItemsId;
/**
* 券码编号
*/
private String couponSn;
/**
* 核销二维码
*/
private String couponQrCode;
/**
* 券码过期时间
*/
private String couponExpireTime;
/**
* 状态 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 5-已取消 9-失败
*/
private Integer status;
/**
* 收钱吧-退款号
*/
private String sqbRefundSn;
/**
* 收钱吧-退款号密码
*/
private String sqbRefundSignature;
/**
* 退款原因
*/
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-03-27
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("goblin_sqb_performance_config")
public class GoblinSqbPerformanceConfig implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 演出ID
*/
@TableId(value = "performances_id", type = IdType.INPUT)
private String performancesId;
/**
* 演出结束自动下架 0-否 1-是
*/
private Integer autoOffline;
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;
import java.math.BigDecimal;
/**
* <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;
/**
* 换购价格(不设置则按商品售价售卖)
*/
private BigDecimal settlementPrice;
/**
* 状态 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.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceConfig;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 演出-收钱吧配置 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-03-27
*/
@Mapper
public interface GoblinSqbPerformanceConfigMapper extends BaseMapper<GoblinSqbPerformanceConfig> {
}
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;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Component
@FeignClient(name = "liquidnet-service-goblin",
......@@ -28,4 +30,7 @@ public interface FeignGoblinTaskClient {
@GetMapping("/rsc/maizhi/job/refundRes")
ResponseDto<Boolean> refundRes();
@PostMapping("/goblin/job/sqb/autoRefund")
ResponseDto<String> sqbAutoRefund(@RequestParam("performancesId") String performancesId);
}
......@@ -68,4 +68,19 @@ public class GoblinTaskHandler {
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 @@
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-goblin-do</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-zxlnft</artifactId>
......@@ -103,6 +109,12 @@
<artifactId>dg-java-sdk</artifactId>
<version>3.0.27</version>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-sqb</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
......
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.GoblinFrontSelectGoodVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbCouponVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderDetailVo;
import com.liquidnet.service.goblin.service.GoblinFrontService;
import com.liquidnet.service.goblin.service.IGoblinSqbService;
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.NotBlank;
import java.util.List;
/**
* 收钱吧订单 Controller
*/
@Slf4j
@Api(tags = "收钱吧订单")
@RestController
@RequestMapping("/goblin/sqb")
public class GoblinSqbController {
@Autowired
private IGoblinSqbService goblinSqbService;
@Autowired
private GoblinFrontService goblinFrontService;
/**
* 获取核销二维码(券码)
*/
@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 goblinSqbService.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(value = "orderId") String orderId) {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧退款] 接收请求 userId={}, orderId={}", userId, orderId);
return goblinSqbService.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 goblinSqbService.syncCouponStatus(userId, orderId);
}
/**
* 用户收钱吧订单列表
*/
@GetMapping("/list")
@ApiOperation("用户收钱吧订单列表")
public ResponseDto<List<GoblinSqbOrderDetailVo>> getOrderList() {
String userId = CurrentUtil.getCurrentUid();
log.info("[收钱吧订单列表] 接收请求 userId={}", userId);
return goblinSqbService.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 goblinSqbService.getOrderDetail(userId, orderId);
}
@GetMapping("/performance/selectGoods")
@ApiOperation("按演出ID获得已关联商品列表(pageNumber从0开始)")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", required = true, dataType = "String", name = "performancesId", value = "演出ID"),
@ApiImplicitParam(paramType = "query", required = true, dataType = "int", name = "pageSize", value = "分页大小"),
@ApiImplicitParam(paramType = "query", required = true, dataType = "int", name = "pageNumber", value = "页码(从0开始)")
})
public ResponseDto<GoblinFrontSelectGoodVo> getPerformanceSelectGoods(
@RequestParam(name = "performancesId", required = true) String performancesId,
@RequestParam(name = "pageSize", required = true) int pageSize,
@RequestParam(name = "pageNumber", required = true) int pageNumber) {
return ResponseDto.success(goblinFrontService.getPerformanceSelectGoods(performancesId, pageNumber, pageSize));
}
}
package com.liquidnet.service.goblin.controller.Inner;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.service.IGoblinSqbService;
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 IGoblinSqbService goblinSqbService;
@PostMapping("/autoRefund")
@ApiOperation("演出结束自动退款")
public ResponseDto<String> autoRefund(@RequestParam("performancesId") String performancesId) {
log.info("[收钱吧自动退款] 收到任务,performancesId={}", performancesId);
return goblinSqbService.autoRefundByPerformance(performancesId);
}
}
......@@ -685,7 +685,7 @@ public class GoblinStoreMgtGoodsController {
|| !mgtGoodsInfoVo.getDelFlg().equals("0")) {
return ResponseDto.failure(ErrorMapping.get("149010"));
}
if (mgtGoodsInfoVo.getSpuType() != 0) {
if (mgtGoodsInfoVo.getSpuType() != 0 && mgtGoodsInfoVo.getSpuType() != 33) {
return ResponseDto.failure(ErrorCode.HTTP_PARAM_ERROR.getCode(), "无效操作,该商品非常规商品");
}
if (!mgtGoodsInfoVo.getName().equals(mgtGoodsAddParam.getName())
......@@ -884,7 +884,7 @@ public class GoblinStoreMgtGoodsController {
if (null == goodsInfoVo || !goodsInfoVo.getDelFlg().equals("0")) {
return ResponseDto.failure(ErrorMapping.get("149010"));
}
if (goodsInfoVo.getSpuType() != 0) {
if (goodsInfoVo.getSpuType() != 0 && goodsInfoVo.getSpuType() != 33) {
return ResponseDto.failure(ErrorCode.HTTP_PARAM_ERROR.getCode(), "无效操作,该商品非常规商品");
}
{// 分类、ISBN校验
......@@ -1237,7 +1237,7 @@ public class GoblinStoreMgtGoodsController {
|| !goodsInfoVo.getStoreId().equals(storeId)) {
return ResponseDto.failure(ErrorMapping.get("149010"));
}
if (goodsInfoVo.getSpuType() != 0) {
if (goodsInfoVo.getSpuType() != 0 && goodsInfoVo.getSpuType() != 33) {
return ResponseDto.failure(ErrorCode.HTTP_PARAM_ERROR.getCode(), "无效操作,该商品非常规商品");
}
{// 分类、ISBN校验
......@@ -1456,7 +1456,7 @@ public class GoblinStoreMgtGoodsController {
if (null == mgtGoodsSkuInfoVo || !mgtGoodsSkuInfoVo.getDelFlg().equals("0") || !mgtGoodsSkuInfoVo.getStoreId().equals(storeId)) {
return ResponseDto.failure(ErrorMapping.get("149011"));
}
if (mgtGoodsSkuInfoVo.getSkuType() != 0) {
if (mgtGoodsSkuInfoVo.getSkuType() != 0 && mgtGoodsSkuInfoVo.getSkuType() != 33) {
return ResponseDto.failure(ErrorCode.HTTP_PARAM_ERROR.getCode(), "无效操作,该商品非常规商品");
}
if (mgtGoodsSkuInfoVo.getShelvesStatus().equals("3")) {
......
package com.liquidnet.service.goblin.controller.manage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtGoodsSqbAddParam;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinSqbGoodsExt;
import com.liquidnet.service.goblin.mapper.GoblinSqbGoodsExtMapper;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreMgtSqbGoodsService;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.GoblinSqbRedisUtils;
import com.liquidnet.service.goblin.util.ObjectUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;
/**
* 收钱吧商品管理
*/
@Api(tags = "收钱吧商品管理")
@Slf4j
@Validated
@RestController
@RequestMapping("store/mgt/sqb/goods")
public class GoblinStoreMgtSqbGoodsController {
@Autowired
private IGoblinStoreMgtSqbGoodsService goblinstoreMgtSqbGoodsService;
@Autowired
private GoblinRedisUtils goblinRedisUtils;
@Autowired
private GoblinSqbRedisUtils goblinSqbRedisUtils;
@Autowired
private GoblinSqbGoodsExtMapper goblinSqbGoodsExtMapper;
@Autowired
private SqbBiz goblinShouQianBaService;
/**
* 查询所有商城及商品列表
* 后端自动拉取所有商城,循环获取每个商城的商品列表,聚合返回前端
*/
@GetMapping("/list")
@ApiOperation("查询收钱吧所有商城及商品列表")
public ResponseDto<List<GoblinSqbPerfGoodsVo>> list() {
return goblinstoreMgtSqbGoodsService.getProductList();
}
@ApiOperationSupport(order = 1)
@ApiOperation(value = "SPU添加-收钱吧商品(批量)")
@PostMapping("add")
public ResponseDto<Object> add(
@ApiParam(value = "门店ID", required = true) @RequestParam("storeId") String storeId,
@ApiParam(value = "待同步的收钱吧商品列表", required = true) @RequestBody @Validated List<GoblinSqbPerfGoodsVo> items) {
String currentUid = CurrentUtil.getCurrentUid();
if (!goblinRedisUtils.hasStoreId(currentUid, storeId)) {
return ResponseDto.failure(ErrorMapping.get("149002"));
}
if (CollectionUtils.isEmpty(items)) {
return ResponseDto.success(buildAddResult(0, 0, 0, Collections.emptyList()));
}
if (log.isDebugEnabled()) {
log.debug("收钱吧商品管理:批量添加:[items={}]", JsonUtils.toJson(items));
}
int addSuccess = 0, alreadyExists = 0, addFail = 0;
List<String> invalidReasons = new ArrayList<>();
for (GoblinSqbPerfGoodsVo sqbGoods : items) {
// TODO 测试用
// int i = 1;
// for (MallProductsQueryData.Sku skuResult : sqbGoods.getSkuResults()) {
// if (StringUtils.isBlank(skuResult.getSkuName())) {
// skuResult.setSkuName("未知SkuName-" + i);
// }
// }
String invalidReason = validateSqbGoodsForSync(sqbGoods);
if (StringUtils.isNotBlank(invalidReason)) {
addFail++;
invalidReasons.add(invalidReason);
continue;
}
if (existsSqbSpu(sqbGoods.getMallSn(), sqbGoods.getSpuId())) {
alreadyExists++;
continue;
}
try {
GoblinStoreMgtGoodsSqbAddParam initParam = new GoblinStoreMgtGoodsSqbAddParam();
GoblinGoodsInfoVo goodsInfoVo = initParam.initGoodsInfoVo(storeId, sqbGoods, currentUid, LocalDateTime.now());
ArrayList<GoblinGoodsSpecVo> goodsSpecVoList = ObjectUtil.getGoblinGoodsSpecVoArrayList();
List<GoblinGoodsSkuInfoVo> goodsSkuInfoVoList = ObjectUtil.getGoblinGoodsSkuInfoVoArrayList();
initParam.initGoodsSkuInfoVo(goodsInfoVo, goodsSkuInfoVoList, goodsSpecVoList, sqbGoods);
goodsInfoVo.setTagVoList(new ArrayList<>());
goodsInfoVo.setExtagVoList(new ArrayList<>());
goodsInfoVo.setArtagVoList(new ArrayList<>());
goodsInfoVo.setServiceSupportVoList(new ArrayList<>());
goblinstoreMgtSqbGoodsService.sqbGoodsAdd(goodsInfoVo, goodsSkuInfoVoList);
saveSqbExtRelations(goodsInfoVo, sqbGoods);
addSuccess++;
} catch (Exception e) {
addFail++;
log.error("收钱吧商品管理:批量添加失败, storeId={}, sqbSpuId={}", storeId, sqbGoods.getSpuId(), e);
}
}
return ResponseDto.success(buildAddResult(addSuccess, alreadyExists, addFail, invalidReasons));
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "商品编辑:SPU编辑-收钱吧商品(批量,传系统spuId)")
@PutMapping("edit_spu")
public ResponseDto<Object> editSpu(
@ApiParam(value = "门店ID", required = true) @RequestParam("storeId") String storeId,
@ApiParam(value = "本地系统SPU ID列表", required = true) @RequestBody @Validated List<String> localSpuIds) {
String currentUid = CurrentUtil.getCurrentUid();
if (!goblinRedisUtils.hasStoreId(currentUid, storeId)) {
return ResponseDto.failure(ErrorMapping.get("149002"));
}
if (CollectionUtils.isEmpty(localSpuIds)) {
return ResponseDto.success(buildEditResult(0, 0, 0, 0, Collections.emptyList()));
}
int editSuccess = 0, notAdded = 0, sqbNotFound = 0, editFail = 0;
List<String> invalidReasons = new ArrayList<>();
for (String localSpuId : localSpuIds) {
if (StringUtils.isBlank(localSpuId)) {
editFail++;
continue;
}
List<GoblinSqbGoodsExt> extList = goblinSqbGoodsExtMapper.selectList(new LambdaQueryWrapper<GoblinSqbGoodsExt>()
.eq(GoblinSqbGoodsExt::getSpuId, localSpuId)
.eq(GoblinSqbGoodsExt::getDelFlg, "0"));
if (CollectionUtils.isEmpty(extList)) {
notAdded++;
continue;
}
GoblinGoodsInfoVo localGoodsInfoVo = goblinRedisUtils.getGoodsInfoVo(localSpuId);
if (localGoodsInfoVo == null) {
notAdded++;
continue;
}
GoblinSqbGoodsExt ext = extList.get(0);
String mallSn = ext.getMallSn();
String sqbSpuId = ext.getSqbSpuId();
String signature = ext.getSignature();
GoblinSqbPerfGoodsVo sqbGoods = fetchSqbGoodsByMallAndSpu(mallSn, sqbSpuId, signature);
if (sqbGoods == null) {
sqbNotFound++;
continue;
}
String invalidReason = validateSqbGoodsForSync(sqbGoods);
if (StringUtils.isNotBlank(invalidReason)) {
editFail++;
invalidReasons.add("localSpuId=" + localSpuId + ", " + invalidReason);
continue;
}
try {
if (goblinstoreMgtSqbGoodsService.sqbGoodsEditSpu(currentUid, sqbGoods, localGoodsInfoVo)) {
editSuccess++;
} else {
editFail++;
}
} catch (Exception e) {
editFail++;
log.error("收钱吧商品管理:批量编辑失败, storeId={}, localSpuId={}, sqbSpuId={}",
storeId, localSpuId, sqbSpuId, e);
}
}
return ResponseDto.success(buildEditResult(editSuccess, notAdded, sqbNotFound, editFail, invalidReasons));
}
private boolean existsSqbSpu(String mallSn, String sqbSpuId) {
return goblinSqbGoodsExtMapper.selectCount(new LambdaQueryWrapper<GoblinSqbGoodsExt>()
.eq(GoblinSqbGoodsExt::getMallSn, mallSn)
.eq(GoblinSqbGoodsExt::getSqbSpuId, sqbSpuId)
.eq(GoblinSqbGoodsExt::getDelFlg, "0")) > 0;
}
private void saveSqbExtRelations(GoblinGoodsInfoVo goodsInfoVo, GoblinSqbPerfGoodsVo sqbGoods) {
LocalDateTime now = LocalDateTime.now();
List<String> localSkuIds = goodsInfoVo.getSkuIdList() == null ? Collections.emptyList() : goodsInfoVo.getSkuIdList();
List<MallProductsQueryData.Sku> sqbSkus = sqbGoods.getSkuResults() == null ? Collections.emptyList() : sqbGoods.getSkuResults();
int relationSize = Math.max(localSkuIds.size(), sqbSkus.size());
if (relationSize == 0) {
relationSize = 1;
}
for (int i = 0; i < relationSize; i++) {
GoblinSqbGoodsExt ext = new GoblinSqbGoodsExt();
ext.setSpuId(goodsInfoVo.getSpuId());
ext.setSkuId(i < localSkuIds.size() ? localSkuIds.get(i) : null);
ext.setMallSn(sqbGoods.getMallSn());
ext.setSignature(sqbGoods.getSignature());
ext.setSqbSpuId(sqbGoods.getSpuId());
ext.setSqbSkuId(i < sqbSkus.size() ? sqbSkus.get(i).getSkuId() : null);
ext.setDelFlg("0");
ext.setCreatedAt(now);
ext.setUpdatedAt(now);
goblinSqbGoodsExtMapper.insert(ext);
// 设置正在商品获取收钱吧相关信息缓存
goblinSqbRedisUtils.setSqbGoodsExt(ext.getSpuId(), ext.getSkuId(),
GoblinSqbGoodsExtVo.of(ext.getSpuId(), ext.getSkuId(), ext.getMallSn(), ext.getSignature(), ext.getSqbSpuId(), ext.getSqbSkuId()));
}
}
private GoblinSqbPerfGoodsVo fetchSqbGoodsByMallAndSpu(String mallSn, String sqbSpuId, String signature) {
if (StringUtils.isBlank(mallSn) || StringUtils.isBlank(sqbSpuId) || StringUtils.isBlank(signature)) {
return null;
}
List<MallProductsQueryData> products = goblinShouQianBaService.queryMallProducts(mallSn, signature);
if (CollectionUtils.isEmpty(products)) {
return null;
}
for (MallProductsQueryData product : products) {
if (sqbSpuId.equals(product.getSpuId())) {
GoblinSqbPerfGoodsVo vo = new GoblinSqbPerfGoodsVo();
vo.setMallSn(mallSn);
vo.setSignature(signature);
vo.setSpuId(product.getSpuId());
vo.setConverImages(product.getConverImages());
vo.setProductIntroduction(product.getProductIntroduction());
vo.setTitle(product.getTitle());
vo.setSkuResults(product.getSkuResults());
return vo;
}
}
return null;
}
private String validateSqbGoodsForSync(GoblinSqbPerfGoodsVo sqbGoods) {
if (sqbGoods == null) {
return "收钱吧商品为空";
}
if (StringUtils.isBlank(sqbGoods.getMallSn())) {
return "收钱吧商品校验失败: mallSn为空, spuId=" + StringUtils.defaultString(sqbGoods.getSpuId());
}
if (StringUtils.isBlank(sqbGoods.getSignature())) {
return "收钱吧商品校验失败: signature为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + StringUtils.defaultString(sqbGoods.getSpuId());
}
if (StringUtils.isBlank(sqbGoods.getSpuId())) {
return "收钱吧商品校验失败: spuId为空, mallSn=" + sqbGoods.getMallSn();
}
if (StringUtils.isBlank(sqbGoods.getTitle())) {
return "收钱吧商品校验失败: title为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId();
}
if (CollectionUtils.isEmpty(sqbGoods.getSkuResults())) {
return "收钱吧商品校验失败: skuResults为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId();
}
for (int i = 0; i < sqbGoods.getSkuResults().size(); i++) {
MallProductsQueryData.Sku sku = sqbGoods.getSkuResults().get(i);
if (sku == null) {
return "收钱吧商品校验失败: skuResults[" + i + "]为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId();
}
if (StringUtils.isBlank(sku.getSkuId())) {
return "收钱吧商品校验失败: skuId为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId() + ", index=" + i;
}
if (StringUtils.isBlank(sku.getSkuName())) {
return "收钱吧商品校验失败: skuName为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId() + ", skuId=" + sku.getSkuId();
}
if (sku.getPrice() == null) {
return "收钱吧商品校验失败: price为空, mallSn=" + sqbGoods.getMallSn() + ", spuId=" + sqbGoods.getSpuId() + ", skuId=" + sku.getSkuId();
}
}
return null;
}
private Map<String, Object> buildAddResult(int addSuccess, int alreadyExists, int addFail, List<String> invalidReasons) {
Map<String, Object> result = new HashMap<>();
result.put("addSuccess", addSuccess);
result.put("alreadyExists", alreadyExists);
result.put("addFail", addFail);
result.put("invalidCount", CollectionUtils.isEmpty(invalidReasons) ? 0 : invalidReasons.size());
result.put("invalidReasons", invalidReasons);
return result;
}
private Map<String, Object> buildEditResult(int editSuccess, int notAdded, int sqbNotFound, int editFail,
List<String> invalidReasons) {
Map<String, Object> result = new HashMap<>();
result.put("editSuccess", editSuccess);
result.put("notAdded", notAdded);
result.put("sqbNotFound", sqbNotFound);
result.put("editFail", editFail);
result.put("invalidCount", CollectionUtils.isEmpty(invalidReasons) ? 0 : invalidReasons.size());
result.put("invalidReasons", invalidReasons);
return result;
}
}
package com.liquidnet.service.goblin.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.*;
......@@ -12,6 +14,8 @@ import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.*;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper;
import com.liquidnet.service.goblin.enums.GoblinStoreConf;
import com.liquidnet.service.goblin.service.GoblinCouponService;
import com.liquidnet.service.goblin.service.GoblinFrontService;
......@@ -58,6 +62,8 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
GoblinMongoUtils mongoUtils;
@Autowired
GoblinCouponService goblinCouponService;
@Autowired
private GoblinSqbPerformanceGoodsMapper goblinSqbPerformanceGoodsMapper;
@Override
public ArrayList<GoblinFrontBannerVo> getListBanner() {
......@@ -330,6 +336,18 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
} else {
goblinGoodsSkuInfoDetailVo.setStockLess(false);
}
if (goblinGoodsInfoVo.getSpuType() == 33) {
// 是关联收钱吧商品
LambdaQueryWrapper<GoblinSqbPerformanceGoods> queryWrapper = new QueryWrapper<GoblinSqbPerformanceGoods>()
.lambda()
.eq(GoblinSqbPerformanceGoods::getSpuId, spuId)
.eq(GoblinSqbPerformanceGoods::getSkuId, sku)
.eq(GoblinSqbPerformanceGoods::getStatus, 1);
GoblinSqbPerformanceGoods goods = goblinSqbPerformanceGoodsMapper.selectOne(queryWrapper);
if (goods != null) {
goblinGoodsSkuInfoDetailVo.setPerformancesId(goods.getPerformancesId());
}
}
goblinGoodsSkuInfoDetailVo.setRestStock(stock);
list.add(goblinGoodsSkuInfoDetailVo);
}
......@@ -1056,6 +1074,136 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
// return null;
}
@Override
public GoblinFrontSelectGoodVo getPerformanceSelectGoods(String performancesId, int page, int pageSize) {
GoblinFrontSelectGoodVo respVo = GoblinFrontSelectGoodVo.getNew();
if (StringUtil.isBlank(performancesId)) {
respVo.setCount(0);
respVo.setGoblinGoodsInfoVoList(ObjectUtil.goblinGoodsInfoVoArrayList());
return respVo;
}
int safePage = Math.max(page, 0);
int safePageSize = pageSize <= 0 ? 40 : Math.min(pageSize, 100);
// 1. 尝试从 Redis 缓存获取全量已关联商品关系(Raw Relations)提高性能
List<GoblinSqbPerformanceGoods> relations = goblinRedisUtils.getSqbPerformanceGoodsListCache(performancesId);
if (relations == null) {
// 2. 缓存失效,查询数据库获取“原始关联关系”
relations = goblinSqbPerformanceGoodsMapper.selectList(
new LambdaQueryWrapper<GoblinSqbPerformanceGoods>()
.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getStatus, 1)
.orderByAsc(GoblinSqbPerformanceGoods::getSort, GoblinSqbPerformanceGoods::getMid));
if (relations == null) {
relations = new ArrayList<>();
}
// 3. 将关联关系(SPU 列表及其结算价/排序等)写入 Redis 缓存
goblinRedisUtils.setSqbPerformanceGoodsListCache(performancesId, relations);
}
// 4. 每次请求都实时校验商品状态(delFlg, shelvesStatus 等)
// 这样如果商品在 SPU 层面下架,虽然关联关系还在缓存,但这里会动态过滤掉
ArrayList<GoblinGoodsInfoVo> allGoods = ObjectUtil.goblinGoodsInfoVoArrayList();
if (!CollectionUtils.isEmpty(relations)) {
// 说明:relations 关联的是 SKU(同一 SPU 可能多条),下单侧需要完整 SKU 关系,所以 Redis 缓存必须保留全量。
// 前端“推荐商品列表”展示按 SPU 去重:取排序最靠前的一条关联作为该 SPU 的展示代表。
LinkedHashMap<String, GoblinSqbPerformanceGoods> uniqueSpuRelMap = new LinkedHashMap<>();
for (GoblinSqbPerformanceGoods rel : relations) {
if (rel == null || StringUtil.isBlank(rel.getSpuId())) {
continue;
}
if (!uniqueSpuRelMap.containsKey(rel.getSpuId())) {
uniqueSpuRelMap.put(rel.getSpuId(), rel);
}
}
for (GoblinSqbPerformanceGoods rel : uniqueSpuRelMap.values()) {
if (rel == null || StringUtil.isBlank(rel.getSpuId())) {
continue;
}
// 从 Redis SPU 缓存获取详情(SPU 详情由其他逻辑负责更新缓存,这里始终拿最新状态)
GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(rel.getSpuId());
if (goodsInfoVo == null) {
continue;
}
// 动态过滤:演出关联列表只展示在线、未删除、且上架状态为 3 的商品
if (!"0".equals(goodsInfoVo.getDelFlg())) {
continue;
}
if (!"0".equals(goodsInfoVo.getSpuAppear())) {
continue;
}
if (!"3".equals(goodsInfoVo.getShelvesStatus())) {
continue;
}
if (StringUtil.isNotBlank(goodsInfoVo.getMarketId())) {
continue;
}
GoblinSqbPerformanceGoodsInfoVo frontGoods = new GoblinSqbPerformanceGoodsInfoVo();
BeanUtils.copyProperties(goodsInfoVo, frontGoods);
BigDecimal normalPrice = resolveGoodsSellPrice(goodsInfoVo);
frontGoods.setSellPrice(normalPrice);
frontGoods.setPrice(normalPrice);
frontGoods.setSettlementPrice(rel.getSettlementPrice());
allGoods.add(frontGoods);
}
}
int total = allGoods.size();
respVo.setCount(total);
int start = safePage * safePageSize;
if (start >= total) {
respVo.setGoblinGoodsInfoVoList(allGoods);
return respVo;
}
int end = Math.min(start + safePageSize, total);
ArrayList<GoblinGoodsInfoVo> pageList = ObjectUtil.goblinGoodsInfoVoArrayList();
pageList.addAll(allGoods.subList(start, end));
respVo.setGoblinGoodsInfoVoList(pageList);
return respVo;
}
private BigDecimal resolveGoodsSellPrice(GoblinGoodsInfoVo goodsInfoVo) {
if (goodsInfoVo == null) {
return null;
}
if (goodsInfoVo.getSellPrice() != null) {
return goodsInfoVo.getSellPrice();
}
BigDecimal minSkuPrice = null;
if (!CollectionUtils.isEmpty(goodsInfoVo.getSkuIdList())) {
for (String skuId : goodsInfoVo.getSkuIdList()) {
GoblinGoodsSkuInfoVo skuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(skuId);
if (skuInfoVo == null) {
continue;
}
BigDecimal skuShowPrice = skuInfoVo.getSellPrice() != null ? skuInfoVo.getSellPrice()
: skuInfoVo.getPrice();
if (skuShowPrice == null) {
continue;
}
if (minSkuPrice == null || skuShowPrice.compareTo(minSkuPrice) < 0) {
minSkuPrice = skuShowPrice;
}
}
}
if (minSkuPrice != null) {
return minSkuPrice;
}
if (goodsInfoVo.getPriceGe() != null) {
return goodsInfoVo.getPriceGe();
}
return goodsInfoVo.getPriceLe();
}
@Override
public List<GoblinGoodsInfoVo> getGoodByMusicTagP(String musicTag, String performanceId) {
return goblinRedisUtils.getMusicTagList(musicTag, performanceId);
......
package com.liquidnet.service.goblin.service.impl;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import com.liquidnet.common.third.sqb.param.request.CouponQueryRequest;
import com.liquidnet.common.third.sqb.param.request.CouponRefundRequest;
import com.liquidnet.common.third.sqb.param.response.data.CouponQueryData;
import com.liquidnet.common.third.sqb.param.response.data.CouponRefundData;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.service.IGoblinSqbService;
import com.liquidnet.service.goblin.util.GoblinMongoUtils;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.GoblinSqbRedisUtils;
import com.liquidnet.service.goblin.util.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 收钱吧订单服务实现
*/
@Slf4j
@Service
public class GoblinSqbServiceImpl implements IGoblinSqbService {
@Autowired
private GoblinSqbRedisUtils goblinSqbRedisUtils;
@Autowired
private GoblinRedisUtils goblinRedisUtils;
@Autowired
private GoblinMongoUtils goblinMongoUtils;
@Autowired
private QueueUtils queueUtils;
@Autowired
private SqbBiz sqbBiz;
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// ================================ 获取券码 ================================
@Override
public ResponseDto<GoblinSqbCouponVo> queryCoupon(String userId, String 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("无权限访问该订单");
if (!Integer.valueOf(1).equals(orderVo.getStatus())) return ResponseDto.failure("订单未支付,无法获取券码");
// 幂等:已存在则直接返回
if (orderVo.getCouponQrCode() != null && !orderVo.getCouponQrCode().isEmpty()) {
GoblinSqbCouponVo couponVo = new GoblinSqbCouponVo();
couponVo.setCouponSn(orderVo.getCouponSn());
couponVo.setCouponQrCode(orderVo.getCouponQrCode());
couponVo.setCouponExpireTime(orderVo.getCouponExpireTime());
return ResponseDto.success(couponVo);
}
// 调用收钱吧查询券码
CouponQueryData couponData = sqbBiz.queryCoupon(orderVo.getSqbOrderSn(), orderVo.getSqbOrderSignature());
if (couponData == null) return ResponseDto.failure("获取券码失败");
// 更新订单 coupon 字段
String now = LocalDateTime.now().format(DTF);
orderVo.setCouponSn(couponData.getVoucherNo());
orderVo.setCouponQrCode(couponData.getUrl());
orderVo.setCouponExpireTime(couponData.getOperationTime());
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
// 券码信息保存在 Redis,MySQL和MongoDB商城基础模型无此字段,无需同步
GoblinSqbCouponVo couponVo = new GoblinSqbCouponVo();
couponVo.setCouponSn(couponData.getVoucherNo());
couponVo.setCouponQrCode(couponData.getUrl());
couponVo.setCouponExpireTime(couponData.getOperationTime());
log.info("[收钱吧券码] 获取成功,orderId={}", orderId);
return ResponseDto.success(couponVo);
}
// ================================ 申请退款 ================================
@Override
public ResponseDto<Boolean> refund(String userId, String orderId, String reason) {
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("无权限访问该订单");
if (!Integer.valueOf(1).equals(orderVo.getStatus())) return ResponseDto.failure("订单状态不可退款");
if (Integer.valueOf(1).equals(orderVo.getCouponUsedStatus())) return ResponseDto.failure("券码已核销,不可退款");
// 更新状态为退款中
String now = LocalDateTime.now().format(DTF);
orderVo.setStatus(4);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 4);
GoblinStoreOrderVo goblinStoreOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (null == goblinStoreOrderVo) {
log.error("[收钱吧] 现场商品退款失败, orderId: {}, userId: {}", orderId, userId);
return ResponseDto.failure("退款失败");
}
List<GoblinOrderSkuVo> orderSkuVos = new ArrayList<>();
for (String skuVoId : goblinStoreOrderVo.getOrderSkuVoIds()) {
GoblinOrderSkuVo goblinOrderSkuVo = goblinRedisUtils.getGoblinOrderSkuVo(skuVoId);
if (null != goblinOrderSkuVo) {
orderSkuVos.add(goblinOrderSkuVo);
}
}
try {
CouponRefundData refundData = sqbBiz.refundCoupon(orderVo.getSqbOrderSn(),
orderVo.getSqbOrderSignature(),
orderVo.getAmount(),
(byte) 2, // 2-按金额退款
buildRefundItem(orderSkuVos), // 需要构建退款商品
reason,
buildRequestId(IDGenerator.nextSnowId())
);
if (refundData == null) {
// 退款失败,回滚状态
orderVo.setStatus(1);
orderVo.setUpdatedAt(LocalDateTime.now().format(DTF));
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 1);
return ResponseDto.failure("退款失败,请稍后重试");
}
// 退款成功
orderVo.setRefundSn(refundData.getTicketsSn());
orderVo.setRefundSignature(refundData.getTicketSignature());
orderVo.setRefundReason(reason);
orderVo.setStatus(3);
orderVo.setUpdatedAt(LocalDateTime.now().format(DTF));
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity());
syncOrderStatus(orderId, 3);
log.info("[收钱吧退款] 退款成功,orderId={}", orderId);
return ResponseDto.success(Boolean.TRUE);
} catch (Exception e) {
log.error("[收钱吧退款] 退款异常,orderId={},回滚 status=1", orderId, e);
orderVo.setStatus(1);
orderVo.setUpdatedAt(LocalDateTime.now().format(DTF));
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 1);
return ResponseDto.failure("退款失败:" + e.getMessage());
}
}
private List<CouponRefundRequest.RefundItem> buildRefundItem(List<GoblinOrderSkuVo> orderSkuVos) {
List<CouponRefundRequest.RefundItem> refundItems = new ArrayList<>();
for (GoblinOrderSkuVo orderSkuVo : orderSkuVos) {
GoblinSqbGoodsExtVo sqbGoodsExt = goblinSqbRedisUtils.getSqbGoodsExt(orderSkuVo.getSpuId(), orderSkuVo.getSkuId());
if (sqbGoodsExt == null) {
log.error("[buildRefundItem] 收钱吧扩展参数为空, spuId: {}, skuId: {}", orderSkuVo.getSpuId(), orderSkuVo.getSkuId());
continue;
}
CouponRefundRequest.RefundItem item = new CouponRefundRequest.RefundItem();
item.setSpuId(sqbGoodsExt.getSqbSpuId());
item.setSkuId(sqbGoodsExt.getSqbSkuId());
item.setTitle(orderSkuVo.getSkuName());
item.setImg(orderSkuVo.getSkuImage());
item.setQuantity(String.valueOf(orderSkuVo.getNum()));
item.setType((byte) 0);
refundItems.add(item);
}
return refundItems;
}
// ================================ 同步核销状态 ================================
@Override
public ResponseDto<Boolean> syncCouponStatus(String userId, String 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("无权限访问该订单");
boolean couponUsed = sqbBiz.syncCouponStatus(sqbBiz.getSqbConfig().getMerchantId(),
Collections.singletonList(orderVo.getCouponSn()),
buildRequestId(IDGenerator.nextSnowId()),
"terminal_001",
(byte) 1);
if (couponUsed) {
String now = LocalDateTime.now().format(DTF);
orderVo.setStatus(2);
orderVo.setCouponUsedStatus(1);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 2);
log.info("[收钱吧核销同步] 核销成功,orderId={}", orderId);
}
return ResponseDto.success(couponUsed);
}
// ================================ 自动退款(定时任务) ================================
@Override
public ResponseDto<String> autoRefundByPerformance(String performancesId) {
log.info("[收钱吧自动退款] 开始处理演出 performancesId={}", performancesId);
// 查询该演出下 status=1(已支付)的订单(MySQL)
// TODO: 通过 GoblinSqbOrderMapper 查询(需注入后解注释)
// LambdaQueryWrapper<GoblinSqbOrder> query = new LambdaQueryWrapper<>();
// query.eq(GoblinSqbOrder::getPerformancesId, performancesId).eq(GoblinSqbOrder::getStatus, 1);
// List<GoblinSqbOrder> orders = goblinSqbOrderMapper.selectList(query);
int successCount = 0;
int failCount = 0;
// TODO: 取消注释并接入 GoblinSqbOrderMapper 后启用
// for (GoblinSqbOrder order : orders) {
// try {
// ResponseDto<Boolean> result = refund(order.getUserId(), order.getOrderId());
// if (result != null && result.isSuccess()) {
// successCount++;
// } else {
// log.warn("[收钱吧自动退款] 订单退款失败 orderId={}", order.getOrderId());
// failCount++;
// }
// } catch (Exception e) {
// log.error("[收钱吧自动退款] 订单退款异常 orderId={}", order.getOrderId(), e);
// failCount++;
// }
// }
String summary = String.format("演出 %s 自动退款完成:成功 %d 笔,失败 %d 笔", performancesId, successCount, failCount);
log.info("[收钱吧自动退款] {}", summary);
return ResponseDto.success(summary);
}
// ================================ 订单列表 ================================
@Override
public ResponseDto<List<GoblinSqbOrderDetailVo>> getOrderList(String userId) {
log.info("[收钱吧订单列表] userId={}", userId);
List<String> orderIds = goblinSqbRedisUtils.getSqbOrderList(userId);
if (CollectionUtils.isEmpty(orderIds)) {
return ResponseDto.success(Collections.emptyList());
}
List<GoblinSqbOrderDetailVo> result = new ArrayList<>();
for (String orderId : orderIds) {
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo == null) continue;
// 取第一个 orderSkuId 判断 skuType
List<String> orderSkuVoIds = storeOrderVo.getOrderSkuVoIds();
if (CollectionUtils.isEmpty(orderSkuVoIds)) continue;
GoblinOrderSkuVo skuVo = goblinRedisUtils.getGoblinOrderSkuVo(orderSkuVoIds.get(0));
if (skuVo == null) continue;
// 过滤:只处理 skuType=33 的收钱吧订单
if (!Integer.valueOf(33).equals(skuVo.getSkuType())) continue;
GoblinSqbOrderVo sqbOrderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
result.add(buildDetailVo(storeOrderVo, skuVo, sqbOrderVo));
}
// 按创建时间倒序排列
result.sort((a, b) -> {
if (a.getCreatedAt() == null) return 1;
if (b.getCreatedAt() == null) return -1;
return b.getCreatedAt().compareTo(a.getCreatedAt());
});
log.info("[收钱吧订单列表] userId={}, 共 {} 条", userId, result.size());
return ResponseDto.success(result);
}
// ================================ 订单详情 ================================
@Override
public ResponseDto<GoblinSqbOrderDetailVo> getOrderDetail(String userId, String orderId) {
log.info("[收钱吧订单详情] userId={}, orderId={}", userId, orderId);
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo == null) return ResponseDto.failure("订单不存在");
if (!userId.equals(storeOrderVo.getUserId())) return ResponseDto.failure("无权限访问该订单");
List<String> orderSkuVoIds = storeOrderVo.getOrderSkuVoIds();
if (CollectionUtils.isEmpty(orderSkuVoIds)) return ResponseDto.failure("订单数据异常");
GoblinOrderSkuVo skuVo = goblinRedisUtils.getGoblinOrderSkuVo(orderSkuVoIds.get(0));
if (skuVo == null) return ResponseDto.failure("订单商品数据异常");
// 校验是收钱吧订单
if (!Integer.valueOf(33).equals(skuVo.getSkuType())) {
return ResponseDto.failure("该订单非收钱吧订单");
}
GoblinSqbOrderVo sqbOrderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
GoblinSqbOrderDetailVo detailVo = buildDetailVo(storeOrderVo, skuVo, sqbOrderVo);
log.info("[收钱吧订单详情] 查询成功,orderId={}", orderId);
return ResponseDto.success(detailVo);
}
// ================================ 私有辅助方法 ================================
private GoblinSqbOrderDetailVo buildDetailVo(GoblinStoreOrderVo storeOrderVo,
GoblinOrderSkuVo skuVo,
GoblinSqbOrderVo sqbOrderVo) {
GoblinSqbOrderDetailVo detailVo = new GoblinSqbOrderDetailVo();
// 来自 GoblinStoreOrderVo
detailVo.setOrderId(storeOrderVo.getOrderId());
detailVo.setOrderCode(storeOrderVo.getOrderCode());
detailVo.setStatus(storeOrderVo.getStatus());
detailVo.setPriceActual(storeOrderVo.getPriceActual());
detailVo.setCreatedAt(storeOrderVo.getCreatedAt());
detailVo.setPayTime(storeOrderVo.getPayTime());
// 来自 GoblinOrderSkuVo
detailVo.setSpuId(skuVo.getSpuId());
detailVo.setSpuName(skuVo.getSpuName());
detailVo.setSkuId(skuVo.getSkuId());
detailVo.setSkuName(skuVo.getSkuName());
detailVo.setSkuImage(skuVo.getSkuImage());
detailVo.setQuantity(skuVo.getNum());
// 来自 GoblinSqbOrderVo
if (sqbOrderVo != null) {
detailVo.setSqbStatus(sqbOrderVo.getStatus());
detailVo.setPerformancesId(sqbOrderVo.getPerformancesId());
detailVo.setSqbAcquiringSn(sqbOrderVo.getSqbAcquiringSn());
detailVo.setCouponSn(sqbOrderVo.getCouponSn());
detailVo.setCouponQrCode(sqbOrderVo.getCouponQrCode());
detailVo.setCouponExpireTime(sqbOrderVo.getCouponExpireTime());
detailVo.setCouponUsedStatus(sqbOrderVo.getCouponUsedStatus());
}
return detailVo;
}
/**
* 构建收钱吧请求唯一ID,规则:AppCode(4位)+ id,总长不超过40位。
*
* @param id 业务唯一标识(如雪花ID)
* @return 不超过40位的请求ID字符串
*/
private String buildRequestId(String id) {
String appCode = sqbBiz.getSqbConfig().getAppCode();
if (appCode == null) appCode = "";
String raw = appCode + id;
return raw.length() > 40 ? raw.substring(0, 40) : raw;
}
/**
* 同步正在商品订单状态
*
* @param orderId
* @param status
*/
private void syncOrderStatus(String orderId, int status) {
final int zhengzaiStatus = sqbOrderStatusConvert(status);
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo == null) return;
String now = LocalDateTime.now().format(DTF);
storeOrderVo.setStatus(zhengzaiStatus);
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) { // 支付成功
storeOrderVo.setPayTime(now);
}
// 1. 同步 Redis
goblinRedisUtils.setGoblinOrder(orderId, storeOrderVo);
// 2. 同步 MongoDB
goblinMongoUtils.updateGoblinStoreOrderVo(orderId, storeOrderVo);
// 3. 同步 MySQL (复用下单 MQ 发送 Update 语句)
String sql;
Object[] data;
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
sql = "UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ?";
data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId};
} else {
sql = "UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ?";
data = new Object[]{zhengzaiStatus, now, orderId};
}
java.util.LinkedList<String> sqls = new java.util.LinkedList<>();
sqls.add(sql);
java.util.LinkedList<Object[]> sqlData = new java.util.LinkedList<>();
sqlData.add(data);
if (queueUtils != null) {
queueUtils.sendMsgByRedis(com.liquidnet.service.base.constant.MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
com.liquidnet.service.base.SqlMapping.gets(sqls, sqlData));
}
}
/**
* goblin_sqb_order表status 转换成 goblin_store_order表对应status
*
* @param sqbOrderStatus
* @return
*/
private int sqbOrderStatusConvert(Integer sqbOrderStatus) {
if (null == sqbOrderStatus) {
return -1;
}
switch (sqbOrderStatus) {
case 0:
// 0-待支付 -> 对应枚举 ORDER_STATUS_0
return GoblinStatusConst.Status.ORDER_STATUS_0.getValue();
case 1:
// 1-已支付 -> 对应枚举 ORDER_STATUS_2 (代发货)
return GoblinStatusConst.Status.ORDER_STATUS_2.getValue();
case 2:
// 2-已核销 -> 对应枚举 ORDER_STATUS_4 (已完成)
return GoblinStatusConst.Status.ORDER_STATUS_4.getValue();
case 3:
// 3-已退款 -> 对应枚举 ORDER_STATUS_6 (退款通过)
return GoblinStatusConst.Status.ORDER_STATUS_6.getValue();
case 4:
// 4-退款中 -> 对应枚举 ORDER_STATUS_61 (发起-退款)
return GoblinStatusConst.Status.ORDER_STATUS_61.getValue();
case 5:
// 5-已取消 -> 对应枚举 ORDER_STATUS_5
return GoblinStatusConst.Status.ORDER_STATUS_5.getValue();
case 9:
// 9-失败 -> 这里归类为 ORDER_STATUS_5 (取消/终态),如有特殊需求可调整
return sqbOrderStatus;
default:
return sqbOrderStatus;
}
}
}
......@@ -59,7 +59,8 @@ public class GoblinStoreMgtGoodsImportService {
*/
private static final String LAST_SKU_SPEC_VALUE_STR = "%#V1%#V2%#V3%#V4%#V5";
public String goodsInformationDataAnalysisProcessing(MultipartFile file, String uid, String storeId) throws IOException {
public String goodsInformationDataAnalysisProcessing(MultipartFile file, String uid, String storeId)
throws IOException {
String originalFilename = file.getOriginalFilename();
AnalysisEventListener<GoblinGoodsImportDto> analysisEventListener = new AnalysisEventListener<GoblinGoodsImportDto>() {
List<GoblinGoodsSkuInfoVo> goodsSkuInfoVos;
......@@ -74,7 +75,8 @@ public class GoblinStoreMgtGoodsImportService {
if (exception instanceof ExcelDataConvertException) {
Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex();
Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex();
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行,第%s列数据格式有误】", rowIndex + 1, columnIndex + 1));
throw new LiquidnetServiceException("-1",
String.format("数据内容不规范【第%s行,第%s列数据格式有误】", rowIndex + 1, columnIndex + 1));
}
super.onException(exception, context);
}
......@@ -83,16 +85,18 @@ public class GoblinStoreMgtGoodsImportService {
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
Integer approximateTotalRowNumber = context.readSheetHolder().getApproximateTotalRowNumber();
if (approximateTotalRowNumber > 501) {
log.warn("店铺商品管理:批量导入数据:异常[UID={},storeId={},totalRowNumber={}]", uid, storeId, approximateTotalRowNumber);
log.warn("店铺商品管理:批量导入数据:异常[UID={},storeId={},totalRowNumber={}]", uid, storeId,
approximateTotalRowNumber);
throw new LiquidnetServiceException("-1", "超出总行数限制500");
} else if (approximateTotalRowNumber <= 1) {
throw new LiquidnetServiceException("-1", "导入文件不能为空");
}
log.info("DT-IN-BEGIN:[storeId={},uid={},fileName={},totalRow={}]", uid, storeId, originalFilename, approximateTotalRowNumber);
log.info("DT-IN-BEGIN:[storeId={},uid={},fileName={},totalRow={}]", uid, storeId, originalFilename,
approximateTotalRowNumber);
goodsSkuInfoVos = ObjectUtil.getGoblinGoodsSkuInfoVoArrayList();
goodsInfoVos = ObjectUtil.goblinGoodsInfoVoArrayList();
// skuBarCodeTmpList = CollectionUtil.arrayListString();
// skuBarCodeTmpList = CollectionUtil.arrayListString();
dtoSpuSpecNameTmpList = CollectionUtil.arrayListString();
dtoSkuSpecValueTmpList = CollectionUtil.arrayListString();
now = LocalDateTime.now();
......@@ -106,10 +110,12 @@ public class GoblinStoreMgtGoodsImportService {
}
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
GoblinGoodsInfoVo lastGoodsInfoVo = CollectionUtils.isEmpty(goodsInfoVos) ? null : goodsInfoVos.get(goodsInfoVos.size() - 1);
GoblinGoodsInfoVo lastGoodsInfoVo = CollectionUtils.isEmpty(goodsInfoVos) ? null
: goodsInfoVos.get(goodsInfoVos.size() - 1);
boolean tobeNextSpuFlg = null == lastGoodsInfoVo || !lastGoodsInfoVo.getName().equals(dto.getSpuName());
goodsInformationDataAnalysisProcessingValid(dto, rowIndex + 1, tobeNextSpuFlg, dtoSpuSpecNameTmpList, dtoSkuSpecValueTmpList, dtoSkuBarCodeTmpList);
goodsInformationDataAnalysisProcessingValid(dto, rowIndex + 1, tobeNextSpuFlg, dtoSpuSpecNameTmpList,
dtoSkuSpecValueTmpList, dtoSkuBarCodeTmpList);
if (tobeNextSpuFlg) {
lastGoodsInfoVo = goodsInformationDataAnalysisProcessingForSpu(dto, uid, storeId, now);
......@@ -119,7 +125,8 @@ public class GoblinStoreMgtGoodsImportService {
goodsInfoVos.add(lastGoodsInfoVo);
}
GoblinGoodsSkuInfoVo lastGoodsSkuInfoVo = goodsInformationDataAnalysisProcessingForSku(dto, lastGoodsInfoVo, tobeNextSpuFlg);
GoblinGoodsSkuInfoVo lastGoodsSkuInfoVo = goodsInformationDataAnalysisProcessingForSku(dto,
lastGoodsInfoVo, tobeNextSpuFlg);
if (null != lastGoodsSkuInfoVo) {
goodsSkuInfoVos.add(lastGoodsSkuInfoVo);
} else {
......@@ -137,16 +144,21 @@ public class GoblinStoreMgtGoodsImportService {
log.debug("dt3-2:{}", JsonUtils.toJson(goodsSkuInfoVos));
}
if (!CollectionUtils.isEmpty(goodsInfoVos)) {
// List<String> existGoodsSkuNoList = goblinMongoUtils.existGoodsSkuNoBySkuNoList(storeId, skuBarCodeTmpList);
// if (!CollectionUtils.isEmpty(existGoodsSkuNoList)) {
// if (existGoodsSkuNoList.size() > 3) {
// throw new LiquidnetServiceException("-1", String.format("规格条码与已添加商品条码重复,重复条码如下: %s,...", StringUtils.join(existGoodsSkuNoList.subList(0, 3), ",")));
// }
// throw new LiquidnetServiceException("-1", String.format("规格条码与已添加商品条码重复,重复条码如下: %s", StringUtils.join(existGoodsSkuNoList, ",")));
// }
log.info("DT-IN-ToMDB:[storeId={},uid={},fileName={},spuCount={},skuCount={}]", uid, storeId, originalFilename, goodsInfoVos.size(), goodsSkuInfoVos.size());
// List<String> existGoodsSkuNoList =
// goblinMongoUtils.existGoodsSkuNoBySkuNoList(storeId, skuBarCodeTmpList);
// if (!CollectionUtils.isEmpty(existGoodsSkuNoList)) {
// if (existGoodsSkuNoList.size() > 3) {
// throw new LiquidnetServiceException("-1",
// String.format("规格条码与已添加商品条码重复,重复条码如下: %s,...",
// StringUtils.join(existGoodsSkuNoList.subList(0, 3), ",")));
// }
// throw new LiquidnetServiceException("-1",
// String.format("规格条码与已添加商品条码重复,重复条码如下: %s",
// StringUtils.join(existGoodsSkuNoList, ",")));
// }
log.info("DT-IN-ToMDB:[storeId={},uid={},fileName={},spuCount={},skuCount={}]", uid, storeId,
originalFilename, goodsInfoVos.size(), goodsSkuInfoVos.size());
goblinMongoUtils.insertMgtGoodsInfoVos(goodsInfoVos);
goblinMongoUtils.insertMgtGoodsSkuInfoVos(goodsSkuInfoVos);
......@@ -160,31 +172,40 @@ public class GoblinStoreMgtGoodsImportService {
LinkedList<Object[]> initGoodsSpuSpecValueObjs = CollectionUtil.linkedListObjectArr();
for (int i = 0, size = goodsInfoVos.size(); i < size; i++) {
GoblinGoodsInfoVo goodsInfoVo = goodsInfoVos.get(i);
initGoodsObjs.add(new Object[]{goodsInfoVo.getSpuId(), goodsInfoVo.getSpuNo(), goodsInfoVo.getSpuBarCode(), goodsInfoVo.getName(), goodsInfoVo.getSubtitle(),
goodsInfoVo.getSellPrice(), goodsInfoVo.getPriceGe(), goodsInfoVo.getPriceLe(), goodsInfoVo.getIntro(), goodsInfoVo.getDetails(),
goodsInfoVo.getCoverPic(), goodsInfoVo.getVideo(), goodsInfoVo.getSpecMode(), goodsInfoVo.getStoreId(), goodsInfoVo.getCateFid(),
goodsInfoVo.getCateSid(), goodsInfoVo.getCateTid(), goodsInfoVo.getStoreCateFid(), goodsInfoVo.getStoreCateSid(), goodsInfoVo.getStoreCateTid(),
goodsInfoVo.getBrandId(), goodsInfoVo.getShelvesHandle(), goodsInfoVo.getShelvesTime(), goodsInfoVo.getSpuValidity(), goodsInfoVo.getVirtualFlg(),
goodsInfoVo.getStatus(), goodsInfoVo.getShelvesStatus(), goodsInfoVo.getSpuAppear(), goodsInfoVo.getShelvesAt(), goodsInfoVo.getCreatedBy(),
goodsInfoVo.getCreatedAt(), goodsInfoVo.getLogisticsTemplate()}
);
initGoodsObjs.add(new Object[] { goodsInfoVo.getSpuId(), goodsInfoVo.getSpuNo(),
goodsInfoVo.getSpuBarCode(), goodsInfoVo.getName(), goodsInfoVo.getSubtitle(),
goodsInfoVo.getSellPrice(), goodsInfoVo.getPriceGe(), goodsInfoVo.getPriceLe(),
goodsInfoVo.getIntro(), goodsInfoVo.getDetails(),
goodsInfoVo.getCoverPic(), goodsInfoVo.getVideo(), goodsInfoVo.getSpecMode(),
goodsInfoVo.getStoreId(), goodsInfoVo.getCateFid(),
goodsInfoVo.getCateSid(), goodsInfoVo.getCateTid(), goodsInfoVo.getStoreCateFid(),
goodsInfoVo.getStoreCateSid(), goodsInfoVo.getStoreCateTid(),
goodsInfoVo.getBrandId(), goodsInfoVo.getShelvesHandle(), goodsInfoVo.getShelvesTime(),
goodsInfoVo.getSpuValidity(), goodsInfoVo.getVirtualFlg(),
goodsInfoVo.getStatus(), goodsInfoVo.getShelvesStatus(), goodsInfoVo.getSpuAppear(),
goodsInfoVo.getShelvesAt(), goodsInfoVo.getCreatedBy(),
goodsInfoVo.getCreatedAt(), goodsInfoVo.getLogisticsTemplate() });
if (!CollectionUtils.isEmpty(goodsInfoVo.getImageList())) {
goodsInfoVo.getImageList().forEach(imageUrl -> initGoodsImageObjs.add(new Object[]{goodsInfoVo.getSpuId(), imageUrl}));
goodsInfoVo.getImageList().forEach(imageUrl -> initGoodsImageObjs
.add(new Object[] { goodsInfoVo.getSpuId(), imageUrl }));
}
goodsInfoVo.getSpecVoList().forEach(specVo -> {
specVo.getSpecValues().forEach(specValues -> {
initGoodsSpuSpecValueObjs.add(new Object[]{goodsInfoVo.getSpuId(), specVo.getSpecName(), specValues.getSpecVname(), specValues.getSpecVsort()});
initGoodsSpuSpecValueObjs.add(new Object[] { goodsInfoVo.getSpuId(),
specVo.getSpecName(), specValues.getSpecVname(), specValues.getSpecVsort() });
});
});
if (initGoodsObjs.size() == BATCH_COUNT_SPU_SQL_TO_QUEUE) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls, initGoodsObjs, initGoodsImageObjs, initGoodsSpuSpecValueObjs));
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls,
initGoodsObjs, initGoodsImageObjs, initGoodsSpuSpecValueObjs));
initGoodsObjs.clear();
initGoodsImageObjs.clear();
initGoodsSpuSpecValueObjs.clear();
}
}
if (initGoodsObjs.size() > 0) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls, initGoodsObjs, initGoodsImageObjs, initGoodsSpuSpecValueObjs));
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls,
initGoodsObjs, initGoodsImageObjs, initGoodsSpuSpecValueObjs));
}
log.info("DT-IN-ToSQL2:[storeId={},uid={},fileName={}]", uid, storeId, originalFilename);
toMqSqls.clear();
......@@ -194,52 +215,77 @@ public class GoblinStoreMgtGoodsImportService {
LinkedList<Object[]> initGoodsSkuSpecValueObjs = CollectionUtil.linkedListObjectArr();
goodsSkuInfoVos.forEach(goodsSkuInfoVo -> {
goblinRedisUtils.setSkuStock(null, goodsSkuInfoVo.getSkuId(), goodsSkuInfoVo.getSkuStock());
initGoodsSkuObjs.add(new Object[]{goodsSkuInfoVo.getSkuId(), goodsSkuInfoVo.getSpuId(), goodsSkuInfoVo.getSkuNo(), goodsSkuInfoVo.getSkuBarCode(), goodsSkuInfoVo.getName(),
goodsSkuInfoVo.getSubtitle(), goodsSkuInfoVo.getSellPrice(), goodsSkuInfoVo.getSkuPic(), goodsSkuInfoVo.getSkuIsbn(), goodsSkuInfoVo.getStock(),
goodsSkuInfoVo.getSkuStock(), goodsSkuInfoVo.getWarningStock(), goodsSkuInfoVo.getPrice(), goodsSkuInfoVo.getPriceMember(), goodsSkuInfoVo.getWeight(),
goodsSkuInfoVo.getBuyFactor(), goodsSkuInfoVo.getBuyRoster(), goodsSkuInfoVo.getBuyLimit(), goodsSkuInfoVo.getStoreId(), goodsSkuInfoVo.getSkuValidity(),
goodsSkuInfoVo.getVirtualFlg(),goodsSkuInfoVo.getStatus(), goodsSkuInfoVo.getShelvesStatus(), goodsSkuInfoVo.getSkuAppear(), goodsSkuInfoVo.getShelvesAt(),
initGoodsSkuObjs.add(new Object[] { goodsSkuInfoVo.getSkuId(), goodsSkuInfoVo.getSpuId(),
goodsSkuInfoVo.getSkuNo(), goodsSkuInfoVo.getSkuBarCode(), goodsSkuInfoVo.getName(),
goodsSkuInfoVo.getSubtitle(), goodsSkuInfoVo.getSellPrice(), goodsSkuInfoVo.getSkuPic(),
goodsSkuInfoVo.getSkuIsbn(), goodsSkuInfoVo.getStock(),
goodsSkuInfoVo.getSkuStock(), goodsSkuInfoVo.getWarningStock(),
goodsSkuInfoVo.getPrice(), goodsSkuInfoVo.getPriceMember(), goodsSkuInfoVo.getWeight(),
goodsSkuInfoVo.getBuyFactor(), goodsSkuInfoVo.getBuyRoster(),
goodsSkuInfoVo.getBuyLimit(), goodsSkuInfoVo.getStoreId(),
goodsSkuInfoVo.getSkuValidity(),
goodsSkuInfoVo.getVirtualFlg(), goodsSkuInfoVo.getStatus(),
goodsSkuInfoVo.getShelvesStatus(), goodsSkuInfoVo.getSkuAppear(),
goodsSkuInfoVo.getShelvesAt(),
uid, goodsSkuInfoVo.getCreatedAt(), goodsSkuInfoVo.getLogisticsTemplate()
});
goodsSkuInfoVo.getSkuSpecList().forEach(skuSpecDto -> initGoodsSkuSpecValueObjs.add(new Object[]{
goodsSkuInfoVo.getSpuId(), goodsSkuInfoVo.getSkuId(), skuSpecDto.getSpecName(), skuSpecDto.getSpecVname()
}));
goodsSkuInfoVo.getSkuSpecList()
.forEach(skuSpecDto -> initGoodsSkuSpecValueObjs.add(new Object[] {
goodsSkuInfoVo.getSpuId(), goodsSkuInfoVo.getSkuId(), skuSpecDto.getSpecName(),
skuSpecDto.getSpecVname()
}));
if (initGoodsSkuObjs.size() == BATCH_COUNT_SKU_SQL_TO_QUEUE) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls, initGoodsSkuObjs, initGoodsSkuSpecValueObjs));
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
SqlMapping.gets(toMqSqls, initGoodsSkuObjs, initGoodsSkuSpecValueObjs));
initGoodsSkuObjs.clear();
initGoodsSkuSpecValueObjs.clear();
}
});
if (initGoodsSkuObjs.size() > 0) {
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(), SqlMapping.gets(toMqSqls, initGoodsSkuObjs, initGoodsSkuSpecValueObjs));
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
SqlMapping.gets(toMqSqls, initGoodsSkuObjs, initGoodsSkuSpecValueObjs));
}
}
}
};
List<GoblinGoodsImportDto> readDtoList = EasyExcel.read(file.getInputStream(), GoblinGoodsImportDto.class, analysisEventListener).sheet(0).doReadSync();
List<GoblinGoodsImportDto> readDtoList = EasyExcel
.read(file.getInputStream(), GoblinGoodsImportDto.class, analysisEventListener).sheet(0).doReadSync();
if (CollectionUtils.isEmpty(readDtoList)) {
throw new LiquidnetServiceException("-1", "导入文件不能为空");
}
long failureRows = readDtoList.stream().filter(r -> StringUtils.isNotEmpty(r.getFailureReason())).count();
String analysisResultMsg = String.format("导入成功%s条数据,导入失败%s条数据", readDtoList.size() - failureRows, failureRows);
log.info("DT-IN-END:[storeId={},uid={},fileName={},readResult:{}]", uid, storeId, originalFilename, analysisResultMsg);
log.info("DT-IN-END:[storeId={},uid={},fileName={},readResult:{}]", uid, storeId, originalFilename,
analysisResultMsg);
return analysisResultMsg;
}
private void goodsInformationDataAnalysisProcessingValid(GoblinGoodsImportDto dto, Integer rowNum, boolean tobeNextSpuFlg,
List<String> dtoSpuSpecNameTmpList, List<String> dtoSkuSpecValueTmpList, List<String> dtoSkuBarCodeTmpList) {
/* 商品编码校验|------------------------------------------------------------------------------ */
if (StringUtils.isNotEmpty(dto.getSpuCode()) && !Pattern.matches(LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, dto.getSpuCode())) {
private void goodsInformationDataAnalysisProcessingValid(GoblinGoodsImportDto dto, Integer rowNum,
boolean tobeNextSpuFlg,
List<String> dtoSpuSpecNameTmpList, List<String> dtoSkuSpecValueTmpList,
List<String> dtoSkuBarCodeTmpList) {
/*
* 商品编码校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isNotEmpty(dto.getSpuCode())
&& !Pattern.matches(LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, dto.getSpuCode())) {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品编码有误】", rowNum));
}
/* 商品名称校验|------------------------------------------------------------------------------ */
/*
* 商品名称校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isEmpty(dto.getSpuName()) || dto.getSpuName().length() > 30) {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品名称有误】", rowNum));
}
/* 商品图片校验|------------------------------------------------------------------------------ */
/*
* 商品图片校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isNotEmpty(dto.getSpuImgs())) {
if (dto.getSpuImgs().startsWith("【图片链接】")) {
String[] spuImgsArr = dto.getSpuImgs().replace("【图片链接】", "").replace(";", ";").split(";");
......@@ -259,7 +305,10 @@ public class GoblinStoreMgtGoodsImportService {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品图片格式有误】", rowNum));
}
}
/* 商品规格校验|------------------------------------------------------------------------------ */
/*
* 商品规格校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isEmpty(dto.getSkuSpec())) {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品规格信息缺失】", rowNum));
}
......@@ -280,7 +329,8 @@ public class GoblinStoreMgtGoodsImportService {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品规格信息无效】", rowNum));
}
String[] lastSpecArr = lastSkuSpecStr.split(":");
if (ArrayUtils.isEmpty(lastSpecArr) || lastSpecArr.length != 2 || lastSpecArr[0].length() > 5 || lastSpecArr[1].length() > 40) {
if (ArrayUtils.isEmpty(lastSpecArr) || lastSpecArr.length != 2 || lastSpecArr[0].length() > 5
|| lastSpecArr[1].length() > 40) {
// 分割出的规格(项:值)内容不规范
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品规格信息有误】", rowNum));
}
......@@ -309,19 +359,33 @@ public class GoblinStoreMgtGoodsImportService {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行商品规格信息重复】", rowNum));
}
dto.setSkuSpecDtos(skuSpecDtos);
/* 规格编码校验|------------------------------------------------------------------------------ */
if (StringUtils.isNotEmpty(dto.getSkuCode()) && !Pattern.matches(LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, dto.getSkuCode())) {
/*
* 规格编码校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isNotEmpty(dto.getSkuCode())
&& !Pattern.matches(LnsRegex.Valid.ALPHABET_NUMBER_UNDER_50, dto.getSkuCode())) {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行规格编码格式有误】", rowNum));
}
/* 价格校验|------------------------------------------------------------------------------ */
if (null == dto.getPrice() || dto.getPrice().compareTo(BigDecimal.valueOf(0.01)) < 0 || dto.getPrice().compareTo(BigDecimal.valueOf(9999999)) > 0) {
/*
* 价格校验|------------------------------------------------------------------------
* ------
*/
if (null == dto.getPrice() || dto.getPrice().compareTo(BigDecimal.valueOf(0.01)) < 0
|| dto.getPrice().compareTo(BigDecimal.valueOf(9999999)) > 0) {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行价格信息有误】", rowNum));
}
/* 库存校验|------------------------------------------------------------------------------ */
/*
* 库存校验|------------------------------------------------------------------------
* ------
*/
if (null == dto.getStock() || dto.getStock() < 0 || dto.getStock() > 9999999) {// 数据不规范停止解析并提示用户
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行库存信息有误】", rowNum));
}
/* 规格图片校验|------------------------------------------------------------------------------ */
/*
* 规格图片校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isNotEmpty(dto.getSkuImg())) {
if (dto.getSkuImg().startsWith("【图片链接】")) {
String[] skuImgArr = dto.getSkuImg().replace("【图片链接】", "").replace(";", ";").split(";");
......@@ -334,63 +398,79 @@ public class GoblinStoreMgtGoodsImportService {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行规格图片格式有误】", rowNum));
}
}
/* 规格条码校验|------------------------------------------------------------------------------ */
/*
* 规格条码校验|----------------------------------------------------------------------
* --------
*/
if (StringUtils.isNotEmpty(dto.getSkuBarCode())) {
if (Pattern.matches(LnsRegex.Valid.ALPHABET_NUMBER_32, dto.getSkuBarCode())) {
// if (dtoSkuBarCodeTmpList.contains(dto.getSkuBarCode())) {
// throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行表格内规格条码重复】", rowNum));
// }
// dtoSkuBarCodeTmpList.add(dto.getSkuBarCode());
// if (dtoSkuBarCodeTmpList.contains(dto.getSkuBarCode())) {
// throw new LiquidnetServiceException("-1",
// String.format("数据内容不规范【第%s行表格内规格条码重复】", rowNum));
// }
// dtoSkuBarCodeTmpList.add(dto.getSkuBarCode());
} else {
throw new LiquidnetServiceException("-1", String.format("数据内容不规范【第%s行规格条码格式有误】", rowNum));
}
}
/* 校验|------------------------------------------------------------------------------ */
/* 校验|------------------------------------------------------------------------------ */
/* 校验|------------------------------------------------------------------------------ */
/*
* 校验|--------------------------------------------------------------------------
* ----
*/
/*
* 校验|--------------------------------------------------------------------------
* ----
*/
/*
* 校验|--------------------------------------------------------------------------
* ----
*/
}
private GoblinGoodsInfoVo goodsInformationDataAnalysisProcessingForSpu(GoblinGoodsImportDto dt, String uid, String storeId, LocalDateTime now) {
private GoblinGoodsInfoVo goodsInformationDataAnalysisProcessingForSpu(GoblinGoodsImportDto dt, String uid,
String storeId, LocalDateTime now) {
GoblinGoodsInfoVo lastGoodsInfoVo = GoblinGoodsInfoVo.getNew();
lastGoodsInfoVo.setName(dt.getSpuName());//*
lastGoodsInfoVo.setSpuId(IDGenerator.nextSnowId());//*
lastGoodsInfoVo.setName(dt.getSpuName());// *
lastGoodsInfoVo.setSpuId(IDGenerator.nextSnowId());// *
if (StringUtils.isNotEmpty(dt.getSpuCode())) {
lastGoodsInfoVo.setSpuNo(dt.getSpuCode());
} else {
lastGoodsInfoVo.setSpuNo(lastGoodsInfoVo.getSpuId());
}
// if (StringUtils.isNotEmpty(dt.getSpuBarCode())) {
// lastGoodsInfoVo.setSpuBarCode(dt.getSpuBarCode());
// } else {
// lastGoodsInfoVo.setSpuBarCode(lastGoodsInfoVo.getSpuId());
// }
// if (StringUtils.isNotEmpty(dt.getSpuBarCode())) {
// lastGoodsInfoVo.setSpuBarCode(dt.getSpuBarCode());
// } else {
// lastGoodsInfoVo.setSpuBarCode(lastGoodsInfoVo.getSpuId());
// }
if (!CollectionUtils.isEmpty(dt.getSpuImgList())) {
lastGoodsInfoVo.setImageList(dt.getSpuImgList());
lastGoodsInfoVo.setCoverPic(lastGoodsInfoVo.getImageList().get(0));
}
lastGoodsInfoVo.setDetails(lastGoodsInfoVo.getName());
lastGoodsInfoVo.setSpuType(0);//*
lastGoodsInfoVo.setSpecMode("1");//*
lastGoodsInfoVo.setSpuType(0);// *
lastGoodsInfoVo.setSpecMode("1");// *
lastGoodsInfoVo.setShelvesHandle("1");
lastGoodsInfoVo.setVirtualFlg("0");
lastGoodsInfoVo.setStatus("3");
lastGoodsInfoVo.setShelvesStatus("0");
lastGoodsInfoVo.setSpuAppear("0");//*
lastGoodsInfoVo.setSpuAppear("0");// *
lastGoodsInfoVo.setSpuCanbuy("1");
lastGoodsInfoVo.setDelFlg("0");
lastGoodsInfoVo.setCreatedAt(now);
lastGoodsInfoVo.setCreatedBy(uid);//*
lastGoodsInfoVo.setStoreId(storeId);//*
lastGoodsInfoVo.setCreatedBy(uid);// *
lastGoodsInfoVo.setStoreId(storeId);// *
return lastGoodsInfoVo;
}
private GoblinGoodsSkuInfoVo goodsInformationDataAnalysisProcessingForSku(GoblinGoodsImportDto dt, GoblinGoodsInfoVo lastGoodsInfoVo, boolean tobeNextSpuFlg) {
private GoblinGoodsSkuInfoVo goodsInformationDataAnalysisProcessingForSku(GoblinGoodsImportDto dt,
GoblinGoodsInfoVo lastGoodsInfoVo, boolean tobeNextSpuFlg) {
GoblinGoodsSkuInfoVo skuInfoVo = GoblinGoodsSkuInfoVo.getNew();
skuInfoVo.setName("");
List<GoblinGoodsSpecDto> skuSpecDtos = dt.getSkuSpecDtos();
List<GoblinGoodsSpecVo> spuSpecVos = tobeNextSpuFlg ? ObjectUtil.getGoblinGoodsSpecVoArrayList() : lastGoodsInfoVo.getSpecVoList();
List<GoblinGoodsSpecVo> spuSpecVos = tobeNextSpuFlg ? ObjectUtil.getGoblinGoodsSpecVoArrayList()
: lastGoodsInfoVo.getSpecVoList();
for (int i = 0, size = skuSpecDtos.size(); i < size; i++) {
GoblinGoodsSpecDto skuSpecDto = skuSpecDtos.get(i);
......@@ -398,18 +478,23 @@ public class GoblinStoreMgtGoodsImportService {
if (tobeNextSpuFlg) {
List<GoblinGoodsSpecValueVo> spuSpecValueVos = ObjectUtil.getGoblinGoodsSpecValueVoArrayList();
spuSpecValueVos.add(GoblinGoodsSpecValueVo.getNew().setSpecVname(skuSpecDto.getSpecVname()).setSpecVsort(1));
GoblinGoodsSpecVo spuSpecVo = GoblinGoodsSpecVo.getNew().setSpecName(skuSpecDto.getSpecName()).setSpecSort(i + 1).setSpecValues(spuSpecValueVos);
spuSpecValueVos
.add(GoblinGoodsSpecValueVo.getNew().setSpecVname(skuSpecDto.getSpecVname()).setSpecVsort(1));
GoblinGoodsSpecVo spuSpecVo = GoblinGoodsSpecVo.getNew().setSpecName(skuSpecDto.getSpecName())
.setSpecSort(i + 1).setSpecValues(spuSpecValueVos);
spuSpecVos.add(spuSpecVo);
} else {
Optional<GoblinGoodsSpecVo> hasSpecOptional = spuSpecVos.stream().filter(r -> r.getSpecName().equals(skuSpecDto.getSpecName())).findAny();
Optional<GoblinGoodsSpecVo> hasSpecOptional = spuSpecVos.stream()
.filter(r -> r.getSpecName().equals(skuSpecDto.getSpecName())).findAny();
if (hasSpecOptional.isPresent()) {
GoblinGoodsSpecVo spuSpecVo = hasSpecOptional.get();
List<GoblinGoodsSpecValueVo> spuSpecValueVos = spuSpecVo.getSpecValues();
Optional<GoblinGoodsSpecValueVo> any = spuSpecValueVos.stream().filter(r -> r.getSpecVname().equals(skuSpecDto.getSpecVname())).findAny();
Optional<GoblinGoodsSpecValueVo> any = spuSpecValueVos.stream()
.filter(r -> r.getSpecVname().equals(skuSpecDto.getSpecVname())).findAny();
if (!any.isPresent()) {
spuSpecValueVos.add(GoblinGoodsSpecValueVo.getNew().setSpecVname(skuSpecDto.getSpecVname()).setSpecVsort(lastGoodsInfoVo.getSkuIdList().size() + 1));
spuSpecValueVos.add(GoblinGoodsSpecValueVo.getNew().setSpecVname(skuSpecDto.getSpecVname())
.setSpecVsort(lastGoodsInfoVo.getSkuIdList().size() + 1));
}
} else {// 不匹配的规格直接跳过(默认只匹配商品第一行数据中的规格项)
return null;
......@@ -417,11 +502,11 @@ public class GoblinStoreMgtGoodsImportService {
}
}
skuInfoVo.setStock(dt.getStock());//*
skuInfoVo.setSkuStock(dt.getStock());//*
skuInfoVo.setPrice(dt.getPrice());//*
skuInfoVo.setPriceMember(dt.getPrice());//*
skuInfoVo.setSkuId(lastGoodsInfoVo.getSpuId().concat(StringUtils.right(String.valueOf(System.nanoTime()), 5)));//*
skuInfoVo.setStock(dt.getStock());// *
skuInfoVo.setSkuStock(dt.getStock());// *
skuInfoVo.setPrice(dt.getPrice());// *
skuInfoVo.setPriceMember(dt.getPrice());// *
skuInfoVo.setSkuId(lastGoodsInfoVo.getSpuId().concat(StringUtils.right(String.valueOf(System.nanoTime()), 5)));// *
if (StringUtils.isNotEmpty(dt.getSkuCode())) {
skuInfoVo.setSkuNo(dt.getSkuCode());
} else {
......@@ -430,7 +515,7 @@ public class GoblinStoreMgtGoodsImportService {
if (StringUtils.isNotEmpty(dt.getSkuImg())) {
skuInfoVo.setSkuPic(dt.getSkuImg());
} else {
// skuInfoVo.setSkuPic("");// 设置默认图片
// skuInfoVo.setSkuPic("");// 设置默认图片
}
if (StringUtils.isNotEmpty(dt.getSkuBarCode())) {
skuInfoVo.setSkuBarCode(dt.getSkuBarCode());
......@@ -438,18 +523,18 @@ public class GoblinStoreMgtGoodsImportService {
skuInfoVo.setSkuBarCode(lastGoodsInfoVo.getSpuNo());
}
skuInfoVo.setSpuId(lastGoodsInfoVo.getSpuId());//*
skuInfoVo.setSkuType(0);//*
skuInfoVo.setSpuId(lastGoodsInfoVo.getSpuId());// *
skuInfoVo.setSkuType(0);// *
skuInfoVo.setBuyFactor("0");
skuInfoVo.setBuyLimit(0);
skuInfoVo.setStatus("3");
skuInfoVo.setShelvesStatus(lastGoodsInfoVo.getShelvesStatus());
skuInfoVo.setSoldoutStatus("0");//*
skuInfoVo.setSkuAppear("0");//*
skuInfoVo.setSoldoutStatus("0");// *
skuInfoVo.setSkuAppear("0");// *
skuInfoVo.setSkuCanbuy("1");
skuInfoVo.setDelFlg("0");
skuInfoVo.setCreatedAt(lastGoodsInfoVo.getCreatedAt());//*
skuInfoVo.setCreatedBy(lastGoodsInfoVo.getCreatedBy());//*
skuInfoVo.setCreatedAt(lastGoodsInfoVo.getCreatedAt());// *
skuInfoVo.setCreatedBy(lastGoodsInfoVo.getCreatedBy());// *
skuInfoVo.setStoreId(lastGoodsInfoVo.getStoreId());
skuInfoVo.setSkuSpecList(skuSpecDtos);
......
package com.liquidnet.service.goblin.service.impl.manage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import com.liquidnet.common.third.sqb.param.request.MallListQueryRequest;
import com.liquidnet.common.third.sqb.param.request.MallProductsQueryRequest;
import com.liquidnet.common.third.sqb.param.response.data.MallListQueryData;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.goblin.dto.GoblinGoodsSpecDto;
import com.liquidnet.service.goblin.dto.manage.GoblinStoreMgtGoodsSqbAddParam;
import com.liquidnet.service.goblin.dto.manage.vo.GoblinMgtCategorySpecVo;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinSqbGoodsExt;
import com.liquidnet.service.goblin.mapper.GoblinSqbGoodsExtMapper;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreMgtSqbGoodsService;
import com.liquidnet.service.goblin.util.GoblinMongoUtils;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import com.liquidnet.service.goblin.util.QueueUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 商品管理:收钱吧商品服务实现
*/
@Slf4j
@Service
public class GoblinStoreMgtSqbGoodsServiceImpl implements IGoblinStoreMgtSqbGoodsService {
@Autowired
QueueUtils queueUtils;
@Autowired
GoblinRedisUtils goblinRedisUtils;
@Autowired
GoblinMongoUtils goblinMongoUtils;
@Autowired
GoblinSqbGoodsExtMapper goblinSqbGoodsExtMapper;
@Autowired
private SqbBiz goblinShouQianBaService;
@Override
public ResponseDto<List<GoblinSqbPerfGoodsVo>> getProductList() {
try {
List<GoblinSqbPerfGoodsVo> result = new ArrayList<>();
List<MallListQueryData> mallList = fetchAllMalls();
if (CollectionUtils.isEmpty(mallList)) {
return ResponseDto.success(new ArrayList<>());
}
// 获取本地已同步的商品 SPU 映射,用于标记同步状态
List<String> mallSns = mallList.stream().map(MallListQueryData::getMallSn).distinct()
.collect(Collectors.toList());
List<GoblinSqbGoodsExt> syncedGoods = goblinSqbGoodsExtMapper.selectList(
new LambdaQueryWrapper<GoblinSqbGoodsExt>()
.in(GoblinSqbGoodsExt::getMallSn, mallSns)
.eq(GoblinSqbGoodsExt::getDelFlg, "0"));
Set<String> syncedSqbSpuKeySet = syncedGoods.stream()
.map(g -> g.getMallSn() + ":" + g.getSqbSpuId())
.collect(Collectors.toSet());
for (MallListQueryData mall : mallList) {
List<MallProductsQueryData> products = fetchAllProducts(mall);
if (!CollectionUtils.isEmpty(products)) {
for (MallProductsQueryData product : products) {
GoblinSqbPerfGoodsVo goblinSqbPerfGoodsVo = getGoblinSqbPerfGoodsVo(mall, product);
goblinSqbPerfGoodsVo
.setSynced(syncedSqbSpuKeySet.contains(mall.getMallSn() + ":" + product.getSpuId()));
result.add(goblinSqbPerfGoodsVo);
}
}
}
return ResponseDto.success(result);
} catch (Exception e) {
log.error("search mall products error", e);
return ResponseDto.failure("查询收钱吧商品失败: " + e.getMessage());
}
}
private static GoblinSqbPerfGoodsVo getGoblinSqbPerfGoodsVo(MallListQueryData mall, MallProductsQueryData product) {
GoblinSqbPerfGoodsVo goblinSqbPerfGoodsVo = new GoblinSqbPerfGoodsVo();
goblinSqbPerfGoodsVo.setMallSn(mall.getMallSn());
goblinSqbPerfGoodsVo.setMallName(mall.getMallName());
goblinSqbPerfGoodsVo.setSignature(mall.getSignature());
goblinSqbPerfGoodsVo.setSpuId(product.getSpuId());
goblinSqbPerfGoodsVo.setTitle(product.getTitle());
goblinSqbPerfGoodsVo.setProductIntroduction(product.getProductIntroduction());
goblinSqbPerfGoodsVo.setConverImages(product.getConverImages());
goblinSqbPerfGoodsVo.setSkuResults(product.getSkuResults());
return goblinSqbPerfGoodsVo;
}
/**
* 循环获取所有商城列表(支持游标分页)
*/
private List<MallListQueryData> fetchAllMalls() {
List<MallListQueryData> allMalls = new ArrayList<>();
int count = 100;
String lastMallSn = null;
while (true) {
MallListQueryRequest mallRequest = new MallListQueryRequest();
mallRequest.setAppid(goblinShouQianBaService.getSqbConfig().getAppId());
MallListQueryRequest.Filter filter = new MallListQueryRequest.Filter();
filter.setSeller(buildSeller());
mallRequest.setFilter(filter);
MallListQueryRequest.Cursor cursor = new MallListQueryRequest.Cursor();
cursor.setCursorField("id");
cursor.setCount(count); // 最大支持1000
cursor.setEndCursor(lastMallSn);
mallRequest.setCursor(cursor);
MallListQueryRequest.Sort sort = new MallListQueryRequest.Sort();
sort.setSortField("id");
sort.setSort("DESC");
mallRequest.setSort(sort);
List<MallListQueryData> batch = goblinShouQianBaService.queryMallList(mallRequest);
if (CollectionUtils.isEmpty(batch)) {
break;
}
allMalls.addAll(batch);
lastMallSn = batch.get(batch.size() - 1).getMallSn();
// 如果拉取数量小于 count,说明是最后一页
if (batch.size() != count) {
break;
}
}
return allMalls;
}
/**
* 循环获取某个商城下的所有商品列表(支持搜索和游标分页)
*/
private List<MallProductsQueryData> fetchAllProducts(MallListQueryData mall) {
MallProductsQueryRequest productRequest = new MallProductsQueryRequest();
productRequest.setAppid(goblinShouQianBaService.getSqbConfig().getAppId());
productRequest.setSeller(buildSeller());
CommonRequest.Mall mallId = new CommonRequest.Mall();
mallId.setMallSn(mall.getMallSn());
mallId.setSignature(mall.getSignature());
productRequest.setMallID(mallId);
return goblinShouQianBaService.queryMallProducts(productRequest);
}
private CommonRequest.Seller buildSeller() {
CommonRequest.Seller seller = new CommonRequest.Seller();
seller.setMerchantId(goblinShouQianBaService.getSqbConfig().getMerchantId());
seller.setMerchantUserId(goblinShouQianBaService.getSqbConfig().getMerchantUserId());
seller.setRole(goblinShouQianBaService.getSqbConfig().getRole());
return seller;
}
public void sqbGoodsAdd(GoblinGoodsInfoVo goodsInfoVo, List<GoblinGoodsSkuInfoVo> goodsSkuInfoVoList) {
goblinMongoUtils.setGoodsInfoVo(goodsInfoVo);
goblinMongoUtils.setGoodsSkuInfoVos(goodsSkuInfoVoList);
String createdBy = goodsInfoVo.getCreatedBy();
LocalDateTime createdAt = goodsInfoVo.getCreatedAt();
String spuId = goodsInfoVo.getSpuId();
LinkedList<Object[]> initGoodsSkuObjs = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> initGoodsSkuSpecValueObjs = CollectionUtil.linkedListObjectArr();
LinkedList<Object[]> initGoodsCategorySpecObjs = CollectionUtil.linkedListObjectArr();
{// 分类规格记录
String cateFid = goodsInfoVo.getCateFid(), cateSid = goodsInfoVo.getCateSid(),
cateTid = goodsInfoVo.getCateTid();
String filterCateId = StringUtils.isBlank(cateTid) ? (StringUtils.isBlank(cateSid) ? cateFid : cateSid)
: cateTid;
GoblinMgtCategorySpecVo mgtCategorySpecVoCache = goblinRedisUtils.getCategorySpec(filterCateId);// 分类绑定的规格信息
List<String> addSpecNameList = goodsSkuInfoVoList.get(0).getSkuSpecList().stream()
.map(GoblinGoodsSpecDto::getSpecName).collect(Collectors.toList());
if (null == mgtCategorySpecVoCache) {// 根据分类ID未查取到规格信息,则Cache、数据库新增
GoblinMgtCategorySpecVo initMgtCategorySpecVo = GoblinMgtCategorySpecVo.getNew().setCateId(filterCateId)
.setSpecNameList(addSpecNameList);
goblinMongoUtils.setCategorySpecVo(initMgtCategorySpecVo);
goblinRedisUtils.setCategorySpec(filterCateId, initMgtCategorySpecVo);
addSpecNameList.forEach(r -> initGoodsCategorySpecObjs.add(new Object[] { filterCateId, r }));
} else {// 根据分类ID查取到规格信息,则进一步比对判断是否新增
List<String> confirmAddSpecNameList = addSpecNameList.stream()
.filter(r -> !mgtCategorySpecVoCache.getSpecNameList().contains(r))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(confirmAddSpecNameList)) {// 不存在于`mgtCategorySpecVoCache`的,则更新Cache,数据库新增
mgtCategorySpecVoCache.getSpecNameList().addAll(confirmAddSpecNameList);
goblinMongoUtils.updateCategorySpecVo(mgtCategorySpecVoCache);
goblinRedisUtils.setCategorySpec(filterCateId, mgtCategorySpecVoCache);
confirmAddSpecNameList
.forEach(r -> initGoodsCategorySpecObjs.add(new Object[] { filterCateId, r }));
}
}
}
int skuSize = goodsSkuInfoVoList.size();
for (int i = 0; i < skuSize; i++) {
GoblinGoodsSkuInfoVo skuInfoVo = goodsSkuInfoVoList.get(i);
String skuId = skuInfoVo.getSkuId();
String buyRoster = skuInfoVo.getBuyRoster();
goblinRedisUtils.setSkuStock(null, skuId, skuInfoVo.getSkuStock());
initGoodsSkuObjs.add(new Object[] {
skuId, skuInfoVo.getSpuId(), skuInfoVo.getSkuNo(), skuInfoVo.getSkuBarCode(),
skuInfoVo.getSkuErpCode(),
skuInfoVo.getErpType(), skuInfoVo.getErpHosting(), skuInfoVo.getErpWarehouseNo(),
skuInfoVo.getSkuType(), skuInfoVo.getName(),
skuInfoVo.getSubtitle(), skuInfoVo.getSellPrice(), skuInfoVo.getSkuPic(), skuInfoVo.getSkuIsbn(),
skuInfoVo.getStock(),
skuInfoVo.getSkuStock(), skuInfoVo.getWarningStock(), skuInfoVo.getPrice(),
skuInfoVo.getPriceMember(), skuInfoVo.getWeight(),
skuInfoVo.getBuyFactor(), buyRoster, skuInfoVo.getBuyLimit(), skuInfoVo.getStoreId(),
skuInfoVo.getSkuValidity(),
skuInfoVo.getVirtualFlg(), skuInfoVo.getStatus(), skuInfoVo.getShelvesStatus(),
skuInfoVo.getSkuAppear(), skuInfoVo.getShelvesAt(),
createdBy, createdAt, skuInfoVo.getLogisticsTemplate()
});
skuInfoVo.getSkuSpecList().forEach(skuSpecDto -> initGoodsSkuSpecValueObjs.add(new Object[] {
spuId, skuId, skuSpecDto.getSpecName(), skuSpecDto.getSpecVname()
}));
}
LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
toMqSqls.add(SqlMapping.get("goblin_goods.insert_for_coupon"));
LinkedList<Object[]> initGoodsObjs = CollectionUtil.linkedListObjectArr();
initGoodsObjs.add(new Object[] {
spuId, goodsInfoVo.getSpuNo(), goodsInfoVo.getSpuBarCode(), goodsInfoVo.getSpuErpCode(),
goodsInfoVo.getErpType(), goodsInfoVo.getSpuType(),
goodsInfoVo.getName(), goodsInfoVo.getSubtitle(), goodsInfoVo.getSellPrice(), goodsInfoVo.getPriceGe(),
goodsInfoVo.getPriceLe(),
goodsInfoVo.getIntro(), goodsInfoVo.getDetails(), goodsInfoVo.getCoverPic(), goodsInfoVo.getVideo(),
goodsInfoVo.getSpecMode(),
goodsInfoVo.getStoreId(), goodsInfoVo.getCateFid(), goodsInfoVo.getCateSid(), goodsInfoVo.getCateTid(),
goodsInfoVo.getStoreCateFid(),
goodsInfoVo.getStoreCateSid(), goodsInfoVo.getStoreCateTid(), goodsInfoVo.getBrandId(),
goodsInfoVo.getShelvesHandle(), goodsInfoVo.getShelvesTime(),
goodsInfoVo.getSpuValidity(), goodsInfoVo.getVirtualFlg(), goodsInfoVo.getStatus(),
goodsInfoVo.getShelvesStatus(), goodsInfoVo.getSpuAppear(),
goodsInfoVo.getShelvesAt(), createdBy, createdAt, goodsInfoVo.getLogisticsTemplate()
});
toMqSqls.add(SqlMapping.get("goblin_goods_sku.insert_for_coupon"));
toMqSqls.add(SqlMapping.get("goblin_goods_image.insert_byreplace"));
LinkedList<Object[]> initGoodsImageObjs = CollectionUtil.linkedListObjectArr();
if (!CollectionUtils.isEmpty(goodsInfoVo.getImageList())) {
goodsInfoVo.getImageList().forEach(imageUrl -> initGoodsImageObjs.add(new Object[] { spuId, imageUrl }));
}
toMqSqls.add(SqlMapping.get("goblin_goods_tag.insert_byreplace"));
LinkedList<Object[]> initGoodsTagObjs = CollectionUtil.linkedListObjectArr();
if (!CollectionUtils.isEmpty(goodsInfoVo.getTagVoList())) {
goodsInfoVo.getTagVoList().forEach(
tagVo -> initGoodsTagObjs.add(new Object[] { spuId, tagVo.getTagId(), tagVo.getSort(), "0" }));
}
if (!CollectionUtils.isEmpty(goodsInfoVo.getExtagVoList())) {
goodsInfoVo.getExtagVoList().forEach(exTagVo -> initGoodsTagObjs
.add(new Object[] { spuId, exTagVo.getTagId(), exTagVo.getSort(), "1" }));
}
toMqSqls.add(SqlMapping.get("goblin_goods_artag.insert_byreplace"));
LinkedList<Object[]> initGoodsArTagObjs = CollectionUtil.linkedListObjectArr();
if (!CollectionUtils.isEmpty(goodsInfoVo.getArtagVoList())) {
goodsInfoVo.getArtagVoList().forEach(arTagVo -> initGoodsArTagObjs
.add(new Object[] { spuId, arTagVo.getTagId(), arTagVo.getSort(), "1" }));
}
toMqSqls.add(SqlMapping.get("goblin_goods_service_support.insert_byreplace"));
LinkedList<Object[]> initGoodsServiceSupportObjs = CollectionUtil.linkedListObjectArr();
if (!CollectionUtils.isEmpty(goodsInfoVo.getServiceSupportVoList())) {
goodsInfoVo.getServiceSupportVoList()
.forEach(ssvo -> initGoodsServiceSupportObjs.add(new Object[] { spuId, ssvo.getSsid() }));
}
toMqSqls.add(SqlMapping.get("goblin_goods_spec.insert_byreplace"));
LinkedList<Object[]> initGoodsSpecObjs = CollectionUtil.linkedListObjectArr();
toMqSqls.add(SqlMapping.get("goblin_goods_spec_value.insert_byreplace"));
LinkedList<Object[]> initGoodsSpecValueObjs = CollectionUtil.linkedListObjectArr();
toMqSqls.add(SqlMapping.get("goblin_goods_spu_spec_value.insert_byreplace"));// SPU规格信息
LinkedList<Object[]> initGoodsSpuSpecValueObjs = CollectionUtil.linkedListObjectArr();
toMqSqls.add(SqlMapping.get("goblin_goods_sku_spec_value.insert_byreplace"));// SKU规格信息
{// 规格记录
List<GoblinGoodsSpecVo> specVoList = goodsInfoVo.getSpecVoList();
specVoList.forEach(s -> {
initGoodsSpecObjs.add(new Object[] { s.getSpecName(), createdAt });
s.getSpecValues().forEach(sv -> {
initGoodsSpecValueObjs.add(new Object[] { s.getSpecName(), sv.getSpecVname(), createdAt });
initGoodsSpuSpecValueObjs
.add(new Object[] { spuId, s.getSpecName(), sv.getSpecVname(), sv.getSpecVsort() });
});
});
}
toMqSqls.add(SqlMapping.get("goblin_goods_category_spec.insert_byreplace"));// 分类关联规格信息
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
SqlMapping.gets(toMqSqls, initGoodsObjs, initGoodsSkuObjs,
initGoodsImageObjs, initGoodsTagObjs, initGoodsArTagObjs, initGoodsServiceSupportObjs,
initGoodsSpecObjs,
initGoodsSpecValueObjs, initGoodsSpuSpecValueObjs, initGoodsSkuSpecValueObjs,
initGoodsCategorySpecObjs));
}
public boolean sqbGoodsEditSpu(String uid, GoblinSqbPerfGoodsVo mgtGoodsAddParam,
GoblinGoodsInfoVo mgtGoodsInfoVo) {
String spuId = mgtGoodsInfoVo.getSpuId();
GoblinStoreMgtGoodsSqbAddParam initParam = new GoblinStoreMgtGoodsSqbAddParam();
GoblinGoodsInfoVo updateSpuInfoVo = initParam.initEditGoodsInfoVo(mgtGoodsAddParam, mgtGoodsInfoVo);
boolean updateImageFlg = false;
List<String> paramImageList = mgtGoodsAddParam.getConverImages();
List<String> befImageList = mgtGoodsInfoVo.getImageList();
{// 图片处理
if (befImageList.size() != paramImageList.size()) {
updateImageFlg = true;
} else {
for (String imageUrl : paramImageList) {
if (!befImageList.contains(imageUrl)) {
updateImageFlg = true;
break;
}
}
}
if (updateImageFlg) {
updateSpuInfoVo.setImageList(paramImageList);
}
}
updateSpuInfoVo.setUpdatedBy(uid);
updateSpuInfoVo.setUpdatedAt(LocalDateTime.now());
if (goblinMongoUtils.updateGoodsInfoVo(updateSpuInfoVo)) {
goblinRedisUtils.delGoodsInfoVo(updateSpuInfoVo.getSpuId());
log.info("商品管理:SPU编辑[UID={},PARAMS={}]", uid, JsonUtils.toJson(mgtGoodsAddParam));
LinkedList<Object[]> updateGoodsInfoObjs = CollectionUtil.linkedListObjectArr();
LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
toMqSqls.add(SqlMapping.get("goblin_goods.update_by_edit"));
updateGoodsInfoObjs.add(new Object[] {
updateSpuInfoVo.getSpuNo(), updateSpuInfoVo.getName(), updateSpuInfoVo.getSubtitle(),
updateSpuInfoVo.getSellPrice(),
updateSpuInfoVo.getIntro(), updateSpuInfoVo.getDetails(), updateSpuInfoVo.getCoverPic(),
updateSpuInfoVo.getVideo(),
updateSpuInfoVo.getSpecMode(), updateSpuInfoVo.getCateFid(), updateSpuInfoVo.getCateSid(),
updateSpuInfoVo.getCateTid(),
updateSpuInfoVo.getShelvesHandle(), updateSpuInfoVo.getShelvesTime(),
updateSpuInfoVo.getSpuValidity(), updateSpuInfoVo.getVirtualFlg(),
updateSpuInfoVo.getLogisticsTemplate(), updateSpuInfoVo.getUpdatedBy(),
updateSpuInfoVo.getUpdatedAt(), updateSpuInfoVo.getSpuErpCode(),
updateSpuInfoVo.getSpuId()
});
toMqSqls.add(SqlMapping.get("goblin_goods_image.delete"));
LinkedList<Object[]> deleteGoodsImageObjs = CollectionUtil.linkedListObjectArr();
toMqSqls.add(SqlMapping.get("goblin_goods_image.insert_byreplace"));
LinkedList<Object[]> initGoodsImageObjs = CollectionUtil.linkedListObjectArr();
if (updateImageFlg) {
deleteGoodsImageObjs.add(new Object[] { spuId });
updateSpuInfoVo.getImageList()
.forEach(imageUrl -> initGoodsImageObjs.add(new Object[] { spuId, imageUrl }));
}
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
SqlMapping.gets(toMqSqls, updateGoodsInfoObjs, deleteGoodsImageObjs,
initGoodsImageObjs));
this.sqbGoodsEditSku(uid, mgtGoodsAddParam, mgtGoodsInfoVo);
return true;
}
return false;
}
public boolean sqbGoodsEditSku(String uid, GoblinSqbPerfGoodsVo mgtGoodsAddParam,
GoblinGoodsInfoVo goodsInfoVo) {
LocalDateTime nowTime = LocalDateTime.now();
List<MallProductsQueryData.Sku> skuResults = mgtGoodsAddParam.getSkuResults();
if (goodsInfoVo == null || CollectionUtils.isEmpty(goodsInfoVo.getSkuIdList()) || CollectionUtils.isEmpty(skuResults)) {
return false;
}
LambdaQueryWrapper<GoblinSqbGoodsExt> wrapper = new LambdaQueryWrapper<GoblinSqbGoodsExt>()
.eq(GoblinSqbGoodsExt::getSpuId, goodsInfoVo.getSpuId())
.eq(GoblinSqbGoodsExt::getDelFlg, "0");
if (StringUtils.isNotBlank(mgtGoodsAddParam.getMallSn())) {
wrapper.eq(GoblinSqbGoodsExt::getMallSn, mgtGoodsAddParam.getMallSn());
}
List<GoblinSqbGoodsExt> extList = goblinSqbGoodsExtMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(extList)) {
return false;
}
Map<String, String> sqbSkuToLocalSkuMap = extList.stream()
.filter(ext -> StringUtils.isNotBlank(ext.getSqbSkuId()) && StringUtils.isNotBlank(ext.getSkuId()))
.collect(Collectors.toMap(GoblinSqbGoodsExt::getSqbSkuId, GoblinSqbGoodsExt::getSkuId, (a, b) -> a));
boolean updated = false;
LinkedList<Object[]> updateGoodsSkuObjs = CollectionUtil.linkedListObjectArr();
for (MallProductsQueryData.Sku sqbSku : skuResults) {
if (sqbSku == null || StringUtils.isBlank(sqbSku.getSkuId())) {
continue;
}
String localSkuId = sqbSkuToLocalSkuMap.get(sqbSku.getSkuId());
if (StringUtils.isBlank(localSkuId)) {
continue;
}
GoblinGoodsSkuInfoVo skuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(localSkuId);
if (skuInfoVo == null || !"0".equals(skuInfoVo.getDelFlg())) {
continue;
}
skuInfoVo.setName(sqbSku.getSkuName());
if (sqbSku.getPrice() != null) {
skuInfoVo.setPrice(BigDecimal.valueOf(sqbSku.getPrice()));
skuInfoVo.setPriceMember(BigDecimal.valueOf(sqbSku.getPrice()));
}
skuInfoVo.setUpdatedBy(uid);
skuInfoVo.setUpdatedAt(nowTime);
if (goblinMongoUtils.updateGoodsSkuInfoVo(skuInfoVo)) {
goblinRedisUtils.delGoodsSkuInfoVo(localSkuId);
updateGoodsSkuObjs.add(new Object[] {
sqbSku.getSkuName(), skuInfoVo.getPrice(), skuInfoVo.getPriceMember(), skuInfoVo.getStock(),
skuInfoVo.getSkuStock(),
skuInfoVo.getBuyLimit(), skuInfoVo.getUpdatedBy(), skuInfoVo.getUpdatedAt(), localSkuId
});
updated = true;
}
}
if (updated) {
LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
toMqSqls.add(SqlMapping.get("goblin_goods_sku.update_by_edit_for_sqb"));
queueUtils.sendMsgByRedis(MQConst.GoblinQueue.SQL_GOODS.getKey(),
SqlMapping.gets(toMqSqls, updateGoodsSkuObjs));
}
return updated;
}
}
......@@ -1150,6 +1150,13 @@ public class GoblinMongoUtils {
}
//获取全部正在下单的活动
/* ---------------------------------------- 官方营销数据源 ---------------------------------------- */
public List<GoblinSelfMarketingVo> getSelfMarketingByPerformanceId(String performanceId) {
Query query = Query.query(Criteria.where("performanceId").is(performanceId).and("status").ne(7));
return mongoTemplate.find(query, GoblinSelfMarketingVo.class, GoblinSelfMarketingVo.class.getSimpleName());
}
public List<GoblinSelfMarketingVo> getGoblinSelfMarketingVoList() {
String nowStr = DateUtil.getNowTime();
List<GoblinSelfMarketingVo> voList = mongoTemplate.find(Query.query(Criteria.where("type").is(2).and("status").ne(7).and("endTime").gte(nowStr).and("startTime").lte(nowStr)), GoblinSelfMarketingVo.class, GoblinSelfMarketingVo.class.getSimpleName());
......
......@@ -24,7 +24,9 @@ import com.liquidnet.service.goblin.entity.GoblinBraceletOrder;
import com.liquidnet.service.goblin.entity.GoblinFrontBanner;
import com.liquidnet.service.goblin.entity.GoblinFrontHotWord;
import com.liquidnet.service.goblin.entity.GoblinFrontNavigation;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import com.liquidnet.service.goblin.mapper.GoblinBraceletOrderMapper;
import com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper;
import com.liquidnet.service.goblin.service.impl.inner.GoblinNftJobServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -1697,6 +1699,11 @@ public class GoblinRedisUtils {
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) {
String redisKey = GoblinRedisConst.REDIS_GOBLIN_ORDER_MASTER.concat(masterCode);
......@@ -3048,8 +3055,19 @@ public class GoblinRedisUtils {
}
/* ---------------------------------------- ---------------------------------------- */
/* ---------------------------------------- ---------------------------------------- */
/* ---------------------------------------- ---------------------------------------- */
/* ---------------------------------------- ---------------------------------------- */
/* --------------------------------收钱吧相关--------------------------------- */
public void setSqbPerformanceGoodsListCache(String performancesId, List<GoblinSqbPerformanceGoods> relations) {
String key = GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId);
redisUtil.set(key, relations, RedisKeyExpireConst.SQB_PERFORMANCE_GOODS_EXPIRE);
}
public List<GoblinSqbPerformanceGoods> getSqbPerformanceGoodsListCache(String performancesId) {
String key = GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId);
return (List<GoblinSqbPerformanceGoods>) redisUtil.get(key);
}
public void delPerformanceGoodsCache(String performancesId) {
redisUtil.del(GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId));
}
}
package com.liquidnet.service.goblin.util;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbGoodsExtVo;
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 java.util.List;
/**
* 收钱吧相关 Redis 操作封装
*/
@Slf4j
@Component
public class GoblinSqbRedisUtils {
@Autowired
private RedisUtil redisUtil;
/* ---------------------------------------- 订单操作(TTL 2h) ---------------------------------------- */
public void setSqbOrder(String orderId, GoblinSqbOrderVo vo) {
redisUtil.set(GoblinRedisConst.SQB_ORDER.concat(orderId), vo, RedisKeyExpireConst.SQB_ORDER_EXPIRE);
}
public GoblinSqbOrderVo getSqbOrder(String orderId) {
String redisKey = GoblinRedisConst.SQB_ORDER.concat(orderId);
Object object = redisUtil.get(redisKey);
if (null == object) {
return null;
} else {
return (GoblinSqbOrderVo) object;
}
}
public void delSqbOrder(String orderId) {
redisUtil.del(GoblinRedisConst.SQB_ORDER.concat(orderId));
}
/* ---------------------------------------- 演出关联商品缓存(TTL 5min) ---------------------------------------- */
public void setPerfGoods(String performancesId, List<GoblinSqbPerfGoodsVo> list) {
redisUtil.set(GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId), list, RedisKeyExpireConst.SQB_PERFORMANCE_GOODS_EXPIRE);
}
public List<GoblinSqbPerfGoodsVo> getPerfGoods(String performancesId) {
return (List<GoblinSqbPerfGoodsVo>) redisUtil.get(GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId));
}
public void delPerfGoods(String performancesId) {
redisUtil.del(GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId));
}
/* ---------------------------------------- 下单防重锁(TTL 10s) ---------------------------------------- */
/**
* 尝试获取下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
* @return true 获取成功,false 已被锁定
*/
public boolean tryOrderLock(String userId, String skuId) {
String key = GoblinRedisConst.SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
return redisUtil.lock(key, 1, RedisKeyExpireConst.SQB_ORDER_LOCK_EXPIRE);
}
/**
* 释放下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
*/
public void releaseOrderLock(String userId, String skuId) {
String key = GoblinRedisConst.SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
redisUtil.uLock(key);
}
// 获取 收钱吧订单id列表
public List<String> getSqbOrderList(String uid) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_ORDER_LIST.concat(uid);
Object obj = redisUtil.get(redisKey);
if (obj == null) {
return CollectionUtil.arrayListString();
} else {
return (List<String>) obj;
}
}
/**
* 设置正在商品获取收钱吧相关信息缓存
* @param spuId 正在spuId
* @param skuId 正在skuId
* @param sqbGoodsExtVo
*/
public void setSqbGoodsExt(String spuId, String skuId, GoblinSqbGoodsExtVo sqbGoodsExtVo) {
if (StringUtil.isBlank(spuId) || StringUtil.isBlank(skuId)) {
return;
}
String redisKey = GoblinRedisConst.SQB_GOBLIN_GOODS_EXT_KEY.concat(spuId).concat(":").concat(skuId);
redisUtil.set(redisKey, sqbGoodsExtVo);
}
/**
* 获取正在商品获取收钱吧相关信息缓存
*
* @param spuId
* @param skuId
* @return
*/
public GoblinSqbGoodsExtVo getSqbGoodsExt(String spuId, String skuId) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_GOODS_EXT_KEY.concat(spuId).concat(":").concat(skuId);
Object obj = redisUtil.get(redisKey);
if (obj == null) {
return null;
} else {
return (GoblinSqbGoodsExtVo) obj;
}
}
}
......@@ -10,10 +10,7 @@ import com.liquidnet.service.goblin.dto.GoblinStoreMgtCouponListVoExcel;
import com.liquidnet.service.goblin.dto.GoblinStoreMgtGoodsListVoExcel;
import com.liquidnet.service.goblin.dto.manage.vo.*;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinFrontBanner;
import com.liquidnet.service.goblin.entity.GoblinOrderAttr;
import com.liquidnet.service.goblin.entity.GoblinOrderSku;
import com.liquidnet.service.goblin.entity.GoblinStoreOrder;
import com.liquidnet.service.goblin.entity.*;
import com.liquidnet.service.goblin.param.BackCouponParam;
import com.liquidnet.service.goblin.param.GoblinOrderSqlParam;
import com.mongodb.BasicDBObject;
......@@ -43,6 +40,7 @@ public class ObjectUtil {
private static final ArrayList<GoblinStoreConfigVo> goblinStoreConfigVoArrayList = new ArrayList<>();
private static final ArrayList<GoblinStoreNoticeVo> goblinStoreNoticeVoArrayList = new ArrayList<>();
private static final ArrayList<GoblinGoodsSkuInfoVo> goblinGoodsSkuInfoVoArrayList = new ArrayList<>();
private static final ArrayList<GoblinSqbGoodsExt> goblinSqbGoodsExtArrayList = new ArrayList<>();
private static final HashMap<String, GoblinGoodsSkuInfoVo> goblinGoodsSkuInfoVoHashMap = new HashMap<>();
private static final ArrayList<GoblinGoodsSpecVo> goblinGoodsSpecVoArrayList = new ArrayList<>();
private static final ArrayList<GoblinGoodsSpecValueVo> goblinGoodsSpecValueVoArrayList = new ArrayList<>();
......@@ -337,6 +335,10 @@ public class ObjectUtil {
return (ArrayList<GoblinGoodsSkuInfoVo>) goblinGoodsSkuInfoVoArrayList.clone();
}
public static ArrayList<GoblinSqbGoodsExt> getGoblinSqbGoodsExtArrayList() {
return (ArrayList<GoblinSqbGoodsExt>) goblinSqbGoodsExtArrayList.clone();
}
public static Map<String, GoblinGoodsSkuInfoVo> getGoblinGoodsSkuInfoVoMap() {
return (HashMap<String, GoblinGoodsSkuInfoVo>) goblinGoodsSkuInfoVoHashMap.clone();
}
......
......@@ -71,6 +71,7 @@ goblin_goods_sku.update_by_edit_coupon_spu=UPDATE goblin_goods_sku SET sku_no=?,
goblin_goods_sku.update_by_shelves=UPDATE goblin_goods_sku SET shelves_status=?,shelves_at=?,updated_by=?,updated_at=? WHERE sku_id=? AND store_id=? AND del_flg='0'
goblin_goods_sku.update_by_soldout=UPDATE goblin_goods_sku SET soldout_status=?,updated_by=?,updated_at=? WHERE sku_id=? AND del_flg='0'
goblin_goods_sku.update_by_edit_for_coupon=UPDATE goblin_goods_sku SET price=?,price_member=?,stock=?,sku_stock=?,buy_limit=?,updated_by=?,updated_at=? WHERE sku_id=? AND del_flg='0'
goblin_goods_sku.update_by_edit_for_sqb=UPDATE goblin_goods_sku SET name=?,price=?,price_member=?,stock=?,sku_stock=?,buy_limit=?,updated_by=?,updated_at=? WHERE sku_id=? AND del_flg='0'
goblin_goods_sku.update_by_del_store=UPDATE goblin_goods_sku SET del_flg='1',updated_by=?,updated_at=?,deleted_by=?,deleted_at=? WHERE store_id=? AND del_flg='0'
goblin_goods_sku.update_by_del_spu=UPDATE goblin_goods_sku SET del_flg='1',updated_by=?,updated_at=?,deleted_by=?,deleted_at=? WHERE spu_id=? AND del_flg='0'
goblin_goods_sku.update_by_del=UPDATE goblin_goods_sku SET del_flg='1',updated_by=?,updated_at=?,deleted_by=?,deleted_at=? WHERE sku_id=? AND del_flg='0'
......@@ -193,8 +194,9 @@ goblin_nft_order.update_artwork=UPDATE goblin_nft_order SET artwork_id=? WHERE o
goblin_sku.stock=UPDATE goblin_goods_sku SET sku_stock = ? , stock = ?, updated_at = ? WHERE sku_erp_code = ? and erp_warehouse_no = ?
#----
candy_user_coupon.update_apply_refund=UPDATE candy_user_coupon SET state=?,operator=?,updated_at=? WHERE ucoupon_id=?
#----
#----
# ----- \u6536\u94B1\u5427 ----
goblin_sqb_order.insert=INSERT INTO goblin_sqb_order (order_id,user_id,performances_id,spu_id,sku_id,quantity,amount,sqb_order_sn,sqb_order_signature,sqb_acquiring_sn, sqb_acquiring_sign,sqb_checkout_items_id,status,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
# ---- \u624B\u73AF\u8BA2\u5355 ----
goblin_bracelet_order_insert = INSERT INTO `goblin_bracelet_order`(`order_id`, `user_id`, `bind_name`, `bind_mobile`, `bind_idcard`, `req_date`, `goods_desc`, `wristband_id`, `wristband_price`, `amount_id`, `amount_price`, `req_seq_id`, `hf_seq_id`, `trade_type`, `party_order_id`, `price`, `price_total`, `price_refund`, `refund_price_charges`, `refund_number`, `status`, `pay_status`, `created_at`, `updated_at`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
gpblin_bracelet_order_update= update goblin_bracelet_order set out_trans_id=?, end_time=?, acct_date=?, price_actual=?, time_pay=?, status=?, pay_status=?, updated_at=? where order_id=?
......
package com.liquidnet.service.goblin.test;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import com.liquidnet.common.third.sqb.param.request.MallListQueryRequest;
import com.liquidnet.common.third.sqb.param.request.MallProductsQueryRequest;
import com.liquidnet.common.third.sqb.param.response.data.MallListQueryData;
import com.liquidnet.common.third.sqb.param.response.data.MallProductsQueryData;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class GoblinShouQianBaServiceImplTest {
@Autowired
private SqbBiz sqbBiz;
@Test
public void queryMallListTest(){
CommonRequest.Seller seller = new CommonRequest.Seller();
seller.setRole(sqbBiz.getSqbConfig().getRole());
seller.setMerchantId(sqbBiz.getSqbConfig().getMerchantId());
seller.setMerchantUserId(sqbBiz.getSqbConfig().getMerchantUserId());
MallListQueryRequest.Filter filter = new MallListQueryRequest.Filter();
filter.setSeller(seller);
MallListQueryRequest.Cursor cursor = new MallListQueryRequest.Cursor();
cursor.setCount(10);
cursor.setCursorField("id");
MallListQueryRequest.Sort sort = new MallListQueryRequest.Sort();
sort.setSort("DESC");
sort.setSortField("id");
MallListQueryRequest mallListQueryRequest = new MallListQueryRequest();
mallListQueryRequest.setAppid(sqbBiz.getSqbConfig().getAppId());
mallListQueryRequest.setFilter(filter);
mallListQueryRequest.setCursor(cursor);
mallListQueryRequest.setSort(sort);
List<MallListQueryData> mallListQueryData = sqbBiz.queryMallList(mallListQueryRequest);
Assert.assertNotNull(mallListQueryData);
}
@Test
public void queryMallProductsTest(){
CommonRequest.Seller seller = new CommonRequest.Seller();
seller.setMerchantId(sqbBiz.getSqbConfig().getMerchantId());
seller.setMerchantUserId(sqbBiz.getSqbConfig().getMerchantUserId());
seller.setRole("super_admin");
CommonRequest.Mall mall = new CommonRequest.Mall();
mall.setMallSn("2025082811504809748");
mall.setSignature("9GB99V5iqvg-J_mhjYzs");
MallProductsQueryRequest mallProductsQueryRequest = new MallProductsQueryRequest();
mallProductsQueryRequest.setAppid(sqbBiz.getSqbConfig().getAppId());
mallProductsQueryRequest.setSeller(seller);
mallProductsQueryRequest.setMallID(mall);
List<MallProductsQueryData> queryData = sqbBiz.queryMallProducts(mallProductsQueryRequest);
Assert.assertNotNull(queryData);
}
}
......@@ -76,6 +76,12 @@
<scope>system</scope>
<systemPath>${basedir}/lib/taobao-sdk-java-auto_1600401599540-20210607-impl.jar</systemPath>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-sqb</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
......
package com.liquidnet.service.order.controller;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.callback.CallbackParams;
import com.liquidnet.common.third.sqb.param.callback.CouponCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.OrderCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.RefundCallbackContent;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.codec.vo.EncryptedReq;
import com.liquidnet.service.goblin.dto.manage.GoblinSqbOrderParam;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo;
import com.liquidnet.service.goblin.service.IGoblinSqbOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotBlank;
@Slf4j
@Api(tags = "商城-收钱吧支付相关")
@RestController
@RequestMapping("/goblin/sqb")
@Validated
public class GoblinSqbOrderController {
@Autowired
private IGoblinSqbOrderService goblinSqbOrderService;
@Autowired
private SqbBiz sqbBiz;
/**
* 创建收钱吧订单
*/
@PostMapping("/pre")
@ApiOperation("创建收钱吧下单-加密")
public ResponseDto<GoblinSqbOrderCreateVo> preOrder(@RequestBody EncryptedReq<GoblinSqbOrderParam> param) {
String userId = CurrentUtil.getCurrentUid();
GoblinSqbOrderParam orderParam = param.getData();
return goblinSqbOrderService.createOrder(userId, orderParam);
}
/**
* 创建收钱吧订单
*/
@PostMapping("/fc7bce6d6c2213b866f76493f9222201")
@ApiOperation("创建收钱吧下单-不加密")
public ResponseDto<GoblinSqbOrderCreateVo> preOrder(@RequestBody @Validated GoblinSqbOrderParam param) {
String userId = CurrentUtil.getCurrentUid();
return goblinSqbOrderService.createOrder(userId, param);
}
/**
* 查询支付状态
*/
@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);
}
/**
* 再次付款(待支付状态下重新拉起微信支付)
*/
@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);
}
//******************************** 回调相关 *******************************
@ApiOperation("下单回调")
@PostMapping("/order/callback")
public ResponseDto<String> orderCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("下单回调参数为空");
return ResponseDto.failure("下单回调参数为空");
}
boolean verified = sqbBiz.verifySignature(callbackParams);
if (!verified) {
log.error("下单回调验签失败");
return ResponseDto.failure("验签失败");
}
OrderCallbackContent orderCallbackContent = JsonUtils.fromJson(callbackParams.getContent(), OrderCallbackContent.class);
log.info("下单回调content: {}", callbackParams.getContent());
// 业务
return goblinSqbOrderService.handlePayCallback(orderCallbackContent);
}
@ApiOperation("退款回调")
@PostMapping("/refund/callback")
public ResponseDto<String> refundCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("退款回调参数为空");
return ResponseDto.failure("退款回调参数为空");
}
boolean verified = sqbBiz.verifySignature(callbackParams);
if (!verified) {
log.error("退款回调验签失败");
return ResponseDto.failure("验签失败");
}
RefundCallbackContent refundCallbackContent = JsonUtils.fromJson(callbackParams.getContent(), RefundCallbackContent.class);
log.info("退款回调content: {}", callbackParams.getContent());
// 业务
return goblinSqbOrderService.handleRefundCallback(refundCallbackContent);
}
@ApiOperation("券状态回调")
@PostMapping("/coupon/callback")
public ResponseDto<String> couponCallback(@RequestBody CallbackParams callbackParams) {
if (null == callbackParams) {
log.error("券状态回调参数为空");
return ResponseDto.failure("券状态回调参数为空");
}
boolean verified = sqbBiz.verifySignature(callbackParams);
if (!verified) {
log.error("券状态回调验签失败");
return ResponseDto.failure("验签失败");
}
CouponCallbackContent callbackContent = JsonUtils.fromJson(callbackParams.getContent(), CouponCallbackContent.class);
log.info("券状态回调content: {}", callbackParams.getContent());
// 业务
return goblinSqbOrderService.handleCouponCallback(callbackContent);
}
}
package com.liquidnet.service.order.service.impl;
import com.liquidnet.common.third.sqb.biz.SqbBiz;
import com.liquidnet.common.third.sqb.param.callback.CouponCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.OrderCallbackContent;
import com.liquidnet.common.third.sqb.param.callback.RefundCallbackContent;
import com.liquidnet.common.third.sqb.param.request.CashierQueryRequest;
import com.liquidnet.common.third.sqb.param.request.CommonRequest;
import com.liquidnet.common.third.sqb.param.request.CreateWechatPrepayOrderRequest;
import com.liquidnet.common.third.sqb.param.request.SettlementCreateRequest;
import com.liquidnet.common.third.sqb.param.response.data.CashierQueryData;
import com.liquidnet.common.third.sqb.param.response.data.CreateWechatPrepayOrderData;
import com.liquidnet.common.third.sqb.param.response.data.OrderCreateData;
import com.liquidnet.common.third.sqb.param.response.data.SettlementCreateData;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.manage.GoblinSqbOrderParam;
import com.liquidnet.service.goblin.dto.vo.*;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import com.liquidnet.service.goblin.service.IGoblinSqbOrderService;
import com.liquidnet.service.order.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Slf4j
public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
@Autowired
private GoblinRedisUtils goblinRedisUtils;
@Autowired
private GoblinSqbRedisUtils goblinSqbRedisUtils;
@Autowired
private GoblinMongoUtils goblinMongoUtils;
@Autowired
private QueueUtils queueUtils;
@Autowired
private SqbBiz sqbBiz;
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// ================================ 创建订单 ================================
@Override
public ResponseDto<GoblinSqbOrderCreateVo> createOrder(String userId, GoblinSqbOrderParam orderParam) {
log.info("[收钱吧下单] 下单开始 userId={}, param: {}", userId, JsonUtils.toJson(orderParam));
String skuId = orderParam.getSkuId();
String spuId = orderParam.getSpuId();
String performancesId = orderParam.getPerformancesId();
Integer quantity = orderParam.getQuantity();
String openId = orderParam.getOpenId();
if (openId == null || openId.trim().isEmpty()) {
return ResponseDto.failure("微信 openId 不能为空");
}
// Step 1: 分布式锁防重
boolean locked = goblinSqbRedisUtils.tryOrderLock(userId, skuId);
if (!locked) {
log.warn("[收钱吧下单] 获取防重锁失败,userId={}, skuId={}", userId, skuId);
return ResponseDto.failure("请勿重复下单");
}
try {
// Step 2: 校验演出-商品关联
List<GoblinSqbPerformanceGoods> performanceGoodsListCache = goblinSqbRedisUtils.getSqbPerformanceGoodsListCache(performancesId);
if (performanceGoodsListCache.isEmpty()) {
log.error("[收钱吧下单] 演出关联商品列表为空,performancesId={}, skuId={}", performancesId, skuId);
return ResponseDto.failure("商品与演出关联不存在");
}
boolean skuIdExists = false;
for (GoblinSqbPerformanceGoods goblinSqbPerformanceGoods : performanceGoodsListCache) {
if (skuId.equals(goblinSqbPerformanceGoods.getSkuId())) {
skuIdExists = true;
}
}
if (!skuIdExists) {
log.error("[收钱吧下单] 演出-商品关联不存在或已禁用,performancesId={}, skuId={}", performancesId, skuId);
return ResponseDto.failure("商品与演出关联不存在");
}
GoblinGoodsSkuInfoVo skuVo = goblinRedisUtils.getGoodsSkuInfoVo(skuId);
if (skuVo == null) {
log.error("[收钱吧下单] 未找到商品, skuId: {}, spuId: {}", skuId, spuId);
return ResponseDto.failure("商品不存在");
}
if (!skuId.equals(skuVo.getSkuId())) {
return ResponseDto.failure("下单失败");
}
if (!skuVo.getShelvesStatus().equals("3")) {
log.info("[收钱吧下单] 商品已下架 skuId: {}, spuId: {}", skuId, spuId);
return ResponseDto.failure("商品已下架");
}
// Step 3: 原子扣减库存
int remaining = goblinRedisUtils.decrSkuStock(null, skuId, quantity);
if (remaining < 0) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
log.warn("[收钱吧下单] 库存不足,skuId={}, quantity={}", skuId, quantity);
return ResponseDto.failure("库存不足");
}
log.info("[收钱吧下单] 扣减库存成功,skuId={}, 剩余库存={}", skuId, remaining);
// 获取该商品对应的商城的编号和密码
GoblinSqbGoodsExtVo sqbGoodsExt = goblinSqbRedisUtils.getSqbGoodsExt(spuId, skuId);
if (sqbGoodsExt == null) {
log.error("[收钱吧下单] 正在商品对应收钱吧参数为空, spuId: {}, skuId: {}", spuId, skuId);
return ResponseDto.failure("下单失败");
}
SettlementCreateData settlementData = sqbBiz.createSettlement(
sqbGoodsExt.getMallSn(),
sqbGoodsExt.getSignature(),
userId,
Collections.singletonList(buildSqbCheckOutItem(skuVo,
quantity, sqbGoodsExt.getSqbSkuId(), sqbGoodsExt.getSqbSpuId())));
if (settlementData == null) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("创建结算明细失败");
}
final String checkoutItemsId = settlementData.getCheckoutItemsId();
log.info("[收钱吧下单] 创建结算明细成功,checkoutItemsId={}", checkoutItemsId);
// Step 4.2: createOrder → 得 sqbOrderSn + sqbOrderSignature + sqbAcquiringSn
String goodsName = skuVo.getName();
OrderCreateData orderData = sqbBiz.createOrder(
sqbGoodsExt.getMallSn(),
sqbGoodsExt.getSignature(),
checkoutItemsId,
userId,
buildRequestId(IDGenerator.nextSnowId()),
buildTitle(goodsName)
);
if (orderData == null) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("创建收钱吧订单失败");
}
String sqbOrderSn = orderData.getOrderSn(); // 订单编号
String sqbOrderSignature = orderData.getOrderSignature(); // 订单密钥
String sqbAcquiringSn = orderData.getAcquiring() != null ? orderData.getAcquiring().getAcquiringSn() : null; // 收单号
String sqbAcquiringSign = orderData.getAcquiring() != null ? orderData.getAcquiring().getSignature() : null; // 收单密钥
log.info("[收钱吧下单] 创建订单成功,订单编号={}, 收单号={}", sqbOrderSn, sqbAcquiringSn);
// Step 4.3: queryCashier → 得 selectedSignature + seq
CashierQueryData cashierData = sqbBiz.queryCashier(sqbAcquiringSn, sqbAcquiringSign, userId);
if (cashierData == null) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("查询收银台失败");
}
String selectedSignature = cashierData.getSelectedSignature(); // 支付工具签名
String seq = cashierData.getSeq(); // 响应唯一序列号
log.info("[收钱吧下单] 查询收银台成功,selectedSignature={}, seq={}", selectedSignature, seq);
List<CashierQueryData.PayTool> payTools = cashierData.getPayTools();
if (payTools.isEmpty()) {
log.error("[收钱吧下单] 支付工具为空");
return ResponseDto.failure("下单失败");
}
CashierQueryData.PayTool payTool = payTools.stream().filter(p ->
p.getType().equals(0)).collect(Collectors.toList()).get(0);
if (null == payTool) {
log.error("[收钱吧下单] 微信支付工具为空");
return ResponseDto.failure("下单失败");
}
// TODO 设置AppID
Map<String, Object> channelExt = new HashMap<>();
channelExt.put("sub_appid", "wx4732efeaa2b08086");
// 创建微信预支付订单
CreateWechatPrepayOrderData prepayData = sqbBiz.createWechatPrepayOrder(sqbAcquiringSn,
sqbAcquiringSign,
userId,
openId,
String.valueOf(cashierData.getAmount()),
buildRequestId(IDGenerator.nextSnowId()),
payTool,
channelExt,
selectedSignature,
seq
);
if (prepayData == null || prepayData.getPaymentVoucher() == null) {
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("创建微信预支付失败");
}
CreateWechatPrepayOrderData.PaymentVoucher pv = prepayData.getPaymentVoucher();
log.info("[收钱吧下单] 创建微信预支付订单成功,timeStamp={}", pv.getTimeStamp());
final String orderId = IDGenerator.nextSnowId();
String masterOrderCode = IDGenerator.nextTimeId();
String orderCode = IDGenerator.nextTimeId();
String now = LocalDateTime.now().format(DTF);
// 获取商品和 SKU 信息,供后续订单基础信息拼接使用
GoblinGoodsInfoVo goodsInfo = goblinMongoUtils.getGoodsInfoVo(spuId);
GoblinGoodsSkuInfoVo skuInfo = goblinMongoUtils.getGoodsSkuInfoVo(skuId);
// 1) 构建基础商城订单 VO (GoblinStoreOrderVo)
GoblinStoreOrderVo storeOrderVo = new GoblinStoreOrderVo();
storeOrderVo.setMasterOrderCode(masterOrderCode);
storeOrderVo.setOrderId(orderId);
storeOrderVo.setStoreId(goodsInfo != null ? goodsInfo.getStoreId() : "SQB_STORE");
storeOrderVo.setStoreName(goodsInfo != null ? goodsInfo.getStoreName() : "收钱吧商品聚合门店");
storeOrderVo.setOrderCode(orderCode);
storeOrderVo.setUserId(userId);
// 姓名手机号为了兼容留空或占位即可
storeOrderVo.setUserName("");
storeOrderVo.setUserMobile("");
BigDecimal skuPrice = skuInfo != null ? skuInfo.getPrice() : BigDecimal.ZERO;
BigDecimal priceTotal = skuPrice.multiply(new BigDecimal(quantity));
storeOrderVo.setPriceTotal(priceTotal);
storeOrderVo.setPriceActual(priceTotal); // 收钱吧暂无运费及优惠逻辑,实际价格等于总价
storeOrderVo.setPriceRefund(BigDecimal.ZERO);
storeOrderVo.setPriceExpress(BigDecimal.ZERO);
storeOrderVo.setPriceCoupon(BigDecimal.ZERO);
storeOrderVo.setStorePriceCoupon(BigDecimal.ZERO);
storeOrderVo.setPriceVoucher(BigDecimal.ZERO);
// 待支付
storeOrderVo.setStatus(0);
storeOrderVo.setPayType("SQB_PAY"); // 标识为收钱吧支付类型
storeOrderVo.setDeviceFrom("shouqianba");
storeOrderVo.setOrderType(0);
storeOrderVo.setWriteOffCode("EMPTY");
storeOrderVo.setPayCountdownMinute(15);
storeOrderVo.setCreatedAt(now);
// 2) 构建基础商城 SKU VO (GoblinOrderSkuVo)
String orderSkuId = IDGenerator.nextSnowId();
GoblinOrderSkuVo orderSkuVo = new GoblinOrderSkuVo();
orderSkuVo.setOrderSkuId(orderSkuId);
orderSkuVo.setOrderId(orderId);
orderSkuVo.setSpuId(spuId);
orderSkuVo.setSpuName(goodsInfo != null ? goodsInfo.getName() : "收钱吧未知商品");
orderSkuVo.setSkuId(skuId);
orderSkuVo.setNum(quantity);
orderSkuVo.setSkuPrice(skuPrice);
orderSkuVo.setSkuPriceActual(priceTotal); // 按件数总价
orderSkuVo.setSkuName(skuInfo != null ? skuInfo.getName() : "收钱吧未匹配SKU");
orderSkuVo.setSkuImage(skuInfo != null ? skuInfo.getSkuPic() : "");
orderSkuVo.setSkuSpecs("[]");
orderSkuVo.setPriceVoucher(BigDecimal.ZERO);
orderSkuVo.setCreatedAt(now);
// 特殊标识类型:33
orderSkuVo.setSkuType(33);
// 存入 Redis 并绑定关系
List<String> orderSkuVoIds = new ArrayList<>();
orderSkuVoIds.add(orderSkuId);
storeOrderVo.setOrderSkuVoIds(orderSkuVoIds);
goblinRedisUtils.setGoblinOrderSku(orderSkuId, orderSkuVo);
goblinRedisUtils.setGoblinOrder(orderId, storeOrderVo);
goblinRedisUtils.setMasterCode(masterOrderCode, orderId);
// 3) 构建收钱吧专属特有字段 (GoblinSqbOrderVo)
GoblinSqbOrderVo orderVo = new GoblinSqbOrderVo();
orderVo.setOrderId(orderId);
orderVo.setUserId(userId);
orderVo.setPerformancesId(performancesId);
orderVo.setOpenId(openId);
orderVo.setSpuId(spuId);
orderVo.setSkuId(skuId);
orderVo.setQuantity(quantity);
orderVo.setAmount(GoblinSqbConvertUtils.yuanToFen(priceTotal));
orderVo.setSqbOrderSn(sqbOrderSn);
orderVo.setSqbOrderSignature(sqbOrderSignature);
orderVo.setSqbAcquiringSn(sqbAcquiringSn);
orderVo.setSqbAcquiringSign(sqbAcquiringSign);
orderVo.setSqbCheckoutItemsId(checkoutItemsId);
orderVo.setRefundReason("");
// 内部状态同步使用
orderVo.setStatus(0);
orderVo.setCreatedAt(now);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
goblinSqbRedisUtils.addSqbOrderList(userId, orderId);
goblinSqbRedisUtils.setOrderIdBySqbOrderSn(sqbOrderSn, orderId);
log.info("[收钱吧下单] 订单写入 Redis 成功,orderId={}", orderId);
// Step 6: 写 MongoDB + 发送复用的 SQL MQ
// 写入 Mongo
goblinMongoUtils.insertGoblinOrderSkuVo(orderSkuVo);
goblinMongoUtils.insertGoblinStoreOrderVo(storeOrderVo);
// 收钱吧扩展暂无需单独写 MongoDB 除非专门需要查询
// 拼接 SQL 数据并丢队列
LinkedList<String> sqls = new LinkedList<>();
sqls.add(com.liquidnet.service.base.SqlMapping.get("goblin.order.create.sku_insert"));
sqls.add(com.liquidnet.service.base.SqlMapping.get("goblin.order.create.order_insert"));
sqls.add(com.liquidnet.service.base.SqlMapping.get("goblin_sqb_order.insert"));
LinkedList<Object[]> sqlDataSku = new LinkedList<>();
sqlDataSku.add(new Object[]{
orderSkuVo.getOrderSkuId(), orderSkuVo.getOrderId(), orderSkuVo.getSpuId(), orderSkuVo.getSpuName(), (goodsInfo != null ? goodsInfo.getCoverPic() : ""),
orderSkuVo.getSkuId(), orderSkuVo.getNum(), orderSkuVo.getSkuPrice(), orderSkuVo.getSkuPriceActual(), orderSkuVo.getSkuName(),
"", orderSkuVo.getSkuImage(), orderSkuVo.getSkuSpecs(), orderSkuVo.getPriceVoucher(),
"", "", "", "", 0, // ERP 等非必要参数直接置空
orderSkuVo.getCreatedAt()
});
LinkedList<Object[]> sqlDataOrder = new LinkedList<>();
sqlDataOrder.add(new Object[]{
storeOrderVo.getMasterOrderCode(), storeOrderVo.getOrderId(), storeOrderVo.getStoreId(), storeOrderVo.getStoreName(), storeOrderVo.getOrderCode(),
storeOrderVo.getUserId(), storeOrderVo.getUserName(), storeOrderVo.getUserMobile(), storeOrderVo.getPriceTotal(), storeOrderVo.getPayCode(),
storeOrderVo.getPriceActual(), storeOrderVo.getPriceRefund(), storeOrderVo.getPriceExpress(), storeOrderVo.getPriceCoupon(), storeOrderVo.getStorePriceCoupon(),
storeOrderVo.getPriceVoucher(), storeOrderVo.getStatus(), storeOrderVo.getUcouponId(), storeOrderVo.getStoreCouponId(), storeOrderVo.getPayType(), storeOrderVo.getDeviceFrom(),
storeOrderVo.getSource(), storeOrderVo.getVersion(), storeOrderVo.getIsMember(), storeOrderVo.getOrderType(), storeOrderVo.getWriteOffCode(), storeOrderVo.getPayCountdownMinute(),
storeOrderVo.getIpAddress(), storeOrderVo.getMarketId(), storeOrderVo.getMarketType(), storeOrderVo.getCreatedAt(), "", ""
});
// 收钱吧订单扩展表写库数据
// order_id, user_id, performances_id, spu_id, sku_id, quantity, amount(分),
// sqb_order_sn, sqb_order_signature, sqb_acquiring_sn, sqb_checkout_items_id, status, created_at, updated_at
LinkedList<Object[]> sqlDataSqbOrder = new LinkedList<>();
sqlDataSqbOrder.add(new Object[]{
orderId, userId, performancesId, spuId, skuId, quantity,
GoblinSqbConvertUtils.yuanToFen(priceTotal),
sqbOrderSn, sqbOrderSignature, sqbAcquiringSn, sqbAcquiringSign, checkoutItemsId,
0, now, now
});
// 借用 Base 模块中 ConsumerGoblinOrderCPRdsReceiver 这个队列消费类进行写库
if (queueUtils != null) {
queueUtils.sendMsgByRedis(com.liquidnet.service.base.constant.MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
com.liquidnet.service.base.SqlMapping.gets(sqls, sqlDataSku, sqlDataOrder, sqlDataSqbOrder));
// 待支付超时回调等处理也可放入原有股票队列逻辑,暂省略
}
log.info("[收钱吧下单] 完成 MongoDB 及 MQ 落库,orderId={}, masterCode={}", orderId, masterOrderCode);
// Step 7: 释放分布式锁,构建返回
goblinSqbRedisUtils.releaseOrderLock(userId, skuId);
GoblinSqbOrderCreateVo createVo = new GoblinSqbOrderCreateVo();
createVo.setOrderId(orderId);
createVo.setAcquiringSn(sqbAcquiringSn);
createVo.setTimeStamp(pv.getTimeStamp());
createVo.setPackageStr(pv.getPackageStr());
createVo.setPaySign(pv.getPaySign());
createVo.setAppId(pv.getAppId());
createVo.setSignType(pv.getSignType());
createVo.setNonceStr(pv.getNonceStr());
log.info("[收钱吧下单] 下单成功,orderId={}", orderId);
return ResponseDto.success(createVo);
} catch (Exception e) {
log.error("[收钱吧下单] 下单异常,userId={}, skuId={}", userId, skuId, e);
goblinRedisUtils.incrSkuStock(null, skuId, quantity);
return ResponseDto.failure("下单失败");
} finally {
goblinSqbRedisUtils.releaseOrderLock(userId, skuId);
}
}
/**
* 构建收钱吧-结算明细条目
*
* @param skuVo
* @param quantity
* @param sqbSkuId
* @param sqbSpuId
* @return
*/
private SettlementCreateRequest.CheckoutItem buildSqbCheckOutItem(GoblinGoodsSkuInfoVo skuVo,
Integer quantity,
String sqbSkuId,
String sqbSpuId) {
// 获取商品与收钱吧商品对应的关联的skuId、spuId
SettlementCreateRequest.CheckoutItem checkoutItem = new SettlementCreateRequest.CheckoutItem();
checkoutItem.setSpuId(sqbSpuId);
checkoutItem.setSkuId(sqbSkuId);
checkoutItem.setPrice(GoblinSqbConvertUtils.yuanToFen(skuVo.getPrice()));
checkoutItem.setQuantity(String.valueOf(quantity));
checkoutItem.setType((byte) 0);
checkoutItem.setTitle(skuVo.getName());
checkoutItem.setImage(skuVo.getSkuPic());
return checkoutItem;
}
// ================================ 查询支付状态 ================================
@Override
public ResponseDto<Integer> queryPayStatus(String userId, String 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("无权限访问该订单");
}
return ResponseDto.success(orderVo.getStatus());
}
// ================================ 回调处理 ================================
@Override
public ResponseDto<String> handlePayCallback(OrderCallbackContent orderCallbackContent) {
log.info("[收钱吧支付回调] 收到回调参数: {}", orderCallbackContent);
String sqbOrderSn = orderCallbackContent.getOrderSn();
if (StringUtil.isBlank(sqbOrderSn)) return ResponseDto.failure("缺少订单标识");
// 可能需要根据收钱吧的orderSn查询正在的orderId
final String orderId = goblinSqbRedisUtils.getOrderIdBySqbOrderSn(sqbOrderSn);
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) return ResponseDto.failure("订单不存在");
if (Integer.valueOf(1).equals(orderVo.getStatus())) return ResponseDto.success("success"); // 幂等
String now = LocalDateTime.now().format(DTF);
orderVo.setStatus(1);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 1);
log.info("[收钱吧支付回调] 更新支付状态成功,orderId={}", orderId);
return ResponseDto.success("success");
}
@Override
public ResponseDto<String> handleRefundCallback(RefundCallbackContent refundCallbackContent) {
log.info("[收钱吧退款回调] 收到回调参数: {}", refundCallbackContent);
String sqbOrderSn = refundCallbackContent.getOrderSn();
if (StringUtil.isBlank(sqbOrderSn)) return ResponseDto.failure("缺少订单标识");
// 可能需要根据收钱吧的orderSn查询正在的orderId
final String orderId = goblinSqbRedisUtils.getOrderIdBySqbOrderSn(sqbOrderSn);
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) return ResponseDto.failure("订单不存在");
if (Integer.valueOf(3).equals(orderVo.getStatus())) return ResponseDto.success("success"); // 幂等
String now = LocalDateTime.now().format(DTF);
orderVo.setStatus(3);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity());
syncOrderStatus(orderId, 3);
log.info("[收钱吧退款回调] 更新退款状态成功,orderId={}", orderId);
return ResponseDto.success("success");
}
@Override
public ResponseDto<String> handleCouponCallback(CouponCallbackContent callbackContent) {
log.info("[收钱吧券状态回调] 收到回调参数: {}", callbackContent);
String sqbOrderSn = callbackContent.getOrderSn();
if (StringUtil.isBlank(sqbOrderSn)) return ResponseDto.failure("缺少订单标识");
// 可能需要根据收钱吧的orderSn查询正在的orderId
final String orderId = goblinSqbRedisUtils.getOrderIdBySqbOrderSn(sqbOrderSn);
GoblinSqbOrderVo orderVo = goblinSqbRedisUtils.getSqbOrder(orderId);
if (orderVo == null) return ResponseDto.failure("订单不存在");
if (Integer.valueOf(2).equals(orderVo.getStatus())) return ResponseDto.success("success"); // 幂等
String now = LocalDateTime.now().format(DTF);
orderVo.setStatus(2);
orderVo.setCouponUsedStatus(1);
orderVo.setUpdatedAt(now);
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
syncOrderStatus(orderId, 2);
log.info("[收钱吧券状态回调] 核销状态更新成功,orderId={}", orderId);
return ResponseDto.success("success");
}
// ================================ 再次付款 ================================
@Override
public ResponseDto<GoblinSqbOrderCreateVo> repay(String userId, String 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("无权限访问该订单");
if (!Integer.valueOf(0).equals(orderVo.getStatus())) {
return ResponseDto.failure("订单状态不支持再次付款(仅待支付状态可重新拉起)");
}
if (orderVo.getSqbAcquiringSn() == null) {
return ResponseDto.failure("订单数据异常,缺少收单号");
}
try {
if (orderVo.getOpenId() == null || orderVo.getOpenId().trim().isEmpty()) {
return ResponseDto.failure("缺少微信 openId,无法拉起支付");
}
// 重新查询收银台,获取新的 selectedSignature + seq
CashierQueryData cashierData = sqbBiz.queryCashier(orderVo.getSqbAcquiringSn(), orderVo.getSqbOrderSignature(), userId);
if (cashierData == null) return ResponseDto.failure("查询收银台失败,请稍后重试");
List<CashierQueryData.PayTool> payTools = cashierData.getPayTools();
if (payTools.isEmpty()) {
log.error("[收钱吧下单] 支付工具为空");
return ResponseDto.failure("下单失败");
}
CashierQueryData.PayTool payTool = payTools.stream().filter(p ->
p.getType().equals(0)).collect(Collectors.toList()).get(0);
if (null == payTool) {
log.error("[收钱吧下单] 微信支付工具为空");
return ResponseDto.failure("下单失败");
}
// TODO 设置AppID
Map<String, Object> channelExt = new HashMap<>();
channelExt.put("sub_appid", "wx4732efeaa2b08086");
// 重新创建微信预支付
CreateWechatPrepayOrderData prepayData = sqbBiz.createWechatPrepayOrder(
orderVo.getSqbAcquiringSn(),
orderVo.getSqbAcquiringSign(),
userId,
orderVo.getOpenId(),
String.valueOf(cashierData.getAmount()),
buildRequestId(IDGenerator.nextSnowId()),
payTool,
channelExt,
cashierData.getSelectedSignature(),
cashierData.getSeq()
);
if (prepayData == null || prepayData.getPaymentVoucher() == null) {
return ResponseDto.failure("创建微信预支付失败,请稍后重试");
}
CreateWechatPrepayOrderData.PaymentVoucher pv = prepayData.getPaymentVoucher();
GoblinSqbOrderCreateVo createVo = new GoblinSqbOrderCreateVo();
createVo.setOrderId(orderId);
createVo.setAcquiringSn(orderVo.getSqbAcquiringSn());
createVo.setTimeStamp(pv.getTimeStamp());
createVo.setPackageStr(pv.getPackageStr());
createVo.setPaySign(pv.getPaySign());
createVo.setAppId(pv.getAppId());
createVo.setSignType(pv.getSignType());
createVo.setNonceStr(pv.getNonceStr());
log.info("[收钱吧再次付款] 成功,orderId={}", orderId);
return ResponseDto.success(createVo);
} catch (Exception e) {
log.error("[收钱吧再次付款] 异常,orderId={}", orderId, e);
return ResponseDto.failure("再次付款失败:" + e.getMessage());
}
}
// ================================ 私有辅助方法 ================================
private CommonRequest.Seller buildSeller() {
CommonRequest.Seller seller = new CommonRequest.Seller();
seller.setMerchantId(sqbBiz.getSqbConfig().getMerchantId());
seller.setMerchantUserId(sqbBiz.getSqbConfig().getMerchantUserId());
seller.setRole(sqbBiz.getSqbConfig().getRole());
return seller;
}
private CashierQueryRequest buildCashierQueryRequest(CommonRequest.Seller seller,
String acquiringSn,
String acquiringSignature) {
CashierQueryRequest req = new CashierQueryRequest();
req.setAppid(sqbBiz.getSqbConfig().getAppId());
req.setSeller(seller);
req.setPaymentMode(4); // 4-微信小程序支付
CashierQueryRequest.PaymentEnv paymentEnv = new CashierQueryRequest.PaymentEnv();
paymentEnv.setClient("wechat");
req.setPaymentEnv(paymentEnv);
CommonRequest.Acquiring acquiring = new CommonRequest.Acquiring();
acquiring.setAcquiringSn(acquiringSn);
acquiring.setSignature(acquiringSignature);
req.setAcquiringInfo(acquiring);
return req;
}
private CreateWechatPrepayOrderRequest buildWechatPrepayRequest(CommonRequest.Seller seller,
String acquiringSn,
String acquiringSignature,
String selectedSignature,
String seq,
CashierQueryData cashierData) {
CreateWechatPrepayOrderRequest req = new CreateWechatPrepayOrderRequest();
req.setAppid(sqbBiz.getSqbConfig().getAppId());
req.setSeller(seller);
req.setAcquiringSn(acquiringSn);
req.setSignature(acquiringSignature);
req.setSelectedSignature(selectedSignature);
req.setSeq(seq);
// 使用收银台返回的第一个可用支付工具
if (cashierData != null && !CollectionUtils.isEmpty(cashierData.getPayTools())) {
List<CreateWechatPrepayOrderRequest.UsingPayTool> usingPayTools = new ArrayList<>();
cashierData.getPayTools().stream()
.filter(t -> Boolean.TRUE.equals(t.getSelectable()))
.findFirst()
.ifPresent(payTool -> {
CreateWechatPrepayOrderRequest.UsingPayTool usingTool = new CreateWechatPrepayOrderRequest.UsingPayTool();
usingTool.setId(payTool.getId());
usingTool.setPayTool(payTool.getType());
usingTool.setPayMode(4);
usingTool.setAmount(payTool.getShowAmount() != null ? String.valueOf(payTool.getShowAmount()) : "0");
usingTool.setIdentity(null);
usingTool.setRequestSn(IDGenerator.nextSnowId());
usingPayTools.add(usingTool);
});
req.setUsingPayTools(usingPayTools);
}
return req;
}
/**
* 构建收钱吧请求唯一ID,规则:AppCode(4位)+ id,总长不超过40位。
*
* @param id 业务唯一标识(如雪花ID)
* @return 不超过40位的请求ID字符串
*/
private String buildRequestId(String id) {
String appCode = sqbBiz.getSqbConfig().getAppCode();
if (appCode == null) appCode = "";
String raw = appCode + id;
return raw.length() > 40 ? raw.substring(0, 40) : raw;
}
/**
* 构建收钱吧订单标题,不超过32位。
*
* @param name 商品名称
* @return 不超过32位的标题字符串
*/
private String buildTitle(String name) {
if (name == null || name.isEmpty()) return "商品支付";
return name.length() > 32 ? name.substring(0, 32) : name;
}
private void syncOrderStatus(String orderId, int status) {
final int zhengzaiStatus = sqbOrderStatusConvert(status);
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
if (storeOrderVo == null) return;
String now = LocalDateTime.now().format(DTF);
storeOrderVo.setStatus(zhengzaiStatus);
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) { // 支付成功
storeOrderVo.setPayTime(now);
}
// 1. 同步 Redis
goblinRedisUtils.setGoblinOrder(orderId, storeOrderVo);
// 2. 同步 MongoDB
goblinMongoUtils.updateGoblinStoreOrderVo(orderId, storeOrderVo);
// 3. 同步 MySQL (复用下单 MQ 发送 Update 语句)
String sql;
Object[] data;
if (zhengzaiStatus == GoblinStatusConst.Status.ORDER_STATUS_2.getValue()) {
sql = "UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ?";
data = new Object[]{zhengzaiStatus, storeOrderVo.getPayTime(), now, orderId};
} else {
sql = "UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ?";
data = new Object[]{zhengzaiStatus, now, orderId};
}
java.util.LinkedList<String> sqls = new java.util.LinkedList<>();
sqls.add(sql);
java.util.LinkedList<Object[]> sqlData = new java.util.LinkedList<>();
sqlData.add(data);
if (queueUtils != null) {
queueUtils.sendMsgByRedis(com.liquidnet.service.base.constant.MQConst.GoblinQueue.GOBLIN_ORDER_CREATE_PAY.getKey(),
com.liquidnet.service.base.SqlMapping.gets(sqls, sqlData));
}
}
/**
* goblin_sqb_order表status 转换成 goblin_store_order表对应status
*
* @param sqbOrderStatus
* @return
*/
private int sqbOrderStatusConvert(Integer sqbOrderStatus) {
if (null == sqbOrderStatus) {
return -1;
}
switch (sqbOrderStatus) {
case 0:
// 0-待支付 -> 对应枚举 ORDER_STATUS_0
return GoblinStatusConst.Status.ORDER_STATUS_0.getValue();
case 1:
// 1-已支付 -> 对应枚举 ORDER_STATUS_2 (代发货)
return GoblinStatusConst.Status.ORDER_STATUS_2.getValue();
case 2:
// 2-已核销 -> 对应枚举 ORDER_STATUS_4 (已完成)
return GoblinStatusConst.Status.ORDER_STATUS_4.getValue();
case 3:
// 3-已退款 -> 对应枚举 ORDER_STATUS_6 (退款通过)
return GoblinStatusConst.Status.ORDER_STATUS_6.getValue();
case 4:
// 4-退款中 -> 对应枚举 ORDER_STATUS_61 (发起-退款)
return GoblinStatusConst.Status.ORDER_STATUS_61.getValue();
case 5:
// 5-已取消 -> 对应枚举 ORDER_STATUS_5
return GoblinStatusConst.Status.ORDER_STATUS_5.getValue();
case 9:
// 9-失败 -> 这里归类为 ORDER_STATUS_5 (取消/终态),如有特殊需求可调整
return sqbOrderStatus;
default:
return sqbOrderStatus;
}
}
}
package com.liquidnet.service.order.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 收钱吧参数转换工具类
* 1. 金额转换:元(BigDecimal) -> 分(Long)
*/
public class GoblinSqbConvertUtils {
/**
* 元转分 (BigDecimal -> Long)
*
* @param amount 元
* @return 分
*/
public static Long yuanToFen(BigDecimal amount) {
if (amount == null) {
return 0L;
}
// 乘以 100 并设置四舍五入,确保转换后的精度
return amount.multiply(new BigDecimal("100"))
.setScale(0, RoundingMode.HALF_UP)
.longValue();
}
}
package com.liquidnet.service.order.utils;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.goblin.constant.GoblinRedisConst;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbGoodsExtVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderVo;
import com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
/**
* 收钱吧相关 Redis 操作封装
*/
@Slf4j
@Component
public class GoblinSqbRedisUtils {
@Autowired
private RedisUtil redisUtil;
/* ---------------------------------------- 订单操作(TTL 2h) ---------------------------------------- */
public void setSqbOrder(String orderId, GoblinSqbOrderVo vo) {
redisUtil.set(GoblinRedisConst.SQB_ORDER.concat(orderId), vo, RedisKeyExpireConst.SQB_ORDER_EXPIRE);
}
public GoblinSqbOrderVo getSqbOrder(String orderId) {
String redisKey = GoblinRedisConst.SQB_ORDER.concat(orderId);
Object object = redisUtil.get(redisKey);
if (null == object) {
return null;
} else {
return (GoblinSqbOrderVo) object;
}
}
public void delSqbOrder(String orderId) {
redisUtil.del(GoblinRedisConst.SQB_ORDER.concat(orderId));
}
/* ---------------------------------------- 演出关联商品缓存(TTL 5min) ---------------------------------------- */
/**
* 获取演出关联商品
*
* @param performancesId
* @return
*/
public List<GoblinSqbPerformanceGoods> getSqbPerformanceGoodsListCache(String performancesId) {
String key = GoblinRedisConst.SQB_PERFORMANCE_GOODS.concat(performancesId);
Object object = redisUtil.get(key);
if (null == object) {
return Collections.emptyList();
} else {
return (List<GoblinSqbPerformanceGoods>) object;
}
}
/* ---------------------------------------- 下单防重锁(TTL 10s) ---------------------------------------- */
/**
* 尝试获取下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
* @return true 获取成功,false 已被锁定
*/
public boolean tryOrderLock(String userId, String skuId) {
String key = GoblinRedisConst.SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
return redisUtil.lock(key, 1, RedisKeyExpireConst.SQB_ORDER_LOCK_EXPIRE);
}
/**
* 释放下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
*/
public void releaseOrderLock(String userId, String skuId) {
String key = GoblinRedisConst.SQB_ORDER_LOCK.concat(userId).concat(":").concat(skuId);
redisUtil.uLock(key);
}
/**
* 添加用户 收钱吧订单列表
*
* @param userId
* @param orderId
*/
public void addSqbOrderList(String userId, String orderId) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_ORDER_LIST.concat(userId);
List<String> list = getSqbOrderList(userId);
if (list.size() >= 40) {
list.remove(0);
list.add(orderId);
} else {
list.add(orderId);
}
redisUtil.set(redisKey, list);
}
/**
* 获取 收钱吧订单id列表
*
* @param uid
* @return
*/
public List<String> getSqbOrderList(String uid) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_ORDER_LIST.concat(uid);
Object obj = redisUtil.get(redisKey);
if (obj == null) {
return CollectionUtil.arrayListString();
} else {
return (List<String>) obj;
}
}
/**
* 获取正在商品获取收钱吧相关信息缓存
*
* @param spuId
* @param skuId
* @return
*/
public GoblinSqbGoodsExtVo getSqbGoodsExt(String spuId, String skuId) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_GOODS_EXT_KEY.concat(spuId).concat(":").concat(skuId);
Object obj = redisUtil.get(redisKey);
if (obj == null) {
return null;
} else {
return (GoblinSqbGoodsExtVo) obj;
}
}
/**
* 设置 收钱吧orderSn 与正在orderId 对应关系
* @param sqbOrderSn
* @param orderId
*/
public void setOrderIdBySqbOrderSn(String sqbOrderSn, String orderId) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_ORDER_SN_KEY.concat(sqbOrderSn);
redisUtil.set(redisKey, orderId);
}
/**
* 获取 收钱吧orderSn 与正在orderId 对应关系
* @return
*/
public String getOrderIdBySqbOrderSn(String sqbOrderSn) {
String redisKey = GoblinRedisConst.SQB_GOBLIN_ORDER_SN_KEY.concat(sqbOrderSn);
Object object = redisUtil.get(redisKey);
if (object == null) {
return null;
} else {
return (String) object;
}
}
}
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