Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
黄成意
/
go_sku_server
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
932a1cb0
authored
Sep 10, 2025
by
hcy
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge branch 'master' into hcy/2025.9.10-香港自营
parents
c36855c9
b4759a26
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
585 additions
and
194 deletions
conf/dev/mongo.ini
doc/sku_price_flow_documentation.md
model/activity.go
model/ly_sku.go
model/prev_sku.go
pkg/config/mongo.go
service/service_activity.go
service/service_attr.go
service/service_ly.go
service/service_price.go
service/service_tags.go
conf/dev/mongo.ini
View file @
932a1cb0
...
...
@@ -4,4 +4,11 @@ host = 192.168.1.237:27017
username
=
"ichunt"
password
=
"huntmon6699"
database
=
ichunt
maxPoolSize
=
8000
[pre_sku_mongo]
host
=
192.168.1.237:27017
username
=
"ichunt"
password
=
"huntmon6699"
database
=
ichunt
maxPoolSize
=
8000
\ No newline at end of file
doc/sku_price_flow_documentation.md
0 → 100644
View file @
932a1cb0
# SKU价格获取完整流程文档
## 概述
本文档详细描述了从
`sku_controller.go`
出发的SKU价格获取完整流程,涵盖HTTP请求处理、并发执行、价格计算、缓存策略等核心环节。
## 1. 入口层 - HTTP请求处理
### 1.1 CommonController (sku_controller.go)
**文件位置**
:
`/controller/sku_controller.go`
**核心功能**
: 接收并处理SKU查询请求
#### 1.1.1 请求参数解析
```
go
GoodsIdStr
:=
ctx
.
Request
.
FormValue
(
"goods_id"
)
```
-
支持两种传参方式:
-
URL参数:
`goods_id=123,456,789`
-
POST表单:
`goods_id[0]=123&goods_id[1]=456`
#### 1.1.2 商品分类逻辑
```
go
if
len
(
goodsId
)
<
19
{
// 自营商品
zyGoodsId
=
append
(
zyGoodsId
,
goodsId
)
}
else
{
// 联营商品
lyGoodsId
=
append
(
lyGoodsId
,
goodsId
)
}
```
#### 1.1.3 并发处理机制
-
**协程数量**
: 每50个商品启动一个协程
-
**并发控制**
: 使用
`sync.WaitGroup`
管理协程生命周期
-
**结果收集**
: 通过channel异步收集结果
-
**超时保护**
: 20秒整体超时机制
```
go
ch
:=
make
(
chan
sync
.
Map
,
50
)
// 结果通道
timeout
:=
time
.
After
(
time
.
Second
*
20
)
// 超时控制
```
## 2. 服务层 - 商品详情处理
### 2.1 自营商品处理 (ZiyingService)
**入口方法**
:
`ZyGoodsDetail`
**处理特点**
: 直接获取预设价格,流程相对简单
### 2.2 联营商品处理 (LyService)
**入口方法**
:
`LyGoodsDetail`
**文件位置**
:
`/service/service_ly_common.go`
**处理特点**
: 完整的价格计算流程
## 3. 价格计算核心流程
### 3.1 GetCoefficientAndPrice 方法
**文件位置**
:
`/service/service_ly_common.go:290-501`
#### 3.1.1 前置判断
```
go
// 检查是否有阶梯价格
if
len
(
sku
.
LadderPrice
)
==
0
{
return
sku
}
```
#### 3.1.2 供应商类型区分
-
**专卖供应商**
(SupplierId = 17): 华云等自有供应商
-
**代购供应商**
(其他SupplierId): MRO、Digikey等第三方供应商
### 3.3 专卖供应商价格计算
#### 3.3.1 价格获取流程
1.
**读取成本价**
: 从Redis获取预设成本价
2.
**折扣系数**
: 调用
`GetDiscountRatio`
获取
3.
**阶梯价格**
: 调用
`GenerateLadderPrice`
生成
4.
**最终价格**
: 成本价 × 折扣系数 × 阶梯系数
### 3.4 代购供应商价格计算(完整流程)
#### 3.4.1 获取折扣系数 (`GetDiscountRatio`)
**文件位置**
:
`/service/service_price.go:427-522`
**数据源**
: Redis中的折扣系数配置
-
**Key格式**
:
`discount_ratio_config`
-
**匹配维度**
: 商品名称、品牌、分类ID、ECCN
-
**优先级顺序**
: 商品名称 > 品牌 > 分类ID > ECCN
**匹配逻辑**
:
```
1. 商品名称匹配 (最高优先级)
2. 标准品牌ID匹配
3. 分类ID匹配
4. ECCN匹配 (最低优先级)
任一条件匹配即命中并跳出
```
#### 3.4.2 获取售价组系数 (`GetPriceRatio`)
**文件位置**
:
`/service/service_price.go:524-679`
**配置源**
: Redis中的售价组配置
-
**Key格式**
:
`magic_cube_price_rule_v2`
-
**支持维度**
:
-
供应商级别
-
商品级别
-
品牌级别
-
分类级别
#### 3.4.3 获取供应商系数
**数据源**
: Redis中的供应商系数配置
-
**Key格式**
:
`pool_supplier_ratio`
-
**匹配逻辑**
: 支持商品名称和品牌匹配
#### 3.4.4 价格计算公式
##### 成本价计算
```
美金成本价 = 原始美金价格 × 折扣系数
人民币成本价 = 原始人民币价格 × 汇率 × 税率 (1.13)
```
##### 售价计算
```
美金售价 = 美金成本价 × 售价组系数
人民币售价 = 人民币成本价 × 售价组系数
```
##### 特殊处理
-
**MRO供应商**
(1688): 支持人民币价格
-
**Digikey供应商**
(7): 支持人民币价格
-
**实时汇率**
: 从
`erp_rate`
键获取
-
**固定税率**
: 1.13
## 4. 价格服务核心方法详解
### 4.1 GenerateLadderPrice - 阶梯价格生成
**适用场景**
: 专卖供应商价格计算
**文件位置**
:
`/service/service_price.go:20-425`
#### 4.1.1 配置获取逻辑
```
go
// 根据MOQ选择配置key
if
sku
.
Moq
<=
50
{
ratioDataKey
=
"cost_ladder_price_egt50_lt200"
}
else
{
ratioDataKey
=
"cost_ladder_price_egt200"
}
```
#### 4.1.2 多维度匹配优先级
1.
**商品名称匹配**
: 精确匹配商品名称
2.
**品牌匹配**
: 匹配品牌名称
3.
**分类匹配**
: 匹配分类ID
4.
**ECCN匹配**
: 匹配ECCN编码
### 4.2 TransformSpecialSupplierPrice - 特殊供应商价格转换
**功能**
: 处理不同供应商的币种转换和价格标准化
**文件位置**
:
`/service/service_price.go:681-777`
#### 4.2.1 支持的币种转换
该方法专门处理以下供应商的特殊币种问题:
-
**Element(6)**
: 存储的是港币,需要转换为美金
-
**RS(21)**
: 存储的是人民币,需要转换为美金
-
**Buerklin(1676)**
: 存储的是欧元,需要转换为美金
#### 4.2.2 配置数据源
**Redis配置键**
:
`magic_cube_supplier_currency`
**配置结构**
:
```
json
{
"currency"
:
3
,
//
币种ID
"has_tax"
:
true
,
//
是否含税
"symbol"
:
"€"
,
//
货币符号
"us_to_cn"
:
false
,
//
是否美金转人民币
"customize_rate_rmb"
:
7.2
,
//
自定义人民币汇率
"customize_rate_usd"
:
1.1
//
自定义美金汇率
}
```
#### 4.2.3 转换逻辑详解
##### 1. 汇率获取优先级
```
自定义汇率 > Redis实时汇率 > 默认汇率
```
##### 2. 币种转换流程
**情况1**
: 有自定义人民币汇率
```
人民币价格 = 原始价格 × 自定义人民币汇率
如含税: 人民币价格 = 人民币价格 ÷ 1.13
```
**情况2**
: 无自定义汇率,需要美金转人民币
```
人民币价格 = 原始美金价格 × 实时汇率 × 1.13
```
**情况3**
: 有自定义美金汇率
```
美金价格 = 原始价格 × 自定义美金汇率
如含税: 美金价格 = 美金价格 ÷ 1.13
```
**情况4**
: 无自定义美金汇率(最复杂)
```
1. 获取币种对人民币汇率
2. 获取人民币对美元汇率
3. 计算币种对美元汇率 = 币种对人民币汇率 ÷ 人民币对美元汇率
4. 美金价格 = 原始价格 × 计算出的汇率
如含税: 美金价格 = 美金价格 ÷ 1.13
```
##### 3. 实际转换示例
**示例**
: Element供应商(港币→美金)
```
原始数据: price_us = 100 (实际是100港币)
配置: currency=3(港币), has_tax=true
汇率: 港币对人民币=0.9, 人民币对美元=7.2
计算: 港币对美元汇率 = 0.9 ÷ 7.2 = 0.125
最终结果: price_us = 100 × 0.125 ÷ 1.13 = 11.06美金
```
#### 4.2.4 特殊处理逻辑
-
**组织ID过滤**
: 仅处理OrgId=1的数据
-
**税率处理**
: 根据has_tax标志决定是否去除13%增值税
-
**符号记录**
: 记录原始货币符号用于前端展示
-
**汇率缓存**
: 使用Redis缓存实时汇率数据
### 4.3 GetActivityPrice - 活动价格处理
**功能**
: 处理限时活动价格
**数据源**
: Redis中的活动价格配置
## 5. 缓存策略与数据结构
### 5.1 Redis Key结构
| Key名称 | 用途 | 数据类型 |
|---------|------|----------|
|
`magic_cube_price_rule_v2`
| 售价组配置 | Hash |
|
`pool_supplier_ratio`
| 供应商系数 | Hash |
|
`erp_rate`
| 实时汇率 | Hash |
|
`cost_ladder_price_*`
| 阶梯价格系数 | String |
|
`discount_ratio_config`
| 折扣系数配置 | String |
### 5.2 缓存更新策略
-
**实时读取**
: 每次价格计算时从Redis获取最新配置
-
**配置更新**
: 通过管理后台更新Redis配置
-
**异常处理**
: 配置缺失时使用默认值
## 6. 并发与性能优化
### 6.1 并发处理架构
```
HTTP请求 → 商品分类 → 批次划分 → 协程处理 → 结果聚合 → 响应返回
```
### 6.2 性能优化点
-
**批量处理**
: 50个商品一批次,减少协程数量
-
**连接复用**
: Redis连接池管理
-
**缓存策略**
: 关键配置Redis缓存
-
**超时控制**
: 20秒整体超时保护
## 7. 异常处理与监控
### 7.1 异常场景处理
-
**空值保护**
: 各种系数缺失时的默认值处理
-
**格式验证**
: 数据格式异常时的容错处理
-
**超时保护**
: 防止长时间阻塞
### 7.2 日志与监控
-
**关键节点日志**
: 价格计算各阶段的状态记录
-
**异常日志**
: 配置缺失、计算异常等场景记录
-
**性能监控**
: 请求耗时、缓存命中率等指标
## 8. 数据流总结
### 8.1 完整数据流
```
用户请求 → 参数解析 → 商品分类 → 并发处理 → 价格计算 → 结果聚合 → 响应返回
↓
Redis配置 → 折扣系数 → 售价组系数 → 供应商系数 → 汇率 → 税率
↓
成本价计算 → 售价计算 → 币种转换 → 最终价格
```
### 8.2 关键决策点
1.
**供应商类型判断**
: 决定价格计算路径
2.
**配置匹配优先级**
: 影响最终系数选择
3.
**币种转换需求**
: 决定是否进行汇率转换
4.
**活动价格判断**
: 是否使用活动价格覆盖
## 9. 使用示例
### 9.1 请求示例
```
bash
# 单个商品查询
curl
"http://api.example.com/sku?goods_id=123456789"
# 多个商品查询
curl
"http://api.example.com/sku"
-d
"goods_id[0]=123&goods_id[1]=456"
```
### 9.2 响应数据结构
```
json
{
"123456789"
:
{
"goods_name"
:
"示例商品"
,
"ladder_price"
:
[
{
"purchases"
:
1
,
"price_us"
:
10.50
,
"price_cn"
:
73.50
,
"price_cost_us"
:
8.50
,
"price_cost_cn"
:
59.50
}
],
"discount_ratio"
:
{
"ratio"
:
0.85
,
"ratio_usd"
:
0.90
}
}
}
```
## 10. 维护与扩展
### 10.1 配置维护
-
**售价组配置**
: 通过管理后台更新
`magic_cube_price_rule_v2`
-
**折扣系数**
: 更新
`discount_ratio_config`
-
**供应商系数**
: 更新
`pool_supplier_ratio`
### 10.2 扩展点
-
**新供应商支持**
: 在
`TransformSpecialSupplierPrice`
中添加转换逻辑
-
**新匹配维度**
: 扩展
`GetDiscountRatio`
和
`GetPriceRatio`
的匹配逻辑
-
**新币种支持**
: 扩展汇率转换逻辑
---
*本文档基于当前代码版本整理,如有更新请及时同步文档内容。*
model/activity.go
View file @
932a1cb0
...
...
@@ -42,10 +42,12 @@ type Activity struct {
StandardBrandIdList
[]
string
ExcludeStandardBrandIds
string
`json:"exclude_standard_brand_ids"`
ExcludeStandardBrandIdList
[]
string
CurrencyRmb
int
`json:"currency_rmb"`
CurrencyUs
int
`json:"currency_us"`
UseType
int
`json:"use_type"`
SignUrl
string
`json:"sign_url"`
CurrencyRmb
int
`json:"currency_rmb"`
CurrencyUs
int
`json:"currency_us"`
UseType
int
`json:"use_type"`
SignUrl
string
`json:"sign_url"`
Source
string
`json:"source"`
SourceList
[]
string
`json:"source_list"`
}
type
ActivityItem
struct
{
...
...
@@ -70,4 +72,5 @@ type ActivityCheckData struct {
Canal
string
ClassId
int
OrgId
int
Source
int
}
model/ly_sku.go
View file @
932a1cb0
...
...
@@ -243,7 +243,7 @@ func InitSkuData(sku string) (data LySku) {
isExpire
:=
gjson
.
Get
(
sku
,
"is_expire"
)
.
Int
()
data
.
IsExpire
=
int
(
isExpire
)
data
.
IsExpire
=
0
//
data.IsExpire = 0
hkDeliveryTime
:=
gjson
.
Get
(
sku
,
"hk_delivery_time"
)
.
String
()
data
.
HkDeliveryTime
=
hkDeliveryTime
...
...
model/prev_sku.go
0 → 100644
View file @
932a1cb0
package
model
import
"gopkg.in/mgo.v2/bson"
// PrevSku 对应prev_sku集合的数据结构
type
PrevSku
struct
{
ID
bson
.
ObjectId
`bson:"_id"`
SkuId
int64
`bson:"sku_id"`
SpuId
int64
`bson:"spu_id"`
SupplierId
int64
`bson:"supplier_id"`
}
pkg/config/mongo.go
View file @
932a1cb0
package
config
type
MongoDbDatabase
struct
{
Host
string
UserName
string
UserName
string
Password
string
Database
string
MaxPoolSize
string
}
func
BuildMongoDbConfgs
()
map
[
string
]
MongoDbDatabase
{
func
BuildMongoDbConfgs
()
map
[
string
]
MongoDbDatabase
{
return
map
[
string
]
MongoDbDatabase
{
"default"
:
{
Host
:
Get
(
"mongo.host"
)
.
String
(),
UserName
:
Get
(
"mongo.username"
)
.
String
(),
Password
:
Get
(
"mongo.password"
)
.
String
(),
Database
:
Get
(
"mongo.database"
)
.
String
(),
MaxPoolSize
:
Get
(
"mongo.maxPoolSize"
)
.
String
(),
"default"
:
{
Host
:
Get
(
"mongo.host"
)
.
String
(),
UserName
:
Get
(
"mongo.username"
)
.
String
(),
Password
:
Get
(
"mongo.password"
)
.
String
(),
Database
:
Get
(
"mongo.database"
)
.
String
(),
MaxPoolSize
:
Get
(
"mongo.maxPoolSize"
)
.
String
(),
},
"pre_sku"
:
{
Host
:
Get
(
"pre_sku_mongo.host"
)
.
String
(),
UserName
:
Get
(
"pre_sku_mongo.username"
)
.
String
(),
Password
:
Get
(
"pre_sku_mongo.password"
)
.
String
(),
Database
:
Get
(
"pre_sku_mongo.database"
)
.
String
(),
MaxPoolSize
:
Get
(
"pre_sku_mongo.maxPoolSize"
)
.
String
(),
},
}
}
service/service_activity.go
View file @
932a1cb0
...
...
@@ -49,74 +49,6 @@ func (as *ActivityService) GetActivityData(checkData model.ActivityCheckData) (p
return
}
// 获取满赠活动信息
func
(
as
*
ActivityService
)
GetGiftActivity
(
checkData
model
.
ActivityCheckData
,
activities
[]
model
.
Activity
)
(
giftActivity
model
.
GiftActivity
)
{
var
hasActivity
bool
nowTimestamp
:=
int
(
time
.
Now
()
.
Unix
())
for
_
,
activity
:=
range
activities
{
if
activity
.
Status
!=
1
{
continue
}
//判断时间是否过期
if
activity
.
StartTime
>
nowTimestamp
||
activity
.
EndTime
<
nowTimestamp
{
continue
}
//如果是整个供应商搞活动,则直接返回系数
if
activity
.
EntireSupplierActivity
{
hasActivity
=
true
goto
INFO
}
//判断是否是排除的sku或者品牌,如果是的话,直接返回没活动
if
as
.
CheckExcludeSku
(
checkData
.
GoodsId
,
activity
)
||
as
.
CheckExcludeBrand
(
checkData
.
BrandId
,
activity
)
{
continue
}
//判断是否是搞活动的品牌
if
as
.
CheckBrand
(
checkData
.
BrandId
,
activity
)
{
hasActivity
=
true
goto
INFO
}
//如果是专卖,则要去判断canal,如果是自营,则去判断分类
if
checkData
.
SupplierId
==
17
{
if
as
.
CheckCanal
(
checkData
.
Canal
,
activity
)
{
hasActivity
=
true
goto
INFO
}
}
else
{
if
as
.
CheckClass
(
checkData
.
ClassId
,
activity
)
{
hasActivity
=
true
goto
INFO
}
}
INFO
:
if
hasActivity
{
for
key
,
item
:=
range
activity
.
ItemList
{
activity
.
ItemList
[
key
]
.
Content
=
"订单满"
+
gconv
.
String
(
item
.
Amount
)
+
"元赠"
+
gconv
.
String
(
item
.
ItemName
)
+
"X"
+
gconv
.
String
(
item
.
Num
)
+
""
+
item
.
Remark
}
giftActivity
.
ActivityName
=
activity
.
ActivityName
giftActivity
.
ActivityId
=
activity
.
ActivityId
giftActivity
=
model
.
GiftActivity
{
CanAdminOrder
:
activity
.
CanAdminOrder
,
ItemList
:
activity
.
ItemList
,
ActivityCommon
:
model
.
ActivityCommon
{
HasActivity
:
hasActivity
,
ActivityId
:
activity
.
ActivityId
,
ActivityName
:
activity
.
ActivityName
,
AllowCoupon
:
activity
.
AllowCoupon
,
UserScope
:
activity
.
UserScope
,
},
}
break
}
}
return
}
func
(
as
*
ActivityService
)
GetPriceActivity
(
checkData
model
.
ActivityCheckData
,
activities
[]
model
.
Activity
)
(
priceActivity
model
.
PriceActivity
)
{
redisConn
:=
gredis
.
Conn
(
"default_r"
)
defer
redisConn
.
Close
()
...
...
@@ -140,6 +72,10 @@ func (as *ActivityService) GetPriceActivity(checkData model.ActivityCheckData, a
if
as
.
CheckExcludeSku
(
checkData
.
GoodsId
,
activity
)
||
as
.
CheckExcludeStandardBrand
(
checkData
.
StandardBrandId
,
activity
)
{
continue
}
//checkSource
if
!
as
.
CheckSource
(
checkData
.
Source
,
activity
)
{
continue
}
//如果是专卖,则要去判断canal,如果是自营,则去判断分类
if
checkData
.
SupplierId
==
17
{
...
...
@@ -175,8 +111,6 @@ func (as *ActivityService) GetPriceActivity(checkData model.ActivityCheckData, a
}
else
if
checkData
.
SupplierId
==
10000
{
//自营活动特殊判断
//fmt.Println(activity.ClassIds)
//fmt.Println(activity.UseType)
if
activity
.
ClassIds
!=
""
{
if
!
as
.
CheckClass
(
checkData
.
ClassId
,
activity
)
{
continue
...
...
@@ -275,10 +209,7 @@ func (as *ActivityService) CheckExcludeBrand(brandId int, activity model.Activit
activity
.
ExcludeBrandIdList
=
strings
.
Split
(
activity
.
ExcludeBrandIds
,
","
)
brandIdStr
:=
gconv
.
String
(
brandId
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
brandIdStr
,
activity
.
ExcludeBrandIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
brandIdStr
,
activity
.
ExcludeBrandIdList
)
}
// 检查是否属于被排除的标准品牌
...
...
@@ -290,10 +221,7 @@ func (as *ActivityService) CheckExcludeStandardBrand(standardBrandId int, activi
activity
.
ExcludeStandardBrandIdList
=
strings
.
Split
(
activity
.
ExcludeStandardBrandIds
,
","
)
standardBrandIdStr
:=
gconv
.
String
(
standardBrandId
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
standardBrandIdStr
,
activity
.
ExcludeStandardBrandIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
standardBrandIdStr
,
activity
.
ExcludeStandardBrandIdList
)
}
// 检查是否属于被排除的sku
...
...
@@ -304,10 +232,7 @@ func (as *ActivityService) CheckExcludeSku(skuId string, activity model.Activity
//先去判断品牌
activity
.
ExcludeSkuIdList
=
strings
.
Split
(
activity
.
ExcludeSkuIds
,
","
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
skuId
,
activity
.
ExcludeSkuIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
skuId
,
activity
.
ExcludeSkuIdList
)
}
// 检查是否属于活动分类(只有自营需要判断)
...
...
@@ -319,10 +244,7 @@ func (as *ActivityService) CheckClass(classId int, activity model.Activity) bool
activity
.
ClassIdList
=
strings
.
Split
(
activity
.
ClassIds
,
","
)
classIdStr
:=
gconv
.
String
(
classId
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
classIdStr
,
activity
.
ClassIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
classIdStr
,
activity
.
ClassIdList
)
}
// 检查是否属于活动品牌
...
...
@@ -334,10 +256,7 @@ func (as *ActivityService) CheckBrand(brandId int, activity model.Activity) bool
activity
.
BrandIdList
=
strings
.
Split
(
activity
.
BrandIds
,
","
)
brandIdStr
:=
gconv
.
String
(
brandId
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
brandIdStr
,
activity
.
BrandIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
brandIdStr
,
activity
.
BrandIdList
)
}
// 检测是否属于活动标准品牌
...
...
@@ -349,10 +268,7 @@ func (as *ActivityService) CheckStandardBrand(standardBrandId int, activity mode
activity
.
StandardBrandIdList
=
strings
.
Split
(
activity
.
StandardBrandIds
,
","
)
standardBrandIdStr
:=
gconv
.
String
(
standardBrandId
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
standardBrandIdStr
,
activity
.
StandardBrandIdList
)
{
return
true
}
return
false
return
php2go
.
InArray
(
standardBrandIdStr
,
activity
.
StandardBrandIdList
)
}
// 检查是否属于供应商渠道
...
...
@@ -363,8 +279,23 @@ func (as *ActivityService) CheckCanal(canal string, activity model.Activity) boo
//先去判断品牌
activity
.
CanalList
=
strings
.
Split
(
activity
.
Canals
,
","
)
//如果存在于有活动价的品牌,就是有折扣活动了
if
php2go
.
InArray
(
canal
,
activity
.
CanalList
)
{
return
php2go
.
InArray
(
canal
,
activity
.
CanalList
)
}
// 检查来源是否有配置,有配置的话是否符合
func
(
as
*
ActivityService
)
CheckSource
(
source
int
,
activity
model
.
Activity
)
bool
{
if
source
==
0
{
source
=
1
}
//如果source为空,则默认为所有来源
if
activity
.
Source
==
""
{
return
true
}
return
false
//先去判断品牌
activity
.
SourceList
=
strings
.
Split
(
activity
.
Source
,
","
)
sourceStr
:=
gconv
.
String
(
source
)
// fmt.Println(sourceStr)
// fmt.Println(activity.SourceList)
//如果存在于有活动价的品牌,就是有折扣活动了
return
php2go
.
InArray
(
sourceStr
,
activity
.
SourceList
)
}
service/service_attr.go
View file @
932a1cb0
...
...
@@ -2,15 +2,17 @@ package service
import
(
"encoding/json"
"github.com/iancoleman/orderedmap"
"go_sku_server/pkg/logger"
"go_sku_server/pkg/mongo"
"strconv"
"strings"
"github.com/iancoleman/orderedmap"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"strconv"
)
//获取Spu的属性
//
获取Spu的属性
func
(
ls
*
LyService
)
GetSpuAttr
(
spuId
string
)
(
attrsResult
interface
{})
{
var
spuAttr
SpuAttr
var
attrsList
[]
interface
{}
...
...
@@ -26,7 +28,17 @@ func (ls *LyService) GetSpuAttr(spuId string) (attrsResult interface{}) {
//fmt.Println(spuAttr.AttrsExtend)
//如果有attrs_extend,就去取attrs_extend
if
len
(
spuAttr
.
AttrsExtend
)
!=
0
{
return
spuAttr
.
AttrsExtend
//便利AttrsExtend的值
for
_
,
value
:=
range
spuAttr
.
AttrsExtend
{
data
:=
make
(
map
[
string
]
interface
{})
data
[
"attr_name"
]
=
value
.
AttrName
//€符号全部替换为逗号
data
[
"attr_value"
]
=
strings
.
ReplaceAll
(
value
.
AttrValue
,
"€"
,
","
)
data
[
"attr_unit"
]
=
value
.
AttrUnit
attrsList
=
append
(
attrsList
,
data
)
attrsResult
=
attrsList
}
return
attrsResult
}
else
if
spuAttr
.
Attrs
!=
""
{
o
:=
orderedmap
.
New
()
err
:=
json
.
Unmarshal
([]
byte
(
spuAttr
.
Attrs
),
&
o
)
...
...
service/service_ly.go
View file @
932a1cb0
...
...
@@ -8,6 +8,7 @@ import (
"go_sku_server/pkg/mongo"
"go_sku_server/service/sorter"
"sort"
"strconv"
"sync"
"gopkg.in/mgo.v2"
...
...
@@ -38,7 +39,10 @@ type Power struct {
func
(
ls
*
LyService
)
LyGoodsDetail
(
ctx
*
gin
.
Context
,
goodsIds
[]
string
,
ch
chan
sync
.
Map
)
{
redisConn
:=
gredis
.
Conn
(
"search_r"
)
redisConnSpu
:=
gredis
.
Conn
(
"spu"
)
// 连接prev_sku MongoDB
prevSkuMongo
:=
mongo
.
Conn
(
"pre_sku"
)
defer
func
()
{
prevSkuMongo
.
Close
()
redisConn
.
Close
()
redisConnSpu
.
Close
()
}()
...
...
@@ -63,14 +67,40 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
GoodsRes
:=
sync
.
Map
{}
for
goodsId
,
skuStr
:=
range
skuArr
{
if
skuStr
==
""
{
GoodsRes
.
Store
(
goodsId
,
false
)
continue
}
//初始化有序map,拼接data数据,就是从redis取出初始数据
sku
:=
model
.
InitSkuData
(
skuStr
)
var
spu
string
if
skuStr
==
""
{
// 如果redis中找不到sku数据,尝试从prev_sku MongoDB中查找
skuId
,
err
:=
strconv
.
ParseInt
(
goodsId
,
10
,
64
)
if
err
!=
nil
{
GoodsRes
.
Store
(
goodsId
,
false
)
continue
}
var
prevSku
model
.
PrevSku
err
=
prevSkuMongo
.
DB
(
"ichunt"
)
.
C
(
"prev_sku"
)
.
Find
(
bson
.
M
{
"sku_id"
:
skuId
})
.
One
(
&
prevSku
)
if
err
!=
nil
{
// 如果在prev_sku中也找不到,则保持默认值
GoodsRes
.
Store
(
goodsId
,
false
)
continue
}
// 根据找到的spu_id去spu的redis中查找
spuIdStr
:=
strconv
.
FormatInt
(
prevSku
.
SpuId
,
10
)
spuStr
,
_
:=
redis
.
String
(
redisConnSpu
.
Do
(
"HGET"
,
"spu"
,
spuIdStr
))
if
spuStr
==
""
{
// 如果spu缓存也没有,保持默认值
GoodsRes
.
Store
(
goodsId
,
false
)
continue
}
sku
.
SupplierId
=
prevSku
.
SupplierId
sku
.
SpuId
=
spuIdStr
spu
=
spuStr
}
else
{
spu
=
spuList
[
sku
.
SpuId
]
}
sku
.
GoodsId
=
goodsId
spu
:=
spuList
[
sku
.
SpuId
]
//读取包装字段的缓存(分别是DGK,avnet,mro)
if
sku
.
SupplierId
==
7
||
sku
.
SupplierId
==
13
||
sku
.
SupplierId
==
1688
||
sku
.
SupplierId
==
17
{
//sku_raw_map哪里写入(成意写的)
...
...
@@ -79,12 +109,12 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
}
sku
=
ls
.
GetGoodsImages
(
sku
,
spu
)
sku
=
ls
.
GetPdf
(
sku
,
spu
)
//用spuInfo补全信息
sku
=
ls
.
CombineSup
(
sku
,
spu
)
// 2023.11.20 DGK商品的包装若为“Tape & Reel (TR)”,则递增量=起订量
if
sku
.
SupplierId
==
7
&&
sku
.
Packing
==
"Tape & Reel (TR)"
{
sku
.
Multiple
=
sku
.
Moq
}
//获取商品名称
//1688就是mro的sku spuName和GoodsName不是一个东西,不能公用
if
sku
.
GoodsName
!=
""
&&
(
sku
.
SupplierId
==
1688
||
sku
.
OrgId
==
3
)
{
...
...
@@ -182,16 +212,13 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
//case为0是为了兼容价格体系之前的价格
case
0
:
case
1
:
//如果是寄售的,也不走价格体系
if
sku
.
Source
!=
12
{
//这里猎芯和华云都是走同一套的价格体系了
//获取系数和价格
sku
=
ls
.
GetCoefficientAndPrice
(
sku
)
//获取自定义价格后的阶梯价
customPriceService
:=
CustomPrice
{}
sku
.
CustomPriceList
,
_
=
customPriceService
.
getCustomPriceList
(
sku
)
sku
=
priceService
.
GetActivityPrice
(
sku
)
}
//这里猎芯和寄售都是走同一套的价格体系了
//获取系数和价格
sku
=
ls
.
GetCoefficientAndPrice
(
sku
)
//获取自定义价格后的阶梯价
customPriceService
:=
CustomPrice
{}
sku
.
CustomPriceList
,
_
=
customPriceService
.
getCustomPriceList
(
sku
)
sku
=
priceService
.
GetActivityPrice
(
sku
)
case
3
:
//如果是寄售的,不走价格体系
if
sku
.
Source
==
12
{
...
...
@@ -240,6 +267,11 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
sku
.
HkDeliveryTime
=
delivery
[
"hk_delivery"
]
}
//只要是寄售的,大陆交期都是0.5工作日
if
sku
.
Source
==
12
{
sku
.
CnDeliveryTime
=
"0.5工作日"
}
}
//处理是否可以购买
...
...
@@ -255,8 +287,8 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
//判断是否可以购买
sku
.
IsBuy
=
ls
.
GetIsBuy
(
sku
)
if
sku
.
IsBuy
==
0
&&
sku
.
OrgId
!=
1
{
sku
.
AcType
=
0
;
if
sku
.
IsBuy
==
0
&&
sku
.
OrgId
!=
1
{
sku
.
AcType
=
0
}
sku
.
Stock
=
ls
.
GetStock
(
sku
)
...
...
@@ -268,8 +300,6 @@ func (ls *LyService) LyGoodsDetail(ctx *gin.Context, goodsIds []string, ch chan
//获取关税以及价格转换
sku
=
ls
.
GetTariffAndPrice
(
sku
)
//用spuInfo补全信息
sku
=
ls
.
CombineSup
(
sku
,
spu
)
//最后一步,将sku的全部信息放到有序map里面
GoodsRes
.
Store
(
goodsId
,
sku
)
//(*goodsRes)[goodsId] = A
...
...
@@ -290,6 +320,7 @@ func (ls *LyService) GetActivity(sku model.LySku) model.LySku {
GoodsName
:
sku
.
GoodsName
,
ClassId
:
sku
.
ClassID2
,
OrgId
:
sku
.
OrgId
,
Source
:
sku
.
Source
,
}
var
activityService
ActivityService
priceActivity
,
giftActivity
:=
activityService
.
GetActivityData
(
checkData
)
...
...
service/service_price.go
View file @
932a1cb0
...
...
@@ -61,14 +61,13 @@ func (ps *PriceService) GenerateLadderPrice(sku model.LySku) model.LySku {
//拿出里面的所有排序
priceRatioSortMap
:=
gjson
.
Get
(
priceRatioCache
,
ratioDataKey
)
.
Map
()
var
sortNumbers
[]
int
for
sortNumberString
,
_
:=
range
priceRatioSortMap
{
for
sortNumberString
:=
range
priceRatioSortMap
{
sortNumber
,
_
:=
strconv
.
Atoi
(
sortNumberString
)
sortNumbers
=
append
(
sortNumbers
,
sortNumber
)
}
//然后确定排序
sortNumbers
=
sorter
.
IntSliceSortDesc
(
sortNumbers
)
//确定排序以后,就可以进行按排序(从大到小)取系数
outerLoop
:
for
_
,
sortNumber
:=
range
sortNumbers
{
priceRatioSort
=
sortNumber
priceRatioList
=
nil
...
...
@@ -82,17 +81,17 @@ func (ps *PriceService) GenerateLadderPrice(sku model.LySku) model.LySku {
priceRatio
.
Purchases
=
gjson
.
Get
(
value
.
String
(),
"purchases"
)
.
Int
()
priceRatioList
=
append
(
priceRatioList
,
priceRatio
)
}
//是否满足特定条件的判断
//是否满足特定条件的判断
var
hasSpecialCheck
=
false
conditionsMet
:=
true
//判断是否有符合的商品名称
goodsNames
:=
gjson
.
Get
(
priceRatioCache
,
"goods_name."
+
sortString
)
.
String
()
if
goodsNames
!=
""
{
hasSpecialCheck
=
true
goodsNameList
:=
strings
.
Split
(
goodsNames
,
"@€@"
)
//找到有对应的商品名称,那么优先级肯定是最高的了
if
php2go
.
InArray
(
sku
.
GoodsName
,
goodsNameList
)
{
foundRatio
=
true
break
if
!
php2go
.
InArray
(
sku
.
GoodsName
,
goodsNameList
)
{
conditionsMet
=
false
}
}
...
...
@@ -101,12 +100,20 @@ func (ps *PriceService) GenerateLadderPrice(sku model.LySku) model.LySku {
if
brandIds
!=
""
{
hasSpecialCheck
=
true
standardBrandIdList
:=
strings
.
Split
(
brandIds
,
","
)
//fmt.Println(standardBrandIdList)
standardBrandId
:=
strconv
.
Itoa
(
sku
.
StandardBrand
.
StandardBrandId
)
//找到有对应的品牌,那么优先级肯定是最高的了
if
php2go
.
InArray
(
standardBrandId
,
standardBrandIdList
)
{
foundRatio
=
true
break
if
!
php2go
.
InArray
(
standardBrandId
,
standardBrandIdList
)
{
conditionsMet
=
false
}
}
//判断是否有符合的分类ID
classIds
:=
gjson
.
Get
(
priceRatioCache
,
"class_ids."
+
sortString
)
.
String
()
if
classIds
!=
""
{
hasSpecialCheck
=
true
classIdList
:=
strings
.
Split
(
classIds
,
","
)
classId
:=
strconv
.
Itoa
(
sku
.
ClassID2
)
if
!
php2go
.
InArray
(
classId
,
classIdList
)
{
conditionsMet
=
false
}
}
...
...
@@ -114,11 +121,9 @@ func (ps *PriceService) GenerateLadderPrice(sku model.LySku) model.LySku {
eccns
:=
gjson
.
Get
(
priceRatioCache
,
"eccn."
+
sortString
)
.
String
()
if
eccns
!=
""
{
hasSpecialCheck
=
true
eccnMatched
:=
false
eccnList
:=
strings
.
Split
(
eccns
,
","
)
//找到有对应的eccn,那么优先级肯定是最高的了
for
_
,
eccn
:=
range
eccnList
{
//判断是否有百分号匹配
//如果是纯%,那就是不对的,要跳过
if
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
==
""
{
continue
}
...
...
@@ -128,36 +133,44 @@ func (ps *PriceService) GenerateLadderPrice(sku model.LySku) model.LySku {
if
hasPrefix
&&
hasSuffix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
Contains
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
if
hasPrefix
&&
!
hasSuffix
{
}
else
if
hasPrefix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
HasSuffix
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
if
!
hasPrefix
&&
hasSuffix
{
}
else
if
hasSuffix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
HasPrefix
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
}
else
{
if
sku
.
Eccn
==
eccn
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
}
if
!
eccnMatched
{
conditionsMet
=
false
}
}
//如果没有设置品牌和商品,那么这个优先级高的就会覆盖下面的了,不需要再去判断品牌和型号了
if
hasSpecialCheck
{
continue
if
conditionsMet
{
foundRatio
=
true
break
}
else
{
continue
}
}
// 如果没有设置任何特殊条件,则默认匹配
foundRatio
=
true
break
}
...
...
@@ -535,14 +548,13 @@ func (ps PriceService) GetPriceRatio(sku model.LySku) (model.LySku, []model.Pric
//拿出里面的所有排序
priceRatioSortMap
:=
gjson
.
Get
(
priceRatioCache
,
"ladder_price"
)
.
Map
()
var
sortNumbers
[]
int
for
sortNumberString
,
_
:=
range
priceRatioSortMap
{
for
sortNumberString
:=
range
priceRatioSortMap
{
sortNumber
,
_
:=
strconv
.
Atoi
(
sortNumberString
)
sortNumbers
=
append
(
sortNumbers
,
sortNumber
)
}
//然后确定排序
sortNumbers
=
sorter
.
IntSliceSortDesc
(
sortNumbers
)
//确定排序以后,就可以进行按排序(从大到小)取系数
outerLoop
:
for
_
,
sortNumber
:=
range
sortNumbers
{
priceRatioSort
=
sortNumber
priceRatioList
=
nil
...
...
@@ -556,16 +568,15 @@ func (ps PriceService) GetPriceRatio(sku model.LySku) (model.LySku, []model.Pric
}
var
hasSpecialCheck
=
false
conditionsMet
:=
true
//判断是否有符合的商品名称
goodsNames
:=
gjson
.
Get
(
priceRatioCache
,
"goods_name."
+
sortString
)
.
String
()
if
goodsNames
!=
""
{
hasSpecialCheck
=
true
goodsNameList
:=
strings
.
Split
(
goodsNames
,
"@€@"
)
//找到有对应的商品名称,那么优先级肯定是最高的了
if
php2go
.
InArray
(
sku
.
GoodsName
,
goodsNameList
)
{
foundRatio
=
true
break
if
!
php2go
.
InArray
(
sku
.
GoodsName
,
goodsNameList
)
{
conditionsMet
=
false
}
}
...
...
@@ -575,10 +586,19 @@ func (ps PriceService) GetPriceRatio(sku model.LySku) (model.LySku, []model.Pric
hasSpecialCheck
=
true
standardBrandIdList
:=
strings
.
Split
(
brandIds
,
","
)
standardBrandId
:=
strconv
.
Itoa
(
sku
.
StandardBrand
.
StandardBrandId
)
//找到有对应的品牌,那么优先级肯定是最高的了
if
php2go
.
InArray
(
standardBrandId
,
standardBrandIdList
)
{
foundRatio
=
true
break
if
!
php2go
.
InArray
(
standardBrandId
,
standardBrandIdList
)
{
conditionsMet
=
false
}
}
//判断是否有符合的分类ID
classIds
:=
gjson
.
Get
(
priceRatioCache
,
"class_ids."
+
sortString
)
.
String
()
if
classIds
!=
""
{
hasSpecialCheck
=
true
classIdList
:=
strings
.
Split
(
classIds
,
","
)
classId
:=
strconv
.
Itoa
(
sku
.
ClassID2
)
if
!
php2go
.
InArray
(
classId
,
classIdList
)
{
conditionsMet
=
false
}
}
...
...
@@ -586,11 +606,9 @@ func (ps PriceService) GetPriceRatio(sku model.LySku) (model.LySku, []model.Pric
eccns
:=
gjson
.
Get
(
priceRatioCache
,
"eccn."
+
sortString
)
.
String
()
if
eccns
!=
""
{
hasSpecialCheck
=
true
eccnMatched
:=
false
eccnList
:=
strings
.
Split
(
eccns
,
","
)
//找到有对应的eccn,那么优先级肯定是最高的了
for
_
,
eccn
:=
range
eccnList
{
//判断是否有百分号匹配
//如果是纯%,那就是不对的,要跳过
if
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
==
""
{
continue
}
...
...
@@ -600,36 +618,44 @@ func (ps PriceService) GetPriceRatio(sku model.LySku) (model.LySku, []model.Pric
if
hasPrefix
&&
hasSuffix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
Contains
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
if
hasPrefix
&&
!
hasSuffix
{
}
else
if
hasPrefix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
HasSuffix
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
if
!
hasPrefix
&&
hasSuffix
{
}
else
if
hasSuffix
{
eccn
=
strings
.
Replace
(
eccn
,
"%"
,
""
,
10
)
if
strings
.
HasPrefix
(
sku
.
Eccn
,
eccn
)
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
}
else
{
if
sku
.
Eccn
==
eccn
{
foundRatio
=
true
break
outerLoop
eccnMatched
=
true
break
}
}
}
if
!
eccnMatched
{
conditionsMet
=
false
}
}
//如果没有设置品牌和商品,那么这个优先级高的就会覆盖下面的了,不需要再去判断品牌和型号了
if
hasSpecialCheck
{
continue
if
conditionsMet
{
foundRatio
=
true
break
}
else
{
continue
}
}
// 如果没有设置任何特殊条件,则默认匹配
foundRatio
=
true
break
}
...
...
service/service_tags.go
View file @
932a1cb0
...
...
@@ -98,7 +98,38 @@ func (ts *TagsService) GetLyTags(sku model.LySku) (goodsTags model.GoodsTag) {
}
}
//还要判断source的标签
sourceTagsStr
,
_
:=
redis
.
String
(
redisCon
.
Do
(
"HGET"
,
"goods_source_tags"
,
sku
.
Source
))
if
sourceTagsStr
!=
""
{
systemTags
:=
gjson
.
Get
(
sourceTagsStr
,
"system_tags"
)
.
Array
()
for
_
,
tag
:=
range
systemTags
{
tagNames
=
append
(
tagNames
,
tag
.
String
())
}
}
//判断goods_label的标签
labelTagsStr
,
_
:=
redis
.
String
(
redisCon
.
Do
(
"HGET"
,
"goods_label_tags"
,
sku
.
GoodsTag
.
GoodsLabel
))
if
labelTagsStr
!=
""
{
labelTags
:=
gjson
.
Get
(
labelTagsStr
,
"system_tags"
)
.
Array
()
for
_
,
tag
:=
range
labelTags
{
tagNames
=
append
(
tagNames
,
tag
.
String
())
}
}
//去重tagNames
tagNames
=
removeDuplicateString
(
tagNames
)
goodsTags
.
GoodsTagNames
=
tagNames
goodsTags
.
GoodsTag
=
tags
return
goodsTags
}
func
removeDuplicateString
(
s
[]
string
)
[]
string
{
result
:=
[]
string
{}
temp
:=
map
[
string
]
struct
{}{}
for
_
,
item
:=
range
s
{
if
_
,
ok
:=
temp
[
item
];
!
ok
{
temp
[
item
]
=
struct
{}{}
result
=
append
(
result
,
item
)
}
}
return
result
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment