145 lines
5.5 KiB
Go
145 lines
5.5 KiB
Go
package dao
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
|
||
"observer-server/biz/consts"
|
||
"observer-server/biz/model/entity"
|
||
"observer-server/common"
|
||
)
|
||
|
||
// FalseTargetReport 假目标上报表 DAO(技术设计.md「假目标上报」)。
|
||
type falseTargetReportDao struct{}
|
||
|
||
var FalseTargetReport = &falseTargetReportDao{}
|
||
|
||
func init() {
|
||
ctx := context.Background()
|
||
_, err := g.DB().Exec(ctx, `CREATE TABLE IF NOT EXISTS false_target_report (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
phone_num TEXT NOT NULL,
|
||
file TEXT NOT NULL,
|
||
labels_json TEXT NOT NULL DEFAULT '',
|
||
species TEXT NOT NULL DEFAULT '',
|
||
status TEXT NOT NULL DEFAULT 'pending', -- pending/approved(拒绝即删记录,无 rejected 存量)
|
||
image_hash INTEGER NOT NULL DEFAULT 0, -- 整帧 dHash 64 位(上报查重;0=未算/存量回填前)
|
||
suspect_real INTEGER NOT NULL DEFAULT 0, -- RF-DETR 高分检出疑似真目标预判标记(0/1)
|
||
created_at TEXT NOT NULL,
|
||
reviewed_at TEXT
|
||
)`)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
// 存量库迁移:查重/预判两列(2026-09-09;新库建表已含列,幂等跳过)
|
||
common.EnsureColumn(ctx, consts.TableFalseTargetReport, "image_hash", "image_hash INTEGER NOT NULL DEFAULT 0")
|
||
common.EnsureColumn(ctx, consts.TableFalseTargetReport, "suspect_real", "suspect_real INTEGER NOT NULL DEFAULT 0")
|
||
}
|
||
|
||
// Insert 插入上报记录并返回自增 id
|
||
func (d *falseTargetReportDao) Insert(ctx context.Context, m *entity.FalseTargetReport) (int64, error) {
|
||
res, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).Data(g.Map{
|
||
"phone_num": m.PhoneNum,
|
||
"file": m.File,
|
||
"labels_json": m.LabelsJson,
|
||
"species": m.Species,
|
||
"status": m.Status,
|
||
"image_hash": m.ImageHash,
|
||
"created_at": m.CreatedAt,
|
||
}).Insert()
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
return res.LastInsertId()
|
||
}
|
||
|
||
// CountTodayByPhone 某用户当日(自然日)上报条数(日限额判定)
|
||
func (d *falseTargetReportDao) CountTodayByPhone(ctx context.Context, phone string, dayStart *gtime.Time) (int, error) {
|
||
return g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).
|
||
Where("phone_num", phone).
|
||
WhereGTE("created_at", dayStart).
|
||
Count()
|
||
}
|
||
|
||
// PageByFilter 管理端分页(phoneNum 精确过滤、status 过滤,按上报时间倒序)
|
||
func (d *falseTargetReportDao) PageByFilter(ctx context.Context, phone, status string, page, size int) ([]*entity.FalseTargetReport, int64, error) {
|
||
base := func() *gdb.Model {
|
||
m := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx)
|
||
if phone != "" {
|
||
m = m.Where("phone_num", phone)
|
||
}
|
||
if status != "" {
|
||
m = m.Where("status", status)
|
||
}
|
||
return m
|
||
}
|
||
total, err := base().Count()
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
var list []*entity.FalseTargetReport
|
||
err = base().OrderDesc("id").Limit((page-1)*size, size).Scan(&list)
|
||
return list, int64(total), err
|
||
}
|
||
|
||
// GetByIds 按 id 批量取(审核前校验)
|
||
func (d *falseTargetReportDao) GetByIds(ctx context.Context, ids []int64) ([]*entity.FalseTargetReport, error) {
|
||
var list []*entity.FalseTargetReport
|
||
err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).WhereIn("id", ids).Scan(&list)
|
||
return list, err
|
||
}
|
||
|
||
// SetStatus 审核通过落状态(仅 pending 可迁转,重复审核由调用方先查后写保证幂等)
|
||
func (d *falseTargetReportDao) SetStatus(ctx context.Context, id int64, status string, reviewedAt *gtime.Time) error {
|
||
_, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).Where("id", id).
|
||
Data(g.Map{"status": status, "reviewed_at": reviewedAt}).Update()
|
||
return err
|
||
}
|
||
|
||
// DeleteByIds 删除记录(拒绝即删:记录与文件同删,不留 rejected 存量)
|
||
func (d *falseTargetReportDao) DeleteByIds(ctx context.Context, ids []int64) error {
|
||
_, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).WhereIn("id", ids).Delete()
|
||
return err
|
||
}
|
||
|
||
// ListHashes 全表非零 dHash(上报查重比对集;0=未算/回填前不参与比对)。
|
||
// 条件必须是 != 0 而非 > 0:dHash 最高位为 1 时 int64 存为负数(SQLite INTEGER 有符号),
|
||
// 用 > 0 会把约一半的 hash 排除在比对集外,查重静默失效(2026-09-10 实测)
|
||
func (d *falseTargetReportDao) ListHashes(ctx context.Context) ([]int64, error) {
|
||
all, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).
|
||
Fields("image_hash").Where("image_hash != 0").Array("image_hash")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
out := make([]int64, 0, len(all))
|
||
for _, v := range all {
|
||
out = append(out, v.Int64())
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
// ListHashMissing 存量回填待办:image_hash=0 的记录
|
||
func (d *falseTargetReportDao) ListHashMissing(ctx context.Context) ([]*entity.FalseTargetReport, error) {
|
||
var list []*entity.FalseTargetReport
|
||
err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).
|
||
Where("image_hash", 0).OrderAsc("id").Scan(&list)
|
||
return list, err
|
||
}
|
||
|
||
// UpdateHash 回填整帧 dHash
|
||
func (d *falseTargetReportDao) UpdateHash(ctx context.Context, id int64, hash int64) error {
|
||
_, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).Where("id", id).
|
||
Data("image_hash", hash).Update()
|
||
return err
|
||
}
|
||
|
||
// SetSuspectReal 预判命中置疑似真目标标记(幂等,只置 1 不清 0)
|
||
func (d *falseTargetReportDao) SetSuspectReal(ctx context.Context, id int64) error {
|
||
_, err := g.DB().Model(consts.TableFalseTargetReport).Ctx(ctx).Where("id", id).
|
||
Data("suspect_real", 1).Update()
|
||
return err
|
||
}
|