本文档详细分析了 SaveMattressExcutor.cs 文件中每一个嵌套方法调用的功能和实现细节。
位置: 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分钟SetToken() 方法绑定TokenData 包含用户ID、用户名、员工ID等信息使用场景: 验证用户会话是否有效,防止未授权访问
位置: 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;
}
关键点:
HelperBase 且有无参构造函数使用场景:
MattressHelper 实例用于床垫业务逻辑BedNetHelper 实例用于床网业务逻辑位置: 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;
}
关键特性:
使用场景: 创建床垫数据的独立副本,避免修改原始请求数据
位置: Tools/AutoInit.cs:21-104
功能: 根据数据库表结构自动初始化对象的非空属性
实现机制:
private static readonly ConcurrentDictionary<string, CacheItem> ColumnInfoCache =
new ConcurrentDictionary<string, CacheItem>();
private static readonly TimeSpan CacheExpirationTime = TimeSpan.FromMinutes(60);
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
};
// 使用新加载的列信息初始化属性(逻辑同上)
}
}
关键特性:
使用场景:
位置: 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;
}
关键点:
使用场景:
位置: Helper/MattressHelper.cs:2174
功能: 计算床垫成本和价格,包括多种单据类型的价格
核心逻辑:
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)
{
// 合并价格信息
}
}
}
}
关键特性:
CalCulateFormula() 方法使用场景:
位置: Helper/MattressHelper.cs:2532
功能: 保存床垫报价单主表和明细数据到数据库
核心流程:
SaveMattressCheck(mattress);
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";
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();
}
// 保存材料明细
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);
}
}
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);
ProcessSubSpecs(mattress, ifErp);
if (!ignoreLock)
LockHelper.UnLockBill(cmd, BillKeyWord, mattress.mattressid,
context.tokendata.username, 0);
关键特性:
位置: 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();
}
关键特性:
u_mattress_interface: 主接口清单u_mattress_interface_qd: 接口清单明细(材料清单)使用场景:
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() // 复制接口清单
│ ├─ 复制主接口清单
│ └─ 复制接口清单明细
│
└─ [提交事务]
SaveMattressExcutor实现了一个完整的床垫报价保存流程,涵盖了:
整个方法调用链清晰、职责明确,是典型的企业级业务处理模式。