feat(员工代收款): 新增员工代收款账单闭环
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s

- 新增 6 张表与成对迁移 000212,扩展企业微信审批场景业务类型白名单
- 后台线下套餐订单与两条代理线下充值入账路径在来源成功事务内建账,来源唯一键幂等
- 核销申请、审批尝试记录、账单分摊预占与驳回重提,审批业务类型 employee_collection_approval
- 企业微信终态消费幂等:通过转已核销、驳回释放预占、通过后撤销不回滚并转异常终态
- 退款成功事务内按 bill_id+refund_id 幂等冲销账单或仅写退款关联提示
- 线下收款方式字典、账单查询/统计/关闭、申请查询与代办权限,均写入事务内审计

OpenSpec Change: add-employee-collection-bills
This commit is contained in:
2026-09-10 18:24:05 +08:00
parent dc4e0d4103
commit ce24d5612e
54 changed files with 5975 additions and 465 deletions

View File

@@ -176,6 +176,22 @@ const (
// 审计留存相关错误 (1220-1229)
CodeAuditDataArchived = 1220 // 查询范围已归档,当前不支持在线查询
// 员工代收款账单相关错误 (1230-1249)
CodeEmployeeCollectionPaymentMethodNotFound = 1230 // 线下收款方式不存在
CodeEmployeeCollectionPaymentMethodCodeExists = 1231 // 线下收款方式编码已存在
CodeEmployeeCollectionPaymentMethodReferenced = 1232 // 线下收款方式已被引用,只能停用
CodeEmployeeCollectionPaymentMethodDisabled = 1233 // 线下收款方式已停用
CodeEmployeeCollectionBillNotFound = 1234 // 员工代收款账单不存在
CodeEmployeeCollectionBillNotSettleable = 1235 // 账单状态不允许核销
CodeEmployeeCollectionAllocationExceeded = 1236 // 分摊金额超过账单可核销余额
CodeEmployeeCollectionAllocationAmountInvalid = 1237 // 分摊金额必须大于零
CodeEmployeeCollectionPaidAmountExceeded = 1238 // 分摊总额超过本次付款金额
CodeEmployeeCollectionApplicationNotFound = 1239 // 核销申请不存在
CodeEmployeeCollectionApplicationStatusInvalid = 1240 // 核销申请状态不允许此操作
CodeEmployeeCollectionApplicationPending = 1241 // 账单存在审批中核销申请,不能关闭
CodeEmployeeCollectionBillClosed = 1242 // 账单已关闭
CodeEmployeeCollectionVoucherInvalid = 1243 // 支付凭证数量或内容不符合要求
// 服务端错误 (2000-2999) -> 5xx HTTP 状态码
CodeInternalError = 2001 // 内部服务器错误
CodeDatabaseError = 2002 // 数据库错误
@@ -320,6 +336,20 @@ var allErrorCodes = []int{
CodeWeComApplicationNotFound,
CodeWeComCredentialInvalid,
CodeAuditDataArchived,
CodeEmployeeCollectionPaymentMethodNotFound,
CodeEmployeeCollectionPaymentMethodCodeExists,
CodeEmployeeCollectionPaymentMethodReferenced,
CodeEmployeeCollectionPaymentMethodDisabled,
CodeEmployeeCollectionBillNotFound,
CodeEmployeeCollectionBillNotSettleable,
CodeEmployeeCollectionAllocationExceeded,
CodeEmployeeCollectionAllocationAmountInvalid,
CodeEmployeeCollectionPaidAmountExceeded,
CodeEmployeeCollectionApplicationNotFound,
CodeEmployeeCollectionApplicationStatusInvalid,
CodeEmployeeCollectionApplicationPending,
CodeEmployeeCollectionBillClosed,
CodeEmployeeCollectionVoucherInvalid,
CodeInternalError,
CodeDatabaseError,
CodeRedisError,
@@ -338,144 +368,158 @@ func init() {
// errorMessages 错误消息映射表(中文)
var errorMessages = map[int]string{
CodeSuccess: "成功",
CodeInvalidParam: "参数验证失败",
CodeMissingToken: "缺失认证令牌",
CodeInvalidToken: "无效或过期的令牌",
CodeUnauthorized: "未授权访问",
CodeForbidden: "禁止访问",
CodeNotFound: "资源未找到",
CodeConflict: "资源冲突",
CodeTooManyRequests: "请求过多,请稍后重试",
CodeRequestTooLarge: "请求体过大",
CodeAccountNotFound: "账号不存在",
CodeAccountDisabled: "账号已禁用",
CodeAccountDeleted: "账号已删除",
CodeUsernameExists: "用户名已存在",
CodePhoneExists: "手机号已存在",
CodeInvalidPassword: "密码格式不正确",
CodePasswordTooWeak: "密码强度不足",
CodeParentIDRequired: "非 root 用户必须提供上级账号",
CodeInvalidParentID: "上级账号不存在或无效",
CodeCannotModifyParent: "禁止修改上级账号",
CodeCannotModifyUserType: "禁止修改用户类型",
CodeRoleNotFound: "角色不存在",
CodeRoleNameExists: "角色名称已存在",
CodePermissionNotFound: "权限不存在",
CodePermCodeExists: "权限编码已存在",
CodeInvalidPermCode: "权限编码格式不正确(应为 module:action 格式)",
CodeRoleAlreadyAssigned: "角色已分配",
CodePermAlreadyAssigned: "权限已分配",
CodeRoleInUse: "角色使用中,无法删除",
CodeShopNotFound: "店铺不存在",
CodeShopCodeExists: "店铺编号已存在",
CodeShopLevelExceeded: "店铺层级不能超过 7 级",
CodeEnterpriseNotFound: "企业不存在",
CodeEnterpriseCodeExists: "企业编号已存在",
CodeCustomerNotFound: "个人客户不存在",
CodeCustomerPhoneExists: "个人客户手机号已存在",
CodeShopDisabled: "店铺已禁用",
CodeInvalidStatus: "状态不允许此操作",
CodeInsufficientBalance: "余额不足",
CodeWithdrawalNotFound: "提现申请不存在",
CodeWalletNotFound: "钱包不存在",
CodeInsufficientQuota: "额度不足",
CodeIotCardNotFound: "IoT 卡不存在",
CodeIotCardBoundToDevice: "IoT 卡已绑定设备,不能单独操作",
CodeIotCardStatusNotAllowed: "卡状态不允许此操作",
CodeAssetAllocationRecordNotFound: "分配记录不存在",
CodeNotDirectSubordinate: "只能操作直属下级店铺",
CodeCannotAllocateToSelf: "不能分配给自己",
CodeCannotRecallFromSelf: "不能从自己回收",
CodeCardAlreadyAuthorized: "卡已授权给该企业",
CodeCardNotAuthorized: "卡未授权给该企业",
CodeCannotAuthorizeOthersCard: "不能授权非自己的卡",
CodeCannotRevokeOthersAuthorization: "不能回收非自己创建的授权",
CodeCannotAuthorizeBoundCard: "不能授权已绑定设备的卡",
CodeCannotAuthorizeToOthersEnterprise: "不能授权给非自己的企业",
CodeDeviceAlreadyAuthorized: "设备已授权给该企业",
CodeDeviceNotAuthorized: "设备未授权给该企业",
CodeDeviceAuthorizedToOther: "设备已授权给其他企业",
CodeCannotAuthorizeOthersDevice: "不能授权非自己的设备",
CodeStorageNotConfigured: "对象存储服务未配置",
CodeStorageUploadFailed: "文件上传失败",
CodeStorageDownloadFailed: "文件下载失败",
CodeStorageFileNotFound: "文件不存在",
CodeStorageInvalidPurpose: "不支持的文件用途",
CodeStorageInvalidFileType: "不支持的文件类型",
CodeCarrierNotFound: "运营商不存在",
CodeCarrierCodeExists: "运营商编码已存在",
CodeGatewayError: "Gateway 请求失败",
CodeGatewayEncryptError: "数据加密失败",
CodeGatewaySignError: "签名生成失败",
CodeGatewayTimeout: "Gateway 请求超时",
CodeGatewayInvalidResp: "Gateway 响应格式错误",
CodeRechargeAmountInvalid: "充值金额不符合要求",
CodeRechargeNotFound: "充值订单不存在",
CodeRechargeAlreadyPaid: "充值订单已支付",
CodePurchaseOnBehalfForbidden: "无权使用线下支付",
CodePurchaseOnBehalfInvalidTarget: "代购目标无效",
CodeForceRechargeRequired: "必须充值指定金额",
CodeForceRechargeAmountMismatch: "强充金额不匹配",
CodePollingConfigNotFound: "轮询配置不存在",
CodePollingConfigNameExists: "轮询配置名称已存在",
CodePollingQueueFull: "轮询队列已满",
CodePollingConcurrencyLimit: "并发数已达上限",
CodePollingAlertRuleNotFound: "告警规则不存在",
CodePollingCleanupConfigNotFound: "数据清理配置不存在",
CodePollingManualTriggerLimit: "手动触发次数已达上限",
CodeNoAvailablePackage: "没有可用套餐",
CodePackageActivationConflict: "套餐正在激活中,请稍后重试",
CodeNoMainPackage: "必须有主套餐才能购买加油包",
CodeRealnameRequired: "设备/卡必须先完成实名认证才能购买套餐",
CodeMixedOrderForbidden: "同订单不能同时购买正式套餐和加油包",
CodeMainPackageExists: "已有主套餐,不能重复购买主套餐",
CodeWechatConfigNotFound: "微信支付配置不存在",
CodeWechatConfigActive: "不能删除当前生效的支付配置,请先停用",
CodeWechatConfigHasPendingOrders: "该配置存在未完成的支付订单,暂时无法删除",
CodeFuiouPayFailed: "支付发起失败,请重试",
CodeFuiouCallbackInvalid: "支付回调签名验证失败",
CodeNoPaymentConfig: "当前无可用的支付配置,请联系管理员",
CodeAssetNotFound: "资产不存在",
CodeWechatConfigUnavailable: "微信配置不可用",
CodeSmsSendFailed: "短信发送失败",
CodeVerificationCodeInvalid: "验证码错误或已过期",
CodePhoneAlreadyBound: "手机号已被其他客户绑定",
CodeAlreadyBoundPhone: "当前客户已绑定手机号,不可重复绑定",
CodeOldPhoneMismatch: "旧手机号与当前绑定不匹配",
CodeNeedRealname: "该套餐需实名认证后购买",
CodeOpenIDNotFound: "未找到微信授权信息,请先完成授权",
CodeRealnameNotAvailable: "请先完成充值或购买套餐后再进行实名认证",
CodeExchangeOrderNotFound: "换货单不存在或无权限",
CodeExchangeInProgress: "该资产存在进行中的换货单",
CodeExchangeStatusInvalid: "换货单当前状态不允许此操作",
CodeExchangeAssetTypeMismatch: "换货资产类型必须一致(卡换卡/设备换设备)",
CodeExchangeNewAssetNotInStock: "新资产非在库状态,不可用于换货",
CodeExchangeAssetNotExchanged: "资产当前状态不允许转新",
CodeExchangeMigrationFailed: "换货数据迁移失败",
CodeExchangeActiveRefund: "该资产存在退款申请",
CodePaymentMethodUnavailable: "当前资产不支持所选支付方式",
CodeWeComApplicationNotFound: "企业微信应用配置不存在或已禁用",
CodeWeComCredentialInvalid: "企业微信凭据配置无效",
CodeAuditDataArchived: "数据已归档,第一阶段不支持在线查询",
CodeInvalidCredentials: "用户名或密码错误",
CodeAccountLocked: "账号已锁定",
CodePasswordExpired: "密码已过期",
CodeInvalidOldPassword: "旧密码错误",
CodeWechatOAuthFailed: "微信授权失败",
CodeWechatUserInfoFailed: "获取微信用户信息失败",
CodeWechatPayFailed: "微信支付发起失败",
CodeWechatCallbackInvalid: "微信回调验证失败",
CodeOpenAPIAuthFailed: "开放接口认证失败",
CodeOpenAPISignInvalid: "开放接口签名无效",
CodeOpenAPITimestampInvalid: "开放接口时间戳无效",
CodeOpenAPINonceReplay: "重复请求,请勿重放",
CodeInternalError: "内部服务器错误",
CodeDatabaseError: "数据库错误",
CodeRedisError: "缓存服务错误",
CodeServiceUnavailable: "服务暂时不可用",
CodeTimeout: "请求超时",
CodeTaskQueueError: "任务队列错误",
CodeSuccess: "成功",
CodeInvalidParam: "参数验证失败",
CodeMissingToken: "缺失认证令牌",
CodeInvalidToken: "无效或过期的令牌",
CodeUnauthorized: "未授权访问",
CodeForbidden: "禁止访问",
CodeNotFound: "资源未找到",
CodeConflict: "资源冲突",
CodeTooManyRequests: "请求过多,请稍后重试",
CodeRequestTooLarge: "请求体过大",
CodeAccountNotFound: "账号不存在",
CodeAccountDisabled: "账号已禁用",
CodeAccountDeleted: "账号已删除",
CodeUsernameExists: "用户名已存在",
CodePhoneExists: "手机号已存在",
CodeInvalidPassword: "密码格式不正确",
CodePasswordTooWeak: "密码强度不足",
CodeParentIDRequired: "非 root 用户必须提供上级账号",
CodeInvalidParentID: "上级账号不存在或无效",
CodeCannotModifyParent: "禁止修改上级账号",
CodeCannotModifyUserType: "禁止修改用户类型",
CodeRoleNotFound: "角色不存在",
CodeRoleNameExists: "角色名称已存在",
CodePermissionNotFound: "权限不存在",
CodePermCodeExists: "权限编码已存在",
CodeInvalidPermCode: "权限编码格式不正确(应为 module:action 格式)",
CodeRoleAlreadyAssigned: "角色已分配",
CodePermAlreadyAssigned: "权限已分配",
CodeRoleInUse: "角色使用中,无法删除",
CodeShopNotFound: "店铺不存在",
CodeShopCodeExists: "店铺编号已存在",
CodeShopLevelExceeded: "店铺层级不能超过 7 级",
CodeEnterpriseNotFound: "企业不存在",
CodeEnterpriseCodeExists: "企业编号已存在",
CodeCustomerNotFound: "个人客户不存在",
CodeCustomerPhoneExists: "个人客户手机号已存在",
CodeShopDisabled: "店铺已禁用",
CodeInvalidStatus: "状态不允许此操作",
CodeInsufficientBalance: "余额不足",
CodeWithdrawalNotFound: "提现申请不存在",
CodeWalletNotFound: "钱包不存在",
CodeInsufficientQuota: "额度不足",
CodeIotCardNotFound: "IoT 卡不存在",
CodeIotCardBoundToDevice: "IoT 卡已绑定设备,不能单独操作",
CodeIotCardStatusNotAllowed: "卡状态不允许此操作",
CodeAssetAllocationRecordNotFound: "分配记录不存在",
CodeNotDirectSubordinate: "只能操作直属下级店铺",
CodeCannotAllocateToSelf: "不能分配给自己",
CodeCannotRecallFromSelf: "不能从自己回收",
CodeCardAlreadyAuthorized: "卡已授权给该企业",
CodeCardNotAuthorized: "卡未授权给该企业",
CodeCannotAuthorizeOthersCard: "不能授权非自己的卡",
CodeCannotRevokeOthersAuthorization: "不能回收非自己创建的授权",
CodeCannotAuthorizeBoundCard: "不能授权已绑定设备的卡",
CodeCannotAuthorizeToOthersEnterprise: "不能授权给非自己的企业",
CodeDeviceAlreadyAuthorized: "设备已授权给该企业",
CodeDeviceNotAuthorized: "设备未授权给该企业",
CodeDeviceAuthorizedToOther: "设备已授权给其他企业",
CodeCannotAuthorizeOthersDevice: "不能授权非自己的设备",
CodeStorageNotConfigured: "对象存储服务未配置",
CodeStorageUploadFailed: "文件上传失败",
CodeStorageDownloadFailed: "文件下载失败",
CodeStorageFileNotFound: "文件不存在",
CodeStorageInvalidPurpose: "不支持的文件用途",
CodeStorageInvalidFileType: "不支持的文件类型",
CodeCarrierNotFound: "运营商不存在",
CodeCarrierCodeExists: "运营商编码已存在",
CodeGatewayError: "Gateway 请求失败",
CodeGatewayEncryptError: "数据加密失败",
CodeGatewaySignError: "签名生成失败",
CodeGatewayTimeout: "Gateway 请求超时",
CodeGatewayInvalidResp: "Gateway 响应格式错误",
CodeRechargeAmountInvalid: "充值金额不符合要求",
CodeRechargeNotFound: "充值订单不存在",
CodeRechargeAlreadyPaid: "充值订单已支付",
CodePurchaseOnBehalfForbidden: "无权使用线下支付",
CodePurchaseOnBehalfInvalidTarget: "代购目标无效",
CodeForceRechargeRequired: "必须充值指定金额",
CodeForceRechargeAmountMismatch: "强充金额不匹配",
CodePollingConfigNotFound: "轮询配置不存在",
CodePollingConfigNameExists: "轮询配置名称已存在",
CodePollingQueueFull: "轮询队列已满",
CodePollingConcurrencyLimit: "并发数已达上限",
CodePollingAlertRuleNotFound: "告警规则不存在",
CodePollingCleanupConfigNotFound: "数据清理配置不存在",
CodePollingManualTriggerLimit: "手动触发次数已达上限",
CodeNoAvailablePackage: "没有可用套餐",
CodePackageActivationConflict: "套餐正在激活中,请稍后重试",
CodeNoMainPackage: "必须有主套餐才能购买加油包",
CodeRealnameRequired: "设备/卡必须先完成实名认证才能购买套餐",
CodeMixedOrderForbidden: "同订单不能同时购买正式套餐和加油包",
CodeMainPackageExists: "已有主套餐,不能重复购买主套餐",
CodeWechatConfigNotFound: "微信支付配置不存在",
CodeWechatConfigActive: "不能删除当前生效的支付配置,请先停用",
CodeWechatConfigHasPendingOrders: "该配置存在未完成的支付订单,暂时无法删除",
CodeFuiouPayFailed: "支付发起失败,请重试",
CodeFuiouCallbackInvalid: "支付回调签名验证失败",
CodeNoPaymentConfig: "当前无可用的支付配置,请联系管理员",
CodeAssetNotFound: "资产不存在",
CodeWechatConfigUnavailable: "微信配置不可用",
CodeSmsSendFailed: "短信发送失败",
CodeVerificationCodeInvalid: "验证码错误或已过期",
CodePhoneAlreadyBound: "手机号已被其他客户绑定",
CodeAlreadyBoundPhone: "当前客户已绑定手机号,不可重复绑定",
CodeOldPhoneMismatch: "旧手机号与当前绑定不匹配",
CodeNeedRealname: "该套餐需实名认证后购买",
CodeOpenIDNotFound: "未找到微信授权信息,请先完成授权",
CodeRealnameNotAvailable: "请先完成充值或购买套餐后再进行实名认证",
CodeExchangeOrderNotFound: "换货单不存在或无权限",
CodeExchangeInProgress: "该资产存在进行中的换货单",
CodeExchangeStatusInvalid: "换货单当前状态不允许此操作",
CodeExchangeAssetTypeMismatch: "换货资产类型必须一致(卡换卡/设备换设备)",
CodeExchangeNewAssetNotInStock: "新资产非在库状态,不可用于换货",
CodeExchangeAssetNotExchanged: "资产当前状态不允许转新",
CodeExchangeMigrationFailed: "换货数据迁移失败",
CodeExchangeActiveRefund: "该资产存在退款申请",
CodePaymentMethodUnavailable: "当前资产不支持所选支付方式",
CodeWeComApplicationNotFound: "企业微信应用配置不存在或已禁用",
CodeWeComCredentialInvalid: "企业微信凭据配置无效",
CodeAuditDataArchived: "数据已归档,第一阶段不支持在线查询",
CodeEmployeeCollectionPaymentMethodNotFound: "线下收款方式不存在",
CodeEmployeeCollectionPaymentMethodCodeExists: "线下收款方式编码已存在",
CodeEmployeeCollectionPaymentMethodReferenced: "线下收款方式已被核销申请引用,只能停用",
CodeEmployeeCollectionPaymentMethodDisabled: "线下收款方式已停用,不能用于新的核销申请",
CodeEmployeeCollectionBillNotFound: "员工代收款账单不存在",
CodeEmployeeCollectionBillNotSettleable: "账单当前状态不允许核销",
CodeEmployeeCollectionAllocationExceeded: "分摊金额超过账单可核销余额",
CodeEmployeeCollectionAllocationAmountInvalid: "分摊金额必须大于零",
CodeEmployeeCollectionPaidAmountExceeded: "分摊总额不能超过本次付款金额",
CodeEmployeeCollectionApplicationNotFound: "核销申请不存在",
CodeEmployeeCollectionApplicationStatusInvalid: "核销申请当前状态不允许此操作",
CodeEmployeeCollectionApplicationPending: "账单存在审批中核销申请,不能关闭",
CodeEmployeeCollectionBillClosed: "账单已关闭",
CodeEmployeeCollectionVoucherInvalid: "支付凭证数量或内容不符合要求",
CodeInvalidCredentials: "用户名或密码错误",
CodeAccountLocked: "账号已锁定",
CodePasswordExpired: "密码已过期",
CodeInvalidOldPassword: "旧密码错误",
CodeWechatOAuthFailed: "微信授权失败",
CodeWechatUserInfoFailed: "获取微信用户信息失败",
CodeWechatPayFailed: "微信支付发起失败",
CodeWechatCallbackInvalid: "微信回调验证失败",
CodeOpenAPIAuthFailed: "开放接口认证失败",
CodeOpenAPISignInvalid: "开放接口签名无效",
CodeOpenAPITimestampInvalid: "开放接口时间戳无效",
CodeOpenAPINonceReplay: "重复请求,请勿重放",
CodeInternalError: "内部服务器错误",
CodeDatabaseError: "数据库错误",
CodeRedisError: "缓存服务错误",
CodeServiceUnavailable: "服务暂时不可用",
CodeTimeout: "请求超时",
CodeTaskQueueError: "任务队列错误",
}
// GetMessage 获取错误码对应的消息
@@ -502,7 +546,10 @@ func GetHTTPStatus(code int) int {
return 401 // Unauthorized
case CodeForbidden:
return 403 // Forbidden
case CodeNotFound:
case CodeNotFound,
CodeEmployeeCollectionPaymentMethodNotFound,
CodeEmployeeCollectionBillNotFound,
CodeEmployeeCollectionApplicationNotFound:
return 404 // Not Found
case CodeAuditDataArchived:
return 410 // Gone
@@ -515,6 +562,13 @@ func GetHTTPStatus(code int) int {
CodeEnterpriseCodeExists,
CodeCustomerPhoneExists,
CodeCarrierCodeExists,
CodeEmployeeCollectionPaymentMethodCodeExists,
CodeEmployeeCollectionPaymentMethodReferenced,
CodeEmployeeCollectionPaymentMethodDisabled,
CodeEmployeeCollectionBillNotSettleable,
CodeEmployeeCollectionApplicationStatusInvalid,
CodeEmployeeCollectionApplicationPending,
CodeEmployeeCollectionBillClosed,
CodeOpenAPINonceReplay:
return 409 // Conflict
case CodeTooManyRequests: