package admin import ( "strings" "time" "github.com/gofiber/fiber/v2" employeecollectionapp "github.com/break/junhong_cmp_fiber/internal/application/employeecollection" "github.com/break/junhong_cmp_fiber/internal/model/dto" employeecollectionquery "github.com/break/junhong_cmp_fiber/internal/query/employeecollection" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/response" ) // EmployeeCollectionHandler 处理员工代收款账单、核销申请与线下收款方式字典请求。 // 边界只做绑定、路径参数校验与统一响应,状态、金额与权限不变量由应用用例判断。 type EmployeeCollectionHandler struct { paymentMethod *employeecollectionapp.PaymentMethodService paymentMethodQuery *employeecollectionquery.PaymentMethodQuery billClose *employeecollectionapp.BillCloseService billQuery *employeecollectionquery.BillQuery application *employeecollectionapp.ApplicationService applicationQuery *employeecollectionquery.ApplicationQuery } // NewEmployeeCollectionHandler 创建员工代收款处理器。 func NewEmployeeCollectionHandler( service *employeecollectionapp.PaymentMethodService, billClose *employeecollectionapp.BillCloseService, application *employeecollectionapp.ApplicationService, ) *EmployeeCollectionHandler { return &EmployeeCollectionHandler{paymentMethod: service, billClose: billClose, application: application} } // SetApplicationQuery 注入核销申请只读投影。 func (h *EmployeeCollectionHandler) SetApplicationQuery(query *employeecollectionquery.ApplicationQuery) { h.applicationQuery = query } // SetPaymentMethodQuery 注入线下收款方式字典只读投影。 func (h *EmployeeCollectionHandler) SetPaymentMethodQuery(query *employeecollectionquery.PaymentMethodQuery) { h.paymentMethodQuery = query } // SetBillQuery 注入员工代收款账单只读投影。 func (h *EmployeeCollectionHandler) SetBillQuery(query *employeecollectionquery.BillQuery) { h.billQuery = query } // CreatePaymentMethod 创建线下收款方式。 // POST /api/admin/employee-collection-payment-methods func (h *EmployeeCollectionHandler) CreatePaymentMethod(c *fiber.Ctx) error { var request dto.CreateEmployeeCollectionPaymentMethodRequest if err := c.BodyParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.paymentMethod.Create(c.UserContext(), request) if err != nil { return err } return response.Success(c, result) } // UpdatePaymentMethod 更新线下收款方式,支持修改名称、排序、启停、备注与未被引用时的稳定编码。 // PUT /api/admin/employee-collection-payment-methods/:id func (h *EmployeeCollectionHandler) UpdatePaymentMethod(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } var request dto.UpdateEmployeeCollectionPaymentMethodRequest if err := c.BodyParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.paymentMethod.Update(c.UserContext(), id, request) if err != nil { return err } return response.Success(c, result) } // DeletePaymentMethod 删除未被核销申请引用的线下收款方式。 // DELETE /api/admin/employee-collection-payment-methods/:id func (h *EmployeeCollectionHandler) DeletePaymentMethod(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } if err := h.paymentMethod.Delete(c.UserContext(), id); err != nil { return err } return response.Success(c, nil) } // ListPaymentMethods 分页查询线下收款方式。 // GET /api/admin/employee-collection-payment-methods func (h *EmployeeCollectionHandler) ListPaymentMethods(c *fiber.Ctx) error { var request dto.EmployeeCollectionPaymentMethodListRequest if err := c.QueryParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.paymentMethodQuery.List(c.UserContext(), request) if err != nil { return err } return response.SuccessWithPagination(c, result.List, result.Total, result.Page, result.PageSize) } // ListBills 分页查询员工代收款账单。 // GET /api/admin/employee-collection-bills func (h *EmployeeCollectionHandler) ListBills(c *fiber.Ctx) error { var request dto.EmployeeCollectionBillListRequest if err := c.QueryParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.billQuery.List(c.UserContext(), request) if err != nil { return err } return response.SuccessWithPagination(c, result.List, result.Total, result.Page, result.PageSize) } // StatisticsBills 汇总员工代收款账单金额与待处理数量。 // GET /api/admin/employee-collection-bills/statistics func (h *EmployeeCollectionHandler) StatisticsBills(c *fiber.Ctx) error { var request dto.EmployeeCollectionBillStatisticsRequest if err := c.QueryParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.billQuery.Statistics(c.UserContext(), request) if err != nil { return err } return response.Success(c, result) } // GetBill 查询员工代收款账单详情。 // GET /api/admin/employee-collection-bills/:id func (h *EmployeeCollectionHandler) GetBill(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } result, err := h.billQuery.Detail(c.UserContext(), id) if err != nil { return err } return response.Success(c, result) } // CloseBill 关闭员工代收款账单,仅超级管理员可操作。 // POST /api/admin/employee-collection-bills/:id/close func (h *EmployeeCollectionHandler) CloseBill(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } var request dto.CloseEmployeeCollectionBillRequest if err := c.BodyParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } bill, err := h.billClose.Close(c.UserContext(), id, request.Reason) if err != nil { return err } result, err := employeecollectionquery.ProjectBill(bill) if err != nil { return err } return response.Success(c, result) } // CreateApplication 为本人可见账单创建核销申请。 // POST /api/admin/employee-collection-applications func (h *EmployeeCollectionHandler) CreateApplication(c *fiber.Ctx) error { request, err := bindApplicationRequest(c) if err != nil { return err } result, err := h.application.Create(c.UserContext(), request) if err != nil { return err } projected, err := employeecollectionquery.ProjectApplicationSubmit(employeecollectionquery.ApplicationSubmitProjection{ Application: result.Application, Attempt: result.Attempt, Allocations: result.Allocations, Bills: result.Bills, InstanceID: result.InstanceID, InstanceStatus: result.InstanceStatus, }) if err != nil { return err } return response.Success(c, projected) } // ResubmitApplication 修改并重提已驳回的核销申请。 // PUT /api/admin/employee-collection-applications/:id func (h *EmployeeCollectionHandler) ResubmitApplication(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } request, err := bindApplicationRequest(c) if err != nil { return err } result, err := h.application.Resubmit(c.UserContext(), id, request) if err != nil { return err } projected, err := employeecollectionquery.ProjectApplicationSubmit(employeecollectionquery.ApplicationSubmitProjection{ Application: result.Application, Attempt: result.Attempt, Allocations: result.Allocations, Bills: result.Bills, InstanceID: result.InstanceID, InstanceStatus: result.InstanceStatus, }) if err != nil { return err } return response.Success(c, projected) } // ListApplications 分页查询核销申请。 // GET /api/admin/employee-collection-applications func (h *EmployeeCollectionHandler) ListApplications(c *fiber.Ctx) error { var request dto.EmployeeCollectionApplicationListRequest if err := c.QueryParser(&request); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } result, err := h.applicationQuery.List(c.UserContext(), request) if err != nil { return err } return response.SuccessWithPagination(c, result.List, result.Total, result.Page, result.PageSize) } // GetApplication 查询核销申请详情,含分摊与全部审批尝试历史。 // GET /api/admin/employee-collection-applications/:id func (h *EmployeeCollectionHandler) GetApplication(c *fiber.Ctx) error { id, err := pathID(c) if err != nil { return err } result, err := h.applicationQuery.Detail(c.UserContext(), id) if err != nil { return err } return response.Success(c, result) } // bindApplicationRequest 绑定并转换创建或重提核销申请请求。 // 付款时间按带时区的 RFC3339 解析;其余边界与业务校验由应用用例统一判断。 func bindApplicationRequest(c *fiber.Ctx) (employeecollectionapp.SubmitApplicationCommand, error) { var request dto.SubmitEmployeeCollectionApplicationRequest if err := c.BodyParser(&request); err != nil { return employeecollectionapp.SubmitApplicationCommand{}, errors.New(errors.CodeInvalidParam, "请求参数解析失败") } paidAt, err := time.Parse(time.RFC3339, strings.TrimSpace(request.PaidAt)) if err != nil { return employeecollectionapp.SubmitApplicationCommand{}, errors.New(errors.CodeInvalidParam, "付款时间必须为带时区的 RFC3339 格式") } allocations := make([]employeecollectionapp.ApplicationAllocationCommand, 0, len(request.Allocations)) for _, item := range request.Allocations { allocations = append(allocations, employeecollectionapp.ApplicationAllocationCommand{ BillID: item.BillID, Amount: item.Amount, }) } return employeecollectionapp.SubmitApplicationCommand{ PaymentMethodID: request.PaymentMethodID, PaidAmount: request.PaidAmount, PayerName: request.PayerName, PaidAt: paidAt, ExternalTransactionNo: request.ExternalTransactionNo, PaymentVoucherKeys: request.PaymentVoucherKeys, Remark: request.Remark, ActingReason: request.ActingReason, Allocations: allocations, }, nil }