# SaveMattressExcutor.cs 方法调用分析

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

---

## 1. BllHelper.GetToken(string token)

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

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

**实现细节**:
```csharp
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<T>(SqlCommand cmd, Context context)

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

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

**实现细节**:
```csharp
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>(T obj)

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

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

**实现原理**:
```csharp
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<T>(SqlCommand cmd, T instance)

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

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

**实现机制**:

### 4.1 缓存机制
```csharp
private static readonly ConcurrentDictionary<string, CacheItem> ColumnInfoCache =
    new ConcurrentDictionary<string, CacheItem>();
private static readonly TimeSpan CacheExpirationTime = TimeSpan.FromMinutes(60);
```

### 4.2 主逻辑
```csharp
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<T>类型
- **默认值策略**:
  - string → 空字符串
  - int/decimal/byte → 0

**使用场景**:
- 初始化床垫主表对象
- 初始化床垫明细列表中的每个对象

---

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

**位置**: `Helper/MattressHelper.cs:1667`

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

**实现细节**:
```csharp
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 多单据类型计算
```csharp
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 数据校验
```csharp
SaveMattressCheck(mattress);
```

### 7.2 字段定义
```csharp
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 新建/修改逻辑
```csharp
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 保存明细
```csharp
// 保存材料明细
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 保存价格历史
```csharp
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 处理子规格
```csharp
ProcessSubSpecs(mattress, ifErp);
```

### 7.7 解锁
```csharp
if (!ignoreLock)
    LockHelper.UnLockBill(cmd, BillKeyWord, mattress.mattressid,
                         context.tokendata.username, 0);
```

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

---

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

**位置**: `Helper/MattressHelper.cs:1918`

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

**实现细节**:
```csharp
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实现了一个完整的床垫报价保存流程，涵盖了：

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

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