You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
10 KiB
301 lines
10 KiB
package user
|
|
|
|
import (
|
|
"context"
|
|
"yunyan/comm"
|
|
"yunyan/lego/core"
|
|
"yunyan/lego/core/cbase"
|
|
"yunyan/lego/sys/mysql"
|
|
"yunyan/lego/sys/postgres"
|
|
redissys "yunyan/lego/sys/redis"
|
|
"yunyan/pb"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// 代理模型
|
|
type modelUserComp struct {
|
|
cbase.ModuleCompBase
|
|
module *User
|
|
}
|
|
|
|
// 组件初始化接口
|
|
func (this *modelUserComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) {
|
|
this.ModuleCompBase.Init(service, module, comp, opt)
|
|
this.module = module.(*User)
|
|
if err = mysql.CreateTable(comm.TableUser, &pb.DBUser{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableUserdevice, &pb.DBUserDivice{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableUserStatistics, &pb.DBUserStatistics{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableUserUseLog, &pb.DBUserUseLog{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableAppStatDaily, &pb.DBAppStat{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableAppStatMonthly, &pb.DBAppStat{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
if err = mysql.CreateTable(comm.TableAppStatYearly, &pb.DBAppStat{}); err != nil {
|
|
this.module.Errorln(err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) findforid(uid string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "uid=?", uid)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findformac(mac string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "phonemac=?", mac)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findforphone(phone string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "phone=?", phone)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findformail(mail string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "mail=?", mail)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findforwx(openid string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "wxopenid=?", openid)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findforgoogle(openid string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "googleopenid=?", openid)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findforapple(openid string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "appleopenid=?", openid)
|
|
return
|
|
}
|
|
func (this *modelUserComp) findforfacebook(openid string) (user *pb.DBUser, err error) {
|
|
user = &pb.DBUser{}
|
|
err = mysql.FindOne(comm.TableUser, user, "facebookopenid=?", openid)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) add(user *pb.DBUser) (err error) {
|
|
err = mysql.Insert(comm.TableUser, user)
|
|
return
|
|
}
|
|
func (this *modelUserComp) save(user *pb.DBUser) (err error) {
|
|
err = mysql.Save(comm.TableUser, user)
|
|
return
|
|
}
|
|
|
|
// 软注销:清理用户业务数据 + 个人痕迹,保留身份锚点与 Isgiveaway
|
|
// 保留 user 行作为防刷锚点,下次同一身份再登录时可复活
|
|
func (this *modelUserComp) cancel(user *pb.DBUser) (err error) {
|
|
uid := user.Uid
|
|
// 1) 删除用户模块自己管的三张表(按 uid)
|
|
if err = mysql.Delete(comm.TableUserdevice, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
if err = mysql.Delete(comm.TableUserStatistics, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
if err = mysql.Delete(comm.TableUserUseLog, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
// 2) 跨模块:allhelp_task 表、chat_summary 表、echomeet_record 表(同一 ServiceDB)
|
|
if err = mysql.Delete(comm.TableAllhelpTask, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
if err = mysql.Delete(comm.TableChatSummary, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
if err = mysql.Delete(comm.TableEchomeetRecord, "uid=?", uid); err != nil {
|
|
return
|
|
}
|
|
// 3) 清理 DBUser 上的业务字段与 PII,保留身份锚点、Createtime、Isgiveaway
|
|
user.Name = ""
|
|
user.Avatar = ""
|
|
user.Phonemodel = ""
|
|
user.Phonemac = ""
|
|
user.Viplv = 0
|
|
user.Vipexptime = 0
|
|
user.Aichatintegral = 0
|
|
user.Aichattotalintegral = 0
|
|
user.Tradeintegral = 0
|
|
user.Tradetotalintegral = 0
|
|
user.Meetintegral = 0
|
|
user.Meettotalintegral = 0
|
|
user.State = pb.UserState_Cancelled
|
|
user.Canceltime = time.Now().Unix()
|
|
// 保留:Uid / Phone / Mail / Wxopenid / Googleopenid / Facebookopenid / Appleopenid
|
|
// Createtime / Isgiveaway / Isactivatecode / Language / Channel / Version / Lastsgintime
|
|
err = mysql.Save(comm.TableUser, user)
|
|
return
|
|
}
|
|
|
|
// 添加设备
|
|
func (this *modelUserComp) addDevice(divice *pb.DBUserDivice) (err error) {
|
|
err = mysql.Insert(comm.TableUserdevice, divice)
|
|
return
|
|
}
|
|
|
|
// 获取用户设备列表
|
|
func (this *modelUserComp) getDevices(uid string) (divices []*pb.DBUserDivice, err error) {
|
|
divices = make([]*pb.DBUserDivice, 0)
|
|
err = mysql.Find(comm.TableUserdevice, &divices, "uid=?", uid)
|
|
return
|
|
}
|
|
|
|
// 获取用户设备列表
|
|
func (this *modelUserComp) getDevice(id uint64) (divice *pb.DBUserDivice, err error) {
|
|
divice = &pb.DBUserDivice{}
|
|
err = mysql.FindOne(comm.TableUserdevice, divice, "id=?", id)
|
|
return
|
|
}
|
|
|
|
// 保存用户设备
|
|
func (this *modelUserComp) saveDevice(divice *pb.DBUserDivice) (err error) {
|
|
err = mysql.Save(comm.TableUserdevice, divice)
|
|
return
|
|
}
|
|
|
|
// 获取用户设备列表
|
|
func (this *modelUserComp) delDevice(id uint64) (err error) {
|
|
err = mysql.Delete(comm.TableUserdevice, "id=?", id)
|
|
return
|
|
}
|
|
|
|
// -------------------
|
|
// 合作商绑定
|
|
// getFactoryDevice / getFactoryDeviceformac 是绑定/解绑路径上的"读后写同一行"校验读,
|
|
// 用 FindOnePrimary 强制走主库,避免读到只读副本复制延迟内的旧状态造成重复绑定竞态。
|
|
func (this *modelUserComp) getFactoryDevice(pid uint32, code string) (model *pb.DBFactoryDevics, err error) {
|
|
model = &pb.DBFactoryDevics{}
|
|
err = postgres.FindOnePrimary(fmt.Sprintf("%s_%x", comm.TableLicense, pid), model, "code=?", code)
|
|
return
|
|
}
|
|
func (this *modelUserComp) getFactoryDeviceformac(pid uint32, devicemac string) (model *pb.DBFactoryDevics, err error) {
|
|
model = &pb.DBFactoryDevics{}
|
|
err = postgres.FindOnePrimary(fmt.Sprintf("%s_%x", comm.TableLicense, pid), model, "productid=? and devicemac=?", pid, devicemac)
|
|
return
|
|
}
|
|
func (this *modelUserComp) saveFactoryDevice(pid uint32, code *pb.DBFactoryDevics) (err error) {
|
|
err = postgres.Save(fmt.Sprintf("%s_%x", comm.TableLicense, pid), code)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) saveCode(addr string, code string) (err error) {
|
|
err = redissys.Conn().Set(context.Background(), redissys.RKey(fmt.Sprintf("%s:%s", comm.Redis_Verification, addr)), code, time.Minute*2).Err()
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getCode(addr string) (code string, err error) {
|
|
code, err = redissys.Conn().Get(context.Background(), redissys.RKey(fmt.Sprintf("%s:%s", comm.Redis_Verification, addr))).Result()
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getProduct(id int32) (model *pb.DBProduct, err error) {
|
|
model = &pb.DBProduct{}
|
|
err = postgres.FindOne(comm.TableProduct, model, "id=?", id)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getProducts() (models []*pb.DBProduct, err error) {
|
|
models = make([]*pb.DBProduct, 0)
|
|
err = postgres.Find(comm.TableProduct, &models, "")
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getProductForName(devicename string) (model *pb.DBProduct, err error) {
|
|
model = &pb.DBProduct{}
|
|
err = postgres.FindOne(comm.TableProduct, model, "devicename=?", devicename)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getProductVersions(pid uint32) (models []*pb.DBProductVersion, err error) {
|
|
models = make([]*pb.DBProductVersion, 0)
|
|
err = postgres.Find(comm.TableProductVersion, &models, "productid=?", pid)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) getChannelApp(channel int32) (model *pb.DBChannelApp, err error) {
|
|
model = &pb.DBChannelApp{}
|
|
err = mysql.FindOne(comm.TableChannelApp, model, "channel=?", channel)
|
|
return
|
|
}
|
|
func (this *modelUserComp) getChannelApps() (models []*pb.DBChannelApp, err error) {
|
|
models = make([]*pb.DBChannelApp, 0)
|
|
err = mysql.Find(comm.TableChannelApp, &models, "")
|
|
return
|
|
}
|
|
func (this *modelUserComp) getStatistics(uid string) (statistics *pb.DBUserStatistics, err error) {
|
|
statistics = &pb.DBUserStatistics{Uid: uid}
|
|
mysql.FindOne(comm.TableUserStatistics, statistics, "uid=?", uid)
|
|
return
|
|
}
|
|
|
|
func (this *modelUserComp) updateStatistics(statistics *pb.DBUserStatistics) (err error) {
|
|
err = mysql.Table(comm.TableUserStatistics).
|
|
Clauses(clause.OnConflict{UpdateAll: true}).
|
|
Create(statistics).Error
|
|
return
|
|
}
|
|
|
|
// 添加积分日志
|
|
func (this *modelUserComp) addIntegralLog(log *pb.DBUserUseLog) (err error) {
|
|
err = mysql.Insert(comm.TableUserUseLog, log)
|
|
return
|
|
}
|
|
|
|
// -------------------
|
|
// 厂家公码(公码定义在 AdminDB;防重领靠 DBUser.Isgiveaway,审计靠 DBUserUseLog)
|
|
func (this *modelUserComp) getFactoryPublicCode(code string) (model *pb.DBFactoryPublicCode, err error) {
|
|
model = &pb.DBFactoryPublicCode{}
|
|
err = postgres.FindOne(comm.TableFactoryPublicCode, model, "code=?", code)
|
|
return
|
|
}
|
|
|
|
// 原子自增 useduses 并防超发:仅当 maxuses=0 或 useduses<maxuses 时才允许 +1
|
|
// 返回 affected:0 表示已达上限或公码不存在,1 表示扣减成功
|
|
func (this *modelUserComp) incrFactoryPublicCodeUsed(code string) (affected int64, err error) {
|
|
tx := postgres.Table(comm.TableFactoryPublicCode).
|
|
Where("code=? AND (maxuses=0 OR useduses<maxuses)", code).
|
|
UpdateColumn("useduses", gorm.Expr("useduses+1"))
|
|
affected = tx.RowsAffected
|
|
err = tx.Error
|
|
return
|
|
}
|
|
|
|
// 原子置位 user.isactivatecode:仅当当前为 false 时才允许置 true,
|
|
// 用于公码扫码的并发兜底(同账号同时发起多次扫码请求时只过一个)
|
|
func (this *modelUserComp) tryMarkActivateCode(uid string) (affected int64, err error) {
|
|
tx := mysql.Table(comm.TableUser).
|
|
Where("uid=? AND isactivatecode=?", uid, false).
|
|
UpdateColumn("isactivatecode", true)
|
|
affected = tx.RowsAffected
|
|
err = tx.Error
|
|
return
|
|
}
|
|
|
|
// 回滚 user.isactivatecode:在公码核销失败(如撞上限)时调用,仅当前为 true 才能改回 false
|
|
func (this *modelUserComp) rollbackActivateCode(uid string) (affected int64, err error) {
|
|
tx := mysql.Table(comm.TableUser).
|
|
Where("uid=? AND isactivatecode=?", uid, true).
|
|
UpdateColumn("isactivatecode", false)
|
|
affected = tx.RowsAffected
|
|
err = tx.Error
|
|
return
|
|
}
|
|
|