chore(init): 初始化解忧商行项目并完成 Phase 0 基座
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
package adminfinance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"hfb_sys/backend/internal/auditlog"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDependencyUnavailable = errors.New("dependency unavailable")
|
||||
ErrInvalidManualDisbursement = errors.New("invalid manual disbursement")
|
||||
ErrManualDisbursementNotFound = errors.New("manual disbursement not found")
|
||||
ErrManualDisbursementNotPaid = errors.New("manual disbursement is not paid")
|
||||
ErrInvalidOperatingExpense = errors.New("invalid operating expense")
|
||||
ErrOperatingExpenseNotFound = errors.New("operating expense not found")
|
||||
ErrOperatingExpenseNotPaid = errors.New("operating expense is not paid")
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo *Repository
|
||||
}
|
||||
|
||||
func NewService(repo *Repository) *Service {
|
||||
return &Service{repo: repo}
|
||||
}
|
||||
|
||||
func (s *Service) Dashboard(ctx context.Context, query DashboardQuery) (*DashboardDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.Dashboard(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) Details(ctx context.Context, query DetailQuery) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if query.Page < 1 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize < 1 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
if query.PageSize > 100 {
|
||||
query.PageSize = 100
|
||||
}
|
||||
return s.repo.Details(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) Disbursements(ctx context.Context, query DisbursementQuery) (*DisbursementListDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if query.Page < 1 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize < 1 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
if query.PageSize > 100 {
|
||||
query.PageSize = 100
|
||||
}
|
||||
return s.repo.Disbursements(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) OperatingExpenses(ctx context.Context, query OperatingExpenseQuery) (*OperatingExpenseListDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if query.Page < 1 {
|
||||
query.Page = 1
|
||||
}
|
||||
if query.PageSize < 1 {
|
||||
query.PageSize = 20
|
||||
}
|
||||
if query.PageSize > 100 {
|
||||
query.PageSize = 100
|
||||
}
|
||||
return s.repo.OperatingExpenses(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) CreateManualDisbursement(
|
||||
ctx context.Context,
|
||||
req CreateManualDisbursementRequest,
|
||||
adminID uint64,
|
||||
meta auditlog.Meta,
|
||||
) (*ManualDisbursementDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
req.Category = strings.TrimSpace(req.Category)
|
||||
req.CustomCategoryName = strings.TrimSpace(req.CustomCategoryName)
|
||||
req.PayeeName = strings.TrimSpace(req.PayeeName)
|
||||
req.Remark = strings.TrimSpace(req.Remark)
|
||||
req.VoucherURL = strings.TrimSpace(req.VoucherURL)
|
||||
if !validManualDisbursementCategory(req.Category) ||
|
||||
!validManualDisbursementCustomCategoryName(req.Category, req.CustomCategoryName) ||
|
||||
utf8.RuneCountInString(req.PayeeName) < 1 || utf8.RuneCountInString(req.PayeeName) > 100 ||
|
||||
req.AmountCent <= 0 || req.PaidAt.IsZero() ||
|
||||
utf8.RuneCountInString(req.Remark) < 1 || utf8.RuneCountInString(req.Remark) > 500 ||
|
||||
!validManualVoucherURL(req.VoucherURL) {
|
||||
return nil, ErrInvalidManualDisbursement
|
||||
}
|
||||
return s.repo.CreateManualDisbursement(ctx, req, adminID, meta)
|
||||
}
|
||||
|
||||
func (s *Service) VoidManualDisbursement(
|
||||
ctx context.Context,
|
||||
id uint64,
|
||||
reason string,
|
||||
adminID uint64,
|
||||
meta auditlog.Meta,
|
||||
) (*ManualDisbursementDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
reason = strings.TrimSpace(reason)
|
||||
if id == 0 || utf8.RuneCountInString(reason) < 1 || utf8.RuneCountInString(reason) > 255 {
|
||||
return nil, ErrInvalidManualDisbursement
|
||||
}
|
||||
return s.repo.VoidManualDisbursement(ctx, id, reason, adminID, meta)
|
||||
}
|
||||
|
||||
func (s *Service) CreateOperatingExpense(ctx context.Context, req CreateOperatingExpenseRequest, adminID uint64, meta auditlog.Meta) (*OperatingExpenseDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
req.Category = strings.TrimSpace(req.Category)
|
||||
req.PayeeName = strings.TrimSpace(req.PayeeName)
|
||||
req.Remark = strings.TrimSpace(req.Remark)
|
||||
req.VoucherURL = strings.TrimSpace(req.VoucherURL)
|
||||
if utf8.RuneCountInString(req.Category) < 1 || utf8.RuneCountInString(req.Category) > 50 ||
|
||||
utf8.RuneCountInString(req.PayeeName) < 1 || utf8.RuneCountInString(req.PayeeName) > 100 ||
|
||||
req.AmountCent <= 0 || req.OccurredAt.IsZero() ||
|
||||
utf8.RuneCountInString(req.Remark) < 1 || utf8.RuneCountInString(req.Remark) > 500 ||
|
||||
!validManualVoucherURL(req.VoucherURL) {
|
||||
return nil, ErrInvalidOperatingExpense
|
||||
}
|
||||
return s.repo.CreateOperatingExpense(ctx, req, adminID, meta)
|
||||
}
|
||||
|
||||
func (s *Service) VoidOperatingExpense(ctx context.Context, id uint64, reason string, adminID uint64, meta auditlog.Meta) (*OperatingExpenseDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
reason = strings.TrimSpace(reason)
|
||||
if id == 0 || utf8.RuneCountInString(reason) < 1 || utf8.RuneCountInString(reason) > 255 {
|
||||
return nil, ErrInvalidOperatingExpense
|
||||
}
|
||||
return s.repo.VoidOperatingExpense(ctx, id, reason, adminID, meta)
|
||||
}
|
||||
|
||||
func validManualDisbursementCategory(category string) bool {
|
||||
switch category {
|
||||
case "user_compensation", "seller_supplement", "operating_expense", "channel_fee", "duoduo_deposit_refund", "other", "custom":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validManualDisbursementCustomCategoryName(category, name string) bool {
|
||||
if category != "custom" {
|
||||
return name == ""
|
||||
}
|
||||
return utf8.RuneCountInString(name) >= 1 && utf8.RuneCountInString(name) <= 50
|
||||
}
|
||||
|
||||
func validManualVoucherURL(raw string) bool {
|
||||
if raw == "" {
|
||||
return true
|
||||
}
|
||||
if len(raw) > 500 {
|
||||
return false
|
||||
}
|
||||
parsed, err := url.Parse(raw)
|
||||
if err != nil || parsed.IsAbs() || parsed.Path != "/api/files/object" {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(parsed.Query().Get("key"), "manual-disbursement/")
|
||||
}
|
||||
Reference in New Issue
Block a user