205 lines
6.4 KiB
Go
205 lines
6.4 KiB
Go
package controllers
|
|
|
|
// ==================== 游客数据转正合并 ====================
|
|
//
|
|
// 正式登录后按手机号把 yz_guest_data 里的条目迁入各模块业务表。
|
|
// 新免登录模块接入时:在 guestMergeAdapters 注册一个适配器即可,
|
|
// rows 为该模块全部有效游客条目,返回成功迁移的条数。
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"server/models"
|
|
"server/services/gueststore"
|
|
|
|
"github.com/beego/beego/v2/core/logs"
|
|
)
|
|
|
|
// 游客数据所属模块编码(与 gueststore 的 module 字段一致)
|
|
const (
|
|
guestModuleNotebook = "notebook"
|
|
guestModuleSchedule = "schedule"
|
|
guestModulePasswordStore = "passwordStore"
|
|
)
|
|
|
|
// guestNoteItem 游客记事本条目:与 /api/notebook 登录态响应字段一致,
|
|
// 前端拿到的形状不因游客/登录而变化。
|
|
type guestNoteItem struct {
|
|
ID uint64 `json:"id"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Pinned bool `json:"pinned"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// guestScheduleItem 游客日程条目:与 /api/schedule 登录态响应字段一致。
|
|
// 游客日程不接提醒发送链路(无短信/邮件推送),仅作待办记录。
|
|
type guestScheduleItem struct {
|
|
ID uint64 `json:"id"`
|
|
Content string `json:"content"`
|
|
ScheduleTime string `json:"schedule_time"`
|
|
RemindChannels []string `json:"remind_channels"`
|
|
AdvanceMinutes int `json:"advance_minutes"`
|
|
RepeatIntervalMinutes int `json:"repeat_interval_minutes"`
|
|
MaxSendCount int `json:"max_send_count"`
|
|
IsFinished bool `json:"is_finished"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
// mergeGuestToUser 把游客的全部模块数据并入正式身份(AuthIdentity.ID=uid, 租户 tid)。
|
|
// 单条失败只记日志不中断;完成后游客数据行统一置 is_deleted=2(保留行本身)。
|
|
func mergeGuestToUser(guest *models.GuestUser, uid uint64, tid int, username string) (map[string]int, error) {
|
|
counts := map[string]int{}
|
|
|
|
// ---- 记事本 ----
|
|
if rows, err := gueststore.List(guest.ID, guestModuleNotebook); err != nil {
|
|
logs.Error("[guest-merge] list notebook failed guest=%d: %v", guest.ID, err)
|
|
} else {
|
|
for _, r := range rows {
|
|
var item guestNoteItem
|
|
if json.Unmarshal(r.Payload, &item) != nil {
|
|
continue
|
|
}
|
|
userID, name := uid, username
|
|
pinned := int8(0)
|
|
if item.Pinned {
|
|
pinned = 1
|
|
}
|
|
if _, err := models.Orm.Insert(&models.BackendNotebook{
|
|
Tid: tid,
|
|
Title: item.Title,
|
|
Content: item.Content,
|
|
Pinned: pinned,
|
|
UserID: &userID,
|
|
UserName: &name,
|
|
}); err != nil {
|
|
logs.Error("[guest-merge] insert notebook row=%d: %v", r.ID, err)
|
|
continue
|
|
}
|
|
counts[guestModuleNotebook]++
|
|
}
|
|
}
|
|
|
|
// ---- 日程 ----
|
|
if rows, err := gueststore.List(guest.ID, guestModuleSchedule); err != nil {
|
|
logs.Error("[guest-merge] list schedule failed guest=%d: %v", guest.ID, err)
|
|
} else {
|
|
for _, r := range rows {
|
|
var item guestScheduleItem
|
|
if json.Unmarshal(r.Payload, &item) != nil {
|
|
continue
|
|
}
|
|
schedTime, terr := time.ParseInLocation("2006-01-02 15:04:05", item.ScheduleTime, time.Local)
|
|
if terr != nil {
|
|
logs.Warn("[guest-merge] schedule row=%d 时间非法,跳过", r.ID)
|
|
continue
|
|
}
|
|
title := firstLineOf(item.Content, "日程提醒")
|
|
schedule := models.BackendSchedule{
|
|
Tid: tid,
|
|
Title: title,
|
|
Content: item.Content,
|
|
ScheduleTime: schedTime,
|
|
UserID: uid,
|
|
}
|
|
schedID, ierr := models.Orm.Insert(&schedule)
|
|
if ierr != nil {
|
|
logs.Error("[guest-merge] insert schedule row=%d: %v", r.ID, ierr)
|
|
continue
|
|
}
|
|
// 提醒行按登录态创建逻辑重建;已完成的不再发送
|
|
status := int8(0)
|
|
if item.IsFinished {
|
|
status = 2
|
|
}
|
|
for _, ch := range normalizeRemindChannels(item.RemindChannels) {
|
|
reminder := models.BackendScheduleReminder{
|
|
Tid: tid,
|
|
ScheduleID: uint64(schedID),
|
|
RemindChannel: ch,
|
|
AdvanceMinutes: item.AdvanceMinutes,
|
|
NextRemindTime: schedTime.Add(-time.Duration(item.AdvanceMinutes) * time.Minute),
|
|
ReceiverUserID: uid,
|
|
RemindStatus: status,
|
|
RepeatIntervalMinutes: item.RepeatIntervalMinutes,
|
|
MaxSendCount: item.MaxSendCount,
|
|
CreateTime: time.Now(),
|
|
UpdateTime: time.Now(),
|
|
}
|
|
if reminder.MaxSendCount <= 0 {
|
|
reminder.MaxSendCount = 1
|
|
}
|
|
if _, rerr := models.Orm.Insert(&reminder); rerr != nil {
|
|
logs.Error("[guest-merge] insert reminder row=%d ch=%s: %v", r.ID, ch, rerr)
|
|
}
|
|
}
|
|
counts[guestModuleSchedule]++
|
|
}
|
|
}
|
|
|
|
// ---- 密码库 ----
|
|
if rows, err := gueststore.List(guest.ID, guestModulePasswordStore); err != nil {
|
|
logs.Error("[guest-merge] list passwordStore failed guest=%d: %v", guest.ID, err)
|
|
} else {
|
|
for _, r := range rows {
|
|
var item PasswordStoreItemDTO
|
|
if json.Unmarshal(r.Payload, &item) != nil {
|
|
continue
|
|
}
|
|
if item.Platform == "" {
|
|
continue
|
|
}
|
|
if _, err := models.Orm.Insert(&models.BackendPasswordStore{
|
|
Tid: tid,
|
|
UserID: uid,
|
|
Platform: item.Platform,
|
|
URL: item.URL,
|
|
Accounts: accountsToJSON(item.Accounts),
|
|
Remark: item.Remark,
|
|
}); err != nil {
|
|
logs.Error("[guest-merge] insert passwordStore row=%d: %v", r.ID, err)
|
|
continue
|
|
}
|
|
counts[guestModulePasswordStore]++
|
|
}
|
|
}
|
|
|
|
if err := gueststore.MarkMerged(guest.ID); err != nil {
|
|
return counts, err
|
|
}
|
|
return counts, nil
|
|
}
|
|
|
|
// firstLineOf 取首行作为标题(与登录态日程创建逻辑一致)。
|
|
func firstLineOf(content, fallback string) string {
|
|
title := strings.SplitN(content, "\n", 2)[0]
|
|
if len(title) > 60 {
|
|
title = title[:60]
|
|
}
|
|
if title == "" {
|
|
title = fallback
|
|
}
|
|
return title
|
|
}
|
|
|
|
// normalizeRemindChannels 与 AppReminderController.Create 相同的通道白名单:
|
|
// APP 归一为 SITE_MSG,非法值丢弃。
|
|
func normalizeRemindChannels(list []string) []string {
|
|
out := make([]string, 0, len(list))
|
|
for _, ch := range list {
|
|
ch = strings.ToUpper(strings.TrimSpace(ch))
|
|
if ch == "APP" {
|
|
ch = "SITE_MSG"
|
|
}
|
|
if ch != "SMS" && ch != "EMAIL" && ch != "BARK" && ch != "SITE_MSG" {
|
|
continue
|
|
}
|
|
out = append(out, ch)
|
|
}
|
|
return out
|
|
}
|