添加 gongyinglia
This commit is contained in:
commit
eb114a74f4
|
|
@ -0,0 +1,374 @@
|
|||
import tkinter as tk
|
||||
from tkinter import ttk, scrolledtext, messagebox
|
||||
from py2neo import Graph
|
||||
import difflib
|
||||
import random
|
||||
import warnings
|
||||
|
||||
# 过滤PNG图像警告
|
||||
warnings.filterwarnings("ignore", category=UserWarning, module="PIL")
|
||||
|
||||
# Neo4j配置
|
||||
graph = Graph("bolt://localhost:7687", auth=("neo4j", "Bdf123456789"))
|
||||
|
||||
|
||||
# 调试打印函数(增强缓存展示)
|
||||
def debug_print(message, cache=None):
|
||||
print(f"[DEBUG] {message}")
|
||||
if cache:
|
||||
print(f"[CACHE] {cache}")
|
||||
|
||||
|
||||
# 模糊匹配函数
|
||||
def fuzzy_match(query, choices):
|
||||
debug_print(f"模糊匹配: {query} in {choices}")
|
||||
if not choices or all(v is None for v in choices):
|
||||
return None
|
||||
matches = difflib.get_close_matches(query, choices, n=1, cutoff=0.3)
|
||||
return matches[0] if matches else None
|
||||
|
||||
|
||||
# 策略推理规则
|
||||
def infer_strategy(risk_level, impact_level):
|
||||
strategy_map = {
|
||||
('高', '大'): ['紧急性策略'],
|
||||
('高', '中'): ['紧急性策略'],
|
||||
('高', '小'): ['转移性策略'],
|
||||
('中', '大'): ['紧急性策略'],
|
||||
('中', '中'): ['预测性策略'],
|
||||
('中', '小'): ['转移性策略'],
|
||||
('低', '大'): ['预测性策略'],
|
||||
('低', '中'): ['预测性策略'],
|
||||
('低', '小'): ['预测性策略'],
|
||||
}
|
||||
return strategy_map.get((risk_level, impact_level), [])
|
||||
|
||||
|
||||
# 检查供应商是否存在
|
||||
def check_supplier_exists(supplier_name):
|
||||
try:
|
||||
query = "MATCH (s:供应商 {名称: $name}) RETURN s LIMIT 1"
|
||||
result = graph.run(query, name=supplier_name).data()
|
||||
exists = bool(result)
|
||||
debug_print(f"供应商 {supplier_name} 存在性检查", {"存在": exists})
|
||||
return exists
|
||||
except Exception as e:
|
||||
debug_print(f"检查供应商存在性失败", {"错误": str(e)})
|
||||
return False
|
||||
|
||||
|
||||
# 查询下级供应商(被影响企业作为起点)
|
||||
def find_downstream_suppliers(supplier_name):
|
||||
try:
|
||||
query = "MATCH (s:供应商 {名称: $name})-[:供应零件给]->(downstream:供应商) RETURN downstream.名称 AS 下级供应商"
|
||||
result = graph.run(query, name=supplier_name).data()
|
||||
suppliers = [item["下级供应商"] for item in result]
|
||||
debug_print(f"查询下级供应商", {"供应商": suppliers})
|
||||
return suppliers
|
||||
except Exception as e:
|
||||
debug_print(f"查询下级供应商失败", {"错误": str(e)})
|
||||
return []
|
||||
|
||||
|
||||
# 查询上级供应商(被影响企业作为终点)
|
||||
def find_upstream_suppliers(supplier_name):
|
||||
try:
|
||||
query = "MATCH (upstream:供应商)-[:供应零件给]->(s:供应商 {名称: $name}) RETURN upstream.名称 AS 上级供应商"
|
||||
result = graph.run(query, name=supplier_name).data()
|
||||
suppliers = [item["上级供应商"] for item in result]
|
||||
debug_print(f"查询上级供应商", {"供应商": suppliers})
|
||||
return suppliers
|
||||
except Exception as e:
|
||||
debug_print(f"查询上级供应商失败", {"错误": str(e)})
|
||||
return []
|
||||
|
||||
|
||||
# 查询生产物料
|
||||
def find_produced_materials(supplier_name):
|
||||
try:
|
||||
query = "MATCH (s:供应商 {名称: $name})-[:生产零件]->(m:物料) RETURN m.编号 AS 物料编号"
|
||||
result = graph.run(query, name=supplier_name).data()
|
||||
materials = [item["物料编号"] for item in result]
|
||||
debug_print(f"查询生产物料", {"物料": materials})
|
||||
return materials
|
||||
except Exception as e:
|
||||
debug_print(f"查询生产物料失败", {"错误": str(e)})
|
||||
return []
|
||||
|
||||
|
||||
# 处理供应商关系和策略替换(整合所有步骤)
|
||||
def process_strategy_replacement(inputs, strategies):
|
||||
try:
|
||||
risk_subject = inputs["风险主体企业"]
|
||||
affected_suppliers = inputs["被影响企业"].split()
|
||||
|
||||
# 企业存在性检查
|
||||
if not check_supplier_exists(risk_subject):
|
||||
messagebox.showerror("错误", f"风险主体供应商 {risk_subject} 不存在于图谱中!")
|
||||
return None
|
||||
for sup in affected_suppliers:
|
||||
if not check_supplier_exists(sup):
|
||||
messagebox.showerror("错误", f"被影响供应商 {sup} 不存在于图谱中!")
|
||||
return None
|
||||
|
||||
# 步骤1:搜索被影响企业影响的企业(下游)
|
||||
affected_influence = None
|
||||
if affected_suppliers:
|
||||
downstream = find_downstream_suppliers(affected_suppliers[0])
|
||||
affected_influence = downstream[0] if downstream else None
|
||||
debug_print("被影响企业影响的企业", {"企业": affected_influence})
|
||||
|
||||
# 步骤2:查询供应物料集合
|
||||
supply_materials = []
|
||||
for aff in affected_suppliers:
|
||||
query = """
|
||||
MATCH (sub:供应商 {名称: $sub})-[rel:供应零件给]->(aff:供应商 {名称: $aff})
|
||||
RETURN rel.物料编号 AS 物料编号
|
||||
"""
|
||||
result = graph.run(query, sub=risk_subject, aff=aff).data()
|
||||
if result and "物料编号" in result[0]:
|
||||
supply_materials.append(result[0]["物料编号"])
|
||||
debug_print("供应物料集合", {"物料": supply_materials})
|
||||
|
||||
# 步骤3:查询可替换企业集合
|
||||
replaceable_suppliers = []
|
||||
if supply_materials:
|
||||
for mat in supply_materials:
|
||||
query = """
|
||||
MATCH (s:供应商)-[:生产零件]->(m:物料 {编号: $mat})
|
||||
WHERE s.名称 <> $risk_subject
|
||||
RETURN s.名称 AS 供应商名称
|
||||
"""
|
||||
replaceable_suppliers.extend([
|
||||
item["供应商名称"]
|
||||
for item in graph.run(query, mat=mat, risk_subject=risk_subject).data()
|
||||
])
|
||||
replaceable_suppliers = list(set(replaceable_suppliers))
|
||||
debug_print("可替换企业集合", {"企业": replaceable_suppliers})
|
||||
|
||||
# 步骤4:查询被影响企业的下级企业集合(上游)
|
||||
lower_level_suppliers = []
|
||||
if affected_suppliers:
|
||||
for aff in affected_suppliers:
|
||||
lower_level_suppliers.extend(find_upstream_suppliers(aff))
|
||||
lower_level_suppliers = list(set(lower_level_suppliers))
|
||||
debug_print("下级企业集合", {"企业": lower_level_suppliers})
|
||||
|
||||
# 步骤5:查询下级企业生产物料集合
|
||||
lower_level_materials = {}
|
||||
if lower_level_suppliers:
|
||||
for sup in lower_level_suppliers:
|
||||
materials = find_produced_materials(sup)
|
||||
if materials:
|
||||
lower_level_materials[sup] = materials
|
||||
debug_print("下级企业生产物料集合", {"物料": lower_level_materials})
|
||||
|
||||
# 步骤6:匹配替换企业
|
||||
replace_supplier = None
|
||||
if lower_level_materials and supply_materials:
|
||||
for sup, mats in lower_level_materials.items():
|
||||
if any(mat in mats for mat in supply_materials):
|
||||
replace_supplier = sup
|
||||
debug_print("匹配到替换企业", {"企业": replace_supplier})
|
||||
break
|
||||
if not replace_supplier and replaceable_suppliers:
|
||||
replace_supplier = random.choice(replaceable_suppliers)
|
||||
debug_print("从可替换企业中选择", {"企业": replace_supplier})
|
||||
|
||||
# 替换策略中的符号
|
||||
processed = []
|
||||
for s in strategies:
|
||||
s = s.replace("#", risk_subject)
|
||||
s = s.replace("*", affected_suppliers[0] if affected_suppliers else "")
|
||||
s = s.replace("%", affected_influence or "")
|
||||
s = s.replace("@_1", replace_supplier or "")
|
||||
processed.append(s)
|
||||
debug_print("替换后的策略", {"策略": processed})
|
||||
|
||||
return processed
|
||||
except Exception as e:
|
||||
debug_print(f"策略替换异常", {"错误": str(e)})
|
||||
messagebox.showerror("错误", f"策略处理失败: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
# 查询风险项策略
|
||||
def query_risk_strategies(inputs):
|
||||
try:
|
||||
if not inputs["具体风险项"] or not inputs["风险主体企业"]:
|
||||
messagebox.showwarning("输入缺失", "具体风险项和风险主体企业为必填项!")
|
||||
return None
|
||||
|
||||
risk_item = inputs["具体风险项"]
|
||||
associated_risk = inputs["关联风险项"]
|
||||
risk_level = inputs["风险等级"]
|
||||
impact_level = inputs["风险影响程度"]
|
||||
|
||||
# 模糊匹配风险项
|
||||
all_risk_names = [node["名称"] for node in graph.nodes.match("风险项") if "名称" in node]
|
||||
matched_risk = fuzzy_match(risk_item, all_risk_names)
|
||||
if not matched_risk:
|
||||
messagebox.showerror("匹配失败", f"风险项 {risk_item} 未找到!")
|
||||
return None
|
||||
|
||||
matched_associated = None
|
||||
if associated_risk:
|
||||
matched_associated = fuzzy_match(associated_risk, all_risk_names)
|
||||
if not matched_associated:
|
||||
messagebox.showerror("匹配失败", f"关联风险项 {associated_risk} 未找到!")
|
||||
return None
|
||||
|
||||
# 推理策略性质
|
||||
strategy_type = infer_strategy(risk_level, impact_level)
|
||||
if not strategy_type:
|
||||
return "策略性质推理失败"
|
||||
|
||||
# 查询策略
|
||||
strategies = []
|
||||
for item in [matched_risk, matched_associated] if matched_associated else [matched_risk]:
|
||||
query = """
|
||||
MATCH (risk:风险项 {名称: $item})-[:有策略类]->(strategy:策略类)
|
||||
MATCH (strategy)-[:策略性质为]->(st:策略性质 {名称: $strategy_type})
|
||||
RETURN strategy.描述 AS 策略名称
|
||||
"""
|
||||
result = graph.run(query, item=item, strategy_type=strategy_type[0]).data()
|
||||
strategies.extend([r["策略名称"] for r in result])
|
||||
debug_print("查询到的策略", {"策略": strategies})
|
||||
|
||||
return strategies, strategy_type, matched_risk, matched_associated
|
||||
except Exception as e:
|
||||
debug_print(f"查询风险策略异常", {"错误": str(e)})
|
||||
messagebox.showerror("错误", f"查询风险策略失败: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
# 查询风险事件
|
||||
def query_risk_events(matched_risk, risk_level, impact_level):
|
||||
query = """
|
||||
MATCH (risk:风险项 {名称: $risk})-[:有风险事件]->(event:风险事件)
|
||||
MATCH (event)-[:风险等级为]->(rl {名称: $risk_level})
|
||||
MATCH (event)-[:风险影响程度为]->(ril {名称: $impact_level})
|
||||
RETURN event.描述 AS 事件名称
|
||||
"""
|
||||
result = graph.run(query, risk=matched_risk, risk_level=risk_level, impact_level=impact_level).data()
|
||||
events = [r["事件名称"] for r in result]
|
||||
debug_print("查询到的风险事件", {"事件": events})
|
||||
return events
|
||||
|
||||
|
||||
# 查询风险事件的策略
|
||||
def query_event_strategies(event_name):
|
||||
query = """
|
||||
MATCH (event:风险事件 {描述: $event_name})-[:策略为]->(strategy:策略)
|
||||
RETURN strategy.描述 AS 策略描述
|
||||
"""
|
||||
result = graph.run(query, event_name=event_name).data()
|
||||
strategies = [r["策略描述"] for r in result]
|
||||
debug_print(f"风险事件 {event_name} 的策略", {"策略": strategies})
|
||||
return strategies
|
||||
|
||||
|
||||
# GUI界面
|
||||
root = tk.Tk()
|
||||
root.title("供应链风险查询系统")
|
||||
root.geometry("800x900")
|
||||
|
||||
# 输入字段
|
||||
fields = [
|
||||
("风险事件", tk.StringVar(), None),
|
||||
("具体风险项", tk.StringVar(), None),
|
||||
("关联风险项", tk.StringVar(), None),
|
||||
("风险等级", tk.StringVar(value="高"), ["高", "中", "低"]),
|
||||
("风险影响程度", tk.StringVar(value="大"), ["大", "中", "小"]),
|
||||
("风险主体企业", tk.StringVar(), None),
|
||||
("被影响企业", tk.StringVar(), None),
|
||||
]
|
||||
|
||||
# 创建输入界面
|
||||
for idx, (label, var, options) in enumerate(fields):
|
||||
frame = ttk.Frame(root)
|
||||
frame.pack(fill="x", padx=10, pady=5)
|
||||
ttk.Label(frame, text=label, width=25).pack(side="left")
|
||||
if options:
|
||||
ttk.OptionMenu(frame, var, var.get(), *options).pack(side="left", fill="x", expand=True)
|
||||
else:
|
||||
ttk.Entry(frame, textvariable=var, width=40).pack(side="left", fill="x", expand=True)
|
||||
|
||||
|
||||
def submit():
|
||||
try:
|
||||
inputs = {label: var.get() for label, var, _ in fields}
|
||||
debug_print("提交查询", {"输入": inputs})
|
||||
|
||||
# 查询风险策略
|
||||
strategy_data = query_risk_strategies(inputs)
|
||||
if not strategy_data:
|
||||
return
|
||||
|
||||
strategies, strategy_type, matched_risk, matched_associated = strategy_data
|
||||
|
||||
# 查询风险事件
|
||||
risk_events = query_risk_events(matched_risk, inputs["风险等级"], inputs["风险影响程度"])
|
||||
|
||||
# 处理供应商关系和策略替换
|
||||
processed_strategies = process_strategy_replacement(inputs, strategies)
|
||||
if not processed_strategies:
|
||||
return
|
||||
|
||||
# 构建结果(只显示特定信息)
|
||||
result = "### 供应链风险分析与策略推荐 ###\n\n"
|
||||
|
||||
result += f"• 具体风险项: {matched_risk}\n"
|
||||
if matched_associated:
|
||||
result += f"• 关联风险项: {matched_associated}\n"
|
||||
result += f"• 策略性质: {strategy_type[0]}\n"
|
||||
|
||||
if processed_strategies:
|
||||
result += f"• 最优策略: {processed_strategies[0]}\n"
|
||||
else:
|
||||
result += "• 最优策略: 未找到匹配策略\n"
|
||||
|
||||
if risk_events:
|
||||
result += "\n• 风险事件及对应策略:\n"
|
||||
for i, event in enumerate(risk_events, 1):
|
||||
result += f" {i}. {event}\n"
|
||||
event_strategies = query_event_strategies(event)
|
||||
if event_strategies:
|
||||
for j, strategy in enumerate(event_strategies, 1):
|
||||
result += f" - 策略: {strategy}\n"
|
||||
else:
|
||||
result += " - 暂无对应策略\n"
|
||||
else:
|
||||
result += "\n• 风险事件: 未找到匹配风险事件\n"
|
||||
|
||||
# 显示结果
|
||||
result_window = tk.Toplevel(root)
|
||||
result_window.title("查询结果")
|
||||
text = scrolledtext.ScrolledText(result_window, wrap=tk.WORD, font=("Arial", 12))
|
||||
text.pack(expand=True, fill="both", padx=10, pady=10)
|
||||
text.insert(tk.END, result)
|
||||
text.config(state="disabled")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"提交查询异常", {"错误": str(e)})
|
||||
messagebox.showerror("错误", f"提交查询时发生错误: {str(e)}")
|
||||
|
||||
|
||||
# 测试数据按钮
|
||||
def test_data():
|
||||
test_inputs = {
|
||||
"具体风险项": "地震后节点企业的产能下降",
|
||||
"风险等级": "高",
|
||||
"风险影响程度": "小",
|
||||
"风险主体企业": "企业A",
|
||||
"被影响企业": "企业B"
|
||||
}
|
||||
for label, var, _ in fields:
|
||||
if label in test_inputs:
|
||||
var.set(test_inputs[label])
|
||||
|
||||
|
||||
ttk.Button(root, text="提交查询", command=submit).pack(pady=20)
|
||||
ttk.Button(root, text="填充测试数据", command=test_data).pack(pady=10)
|
||||
|
||||
root.mainloop()
|
||||
Loading…
Reference in New Issue