Files
app-api/internal/repository/ota.go
T
eafonyang 0aad0f9c1d fix(repository): 优化 OTA 查询排序规则,确保取最新录入记录
- 将 GetLatestOTA 查询结果排序规则由单一 verCode 降序调整为 verCode 降序、id 降序
- 同步更新 GetLatestOTANotInBlackList 查询排序规则,保持一致性
- 同步更新 GetLatestOTANotTarget 查询排序规则,保持一致性
- 在 GetVerNameByVersion 方法中,增加按 id 降序排序以获取最后录入的版本名称
- 添加注释说明排序规则调整及其目的
2026-09-03 17:57:28 +08:00

178 lines
5.4 KiB
Go

package repository
import (
"context"
"database/sql"
"fmt"
"github.com/luxsin/app-api/internal/model"
)
type OTARepository struct {
db *sql.DB
}
func NewOTARepository(db *sql.DB) *OTARepository {
return &OTARepository{db: db}
}
// GetLatestOTA 按 model+hw+beta+status=1 查询最新一条 OTA 记录
// 排序规则:verCode 降序;verCode 相同时取 id 较大的(即最后录入的)那条
func (r *OTARepository) GetLatestOTA(ctx context.Context, modelVal string, hw, beta int) (*model.OTA, error) {
const query = `SELECT id, verCode, verName, url, md5, ` + "`force`" + `, ` + "`desc`" + `, model, hw, target, beta,
startTime, endTime, status
FROM ota WHERE model = ? AND hw = ? AND beta = ? AND status = 1
ORDER BY verCode DESC, id DESC LIMIT 1`
var o model.OTA
var desc, mdl sql.NullString
var startTime, endTime sql.NullTime
err := r.db.QueryRowContext(ctx, query, modelVal, hw, beta).Scan(
&o.ID, &o.VerCode, &o.VerName, &o.URL, &o.MD5, &o.Force, &desc, &mdl,
&o.HW, &o.Target, &o.Beta,
&startTime, &endTime, &o.Status,
)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("query ota: %w", err)
}
if desc.Valid {
o.Desc = &desc.String
}
if mdl.Valid {
o.Model = &mdl.String
}
if startTime.Valid {
o.StartTime = &startTime.Time
}
if endTime.Valid {
o.EndTime = &endTime.Time
}
return &o, nil
}
// GetLatestOTANotInBlackList 查询最新一条不在黑名单中的 OTA 记录
// 排序规则与 GetLatestOTA 一致:verCode 降序,verCode 相同时取 id 较大的那条
func (r *OTARepository) GetLatestOTANotInBlackList(ctx context.Context, modelVal string, hw, beta int, mac string) (*model.OTA, error) {
const query = `SELECT id, verCode, verName, url, md5, ` + "`force`" + `, ` + "`desc`" + `, model, hw, target, beta,
startTime, endTime, status
FROM ota WHERE status = 1 AND model = ? AND hw = ? AND beta = ?
AND id NOT IN (SELECT ota_id FROM black_list WHERE mac = ?)
ORDER BY verCode DESC, id DESC LIMIT 1`
var o model.OTA
var desc, mdl sql.NullString
var startTime, endTime sql.NullTime
err := r.db.QueryRowContext(ctx, query, modelVal, hw, beta, mac).Scan(
&o.ID, &o.VerCode, &o.VerName, &o.URL, &o.MD5, &o.Force, &desc, &mdl,
&o.HW, &o.Target, &o.Beta,
&startTime, &endTime, &o.Status,
)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("query ota not in blacklist: %w", err)
}
if desc.Valid {
o.Desc = &desc.String
}
if mdl.Valid {
o.Model = &mdl.String
}
if startTime.Valid {
o.StartTime = &startTime.Time
}
if endTime.Valid {
o.EndTime = &endTime.Time
}
return &o, nil
}
// GetLatestOTANotTarget 查询最新一条非定向的 OTA 记录
// 排序规则与 GetLatestOTA 一致:verCode 降序,verCode 相同时取 id 较大的那条
func (r *OTARepository) GetLatestOTANotTarget(ctx context.Context, modelVal string, hw, beta int) (*model.OTA, error) {
const query = `SELECT id, verCode, verName, url, md5, ` + "`force`" + `, ` + "`desc`" + `, model, hw, target, beta,
startTime, endTime, status
FROM ota WHERE status = 1 AND model = ? AND hw = ? AND beta = ? AND target = 0
ORDER BY verCode DESC, id DESC LIMIT 1`
var o model.OTA
var desc, mdl sql.NullString
var startTime, endTime sql.NullTime
err := r.db.QueryRowContext(ctx, query, modelVal, hw, beta).Scan(
&o.ID, &o.VerCode, &o.VerName, &o.URL, &o.MD5, &o.Force, &desc, &mdl,
&o.HW, &o.Target, &o.Beta,
&startTime, &endTime, &o.Status,
)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("query ota not target: %w", err)
}
if desc.Valid {
o.Desc = &desc.String
}
if mdl.Valid {
o.Model = &mdl.String
}
if startTime.Valid {
o.StartTime = &startTime.Time
}
if endTime.Valid {
o.EndTime = &endTime.Time
}
return &o, nil
}
// GetVerNameByVersion 按 model+verCode 查询版本名称
// 同一 model+verCode 存在多条记录时,取 id 较大的(即最后录入的)那条
func (r *OTARepository) GetVerNameByVersion(ctx context.Context, modelVal string, version int) (string, bool, error) {
const query = `SELECT verName FROM ota WHERE model = ? AND verCode = ? ORDER BY id DESC LIMIT 1`
var verName string
err := r.db.QueryRowContext(ctx, query, modelVal, version).Scan(&verName)
if err == sql.ErrNoRows {
return "", false, nil
}
if err != nil {
return "", false, fmt.Errorf("query ota verName: %w", err)
}
return verName, true, nil
}
// IsInBlackList 检查 mac 是否在指定 ota_id 的黑名单中
func (r *OTARepository) IsInBlackList(ctx context.Context, otaID int, mac string) (bool, error) {
const query = `SELECT COUNT(*) FROM black_list WHERE ota_id = ? AND mac = ?`
var count int
if err := r.db.QueryRowContext(ctx, query, otaID, mac).Scan(&count); err != nil {
return false, fmt.Errorf("query black_list: %w", err)
}
return count > 0, nil
}
// FindTargetDevice 检查 mac 是否在指定 ota_id 的定向设备中
func (r *OTARepository) FindTargetDevice(ctx context.Context, otaID int, mac string) (*model.OTATargetDevice, error) {
const query = `SELECT id, ota_id, mac_addr, create_at FROM ota_target_device WHERE ota_id = ? AND mac_addr = ?`
var d model.OTATargetDevice
err := r.db.QueryRowContext(ctx, query, otaID, mac).Scan(&d.ID, &d.OTAID, &d.MacAddr, &d.CreateAt)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("query ota_target_device: %w", err)
}
return &d, nil
}