325 lines
16 KiB
Go
325 lines
16 KiB
Go
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) {
|
||
rules := g.Map{}
|
||
if in.RuleJson != nil {
|
||
for key, value := range in.RuleJson.Map() {
|
||
rules[key] = value
|
||
}
|
||
}
|
||
rules["auto_order_delay_min_seconds"] = in.AutoOrderDelayMinSeconds
|
||
rules["auto_order_delay_max_seconds"] = in.AutoOrderDelayMaxSeconds
|
||
in.RuleJson = gjson.New(rules)
|
||
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("会员等级不存在")
|
||
}
|
||
delayMin, delayMax := 0, 0
|
||
if source.RuleJson != nil {
|
||
delayMin = source.RuleJson.Get("auto_order_delay_min_seconds").Int()
|
||
delayMax = source.RuleJson.Get("auto_order_delay_max_seconds").Int()
|
||
}
|
||
_, 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, AutoOrderDelayMinSeconds: delayMin, AutoOrderDelayMaxSeconds: delayMax, 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
|
||
}
|