一、接口核心功能
该接口用于查询电商系统中商品类目的属性信息,支持:
按类目ID查询属性集合
按属性类型过滤(关键属性$K$、销售属性$S$、普通属性$N$)
分页返回属性数据
多语言属性名支持
二、接口定义
GET /api/category/attributes
请求参数:
参数名 类型 必填 说明
category_id long 是 目标类目ID
attribute_type string 否 属性类型过滤(K/S/N)
page int 否 当前页码(默认1)
page_size int 否 每页数量(默认20)
lang string 否 语言标识(如zh-CN/en-US)
三、技术实现要点
- 数据模型设计
class CategoryAttribute {
Long attrId; // 属性ID
String attrKey; // 属性标识符
Map names; // 多语言名称 {lang:name}
String type; // 属性类型
List options; // 属性选项值
boolean required; // 是否必填
}
- 分页算法 设总记录数为$T$,每页大小为$S$,则总页数: $$P = \left\lceil \frac{T}{S} \right\rceil$$
当前页$C$的数据偏移量: $$O = (C - 1) \times S$$
- 查询逻辑(伪代码)
def query_attributes(category_id, attr_type, page, page_size):
# 验证类目有效性
if not valid_category(category_id):
return error("无效类目ID")
# 构建查询条件
filters = [CATEGORY_ID == category_id]
if attr_type:
filters.append(ATTR_TYPE == attr_type)
# 执行分页查询
total = AttributeTable.count(filters)
attributes = AttributeTable.select(
fields = [ATTR_ID, ATTR_KEY, NAMES, TYPE, OPTIONS],
where = filters,
offset = (page-1)*page_size,
limit = page_size
)
# 组装响应
return {
"page": page,
"total": total,
"data": process_i18n(attributes, lang)
}
四、响应数据结构
{
"code": 0,
"msg": "success",
"data": {
"page": 1,
"total_pages": 5,
"items": [
{
"attr_id": "P1001",
"attr_key": "material",
"attr_name": "材质",
"type": "K",
"options": ["棉", "涤纶", "丝绸"],
"required": true
},
{
"attr_id": "S2005",
"attr_key": "size",
"attr_name": "尺码",
"type": "S",
"options": ["S", "M", "L"]
}
]
}
}
五、性能优化策略
使用Redis缓存类目属性数据,缓存键设计为: $$K_{cache} = \text{attr_} + \text{category_id} + _ + \text{lang}$$
缓存失效策略:类目变更时主动清除
对(category_id, type)建立联合索引
属性值选项使用JSONB类型存储(PostgreSQL)
graph TD
A[客户端请求] --> B[网关层]
B --> C{缓存命中?}
C -->|是| D[直接返回]
C -->|否| E[异步查询DB]
E --> F[回填缓存]
F --> D
六、错误处理规范
错误码 触发场景 解决方案
4001 类目ID不存在 检查类目树状态
4002 属性类型参数错误 验证K/S/N枚举值
5003 分页参数超出范围 计算最大页数$P_{max}$后返回
七、最佳实践建议
前端调用时建议设置page_size最大值不超过100
批量查询时使用类目ID集合: $$Q = {c_1, c_2, \dots, c_n}$$ 通过/api/category/attributes/batch接口减少请求次数
敏感属性(如价格)需增加权限校验:
if(attr.isSensitive() && !user.hasPermission(VIEW_SENSITIVE)){
throw new ForbiddenException();
}