121 lines
4.6 KiB
Go
121 lines
4.6 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
|
|
"observer-server/biz/dao"
|
|
"observer-server/biz/model/dto"
|
|
"observer-server/common"
|
|
)
|
|
|
|
// licenseService 授权业务:查询(缓存)、管理端手动授权/撤销(单写者串行事务)
|
|
type licenseService struct{}
|
|
|
|
var License = &licenseService{}
|
|
|
|
// Get 查询授权:expires_at > now 视为 active(未充值 NULL 即未授权);
|
|
// 手机号由登录中间件从 token 解出;查询走缓存(落授权后已清缓存)。
|
|
func (s *licenseService) Get(ctx context.Context, req *dto.LicenseReq) (*dto.LicenseRes, error) {
|
|
lic, err := dao.License.GetByPhone(ctx, common.PhoneFromCtx(ctx))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if lic == nil || lic.ExpiresAt == nil || !lic.ExpiresAt.Time.After(time.Now()) {
|
|
return &dto.LicenseRes{Active: false}, nil
|
|
}
|
|
return &dto.LicenseRes{Active: true, ExpiresAt: lic.ExpiresAt}, nil
|
|
}
|
|
|
|
// CalcExpiresAt 计算自然日到期时间:base 所在日 0 点 + days 天(day 套餐即当天 24:00 失效)。
|
|
// 续费叠加由调用方传入 base = max(现有到期, now),本方法不感知现有授权。
|
|
func (s *licenseService) CalcExpiresAt(base time.Time, days int) *gtime.Time {
|
|
start := time.Date(base.Year(), base.Month(), base.Day(), 0, 0, 0, 0, base.Location())
|
|
return gtime.New(start.AddDate(0, 0, days))
|
|
}
|
|
|
|
// AdminListLicenses 管理端账号/授权分页列表(含未充值账号)
|
|
func (s *licenseService) AdminListLicenses(ctx context.Context, req *dto.AdminLicenseListReq) (*dto.AdminLicenseListRes, error) {
|
|
page, size := common.NormalizePage(req.Page, req.Size)
|
|
list, total, err := dao.License.PageByPhone(ctx, req.PhoneNum, page, size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]*dto.AdminLicenseItem, 0, len(list))
|
|
for _, lic := range list {
|
|
items = append(items, &dto.AdminLicenseItem{
|
|
PhoneNum: lic.PhoneNum,
|
|
ExpiresAt: lic.ExpiresAt, Remark: lic.Remark,
|
|
CreatedAt: lic.CreatedAt, UpdatedAt: lic.UpdatedAt,
|
|
})
|
|
}
|
|
return &dto.AdminLicenseListRes{Total: total, List: items}, nil
|
|
}
|
|
|
|
// AdminGrant 手动授权(免费发卡):与支付回调 persistPaid 同语义——单写者串行事务内
|
|
// base = max(现有到期, now) 后按自然日叠加,已过期账号从当前时间起算;
|
|
// 未注册账号自动建占位行(无密码,用户注册时补密码);提交后清授权缓存。
|
|
func (s *licenseService) AdminGrant(ctx context.Context, req *dto.AdminGrantReq) (*dto.AdminGrantRes, error) {
|
|
plan, err := common.GetPlan(ctx, req.PlanId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if plan == nil {
|
|
return nil, gerror.NewCode(common.CodePlanNotConfigured, "套餐不存在")
|
|
}
|
|
var expiresAt *gtime.Time
|
|
err = common.Serial().Submit(ctx, func() error {
|
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
lic, err := dao.License.GetByPhoneInTx(ctx, tx, req.PhoneNum)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
base := time.Now()
|
|
if lic != nil && lic.ExpiresAt != nil && lic.ExpiresAt.Time.After(base) {
|
|
base = lic.ExpiresAt.Time
|
|
}
|
|
expiresAt = License.CalcExpiresAt(base, plan.Days)
|
|
return dao.License.UpsertAuthInTx(ctx, tx, req.PhoneNum, expiresAt, gtime.Now())
|
|
})
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
common.ClearCache(ctx, "license:"+req.PhoneNum)
|
|
return &dto.AdminGrantRes{ExpiresAt: expiresAt}, nil
|
|
}
|
|
|
|
// AdminRevoke 撤销授权:清空授权字段、保留账号行(密码不动),提交后清缓存。
|
|
func (s *licenseService) AdminRevoke(ctx context.Context, req *dto.AdminRevokeReq) (*dto.AdminRevokeRes, error) {
|
|
err := common.Serial().Submit(ctx, func() error {
|
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
return dao.License.ClearAuthInTx(ctx, tx, req.PhoneNum)
|
|
})
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
common.ClearCache(ctx, "license:"+req.PhoneNum)
|
|
return &dto.AdminRevokeRes{}, nil
|
|
}
|
|
|
|
// AdminRemark 写账号备注(管理端运营记录):空串即清空,不覆盖密码/授权;
|
|
// 单写者串行事务内更新,提交后清授权缓存(列表不缓存不受影响)。
|
|
func (s *licenseService) AdminRemark(ctx context.Context, req *dto.AdminRemarkReq) (*dto.AdminRemarkRes, error) {
|
|
err := common.Serial().Submit(ctx, func() error {
|
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
return dao.License.SetRemarkInTx(ctx, tx, req.PhoneNum, req.Remark)
|
|
})
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
common.ClearCache(ctx, "license:"+req.PhoneNum)
|
|
return &dto.AdminRemarkRes{}, nil
|
|
}
|