package Stock import ( "ERP_storage/logs" "ERP_storage/models/Basic" "ERP_storage/models/IOTNetworkCard" "errors" "fmt" "strconv" "strings" "time" _ "github.com/astaxie/beego/cache/redis" "github.com/beego/beego/v2/adapter/orm" orm2 "github.com/beego/beego/v2/client/orm" "gogs.baozhida.cn/zoie/ERP_libs/lib" ) type Device struct { Id int `orm:"column(ID);size(11);auto;pk"` T_contract_number string `orm:"size(256);null"` // 合同编号 T_product_id int `orm:"size(20);null"` // 产品id T_in_number string `orm:"size(256);null"` // 入库编号 T_out_number string `orm:"size(256);null"` // 出库编号 T_sn string `orm:"size(256);null"` // 设备sn T_iccid string `orm:"size(256);null"` // sim卡号 T_imei string `orm:"size(256);null"` // 模组imei T_State int `orm:"size(2);default(2)"` // 1-已出库 2-未出库/入库 T_remark string `orm:"type(text);null"` // 备注 T_project string `orm:"type(text);null"` // 出库项目 T_project_log string `orm:"type(text);null"` // 出库项目 T_device_number string `orm:"size(256);null"` // 移动端设备编号 T_batch_number string `orm:"type(256);null"` // 批次号 CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` //auto_now_add 第一次保存时才设置时间 UpdateTime time.Time `orm:"column(update_time);type(timestamp);null;auto_now"` //auto_now 每次 model 保存时都会对时间自动更新 } func (t *Device) TableName() string { return "device" // 数据库名称 // ************** 替换 FormulaList ************** } type DeviceDaoImpl struct { orm orm.Ormer } func NewDevice(orm orm.Ormer) *DeviceDaoImpl { return &DeviceDaoImpl{orm: orm} } func init() { //注册模型 orm.RegisterModel(new(Device)) } type Device_R struct { Id int T_contract_number string // 合同编号 T_in_number string // 入库编号 T_out_number string // 出库编号 T_sn string // 设备sn T_iccid string // 物联网卡号 T_imei string // 模组imei T_State int // 1-已出库 2-未出库 T_device_state int // 1-已出库 2-未出库 T_remark string T_project string T_project_log string // ---------产品信息----------- T_product_id int T_product_name string T_product_class int T_product_class_name string T_product_model string T_product_spec string T_product_img string T_device_number string // 设备编号 } // 单个设备转换 func DeviceToDevice_R(t Device_R) (r Device_R) { r.Id = t.Id r.T_contract_number = t.T_contract_number r.T_product_id = t.T_product_id r.T_in_number = t.T_in_number r.T_out_number = t.T_out_number r.T_sn = t.T_sn r.T_iccid = t.T_iccid r.T_imei = t.T_imei r.T_State = t.T_device_state r.T_device_state = t.T_device_state r.T_remark = t.T_remark r.T_project = t.T_project r.T_project_log = t.T_project_log r.T_product_name = t.T_product_name r.T_product_class = t.T_product_class r.T_product_model = t.T_product_model r.T_product_spec = t.T_product_spec r.T_product_img = t.T_product_img r.T_product_class_name = Basic.Read_ProductClass_Get(t.T_product_class) // 单个查询物联网卡信息 if len(r.T_iccid) > 0 { card, _ := IOTNetworkCard.Read_IOTNetworkCard_ByT_iccid(r.T_iccid) r.T_device_number = card.T_device_number } return r } // 批量处理设备转换 func DevicesToDevice_Rs(devices []Device_R) []Device_R { if len(devices) == 0 { return []Device_R{} } // 收集所有ICCID iccids := make([]string, 0, len(devices)) for _, device := range devices { if len(device.T_iccid) > 0 { iccids = append(iccids, device.T_iccid) } } // 批量查询物联网卡信息 cardMap, err := IOTNetworkCard.Read_IOTNetworkCards_ByT_iccids(iccids) if err != nil { logs.Error(lib.FuncName(), "批量查询物联网卡失败:", err) } // 批量查询产品类别信息 productClassMap := make(map[int]string) for _, device := range devices { if device.T_product_class > 0 { if _, exists := productClassMap[device.T_product_class]; !exists { productClassMap[device.T_product_class] = Basic.Read_ProductClass_Get(device.T_product_class) } } } // 转换设备信息 results := make([]Device_R, len(devices)) for i, device := range devices { r := Device_R{ Id: device.Id, T_contract_number: device.T_contract_number, T_product_id: device.T_product_id, T_in_number: device.T_in_number, T_out_number: device.T_out_number, T_sn: device.T_sn, T_iccid: device.T_iccid, T_imei: device.T_imei, T_State: device.T_device_state, T_device_state: device.T_device_state, T_remark: device.T_remark, T_project: device.T_project, T_project_log: device.T_project_log, T_product_name: device.T_product_name, T_product_class: device.T_product_class, T_product_model: device.T_product_model, T_product_spec: device.T_product_spec, T_product_img: device.T_product_img, } // 设置产品类别名称 if className, exists := productClassMap[device.T_product_class]; exists { r.T_product_class_name = className } // 设置设备编号 if len(device.T_iccid) > 0 { if card, exists := cardMap[device.T_iccid]; exists { r.T_device_number = card.T_device_number } } results[i] = r } return results } // 添加 func (dao *DeviceDaoImpl) Add_Device(r Device) (id int64, err error) { now := time.Now().Format("15:04:05") date, _ := lib.TimeStrToTime(r.CreateTime.Format("2006-01-02") + " " + now) r.CreateTime = date id, err = dao.orm.Insert(&r) if err != nil { logs.Error(lib.FuncName(), err) } return id, err } // T_type 1-出库 2-入库 func (dao *DeviceDaoImpl) AddOrUpdate_Device(r Device, T_type int) (id int64, err error) { now := time.Now().Format("15:04:05") date, _ := lib.TimeStrToTime(r.CreateTime.Format("2006-01-02") + " " + now) r.CreateTime = date id, err = dao.orm.Insert(&r) if err != nil { logs.Error(lib.FuncName(), err) } return } func (dao *DeviceDaoImpl) Read_Device_ByT_sn(T_sn string) (r Device, err error) { qs := dao.orm.QueryTable(new(Device)) var list []Device _, err = qs.Limit(1, 0).Filter("T_sn", T_sn).OrderBy("-CreateTime").All(&list) if err != nil { logs.Error(lib.FuncName(), err) } if len(list) == 0 { return r, orm.ErrNoRows } if len(list) > 0 { r = list[0] } return } func (dao *DeviceDaoImpl) Read_DeviceSn_List(T_contract_number string, T_product_id int, T_in_number, T_out_number string) (r []string, err error) { qs := dao.orm.QueryTable(new(Device)) // 过滤 cond := orm.NewCondition() cond.And("T_product_id", T_product_id) if len(T_contract_number) > 0 { cond = cond.And("T_contract_number", T_contract_number) } if len(T_in_number) > 0 { cond = cond.And("T_in_number", T_in_number) } if len(T_out_number) > 0 { cond = cond.And("T_out_number", T_out_number) } var maps []Device _, err = qs.SetCond((*orm2.Condition)(cond)).All(&maps) if err != nil { logs.Error(lib.FuncName(), err) return } for _, v := range maps { r = append(r, v.T_sn) } return } func (dao *DeviceDaoImpl) Read_Device_List(T_name, T_product_name, T_product_model string, T_State, page, page_z int) (r []Device_R, cnt int64) { var offset int if page <= 1 { offset = 0 } else { offset = (page - 1) * page_z } // 使用参数化查询代替字符串拼接,防止SQL注入并提高性能 whereConditions := []string{"d.t__state > 0"} var params []interface{} // 构建搜索条件 if len(T_name) > 0 { searchTerm := "%" + T_name + "%" whereConditions = append(whereConditions, "(d.t_contract_number LIKE ? OR d.t_out_number LIKE ? OR d.t_sn LIKE ? OR d.t_iccid LIKE ? OR d.t_project LIKE ?)") params = append(params, searchTerm, searchTerm, searchTerm, searchTerm, searchTerm) } if T_State > 0 { whereConditions = append(whereConditions, "d.t__state = ?") params = append(params, T_State) } if len(T_product_name) > 0 { whereConditions = append(whereConditions, "p.t_name LIKE ?") params = append(params, "%"+T_product_name+"%") } if len(T_product_model) > 0 { whereConditions = append(whereConditions, "p.t_model LIKE ?") params = append(params, "%"+T_product_model+"%") } // 组合WHERE条件 whereClause := " WHERE " + strings.Join(whereConditions, " AND ") // 优化子查询:使用窗口函数替代子查询(MySQL 5.7不支持窗口函数,使用JOIN优化) // 使用FORCE INDEX提示优化器使用主键索引 countSQL := ` SELECT COUNT(*) FROM ( SELECT d.t_sn FROM device d FORCE INDEX (PRIMARY) JOIN product p ON d.t_product_id = p.ID JOIN ( SELECT t_sn, MAX(create_time) AS max_create_time FROM device GROUP BY t_sn ) latest ON d.t_sn = latest.t_sn AND d.create_time = latest.max_create_time ` + whereClause + ` ) AS count_query` // 执行计数查询 var count int64 err := dao.orm.Raw(countSQL, params...).QueryRow(&count) if err != nil { logs.Error(lib.FuncName(), "Count query error:", err) return nil, 0 } if count == 0 { return nil, 0 } // 主查询 - 只选择需要的列,避免使用SELECT * dataSQL := ` SELECT d.ID, d.t_contract_number, d.t_product_id, d.t_in_number, d.t_out_number, d.t_sn, d.t_iccid, d.t_imei, d.t__state AS t_device_state, d.t_remark, d.t_project, d.t_project_log, d.t_device_number, d.t_batch_number, p.t_name AS t_product_name, p.t_model AS t_product_model, p.t_class AS t_product_class, p.t_spec AS t_product_spec, p.t_img AS t_product_img FROM device d FORCE INDEX (PRIMARY) JOIN product p ON d.t_product_id = p.ID JOIN ( SELECT t_sn, MAX(create_time) AS max_create_time FROM device GROUP BY t_sn ) latest ON d.t_sn = latest.t_sn AND d.create_time = latest.max_create_time ` + whereClause + ` ORDER BY d.create_time DESC` // 添加分页 if page_z != 9999 { dataSQL += " LIMIT ?, ?" params = append(params, offset, page_z) } // 执行数据查询 var maps []Device_R _, err = dao.orm.Raw(dataSQL, params...).QueryRows(&maps) if err != nil { logs.Error(lib.FuncName(), "Data query error:", err) return nil, 0 } // 批量获取设备历史记录,避免N+1查询 if len(maps) > 0 { // 批量获取历史记录 historyMap := dao.batchReadDeviceHistory(maps) // 填充历史记录 for i := range maps { if history, exists := historyMap[maps[i].T_sn]; exists { maps[i].T_remark = history.T_remark maps[i].T_project_log = history.T_project_log } } // 批量处理设备转换,避免N+1查询 r = DevicesToDevice_Rs(maps) } return r, count } func (dao *DeviceDaoImpl) Read_Device_List_upback(T_name, T_product_name, T_product_model string, T_State, page, page_z int) (r []Device_R, cnt int64) { var offset int if page <= 1 { offset = 0 } else { offset = (page - 1) * page_z } // 过滤 sqlWhere := " WHERE d.t__state > 0" if len(T_name) > 0 { sqlWhere += " AND (d.t_contract_number like \"%" + T_name + "%\" or d.t_out_number like \"%" + T_name + "%\" or d.t_sn like \"%" + T_name + "%\" or d.t_iccid like \"%" + T_name + "%\" or d.t_project like \"%" + T_name + "%\")" } if T_State > 0 { sqlWhere += fmt.Sprintf(" AND d.t__state = %d", T_State) } if len(T_product_name) > 0 { sqlWhere += " AND p.t_name like \"%" + T_product_name + "%\"" } if len(T_product_model) > 0 { sqlWhere += " AND p.t_model like \"%" + T_product_model + "%\"" } var maps_z []orm2.ParamsList // 获取总条数 sql := "SELECT COUNT(*) FROM device d JOIN product p ON d.t_product_id = p.ID JOIN (SELECT t_sn, MAX(create_time) AS max_create_time FROM device GROUP BY t_sn) latest " + "ON d.t_sn = latest.t_sn AND d.create_time = latest.max_create_time" sql = sql + sqlWhere fmt.Println(sql) _, err := dao.orm.Raw(sql).ValuesList(&maps_z) if err != nil { return r, 0 } if len(maps_z) == 0 { return r, 0 } sql = "SELECT *,d.t__state as t_device_state,p.t_name AS t_product_name,p.t_model AS t_product_model,p.t_class AS t_product_class,p.t_spec AS t_product_spec " + "FROM device d JOIN product p ON d.t_product_id = p.ID JOIN (SELECT t_sn, MAX(create_time) AS max_create_time FROM device GROUP BY t_sn) latest " + "ON d.t_sn = latest.t_sn AND d.create_time = latest.max_create_time" sql = sql + sqlWhere sql += " ORDER BY d.create_time DESC" if page_z != 9999 { sql += " LIMIT " + strconv.Itoa(offset) + "," + strconv.Itoa(page_z) } fmt.Println(sql) var maps []Device_R _, err = dao.orm.Raw(sql).QueryRows(&maps) if err != nil { return r, 0 } for _, v := range maps { v.T_remark, v.T_project_log = dao.Read_Device_History(v.T_sn) r = append(r, DeviceToDevice_R(v)) } count, _ := strconv.Atoi(maps_z[0][0].(string)) return r, int64(count) } /** * 批量获取设备历史记录,避免N+1查询问题 * @param devices 设备列表 * @return 设备SN到历史记录的映射 */ func (dao *DeviceDaoImpl) batchReadDeviceHistory(devices []Device_R) map[string]struct { T_remark string T_project_log string } { if len(devices) == 0 { return make(map[string]struct { T_remark string T_project_log string }) } // 收集所有设备SN snList := make([]string, 0, len(devices)) for _, device := range devices { snList = append(snList, device.T_sn) } // 构建IN子句 placeholders := make([]string, len(snList)) args := make([]interface{}, len(snList)) for i, sn := range snList { placeholders[i] = "?" args[i] = sn } // 批量查询所有设备的历史记录 sql := `SELECT t_sn, t__state, create_time, t_out_number, t_in_number, t_project FROM device WHERE t_sn IN (` + strings.Join(placeholders, ",") + `) ORDER BY t_sn, create_time` var historyRecords []struct { T_sn string T_State int CreateTime time.Time T_out_number string T_in_number string T_project string } _, err := dao.orm.Raw(sql, args...).QueryRows(&historyRecords) if err != nil { logs.Error(lib.FuncName(), err) return make(map[string]struct { T_remark string T_project_log string }) } // 按设备SN分组并构建历史记录 result := make(map[string]struct { T_remark string T_project_log string }) for _, record := range historyRecords { if _, exists := result[record.T_sn]; !exists { result[record.T_sn] = struct { T_remark string T_project_log string }{} } history := result[record.T_sn] if record.T_State == 1 { // 出库记录 history.T_remark += fmt.Sprintf("%s:%s(%s)|", record.CreateTime.Format("2006-01-02"), "出库", record.T_out_number) if len(record.T_project) > 0 { history.T_project_log += fmt.Sprintf("%s:%s(%s):%s|", record.CreateTime.Format("2006-01-02"), "出库", record.T_out_number, record.T_project) } } else if record.T_State == 2 { // 入库记录 history.T_remark += fmt.Sprintf("%s:%s(%s)|", record.CreateTime.Format("2006-01-02"), "入库", record.T_in_number) } result[record.T_sn] = history } return result } /** * 获取单个设备的历史记录(保留原方法以兼容其他调用) * @param T_sn 设备SN * @return 备注和项目日志 */ func (dao *DeviceDaoImpl) Read_Device_History(T_sn string) (T_remark string, T_project string) { qs := dao.orm.QueryTable(new(Device)) var maps []Device _, err := qs.Filter("T_sn", T_sn).OrderBy("CreateTime").All(&maps) if err != nil { logs.Error(lib.FuncName(), err) return } for _, v := range maps { if v.T_State == 1 { T_remark += fmt.Sprintf("%s:%s(%s)|", v.CreateTime.Format("2006-01-02"), "出库", v.T_out_number) if len(v.T_project) > 0 { T_project += fmt.Sprintf("%s:%s(%s):%s|", v.CreateTime.Format("2006-01-02"), "出库", v.T_out_number, v.T_project) } } if v.T_State == 2 { T_remark += fmt.Sprintf("%s:%s(%s)|", v.CreateTime.Format("2006-01-02"), "入库", v.T_in_number) } } return } func (dao *DeviceDaoImpl) Delete_Device_ByT_in_number(T_number, T_sn string) (err error) { qs := dao.orm.QueryTable(new(Device)) var device Device err = qs.Filter("T_sn", T_sn).Filter("T_in_number", T_number).One(&device) if err != nil { return } var count int64 count, err = qs.Filter("T_sn", T_sn).Filter("CreateTime__gt", device.CreateTime).Count() if err != nil { logs.Error(lib.FuncName(), err) return } if count > 1 { return errors.New("设备已出库,无法删除") } _, err = dao.orm.Delete(&device) if err != nil { logs.Error(lib.FuncName(), err) return } return err } func (dao *DeviceDaoImpl) Delete_Device_ByT_out_number(T_number, T_sn string) (err error) { qs := dao.orm.QueryTable(new(Device)) var device Device err = qs.Filter("T_sn", T_sn).Filter("T_out_number", T_number).One(&device) if err != nil { return } var count int64 count, err = qs.Filter("T_sn", T_sn).Filter("CreateTime__gt", device.CreateTime).Count() if err != nil { logs.Error(lib.FuncName(), err) return } if count > 1 { return errors.New("设备已入库,无法删除") } _, err = dao.orm.Delete(&device) if err != nil { logs.Error(lib.FuncName(), err) return } return err } func (dao *DeviceDaoImpl) Update_Device_ByT_out_number_T_project(T_number, T_project string) error { qs := dao.orm.QueryTable(new(Device)) var list []Device _, err := qs.Filter("T_out_number", T_number).All(&list) if err != nil { logs.Error(lib.FuncName(), err) return err } for _, deivce := range list { deivce.T_project = T_project _, err = dao.orm.Update(&deivce, "T_project") if err != nil { logs.Error(lib.FuncName(), err) return err } } return nil } func (dao *DeviceDaoImpl) Update_Device_CreateTimeByT_out_number(T_number, T_date string) error { now := time.Now().Format("15:04:05") date, _ := lib.TimeStrToTime(T_date + " " + now) qs := dao.orm.QueryTable(new(Device)) var list []Device _, err := qs.Filter("T_out_number", T_number).All(&list) if err != nil { logs.Error(lib.FuncName(), err) return err } for _, device := range list { //device.CreateTime = date //_, err = dao.orm.Update(&device, "CreateTime") _, err = dao.orm.Raw("UPDATE device SET create_time = ? WHERE id = ?", date, device.Id).Exec() if err != nil { logs.Error(lib.FuncName(), err) return err } } return nil } func (dao *DeviceDaoImpl) Update_Device_CreateTimeByT_in_number(T_number, T_date string) error { now := time.Now().Format("15:04:05") date, _ := lib.TimeStrToTime(T_date + " " + now) qs := dao.orm.QueryTable(new(Device)) var list []Device _, err := qs.Filter("T_in_number", T_number).All(&list) if err != nil { logs.Error(lib.FuncName(), err) return err } for _, device := range list { //device.CreateTime = date //_, err = dao.orm.Update(&device, "CreateTime") _, err = dao.orm.Raw("UPDATE device SET create_time = ? WHERE id = ?", date, device.Id).Exec() if err != nil { logs.Error(lib.FuncName(), err) return err } } return nil } /** * 根据IMEI查询上次入库的批次号 * @param imei 设备IMEI * @return 批次号,如果未找到则返回空字符串 */ func (dao *DeviceDaoImpl) Read_Device_BatchNumber_ByImei(imei string) (string, error) { if len(imei) == 0 { return "", nil } // 直接查询device表,获取该IMEI对应的最新入库记录的批次号 // T_State = 2 是入库状态,入库时已将t_batch_number保存到device表 sql := `SELECT t_batch_number FROM device WHERE t_imei = ? AND t__state = 2 ORDER BY create_time DESC LIMIT 1` var batchNumber string err := dao.orm.Raw(sql, imei).QueryRow(&batchNumber) if err != nil { if err.Error() == orm.ErrNoRows.Error() { // 未找到记录,返回空字符串 return "", nil } logs.Error(lib.FuncName(), err) return "", err } return batchNumber, nil } /** * 根据入库单号批量更新所有相关设备的批次号 * 先查询出所有t_in_number对应的设备,然后根据T_imei修改,不属于该入库编号的也要同步修改 * @param inNumber 入库单号 * @param batchNumber 批次号 * @return 错误信息 */ func (dao *DeviceDaoImpl) Update_Device_BatchNumber_ByInNumber(inNumber, batchNumber string) error { if len(inNumber) == 0 || len(batchNumber) == 0 { return nil } // 1. 先查询出所有该入库单号下的设备IMEI列表 sql := `SELECT DISTINCT t_imei FROM device WHERE t_in_number = ? AND t__state = 2 AND t_imei IS NOT NULL AND t_imei != ''` var imeiList []string _, err := dao.orm.Raw(sql, inNumber).QueryRows(&imeiList) if err != nil { logs.Error(lib.FuncName(), err) return err } if len(imeiList) == 0 { // 没有找到相关设备,直接返回 return nil } // 2. 根据IMEI列表批量更新所有相关设备的批次号(包括不属于当前入库编号的设备) // 构建IN子句 placeholders := make([]string, len(imeiList)) args := make([]interface{}, len(imeiList)+1) args[0] = batchNumber for i, imei := range imeiList { placeholders[i] = "?" args[i+1] = imei } updateSQL := `UPDATE device SET t_batch_number = ? WHERE t_imei IN (` + strings.Join(placeholders, ",") + `)` _, err = dao.orm.Raw(updateSQL, args...).Exec() if err != nil { logs.Error(lib.FuncName(), err) return err } return nil }