Files
olons_admin/server/internal/logic/sys/portal.go
T

586 lines
24 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"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"strconv"
"strings"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/hgorm/handler"
"hotgo/internal/library/hgorm/hook"
"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/grand"
)
type sSysPortal struct{}
func NewSysPortal() *sSysPortal { return &sSysPortal{} }
func init() { service.RegisterSysPortal(NewSysPortal()) }
func (s *sSysPortal) siteModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.SiteConfigVersion.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysPortal) contentModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.Content.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysPortal) contentVersionModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.ContentVersion.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysPortal) bannerModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.Banner.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysPortal) productModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.Product.Ctx(ctx), &handler.Option{FilterTenant: true})
}
func (s *sSysPortal) SiteConfigCurrent(ctx context.Context, in *sysin.SiteConfigCurrentInp) (res *sysin.SiteConfigCurrentModel, err error) {
res = new(sysin.SiteConfigCurrentModel)
c := dao.SiteConfigVersion.Columns()
if err = s.siteModel(ctx).Where(c.ConfigGroup, in.ConfigGroup).WhereIn(c.PublishStatus, []int{consts.BizPublishDraft, consts.BizPublishPending}).OrderDesc(c.Id).Limit(1).Scan(&res.Draft); err != nil {
return nil, gerror.Wrap(err, "获取站点配置草稿失败")
}
if err = s.siteModel(ctx).Where(c.ConfigGroup, in.ConfigGroup).Where(c.PublishStatus, consts.BizPublishPublished).OrderDesc(c.Id).Limit(1).Scan(&res.Published); err != nil {
return nil, gerror.Wrap(err, "获取已发布站点配置失败")
}
return res, nil
}
func (s *sSysPortal) SiteConfigHistory(ctx context.Context, in *sysin.SiteConfigHistoryInp) (list []*sysin.SiteConfigHistoryModel, totalCount int, err error) {
c := dao.SiteConfigVersion.Columns()
err = s.siteModel(ctx).Fields(sysin.SiteConfigHistoryModel{}).Where(c.ConfigGroup, in.ConfigGroup).Page(in.Page, in.PerPage).OrderDesc(c.Id).ScanAndCount(&list, &totalCount, false)
if err != nil {
err = gerror.Wrap(err, "获取配置版本记录失败")
}
return
}
func (s *sSysPortal) SiteConfigSave(ctx context.Context, in *sysin.SiteConfigSaveInp) (id uint64, err error) {
raw, err := json.Marshal(in.Config)
if err != nil {
return 0, gerror.Wrap(err, "序列化站点配置失败")
}
sum := sha256.Sum256(raw)
digest := hex.EncodeToString(sum[:])
userId := uint64(contexts.GetUserId(ctx))
c := dao.SiteConfigVersion.Columns()
now := gtime.Now()
if in.Id > 0 {
_, _ = s.siteModel(ctx).
Where(c.ConfigGroup, in.ConfigGroup).
Where(c.PublishStatus, consts.BizPublishPublished).
WhereNot(c.Id, in.Id).
Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: userId}).
Update()
result, updateErr := s.siteModel(ctx).
WherePri(in.Id).
Where(c.Version, in.Version).
Data(gdb.Map{
c.ConfigJson: gjson.New(raw),
c.ConfigDigest: digest,
c.PublishStatus: consts.BizPublishPublished,
c.EffectiveAt: in.EffectiveAt,
c.PublishedAt: now,
c.ApprovalRequestId: 0,
c.Remark: in.Remark,
c.UpdatedBy: userId,
c.Version: gdb.Raw(c.Version + "+1"),
}).Update()
if updateErr != nil {
return 0, gerror.Wrap(updateErr, "保存站点配置失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return 0, gerror.New("配置已更新,请刷新后重试")
}
return in.Id, nil
}
_, _ = s.siteModel(ctx).
Where(c.ConfigGroup, in.ConfigGroup).
Where(c.PublishStatus, consts.BizPublishPublished).
Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: userId}).
Update()
configNo := "CFG" + gtime.Now().Format("YmdHis") + grand.S(5)
result, err := s.siteModel(ctx).
Fields(c.ConfigNo, c.ConfigGroup, c.SchemaVersion, c.ConfigJson, c.ConfigDigest, c.PublishStatus, c.EffectiveAt, c.PublishedAt, c.BaseVersionId, c.Version, c.Remark, c.CreatedBy, c.UpdatedBy).
Hook(hook.SaveTenant).
Data(gdb.Map{
c.ConfigNo: configNo,
c.ConfigGroup: in.ConfigGroup,
c.SchemaVersion: "1.0",
c.ConfigJson: gjson.New(raw),
c.ConfigDigest: digest,
c.PublishStatus: consts.BizPublishPublished,
c.EffectiveAt: in.EffectiveAt,
c.PublishedAt: now,
c.CreatedBy: userId,
c.UpdatedBy: userId,
}).Insert()
if err != nil {
return 0, gerror.Wrap(err, "保存站点配置失败")
}
last, _ := result.LastInsertId()
return uint64(last), nil
}
func (s *sSysPortal) SiteConfigSubmit(ctx context.Context, in *sysin.SiteConfigSubmitInp) (res *sysin.PortalApprovalModel, err error) {
var row *entity.SiteConfigVersion
c := dao.SiteConfigVersion.Columns()
if err = s.siteModel(ctx).WherePri(in.Id).Scan(&row); err != nil {
return nil, gerror.Wrap(err, "读取配置草稿失败")
}
if row == nil || row.Id == 0 {
return nil, gerror.New("配置草稿不存在")
}
if row.PublishStatus != consts.BizPublishDraft {
return nil, gerror.New("仅草稿可以提交审批")
}
if row.Version != in.Version {
return nil, gerror.New("配置已更新,请刷新后重试")
}
payload := gjson.New(row)
approval, err := service.SysApproval().Create(ctx, &sysin.ApprovalCreateInp{ApprovalType: "site_config_publish", TargetType: "site_config_version", TargetId: row.Id, Action: "publish", Title: "发布站点配置 " + row.ConfigNo, PayloadJson: payload, 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.siteModel(ctx).WherePri(row.Id).Where(c.Version, row.Version).Data(gdb.Map{c.PublishStatus: consts.BizPublishPending, c.ApprovalRequestId: approval.Id, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return nil, gerror.Wrap(err, "更新配置审批状态失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return nil, gerror.New("配置已更新,请刷新后重试")
}
return &sysin.PortalApprovalModel{ApprovalId: approval.Id, ApprovalNo: approval.ApprovalNo}, nil
}
func (s *sSysPortal) SiteConfigPublish(ctx context.Context, in *sysin.SiteConfigPublishInp) error {
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
var row *entity.SiteConfigVersion
c := dao.SiteConfigVersion.Columns()
if err := s.siteModel(ctx).WherePri(in.Id).LockUpdate().Scan(&row); err != nil {
return gerror.Wrap(err, "读取配置版本失败")
}
if row == nil {
return gerror.New("配置版本不存在")
}
if row.Version != in.Version {
return gerror.New("配置已更新,请刷新后重试")
}
if row.PublishStatus != consts.BizPublishPending {
return gerror.New("仅待审配置可以发布")
}
var approval *entity.ApprovalRequest
if err := service.SysApproval().Model(ctx).WherePri(row.ApprovalRequestId).Scan(&approval); err != nil {
return err
}
if approval == nil || approval.ApprovalStatus != consts.ApprovalStatusApproved {
return gerror.New("审批尚未通过,不能发布")
}
now := gtime.Now()
if row.EffectiveAt != nil && row.EffectiveAt.After(now) {
return gerror.New("计划生效时间尚未到达")
}
_, _ = s.siteModel(ctx).Where(c.ConfigGroup, row.ConfigGroup).Where(c.PublishStatus, consts.BizPublishPublished).Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: contexts.GetUserId(ctx)}).Update()
_, err := s.siteModel(ctx).WherePri(row.Id).Where(c.Version, row.Version).Data(gdb.Map{c.PublishStatus: consts.BizPublishPublished, c.PublishedAt: now, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "发布站点配置失败")
}
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "portal", Action: "publish", TargetType: "site_config_version", TargetId: row.Id, After: row, ApprovalNo: approval.ApprovalNo, Reason: "审批通过后发布"})
})
}
func (s *sSysPortal) SiteConfigRollback(ctx context.Context, in *sysin.SiteConfigRollbackInp) (id uint64, err error) {
var source *entity.SiteConfigVersion
if err = s.siteModel(ctx).WherePri(in.Id).Scan(&source); err != nil {
return 0, gerror.Wrap(err, "读取历史配置失败")
}
if source == nil {
return 0, gerror.New("历史配置不存在")
}
save := &sysin.SiteConfigSaveInp{ConfigGroup: source.ConfigGroup, Remark: "回滚自 " + source.ConfigNo + ":" + in.Reason}
if err = source.ConfigJson.Scan(&save.Config); err != nil {
return 0, gerror.Wrap(err, "解析历史配置失败")
}
return s.SiteConfigSave(ctx, save)
}
func (s *sSysPortal) ContentList(ctx context.Context, in *sysin.ContentListInp) (list []*sysin.ContentListModel, totalCount int, err error) {
c := dao.Content.Columns()
mod := s.contentModel(ctx).Fields(sysin.ContentListModel{})
if in.Title != "" {
mod = mod.WhereLike(c.Title, "%"+in.Title+"%")
}
if in.ContentType != "" {
mod = mod.Where(c.ContentType, in.ContentType)
}
if err = mod.Page(in.Page, in.PerPage).OrderDesc(c.Id).ScanAndCount(&list, &totalCount, false); err != nil {
return nil, 0, gerror.Wrap(err, "获取文本内容列表失败")
}
vc := dao.ContentVersion.Columns()
for _, item := range list {
var v *entity.ContentVersion
_ = s.contentVersionModel(ctx).Where(vc.ContentId, item.Id).OrderDesc(vc.VersionNo).Limit(1).Scan(&v)
if v != nil {
item.LatestVersionId = v.Id
item.LatestVersionNo = v.VersionNo
item.Summary = v.Summary
item.CoverUrl = v.CoverUrl
}
}
return
}
func (s *sSysPortal) ContentView(ctx context.Context, in *sysin.ContentViewInp) (res *sysin.ContentViewModel, err error) {
res = new(sysin.ContentViewModel)
if err = s.contentModel(ctx).WherePri(in.Id).Scan(&res.Content); err != nil {
return nil, gerror.Wrap(err, "获取文本内容失败")
}
if res.Id == 0 {
return nil, gerror.New("文本内容不存在")
}
vc := dao.ContentVersion.Columns()
if err = s.contentVersionModel(ctx).Where(vc.ContentId, in.Id).OrderDesc(vc.VersionNo).Scan(&res.Versions); err != nil {
return nil, gerror.Wrap(err, "获取内容版本失败")
}
if len(res.Versions) > 0 {
res.LatestVersion = res.Versions[0]
}
return res, nil
}
func (s *sSysPortal) ContentEdit(ctx context.Context, in *sysin.ContentEditInp) (id uint64, err error) {
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
userId := uint64(contexts.GetUserId(ctx))
c := dao.Content.Columns()
if in.Id == 0 {
result, e := s.contentModel(ctx).Fields(c.ContentKey, c.ContentType, c.Title, c.PublishStatus, c.Sort, c.Version, c.Remark, c.CreatedBy, c.UpdatedBy).Hook(hook.SaveTenant).Data(gdb.Map{c.ContentKey: in.ContentKey, c.ContentType: in.ContentType, c.Title: in.Title, c.PublishStatus: consts.BizPublishDraft, c.Sort: in.Sort, c.Remark: in.Remark, c.CreatedBy: userId, c.UpdatedBy: userId}).Insert()
if e != nil {
return gerror.Wrap(e, "创建文本内容失败")
}
last, _ := result.LastInsertId()
id = uint64(last)
} else {
id = in.Id
result, e := s.contentModel(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(gdb.Map{c.ContentType: in.ContentType, c.Title: in.Title, c.Sort: in.Sort, c.Remark: in.Remark, c.UpdatedBy: userId, c.Version: gdb.Raw(c.Version + "+1")}).Update()
if e != nil {
return gerror.Wrap(e, "更新文本内容失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return gerror.New("内容已更新,请刷新后重试")
}
}
vc := dao.ContentVersion.Columns()
maxNo, e := s.contentVersionModel(ctx).Where(vc.ContentId, id).Max(vc.VersionNo)
if e != nil {
return gerror.Wrap(e, "读取内容版本号失败")
}
_, e = s.contentVersionModel(ctx).Fields(vc.ContentId, vc.VersionNo, vc.Title, vc.Summary, vc.BodyHtml, vc.BodyText, vc.CoverUrl, vc.PublishStatus, vc.EffectiveAt, vc.CreatedBy).Hook(hook.SaveTenant).Data(gdb.Map{vc.ContentId: id, vc.VersionNo: uint(maxNo) + 1, vc.Title: in.Title, vc.Summary: in.Summary, vc.BodyHtml: in.BodyHtml, vc.BodyText: in.BodyText, vc.CoverUrl: in.CoverUrl, vc.PublishStatus: consts.BizPublishDraft, vc.EffectiveAt: in.EffectiveAt, vc.CreatedBy: userId}).Insert()
if e != nil {
return gerror.Wrap(e, "创建内容版本失败")
}
return nil
})
return
}
func (s *sSysPortal) ContentDelete(ctx context.Context, in *sysin.ContentDeleteInp) error {
var row *entity.Content
if err := s.contentModel(ctx).WherePri(in.Id).Scan(&row); err != nil {
return err
}
if row == nil {
return gerror.New("文本内容不存在")
}
c := dao.Content.Columns()
if row.PublishStatus == consts.BizPublishPublished {
_, err := s.contentModel(ctx).WherePri(in.Id).Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "下线文本内容失败")
}
return nil
}
_, err := s.contentModel(ctx).WherePri(in.Id).Data(gdb.Map{c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx)}).Unscoped().Update()
if err != nil {
return gerror.Wrap(err, "删除文本内容失败")
}
return nil
}
func (s *sSysPortal) ContentPublish(ctx context.Context, in *sysin.ContentPublishInp) error {
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
var content *entity.Content
var version *entity.ContentVersion
if err := s.contentModel(ctx).WherePri(in.Id).LockUpdate().Scan(&content); err != nil {
return err
}
if content == nil {
return gerror.New("文本内容不存在")
}
vc := dao.ContentVersion.Columns()
if err := s.contentVersionModel(ctx).WherePri(in.VersionId).Where(vc.ContentId, in.Id).Scan(&version); err != nil {
return err
}
if version == nil {
return gerror.New("内容版本不存在")
}
if version.EffectiveAt != nil && version.EffectiveAt.After(gtime.Now()) {
return gerror.New("计划发布时间尚未到达")
}
_, _ = s.contentVersionModel(ctx).Where(vc.ContentId, in.Id).Where(vc.PublishStatus, consts.BizPublishPublished).Data(gdb.Map{vc.PublishStatus: consts.BizPublishOffline}).Update()
_, err := s.contentVersionModel(ctx).WherePri(version.Id).Data(gdb.Map{vc.PublishStatus: consts.BizPublishPublished}).Update()
if err != nil {
return gerror.Wrap(err, "发布内容版本失败")
}
c := dao.Content.Columns()
_, err = s.contentModel(ctx).WherePri(in.Id).Data(gdb.Map{c.PublishedVersionId: version.Id, c.PublishStatus: consts.BizPublishPublished, c.PublishedAt: gtime.Now(), c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
return gerror.Wrap(err, "发布文本内容失败")
})
}
func (s *sSysPortal) ContentOffline(ctx context.Context, in *sysin.ContentOfflineInp) error {
c := dao.Content.Columns()
_, err := s.contentModel(ctx).WherePri(in.Id).Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "下线文本内容失败")
}
return service.SysBizAudit().Record(ctx, &sysin.BizAuditRecordInp{Module: "portal", Action: "offline", TargetType: "content", TargetId: in.Id, Reason: in.Reason})
}
func (s *sSysPortal) BannerList(ctx context.Context, in *sysin.BannerListInp) (list []*sysin.BannerListModel, totalCount int, err error) {
c := dao.Banner.Columns()
mod := s.bannerModel(ctx).Fields(sysin.BannerListModel{})
if in.Name != "" {
mod = mod.WhereLike(c.Name, "%"+in.Name+"%")
}
if in.Position != "" {
mod = mod.Where(c.Position, in.Position)
}
if in.PublishStatus > 0 {
mod = mod.Where(c.PublishStatus, in.PublishStatus)
}
err = mod.Page(in.Page, in.PerPage).OrderAsc(c.Sort).OrderDesc(c.Id).ScanAndCount(&list, &totalCount, false)
if err != nil {
err = gerror.Wrap(err, "获取Banner列表失败")
}
return
}
func (s *sSysPortal) BannerView(ctx context.Context, in *sysin.BannerViewInp) (res *entity.Banner, err error) {
err = s.bannerModel(ctx).WherePri(in.Id).Scan(&res)
if err != nil {
return nil, gerror.Wrap(err, "获取Banner失败")
}
if res == nil {
return nil, gerror.New("Banner不存在")
}
return
}
func (s *sSysPortal) BannerEdit(ctx context.Context, in *sysin.BannerEditInp) (id uint64, err error) {
c := dao.Banner.Columns()
userId := uint64(contexts.GetUserId(ctx))
if in.Id > 0 {
result, e := s.bannerModel(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(gdb.Map{
c.Name: in.Name, c.Position: in.Position, c.ImageAttachmentId: in.ImageAttachmentId,
c.ImageUrl: in.ImageUrl, c.AltText: in.AltText, c.TargetType: in.TargetType,
c.TargetValue: in.TargetValue, c.StartAt: in.StartAt, c.EndAt: in.EndAt,
c.Sort: in.Sort, c.Remark: in.Remark, c.UpdatedBy: userId, c.Version: gdb.Raw(c.Version + "+1"),
}).Update()
if e != nil {
return 0, gerror.Wrap(e, "更新Banner失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return 0, gerror.New("Banner已更新,请刷新后重试")
}
return in.Id, nil
}
in.PublishStatus = consts.BizPublishDraft
in.CreatedBy = userId
in.UpdatedBy = userId
result, e := s.bannerModel(ctx).Fields(c.Name, c.Position, c.ImageAttachmentId, c.ImageUrl, c.AltText, c.TargetType, c.TargetValue, c.StartAt, c.EndAt, c.PublishStatus, c.Sort, c.Version, c.Remark, c.CreatedBy, c.UpdatedBy).Hook(hook.SaveTenant).Data(in).Insert()
if e != nil {
return 0, gerror.Wrap(e, "创建Banner失败")
}
last, _ := result.LastInsertId()
return uint64(last), nil
}
func (s *sSysPortal) BannerDelete(ctx context.Context, in *sysin.BannerDeleteInp) error {
var row *entity.Banner
if err := s.bannerModel(ctx).WherePri(in.Id).Scan(&row); err != nil {
return err
}
if row == nil {
return gerror.New("Banner不存在")
}
c := dao.Banner.Columns()
if row.PublishStatus == consts.BizPublishPublished {
_, err := s.bannerModel(ctx).WherePri(in.Id).Data(gdb.Map{c.PublishStatus: consts.BizPublishOffline, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
return err
}
_, err := s.bannerModel(ctx).WherePri(in.Id).Data(gdb.Map{c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx)}).Unscoped().Update()
return gerror.Wrap(err, "删除Banner失败")
}
func (s *sSysPortal) BannerStatus(ctx context.Context, in *sysin.BannerStatusInp) error {
c := dao.Banner.Columns()
result, err := s.bannerModel(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(gdb.Map{c.PublishStatus: in.PublishStatus, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "更新Banner状态失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return gerror.New("Banner已更新,请刷新后重试")
}
return nil
}
func (s *sSysPortal) ManualProductList(ctx context.Context, in *sysin.ManualProductListInp) (list []*sysin.ManualProductListModel, totalCount int, err error) {
c := dao.Product.Columns()
mod := s.productModel(ctx).Fields(sysin.ManualProductListModel{}).Where(c.ProductType, consts.ProductTypeManual)
if in.Name != "" {
mod = mod.WhereLike(c.Name, "%"+in.Name+"%")
}
if in.PublishStatus > 0 {
mod = mod.Where(c.PublishStatus, in.PublishStatus)
}
if err = mod.Page(in.Page, in.PerPage).OrderAsc(c.Sort).OrderDesc(c.Id).ScanAndCount(&list, &totalCount, false); err != nil {
return nil, 0, gerror.Wrap(err, "获取手工商城列表失败")
}
for _, item := range list {
item.Price = formatCents(item.PriceAmount)
}
return
}
func (s *sSysPortal) ManualProductView(ctx context.Context, in *sysin.ManualProductViewInp) (res *sysin.ManualProductViewModel, err error) {
c := dao.Product.Columns()
if err = s.productModel(ctx).WherePri(in.Id).Where(c.ProductType, consts.ProductTypeManual).Scan(&res); err != nil {
return nil, gerror.Wrap(err, "获取手工商城商品失败")
}
if res == nil {
return nil, gerror.New("商品不存在")
}
res.Price = formatCents(res.PriceAmount)
return
}
func (s *sSysPortal) ManualProductEdit(ctx context.Context, in *sysin.ManualProductEditInp) (id uint64, err error) {
amount, err := parseCents(in.Price)
if err != nil {
return 0, err
}
in.PriceAmount = amount
in.ProductType = consts.ProductTypeManual
if in.PaymentType == "" {
in.PaymentType = "balance"
}
if in.Currency == "" {
in.Currency = "CNY"
}
c := dao.Product.Columns()
userId := uint64(contexts.GetUserId(ctx))
if in.Id > 0 {
result, e := s.productModel(ctx).WherePri(in.Id).Where(c.Version, in.Version).Data(gdb.Map{
c.Name: in.Name,
c.PriceAmount: in.PriceAmount,
c.CoverAttachmentId: in.CoverAttachmentId,
c.CoverUrl: in.CoverUrl,
c.UpdatedBy: userId,
c.Version: gdb.Raw(c.Version + "+1"),
}).Update()
if e != nil {
return 0, gerror.Wrap(e, "更新手工商城商品失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return 0, gerror.New("商品已更新,请刷新后重试")
}
return in.Id, nil
}
in.ProductNo = "MP" + gtime.Now().Format("YmdHis") + grand.S(5)
in.PublishStatus = consts.BizPublishDraft
in.CreatedBy = userId
in.UpdatedBy = userId
result, e := s.productModel(ctx).Fields(c.ProductNo, c.CategoryId, c.ProductType, c.PaymentType, c.Name, c.ShopName, c.PriceAmount, c.Currency, c.CoverAttachmentId, c.CoverUrl, c.DetailHtml, c.IsRecommended, c.PublishStatus, c.StartAt, c.EndAt, c.Sort, c.Version, c.Remark, c.CreatedBy, c.UpdatedBy).Hook(hook.SaveTenant).Data(in).Insert()
if e != nil {
return 0, gerror.Wrap(e, "创建手工商城商品失败")
}
last, _ := result.LastInsertId()
return uint64(last), nil
}
func (s *sSysPortal) ManualProductDelete(ctx context.Context, in *sysin.ManualProductDeleteInp) error {
c := dao.Product.Columns()
_, err := s.productModel(ctx).
WhereIn(c.Id, in.Ids).
Where(c.ProductType, consts.ProductTypeManual).
Data(gdb.Map{c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx)}).
Unscoped().
Update()
return gerror.Wrap(err, "删除商品失败")
}
func (s *sSysPortal) ManualProductStatus(ctx context.Context, in *sysin.ManualProductStatusInp) error {
c := dao.Product.Columns()
result, err := s.productModel(ctx).WherePri(in.Id).Where(c.Version, in.Version).Where(c.ProductType, consts.ProductTypeManual).Data(gdb.Map{c.PublishStatus: in.PublishStatus, c.UpdatedBy: contexts.GetUserId(ctx), c.Version: gdb.Raw(c.Version + "+1")}).Update()
if err != nil {
return gerror.Wrap(err, "更新商品状态失败")
}
rows, _ := result.RowsAffected()
if rows == 0 {
return gerror.New("商品已更新,请刷新后重试")
}
return nil
}
func parseCents(value string) (int64, error) {
value = strings.TrimSpace(value)
if value == "" {
return 0, gerror.New("价格不能为空")
}
if strings.HasPrefix(value, "-") {
return 0, gerror.New("价格不能为负数")
}
parts := strings.Split(value, ".")
if len(parts) > 2 {
return 0, gerror.New("价格格式不正确")
}
yuan, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
return 0, gerror.New("价格格式不正确")
}
fraction := ""
if len(parts) == 2 {
fraction = parts[1]
}
if len(fraction) > 2 {
return 0, gerror.New("价格最多保留两位小数")
}
for len(fraction) < 2 {
fraction += "0"
}
fen := int64(0)
if fraction != "" {
fen, err = strconv.ParseInt(fraction, 10, 64)
if err != nil {
return 0, gerror.New("价格格式不正确")
}
}
if yuan > (1<<63-1-fen)/100 {
return 0, gerror.New("价格超出允许范围")
}
return yuan*100 + fen, nil
}
func formatCents(value int64) string { return fmt.Sprintf("%d.%02d", value/100, value%100) }