SaveMattressExcutor_Analysis.md 24 KB

SaveMattressExcutor.cs 方法调用分析

本文档详细分析了 SaveMattressExcutor.cs 文件中每一个嵌套方法调用的功能和实现细节。


1. BllHelper.GetToken(string token)

位置: BLL/BllHelper.cs:36-46

功能: 从内存缓存中获取用户token对应的会话信息

实现细节:

public static TokenData GetToken(string token)
{
    if (_tokens.ContainsKey(token))
    {
        return _tokens[token];
    }
    else
    {
        return null;
    }
}

关键点:

  • 使用 LJCache<string, TokenData> 缓存,默认过期时间120分钟
  • Token在用户登录时通过 SetToken() 方法绑定
  • 返回的 TokenData 包含用户ID、用户名、员工ID等信息
  • 如果token不存在或已过期,返回null

使用场景: 验证用户会话是否有效,防止未授权访问


2. HelperBase.GetHelper(SqlCommand cmd, Context context)

位置: BLL/HelperBase.cs:32-38

功能: 泛型工厂方法,创建并初始化Helper实例

实现细节:

public static T GetHelper<T>(SqlCommand cmd, Context context = null)
    where T : HelperBase, new()
{
    var rslt = new T();
    rslt.cmd = cmd;
    rslt.context = context ?? new Context();
    return rslt;
}

关键点:

  • 泛型约束:T必须继承自 HelperBase 且有无参构造函数
  • 注入数据库命令对象和上下文信息
  • Context包含操作时间和token数据

使用场景:

  • 获取 MattressHelper 实例用于床垫业务逻辑
  • 获取 BedNetHelper 实例用于床网业务逻辑

3. ObjectHelper.DeepCopy(T obj)

位置: Tools/ObjectHelper.cs:14-17

功能: 对对象进行深度复制,创建完全独立的副本

实现原理:

public static T DeepCopy<T>(T obj)
{
    return Copy(obj, true);
}

private static T Copy<T>(T obj, bool deep = false)
{
    // 处理值类型和字符串
    if (obj == null || obj.GetType().IsValueType || obj is string)
    {
        return obj;
    }

    // 创建新实例
    object retval = Activator.CreateInstance(obj.GetType());

    // 处理字典类型
    if (obj is IDictionary) { /* 复制字典项 */ }
    // 处理列表类型
    else if (obj is IList) { /* 复制列表项 */ }
    // 处理普通对象
    else
    {
        // 使用反射获取所有字段(包括私有字段)
        var fields = obj.GetType().GetFields(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.Static
        ).ToList();

        // 递归复制基类字段
        var type = obj.GetType();
        while ((type = type.BaseType) != typeof(object))
        {
            fields.AddRange(type.GetFields(
                BindingFlags.NonPublic | BindingFlags.Instance
            ));
        }

        // 深度复制每个字段
        foreach (FieldInfo field in fields)
        {
            if (field.IsLiteral) continue; // 跳过常量
            var value = field.GetValue(obj);
            if (deep)
            {
                value = Copy(value, true); // 递归深度复制
            }
            field.SetValue(retval, value);
        }
    }
    return (T)retval;
}

关键特性:

  • 递归复制: 对嵌套对象也进行深度复制
  • 完整复制: 包括私有字段和基类字段
  • 类型保持: 保持原始对象的类型结构
  • 性能考虑: 使用反射,有一定性能开销

使用场景: 创建床垫数据的独立副本,避免修改原始请求数据


4. AutoInit.AutoInitS(SqlCommand cmd, T instance)

位置: Tools/AutoInit.cs:21-104

功能: 根据数据库表结构自动初始化对象的非空属性

实现机制:

4.1 缓存机制

private static readonly ConcurrentDictionary<string, CacheItem> ColumnInfoCache =
    new ConcurrentDictionary<string, CacheItem>();
private static readonly TimeSpan CacheExpirationTime = TimeSpan.FromMinutes(60);

4.2 主逻辑

