Files
yunzerwebsiteallinone/go/services/gueststore/gueststore.go
T
2026-11-03 11:13:55 +08:00

182 lines
5.0 KiB
Go

// Package gueststore 游客(免登录模块)通用存储。
//
// 一条业务条目 = yz_guest_data 一行 JSON 文档,按 guest_id + module 隔离;
// 删除只做软删(is_deleted=1)永久保留,合并进正式账号后置 is_deleted=2。
// 业务控制器通过 identity.IsGuest 分流到这里,响应结构与登录态接口保持一致。
package gueststore
import (
"encoding/json"
"errors"
"strings"
"time"
"server/models"
"github.com/beego/beego/v2/client/orm"
)
// ErrNotFound 条目不存在(或不属于该游客 / 已被删除)。
var ErrNotFound = errors.New("记录不存在")
// Row 一条游客数据文档(对外统一用表 ID 作为条目 ID,payload 内的 id 仅冗余)。
type Row struct {
ID uint64
Payload json.RawMessage
CreateTime time.Time
UpdateTime *time.Time
}
// Upsert 按手机号定位/创建游客身份,并刷新 openid / 活跃时间。
// 已合并(merged_uid>0)的旧记录会被复位为新的游客周期:
// 历史数据行在合并时已置 is_deleted=2,不会再次出现在列表里。
func Upsert(phone, openid, unionid string) (*models.GuestUser, error) {
phone = strings.TrimSpace(phone)
if phone == "" {
return nil, errors.New("手机号不能为空")
}
now := time.Now()
var row models.GuestUser
err := models.Orm.QueryTable(new(models.GuestUser)).Filter("phone", phone).One(&row)
if err == orm.ErrNoRows {
row = models.GuestUser{Phone: phone, OpenID: openid, UnionID: unionid, LastActiveTime: &now}
id, ierr := models.Orm.Insert(&row)
if ierr != nil {
return nil, ierr
}
row.ID = uint64(id)
return &row, nil
}
if err != nil {
return nil, err
}
upd := map[string]interface{}{"last_active_time": now}
if openid != "" {
upd["openid"] = openid
}
if unionid != "" {
upd["unionid"] = unionid
}
if row.MergedUID > 0 {
// 复位游客周期,允许同一手机号再次以游客身份产生新数据
upd["merged_uid"] = uint64(0)
upd["merged_time"] = nil
row.MergedUID = 0
}
if _, err = models.Orm.QueryTable(new(models.GuestUser)).Filter("id", row.ID).Update(upd); err != nil {
return nil, err
}
row.LastActiveTime = &now
return &row, nil
}
// Insert 写入一条业务条目,返回行 ID(即条目 ID)。
func Insert(guestID uint64, module string, payload interface{}) (uint64, error) {
b, err := json.Marshal(payload)
if err != nil {
return 0, err
}
now := time.Now()
id, err := models.Orm.Insert(&models.GuestData{
GuestID: guestID,
Module: module,
Payload: string(b),
CreateTime: now,
UpdateTime: &now,
})
if err != nil {
return 0, err
}
return uint64(id), nil
}
// Get 读取单条(限本游客 + 本模块 + 未删除)。
func Get(guestID uint64, module string, id uint64) (Row, error) {
var d models.GuestData
err := models.Orm.QueryTable(new(models.GuestData)).
Filter("id", id).
Filter("guest_id", guestID).
Filter("module", module).
Filter("is_deleted", 0).
One(&d)
if err == orm.ErrNoRows {
return Row{}, ErrNotFound
}
if err != nil {
return Row{}, err
}
return toRow(d), nil
}
// List 列出某游客某模块的全部有效条目(新→旧)。
func List(guestID uint64, module string) ([]Row, error) {
var rows []models.GuestData
_, err := models.Orm.QueryTable(new(models.GuestData)).
Filter("guest_id", guestID).
Filter("module", module).
Filter("is_deleted", 0).
OrderBy("-id").
All(&rows)
if err != nil && err != orm.ErrNoRows {
return nil, err
}
out := make([]Row, 0, len(rows))
for _, d := range rows {
out = append(out, toRow(d))
}
return out, nil
}
// Update 覆盖一条条目的 payload。
func Update(guestID uint64, module string, id uint64, payload interface{}) error {
b, err := json.Marshal(payload)
if err != nil {
return err
}
n, err := models.Orm.QueryTable(new(models.GuestData)).
Filter("id", id).
Filter("guest_id", guestID).
Filter("module", module).
Filter("is_deleted", 0).
Update(map[string]interface{}{"payload": string(b), "update_time": time.Now()})
if err != nil {
return err
}
if n == 0 {
return ErrNotFound
}
return nil
}
// Delete 软删除一条条目(保留数据行,永久不清理)。
func Delete(guestID uint64, module string, id uint64) error {
n, err := models.Orm.QueryTable(new(models.GuestData)).
Filter("id", id).
Filter("guest_id", guestID).
Filter("module", module).
Filter("is_deleted", 0).
Update(map[string]interface{}{"is_deleted": 1, "update_time": time.Now()})
if err != nil {
return err
}
if n == 0 {
return ErrNotFound
}
return nil
}
// MarkMerged 游客转正后:该游客全部有效数据行置 is_deleted=2(保留行本身)。
func MarkMerged(guestID uint64) error {
_, err := models.Orm.QueryTable(new(models.GuestData)).
Filter("guest_id", guestID).
Filter("is_deleted", 0).
Update(map[string]interface{}{"is_deleted": 2, "update_time": time.Now()})
return err
}
func toRow(d models.GuestData) Row {
return Row{ID: d.ID, Payload: json.RawMessage(d.Payload), CreateTime: d.CreateTime, UpdateTime: d.UpdateTime}
}