diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index 0c0625d..25bfaa2 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -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 \ No newline at end of file