fix(员工代收款): 修正路由前缀注册方式,消除 /api/admin 根级 /:id 抢占
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m19s

Register 的 basePath 仅用于生成 OpenAPI 文档,不参与路由注册;员工代收款
三个注册函数把资源前缀传给了 basePath、path 只写相对段,导致
GET /api/admin/:id 与 POST /api/admin/:id/close 落在 /api/admin 根上。
账单单段路径被当作路径 ID 解析返回“无效的路径ID”,并抢占其后注册的
同层单段 GET(/api/admin/refunds、/system-configs 等)。

改为 router.Group(前缀) 注册,与仓库既有写法一致;路由布局与文档路径不变。
This commit is contained in:
2026-09-11 14:40:23 +08:00
parent 9c3e3fe32b
commit ff1362df3f

View File

@@ -23,10 +23,13 @@ func requireEmployeeCollectionConfigAccess(handler fiber.Handler) fiber.Handler
} }
func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) { func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
// 路由前缀必须交给 GroupRegister 的 basePath 只用于生成 OpenAPI 文档,不参与路由注册,
// 否则相对路径会落到 /api/admin 根上并抢占同层后续路由。
group := router.Group("/employee-collection-payment-methods")
paymentMethodPath := basePath + "/employee-collection-payment-methods" paymentMethodPath := basePath + "/employee-collection-payment-methods"
wrap := requireEmployeeCollectionConfigAccess wrap := requireEmployeeCollectionConfigAccess
Register(router, doc, paymentMethodPath, "GET", "", handler.ListPaymentMethods, RouteSpec{ Register(group, doc, paymentMethodPath, "GET", "", handler.ListPaymentMethods, RouteSpec{
Summary: "查询线下收款方式", Summary: "查询线下收款方式",
Description: "超级管理员可查询全部字典项;其他后台账号仅返回启用项,用于新核销申请选择收款方式。", Description: "超级管理员可查询全部字典项;其他后台账号仅返回启用项,用于新核销申请选择收款方式。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -34,7 +37,7 @@ func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.Employ
Output: new(dto.EmployeeCollectionPaymentMethodListResponse), Output: new(dto.EmployeeCollectionPaymentMethodListResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, paymentMethodPath, "POST", "", wrap(handler.CreatePaymentMethod), RouteSpec{ Register(group, doc, paymentMethodPath, "POST", "", wrap(handler.CreatePaymentMethod), RouteSpec{
Summary: "创建线下收款方式", Summary: "创建线下收款方式",
Description: "仅超级管理员。稳定编码在未删除记录中唯一。", Description: "仅超级管理员。稳定编码在未删除记录中唯一。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -42,7 +45,7 @@ func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.Employ
Output: new(dto.EmployeeCollectionPaymentMethodResponse), Output: new(dto.EmployeeCollectionPaymentMethodResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, paymentMethodPath, "PUT", "/:id", wrap(handler.UpdatePaymentMethod), RouteSpec{ Register(group, doc, paymentMethodPath, "PUT", "/:id", wrap(handler.UpdatePaymentMethod), RouteSpec{
Summary: "更新线下收款方式", Summary: "更新线下收款方式",
Description: "仅超级管理员。可修改名称、排序、启停与备注;已被核销申请引用时不允许修改稳定编码。", Description: "仅超级管理员。可修改名称、排序、启停与备注;已被核销申请引用时不允许修改稳定编码。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -51,7 +54,7 @@ func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.Employ
Output: new(dto.EmployeeCollectionPaymentMethodResponse), Output: new(dto.EmployeeCollectionPaymentMethodResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, paymentMethodPath, "DELETE", "/:id", wrap(handler.DeletePaymentMethod), RouteSpec{ Register(group, doc, paymentMethodPath, "DELETE", "/:id", wrap(handler.DeletePaymentMethod), RouteSpec{
Summary: "删除线下收款方式", Summary: "删除线下收款方式",
Description: "仅超级管理员。已被核销申请引用的字典项不可物理删除,只能停用。", Description: "仅超级管理员。已被核销申请引用的字典项不可物理删除,只能停用。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -64,10 +67,11 @@ func registerEmployeeCollectionRoutes(router fiber.Router, handler *admin.Employ
// registerEmployeeCollectionBillRoutes 注册员工代收款账单查询、统计与关闭路由。 // registerEmployeeCollectionBillRoutes 注册员工代收款账单查询、统计与关闭路由。
// 账单可见性由应用层强制:欠款人仅见本人账单,超级管理员见全部。 // 账单可见性由应用层强制:欠款人仅见本人账单,超级管理员见全部。
func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) { func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
group := router.Group("/employee-collection-bills")
billPath := basePath + "/employee-collection-bills" billPath := basePath + "/employee-collection-bills"
adminOnly := requireEmployeeCollectionConfigAccess adminOnly := requireEmployeeCollectionConfigAccess
Register(router, doc, billPath, "GET", "", handler.ListBills, RouteSpec{ Register(group, doc, billPath, "GET", "", handler.ListBills, RouteSpec{
Summary: "查询员工代收款账单", Summary: "查询员工代收款账单",
Description: "欠款人仅返回本人欠款账单,超级管理员返回全部;支持来源、状态、时间、欠款人、客户筛选与分页。", Description: "欠款人仅返回本人欠款账单,超级管理员返回全部;支持来源、状态、时间、欠款人、客户筛选与分页。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -76,7 +80,7 @@ func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.Em
Auth: true, Auth: true,
}) })
// 统计必须先于 /:id 注册,保证路径按字面量优先匹配。 // 统计必须先于 /:id 注册,保证路径按字面量优先匹配。
Register(router, doc, billPath, "GET", "/statistics", handler.StatisticsBills, RouteSpec{ Register(group, doc, billPath, "GET", "/statistics", handler.StatisticsBills, RouteSpec{
Summary: "查询员工代收款账单统计", Summary: "查询员工代收款账单统计",
Description: "与账单列表使用相同筛选条件与可见性范围,返回应收、已核销、未核销金额合计与待处理账单数;不接受分页参数。", Description: "与账单列表使用相同筛选条件与可见性范围,返回应收、已核销、未核销金额合计与待处理账单数;不接受分页参数。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -84,7 +88,7 @@ func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.Em
Output: new(dto.EmployeeCollectionBillStatisticsResponse), Output: new(dto.EmployeeCollectionBillStatisticsResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, billPath, "GET", "/:id", handler.GetBill, RouteSpec{ Register(group, doc, billPath, "GET", "/:id", handler.GetBill, RouteSpec{
Summary: "查询员工代收款账单详情", Summary: "查询员工代收款账单详情",
Description: "返回账单、来源退款冲销、分摊、核销申请与企业微信审批历史;附件只返回对象键引用。无权限与不存在返回同一错误。", Description: "返回账单、来源退款冲销、分摊、核销申请与企业微信审批历史;附件只返回对象键引用。无权限与不存在返回同一错误。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -92,7 +96,7 @@ func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.Em
Output: new(dto.EmployeeCollectionBillDetailResponse), Output: new(dto.EmployeeCollectionBillDetailResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, billPath, "POST", "/:id/close", adminOnly(handler.CloseBill), RouteSpec{ Register(group, doc, billPath, "POST", "/:id/close", adminOnly(handler.CloseBill), RouteSpec{
Summary: "关闭员工代收款账单", Summary: "关闭员工代收款账单",
Description: "仅超级管理员。仅允许关闭待核销或部分核销且不存在审批中分摊的账单;关闭只作废未核销余额并保留已核销金额。", Description: "仅超级管理员。仅允许关闭待核销或部分核销且不存在审批中分摊的账单;关闭只作废未核销余额并保留已核销金额。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -106,9 +110,10 @@ func registerEmployeeCollectionBillRoutes(router fiber.Router, handler *admin.Em
// registerEmployeeCollectionApplicationRoutes 注册核销申请的创建、重提与受控查询路由。 // registerEmployeeCollectionApplicationRoutes 注册核销申请的创建、重提与受控查询路由。
// 可见性与代办权限由应用层强制:非超级管理员只能为本人账单操作,超级管理员代办必须填写原因。 // 可见性与代办权限由应用层强制:非超级管理员只能为本人账单操作,超级管理员代办必须填写原因。
func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) { func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *admin.EmployeeCollectionHandler, doc *openapi.Generator, basePath string) {
group := router.Group("/employee-collection-applications")
applicationPath := basePath + "/employee-collection-applications" applicationPath := basePath + "/employee-collection-applications"
Register(router, doc, applicationPath, "POST", "", handler.CreateApplication, RouteSpec{ Register(group, doc, applicationPath, "POST", "", handler.CreateApplication, RouteSpec{
Summary: "创建核销申请", Summary: "创建核销申请",
Description: "员工为本人可见账单创建;超级管理员可为账单欠款人代办且必须填写 acting_reason。服务端按账单可核销余额与付款金额校验分摊任一失败不保存申请与预占。", Description: "员工为本人可见账单创建;超级管理员可为账单欠款人代办且必须填写 acting_reason。服务端按账单可核销余额与付款金额校验分摊任一失败不保存申请与预占。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -116,7 +121,7 @@ func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *a
Output: new(dto.EmployeeCollectionApplicationSubmitResponse), Output: new(dto.EmployeeCollectionApplicationSubmitResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, applicationPath, "GET", "", handler.ListApplications, RouteSpec{ Register(group, doc, applicationPath, "GET", "", handler.ListApplications, RouteSpec{
Summary: "查询核销申请", Summary: "查询核销申请",
Description: "非超级管理员仅返回本人申请,超级管理员返回全部;支持状态、收款方式、申请人与创建时间筛选和分页。", Description: "非超级管理员仅返回本人申请,超级管理员返回全部;支持状态、收款方式、申请人与创建时间筛选和分页。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -124,7 +129,7 @@ func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *a
Output: new(dto.EmployeeCollectionApplicationListResponse), Output: new(dto.EmployeeCollectionApplicationListResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, applicationPath, "GET", "/:id", handler.GetApplication, RouteSpec{ Register(group, doc, applicationPath, "GET", "/:id", handler.GetApplication, RouteSpec{
Summary: "查询核销申请详情", Summary: "查询核销申请详情",
Description: "返回申请事实、账单分摊与全部审批尝试历史;附件只返回对象键引用。无权限与不存在返回同一错误。", Description: "返回申请事实、账单分摊与全部审批尝试历史;附件只返回对象键引用。无权限与不存在返回同一错误。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},
@@ -132,7 +137,7 @@ func registerEmployeeCollectionApplicationRoutes(router fiber.Router, handler *a
Output: new(dto.EmployeeCollectionApplicationDetailResponse), Output: new(dto.EmployeeCollectionApplicationDetailResponse),
Auth: true, Auth: true,
}) })
Register(router, doc, applicationPath, "PUT", "/:id", handler.ResubmitApplication, RouteSpec{ Register(group, doc, applicationPath, "PUT", "/:id", handler.ResubmitApplication, RouteSpec{
Summary: "修改并重提核销申请", Summary: "修改并重提核销申请",
Description: "仅申请人或被代办的超级管理员,且仅已驳回申请可修改;重提新增一条审批尝试记录与新的企业微信审批实例,历史材料不被覆盖。", Description: "仅申请人或被代办的超级管理员,且仅已驳回申请可修改;重提新增一条审批尝试记录与新的企业微信审批实例,历史材料不被覆盖。",
Tags: []string{"员工代收款"}, Tags: []string{"员工代收款"},