修改费用计算代码

This commit is contained in:
chentianrui
2025-08-22 18:13:09 +08:00
parent 8d595b339c
commit be848c3e78
20 changed files with 2569 additions and 360 deletions
@@ -3,6 +3,7 @@ import os
import math
from typing import Dict, List, Any, Tuple
from copy import deepcopy
from memory_profiler import profile
from equipment_calculation.item_acquisition import get_quantity_nodes, get_classified_resource_nodes, load_project_data
@@ -140,7 +141,10 @@ def process_DXdata(json_data):
# 在 resource_fee_calculator.py 文件中修改 calc_rcj_count 函数中的相关代码
def calc_rcj_count(
rcj_nodes: List[Tuple[Dict[str, Any], str]], project_children: List[Dict[str, Any]], json_file_path: str = None
rcj_nodes: List[Tuple[Dict[str, Any], str]],
project_children: List[Dict[str, Any]],
json_file_path: str = None,
json_data: dict | None = None,
) -> List[Dict[str, Any]]:
"""
计算人材机节点的数量,考虑父级消耗量
@@ -231,8 +235,8 @@ def calc_rcj_count(
result_nodes.append(node_copy)
return result_nodes
# 创建工程信息上下文(顶层上下文)
project_context = create_project_contexts(json_file_path=json_file_path)
# 创建工程信息上下文(顶层上下文,优先使用 json_data
project_context = create_project_contexts(json_file_path=json_file_path, json_data=json_data)
# 遍历所有节点,计算数量
for node, parent_id in rcj_nodes:
@@ -684,6 +688,7 @@ def calculate_rcj_fees(
json_file_path: str,
project_name: str,
project_guid: str = None,
json_data: dict | None = None,
) -> Dict[str, Any]:
"""
计算项目划分节点下所有人材机节点的合价
@@ -698,7 +703,7 @@ def calculate_rcj_fees(
"""
# 加载项目数据
data, project_data, cost_setting, project_division, target_node = load_project_data(
json_file_path, project_name, project_guid
json_file_path, project_name, project_guid, json_data=json_data
)
# 如果没有找到项目划分节点
@@ -717,7 +722,56 @@ def calculate_rcj_fees(
material_nodes = [] # 材料节点 (节点, 父级节点) 元组列表
machine_nodes = [] # 机械节点 (节点, 父级节点) 元组列表
# 递归处理工程量节点,提取人材机节点
# 工具:类型判断与资源拆分上提
def _is_resource_type(t):
return t in ("人工", "材料", "机械", "2", "3", "4")
def _normalize_type(t):
return {"2": "人工", "3": "材料", "4": "机械"}.get(t, t)
def _flatten_resource_splits(n, inherited_type=None, qty_factor: float = 1.0):
"""
将可能带 children 的人材机节点递归拆平:
- 若是人材机且有children:不计入父,children 继承本节点类型继续拆分;
- 若是人材机且无children:作为叶子返回;
- 若非人材机:递归其children以发现资源叶子。
返回叶子资源节点列表(dict)。
"""
results = []
if not isinstance(n, dict):
return results
n_type = _normalize_type(n.get("类型") or n.get("type"))
children = n.get("children") or []
if _is_resource_type(n_type):
# 当前节点是人材机
current_type = _normalize_type(inherited_type or n_type)
# 当前资源节点自身数量,默认1
try:
self_qty = float((n.get("数量") or "1").strip())
except Exception:
self_qty = 1.0
new_factor = qty_factor * self_qty
if children:
for ch in children:
results.extend(_flatten_resource_splits(ch, current_type, new_factor))
else:
leaf = deepcopy(n)
leaf["类型"] = current_type
# 叶子数量 = 父链乘积 * 自身(若叶子也有数量则已计入 new_factor)
try:
# 如果叶子本身还有数量字段,已包含在 new_factor 中,这里直接覆盖为乘积
leaf["数量"] = str(new_factor)
except Exception:
leaf["数量"] = str(new_factor)
results.append(leaf)
else:
# 非资源,继续向下查找
for ch in children:
results.extend(_flatten_resource_splits(ch, inherited_type, qty_factor))
return results
# 递归处理工程量节点,提取人材机节点(对children内的人材机执行拆分上提)
def process_nodes(nodes):
for node in nodes:
# 如果是定额节点,处理其材机列表和children
@@ -756,22 +810,25 @@ def calculate_rcj_fees(
f"定额节点 '{node.get('项目名称', node.get('name', '未命名'))}' 有子节点,数量: {len(node['children'])}"
)
for child in node["children"]:
child_type = child.get("类型", child.get("type"))
if child_type in ["人工", "2"]:
labor_nodes.append((child, node))
print(
f"从children找到人工节点: {child.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
elif child_type in ["材料", "3"]:
material_nodes.append((child, node))
print(
f"从children找到材料节点: {child.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
elif child_type in ["机械", "4"]:
machine_nodes.append((child, node))
print(
f"从children找到机械节点: {child.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
# 将children中的资源节点拆分为叶子资源,排除中间父级
flattened = _flatten_resource_splits(child)
for leaf in flattened:
leaf_type = _normalize_type(leaf.get("类型") or leaf.get("type"))
if leaf_type == "人工":
labor_nodes.append((leaf, node))
print(
f"从children(拆分后)找到人工节点: {leaf.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
elif leaf_type == "材料":
material_nodes.append((leaf, node))
print(
f"从children(拆分后)找到材料节点: {leaf.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
elif leaf_type == "机械":
machine_nodes.append((leaf, node))
print(
f"从children(拆分后)找到机械节点: {leaf.get('名称', '未命名')}, 父节点: {node.get('项目名称', node.get('name', '未命名'))}"
)
else:
print(f"定额节点 '{node.get('项目名称', node.get('name', '未命名'))}' 没有子节点")
@@ -820,11 +877,14 @@ def calculate_rcj_fees(
# result_nodes.append(node_copy)
# return result_nodes
# 创建工程信息上下文(顶层上下文)
project_context = create_project_contexts(json_file_path=json_file_path)
# 创建工程信息上下文(顶层上下文,优先使用 json_data
project_context = create_project_contexts(json_file_path=json_file_path, json_data=json_data)
with open(json_file_path, "r", encoding="utf-8") as file:
data = json.load(file)
if json_data is not None:
data = json_data
else:
with open(json_file_path, "r", encoding="utf-8") as file:
data = json.load(file)
processed_data = process_DXdata(data)
@@ -984,6 +1044,7 @@ def calculate_resource_fees(
project_name: str,
project_guid: str = None,
calculation_strategy=None,
json_data: dict | None = None,
) -> str:
"""
计算人材机合价
@@ -1004,7 +1065,7 @@ def calculate_resource_fees(
calculation_strategy = DefaultCalculationStrategy()
# 获取项目划分节点的GUID
_, _, _, _, target_node = load_project_data(json_file_path, project_name, project_guid)
_, _, _, _, target_node = load_project_data(json_file_path, project_name, project_guid, json_data=json_data)
# 如果传入了GUID但与节点的GUID不一致,优先使用传入的GUID
node_guid = target_node.get("GUID") or target_node.get("guid", "") if target_node else ""
project_guid = project_guid if project_guid else node_guid
@@ -1055,10 +1116,16 @@ def calculate_resource_fees(
# 计算人材机节点的合价,传递project_guid参数
# 这里使用 calculation_strategy 的 calculate_rcj_fees 方法
if hasattr(calculation_strategy, "calculate_rcj_fees"):
rcj_results = calculation_strategy.calculate_rcj_fees(json_file_path, project_name, project_guid)
try:
rcj_results = calculation_strategy.calculate_rcj_fees(
json_file_path, project_name, project_guid, json_data=json_data
)
except TypeError:
# 向后兼容:旧策略不支持 json_data 形参
rcj_results = calculation_strategy.calculate_rcj_fees(json_file_path, project_name, project_guid)
else:
# 如果计算策略没有实现 calculate_rcj_fees 方法,使用原始函数
rcj_results = calculate_rcj_fees(json_file_path, project_name, project_guid)
rcj_results = calculate_rcj_fees(json_file_path, project_name, project_guid, json_data=json_data)
# 检查是否有人材机数据
has_data = False