refactor(material): implement contiguous sequence grouping for specs with count-based descending sort

This commit is contained in:
DXC
2026-04-13 09:14:59 +08:00
parent 4728f91cc7
commit 6aa2142f01

View File

@ -959,8 +959,12 @@ class MaterialBaseService:
@staticmethod @staticmethod
def get_latest_specs(): def get_latest_specs():
""" """
获取所有规格型号的最大连号,按智能分组返回 获取所有规格型号的最大连号,按连续区间分组返回
返回格式: [{"group": "S", "latest": "S0115/S0115"}, {"group": "Opt4xxx", "latest": "Opt4018/Opt4018"}, ...] - 前缀统一大写处理
- 只有数字完全连续N, N+1, N+2...)才认定为同一组
- 数字不连续时断开,形成新组
- 按每组数量降序排列
- 返回每个连续区间的最大值
""" """
import re import re
@ -970,55 +974,81 @@ class MaterialBaseService:
MaterialBase.spec_model != '' MaterialBase.spec_model != ''
).all() ).all()
# 2. 数据结构:{分组名: (原始规格, 数字部分)} # 2. 解析并收集所有有效的 (prefix, num, original_spec)
groups = {} parsed = []
def parse_spec(spec_full):
"""
解析规格型号
返回: (prefix, num, group_name, original_spec)
"""
# 取斜杠前的部分作为基准
base_spec = spec_full.split('/')[0]
# 使用正则解析:字母前缀 + 数字
match = re.match(r'^([A-Za-z]+)(\d+)$', base_spec)
if not match:
return (base_spec, 0, base_spec, spec_full)
prefix, num_str = match.groups()
num = int(num_str)
# 智能分组逻辑
if prefix == 'Opt':
# Opt 按千位段分组
thousand = num // 1000
group = f"Opt{thousand}xxx"
else:
# 常规前缀按原值分组
group = prefix
return (prefix, num, group, spec_full)
# 3. 遍历并分组
for material in specs: for material in specs:
spec = material.spec_model spec = material.spec_model
if not spec: if not spec:
continue continue
prefix, num, group, original_spec = parse_spec(spec) base_spec = spec.split('/')[0]
if group not in groups: match = re.match(r'^([A-Za-z]+)(\d+)$', base_spec)
groups[group] = (original_spec, num) if not match:
continue
prefix, num_str = match.groups()
prefix = prefix.upper()
num = int(num_str)
parsed.append((prefix, num, spec))
# 3. 先按 prefix 升序,再按 num 升序排序
parsed.sort(key=lambda x: (x[0], x[1]))
# 4. 遍历切分连续区间
# 核心逻辑:当 current_num != prev_num + 1 时,断开形成新组
intervals = []
current_prefix = None
current_start = None
current_end = None
current_last_spec = None
for prefix, num, spec in parsed:
if current_prefix is None:
current_prefix = prefix
current_start = num
current_end = num
current_last_spec = spec
elif prefix == current_prefix and num == current_end + 1:
current_end = num
current_last_spec = spec
else: else:
_, existing_num = groups[group] intervals.append({
if num > existing_num: 'prefix': current_prefix,
groups[group] = (original_spec, num) 'start': current_start,
'end': current_end,
'count': current_end - current_start + 1,
'latest': current_last_spec
})
current_prefix = prefix
current_start = num
current_end = num
current_last_spec = spec
# 4. 构建返回结果,按分组名排序 if current_prefix is not None:
result = [ intervals.append({
{"group": group, "latest": spec} 'prefix': current_prefix,
for group, (spec, _) in sorted(groups.items()) 'start': current_start,
] 'end': current_end,
'count': current_end - current_start + 1,
'latest': current_last_spec
})
# 5. 按每组数量降序排列,再按前缀升序
intervals.sort(key=lambda x: (-x['count'], x['prefix']))
# 6. 构建返回结果
result = []
for item in intervals:
prefix = item['prefix']
start = item['start']
end = item['end']
result.append({
"group": f"{prefix}({start}-{end})",
"count": item['count'],
"latest": item['latest']
})
return result return result