Files
observer/server/biz/dao/license.go
T
2026-08-25 16:01:07 +08:00

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
}