You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
11 KiB

import json
import logging
from util.mysql_utils import mysql_client
# 配置日志
logger = logging.getLogger(__name__)
class GraphStyleService:
@staticmethod
def _get_or_create_group(group_name: str) -> int:
"""内部辅助方法:获取或创建方案组 ID"""
if not group_name or group_name.strip() == "":
group_name = "默认方案"
group_name = group_name.strip()
# 1. 查询是否存在
check_sql = "SELECT id FROM graph_style_groups WHERE group_name = %s LIMIT 1"
existing = mysql_client.execute_query(check_sql, (group_name,))
if existing:
return int(existing[0]['id'])
# 2. 不存在则插入
insert_sql = "INSERT INTO graph_style_groups (group_name, is_active, is_default) VALUES (%s, %s, %s)"
mysql_client.execute_update(insert_sql, (group_name, False, False))
# 3. 获取新生成的 ID
final_check = mysql_client.execute_query(check_sql, (group_name,))
return int(final_check[0]['id']) if final_check else 1
@staticmethod
def create_config(canvas_name: str, current_label: str, styles_dict: dict, group_name: str = None) -> bool:
"""【纯新增】用于另存为或初始保存"""
config_json = json.dumps(styles_dict, ensure_ascii=False)
target_group_id = GraphStyleService._get_or_create_group(group_name)
sql = """
INSERT INTO graph_configs (canvas_name, current_label, config_json, group_id)
VALUES (%s, %s, %s, %s)
"""
affected_rows = mysql_client.execute_update(sql, (canvas_name, current_label, config_json, target_group_id))
return affected_rows > 0
@staticmethod
def update_config(config_id: int, canvas_name: str, current_label: str, styles_dict: dict,
group_name: str = None, is_auto_save: bool = False, target_group_id: int = None) -> bool:
"""
核心更新逻辑支持精准 ID 移动优化了逻辑优先级判断
"""
if not config_id:
logger.error("更新失败:缺少 config_id")
return False
config_json_str = json.dumps(styles_dict, ensure_ascii=False)
try:
# --- 步骤 1:查询当前数据库状态 ---
curr_sql = "SELECT group_id, canvas_name, current_label, config_json FROM graph_configs WHERE id = %s"
current_data = mysql_client.execute_query(curr_sql, (config_id,))
if not current_data:
logger.warning(f"更新失败:找不到 ID 为 {config_id} 的配置")
return False
curr_row = current_data[0]
old_group_id = int(curr_row['group_id'])
# --- 步骤 2:确定目标组 ID (调整优先级) ---
# 优先级 1: 只要传了 target_group_id,就说明是移动操作,优先级最高
if target_group_id is not None:
final_group_id = int(target_group_id)
logger.info(f"【移动模式】配置 {config_id}: 强制设定目标组 ID 为 {final_group_id}")
# 优先级 2: 自动保存模式下,锁定 group_id 不允许变动
elif is_auto_save:
final_group_id = old_group_id
logger.debug(f"【自保模式】配置 {config_id}: 锁定原组 ID {final_group_id}")
# 优先级 3: 传了 group_name 但没传 target_group_id (旧版移动逻辑)
elif group_name:
final_group_id = GraphStyleService._get_or_create_group(group_name)
logger.info(f"【名称模式】配置 {config_id}: 根据名称 [{group_name}] 获得 ID {final_group_id}")
# 兜底:保持不变
else:
final_group_id = old_group_id
# --- 步骤 3:差异比对 ---
# 增加对数据一致性的判定
has_changed = (
int(final_group_id) != old_group_id or
canvas_name != curr_row['canvas_name'] or
current_label != curr_row['current_label'] or
config_json_str != curr_row['config_json']
)
if not has_changed:
logger.info(
f"配置 {config_id} 内容无变化 (最终目标ID:{final_group_id}, 原ID:{old_group_id}),跳过数据库更新")
return True
# --- 步骤 4:执行更新 ---
sql = """
UPDATE graph_configs
SET group_id = %s, canvas_name = %s, current_label = %s, config_json = %s
WHERE id = %s
"""
params = (final_group_id, canvas_name, current_label, config_json_str, config_id)
affected_rows = mysql_client.execute_update(sql, params)
if affected_rows > 0:
logger.info(f"更新成功,ID: {config_id}, 归属组已变更为: {final_group_id}")
return True
else:
logger.error(f"数据库更新执行成功但受影响行数为 0,ID: {config_id}")
return False
except Exception as e:
logger.error(f"Service 层更新异常: {str(e)}", exc_info=True)
return False
@staticmethod
def get_grouped_configs() -> list:
"""获取嵌套结构的方案列表"""
groups_sql = "SELECT id, group_name, is_active, is_default FROM graph_style_groups ORDER BY is_default DESC, id ASC"
groups = mysql_client.execute_query(groups_sql) or []
configs_sql = "SELECT id, group_id, canvas_name, current_label, config_json, create_time FROM graph_configs"
configs = mysql_client.execute_query(configs_sql) or []
# 格式化配置数据
for conf in configs:
conf['styles'] = json.loads(conf['config_json']) if conf.get('config_json') else {}
# 保持 key 简洁
if 'config_json' in conf:
del conf['config_json']
if conf.get('create_time') and not isinstance(conf['create_time'], str):
conf['create_time'] = conf['create_time'].strftime('%Y-%m-%d %H:%M:%S')
# 组装嵌套结构
for g in groups:
g['is_active'] = bool(g['is_active'])
g['is_default'] = bool(g['is_default'])
g['configs'] = [c for c in configs if c['group_id'] == g['id']]
g['expanded'] = g['is_active']
return groups
@staticmethod
3 months ago
def get_active_configs() -> list:
"""
获取 is_active = 1 的方案组及其配置项
返回长度为 0 1 的列表因为通常只有一个激活组
"""
# 1. 查询 is_active = 1 的组(按 id 排序,取第一个以防有多个)
group_sql = """
SELECT id, group_name, is_active, is_default
FROM graph_style_groups
WHERE is_active = 1
ORDER BY id ASC
LIMIT 1
"""
groups = mysql_client.execute_query(group_sql) or []
if not groups:
return [] # 没有激活的组
group = groups[0]
group_id = group['id']
# 2. 查询该组下的所有配置
configs_sql = """
SELECT id, group_id, canvas_name, current_label, config_json, create_time
FROM graph_configs
WHERE group_id = %s
"""
configs = mysql_client.execute_query(configs_sql, (group_id,)) or []
# 3. 处理配置项
for conf in configs:
if conf.get('config_json'):
try:
conf['styles'] = json.loads(conf['config_json'])
except (TypeError, ValueError):
conf['styles'] = {}
del conf['config_json']
if conf.get('create_time') and not isinstance(conf['create_time'], str):
conf['create_time'] = conf['create_time'].strftime('%Y-%m-%d %H:%M:%S')
# 4. 组装结果
group['configs'] = configs
return [group] # 返回单元素列表,保持接口兼容性
@staticmethod
def apply_group_all(group_id: int) -> bool:
"""切换当前激活的方案组"""
try:
# 重置所有组的激活状态
mysql_client.execute_update("UPDATE graph_style_groups SET is_active = %s", (False,))
# 激活目标组
affected_rows = mysql_client.execute_update(
"UPDATE graph_style_groups SET is_active = %s WHERE id = %s",
(True, group_id)
)
return affected_rows > 0
except Exception as e:
logger.error(f"应用全案异常: {str(e)}")
return False
@staticmethod
def set_default_group(group_id: int) -> bool:
"""设为默认方案组"""
try:
mysql_client.execute_update("UPDATE graph_style_groups SET is_default = %s", (False,))
affected_rows = mysql_client.execute_update(
"UPDATE graph_style_groups SET is_default = %s WHERE id = %s",
(True, group_id)
)
return affected_rows > 0
except Exception as e:
logger.error(f"设置默认方案异常: {str(e)}")
return False
@staticmethod
def delete_group(group_id: int) -> bool:
"""级联删除组及其下的所有配置"""
try:
# 先删配置,再删组(如果没设外键级联)
mysql_client.execute_update("DELETE FROM graph_configs WHERE group_id = %s", (group_id,))
affected_rows = mysql_client.execute_update("DELETE FROM graph_style_groups WHERE id = %s", (group_id,))
return affected_rows > 0
except Exception as e:
logger.error(f"删除方案组异常: {str(e)}")
return False
@staticmethod
def delete_config(config_id: int) -> bool:
"""删除单个配置"""
try:
affected_rows = mysql_client.execute_update("DELETE FROM graph_configs WHERE id = %s", (config_id,))
return affected_rows > 0
except Exception as e:
logger.error(f"删除配置异常: {str(e)}")
return False
@staticmethod
def batch_delete_configs(config_ids: list) -> int:
"""批量删除"""
if not config_ids: return 0
try:
placeholders = ', '.join(['%s'] * len(config_ids))
sql = f"DELETE FROM graph_configs WHERE id IN ({placeholders})"
return mysql_client.execute_update(sql, tuple(config_ids))
except Exception as e:
logger.error(f"批量删除异常: {str(e)}")
return 0
@staticmethod
def get_group_list() -> list:
"""简单的方案名称列表"""
sql = "SELECT id, group_name, is_active, is_default FROM graph_style_groups ORDER BY is_default DESC, id DESC"
return mysql_client.execute_query(sql) or []