public static void AutoInitS<T>(SqlCommand cmd, T instance)
{
    var tableName = instance.GetType().Name;
    var currentTime = DateTime.Now;

    // 检查缓存
    if (ColumnInfoCache.TryGetValue(tableName, out var cache) &&
        currentTime - cache.CacheTime < CacheExpirationTime)
    {
        // 使用缓存的列信息
        foreach (var item in cache.ColumnInfo)
        {
            var column = item.Value.Column;
            var isNullable = item.Value.IsNullable;

            var property = instance.GetType().GetProperties()
                .FirstOrDefault(prop => prop.Name.Equals(column,
                    StringComparison.OrdinalIgnoreCase));

            if (property == null) continue;

            // 初始化非空且值为null的属性
            if (isNullable.Equals("NO") && property.GetValue(instance) == null)
            {
                var type = property.PropertyType;

                // 处理Nullable<T>类型
                if (type.IsGenericType &&
                    type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    type = type.GetGenericArguments()[0];
                }

                // 根据类型设置默认值
                if (type.IsAssignableFrom(typeof(string)))
                {
                    property.SetValue(instance, "");
                }
                else if (type.IsAssignableFrom(typeof(int)) ||
                         type.IsAssignableFrom(typeof(decimal)) ||
                         type.IsAssignableFrom(typeof(byte)))
                {
                    property.SetValue(instance, Activator.CreateInstance(type));
                }
            }
        }
    }
    else
    {
        // 缓存过期,从数据库重新加载列信息
        var columnInfo = new Dictionary<string, ColumnItem>();
        cmd.CommandText = @"SELECT COLUMN_NAME, IS_NULLABLE
                           FROM INFORMATION_SCHEMA.COLUMNS
                           WHERE TABLE_NAME = @tableName";
        cmd.Parameters.Clear();
        cmd.Parameters.AddWithValue("@tableName", instance.GetType().Name);

        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                var column = Convert.ToString(reader["COLUMN_NAME"]).Trim();
                var isNullable = Convert.ToString(reader["IS_NULLABLE"]).Trim();
                columnInfo[column] = new ColumnItem(column, isNullable);
            }
        }

        // 更新缓存
        ColumnInfoCache[tableName] = new CacheItem
        {
            CacheTime = currentTime,
            ColumnInfo = columnInfo
        };

        // 使用新加载的列信息初始化属性(逻辑同上)
    }
}

