refactor(material): implement contiguous sequence grouping for specs with count-based descending sort
This commit is contained in:
@ -959,8 +959,12 @@ class MaterialBaseService:
|
||||
@staticmethod
|
||||
def get_latest_specs():
|
||||
"""
|
||||
获取所有规格型号的最大连号,按智能分组返回
|
||||
返回格式: [{"group": "S", "latest": "S0115/S0115"}, {"group": "Opt4xxx", "latest": "Opt4018/Opt4018"}, ...]
|
||||
获取所有规格型号的最大连号,按连续区间分组返回
|
||||
- 前缀统一大写处理
|
||||
- 只有数字完全连续(N, N+1, N+2...)才认定为同一组
|
||||
- 数字不连续时断开,形成新组
|
||||
- 按每组数量降序排列
|
||||
- 返回每个连续区间的最大值
|
||||
"""
|
||||
import re
|
||||
|
||||
@ -970,55 +974,81 @@ class MaterialBaseService:
|
||||
MaterialBase.spec_model != ''
|
||||
).all()
|
||||
|
||||
# 2. 数据结构:{分组名: (原始规格, 数字部分)}
|
||||
groups = {}
|
||||
# 2. 解析并收集所有有效的 (prefix, num, original_spec)
|
||||
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:
|
||||
spec = material.spec_model
|
||||
if not spec:
|
||||
continue
|
||||
|
||||
prefix, num, group, original_spec = parse_spec(spec)
|
||||
base_spec = spec.split('/')[0]
|
||||
|
||||
if group not in groups:
|
||||
groups[group] = (original_spec, num)
|
||||
match = re.match(r'^([A-Za-z]+)(\d+)$', base_spec)
|
||||
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:
|
||||
_, existing_num = groups[group]
|
||||
if num > existing_num:
|
||||
groups[group] = (original_spec, num)
|
||||
intervals.append({
|
||||
'prefix': current_prefix,
|
||||
'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. 构建返回结果,按分组名排序
|
||||
result = [
|
||||
{"group": group, "latest": spec}
|
||||
for group, (spec, _) in sorted(groups.items())
|
||||
]
|
||||
if current_prefix is not None:
|
||||
intervals.append({
|
||||
'prefix': current_prefix,
|
||||
'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
|
||||
Reference in New Issue
Block a user