Files
junhong_cmp_fiber/internal/query/employeecollection/payment_method.go
break ce24d5612e
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
feat(员工代收款): 新增员工代收款账单闭环
- 新增 6 张表与成对迁移 000212,扩展企业微信审批场景业务类型白名单
- 后台线下套餐订单与两条代理线下充值入账路径在来源成功事务内建账,来源唯一键幂等
- 核销申请、审批尝试记录、账单分摊预占与驳回重提,审批业务类型 employee_collection_approval
- 企业微信终态消费幂等:通过转已核销、驳回释放预占、通过后撤销不回滚并转异常终态
- 退款成功事务内按 bill_id+refund_id 幂等冲销账单或仅写退款关联提示
- 线下收款方式字典、账单查询/统计/关闭、申请查询与代办权限,均写入事务内审计

OpenSpec Change: add-employee-collection-bills
2026-09-10 18:24:05 +08:00

100 lines
3.8 KiB
Go

// Package employeecollection 提供员工代收款账单与线下收款方式字典的只读投影。
// Query 只做权限范围、筛选、分页与 DTO 投影,不修改任何状态。
package employeecollection
import (
"context"
"strings"
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
)
// PaymentMethodQuery 查询线下收款方式字典。
type PaymentMethodQuery struct {
db *gorm.DB
}
// NewPaymentMethodQuery 创建线下收款方式字典查询。
func NewPaymentMethodQuery(db *gorm.DB) *PaymentMethodQuery {
return &PaymentMethodQuery{db: db}
}
// List 分页查询线下收款方式。
// 超级管理员可查询全部字典项;其他后台账号仅返回启用项,保证停用方式不可用于新申请。
func (q *PaymentMethodQuery) List(
ctx context.Context,
request dto.EmployeeCollectionPaymentMethodListRequest,
) (*dto.EmployeeCollectionPaymentMethodListResponse, error) {
if q == nil || q.db == nil {
return nil, errors.New(errors.CodeServiceUnavailable, "线下收款方式查询尚未配置")
}
isSuperAdmin := middleware.GetUserTypeFromContext(ctx) == constants.UserTypeSuperAdmin
if !isSuperAdmin && middleware.GetUserIDFromContext(ctx) == 0 {
return nil, errors.New(errors.CodeUnauthorized)
}
page, pageSize := normalizePage(request.Page, request.PageSize)
query := q.db.WithContext(ctx).Model(&model.EmployeeCollectionPaymentMethod{})
if !isSuperAdmin {
query = query.Where("status = ?", constants.EmployeeCollectionPaymentMethodStatusEnabled)
} else if request.Enabled != nil {
status := constants.EmployeeCollectionPaymentMethodStatusDisabled
if *request.Enabled {
status = constants.EmployeeCollectionPaymentMethodStatusEnabled
}
query = query.Where("status = ?", status)
}
if keyword := strings.TrimSpace(request.Keyword); keyword != "" {
pattern := "%" + keyword + "%"
query = query.Where("code LIKE ? OR name LIKE ?", pattern, pattern)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询线下收款方式总数失败")
}
var items []model.EmployeeCollectionPaymentMethod
if err := query.Order("sort_order ASC, id ASC").
Offset((page - 1) * pageSize).Limit(pageSize).
Find(&items).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询线下收款方式列表失败")
}
list := make([]*dto.EmployeeCollectionPaymentMethodResponse, 0, len(items))
for index := range items {
list = append(list, toPaymentMethodResponse(&items[index]))
}
return &dto.EmployeeCollectionPaymentMethodListResponse{
List: list, Total: total, Page: page, PageSize: pageSize,
}, nil
}
// toPaymentMethodResponse 将字典项投影为对外响应,只暴露既有对象键与稳定字段。
func toPaymentMethodResponse(paymentMethod *model.EmployeeCollectionPaymentMethod) *dto.EmployeeCollectionPaymentMethodResponse {
return &dto.EmployeeCollectionPaymentMethodResponse{
ID: paymentMethod.ID, Code: paymentMethod.Code, Name: paymentMethod.Name,
Enabled: paymentMethod.Status == constants.EmployeeCollectionPaymentMethodStatusEnabled,
Sort: paymentMethod.SortOrder, Remark: paymentMethod.Remark,
CreatedAt: paymentMethod.CreatedAt, UpdatedAt: paymentMethod.UpdatedAt,
}
}
// normalizePage 归一化分页参数,执行 DefaultPageSize 与 MaxPageSize 上限。
func normalizePage(page, pageSize int) (int, int) {
if page <= 0 {
page = constants.DefaultPage
}
if pageSize <= 0 {
pageSize = constants.DefaultPageSize
}
if pageSize > constants.MaxPageSize {
pageSize = constants.MaxPageSize
}
return page, pageSize
}