feat: add Go backend

This commit is contained in:
richjohnrambo
2026-10-10 20:56:17 +08:00
parent 50fcfd2d3d
commit f6024f0964
1040 changed files with 107169 additions and 0 deletions
+310
View File
@@ -0,0 +1,310 @@
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
}