chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,548 @@
|
||||
package cardpackage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
const (
|
||||
orderNoPrefix = "SO"
|
||||
subscriptionNoPrefix = "SUB"
|
||||
)
|
||||
|
||||
func (s *Service) CreatePackageSubscription(ctx context.Context, tx gdb.TX, in *CreatePackageSubscriptionInput) (*CreatePackageSubscriptionOutput, error) {
|
||||
if err := validateSubscribeInput(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
customer, err := s.loadTargetDept(ctx, tx, in.CustomerAgentId, in.Operator, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkg, err := s.loadPackage(ctx, tx, strings.TrimSpace(in.PackageCode))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := gtime.Now()
|
||||
periodBegin := in.PackageBeginTime
|
||||
periodEnd := packageEndAt(periodBegin, in.DurationMonths+in.GiftDurationMonths, in.EndAtPreviousMonthEnd)
|
||||
packageStatus := CalcPackageStatus(now, periodBegin, periodEnd)
|
||||
effectiveStatus := CalcEffectiveStatus(now, periodBegin, periodEnd)
|
||||
amount, err := parseAmount(in.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
quantity := len(in.Cards)
|
||||
unitPrice := 0.0
|
||||
if quantity > 0 {
|
||||
unitPrice = roundAmount(amount / float64(quantity))
|
||||
}
|
||||
businessType := in.BusinessType
|
||||
if businessType == 0 {
|
||||
businessType = consts.PackageBusinessTypeSubscribe
|
||||
}
|
||||
|
||||
orderSn, err := batchimport.NextRandomNo(orderNoPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orderId, err := s.insertOrder(ctx, tx, in, orderSn, amount, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = s.insertOrderItem(ctx, tx, in, orderId, orderSn, pkg, amount, unitPrice, quantity, periodBegin, periodEnd, now); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bindRows := make([]do.TSimCardBindPkg, 0, quantity)
|
||||
bindsByIccid := make(map[string]*entity.TSimCardBindPkg, quantity)
|
||||
for _, card := range in.Cards {
|
||||
iccid := strings.TrimSpace(card.Iccid)
|
||||
if iccid == "" {
|
||||
return nil, gerror.New("订购卡ICCID不能为空")
|
||||
}
|
||||
subscriptionNo, err := batchimport.NextRandomNo(subscriptionNoPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindRow := do.TSimCardBindPkg{
|
||||
SubscriptionNo: subscriptionNo,
|
||||
BatchNo: in.BatchNo,
|
||||
Iccid: iccid,
|
||||
Msisdn: strings.TrimSpace(card.Msisdn),
|
||||
DeptId: fmt.Sprintf("%d", in.CustomerAgentId),
|
||||
PackageType: pkg.PackageType,
|
||||
PackageCode: pkg.PackageCode,
|
||||
PreviousPackageCode: strings.TrimSpace(in.PreviousPackageCode),
|
||||
BusinessType: businessType,
|
||||
EffectiveStatus: effectiveStatus,
|
||||
HandleTime: now,
|
||||
EffectiveTime: periodBegin,
|
||||
ExpectedExpireTime: periodEnd,
|
||||
DurationMonths: in.DurationMonths,
|
||||
GiftDurationMonths: in.GiftDurationMonths,
|
||||
OrderCode: orderSn,
|
||||
Remark: strings.TrimSpace(in.BindRemark),
|
||||
TenantId: in.Operator.TenantId,
|
||||
CreatedDept: in.Operator.DeptId,
|
||||
CreatedBy: in.Operator.UserId,
|
||||
CreatedAt: now,
|
||||
UpdatedBy: in.Operator.UserId,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
bindRows = append(bindRows, bindRow)
|
||||
bindsByIccid[iccid] = &entity.TSimCardBindPkg{
|
||||
SubscriptionNo: subscriptionNo,
|
||||
BatchNo: in.BatchNo,
|
||||
Iccid: iccid,
|
||||
Msisdn: strings.TrimSpace(card.Msisdn),
|
||||
DeptId: fmt.Sprintf("%d", in.CustomerAgentId),
|
||||
PackageType: pkg.PackageType,
|
||||
PackageCode: pkg.PackageCode,
|
||||
PreviousPackageCode: strings.TrimSpace(in.PreviousPackageCode),
|
||||
BusinessType: businessType,
|
||||
EffectiveStatus: effectiveStatus,
|
||||
HandleTime: now,
|
||||
EffectiveTime: periodBegin,
|
||||
ExpectedExpireTime: periodEnd,
|
||||
DurationMonths: in.DurationMonths,
|
||||
GiftDurationMonths: in.GiftDurationMonths,
|
||||
OrderCode: orderSn,
|
||||
Remark: strings.TrimSpace(in.BindRemark),
|
||||
TenantId: in.Operator.TenantId,
|
||||
CreatedDept: in.Operator.DeptId,
|
||||
CreatedBy: in.Operator.UserId,
|
||||
CreatedAt: now,
|
||||
UpdatedBy: in.Operator.UserId,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
}
|
||||
if !in.SkipBindInsert {
|
||||
if err = s.InsertCardPackageBinds(ctx, tx, bindRows); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &CreatePackageSubscriptionOutput{
|
||||
OrderId: orderId,
|
||||
OrderSn: orderSn,
|
||||
Customer: customer,
|
||||
Package: pkg,
|
||||
PackageBeginAt: periodBegin,
|
||||
PackageEndAt: periodEnd,
|
||||
SubscribeTime: now,
|
||||
PackageStatus: packageStatus,
|
||||
EffectiveStatus: effectiveStatus,
|
||||
Amount: amount,
|
||||
UnitPrice: unitPrice,
|
||||
Quantity: quantity,
|
||||
BindsByIccid: bindsByIccid,
|
||||
BindRows: bindRows,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) InsertCardPackageBinds(ctx context.Context, tx gdb.TX, rows []do.TSimCardBindPkg) error {
|
||||
if err := s.repo.InsertCardPackageBinds(ctx, tx, rows); err != nil {
|
||||
return gerror.Wrap(err, "写入卡套餐绑定失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSubscribeInput(in *CreatePackageSubscriptionInput) error {
|
||||
if in == nil {
|
||||
return gerror.New("订购参数不能为空")
|
||||
}
|
||||
if strings.TrimSpace(in.BatchNo) == "" {
|
||||
return gerror.New("订购批次号不能为空")
|
||||
}
|
||||
if len(in.Cards) == 0 {
|
||||
return gerror.New("订购卡不能为空")
|
||||
}
|
||||
if in.CustomerAgentId <= 0 {
|
||||
return gerror.New("客户不能为空")
|
||||
}
|
||||
if strings.TrimSpace(in.PackageCode) == "" {
|
||||
return gerror.New("套餐不能为空")
|
||||
}
|
||||
if in.PackageBeginTime == nil {
|
||||
return gerror.New("套餐开始时间不能为空")
|
||||
}
|
||||
if in.DurationMonths <= 0 {
|
||||
return gerror.New("套餐时长必须大于0")
|
||||
}
|
||||
if in.GiftDurationMonths < 0 {
|
||||
return gerror.New("赠送时长不能小于0")
|
||||
}
|
||||
payType := in.PayType
|
||||
if payType == 0 {
|
||||
payType = consts.PayTypeOffline
|
||||
}
|
||||
if payType == consts.PayTypeOffline && !in.OfflineContractCompleted {
|
||||
return gerror.New("请确认线下合同、销售和走账已完成")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) loadTargetDept(ctx context.Context, tx gdb.TX, deptId int64, operator batchimport.OperatorContext, allowDescendant bool) (*entity.SysDept, error) {
|
||||
if operator.DeptId == 0 {
|
||||
return nil, gerror.New("当前用户未绑定机构")
|
||||
}
|
||||
|
||||
customer, err := s.repo.LoadTargetDept(ctx, tx, deptId, operator.TenantId)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询客户/机构失败")
|
||||
}
|
||||
if customer == nil || customer.DeptId == 0 {
|
||||
return nil, gerror.New("客户/机构不存在或不在可操作范围内")
|
||||
}
|
||||
if customer.DeptId == operator.DeptId || customer.ParentId == operator.DeptId {
|
||||
return customer, nil
|
||||
}
|
||||
if allowDescendant && deptAncestorsContain(customer.Ancestors, operator.DeptId) {
|
||||
return customer, nil
|
||||
}
|
||||
if customer.DeptId != operator.DeptId && customer.ParentId != operator.DeptId {
|
||||
return nil, gerror.New("客户/机构不存在或不在可操作范围内")
|
||||
}
|
||||
return customer, nil
|
||||
}
|
||||
|
||||
func deptAncestorsContain(ancestors string, deptId int64) bool {
|
||||
if deptId <= 0 {
|
||||
return false
|
||||
}
|
||||
target := strconv.FormatInt(deptId, 10)
|
||||
for _, item := range strings.Split(strings.Trim(ancestors, ","), ",") {
|
||||
if strings.TrimSpace(item) == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Service) loadPackage(ctx context.Context, tx gdb.TX, packageCode string) (*entity.TSimFlowPackageInfo, error) {
|
||||
pkg, err := s.repo.LoadPackage(ctx, tx, packageCode)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "查询套餐失败")
|
||||
}
|
||||
if pkg == nil {
|
||||
return nil, gerror.New("套餐不存在")
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
func (s *Service) insertOrder(ctx context.Context, tx gdb.TX, in *CreatePackageSubscriptionInput, orderSn string, amount float64, now *gtime.Time) (int64, error) {
|
||||
remark := strings.TrimSpace(in.OrderRemark)
|
||||
if remark == "" {
|
||||
remark = "顶级出库划拨业务订单载体"
|
||||
}
|
||||
payment := normalizeSubscriptionPayment(in, amount, now)
|
||||
orderId, err := s.repo.InsertOrder(ctx, tx, do.TOrder{
|
||||
OrderSn: orderSn,
|
||||
ParentOrderId: 0,
|
||||
ParentOrderSn: "",
|
||||
CustomerType: consts.CustomerTypeAgent,
|
||||
CustomerAgentId: in.CustomerAgentId,
|
||||
EndUserId: 0,
|
||||
AgentId: in.Operator.DeptId,
|
||||
OrderType: orderTypeForBusinessType(in.BusinessType),
|
||||
OrderStatus: consts.OrderStatusCompleted,
|
||||
ShippingStatus: 0,
|
||||
ReturnStatus: 0,
|
||||
Consignee: "",
|
||||
Mobile: "",
|
||||
Email: "",
|
||||
Address: "",
|
||||
BuyerNote: strings.TrimSpace(in.BuyerNote),
|
||||
AdminNote: strings.TrimSpace(in.AdminNote),
|
||||
Remark: remark,
|
||||
ShippingMethod: 3,
|
||||
LogisticsId: 0,
|
||||
LogisticsName: "",
|
||||
ShippingTypeId: 0,
|
||||
ShippingTypeName: "",
|
||||
TrackingNo: "",
|
||||
PayType: payment.PayType,
|
||||
PayStatus: payment.PayStatus,
|
||||
PayOrderSn: "",
|
||||
PayTime: payment.PayTime,
|
||||
TotalAmount: amount,
|
||||
ProductAmount: amount,
|
||||
Balance: payment.BalanceAmount,
|
||||
PaidAmount: amount,
|
||||
OfflinePaidAmount: payment.OfflinePaidAmount,
|
||||
IsNeedCommission: 0,
|
||||
DistributionStatus: 0,
|
||||
TenantId: in.Operator.TenantId,
|
||||
CreatedDept: in.Operator.DeptId,
|
||||
CreatedBy: in.Operator.UserId,
|
||||
CreatedAt: now,
|
||||
UpdatedBy: in.Operator.UserId,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, gerror.Wrap(err, "创建订单失败")
|
||||
}
|
||||
return orderId, nil
|
||||
}
|
||||
|
||||
func orderTypeForBusinessType(businessType int) int {
|
||||
switch businessType {
|
||||
case consts.PackageBusinessTypeRenew:
|
||||
return consts.OrderTypeCardRenew
|
||||
case consts.PackageBusinessTypeChange:
|
||||
return consts.OrderTypeCardChange
|
||||
default:
|
||||
return consts.OrderTypeCardSubscribe
|
||||
}
|
||||
}
|
||||
|
||||
type subscriptionPayment struct {
|
||||
PayType int
|
||||
PayStatus int
|
||||
BalanceAmount float64
|
||||
OfflinePaidAmount float64
|
||||
PayTime *gtime.Time
|
||||
}
|
||||
|
||||
func normalizeSubscriptionPayment(in *CreatePackageSubscriptionInput, amount float64, now *gtime.Time) subscriptionPayment {
|
||||
payment := subscriptionPayment{
|
||||
PayType: consts.PayTypeOffline,
|
||||
PayStatus: consts.PayStatusPaid,
|
||||
OfflinePaidAmount: amount,
|
||||
PayTime: now,
|
||||
}
|
||||
if in == nil {
|
||||
return payment
|
||||
}
|
||||
if in.PayType > 0 {
|
||||
payment.PayType = in.PayType
|
||||
}
|
||||
if in.PayStatus > 0 {
|
||||
payment.PayStatus = in.PayStatus
|
||||
}
|
||||
if in.PayTime != nil {
|
||||
payment.PayTime = in.PayTime
|
||||
}
|
||||
switch payment.PayType {
|
||||
case consts.PayTypeBalance:
|
||||
payment.BalanceAmount = roundAmount(in.BalanceAmount)
|
||||
if payment.BalanceAmount == 0 && amount > 0 {
|
||||
payment.BalanceAmount = amount
|
||||
}
|
||||
payment.OfflinePaidAmount = 0
|
||||
default:
|
||||
payment.OfflinePaidAmount = roundAmount(in.OfflinePaidAmount)
|
||||
if payment.OfflinePaidAmount == 0 && amount > 0 {
|
||||
payment.OfflinePaidAmount = amount
|
||||
}
|
||||
payment.BalanceAmount = roundAmount(in.BalanceAmount)
|
||||
}
|
||||
return payment
|
||||
}
|
||||
|
||||
func (s *Service) insertOrderItem(ctx context.Context, tx gdb.TX, in *CreatePackageSubscriptionInput, orderId int64, orderSn string, pkg *entity.TSimFlowPackageInfo, amount, unitPrice float64, quantity int, beginAt, endAt *gtime.Time, now *gtime.Time) error {
|
||||
durationLabel := strings.TrimSpace(in.DurationDisplayLabel)
|
||||
if durationLabel == "" {
|
||||
durationLabel = fmt.Sprintf("%d个月", in.DurationMonths)
|
||||
}
|
||||
if strings.TrimSpace(in.DurationDisplayLabel) == "" && in.DurationDisplayValue > 0 && strings.TrimSpace(in.DurationUnit) != "" {
|
||||
durationLabel = fmt.Sprintf("%d%s", in.DurationDisplayValue, consts.RenewDurationUnitText(strings.TrimSpace(in.DurationUnit)))
|
||||
}
|
||||
skuData := marshalJSON([]g.Map{
|
||||
{"skuName": "套餐", "skuValue": pkg.PackageName, "packageCode": pkg.PackageCode, "packageType": pkg.PackageType},
|
||||
{"skuName": "数量", "skuValue": fmt.Sprintf("%d", quantity)},
|
||||
{"skuName": "生效时间", "skuValue": formatTime(beginAt)},
|
||||
{"skuName": "时长", "skuValue": durationLabel},
|
||||
})
|
||||
extraSkuData := marshalJSON(g.Map{
|
||||
"outboundBatchNo": in.BatchNo,
|
||||
"businessBatchNo": in.BatchNo,
|
||||
"offlineContractCompleted": in.OfflineContractCompleted,
|
||||
"packageBeginTime": formatTime(beginAt),
|
||||
"packageEndTime": formatTime(endAt),
|
||||
"durationMonths": in.DurationMonths,
|
||||
"giftDurationMonths": in.GiftDurationMonths,
|
||||
"durationValue": in.DurationDisplayValue,
|
||||
"giftDurationValue": in.GiftDurationDisplayValue,
|
||||
"durationLabel": durationLabel,
|
||||
"giftDurationLabel": strings.TrimSpace(in.GiftDurationDisplayLabel),
|
||||
"durationUnit": strings.TrimSpace(in.DurationUnit),
|
||||
"amount": amount,
|
||||
})
|
||||
if err := s.repo.InsertOrderItem(ctx, tx, do.TOrderItem{
|
||||
OrderId: orderId,
|
||||
OrderSn: orderSn,
|
||||
CustomerType: consts.CustomerTypeAgent,
|
||||
CustomerAgentId: in.CustomerAgentId,
|
||||
EndUserId: 0,
|
||||
Price: unitPrice,
|
||||
Quantity: quantity,
|
||||
ProductId: pkg.Id,
|
||||
ProductName: pkg.PackageName,
|
||||
ProductSn: pkg.PackageCode,
|
||||
PicThumb: "",
|
||||
SkuId: 0,
|
||||
SkuData: skuData,
|
||||
DeliveryQuantity: 0,
|
||||
ProductType: consts.ProductTypeCard,
|
||||
IsGift: 0,
|
||||
AgentId: in.Operator.DeptId,
|
||||
PrepayPrice: 0,
|
||||
Commission: "",
|
||||
OriginPrice: unitPrice,
|
||||
ExtraSkuData: extraSkuData,
|
||||
PayMode: consts.PayModeYearMonth,
|
||||
ServiceBeginTime: beginAt,
|
||||
ServiceEndTime: endAt,
|
||||
TenantId: in.Operator.TenantId,
|
||||
CreatedDept: in.Operator.DeptId,
|
||||
CreatedBy: in.Operator.UserId,
|
||||
CreatedAt: now,
|
||||
UpdatedBy: in.Operator.UserId,
|
||||
UpdatedAt: now,
|
||||
}); err != nil {
|
||||
return gerror.Wrap(err, "创建订单项失败")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addMonths(t *gtime.Time, months int) *gtime.Time {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
if months <= 0 {
|
||||
return endOfDayAt(t.Time)
|
||||
}
|
||||
beginAt := t.Time
|
||||
targetMonth := time.Date(
|
||||
beginAt.Year(),
|
||||
beginAt.Month()+time.Month(months),
|
||||
1,
|
||||
beginAt.Hour(),
|
||||
beginAt.Minute(),
|
||||
beginAt.Second(),
|
||||
beginAt.Nanosecond(),
|
||||
beginAt.Location(),
|
||||
)
|
||||
day := beginAt.Day()
|
||||
lastDay := time.Date(
|
||||
targetMonth.Year(),
|
||||
targetMonth.Month()+1,
|
||||
0,
|
||||
beginAt.Hour(),
|
||||
beginAt.Minute(),
|
||||
beginAt.Second(),
|
||||
beginAt.Nanosecond(),
|
||||
beginAt.Location(),
|
||||
).Day()
|
||||
if day > lastDay {
|
||||
day = lastDay
|
||||
}
|
||||
return endOfDayAt(time.Date(
|
||||
targetMonth.Year(),
|
||||
targetMonth.Month(),
|
||||
day,
|
||||
beginAt.Hour(),
|
||||
beginAt.Minute(),
|
||||
beginAt.Second(),
|
||||
beginAt.Nanosecond(),
|
||||
beginAt.Location(),
|
||||
))
|
||||
}
|
||||
|
||||
func endOfDayAt(t time.Time) *gtime.Time {
|
||||
return gtime.NewFromTime(time.Date(
|
||||
t.Year(),
|
||||
t.Month(),
|
||||
t.Day(),
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
0,
|
||||
t.Location(),
|
||||
))
|
||||
}
|
||||
|
||||
func packageEndAt(beginAt *gtime.Time, months int, previousMonthEnd bool) *gtime.Time {
|
||||
endAt := addMonths(beginAt, months)
|
||||
if !previousMonthEnd || endAt == nil {
|
||||
return endAt
|
||||
}
|
||||
return previousMonthEndAt(endAt)
|
||||
}
|
||||
|
||||
func previousMonthEndAt(t *gtime.Time) *gtime.Time {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
tt := t.Time
|
||||
return gtime.NewFromTime(time.Date(tt.Year(), tt.Month(), 0, 23, 59, 59, 0, tt.Location()))
|
||||
}
|
||||
|
||||
func CalcPackageStatus(now, beginAt, endAt *gtime.Time) int {
|
||||
if beginAt != nil && beginAt.After(now) {
|
||||
return consts.PackageStatusPending
|
||||
}
|
||||
if endAt != nil && !endAt.After(now) {
|
||||
return consts.PackageStatusExpired
|
||||
}
|
||||
return consts.PackageStatusEffective
|
||||
}
|
||||
|
||||
func calcPackageStatuses(now, beginAt, endAt *gtime.Time) (packageStatus, effectiveStatus int) {
|
||||
return CalcPackageStatus(now, beginAt, endAt), CalcEffectiveStatus(now, beginAt, endAt)
|
||||
}
|
||||
|
||||
func CalcEffectiveStatus(now, beginAt, endAt *gtime.Time) int {
|
||||
if beginAt != nil && beginAt.After(now) {
|
||||
return consts.EffectiveStatusPending
|
||||
}
|
||||
if endAt != nil && !endAt.After(now) {
|
||||
return consts.EffectiveStatusExpired
|
||||
}
|
||||
return consts.EffectiveStatusEffective
|
||||
}
|
||||
|
||||
func parseAmount(value string) (float64, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return 0, nil
|
||||
}
|
||||
amount, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return 0, gerror.New("订单金额格式不正确")
|
||||
}
|
||||
if amount < 0 {
|
||||
return 0, gerror.New("订单金额不能小于0")
|
||||
}
|
||||
return roundAmount(amount), nil
|
||||
}
|
||||
|
||||
func roundAmount(value float64) float64 {
|
||||
return math.Round(value*100) / 100
|
||||
}
|
||||
|
||||
func marshalJSON(value any) string {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
Reference in New Issue
Block a user