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

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

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

parents 5779fdfa 7fb073da
......@@ -47,9 +47,8 @@ public class GoblinStoreMgtGoodsEditSkuParam implements Serializable {
@NotNull(message = "单品默认图片不能为空")
@Size(max = 256, message = "单品默认图片URL过长")
private String skuPic;
@ApiModelProperty(position = 14, required = true, value = "单品规格信息")
@ApiModelProperty(position = 14, required = false, value = "单品规格信息;编辑时可不传或传[]表示沿用/无规格(如收钱吧同步SKU)")
@Valid
@NotNull(message = "规格信息不能为空")
private List<GoblinGoodsSpecDto> skuSpecList;
@ApiModelProperty(position = 15, required = false, value = "单品销售价-原价[20,2]")
private BigDecimal sellPrice;
......
......@@ -35,4 +35,10 @@ public class GoblinAdminSqbGoodsVo implements Serializable {
@ApiModelProperty(value = "单品库存")
private Integer skuStock;
@ApiModelProperty(value = "是否允许关联演出(SPU+SKU 均上架且未删除)")
private Boolean linkable;
@ApiModelProperty(value = "上下架展示:上架 / 已下架 等")
private String shelfStatusLabel;
}
......@@ -42,4 +42,10 @@ public class GoblinSqbPerfLinkedGoodsVo implements Serializable {
@ApiModelProperty(value = "状态 0-禁用 1-启用")
private Integer status;
@ApiModelProperty(value = "是否满足上架可售(与前台一致:SPU/SKU 上架且未删)")
private Boolean linkable;
@ApiModelProperty(value = "上下架展示文案")
private String shelfStatusLabel;
}
......@@ -51,6 +51,7 @@
<th>商品ID</th>
<th>商品头图</th>
<th>商品名称</th>
<th>状态</th>
<th>商品售价(元)</th>
<th>换购价(元)</th>
<th>商品库存</th>
......@@ -59,7 +60,7 @@
</thead>
<tbody id="linkedGoodsBody">
<tr id="emptyRow">
<td colspan="7" style="text-align:center;color:#999;">暂无关联商品</td>
<td colspan="8" style="text-align:center;color:#999;">暂无关联商品</td>
</tr>
</tbody>
</table>
......@@ -129,7 +130,8 @@
var spu = item.spuTitle != null ? item.spuTitle : item.spuName;
var sku = item.skuTitle != null ? item.skuTitle : item.skuName;
var text = item.title || formatSqbSpuSkuLabel(spu, sku);
return { id: item.skuId, text: text, _raw: item };
var linkable = item.linkable !== false;
return { id: item.skuId, text: text, disabled: !linkable, _raw: item };
})};
}
}
......@@ -138,6 +140,11 @@
// 选中即添加
$('#goodsSelect').on('select2:select', function (e) {
var raw = e.params.data._raw;
if (raw && raw.linkable === false) {
layer.msg('该商品已下架,不可关联', { icon: 2 });
$(this).val(null).trigger('change');
return;
}
if (raw) { addLinked(raw); }
// 清空选择框
$(this).val(null).trigger('change');
......@@ -158,6 +165,8 @@
skuId: item.skuId, spuId: item.spuId,
spuName: item.spuName || '', skuName: item.skuName || '',
coverPic: item.coverPic || '', price: item.price, stock: item.stock,
shelfStatusLabel: item.shelfStatusLabel || '',
linkable: item.linkable !== false,
settlementPrice: (item.settlementPrice != null ? item.settlementPrice : ''),
persisted: true
};
......@@ -170,7 +179,7 @@
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>';
tbody.innerHTML = '<tr id="emptyRow"><td colspan="8" style="text-align:center;color:#999;">暂无关联商品</td></tr>';
return;
}
var html = '';
......@@ -181,10 +190,13 @@
var imgHtml = item.coverPic
? '<img src="' + item.coverPic + '" class="img-thumb" title="点击放大" alt="商品头图"/>'
: '<span style="color:#ccc;font-size:12px;">无图</span>';
var shelfTxt = item.shelfStatusLabel || (item.linkable === false ? '已下架' : '上架');
var shelfStyle = item.linkable === false ? 'color:#c00;font-weight:bold;' : 'color:#090;';
html += '<tr data-idx="' + idx + '">';
html += '<td><small>' + item.skuId + '</small></td>';
html += '<td>' + imgHtml + '</td>';
html += '<td>' + formatSqbSpuSkuLabel(item.spuName, item.skuName) + '</td>';
html += '<td style="' + shelfStyle + '">' + shelfTxt + '</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>';
......@@ -196,6 +208,10 @@
}
function addLinked(item) {
if (item.linkable === false) {
layer.msg('该商品已下架,不可关联', { icon: 2 });
return;
}
for (var i = 0; i < linkedGoods.length; i++) {
if (linkedGoods[i].skuId === item.skuId) {
layer.msg('该商品已在关联列表中');
......@@ -209,6 +225,8 @@
coverPic: item.skuPic || item.coverPic || '',
price: item.price,
stock: (item.skuStock != null ? item.skuStock : item.stock),
shelfStatusLabel: item.shelfStatusLabel || (item.linkable === false ? '已下架' : '上架'),
linkable: item.linkable !== false,
settlementPrice: null, autoOffline: 0, persisted: false
});
renderLinkedGoods();
......
......@@ -7,6 +7,8 @@ import com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsSe
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.GoblinGoodsInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfLinkedGoodsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfListRespVo;
import com.liquidnet.service.goblin.entity.GoblinGoods;
......@@ -42,6 +44,88 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
/** skuType=33 代表收钱吧商品 */
private static final int SQB_SKU_TYPE = 33;
/**
* 与实体/上下架接口约定一致:0-待上架|1-下架|2-违规|3-上架(见 {@link GoblinGoods#getShelvesStatus} 注释)
*/
private static final String SHELF_WAIT = "0";
private static final String SHELF_OFF = "1";
private static final String SHELF_VIOLATION = "2";
private static final String SHELF_ON = "3";
private static String trimFlg(String s) {
return s == null ? "" : s.trim();
}
/** 演出关联列表:是否可售 + 展示文案(与 C 端 {@code GoblinFrontServiceImpl#getPerformanceSelectGoods} 过滤条件对齐) */
private static final class PerfShelfEval {
final boolean linkable;
final String statusLabel;
PerfShelfEval(boolean linkable, String statusLabel) {
this.linkable = linkable;
this.statusLabel = statusLabel;
}
}
/**
* Redis 中 SPU、SKU 均有缓存时优先用缓存;否则用 MySQL 行。可售=未删+SPU 展示+非活动独占+SPU/SKU 均为上架(3)。
*/
private PerfShelfEval evalPerfShelfState(String skuId, String spuId, GoblinGoodsSku mysqlSku, GoblinGoods mysqlSpu) {
String sid = skuId == null ? "" : skuId.trim();
String pid = spuId == null ? "" : spuId.trim();
if (!StringUtils.hasText(sid) || !StringUtils.hasText(pid)) {
return new PerfShelfEval(false, "数据异常");
}
GoblinGoodsSkuInfoVo skuVo = goblinRedisUtils.getGoodsSkuInfoVo(sid);
GoblinGoodsInfoVo spuVo = goblinRedisUtils.getGoodsInfoVo(pid);
if (skuVo != null && spuVo != null) {
return evalFromSnapshot(trimFlg(skuVo.getDelFlg()), trimFlg(spuVo.getDelFlg()),
trimFlg(skuVo.getShelvesStatus()), trimFlg(spuVo.getShelvesStatus()),
trimFlg(spuVo.getSpuAppear()), spuVo.getMarketId(), true);
}
if (mysqlSku == null || mysqlSpu == null) {
return new PerfShelfEval(false, "缺少商品数据");
}
return evalFromSnapshot(trimFlg(mysqlSku.getDelFlg()), trimFlg(mysqlSpu.getDelFlg()),
trimFlg(mysqlSku.getShelvesStatus()), trimFlg(mysqlSpu.getShelvesStatus()),
trimFlg(mysqlSpu.getSpuAppear()), null, false);
}
/**
* @param hasMarketId true 表示来自 Redis,可检查 marketId;MySQL DO 无该字段时传 false
*/
private static PerfShelfEval evalFromSnapshot(String skuDel, String spuDel, String skuShelf, String spuShelf,
String spuAppear, String marketId, boolean hasMarketId) {
if (!"0".equals(skuDel) || !"0".equals(spuDel)) {
return new PerfShelfEval(false, "已删除");
}
if (!"0".equals(spuAppear)) {
return new PerfShelfEval(false, "SPU隐藏");
}
if (hasMarketId && StringUtils.hasText(marketId)) {
return new PerfShelfEval(false, "活动商品");
}
boolean on = SHELF_ON.equals(skuShelf) && SHELF_ON.equals(spuShelf);
if (on) {
return new PerfShelfEval(true, "上架");
}
return new PerfShelfEval(false, humanizeNotOnShelf(skuShelf, spuShelf));
}
/** 非「上架」时的文案:区分待上架(0)、下架(1)、违规(2),避免把同步初始态 0 误标成「已下架」 */
private static String humanizeNotOnShelf(String skuShelf, String spuShelf) {
if (SHELF_WAIT.equals(skuShelf) || SHELF_WAIT.equals(spuShelf)) {
return "待上架";
}
if (SHELF_OFF.equals(skuShelf) || SHELF_OFF.equals(spuShelf)) {
return "已下架";
}
if (SHELF_VIOLATION.equals(skuShelf) || SHELF_VIOLATION.equals(spuShelf)) {
return "违规";
}
return "未上架";
}
private static String sqbSpuSkuDisplay(String spuTitle, String skuTitle) {
String s = spuTitle != null ? spuTitle.trim() : "";
String k = skuTitle != null ? skuTitle.trim() : "";
......@@ -117,17 +201,28 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
GoblinGoodsSku sku = goblinGoodsSkuMapper.selectOne(skuQuery);
if (sku == null) {
log.warn("[演出商品关联] SKU不存在,skuId={}", skuId);
continue;
return ResponseDto.failure("SKU不存在:" + skuId);
}
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 (sku.getSkuType() == null || sku.getSkuType() != SQB_SKU_TYPE) {
return ResponseDto.failure("仅可关联收钱吧商品(skuType=33),skuId=" + skuId);
}
if (existing == null) {
GoblinGoods spu = goblinGoodsMapper.selectOne(new LambdaQueryWrapper<GoblinGoods>()
.eq(GoblinGoods::getSpuId, sku.getSpuId()).last("LIMIT 1"));
if (!evalPerfShelfState(skuId, sku.getSpuId(), sku, spu).linkable) {
return ResponseDto.failure("商品未上架或已下架,不可关联。请先让商户上架后再操作。skuId=" + skuId);
}
}
int sortVal = item.getSort() != null ? item.getSort() : 0;
if (existing != null) {
LambdaUpdateWrapper<GoblinSqbPerformanceGoods> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
......@@ -229,14 +324,17 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
.collect(Collectors.toList());
Map<String, String> spuNameById = new HashMap<>();
Map<String, String> spuCoverById = new HashMap<>();
Map<String, GoblinGoods> spuById = new HashMap<>();
if (!spuIdList.isEmpty()) {
LambdaQueryWrapper<GoblinGoods> goodsQuery = new LambdaQueryWrapper<>();
goodsQuery.in(GoblinGoods::getSpuId, spuIdList)
.select(GoblinGoods::getSpuId, GoblinGoods::getName, GoblinGoods::getCoverPic);
.select(GoblinGoods::getSpuId, GoblinGoods::getName, GoblinGoods::getCoverPic,
GoblinGoods::getShelvesStatus, GoblinGoods::getDelFlg, GoblinGoods::getSpuAppear);
for (GoblinGoods g : goblinGoodsMapper.selectList(goodsQuery)) {
if (g.getSpuId() != null) {
spuNameById.put(g.getSpuId(), g.getName());
spuCoverById.put(g.getSpuId(), g.getCoverPic());
spuById.put(g.getSpuId(), g);
}
}
}
......@@ -250,13 +348,21 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
vo.setSettlementPrice(rel.getSettlementPrice());
vo.setStatus(rel.getStatus());
GoblinGoodsSku sku = skuMap.get(rel.getSkuId());
String relSkuId = rel.getSkuId() == null ? null : rel.getSkuId().trim();
GoblinGoodsSku sku = relSkuId == null ? null : skuMap.get(relSkuId);
if (sku != null) {
vo.setSkuName(sku.getName());
vo.setSpuName(spuNameById.getOrDefault(rel.getSpuId(), ""));
vo.setCoverPic(pickSkuOrSpuCover(sku.getSkuPic(), spuCoverById.get(rel.getSpuId())));
vo.setPrice(sku.getPrice());
vo.setStock(sku.getSkuStock());
GoblinGoods spu = spuById.get(rel.getSpuId());
PerfShelfEval ev = evalPerfShelfState(relSkuId, rel.getSpuId(), sku, spu);
vo.setLinkable(ev.linkable);
vo.setShelfStatusLabel(ev.statusLabel);
} else {
vo.setLinkable(false);
vo.setShelfStatusLabel("无SKU记录");
}
goodsList.add(vo);
}
......@@ -273,7 +379,7 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
public ResponseDto<List<GoblinAdminSqbGoodsVo>> searchGoods(String keyword) {
try {
LambdaQueryWrapper<GoblinGoodsSku> skuQuery = new LambdaQueryWrapper<>();
skuQuery.eq(GoblinGoodsSku::getSkuType, SQB_SKU_TYPE);
skuQuery.eq(GoblinGoodsSku::getSkuType, SQB_SKU_TYPE).ne(GoblinGoodsSku::getDelFlg, "1");
if (StringUtils.hasText(keyword)) {
LambdaQueryWrapper<GoblinGoods> goodsQuery = new LambdaQueryWrapper<>();
goodsQuery.select(GoblinGoods::getSpuId)
......@@ -304,14 +410,17 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
.collect(Collectors.toList());
Map<String, String> spuTitleById = new HashMap<>();
Map<String, String> spuCoverById = new HashMap<>();
Map<String, GoblinGoods> spuRowById = new HashMap<>();
if (!spuIdForTitle.isEmpty()) {
LambdaQueryWrapper<GoblinGoods> gq = new LambdaQueryWrapper<>();
gq.in(GoblinGoods::getSpuId, spuIdForTitle)
.select(GoblinGoods::getSpuId, GoblinGoods::getName, GoblinGoods::getCoverPic);
.select(GoblinGoods::getSpuId, GoblinGoods::getName, GoblinGoods::getCoverPic,
GoblinGoods::getShelvesStatus, GoblinGoods::getDelFlg, GoblinGoods::getSpuAppear);
for (GoblinGoods g : goblinGoodsMapper.selectList(gq)) {
if (g.getSpuId() != null) {
spuTitleById.put(g.getSpuId(), g.getName());
spuCoverById.put(g.getSpuId(), g.getCoverPic());
spuRowById.put(g.getSpuId(), g);
}
}
}
......@@ -325,7 +434,11 @@ public class SqbPerformanceGoodsServiceImpl implements ISqbPerformanceGoodsServi
String skuTitle = sku.getName() != null ? sku.getName() : "";
vo.setSpuTitle(spuTitle);
vo.setSkuTitle(skuTitle);
vo.setTitle(sqbSpuSkuDisplay(spuTitle, skuTitle));
GoblinGoods spu = spuRowById.get(sku.getSpuId());
PerfShelfEval ev = evalPerfShelfState(sku.getSkuId(), sku.getSpuId(), sku, spu);
vo.setLinkable(ev.linkable);
vo.setShelfStatusLabel(ev.statusLabel);
vo.setTitle(sqbSpuSkuDisplay(spuTitle, skuTitle) + (ev.linkable ? "" : "(" + ev.statusLabel + ")"));
vo.setSkuPic(pickSkuOrSpuCover(sku.getSkuPic(), spuCoverById.get(sku.getSpuId())));
vo.setPrice(sku.getPrice());
vo.setSkuStock(sku.getSkuStock());
......
......@@ -224,6 +224,21 @@ public class GoblinRedisUtils {
return vo;
}
/**
* 与 goblin 服务一致:SPU 基础缓存(用于后台与 C 端上下架状态对齐)
*/
public GoblinGoodsInfoVo getGoodsInfoVo(String spuId) {
if (spuId == null || spuId.isEmpty()) {
return null;
}
String rk = GoblinRedisConst.BASIC_GOODS.concat(spuId);
GoblinGoodsInfoVo vo = (GoblinGoodsInfoVo) redisDataSourceUtil.getRedisGoblinUtil().get(rk);
if (null == vo) {
return null;
}
return vo;
}
//覆盖 站位宾哥要dto
public void setNftNumDetails(String num, String skuId, GalaxyNftPublishAndBuyReqDto dto) {
String redisKey = GoblinRedisConst.GOBLIN_NUM_DETAILS.concat(num).concat(skuId);
......
......@@ -948,8 +948,12 @@ public class GoblinStoreMgtGoodsController {
Map<String, String> delSpuSpecMap = CollectionUtil.mapStringString();
Map<String, String> addSpuSpecMap = CollectionUtil.mapStringString();
{// 规格校验、处理
Map<String, String> befSkuSpecNameVnameMap = mgtGoodsSkuInfoVo.getSkuSpecList().stream()
.collect(Collectors.toMap(GoblinGoodsSpecDto::getSpecName, GoblinGoodsSpecDto::getSpecVname));
if (null == mgtGoodsEditSkuParam.getSkuSpecList()) {
mgtGoodsEditSkuParam.setSkuSpecList(new ArrayList<>());
}
List<GoblinGoodsSpecDto> befSkuSpecList = mgtGoodsSkuInfoVo.getSkuSpecList();
Map<String, String> befSkuSpecNameVnameMap = CollectionUtils.isEmpty(befSkuSpecList) ? CollectionUtil.mapStringString()
: befSkuSpecList.stream().collect(Collectors.toMap(GoblinGoodsSpecDto::getSpecName, GoblinGoodsSpecDto::getSpecVname));
List<GoblinGoodsSpecDto> paramSkuSpecList = mgtGoodsEditSkuParam.getSkuSpecList();
Set<String> befSkuSpecNameSet = befSkuSpecNameVnameMap.keySet();
Map<String, String> paramUpdateSkuSpecMap = CollectionUtil.mapStringString();
......@@ -1268,6 +1272,9 @@ public class GoblinStoreMgtGoodsController {
}
}
}
if (CollectionUtils.isEmpty(mgtGoodsEditSkuParam.getSkuSpecList())) {
return ResponseDto.failure(ErrorCode.HTTP_PARAM_ERROR.getCode(), "规格信息不能为空");
}
List<String> skuIdList = goodsInfoVo.getSkuIdList();
List<GoblinGoodsSpecDto> paramSkuSpecDtoList = mgtGoodsEditSkuParam.getSkuSpecList();
for (String skuId : skuIdList) {// 比对所有SKU规格信息
......
......@@ -1313,19 +1313,21 @@ public class GoblinStoreMgtGoodsServiceImpl implements IGoblinstoreMgtGoodsServi
boolean updateGoodsInfoVoFlg = false;
List<GoblinGoodsSpecDto> skuSpecList = updateSkuInfoVo.getSkuSpecList();
List<GoblinGoodsSpecVo> specVoList = goodsInfoVo.getSpecVoList();
skuSpecList.forEach(spec -> {
String specName = spec.getSpecName(), specVname = spec.getSpecVname();
GoblinGoodsSpecVo specVo = specVoList.stream().filter(r -> r.getSpecName().equals(specName)).findAny()
.get();
specVo.getSpecValues().forEach(r -> initSpuSpecValueObjs
.add(new Object[] { spuId, specName, r.getSpecVname(), r.getSpecVsort() }));
updateSkuSpecValueObjs.add(new Object[] { specVname, skuId, specName });
if (!CollectionUtils.isEmpty(marketSkuIdList)) {
marketSkuIdList.forEach(marketSkuId -> updateSkuSpecValueObjs
.add(new Object[] { specVname, marketSkuId, specName }));
}
});
if (!CollectionUtils.isEmpty(skuSpecList)) {
skuSpecList.forEach(spec -> {
String specName = spec.getSpecName(), specVname = spec.getSpecVname();
GoblinGoodsSpecVo specVo = specVoList.stream().filter(r -> r.getSpecName().equals(specName)).findAny()
.get();
specVo.getSpecValues().forEach(r -> initSpuSpecValueObjs
.add(new Object[] { spuId, specName, r.getSpecVname(), r.getSpecVsort() }));
updateSkuSpecValueObjs.add(new Object[] { specVname, skuId, specName });
if (!CollectionUtils.isEmpty(marketSkuIdList)) {
marketSkuIdList.forEach(marketSkuId -> updateSkuSpecValueObjs
.add(new Object[] { specVname, marketSkuId, specName }));
}
});
}
if (!delSpuSpecMap.isEmpty()) {// 删除了规格值,需要同步SPU里的规格信息
updateGoodsInfoVoFlg = true;
goodsInfoVo.setUpdatedBy(uid);
......
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