Files
junhong_cmp_fiber/internal/routes/employee_collection.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

145 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"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"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
// requireEmployeeCollectionConfigAccess 限制线下收款方式字典维护仅超级管理员可用,
// 非超级管理员与其他不可见资源返回同一禁止访问错误,避免可枚举差异。
func requireEmployeeCollectionConfigAccess(handler fiber.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
if middleware.GetUserTypeFromContext(c.UserContext()) != constants.UserTypeSuperAdmin {
return errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
}
return handler(c)
}
}
func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
paymentMethodPath := basePath + "/employee-collection-payment-methods"
wrap := requireEmployeeCollectionConfigAccess
Register(router, doc, paymentMethodPath, "GET", "", handler.ListPaymentMethods, RouteSpec{
Summary: "查询线下收款方式",
Description: "超级管理员可查询全部字典项;其他后台账号仅返回启用项,用于新核销申请选择收款方式。",
Tags: []string{"员工代收款"},
Input: new(dto.EmployeeCollectionPaymentMethodListRequest),
Output: new(dto.EmployeeCollectionPaymentMethodListResponse),
Auth: true,
})
Register(router, doc, paymentMethodPath, "POST", "", wrap(handler.CreatePaymentMethod), RouteSpec{
Summary: "创建线下收款方式",
Description: "仅超级管理员。稳定编码在未删除记录中唯一。",
Tags: []string{"员工代收款"},
Input: new(dto.CreateEmployeeCollectionPaymentMethodRequest),
Output: new(dto.EmployeeCollectionPaymentMethodResponse),
Auth: true,
})
Register(router, doc, paymentMethodPath, "PUT", "/:id", wrap(handler.UpdatePaymentMethod), RouteSpec{
Summary: "更新线下收款方式",
Description: "仅超级管理员。可修改名称、排序、启停与备注;已被核销申请引用时不允许修改稳定编码。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Body: new(dto.UpdateEmployeeCollectionPaymentMethodRequest),
Output: new(dto.EmployeeCollectionPaymentMethodResponse),
Auth: true,
})
Register(router, doc, paymentMethodPath, "DELETE", "/:id", wrap(handler.DeletePaymentMethod), RouteSpec{
Summary: "删除线下收款方式",
Description: "仅超级管理员。已被核销申请引用的字典项不可物理删除,只能停用。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Output: nil,
Auth: true,
})
}
// registerEmployeeCollectionBillRoutes 注册员工代收款账单查询、统计与关闭路由。
// 账单可见性由应用层强制:欠款人仅见本人账单,超级管理员见全部。
func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
billPath := basePath + "/employee-collection-bills"
adminOnly := requireEmployeeCollectionConfigAccess
Register(router, doc, billPath, "GET", "", handler.ListBills, RouteSpec{
Summary: "查询员工代收款账单",
Description: "欠款人仅返回本人欠款账单,超级管理员返回全部;支持来源、状态、时间、欠款人、客户筛选与分页。",
Tags: []string{"员工代收款"},
Input: new(dto.EmployeeCollectionBillListRequest),
Output: new(dto.EmployeeCollectionBillListResponse),
Auth: true,
})
// 统计必须先于 /:id 注册,保证路径按字面量优先匹配。
Register(router, doc, billPath, "GET", "/statistics", handler.StatisticsBills, RouteSpec{
Summary: "查询员工代收款账单统计",
Description: "与账单列表使用相同筛选条件与可见性范围,返回应收、已核销、未核销金额合计与待处理账单数;不接受分页参数。",
Tags: []string{"员工代收款"},
Input: new(dto.EmployeeCollectionBillStatisticsRequest),
Output: new(dto.EmployeeCollectionBillStatisticsResponse),
Auth: true,
})
Register(router, doc, billPath, "GET", "/:id", handler.GetBill, RouteSpec{
Summary: "查询员工代收款账单详情",
Description: "返回账单、来源退款冲销、分摊、核销申请与企业微信审批历史;附件只返回对象键引用。无权限与不存在返回同一错误。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Output: new(dto.EmployeeCollectionBillDetailResponse),
Auth: true,
})
Register(router, doc, billPath, "POST", "/:id/close", adminOnly(handler.CloseBill), RouteSpec{
Summary: "关闭员工代收款账单",
Description: "仅超级管理员。仅允许关闭待核销或部分核销且不存在审批中分摊的账单;关闭只作废未核销余额并保留已核销金额。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Body: new(dto.CloseEmployeeCollectionBillRequest),
Output: new(dto.EmployeeCollectionBillResponse),
Auth: true,
})
}
// registerEmployeeCollectionApplicationRoutes 注册核销申请的创建、重提与受控查询路由。
// 可见性与代办权限由应用层强制:非超级管理员只能为本人账单操作,超级管理员代办必须填写原因。
func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
applicationPath := basePath + "/employee-collection-applications"
Register(router, doc, applicationPath, "POST", "", handler.CreateApplication, RouteSpec{
Summary: "创建核销申请",
Description: "员工为本人可见账单创建;超级管理员可为账单欠款人代办且必须填写 acting_reason。服务端按账单可核销余额与付款金额校验分摊任一失败不保存申请与预占。",
Tags: []string{"员工代收款"},
Input: new(dto.SubmitEmployeeCollectionApplicationRequest),
Output: new(dto.EmployeeCollectionApplicationSubmitResponse),
Auth: true,
})
Register(router, doc, applicationPath, "GET", "", handler.ListApplications, RouteSpec{
Summary: "查询核销申请",
Description: "非超级管理员仅返回本人申请,超级管理员返回全部;支持状态、收款方式、申请人与创建时间筛选和分页。",
Tags: []string{"员工代收款"},
Input: new(dto.EmployeeCollectionApplicationListRequest),
Output: new(dto.EmployeeCollectionApplicationListResponse),
Auth: true,
})
Register(router, doc, applicationPath, "GET", "/:id", handler.GetApplication, RouteSpec{
Summary: "查询核销申请详情",
Description: "返回申请事实、账单分摊与全部审批尝试历史;附件只返回对象键引用。无权限与不存在返回同一错误。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Output: new(dto.EmployeeCollectionApplicationDetailResponse),
Auth: true,
})
Register(router, doc, applicationPath, "PUT", "/:id", handler.ResubmitApplication, RouteSpec{
Summary: "修改并重提核销申请",
Description: "仅申请人或被代办的超级管理员,且仅已驳回申请可修改;重提新增一条审批尝试记录与新的企业微信审批实例,历史材料不被覆盖。",
Tags: []string{"员工代收款"},
Input: new(dto.IDReq),
Body: new(dto.SubmitEmployeeCollectionApplicationRequest),
Output: new(dto.EmployeeCollectionApplicationSubmitResponse),
Auth: true,
})
}