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.PublishStatus: consts.BizPublishPublished, 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.BizPublishPublished 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) }