- 标注:AI 预标注直写 labels_json(去候选确认两阶段);重叠去重(minIoU);全量标注按钮 - 训练:脚本迁移入 server/training/(Go 化 prepare_yolo/analyze_rfdetr,保留 train_server.py);tflite 产物自检并入训练流程(check_tflite) - 数据目录/权重不进 git;.gitignore 迁移至仓库根
146 lines
5.0 KiB
Go
146 lines
5.0 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"
|
|
)
|
|
|
|
// License 账号授权表 DAO:一手机号一记录(账号+授权合一);查询走缓存,写后必须清缓存。
|
|
type licenseDao struct{}
|
|
|
|
var License = &licenseDao{}
|
|
|
|
func init() {
|
|
ctx := context.Background()
|
|
common.DropLegacyTableIfHasColumn(ctx, consts.TableLicense, "device_id")
|
|
_, err := g.DB().Exec(ctx, `CREATE TABLE IF NOT EXISTS license (
|
|
phone_num TEXT PRIMARY KEY,
|
|
password TEXT NOT NULL,
|
|
expires_at TEXT,
|
|
remark TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)`)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
common.EnsureColumn(ctx, consts.TableLicense, "remark", "remark TEXT")
|
|
}
|
|
|
|
// GetByPhone 按手机号查询(走缓存,键含手机号)
|
|
func (d *licenseDao) GetByPhone(ctx context.Context, phone string) (*entity.License, error) {
|
|
return d.getByPhone(ctx, nil, phone, true)
|
|
}
|
|
|
|
// GetByPhoneNoCache 按手机号查询(绕过缓存:登录校验密码必须即时读库)
|
|
func (d *licenseDao) GetByPhoneNoCache(ctx context.Context, phone string) (*entity.License, error) {
|
|
return d.getByPhone(ctx, nil, phone, false)
|
|
}
|
|
|
|
// GetByPhoneInTx 事务内按手机号查询:读改写链路内必须用此方法(绕过缓存,
|
|
// 防止并发写后缓存回填旧值导致续费叠加错乱)
|
|
func (d *licenseDao) GetByPhoneInTx(ctx context.Context, tx gdb.TX, phone string) (*entity.License, error) {
|
|
return d.getByPhone(ctx, tx, phone, false)
|
|
}
|
|
|
|
func (d *licenseDao) getByPhone(ctx context.Context, tx gdb.TX, phone string, cache bool) (*entity.License, error) {
|
|
m := g.DB().Model(consts.TableLicense).Ctx(ctx)
|
|
if tx != nil {
|
|
m = m.TX(tx)
|
|
} else if cache {
|
|
m = m.Cache(common.CacheOption(ctx, "license:"+phone))
|
|
}
|
|
var e entity.License
|
|
if err := m.Where("phone_num", phone).Scan(&e); err != nil {
|
|
if common.IsNoRows(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
return &e, nil
|
|
}
|
|
|
|
// Register 注册账号:插入 license 行(无授权);冲突由 service 先查后插保证。
|
|
func (d *licenseDao) Register(ctx context.Context, tx gdb.TX, phone, passwordHash string) error {
|
|
now := gtime.Now()
|
|
_, err := g.DB().Model(consts.TableLicense).Ctx(ctx).TX(tx).Insert(g.Map{
|
|
"phone_num": phone,
|
|
"password": passwordHash,
|
|
"expires_at": nil,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
})
|
|
return err
|
|
}
|
|
|
|
// SetPasswordInTx 补写密码(注册时已有运营发卡占位行:只补密码,不覆盖授权)
|
|
func (d *licenseDao) SetPasswordInTx(ctx context.Context, tx gdb.TX, phone, passwordHash string) error {
|
|
_, err := g.DB().Model(consts.TableLicense).Ctx(ctx).TX(tx).
|
|
Where("phone_num", phone).
|
|
Data(gdb.Map{"password": passwordHash, "updated_at": gtime.Now()}).
|
|
Update()
|
|
return err
|
|
}
|
|
|
|
// UpsertAuthInTx 落授权(支付回调/手动授权):INSERT 占位(password 空串,运营发卡场景)
|
|
// 或 ON CONFLICT 只更新授权字段——password/created_at 一律不动,防止覆盖已注册账号密码。
|
|
func (d *licenseDao) UpsertAuthInTx(ctx context.Context, tx gdb.TX, phone string, expiresAt any, updatedAt any) error {
|
|
_, err := tx.Exec(`INSERT INTO license (phone_num, password, expires_at, created_at, updated_at)
|
|
VALUES (?, '', ?, ?, ?)
|
|
ON CONFLICT(phone_num) DO UPDATE SET
|
|
expires_at = excluded.expires_at,
|
|
updated_at = excluded.updated_at`,
|
|
phone, expiresAt, updatedAt, updatedAt)
|
|
return err
|
|
}
|
|
|
|
// ClearAuthInTx 事务内撤销授权(管理端 revoke):清空授权字段、保留账号行(密码不动)
|
|
func (d *licenseDao) ClearAuthInTx(ctx context.Context, tx gdb.TX, phone string) error {
|
|
_, err := g.DB().Model(consts.TableLicense).Ctx(ctx).TX(tx).
|
|
Where("phone_num", phone).
|
|
Data(gdb.Map{"expires_at": nil, "updated_at": gtime.Now()}).
|
|
Update()
|
|
return err
|
|
}
|
|
|
|
// SetRemarkInTx 事务内写备注(管理端 remark,空串即清空):不覆盖密码/授权字段
|
|
func (d *licenseDao) SetRemarkInTx(ctx context.Context, tx gdb.TX, phone, remark string) error {
|
|
_, err := g.DB().Model(consts.TableLicense).Ctx(ctx).TX(tx).
|
|
Where("phone_num", phone).
|
|
Data(gdb.Map{"remark": remark, "updated_at": gtime.Now()}).
|
|
Update()
|
|
return err
|
|
}
|
|
|
|
// PageByPhone 管理端账号分页查询:phone 模糊筛选,按更新时间倒序;
|
|
// 列表不缓存(单点 GetByPhone 仍走缓存,写后清缓存不变)。
|
|
func (d *licenseDao) PageByPhone(ctx context.Context, phone string, page, size int) ([]*entity.License, int64, error) {
|
|
base := func() *gdb.Model {
|
|
m := g.DB().Model(consts.TableLicense).Ctx(ctx)
|
|
if phone != "" {
|
|
m = m.WhereLike("phone_num", "%"+phone+"%")
|
|
}
|
|
return m
|
|
}
|
|
total, err := base().Count()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var list []*entity.License
|
|
err = base().OrderDesc("updated_at").Limit((page-1)*size, size).Scan(&list)
|
|
if err != nil {
|
|
if common.IsNoRows(err) {
|
|
return []*entity.License{}, int64(total), nil
|
|
}
|
|
return nil, 0, err
|
|
}
|
|
return list, int64(total), nil
|
|
}
|