七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
This commit is contained in:
190
internal/exporter/agent_recharge_scene.go
Normal file
190
internal/exporter/agent_recharge_scene.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// AgentRechargeDataSource 代理充值导出数据源。
|
||||
type AgentRechargeDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewAgentRechargeDataSource 创建代理充值导出数据源。
|
||||
func NewAgentRechargeDataSource(db *gorm.DB) *AgentRechargeDataSource {
|
||||
return &AgentRechargeDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *AgentRechargeDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneAgentRecharge
|
||||
}
|
||||
|
||||
// Count 统计代理充值导出行数。
|
||||
func (s *AgentRechargeDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.baseQuery(ctx), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回代理充值导出表头。
|
||||
func (s *AgentRechargeDataSource) Headers(context.Context, ExportParams) ([]string, error) {
|
||||
return []string{
|
||||
"充值单号", "店铺名称", "充值类型", "充值金额(元)", "实付金额(元)", "充值前余额(元)", "充值后余额(元)",
|
||||
"状态", "支付方式", "运营备注", "驳回原因", "创建时间", "支付时间", "完成时间", "提交人", "审批来源", "审批状态", "支付凭证",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询代理充值导出数据。
|
||||
func (s *AgentRechargeDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []agentRechargeExportRow
|
||||
query := s.applyFilters(s.baseQuery(ctx), params).
|
||||
Select(`
|
||||
r.recharge_no,
|
||||
r.amount,
|
||||
r.payment_method,
|
||||
r.status,
|
||||
r.remark,
|
||||
COALESCE(r.rejection_reason, '') AS rejection_reason,
|
||||
r.created_at,
|
||||
r.paid_at,
|
||||
r.completed_at,
|
||||
COALESCE(sh.shop_name, '') AS shop_name,
|
||||
COALESCE(ac.username, '') AS submitter_name,
|
||||
ai.provider AS approval_provider,
|
||||
ai.status AS approval_status,
|
||||
wt.amount AS actual_amount,
|
||||
wt.balance_before,
|
||||
wt.balance_after,
|
||||
COALESCE((SELECT string_agg(voucher.value, ',' ORDER BY voucher.ordinality)
|
||||
FROM jsonb_array_elements_text(COALESCE(r.payment_voucher_key, '[]'::jsonb)) WITH ORDINALITY AS voucher(value, ordinality)), '') AS voucher_keys
|
||||
`).
|
||||
Joins("LEFT JOIN tb_shop AS sh ON sh.id = r.shop_id").
|
||||
Joins("LEFT JOIN tb_account AS ac ON ac.id = r.user_id").
|
||||
Joins("LEFT JOIN tb_approval_instance AS ai ON ai.id = r.approval_instance_id").
|
||||
Joins(`LEFT JOIN LATERAL (
|
||||
SELECT t.amount, t.balance_before, t.balance_after
|
||||
FROM tb_agent_wallet_transaction AS t
|
||||
WHERE t.reference_type = ?
|
||||
AND t.reference_id = r.id
|
||||
AND t.transaction_type = ?
|
||||
AND t.status = ?
|
||||
AND t.deleted_at IS NULL
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1
|
||||
) AS wt ON TRUE`, constants.ReferenceTypeTopup, constants.AgentTransactionTypeRecharge, constants.TransactionStatusSuccess).
|
||||
Order("r.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
item.RechargeNo,
|
||||
item.ShopName,
|
||||
formatRechargeType(item.PaymentMethod),
|
||||
formatMoneyYuan(item.Amount),
|
||||
formatOptionalMoneyYuan(item.ActualAmount),
|
||||
formatOptionalMoneyYuan(item.BalanceBefore),
|
||||
formatOptionalMoneyYuan(item.BalanceAfter),
|
||||
constants.GetRechargeStatusName(item.Status),
|
||||
constants.GetBusinessPaymentMethodName(item.PaymentMethod),
|
||||
item.Remark,
|
||||
item.RejectionReason,
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
formatOptionalTime(item.PaidAt),
|
||||
formatOptionalTime(item.CompletedAt),
|
||||
item.SubmitterName,
|
||||
formatApprovalProvider(item.ApprovalProvider),
|
||||
formatOptionalApprovalStatus(item.ApprovalStatus),
|
||||
item.VoucherKeys,
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *AgentRechargeDataSource) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return s.db.WithContext(ctx).Table("tb_agent_recharge_record AS r").Where("r.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
func (s *AgentRechargeDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "r.shop_id")
|
||||
if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
|
||||
query = query.Where("r.shop_id = ?", shopID)
|
||||
}
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("r.status = ?", status)
|
||||
}
|
||||
if start, ok := filterTime(params.Filters, "start_date"); ok {
|
||||
query = query.Where("r.created_at >= ?", start)
|
||||
}
|
||||
if end, ok := filterEndDate(params.Filters, "end_date"); ok {
|
||||
query = query.Where("r.created_at <= ?", end)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
type agentRechargeExportRow struct {
|
||||
RechargeNo string `gorm:"column:recharge_no"`
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
Amount int64 `gorm:"column:amount"`
|
||||
ActualAmount *int64 `gorm:"column:actual_amount"`
|
||||
BalanceBefore *int64 `gorm:"column:balance_before"`
|
||||
BalanceAfter *int64 `gorm:"column:balance_after"`
|
||||
PaymentMethod string `gorm:"column:payment_method"`
|
||||
Status int `gorm:"column:status"`
|
||||
Remark string `gorm:"column:remark"`
|
||||
RejectionReason string `gorm:"column:rejection_reason"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
PaidAt *time.Time `gorm:"column:paid_at"`
|
||||
CompletedAt *time.Time `gorm:"column:completed_at"`
|
||||
SubmitterName string `gorm:"column:submitter_name"`
|
||||
ApprovalProvider *string `gorm:"column:approval_provider"`
|
||||
ApprovalStatus *int `gorm:"column:approval_status"`
|
||||
VoucherKeys string `gorm:"column:voucher_keys"`
|
||||
}
|
||||
|
||||
func formatRechargeType(paymentMethod string) string {
|
||||
if paymentMethod == constants.RechargeMethodOffline {
|
||||
return "员工线下代充值"
|
||||
}
|
||||
return "在线充值"
|
||||
}
|
||||
|
||||
func formatOptionalMoneyYuan(value *int64) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
return formatMoneyYuan(*value)
|
||||
}
|
||||
|
||||
func formatApprovalProvider(provider *string) string {
|
||||
if provider == nil || *provider == "" {
|
||||
return ""
|
||||
}
|
||||
if *provider == constants.IntegrationProviderWeCom {
|
||||
return "企业微信"
|
||||
}
|
||||
return *provider
|
||||
}
|
||||
|
||||
func formatOptionalApprovalStatus(status *int) string {
|
||||
if status == nil {
|
||||
return ""
|
||||
}
|
||||
return constants.GetApprovalStatusName(*status)
|
||||
}
|
||||
168
internal/exporter/agent_wallet_transaction_scene.go
Normal file
168
internal/exporter/agent_wallet_transaction_scene.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// AgentWalletTransactionDataSource 代理主钱包流水导出数据源。
|
||||
type AgentWalletTransactionDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewAgentWalletTransactionDataSource 创建代理主钱包流水导出数据源。
|
||||
func NewAgentWalletTransactionDataSource(db *gorm.DB) *AgentWalletTransactionDataSource {
|
||||
return &AgentWalletTransactionDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *AgentWalletTransactionDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneAgentWalletTransaction
|
||||
}
|
||||
|
||||
// Count 统计代理主钱包流水导出行数。
|
||||
func (s *AgentWalletTransactionDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.baseQuery(ctx), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回代理主钱包流水导出表头。
|
||||
func (s *AgentWalletTransactionDataSource) Headers(context.Context, ExportParams) ([]string, error) {
|
||||
return []string{
|
||||
"店铺名称", "交易类型", "交易金额(元)", "状态", "资产类型", "资产标识", "交易时间",
|
||||
"交易前金额(元)", "交易后金额(元)", "购买套餐名称", "操作人", "交易ID", "关联业务订单号", "交易渠道/支付方式",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询代理主钱包流水导出数据。
|
||||
func (s *AgentWalletTransactionDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []agentWalletTransactionExportRow
|
||||
query := s.applyFilters(s.baseQuery(ctx), params).
|
||||
Select(`
|
||||
t.id,
|
||||
t.transaction_type,
|
||||
t.amount,
|
||||
t.status,
|
||||
t.asset_type,
|
||||
t.asset_identifier,
|
||||
t.created_at,
|
||||
t.balance_before,
|
||||
t.balance_after,
|
||||
COALESCE(sh.shop_name, '') AS shop_name,
|
||||
COALESCE(items.package_names, t.metadata ->> 'package_name', '') AS package_name,
|
||||
COALESCE(ac.username, '') AS operator_name,
|
||||
COALESCE(
|
||||
CASE t.reference_type
|
||||
WHEN ? THEN o.order_no
|
||||
WHEN ? THEN ar.recharge_no
|
||||
WHEN ? THEN rr.refund_no
|
||||
WHEN ? THEN wr.withdrawal_no
|
||||
WHEN ? THEN ex.exchange_no
|
||||
END,
|
||||
''
|
||||
) AS business_order_no,
|
||||
COALESCE(t.metadata ->> 'payment_method', '') AS payment_method
|
||||
`, constants.ReferenceTypeOrder, constants.ReferenceTypeTopup, constants.ReferenceTypeRefund, constants.ReferenceTypeWithdrawal, constants.ReferenceTypeExchange).
|
||||
Joins("LEFT JOIN tb_shop AS sh ON sh.id = t.shop_id").
|
||||
Joins("LEFT JOIN tb_account AS ac ON ac.id = COALESCE(NULLIF(t.creator, 0), t.user_id)").
|
||||
Joins("LEFT JOIN tb_order AS o ON t.reference_type = ? AND o.id = t.reference_id AND o.deleted_at IS NULL", constants.ReferenceTypeOrder).
|
||||
Joins("LEFT JOIN tb_agent_recharge_record AS ar ON t.reference_type = ? AND ar.id = t.reference_id AND ar.deleted_at IS NULL", constants.ReferenceTypeTopup).
|
||||
Joins("LEFT JOIN tb_refund_request AS rr ON t.reference_type = ? AND rr.id = t.reference_id AND rr.deleted_at IS NULL", constants.ReferenceTypeRefund).
|
||||
Joins("LEFT JOIN tb_commission_withdrawal_request AS wr ON t.reference_type = ? AND wr.id = t.reference_id AND wr.deleted_at IS NULL", constants.ReferenceTypeWithdrawal).
|
||||
Joins("LEFT JOIN tb_exchange_order AS ex ON t.reference_type = ? AND ex.id = t.reference_id AND ex.deleted_at IS NULL", constants.ReferenceTypeExchange).
|
||||
Joins(`LEFT JOIN LATERAL (
|
||||
SELECT string_agg(oi.package_name, ',' ORDER BY oi.id) AS package_names
|
||||
FROM tb_order_item AS oi
|
||||
WHERE t.reference_type = ?
|
||||
AND oi.order_id = t.reference_id
|
||||
AND oi.deleted_at IS NULL
|
||||
) AS items ON TRUE`, constants.ReferenceTypeOrder).
|
||||
Order("t.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
paymentMethod := item.PaymentMethod
|
||||
if paymentMethod == "" && item.TransactionType == constants.AgentTransactionTypeDeduct {
|
||||
paymentMethod = constants.PaymentMethodWallet
|
||||
}
|
||||
rows = append(rows, []string{
|
||||
item.ShopName,
|
||||
constants.GetAgentTransactionTypeName(item.TransactionType),
|
||||
formatMoneyYuan(item.Amount),
|
||||
constants.GetTransactionStatusName(item.Status),
|
||||
constants.GetWalletAssetTypeName(item.AssetType),
|
||||
item.AssetIdentifier,
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
formatMoneyYuan(item.BalanceBefore),
|
||||
formatMoneyYuan(item.BalanceAfter),
|
||||
item.PackageName,
|
||||
item.OperatorName,
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.BusinessOrderNo,
|
||||
constants.GetBusinessPaymentMethodName(paymentMethod),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *AgentWalletTransactionDataSource) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return s.db.WithContext(ctx).
|
||||
Table("tb_agent_wallet_transaction AS t").
|
||||
Joins("INNER JOIN tb_agent_wallet AS w ON w.id = t.agent_wallet_id AND w.wallet_type = ? AND w.deleted_at IS NULL", constants.AgentWalletTypeMain).
|
||||
Where("t.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
func (s *AgentWalletTransactionDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "t.shop_id")
|
||||
if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
|
||||
query = query.Where("t.shop_id = ?", shopID)
|
||||
}
|
||||
if transactionType, ok := filterString(params.Filters, "transaction_type"); ok {
|
||||
query = query.Where("t.transaction_type = ?", transactionType)
|
||||
}
|
||||
if start, ok := filterTime(params.Filters, "start_date"); ok {
|
||||
query = query.Where("t.created_at >= ?", start)
|
||||
}
|
||||
if end, ok := filterEndDate(params.Filters, "end_date"); ok {
|
||||
query = query.Where("t.created_at <= ?", end)
|
||||
}
|
||||
if assetIdentifier, ok := filterString(params.Filters, "asset_identifier"); ok {
|
||||
query = query.Where("t.asset_identifier = ?", assetIdentifier)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
type agentWalletTransactionExportRow struct {
|
||||
ID uint `gorm:"column:id"`
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
TransactionType string `gorm:"column:transaction_type"`
|
||||
Amount int64 `gorm:"column:amount"`
|
||||
Status int `gorm:"column:status"`
|
||||
AssetType string `gorm:"column:asset_type"`
|
||||
AssetIdentifier string `gorm:"column:asset_identifier"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
BalanceBefore int64 `gorm:"column:balance_before"`
|
||||
BalanceAfter int64 `gorm:"column:balance_after"`
|
||||
PackageName string `gorm:"column:package_name"`
|
||||
OperatorName string `gorm:"column:operator_name"`
|
||||
BusinessOrderNo string `gorm:"column:business_order_no"`
|
||||
PaymentMethod string `gorm:"column:payment_method"`
|
||||
}
|
||||
176
internal/exporter/exchange_scene.go
Normal file
176
internal/exporter/exchange_scene.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// ExchangeDataSource 换货记录导出数据源。
|
||||
type ExchangeDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewExchangeDataSource 创建换货记录导出数据源。
|
||||
func NewExchangeDataSource(db *gorm.DB) *ExchangeDataSource {
|
||||
return &ExchangeDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *ExchangeDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneExchange
|
||||
}
|
||||
|
||||
// Count 统计换货记录导出行数。
|
||||
func (s *ExchangeDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.baseQuery(ctx), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回换货记录导出表头。
|
||||
func (s *ExchangeDataSource) Headers(context.Context, ExportParams) ([]string, error) {
|
||||
return []string{
|
||||
"换货单号", "换货类型", "换货原因", "问题描述/备注", "旧资产类型", "旧资产标识符", "新资产标识符",
|
||||
"收货人姓名", "收货人电话", "收货地址", "快递公司", "快递单号", "状态", "创建人", "创建时间",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询换货记录导出数据。
|
||||
func (s *ExchangeDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []exchangeExportRow
|
||||
query := s.applyFilters(s.baseQuery(ctx), params).
|
||||
Select(`
|
||||
e.exchange_no,
|
||||
e.flow_type,
|
||||
e.exchange_reason,
|
||||
COALESCE(e.remark, '') AS remark,
|
||||
e.old_asset_type,
|
||||
e.old_asset_identifier,
|
||||
e.new_asset_identifier,
|
||||
e.recipient_name,
|
||||
e.recipient_phone,
|
||||
e.recipient_address,
|
||||
e.express_company,
|
||||
e.express_no,
|
||||
e.status,
|
||||
e.created_at,
|
||||
COALESCE(ac.username, '') AS creator_name
|
||||
`).
|
||||
Joins("LEFT JOIN tb_account AS ac ON ac.id = e.creator").
|
||||
Order("e.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
item.ExchangeNo,
|
||||
constants.GetExchangeFlowTypeName(item.FlowType),
|
||||
item.ExchangeReason,
|
||||
item.Remark,
|
||||
formatExchangeAssetType(item.OldAssetType),
|
||||
item.OldAssetIdentifier,
|
||||
item.NewAssetIdentifier,
|
||||
item.RecipientName,
|
||||
item.RecipientPhone,
|
||||
item.RecipientAddress,
|
||||
item.ExpressCompany,
|
||||
item.ExpressNo,
|
||||
constants.GetExchangeStatusName(item.Status),
|
||||
item.CreatorName,
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *ExchangeDataSource) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return s.db.WithContext(ctx).Table("tb_exchange_order AS e").Where("e.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
func (s *ExchangeDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "e.shop_id")
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("e.status = ?", status)
|
||||
}
|
||||
if flowType, ok := filterString(params.Filters, "flow_type"); ok {
|
||||
query = query.Where("COALESCE(NULLIF(e.flow_type, ''), ?) = ?", constants.ExchangeFlowTypeShipping, flowType)
|
||||
}
|
||||
query = applyExchangeAssetKeyword(query, "old", filterValue(params.Filters, "old_asset_keyword"))
|
||||
query = applyExchangeAssetKeyword(query, "new", filterValue(params.Filters, "new_asset_keyword"))
|
||||
if start, ok := filterTime(params.Filters, "created_at_start"); ok {
|
||||
query = query.Where("e.created_at >= ?", start)
|
||||
}
|
||||
if end, ok := filterTime(params.Filters, "created_at_end"); ok {
|
||||
query = query.Where("e.created_at <= ?", end)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func applyExchangeAssetKeyword(query *gorm.DB, side, keyword string) *gorm.DB {
|
||||
if keyword == "" {
|
||||
return query
|
||||
}
|
||||
like := "%" + keyword + "%"
|
||||
cardIDs := query.Session(&gorm.Session{NewDB: true}).Table("tb_iot_card").Select("id").
|
||||
Where("deleted_at IS NULL").
|
||||
Where("iccid LIKE ? OR msisdn LIKE ? OR virtual_no LIKE ?", like, like, like)
|
||||
deviceIDs := query.Session(&gorm.Session{NewDB: true}).Table("tb_device").Select("id").
|
||||
Where("deleted_at IS NULL").
|
||||
Where("virtual_no LIKE ? OR imei LIKE ? OR sn LIKE ?", like, like, like)
|
||||
prefix := "e." + side
|
||||
return query.Where(
|
||||
"("+prefix+"_asset_type = ? AND "+prefix+"_asset_id IN (?)) OR ("+prefix+"_asset_type = ? AND "+prefix+"_asset_id IN (?))",
|
||||
constants.ExchangeAssetTypeIotCard, cardIDs, constants.ExchangeAssetTypeDevice, deviceIDs,
|
||||
)
|
||||
}
|
||||
|
||||
func filterValue(filters map[string]any, key string) string {
|
||||
value, _ := filterString(filters, key)
|
||||
return value
|
||||
}
|
||||
|
||||
func formatExchangeAssetType(assetType string) string {
|
||||
switch assetType {
|
||||
case constants.ExchangeAssetTypeIotCard:
|
||||
return "物联网卡"
|
||||
case constants.ExchangeAssetTypeDevice:
|
||||
return "设备"
|
||||
case "":
|
||||
return ""
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
type exchangeExportRow struct {
|
||||
ExchangeNo string `gorm:"column:exchange_no"`
|
||||
FlowType string `gorm:"column:flow_type"`
|
||||
ExchangeReason string `gorm:"column:exchange_reason"`
|
||||
Remark string `gorm:"column:remark"`
|
||||
OldAssetType string `gorm:"column:old_asset_type"`
|
||||
OldAssetIdentifier string `gorm:"column:old_asset_identifier"`
|
||||
NewAssetIdentifier string `gorm:"column:new_asset_identifier"`
|
||||
RecipientName string `gorm:"column:recipient_name"`
|
||||
RecipientPhone string `gorm:"column:recipient_phone"`
|
||||
RecipientAddress string `gorm:"column:recipient_address"`
|
||||
ExpressCompany string `gorm:"column:express_company"`
|
||||
ExpressNo string `gorm:"column:express_no"`
|
||||
Status int `gorm:"column:status"`
|
||||
CreatorName string `gorm:"column:creator_name"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
}
|
||||
@@ -255,6 +255,18 @@ func filterTime(filters map[string]any, key string) (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// filterEndDate 解析截止时间;纯日期输入覆盖到当天结束,带时分秒输入保持原值。
|
||||
func filterEndDate(filters map[string]any, key string) (time.Time, bool) {
|
||||
text, ok := filterString(filters, key)
|
||||
if !ok {
|
||||
return time.Time{}, false
|
||||
}
|
||||
if parsed, err := time.ParseInLocation("2006-01-02", text, time.Local); err == nil {
|
||||
return parsed.Add(24*time.Hour - time.Nanosecond), true
|
||||
}
|
||||
return filterTime(filters, key)
|
||||
}
|
||||
|
||||
func formatOptionalUint(value *uint) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
|
||||
@@ -2,6 +2,7 @@ package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -36,7 +37,7 @@ func (s *IotCardDataSource) Count(ctx context.Context, params ExportParams) (int
|
||||
|
||||
// Headers 返回 IoT 卡导出表头。
|
||||
func (s *IotCardDataSource) Headers(ctx context.Context, params ExportParams) ([]string, error) {
|
||||
return []string{"ICCID", "MSISDN", "绑定设备虚拟号", "运营商", "店铺名称", "绑定设备名称", "是否实名", "实名时间", "网络状态"}, nil
|
||||
return []string{"ICCID", "MSISDN", "绑定设备虚拟号", "运营商", "店铺名称", "绑定设备名称", "是否实名", "实名时间", "网络状态", "套餐名称", "使用流量(MB)", "剩余流量(MB)"}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询 IoT 卡导出数据。
|
||||
@@ -56,7 +57,10 @@ func (s *IotCardDataSource) Fetch(ctx context.Context, params ExportParams, offs
|
||||
COALESCE(d.device_name, '') AS device_name,
|
||||
c.real_name_status,
|
||||
c.first_realname_at,
|
||||
c.network_status
|
||||
c.network_status,
|
||||
COALESCE(pkg.package_name, '') AS package_name,
|
||||
COALESCE(pkg.data_usage_mb, 0) AS data_usage_mb,
|
||||
COALESCE(pkg.data_limit_mb, 0) AS data_limit_mb
|
||||
`).
|
||||
Order("c.id ASC").
|
||||
Limit(limit).
|
||||
@@ -77,6 +81,9 @@ func (s *IotCardDataSource) Fetch(ctx context.Context, params ExportParams, offs
|
||||
formatRealNameVerified(item.RealNameStatus),
|
||||
formatOptionalTime(item.FirstRealnameAt),
|
||||
constants.GetNetworkStatusName(item.NetworkStatus),
|
||||
item.PackageName,
|
||||
strconv.FormatInt(item.DataUsageMB, 10),
|
||||
strconv.FormatInt(remainingPackageDataMB(item.DataLimitMB, item.DataUsageMB), 10),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
@@ -100,6 +107,22 @@ func (s *IotCardDataSource) baseQuery(ctx context.Context) *gorm.DB {
|
||||
LIMIT 1
|
||||
) AS d ON TRUE
|
||||
`, constants.BindStatusBound).
|
||||
Joins(`
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
COALESCE(NULLIF(pu.package_name, ''), p.package_name, '') AS package_name,
|
||||
pu.data_usage_mb,
|
||||
pu.data_limit_mb
|
||||
FROM tb_package_usage AS pu
|
||||
LEFT JOIN tb_package AS p ON p.id = pu.package_id AND p.deleted_at IS NULL
|
||||
WHERE pu.iot_card_id = c.id
|
||||
AND pu.status = ?
|
||||
AND pu.master_usage_id IS NULL
|
||||
AND pu.deleted_at IS NULL
|
||||
ORDER BY pu.priority ASC, pu.activated_at ASC, pu.id ASC
|
||||
LIMIT 1
|
||||
) AS pkg ON TRUE
|
||||
`, constants.PackageUsageStatusActive).
|
||||
Where("c.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
@@ -206,6 +229,17 @@ type iotCardExportRow struct {
|
||||
RealNameStatus int `gorm:"column:real_name_status"`
|
||||
FirstRealnameAt *time.Time `gorm:"column:first_realname_at"`
|
||||
NetworkStatus int `gorm:"column:network_status"`
|
||||
PackageName string `gorm:"column:package_name"`
|
||||
DataUsageMB int64 `gorm:"column:data_usage_mb"`
|
||||
DataLimitMB int64 `gorm:"column:data_limit_mb"`
|
||||
}
|
||||
|
||||
func remainingPackageDataMB(limit, used int64) int64 {
|
||||
remaining := limit - used
|
||||
if remaining < 0 {
|
||||
return 0
|
||||
}
|
||||
return remaining
|
||||
}
|
||||
|
||||
func formatRealNameVerified(status int) string {
|
||||
|
||||
228
internal/exporter/package_scene.go
Normal file
228
internal/exporter/package_scene.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// PackageDataSource 套餐导出数据源。
|
||||
type PackageDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewPackageDataSource 创建套餐导出数据源。
|
||||
func NewPackageDataSource(db *gorm.DB) *PackageDataSource {
|
||||
return &PackageDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *PackageDataSource) Scene() string {
|
||||
return constants.ExportTaskScenePackage
|
||||
}
|
||||
|
||||
// Count 统计套餐导出行数。
|
||||
func (s *PackageDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.baseQuery(ctx, params), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回套餐导出固定表头。
|
||||
func (s *PackageDataSource) Headers(context.Context, ExportParams) ([]string, error) {
|
||||
return []string{
|
||||
"套餐编码", "套餐名称", "套餐系列名称", "套餐类型", "套餐时长(月)",
|
||||
"套餐时长说明", "套餐周期类型", "套餐天数", "真流量额度(MB)", "虚流量额度(MB)",
|
||||
"是否启用虚流量", "虚流量比例", "流量重置周期", "到期时间基准", "成本价(元)",
|
||||
"建议售价(元)", "价格配置状态", "状态", "上架状态", "是否赠送套餐",
|
||||
"创建人ID", "更新人ID", "创建时间", "更新时间", "删除时间",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询套餐导出数据。
|
||||
func (s *PackageDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []packageExportRow
|
||||
query := s.applyFilters(s.baseQuery(ctx, params), params).
|
||||
Joins("LEFT JOIN tb_package_series AS ps ON ps.id = p.series_id AND ps.deleted_at IS NULL").
|
||||
Select(s.selectColumns(params)).
|
||||
Order("p.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, buildPackageExportRow(item))
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *PackageDataSource) baseQuery(ctx context.Context, params ExportParams) *gorm.DB {
|
||||
query := s.db.WithContext(ctx).Table("tb_package AS p").Where("p.deleted_at IS NULL")
|
||||
switch params.UserType {
|
||||
case constants.UserTypeSuperAdmin, constants.UserTypePlatform:
|
||||
return query
|
||||
case constants.UserTypeAgent:
|
||||
if params.CreatorShopID == nil || *params.CreatorShopID == 0 {
|
||||
return query.Where("1 = 0")
|
||||
}
|
||||
return query.
|
||||
Joins(`INNER JOIN tb_shop_package_allocation AS a
|
||||
ON a.package_id = p.id
|
||||
AND a.shop_id = ?
|
||||
AND a.status = ?
|
||||
AND a.deleted_at IS NULL`, *params.CreatorShopID, constants.StatusEnabled).
|
||||
Where("p.is_gift = ?", false)
|
||||
default:
|
||||
return query.Where("1 = 0")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PackageDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
if packageName, ok := filterString(params.Filters, "package_name"); ok {
|
||||
query = query.Where("p.package_name LIKE ?", "%"+packageName+"%")
|
||||
}
|
||||
if seriesID, ok := filterUint(params.Filters, "series_id"); ok {
|
||||
query = query.Where("p.series_id = ?", seriesID)
|
||||
}
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("p.status = ?", status)
|
||||
}
|
||||
if shelfStatus, ok := filterInt(params.Filters, "shelf_status"); ok {
|
||||
if params.UserType == constants.UserTypeAgent {
|
||||
query = query.Where("a.shelf_status = ?", shelfStatus)
|
||||
} else {
|
||||
query = query.Where("p.shelf_status = ?", shelfStatus)
|
||||
}
|
||||
}
|
||||
if packageType, ok := filterString(params.Filters, "package_type"); ok {
|
||||
query = query.Where("p.package_type = ?", packageType)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (s *PackageDataSource) selectColumns(params ExportParams) string {
|
||||
costPriceColumn := "p.cost_price"
|
||||
shelfStatusColumn := "p.shelf_status"
|
||||
if params.UserType == constants.UserTypeAgent {
|
||||
costPriceColumn = "a.cost_price"
|
||||
shelfStatusColumn = "a.shelf_status"
|
||||
}
|
||||
return `
|
||||
p.package_code,
|
||||
p.package_name,
|
||||
COALESCE(ps.series_name, '') AS series_name,
|
||||
p.package_type,
|
||||
p.duration_months,
|
||||
p.calendar_type,
|
||||
p.duration_days,
|
||||
p.real_data_mb,
|
||||
p.virtual_data_mb,
|
||||
p.enable_virtual_data,
|
||||
p.virtual_ratio,
|
||||
p.data_reset_cycle,
|
||||
p.expiry_base,
|
||||
` + costPriceColumn + ` AS cost_price,
|
||||
p.suggested_retail_price,
|
||||
p.price_config_status,
|
||||
p.status,
|
||||
` + shelfStatusColumn + ` AS shelf_status,
|
||||
p.is_gift,
|
||||
p.creator,
|
||||
p.updater,
|
||||
p.created_at,
|
||||
p.updated_at`
|
||||
}
|
||||
|
||||
type packageExportRow struct {
|
||||
PackageCode string `gorm:"column:package_code"`
|
||||
PackageName string `gorm:"column:package_name"`
|
||||
SeriesName string `gorm:"column:series_name"`
|
||||
PackageType string `gorm:"column:package_type"`
|
||||
DurationMonths int `gorm:"column:duration_months"`
|
||||
CalendarType string `gorm:"column:calendar_type"`
|
||||
DurationDays int `gorm:"column:duration_days"`
|
||||
RealDataMB int64 `gorm:"column:real_data_mb"`
|
||||
VirtualDataMB int64 `gorm:"column:virtual_data_mb"`
|
||||
EnableVirtualData bool `gorm:"column:enable_virtual_data"`
|
||||
VirtualRatio float64 `gorm:"column:virtual_ratio"`
|
||||
DataResetCycle string `gorm:"column:data_reset_cycle"`
|
||||
ExpiryBase string `gorm:"column:expiry_base"`
|
||||
CostPrice int64 `gorm:"column:cost_price"`
|
||||
SuggestedRetailPrice int64 `gorm:"column:suggested_retail_price"`
|
||||
PriceConfigStatus int `gorm:"column:price_config_status"`
|
||||
Status int `gorm:"column:status"`
|
||||
ShelfStatus int `gorm:"column:shelf_status"`
|
||||
IsGift bool `gorm:"column:is_gift"`
|
||||
Creator uint `gorm:"column:creator"`
|
||||
Updater uint `gorm:"column:updater"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at"`
|
||||
}
|
||||
|
||||
func buildPackageExportRow(item packageExportRow) []string {
|
||||
return []string{
|
||||
item.PackageCode,
|
||||
item.PackageName,
|
||||
item.SeriesName,
|
||||
constants.GetPackageTypeName(item.PackageType),
|
||||
strconv.Itoa(item.DurationMonths),
|
||||
formatPackageDurationDescription(item),
|
||||
constants.GetPackageCalendarTypeName(item.CalendarType),
|
||||
strconv.Itoa(item.DurationDays),
|
||||
strconv.FormatInt(item.RealDataMB, 10),
|
||||
strconv.FormatInt(item.VirtualDataMB, 10),
|
||||
formatYesNo(item.EnableVirtualData),
|
||||
strconv.FormatFloat(item.VirtualRatio, 'f', 6, 64),
|
||||
constants.GetPackageDataResetCycleName(item.DataResetCycle),
|
||||
constants.GetPackageExpiryBaseName(item.ExpiryBase),
|
||||
formatMoneyYuan(item.CostPrice),
|
||||
formatPackageSuggestedRetailPrice(item),
|
||||
constants.GetPackagePriceConfigStatusName(item.PriceConfigStatus),
|
||||
constants.GetStatusName(item.Status),
|
||||
constants.GetShelfStatusName(item.ShelfStatus),
|
||||
formatYesNo(item.IsGift),
|
||||
strconv.FormatUint(uint64(item.Creator), 10),
|
||||
strconv.FormatUint(uint64(item.Updater), 10),
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
item.UpdatedAt.Format(exportTimeLayout),
|
||||
"",
|
||||
}
|
||||
}
|
||||
|
||||
func formatPackageDurationDescription(item packageExportRow) string {
|
||||
if item.CalendarType == constants.PackageCalendarTypeByDay && item.DurationDays > 0 {
|
||||
return strconv.Itoa(item.DurationDays) + "天"
|
||||
}
|
||||
if item.DurationMonths > 0 {
|
||||
return strconv.Itoa(item.DurationMonths) + "个月"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func formatPackageSuggestedRetailPrice(item packageExportRow) string {
|
||||
if item.PriceConfigStatus == constants.PackagePriceConfigStatusUnconfigured {
|
||||
return ""
|
||||
}
|
||||
return formatMoneyYuan(item.SuggestedRetailPrice)
|
||||
}
|
||||
|
||||
func formatYesNo(value bool) string {
|
||||
if value {
|
||||
return "是"
|
||||
}
|
||||
return "否"
|
||||
}
|
||||
206
internal/exporter/refund_scene.go
Normal file
206
internal/exporter/refund_scene.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// RefundDataSource 退款记录导出数据源。
|
||||
type RefundDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewRefundDataSource 创建退款记录导出数据源。
|
||||
func NewRefundDataSource(db *gorm.DB) *RefundDataSource {
|
||||
return &RefundDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *RefundDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneRefund
|
||||
}
|
||||
|
||||
// Count 统计退款记录导出行数。
|
||||
func (s *RefundDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.baseQuery(ctx), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回退款记录导出表头。
|
||||
func (s *RefundDataSource) Headers(context.Context, ExportParams) ([]string, error) {
|
||||
return []string{
|
||||
"退款单号", "代理店铺名称", "关联的支付订单号", "资产类型", "资产标识", "套餐名称", "原订单金额(元)",
|
||||
"实收金额(元)", "可退金额(元)", "申请退款金额(元)", "实际退款金额(元)", "状态", "退款原因", "审批备注",
|
||||
"审批来源", "审批状态", "退款处理状态", "退款申请时间", "退款审批时间", "提交人", "退款凭证",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询退款记录导出数据。
|
||||
func (s *RefundDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []refundExportRow
|
||||
query := s.applyFilters(s.baseQuery(ctx), params).
|
||||
Select(`
|
||||
r.refund_no,
|
||||
r.order_no,
|
||||
r.order_type,
|
||||
r.asset_identifier,
|
||||
r.actual_received_amount,
|
||||
r.requested_refund_amount,
|
||||
r.approved_refund_amount,
|
||||
r.status,
|
||||
r.refund_reason,
|
||||
r.remark,
|
||||
r.commission_deducted,
|
||||
r.asset_reset,
|
||||
r.created_at,
|
||||
r.processed_at,
|
||||
COALESCE(sh.shop_name, '') AS shop_name,
|
||||
o.total_amount AS original_amount,
|
||||
o.actual_paid_amount AS refundable_amount,
|
||||
COALESCE(pu.package_name, items.package_names, '') AS package_name,
|
||||
COALESCE(ac.username, '') AS submitter_name,
|
||||
ai.provider AS approval_provider,
|
||||
ai.status AS approval_status,
|
||||
COALESCE((SELECT string_agg(voucher.value, ',' ORDER BY voucher.ordinality)
|
||||
FROM jsonb_array_elements_text(COALESCE(r.refund_voucher_key, '[]'::jsonb)) WITH ORDINALITY AS voucher(value, ordinality)), '') AS voucher_keys
|
||||
`).
|
||||
Joins("LEFT JOIN tb_shop AS sh ON sh.id = r.shop_id").
|
||||
Joins("LEFT JOIN tb_order AS o ON o.id = r.order_id AND o.deleted_at IS NULL").
|
||||
Joins("LEFT JOIN tb_package_usage AS pu ON pu.id = r.package_usage_id AND pu.deleted_at IS NULL").
|
||||
Joins("LEFT JOIN tb_account AS ac ON ac.id = r.creator").
|
||||
Joins("LEFT JOIN tb_approval_instance AS ai ON ai.id = r.approval_instance_id").
|
||||
Joins(`LEFT JOIN LATERAL (
|
||||
SELECT string_agg(oi.package_name, ',' ORDER BY oi.id) AS package_names
|
||||
FROM tb_order_item AS oi
|
||||
WHERE oi.order_id = r.order_id AND oi.deleted_at IS NULL
|
||||
) AS items ON TRUE`).
|
||||
Order("r.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
item.RefundNo,
|
||||
item.ShopName,
|
||||
item.OrderNo,
|
||||
formatRefundAssetType(item.OrderType),
|
||||
item.AssetIdentifier,
|
||||
item.PackageName,
|
||||
formatOptionalMoneyYuan(item.OriginalAmount),
|
||||
formatMoneyYuan(item.ActualReceivedAmount),
|
||||
formatOptionalMoneyYuan(item.RefundableAmount),
|
||||
formatMoneyYuan(item.RequestedRefundAmount),
|
||||
formatOptionalMoneyYuan(item.ApprovedRefundAmount),
|
||||
constants.GetRefundStatusName(item.Status),
|
||||
item.RefundReason,
|
||||
item.Remark,
|
||||
formatRefundApprovalSource(item.ApprovalProvider),
|
||||
formatOptionalApprovalStatus(item.ApprovalStatus),
|
||||
formatRefundProcessingStatus(item.Status, item.CommissionDeducted, item.AssetReset),
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
formatOptionalTime(item.ProcessedAt),
|
||||
item.SubmitterName,
|
||||
item.VoucherKeys,
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *RefundDataSource) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return s.db.WithContext(ctx).Table("tb_refund_request AS r").Where("r.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
func (s *RefundDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "r.shop_id")
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("r.status = ?", status)
|
||||
}
|
||||
if orderID, ok := filterUint(params.Filters, "order_id"); ok {
|
||||
query = query.Where("r.order_id = ?", orderID)
|
||||
}
|
||||
if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
|
||||
query = query.Where("r.shop_id = ?", shopID)
|
||||
}
|
||||
if identifier, ok := filterString(params.Filters, "asset_identifier"); ok {
|
||||
query = query.Where("r.asset_identifier = ?", identifier)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
type refundExportRow struct {
|
||||
RefundNo string `gorm:"column:refund_no"`
|
||||
ShopName string `gorm:"column:shop_name"`
|
||||
OrderNo string `gorm:"column:order_no"`
|
||||
OrderType string `gorm:"column:order_type"`
|
||||
AssetIdentifier string `gorm:"column:asset_identifier"`
|
||||
PackageName string `gorm:"column:package_name"`
|
||||
OriginalAmount *int64 `gorm:"column:original_amount"`
|
||||
ActualReceivedAmount int64 `gorm:"column:actual_received_amount"`
|
||||
RefundableAmount *int64 `gorm:"column:refundable_amount"`
|
||||
RequestedRefundAmount int64 `gorm:"column:requested_refund_amount"`
|
||||
ApprovedRefundAmount *int64 `gorm:"column:approved_refund_amount"`
|
||||
Status int `gorm:"column:status"`
|
||||
RefundReason string `gorm:"column:refund_reason"`
|
||||
Remark string `gorm:"column:remark"`
|
||||
ApprovalProvider *string `gorm:"column:approval_provider"`
|
||||
ApprovalStatus *int `gorm:"column:approval_status"`
|
||||
CommissionDeducted bool `gorm:"column:commission_deducted"`
|
||||
AssetReset bool `gorm:"column:asset_reset"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
ProcessedAt *time.Time `gorm:"column:processed_at"`
|
||||
SubmitterName string `gorm:"column:submitter_name"`
|
||||
VoucherKeys string `gorm:"column:voucher_keys"`
|
||||
}
|
||||
|
||||
func formatRefundAssetType(orderType string) string {
|
||||
switch orderType {
|
||||
case model.OrderTypeSingleCard:
|
||||
return "物联网卡"
|
||||
case model.OrderTypeDevice:
|
||||
return "设备"
|
||||
case "":
|
||||
return ""
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
func formatRefundApprovalSource(provider *string) string {
|
||||
if provider == nil || *provider == "" {
|
||||
return "历史审批"
|
||||
}
|
||||
return formatApprovalProvider(provider)
|
||||
}
|
||||
|
||||
func formatRefundProcessingStatus(status int, commissionDeducted, assetReset bool) string {
|
||||
switch status {
|
||||
case model.RefundStatusPending, model.RefundStatusReturned:
|
||||
return "待审批"
|
||||
case model.RefundStatusRejected:
|
||||
return "无需处理"
|
||||
case model.RefundStatusApproved:
|
||||
if commissionDeducted && assetReset {
|
||||
return "已完成"
|
||||
}
|
||||
return "处理中"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,11 @@ func NewDefaultRegistry(db *gorm.DB) *Registry {
|
||||
NewDeviceDataSource(db),
|
||||
NewIotCardDataSource(db),
|
||||
NewOrderDataSource(db),
|
||||
NewPackageDataSource(db),
|
||||
NewAgentWalletTransactionDataSource(db),
|
||||
NewAgentRechargeDataSource(db),
|
||||
NewRefundDataSource(db),
|
||||
NewExchangeDataSource(db),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,7 +64,14 @@ func (r *Registry) Scenes() []string {
|
||||
// IsSupportedScene 判断是否为受支持的场景。
|
||||
func IsSupportedScene(scene string) bool {
|
||||
switch scene {
|
||||
case constants.ExportTaskSceneDevice, constants.ExportTaskSceneIotCard, constants.ExportTaskSceneOrder:
|
||||
case constants.ExportTaskSceneDevice,
|
||||
constants.ExportTaskSceneIotCard,
|
||||
constants.ExportTaskSceneOrder,
|
||||
constants.ExportTaskScenePackage,
|
||||
constants.ExportTaskSceneAgentWalletTransaction,
|
||||
constants.ExportTaskSceneAgentRecharge,
|
||||
constants.ExportTaskSceneRefund,
|
||||
constants.ExportTaskSceneExchange:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user