关键特性:

  • 性能优化: 使用60分钟缓存避免频繁查询数据库
  • 智能匹配: 属性名不区分大小写匹配
  • 类型安全: 正确处理Nullable类型
  • 默认值策略:
    • string → 空字符串
    • int/decimal/byte → 0
  • 使用场景:

    • 初始化床垫主表对象
    • 初始化床垫明细列表中的每个对象

    5. MattressHelper.GetMattress(int mattressid, string fields)

    位置: Helper/MattressHelper.cs:1667

    功能: 从数据库获取床垫报价单数据

    实现细节:

    public u_mattress GetMattress(int mattressid, string fields = null)
    {
        if(mattressid <= 0)
        {
            throw new LJCommonException("查找床垫报价失败,ID错误!");
        }
    
        var mattress = new u_mattress() { mattressid = mattressid };
    
        // 默认字段列表(非常长,包含所有业务字段)
        fields = fields ?? @"mattressid,mattressname,deptid,mattresstypeid,
                            mattresscode,mattress_width,mattress_length,
                            mattress_height,packtype,packqty,woodpallettype,
                            old_mtrlname,total_hr_cost,total_material_cost,
                            fees_dscrp,total_fees_cost,taxrate,commissionrate,
                            commission,fob,profitrate,profitrate_point,
                            butao_point,chaizhuang_point,haimian_point,
                            dannum_rate,hrcost,biandaicost,zhizao_amt,
                            guanli_rate,discount,extras_cost,dept_profitrate,
                            dept_profitrate_rangli,moneyrate,mattressrelcode,
                            flag,auditingrep,auditingdate,createtime,createby,
                            total_cost,nottax_factory_cost,nottax_dept_cost,
                            taxes,dept_cost,foreign_cost,foreign_cost_bz,
                            xd_flag,xd_auditingrep,xd_auditingdate,qr_flag,
                            qr_auditingrep,qr_auditingdate,js1_flag,
                            erp_mtrlcode,erp_cb_updatetime,if_bcp_type,
                            if_zhedie_type,if_w_butao,biandai_qty,other_rate,
                            if_moneyrate,parentid,...";
    
        DbSqlHelper.SelectOne(cmd, mattress, fields);
        return mattress;
    }
    

    关键点:

    • 参数校验:mattressid必须大于0
    • 字段选择:支持自定义字段列表,默认获取所有业务字段
    • 状态检查:用于验证床垫是否已审核或已下单
    • 异常处理:ID错误时抛出业务异常

    使用场景:

    • 检查床垫报价单状态(是否已审核、已下单)
    • 获取现有床垫数据进行修改

    6. MattressHelper.MattressCalculateCost(...)

    位置: Helper/MattressHelper.cs:2174

    功能: 计算床垫成本和价格,包括多种单据类型的价格

    核心逻辑:

    6.1 多单据类型计算

    public void MattressCalculateCost(u_mattress mattress,
        List<u_mattress_mx_mtrl> mattressMx,
        List<u_mattress_mx_extra> extraProcesses,
        List<u_mattress_mx_extra> extraCosts)
    {
        // 默认只保存标准的单据
        mattress.dannum_type = 2;
    
        // 作为记录不含税出厂价、部门含税价的各个大小单价格的载体
        var _mattressMain = ObjectHelper.DeepCopy(mattress);
    
        List<u_mattress> sumMattress = new List<u_mattress>();
    
        // 循环计算4种单据类型的价格
        for (var i = 1; i < 5; i++)
        {
            var _mattress = ObjectHelper.DeepCopy(mattress);
    
            if (mattress.dannum_type.Value != i)
            {
                _mattress.dannum_type = i;
    
                // 调用公式计算
                CalCulateFormula(_mattress, mattressMx, true, true,
                               extraProcesses, extraCosts);
    
                // 提取关键价格
                var nottax_factory_cost = Replacements
                    .Where(o => o.label == "【不含税出厂价】").ToList();
                var dept_cost = Replacements
                    .Where(o => o.label == "【部门含税价】").ToList();
    
                if (nottax_factory_cost.Count > 0)
                {
                    decimal nottax_value = Convert.ToDecimal(
                        nottax_factory_cost[0].value);
    
                    // 分别记录4种单据类型的不含税价
                    if (i == 1) _mattress.dijia_cost1 = nottax_value;
                    else if (i == 2) _mattress.dijia_cost2 = nottax_value;
                    else if (i == 3) _mattress.dijia_cost3 = nottax_value;
                    else if (i == 4) _mattress.dijia_cost4 = nottax_value;
                }
    
                // 同样记录4种单据类型的含税价
                if (dept_cost.Count > 0)
                {
                    decimal dept_costValue = Convert.ToDecimal(
                        dept_cost[0].value);
                    if (i == 1) _mattress.dannum_cost1 = dept_costValue;
                    else if (i == 2) _mattress.dannum_cost2 = dept_costValue;
                    else if (i == 3) _mattress.dannum_cost3 = dept_costValue;
                    else if (i == 4) _mattress.dannum_cost4 = dept_costValue;
                }
    
                sumMattress.Add(_mattress);
            }
        }
    
        // 计算当前单据类型的价格
        CalCulateFormula(mattress, mattressMx, true, true,
                        extraProcesses, extraCosts);
    
        // 将其他单据类型的价格合并到主对象
        for (var i = 1; i < 5; i++)
        {
            if (mattress.dannum_type.Value == i)
            {
                // 当前类型直接提取
                // (代码逻辑同上)
            }
            else
            {
                // 其他类型从sumMattress中获取
                var matchMattress = sumMattress
                    .Where(o => o.dannum_type == i).ToList();
                if (matchMattress.Count > 0)
                {
                    // 合并价格信息
                }
            }
        }
    }
    

    关键特性:

    • 4种单据类型: 小单、中单、大单、特大单
    • 价格双轨制:
      • 不含税出厂价 (dijia_cost1-4)
      • 部门含税价 (dannum_cost1-4)
    • 深度复制: 每种类型独立计算,互不影响
    • 公式计算: 底层调用 CalCulateFormula() 方法

    使用场景:

    • 保存前计算床垫成本
    • 生成多种规格的价格供客户选择

    7. MattressHelper.SaveMattressPro(u_mattress mattress, ...)

    位置: Helper/MattressHelper.cs:2532

    功能: 保存床垫报价单主表和明细数据到数据库

    核心流程:

    7.1 数据校验

    SaveMattressCheck(mattress);
    

    7.2 字段定义

    var fields = @"mattressname, deptid, mattresscode, mattresstypeid,
                   mattress_width, mattress_length, mattress_height,
                   packtype, packqty, woodpallettype, total_hr_cost,
                   total_material_cost, fees_dscrp, total_fees_cost,
                   total_cost, taxrate, taxes, commissionrate, commission,
                   fob, profitrate, dept_profitrate, moneyrate,
                   nottax_factory_cost, nottax_dept_cost, foreign_cost,
                   diameter, area, cabinet_type, hrcost, biandaicost,
                   mattressrelcode, other_rate, flag, dept_profitrate_rangli,
                   profitrate_point, if_moneyrate, discount, if_m_chai,
                   if_z_chai, if_d_chai, if_n_butao, if_w_butao,
                   if_m_wbutao_way, s_cover_qty, z_cover_qty, x_cover_qty,
                   biandai_qty, s_m_cover_qty, z_m_cover_qty, x_m_cover_qty,
                   chaizhuang_point, haimian_point, if_zhedie_type,
                   qr_auditingrep, qr_auditingdate, if_bcp_type,
                   zhizao_amt, foreign_cost_bz, cubage, extras_cost,
                   extras_cost_dscrp, parentid, flag, xd_flag, dannum_type,
                   dannum_cost1,dannum_cost2, dannum_cost3, dannum_cost4,
                   dijia_cost1,dijia_cost2,dijia_cost3,dijia_cost4,
                   version,total_mtrl_hr_cost";
    
    var fieldsMx = "mattressmxid,mattressid,formulaid,formula,replace_formula,
                    if_success,priceunit,shrinkage,mtrlid,price,gram_weight,
                    cloth_width,if_inputqty,qty,costamt,if_areaprice,thickness,
                    chastr,xu,useqty,useformula,replace_useformula,gydscrp,
                    mattress_width,mattress_length,sidecover";
    
    var fieldsExtra = "mattressmxid,mattressid, extraid, extramxid, extraname,
                      price, qty, dscrp, mtrlid";
    

    7.3 新建/修改逻辑

    mattress.qr_auditingdate = context.opdate;
    mattress.qr_auditingrep = context.tokendata.username;
    
    if (mattress.mattressid <= 0)
    {
        // 新建床垫
        AutoInit.AutoInitS(mattress);
        mattress.createtime = context.opdate;
        if (string.IsNullOrEmpty(mattress.createby))
            mattress.createby = context.tokendata.username;
    
        // 获取新ID
        mattress.mattressid = BllHelper.GetID(cmd, "u_mattress");
        mattress.version = 1;
        fields += ",mattressid, createtime, createby";
    
        // 自动生成床垫编码
        if (string.IsNullOrEmpty(mattress.mattresscode))
        {
            var mattresstype = new u_mattress_type() {
                mattresstypeid = mattress.mattresstypeid
            };
            DbSqlHelper.SelectOne(cmd, mattresstype, "typecode");
    
            mattress.mattresscode =
                $"{mattresstype.typecode}-" +
                $"{context.opdate.ToString("yyyyMMdd")}" +
                $"{(mattress.mattressid % 10000).ToString("D4")}";
        }
    
        DbSqlHelper.Insert(cmd, "u_mattress", null, mattress, fields);
    }
    else
    {
        // 修改床垫
        AutoInit.AutoInitS(mattress);
        mattress.version++;
        DbSqlHelper.Update(cmd, "u_mattress", null, mattress,
                          "mattressid", fields);
    
        // 删除所有明细
        cmd.CommandText = @"DELETE u_mattress_mx_mtrl WITH (ROWLOCK)
                           WHERE mattressid = @mattressid";
        cmd.Parameters.Clear();
        cmd.Parameters.AddWithValue("@mattressid", mattress.mattressid);
        cmd.ExecuteNonQuery();
    
        // 删除所有额外费用明细
        cmd.CommandText = @"DELETE u_mattress_mx_extra WITH (ROWLOCK)
                           WHERE mattressid = @mattressid";
        cmd.Parameters.Clear();
        cmd.Parameters.AddWithValue("@mattressid", mattress.mattressid);
        cmd.ExecuteNonQuery();
    }
    

    7.4 保存明细

    // 保存材料明细
    if (mattress.mxList != null && mattress.mxList.Count > 0)
    {
        foreach (var item in mattress.mxList)
        {
            AutoInit.AutoInitS(cmd, item);
            item.mattressid = mattress.mattressid;
    
            if (item.mattressmxid == null || item.mattressmxid <= 0)
            {
                item.mattressmxid = BllHelper.GetID(cmd, "u_mattress_mx_mtrl");
            }
    
            DbSqlHelper.Insert(cmd, "u_mattress_mx_mtr l", null, item, fieldsMx);
        }
    }
    
    // 保存特殊工艺
    if (mattress.extraList != null && mattress.extraList.Count > 0)
    {
        foreach (var item in mattress.extraList)
        {
            AutoInit.AutoInitS(cmd, item);
            item.mattressid = mattress.mattressid;
            item.mattressmxid = BllHelper.GetID(cmd, "u_mattress_mx_extra");
            DbSqlHelper.Insert(cmd, "u_mattress_mx_extra", null, item, fieldsExtra);
        }
    }
    
    // 保存特殊费用
    if (mattress.extraCostList != null && mattress.extraCostList.Count > 0)
    {
        foreach (var item in mattress.extraCostList)
        {
            AutoInit.AutoInitS(cmd, item);
            item.mattressid = mattress.mattressid;
            item.mattressmxid = BllHelper.GetID(cmd, "u_mattress_mx_extra");
            DbSqlHelper.Insert(cmd, "u_mattress_mx_extra", null, item, fieldsExtra);
        }
    }
    

    7.5 保存价格历史

    var hisprice = new u_his_price
    {
        bednetid_mattressid = mattress.mattressid,
        typeid = 1,
        cmpdate = context.opdate,
        cmpemp = context.tokendata.username,
        nottax_dept_cost = mattress.nottax_dept_cost,
        dept_cost = mattress.dept_cost,
        foreign_cost = mattress.foreign_cost,
        // ... 保存4种单据类型的价格
        dannum_cost1 = mattress.dannum_cost1,
        // ... (dannum_cost2-4)
        dijia_cost1 = mattress.dijia_cost1,
        // ... (dijia_cost2-4)
        fob = mattress.fob,
        cabinet_type = mattress.cabinet_type,
        taxrate = mattress.taxrate,
        commission = mattress.commission
    };
    DbSqlHelper.Insert(cmd, "u_his_price", null, hisprice, fieldsHs);
    

    7.6 处理子规格

    ProcessSubSpecs(mattress, ifErp);
    

    7.7 解锁

    if (!ignoreLock)
        LockHelper.UnLockBill(cmd, BillKeyWord, mattress.mattressid,
                             context.tokendata.username, 0);
    

    关键特性:

    • 版本控制: 每次修改version自增
    • 自动编码: 自动生成床垫编码(类型代码+日期+序号)
    • 完整性: 先删后插明细,确保数据一致性
    • 审计: 记录确认人和确认时间
    • 历史记录: 保存价格变更历史
    • 锁机制: 保存后自动解锁

    8. MattressHelper.CopyMattressInterface(int copyid, int mattressid)

    位置: Helper/MattressHelper.cs:1918

    功能: 复制床垫接口清单数据

    实现细节:

    public void CopyMattressInterface(int copyid, int mattressid)
    {
        if (copyid == 0 || mattressid == 0) return;
    
        // 复制主接口清单
        cmd.CommandText = @"INSERT INTO u_mattress_interface (
                            mattressid, printid, itemname, bj_pzname,
                            bj_namemx, bj_inputtype, actual_size, sb_craft,
                            actual_size_sb, erp_pzid, ss_rate, ls_rate
                            )
                            SELECT @mattressid, printid, itemname, bj_pzname,
                                   bj_namemx, bj_inputtype, actual_size,
                                   sb_craft, actual_size_sb, erp_pzid,
                                   ss_rate, ls_rate
                            FROM u_mattress_interface
                            WHERE mattressid = @copy_id";
        cmd.Parameters.Clear();
        cmd.Parameters.AddWithValue("@mattressid", mattressid);
        cmd.Parameters.AddWithValue("@copy_id", copyid);
        cmd.ExecuteNonQuery();
    
        // 复制接口清单明细
        cmd.CommandText = @"INSERT INTO u_mattress_interface_qd (
                            mattressid, printid, itemname, bj_pzname,
                            bj_pzname_mx, mtrlid, erp_mtrlid, wrkgrpid,
                            useqty, dscrp, actual_useqty, qd_actual_size,
                            qd_pfgroupqty, formulaid
                            )
                            SELECT @mattressid, printid, itemname, bj_pzname,
                                   bj_pzname_mx, mtrlid, erp_mtrlid,
                                   wrkgrpid, useqty, dscrp, actual_useqty,
                                   qd_actual_size, qd_pfgroupqty, formulaid
                            FROM u_mattress_interface_qd
                            WHERE mattressid = @copy_id";
        cmd.Parameters.Clear();
        cmd.Parameters.AddWithValue("@mattressid", mattressid);
        cmd.Parameters.AddWithValue("@copy_id", copyid);
        cmd.ExecuteNonQuery();
    }
    

    关键特性:

    • 批量复制: 使用INSERT INTO...SELECT语句批量复制
    • 两表复制:
      • u_mattress_interface: 主接口清单
      • u_mattress_interface_qd: 接口清单明细(材料清单)
    • 参数验证: copyid和mattressid必须都大于0

    使用场景:

    • 复制报价时保留原有接口清单配置
    • 快速创建相似产品配置

    方法调用流程图

    SaveMattressExcutor.Execute()
    │
    ├─ BllHelper.GetToken()                    // 验证用户会话
    │
    ├─ HelperBase.GetHelper<MattressHelper>()  // 获取床垫Helper
    ├─ HelperBase.GetHelper<BedNetHelper>()    // 获取床网Helper
    │
    ├─ ObjectHelper.DeepCopy()                 // 创建数据副本
    │
    ├─ AutoInit.AutoInitS()                    // 初始化主表对象
    ├─ AutoInit.AutoInitS() [循环]             // 初始化明细对象
    │
    ├─ MattressHelper.GetMattress()            // 获取现有数据(修改场景)
    │   └─ 检查状态:已审核/已下单
    │
    ├─ MattressHelper.MattressCalculateCost()  // 计算成本价格
    │   └─ CalCulateFormula()                   // 公式计算
    │       ├─ 床网计算
    │       ├─ 材料成本
    │       └─ 价格计算
    │
    ├─ [如果是复制] 复制初始化
    │   └─ 重置ID和审核状态
    │
    ├─ [开始事务]
    │   │
    │   ├─ MattressHelper.SaveMattressPro()    // 保存主表和明细
    │   │   ├─ SaveMattressCheck()              // 数据校验
    │   │   ├─ AutoInit.AutoInitS()            // 初始化属性
    │   │   ├─ BllHelper.GetID()                // 获取新ID
    │   │   ├─ DbSqlHelper.Insert/Update()     // 数据库操作
    │   │   ├─ [保存明细]
    │   │   ├─ [保存价格历史]
    │   │   ├─ ProcessSubSpecs()                // 处理子规格
    │   │   └─ LockHelper.UnLockBill()          // 解锁
    │   │
    │   └─ [如果是复制]
    │       └─ MattressHelper.CopyMattressInterface()  // 复制接口清单
    │           ├─ 复制主接口清单
    │           └─ 复制接口清单明细
    │
    └─ [提交事务]
    

    性能优化要点

    1. 缓存机制: AutoInit使用60分钟缓存减少数据库查询
    2. 批量操作: CopyMattressInterface使用批量INSERT提升性能
    3. 事务管理: 整个保存过程在事务中完成,确保数据一致性
    4. 版本控制: 使用version字段防止并发修改冲突
    5. 锁机制: 使用表锁和行锁防止并发问题

    异常处理策略

    1. 业务异常: 使用LJCommonException抛出业务错误
    2. 状态验证: 检查审核状态、下单状态等
    3. 事务回滚: 发生异常时回滚所有操作
    4. 用户友好: 返回清晰的错误信息

    安全考虑

    1. 会话验证: 每个请求验证token有效性
    2. 权限检查: 虽然此方法未展示,但其他方法有权限验证
    3. SQL注入: 使用参数化查询防止SQL注入
    4. 数据完整性: 使用事务和锁保证数据一致性

    总结

    SaveMattressExcutor实现了一个完整的床垫报价保存流程,涵盖了:

    • 数据验证: 用户会话、业务状态、数据完整性
    • 业务逻辑: 成本计算、价格生成、子规格处理
    • 数据持久化: 主表、明细、历史记录保存
    • 并发控制: 版本控制、锁机制、事务管理
    • 性能优化: 缓存、批量操作、合理的事务范围

    整个方法调用链清晰、职责明确,是典型的企业级业务处理模式。