Files
olons_admin/server/internal/logic/sys/member_level.go
T
2026-10-10 20:56:17 +08:00

311 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package sys
import (
"context"
"fmt"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/dict"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/hgorm/hook"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
)
type sSysMemberLevel struct{}
func NewSysMemberLevel() *sSysMemberLevel { return &sSysMemberLevel{} }
func init() {
service.RegisterSysMemberLevel(NewSysMemberLevel())
dict.RegisterFunc("memberLevelOption", "会员等级选项", service.SysMemberLevel().Option, true)
}
func (s *sSysMemberLevel) model(ctx context.Context) *gdb.Model {
return handler.Model(dao.AppMemberLevel.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysMemberLevel) versionModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.AppMemberLevelVersion.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysMemberLevel) List(ctx context.Context, in *sysin.MemberLevelListInp) (list []*sysin.MemberLevelListModel, total int, err error) {
lc, vc := dao.AppMemberLevel.Columns(), dao.AppMemberLevelVersion.Columns()
// 已发布等级展示当前生效版本;尚未发布时展示最新草稿,避免列表误显示为零值。
versionJoin := fmt.Sprintf(
"%s=COALESCE(NULLIF(%s,0),(SELECT lv.%s FROM %s lv WHERE lv.%s=%s ORDER BY lv.%s DESC,lv.%s DESC LIMIT 1))",
col("v", vc.Id), col("l", lc.CurrentVersionId), vc.Id, dao.AppMemberLevelVersion.Table(), vc.LevelId, col("l", lc.Id), vc.VersionNo, vc.Id,
)
mod := s.model(ctx).As("l").LeftJoin(dao.AppMemberLevelVersion.Table()+" v", versionJoin).Fields(col("l", lc.Id), col("l", lc.LevelCode), col("l", lc.Name), col("l", lc.IconUrl), col("l", lc.CurrentVersionId), col("l", lc.MemberCount), col("l", lc.Status), col("l", lc.Sort), col("l", lc.Remark), col("l", lc.CreatedAt), col("v", vc.VersionNo), col("v", vc.UpgradeAmount), col("v", vc.GiftAmount), col("v", vc.DailyOrderLimit), col("v", vc.CommissionRate), col("v", vc.DailyWithdrawLimit), col("v", vc.PublishStatus), col("v", vc.EffectiveAt))
if in.Name != "" {
mod = mod.WhereLike(col("l", lc.Name), "%"+in.Name+"%")
}
if in.Status > 0 {
mod = mod.Where(col("l", lc.Status), in.Status)
}
if in.PublishStatus > 0 {
mod = mod.Where(col("v", vc.PublishStatus), in.PublishStatus)
}
err = mod.Page(in.Page, in.PerPage).OrderAsc(col("l", lc.Sort)).OrderDesc(col("l", lc.Id)).ScanAndCount(&list, &total, false)
if err != nil {
err = gerror.Wrap(err, "获取会员等级列表失败")
}
return
}
func (s *sSysMemberLevel) View(ctx context.Context, in *sysin.MemberLevelViewInp) (res *sysin.MemberLevelViewModel, err error) {
res = new(sysin.MemberLevelViewModel)
if err = s.model(ctx).WherePri(in.Id).Scan(&res.Level); err != nil {
return nil, gerror.Wrap(err, "获取会员等级失败")
}
if res.Level == nil {
return nil, gerror.New("会员等级不存在")
}
vc := dao.AppMemberLevelVersion.Columns()
if err = s.versionModel(ctx).Where(vc.LevelId, in.Id).WhereIn(vc.PublishStatus, []int{consts.BizPublishDraft, consts.BizPublishPending}).OrderDesc(vc.Id).Limit(1).Scan(&res.Draft); err != nil {
return nil, err
}
if res.Level.CurrentVersionId > 0 {
if err = s.versionModel(ctx).WherePri(res.Level.CurrentVersionId).Scan(&res.Published); err != nil {
return nil, err
}
}
return
}
func (s *sSysMemberLevel) Save(ctx context.Context, in *sysin.MemberLevelSaveInp) (id, versionId uint64, err error) {
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
lc, vc := dao.AppMemberLevel.Columns(), dao.AppMemberLevelVersion.Columns()
count, e := s.model(ctx).Where(lc.LevelCode, in.LevelCode).WhereNot(lc.Id, in.Id).Count()
if e != nil {
return e
}
if count > 0 {
return gerror.New("等级编码已存在")
}
count, e = s.model(ctx).Where(lc.Name, in.Name).WhereNot(lc.Id, in.Id).Count()
if e != nil {
return e
}
if count > 0 {
return gerror.New("等级名称已存在")
}
userId := contexts.GetUserId(ctx)
if in.Id == 0 {
result, e := s.model(ctx).Fields(lc.LevelCode, lc.Name, lc.IconAttachmentId, lc.IconUrl, lc.Status, lc.Sort, lc.Remark, lc.CreatedBy, lc.UpdatedBy).Hook(hook.SaveTenant).Data(g.Map{lc.LevelCode: in.LevelCode, lc.Name: in.Name, lc.IconAttachmentId: in.IconAttachmentId, lc.IconUrl: in.IconUrl, lc.Status: in.Status, lc.Sort: in.Sort, lc.Remark: in.Remark, lc.CreatedBy: userId, lc.UpdatedBy: userId}).Insert()
if e != nil {
return gerror.Wrap(e, "创建会员等级失败")
}
last, _ := result.LastInsertId()
in.Id = uint64(last)
} else {
_, e = s.model(ctx).WherePri(in.Id).Data(g.Map{lc.LevelCode: in.LevelCode, lc.Name: in.Name, lc.IconAttachmentId: in.IconAttachmentId, lc.IconUrl: in.IconUrl, lc.Status: in.Status, lc.Sort: in.Sort, lc.Remark: in.Remark, lc.UpdatedBy: userId}).Update()
if e != nil {
return gerror.Wrap(e, "更新会员等级失败")
}
}
id = in.Id
data := g.Map{vc.UpgradeAmount: in.UpgradeAmount, vc.GiftAmount: in.GiftAmount, vc.DailyOrderLimit: in.DailyOrderLimit, vc.CommissionRate: in.CommissionRate, vc.DailyWithdrawLimit: in.DailyWithdrawLimit, vc.RuleJson: in.RuleJson, vc.PublishStatus: consts.BizPublishPublished, vc.EffectiveAt: nil, vc.PublishedAt: gtime.Now(), vc.Remark: in.Remark, vc.UpdatedBy: userId}
var level *entity.AppMemberLevel
if e = s.model(ctx).WherePri(in.Id).Scan(&level); e != nil {
return e
}
if level == nil {
return gerror.New("会员等级不存在")
}
versionId = level.CurrentVersionId
if versionId == 0 {
var latest *entity.AppMemberLevelVersion
if e = s.versionModel(ctx).Where(vc.LevelId, in.Id).OrderDesc(vc.Id).Limit(1).Scan(&latest); e != nil {
return e
}
if latest != nil {
versionId = latest.Id
}
}
if versionId > 0 {
data[vc.Version] = gdb.Raw(vc.Version + "+1")
if _, e = s.versionModel(ctx).WherePri(versionId).Where(vc.LevelId, in.Id).Data(data).Update(); e != nil {
return gerror.Wrap(e, "更新等级规则失败")
}
} else {
data[vc.LevelId] = in.Id
data[vc.VersionNo] = 1
data[vc.CreatedBy] = userId
result, insertErr := s.versionModel(ctx).Fields(vc.LevelId, vc.VersionNo, vc.UpgradeAmount, vc.GiftAmount, vc.DailyOrderLimit, vc.CommissionRate, vc.DailyWithdrawLimit, vc.RuleJson, vc.PublishStatus, vc.EffectiveAt, vc.PublishedAt, vc.Remark, vc.CreatedBy, vc.UpdatedBy).Hook(hook.SaveTenant).Data(data).Insert()
if insertErr != nil {
return gerror.Wrap(insertErr, "创建等级规则失败")
}
last, _ := result.LastInsertId()
versionId = uint64(last)
}
if _, e = s.model(ctx).WherePri(in.Id).Data(g.Map{lc.CurrentVersionId: versionId, lc.UpdatedBy: userId}).Update(); e != nil {
return gerror.Wrap(e, "更新等级当前规则失败")
}
_ = dict.ClearFuncCache("memberLevelOption")
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member_level", Action: "level.save", TargetType: "app_member_level", TargetId: in.Id, After: g.Map{"upgradeAmount": in.UpgradeAmount, "giftAmount": in.GiftAmount, "commissionRate": in.CommissionRate}, Reason: in.Remark})
})
return
}
func (s *sSysMemberLevel) Submit(ctx context.Context, in *sysin.MemberLevelSubmitInp) (res *sysin.MemberLevelApprovalModel, err error) {
var v *entity.AppMemberLevelVersion
vc := dao.AppMemberLevelVersion.Columns()
if err = s.versionModel(ctx).WherePri(in.VersionId).Scan(&v); err != nil {
return
}
if v == nil {
return nil, gerror.New("等级规则版本不存在")
}
if v.PublishStatus != consts.BizPublishDraft {
return nil, gerror.New("仅草稿可以提交审批")
}
if v.Version != in.Version {
return nil, gerror.New("等级规则已更新,请刷新后重试")
}
var level *entity.AppMemberLevel
_ = s.model(ctx).WherePri(v.LevelId).Scan(&level)
approval, err := service.SysApproval().Create(ctx, &sysin.ApprovalCreateInp{ApprovalType: "member_level_publish", TargetType: "app_member_level_version", TargetId: v.Id, Action: "publish", Title: "发布会员等级 " + level.Name + fmt.Sprintf(" V%d", v.VersionNo), PayloadJson: gjson.New(v), RiskLevel: consts.ApprovalRiskHigh, RequiredApprovals: 1, Remark: in.Reason})
if err != nil {
return nil, err
}
if err = service.SysApproval().Submit(ctx, &sysin.ApprovalSubmitInp{Id: approval.Id, Version: 0}); err != nil {
return nil, err
}
result, err := s.versionModel(ctx).WherePri(v.Id).Where(vc.Version, v.Version).Data(g.Map{vc.PublishStatus: consts.BizPublishPending, vc.ApprovalRequestId: approval.Id, vc.UpdatedBy: contexts.GetUserId(ctx), vc.Version: gdb.Raw(vc.Version + "+1")}).Update()
if err != nil {
return nil, gerror.Wrap(err, "更新等级审批状态失败")
}
rows, _ := result.RowsAffected()
if rows != 1 {
return nil, gerror.New("等级规则已更新,请刷新后重试")
}
return &sysin.MemberLevelApprovalModel{ApprovalId: approval.Id, ApprovalNo: approval.ApprovalNo}, nil
}
func (s *sSysMemberLevel) Publish(ctx context.Context, in *sysin.MemberLevelPublishInp) error {
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
vc, lc := dao.AppMemberLevelVersion.Columns(), dao.AppMemberLevel.Columns()
var v *entity.AppMemberLevelVersion
if err := s.versionModel(ctx).WherePri(in.VersionId).LockUpdate().Scan(&v); err != nil {
return err
}
if v == nil {
return gerror.New("等级规则版本不存在")
}
if v.PublishStatus != consts.BizPublishPending {
return gerror.New("仅待审版本可以发布")
}
if v.Version != in.Version {
return gerror.New("等级规则已更新,请刷新后重试")
}
var approval *entity.ApprovalRequest
if err := service.SysApproval().Model(ctx).WherePri(v.ApprovalRequestId).Scan(&approval); err != nil {
return err
}
if approval == nil || approval.ApprovalStatus != consts.ApprovalStatusApproved {
return gerror.New("审批尚未通过")
}
if v.EffectiveAt != nil && v.EffectiveAt.After(gtime.Now()) {
return gerror.New("计划生效时间尚未到达")
}
_, _ = s.versionModel(ctx).Where(vc.LevelId, v.LevelId).Where(vc.PublishStatus, consts.BizPublishPublished).Data(g.Map{vc.PublishStatus: consts.BizPublishArchived, vc.UpdatedBy: contexts.GetUserId(ctx)}).Update()
result, err := s.versionModel(ctx).WherePri(v.Id).Where(vc.Version, v.Version).Data(g.Map{vc.PublishStatus: consts.BizPublishPublished, vc.PublishedAt: gtime.Now(), vc.UpdatedBy: contexts.GetUserId(ctx), vc.Version: gdb.Raw(vc.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "发布等级规则失败")
}
rows, _ := result.RowsAffected()
if rows != 1 {
return gerror.New("等级规则已更新,请刷新后重试")
}
if _, err = s.model(ctx).WherePri(v.LevelId).Data(g.Map{lc.CurrentVersionId: v.Id, lc.UpdatedBy: contexts.GetUserId(ctx)}).Update(); err != nil {
return err
}
_, err = service.SysApproval().Model(ctx).WherePri(approval.Id).Data(g.Map{dao.ApprovalRequest.Columns().ExecutedAt: gtime.Now(), dao.ApprovalRequest.Columns().UpdatedBy: contexts.GetUserId(ctx)}).Update()
if err != nil {
return err
}
_ = dict.ClearFuncCache("memberLevelOption")
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member_level", Action: "level.publish", TargetType: "app_member_level", TargetId: v.LevelId, After: v, ApprovalNo: approval.ApprovalNo, Reason: v.Remark})
})
}
func (s *sSysMemberLevel) Rollback(ctx context.Context, in *sysin.MemberLevelRollbackInp) (versionId uint64, err error) {
var source *entity.AppMemberLevelVersion
if err = s.versionModel(ctx).WherePri(in.VersionId).Scan(&source); err != nil {
return
}
if source == nil {
return 0, gerror.New("历史版本不存在")
}
var level *entity.AppMemberLevel
if err = s.model(ctx).WherePri(source.LevelId).Scan(&level); err != nil {
return
}
if level == nil {
return 0, gerror.New("会员等级不存在")
}
_, versionId, err = s.Save(ctx, &sysin.MemberLevelSaveInp{Id: level.Id, LevelCode: level.LevelCode, Name: level.Name, IconAttachmentId: level.IconAttachmentId, IconUrl: level.IconUrl, UpgradeAmount: source.UpgradeAmount, GiftAmount: source.GiftAmount, DailyOrderLimit: source.DailyOrderLimit, CommissionRate: source.CommissionRate, DailyWithdrawLimit: source.DailyWithdrawLimit, RuleJson: source.RuleJson, EffectiveAt: nil, Sort: level.Sort, Status: level.Status, Remark: "回滚自 V" + fmt.Sprint(source.VersionNo) + ":" + in.Reason})
return
}
func (s *sSysMemberLevel) History(ctx context.Context, in *sysin.MemberLevelHistoryInp) (list []*entity.AppMemberLevelVersion, total int, err error) {
c := dao.AppMemberLevelVersion.Columns()
err = s.versionModel(ctx).Where(c.LevelId, in.LevelId).Page(in.Page, in.PerPage).OrderDesc(c.VersionNo).ScanAndCount(&list, &total, false)
if err != nil {
err = gerror.Wrap(err, "获取等级版本历史失败")
}
return
}
func (s *sSysMemberLevel) Status(ctx context.Context, in *sysin.MemberLevelStatusInp) error {
c := dao.AppMemberLevel.Columns()
_, err := s.model(ctx).WherePri(in.Id).Data(g.Map{c.Status: in.Status, c.UpdatedBy: contexts.GetUserId(ctx)}).Update()
if err != nil {
return gerror.Wrap(err, "更新等级状态失败")
}
_ = dict.ClearFuncCache("memberLevelOption")
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member_level", Action: "level.status", TargetType: "app_member_level", TargetId: in.Id, After: g.Map{"status": in.Status}})
}
func (s *sSysMemberLevel) Delete(ctx context.Context, in *sysin.MemberLevelDeleteInp) error {
count, err := handler.Model(dao.AppMember.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(dao.AppMember.Columns().LevelId, in.Id).Count()
if err != nil {
return err
}
if count > 0 {
return gerror.New("该等级仍有会员使用,不能删除,请改为停用")
}
count, err = handler.Model(dao.TaskHallLevel.Ctx(ctx), &handler.Option{FilterTenant: true}).Where(dao.TaskHallLevel.Columns().LevelId, in.Id).Count()
if err != nil {
return err
}
if count > 0 {
return gerror.New("该等级仍被任务大厅引用,不能删除")
}
c := dao.AppMemberLevel.Columns()
_, err = s.model(ctx).WherePri(in.Id).Data(g.Map{c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx), c.UpdatedBy: contexts.GetUserId(ctx)}).Update()
if err != nil {
return gerror.Wrap(err, "删除会员等级失败")
}
_ = dict.ClearFuncCache("memberLevelOption")
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "member_level", Action: "level.delete", TargetType: "app_member_level", TargetId: in.Id, Reason: in.Reason})
}
func (s *sSysMemberLevel) Option(ctx context.Context) (opts []*model.Option, err error) {
var rows []*entity.AppMemberLevel
c := dao.AppMemberLevel.Columns()
if err = s.model(ctx).Fields(c.Id, c.Name).Where(c.Status, consts.StatusEnabled).WhereGT(c.CurrentVersionId, 0).OrderAsc(c.Sort).OrderAsc(c.Id).Scan(&rows); err != nil {
return nil, gerror.Wrap(err, "获取会员等级选项失败")
}
opts = make([]*model.Option, len(rows))
for i, row := range rows {
opts[i] = dict.GenHashOption(row.Id, gconv.String(row.Name))
}
return
}