# -*- coding: utf-8 -*- """触发器总开关+批量刷新: ①计算引擎:触发器统一加 WHEN 开关=0 条件;新增 开关表/开关设/批量刷新方法;重建时确保开关表存在 ②管线(图号/箱号粘贴、删除):开关=1 → 批量写 → 刷新图号全部计算 → 开关=0 (全在原事务内) 幂等。""" import io ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件" n = 0 def edit(path, pairs, tag): global s, n for old, new in pairs: if old not in s: if new in s: print(tag, "已有(跳过)") continue raise AssertionError(tag + " 锚点缺失: " + old[:70]) s = s.replace(old, new, 1) n += 1 io.open(path, "w", encoding="utf-8-sig").write(s) print(tag, "ok") p = ROOT + r"\计算引擎.cs" s = io.open(p, encoding="utf-8-sig").read() # 1) 开关常量与开关表 @@SKIP@@ public const string 触发器前缀 = "trgjs_";''', ''' /// 触发器名前缀(重建时按前缀全清)。 public const string 触发器前缀 = "trgjs_"; /// 触发器总开关条件(批量操作时设 开关=1 跳过触发器体,提交前批量刷新+设回 0)。 public const string 开关条件 = "(SELECT 开关 FROM _触发器开关 WHERE id=1)=0";''') # 上面的辅助不适用,直接顺序替换 def rep2(old, new): global s, n if old not in s: if new in s: print("跳过(已改)") return raise AssertionError("锚点缺失: " + old[:70]) s = s.replace(old, new, 1) n += 1 rep2(''' /// 触发器名前缀(重建时按前缀全清)。 public const string 触发器前缀 = "trgjs_";''', ''' /// 触发器名前缀(重建时按前缀全清)。 public const string 触发器前缀 = "trgjs_"; /// 触发器总开关条件:批量操作时置 开关=1,触发器整体短路,提交前批量刷新后置回 0。 public const string 开关条件 = "(SELECT 开关 FROM _触发器开关 WHERE id=1)=0";''') # 2) EnsureFormulaTable 里顺带建开关表 rep2(''' using (var cmd = new System.Data.SQLite.SQLiteCommand( "CREATE TABLE IF NOT EXISTS \\"" + 公式表名 + "\\" (" + "层级 TEXT, 目标列 TEXT, 表达式 TEXT, 排序 INTEGER, 启用 INTEGER DEFAULT 1);", conn)) cmd.ExecuteNonQuery();''', ''' using (var cmd = new System.Data.SQLite.SQLiteCommand( "CREATE TABLE IF NOT EXISTS \\"" + 公式表名 + "\\" (" + "层级 TEXT, 目标列 TEXT, 表达式 TEXT, 排序 INTEGER, 启用 INTEGER DEFAULT 1);", conn)) cmd.ExecuteNonQuery(); // 开关表放项目库(触发器同库),由 重建项目全部触发器 保证存在''') # 3) 所有触发器 CREATE 加 WHEN 开关条件 rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" + 元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet + " WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" + 元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet + " WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" + 元件表 + "\\" FOR EACH ROW BEGIN " + 刷箱号 + 刷图号 + "END;");''', ''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" + 元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet + " WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" + 元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet + " WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" + 元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷箱号 + 刷图号 + "END;");''') rep2(''' Exec(conn, tx, "DROP TRIGGER IF EXISTS " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" + 箱号ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew + " WHERE rowid=NEW.rowid; UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "'; END;");''', ''' Exec(conn, tx, "DROP TRIGGER IF EXISTS " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" + 箱号ID.Replace("'", "''") + "' AND " + 开关条件 + " BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew + " WHERE rowid=NEW.rowid; UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "'; END;");''') rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" + 箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" + 箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");''', ''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;"); Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;");''') rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + "th_" + suffix + "_u AFTER UPDATE OF \\"" + 图号输入列[0] + "\\" ON \\"" + 图号表名 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" + 图号ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 图号表名 + "\\" SET " + thSetNew + " WHERE rowid=NEW.rowid; END;");''', ''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + "th_" + suffix + "_u AFTER UPDATE OF \\"" + 图号输入列[0] + "\\" ON \\"" + 图号表名 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" + 图号ID.Replace("'", "''") + "' AND " + 开关条件 + " BEGIN UPDATE \\"" + 图号表名 + "\\" SET " + thSetNew + " WHERE rowid=NEW.rowid; END;");''') # 4) 重建项目全部触发器:确保开关表+默认行 rep2(''' var old = new List();''', ''' using (var cmd = new System.Data.SQLite.SQLiteCommand( "CREATE TABLE IF NOT EXISTS _触发器开关 (id INTEGER PRIMARY KEY, 开关 INTEGER DEFAULT 0);", conn, tx)) cmd.ExecuteNonQuery(); Exec(conn, tx, "INSERT OR IGNORE INTO _触发器开关 (id, 开关) VALUES (1, 0);"); var old = new List();''') # 5) 批量刷新方法(管线提交前调用):按表索引刷新该图号全部计算 rep2(''' /// 新表(管线内建表后)即时补触发器——只处理该表自身。''', ''' /// /// 批量刷新某图号的全部计算(触发器关闭期间用):元件表派生列→箱号行聚合→图号行聚合。 /// 全部为集合 SQL,每表一条 UPDATE;开关由调用方控制。 /// public static void 批量刷新图号计算(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, string 图号ID, string 图号表名, System.Data.SQLite.SQLiteConnection fakConn) { var 元件公式 = 加载公式(fakConn, "元件"); var 箱号公式 = 加载公式(fakConn, "箱号"); var 图号公式 = 加载公式(fakConn, "图号"); string xhTbl = null; using (var cmd = new System.Data.SQLite.SQLiteCommand( "SELECT 实际表名 FROM 表索引 WHERE 类型='箱号' AND 图号=@th LIMIT 1;", conn, tx)) { cmd.Parameters.AddWithValue("@th", 图号ID); object o = cmd.ExecuteScalar(); xhTbl = o == null || o == DBNull.Value ? null : o.ToString(); } if (string.IsNullOrEmpty(xhTbl)) return; // 1) 每张元件表:派生列集合 UPDATE var yj表们 = new List(); using (var cmd = new System.Data.SQLite.SQLiteCommand( "SELECT 实际表名 FROM 表索引 WHERE 类型='元件' AND 图号=@th;", conn, tx)) { cmd.Parameters.AddWithValue("@th", 图号ID); using (var r = cmd.ExecuteReader()) while (r.Read()) yj表们.Add(r.GetString(0)); } string yjSet = 生成Set片段(元件公式, "ROW", null); foreach (var yj in yj表们) Exec(conn, tx, "UPDATE \\"" + yj + "\\" SET " + yjSet + ";"); // 2) 箱号每行:聚合(行自己的元件表 SUM)——行级一条 UPDATE string xhSet = 生成Set片段(箱号公式, "ROW", null); // 由下方逐行替换 SUM // 逐行:SUM 需要按行的元件表,取 表索引(元件) 列表 using (var cmd = new System.Data.SQLite.SQLiteCommand( "SELECT 箱号, 实际表名 FROM 表索引 WHERE 类型='元件' AND 图号=@th;", conn, tx)) { cmd.Parameters.AddWithValue("@th", 图号ID); using (var r = cmd.ExecuteReader()) { var pairs = new List(); while (r.Read()) pairs.Add(new[] { r.IsDBNull(0) ? "" : r.GetString(0), r.IsDBNull(1) ? "" : r.GetString(1) }); foreach (var pr in pairs) { string 行Set = 生成Set片段(箱号公式, "ROW", pr[1]); Exec(conn, tx, "UPDATE \\"" + xhTbl + "\\" SET " + 行Set + " WHERE \\"内部ID\\"='" + pr[0].Replace("'", "''") + "';"); } } } // 3) 图号行:聚合(FROM 箱号表) string thSet = 生成Set片段(图号公式, "ROW", xhTbl); Exec(conn, tx, "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';"); } /// 开关设值(1=批量模式跳过触发器,0=恢复自动触发)。 public static void 开关设(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, int 值) { Exec(conn, tx, "UPDATE _触发器开关 SET 开关=" + 值 + " WHERE id=1;"); } /// 新表(管线内建表后)即时补触发器——只处理该表自身。''') io.open(p, "w", encoding="utf-8-sig").write(s) print("计算引擎 完成", n, "处")