修复和平小店商品图片和库存展示

This commit is contained in:
yml2213
2026-08-09 12:38:08 +08:00
parent 1b697ced09
commit 8919d020c5
6 changed files with 69 additions and 15 deletions
@@ -12,9 +12,16 @@ const priceLabel = computed(() => {
})
const stockText = computed(() => {
if (!selectedProduct.value) return ''
if (isXiaodian.value && selectedProduct.value.stockLabel) return selectedProduct.value.stockLabel
if (isXiaodian.value && selectedProduct.value.count < 0) return '库存未公开'
return `库存 ${selectedProduct.value.count}`
})
const stockIsLow = computed(() => {
if (!selectedProduct.value) return false
if (isXiaodian.value && selectedProduct.value.stockLabel) return false
return selectedProduct.value.count >= 0 && selectedProduct.value.count < 500
})
</script>
<template>
@@ -24,7 +31,7 @@ const stockText = computed(() => {
<div class="selected-product-meta">
<template v-if="selectedProduct">
<span class="chip chip-score">{{ selectedProduct.score }} {{ priceLabel }}</span>
<span class="chip chip-stock" :class="{ low: selectedProduct.count >= 0 && selectedProduct.count < 500 }">
<span class="chip chip-stock" :class="{ low: stockIsLow }">
{{ stockText }}
</span>
</template>
@@ -8,7 +8,13 @@ const { filteredProducts, selectedProductId, selectProduct, searchQuery, xiaodia
const { currentActivity } = useActivity()
const isXiaodian = computed(() => currentActivity.value?.id === 'xiaodian')
const priceLabel = (product: { payLabel?: string }) => isXiaodian.value ? (product.payLabel || '点券') : '积分'
const stockText = (count: number) => isXiaodian.value && count < 0 ? '库存未公开' : `库存 ${count}`
const stockText = (product: { count: number; stockLabel?: string }) => {
if (isXiaodian.value && product.stockLabel) return product.stockLabel
if (isXiaodian.value && product.count < 0) return '库存未公开'
return `库存 ${product.count}`
}
const stockIsLow = (product: { count: number; stockLabel?: string }) =>
(!isXiaodian.value || !product.stockLabel) && product.count >= 0 && product.count < 500
const parentRef = ref<HTMLElement | null>(null)
const itemSize = 72
@@ -80,10 +86,10 @@ watch(xiaodianTab, () => {
>{{ filteredProducts[item.index].score }} {{ priceLabel(filteredProducts[item.index]) }}</span>
<span
class="chip chip-stock"
:class="{ low: filteredProducts[item.index].count >= 0 && filteredProducts[item.index].count < 500 }"
:title="stockText(filteredProducts[item.index].count)"
:class="{ low: stockIsLow(filteredProducts[item.index]) }"
:title="stockText(filteredProducts[item.index])"
>
{{ stockText(filteredProducts[item.index].count) }}
{{ stockText(filteredProducts[item.index]) }}
</span>
</div>
</div>
+1
View File
@@ -12,6 +12,7 @@ export interface Product {
payType?: number
payLabel?: string
count: number
stockLabel?: string
pic: string
status: number
openStatus: number
+37 -3
View File
@@ -261,7 +261,7 @@ type xiaodianItem struct {
Price string `json:"iPrice"`
JB2Price string `json:"iJb2Price"`
PayType string `json:"sPayType"`
GoodsLeft string `json:"iGoodsLeft"`
GoodsLeft xiaodianFlexibleString `json:"iGoodsLeft"`
GoodsPic string `json:"sGoodsPic"`
ShowBegin string `json:"dtShowBeginTime"`
ShowEnd string `json:"dtShowEndTime"`
@@ -548,6 +548,7 @@ func (c *DouyuClient) xiaodianProducts(cookie string, options types.XiaodianProd
}
seen[item.GoodsID] = struct{}{}
payType, payLabel, price := xiaodianPayment(item)
stock, stockLabel := xiaodianStock(item.GoodsLeft)
products = append(products, types.Product{
CommodityID: item.GoodsID,
CommodityName: item.GoodsName,
@@ -556,13 +557,46 @@ func (c *DouyuClient) xiaodianProducts(cookie string, options types.XiaodianProd
Score: price,
PayType: payType,
PayLabel: payLabel,
Count: parseXiaodianInt(item.GoodsLeft),
Pic: item.GoodsPic,
Count: stock,
StockLabel: stockLabel,
Pic: xiaodianImageURL(item.GoodsPic),
})
}
return products, nil
}
// xiaodianStock 保留道具接口的展示语义。iGoodsLeft 除数字外还会返回“999+”和“-1”,
// 前者不是售罄,后者表示库存未公开,二者都不能按 strconv.Atoi 失败后归零处理。
func xiaodianStock(value xiaodianFlexibleString) (int, string) {
raw := strings.TrimSpace(string(value))
switch raw {
case "", "-1":
return -1, "库存未公开"
}
if strings.HasSuffix(raw, "+") {
minimum, err := strconv.Atoi(strings.TrimSuffix(raw, "+"))
if err == nil && minimum >= 0 {
return minimum, "库存 " + raw
}
return -1, "库存未公开"
}
stock, err := strconv.Atoi(raw)
if err != nil || stock < 0 {
return -1, "库存未公开"
}
return stock, ""
}
// xiaodianImageURL 与官方页面一致,使用 OSS 图片的 HTTPS 地址,避免桌面 WebView 拦截 HTTP 资源。
func xiaodianImageURL(raw string) string {
endpoint, err := url.Parse(strings.TrimSpace(raw))
if err != nil || endpoint.Scheme != "http" || endpoint.Hostname() != "ossweb-img.qq.com" {
return raw
}
endpoint.Scheme = "https"
return endpoint.String()
}
func xiaodianPayment(item xiaodianItem) (int, string, int) {
payType := 1
label := "点券"
+4 -1
View File
@@ -183,7 +183,7 @@ func TestProductsAutomaticallyResolvesXiaodianRoleAndPaginates(t *testing.T) {
} else {
switch offset {
case "0":
items = []map[string]string{{"iGoodsId": "g1", "iActionId": "act-g1", "sGoodsName": "商品一", "iCategoryId": "1", "iPrice": "10", "sPayType": "qpqb", "iGoodsLeft": "-1"}, {"iGoodsId": "g2", "sGoodsName": "特惠商品", "iCategoryId": "45", "iPrice": "0", "iJb2Price": "20", "sPayType": "jb2", "iGoodsLeft": "3"}}
items = []map[string]string{{"iGoodsId": "g1", "iActionId": "act-g1", "sGoodsName": "商品一", "iCategoryId": "1", "iPrice": "10", "sPayType": "qpqb", "iGoodsLeft": "999+", "sGoodsPic": "http://ossweb-img.qq.com/images/test.png"}, {"iGoodsId": "g2", "sGoodsName": "特惠商品", "iCategoryId": "45", "iPrice": "0", "iJb2Price": "20", "sPayType": "jb2", "iGoodsLeft": "3"}}
case "2":
items = []map[string]string{{"iGoodsId": "g2", "sGoodsName": "重复商品", "iCategoryId": "45", "iPrice": "0", "iJb2Price": "20", "sPayType": "jb2", "iGoodsLeft": "3"}, {"iGoodsId": "g4", "sGoodsName": "商品四", "iCategoryId": "1", "iPrice": "40", "sPayType": "qpqb", "iGoodsLeft": "0"}}
}
@@ -228,6 +228,9 @@ func TestProductsAutomaticallyResolvesXiaodianRoleAndPaginates(t *testing.T) {
if products[0].ActionID != "act-g1" {
t.Fatalf("商品动作 ID = %q", products[0].ActionID)
}
if products[0].Count != 999 || products[0].StockLabel != "库存 999+" || products[0].Pic != "https://ossweb-img.qq.com/images/test.png" {
t.Fatalf("商品库存或图片 = %#v", products[0])
}
if products[0].PayType != 1 || products[0].PayLabel != "点券" || products[1].PayType != 5 || products[1].PayLabel != "扭蛋碎片" || products[1].Score != 20 {
t.Fatalf("支付字段 = %#v", products)
}
+9 -6
View File
@@ -97,12 +97,15 @@ type Product struct {
CommodityID string `json:"commodityId"`
CommodityName string `json:"commodityName"`
// ActionID 是和平小店商品所属的动作 ID;不同商品可能不属于同一个动作。
ActionID string `json:"actionId,omitempty"`
Category string `json:"category"`
Score int `json:"score"`
PayType int `json:"payType,omitempty"`
PayLabel string `json:"payLabel,omitempty"`
Count int `json:"count"`
ActionID string `json:"actionId,omitempty"`
Category string `json:"category"`
Score int `json:"score"`
PayType int `json:"payType,omitempty"`
PayLabel string `json:"payLabel,omitempty"`
Count int `json:"count"`
// StockLabel 保留和平小店的非数字库存显示,例如“库存 999+”或“库存未公开”。
// Count 仅用于可精确表示的库存数值和兼容既有调用方。
StockLabel string `json:"stockLabel,omitempty"`
Pic string `json:"pic"`
Status int `json:"status"`
OpenStatus int `json:"openStatus"`