Files
qitongxue-api/internal/consts/consts.go
2026-09-30 17:01:18 +08:00

516 lines
18 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 consts
// JWT audience
const (
AudUser = "user"
AudAdmin = "admin"
)
// 请求上下文变量(中间件 SetCtxVar)
const (
CtxUserId = "userId"
CtxLevelKey = "levelKey"
CtxAdminId = "adminId"
// 管理员审计上下文(AdminPerm 中间件写入,WriteAudit 读取)
CtxAdminRole = "adminRole"
CtxAdminAccount = "adminAccount"
CtxAdminName = "adminName"
)
// 管理员角色(admin_users.role_value)
const (
AdminRoleSuper = "super" // 超管:全部权限
AdminRoleOperator = "operator" // 运营:内容读写,不含管理员管理与审计
AdminRoleReadonly = "readonly" // 只读:仅可查看
)
// 管理员状态(admin_users.status)
const (
AdminStatusDisabled = 0
AdminStatusEnabled = 1
)
// 审计结果(admin_audit_logs.result)
const (
AuditResultFail = 0 // 失败
AuditResultSuccess = 1 // 成功
)
// 业务错误码(响应 code 字段)
const (
CodeLocked = 4001 // 模块未授权(等级锁定)
CodeQuotaExhausted = 4002 // 额度用尽(免费/会员/付费额度都已耗尽)
CodeNoteLimit = 4003 // 笔记数量超上限(免费版最多 50 条)
CodeNoteNotFound = 4004 // 笔记不存在/无权
CodeEventUnavailable = 4005 // 活动不存在或未发布
CodeSeatRateLimited = 4006 // 查座频率超限
CodeSeatCooling = 4007 // 查座连续失败冷却中
CodeImportConflict = 4008 // 导入存在未处理冲突
CodeLayoutConflict = 4009 // 布局版本冲突(CAS 失败)
CodePayFail = 4101 // 下单/支付参数异常
)
// 默认等级:会员到期后回落的目标等级
const DefaultLevelKey = "v1"
// 表名
const (
TableUsers = "users"
TableModules = "modules"
TableLevels = "levels"
TableTools = "tools"
TableUsageLogs = "tool_usage_logs"
TableWorkbench = "user_workbench"
TableAdminUsers = "admin_users"
TableAdminAuditLog = "admin_audit_logs"
TableFeedbacks = "feedbacks"
TableMemberPlans = "member_plans"
TableMemberOrders = "member_orders"
TableQuotaPacks = "quota_packs"
TableUserToolQuota = "user_tool_quota"
TableQuotaUsage = "user_quota_usage"
TableSettings = "settings"
TableNotes = "notes"
// 年会域(T08 建前 6 张;T09/T10 分别追加 event_import_logs / event_seat_queries)
TableEnterprises = "enterprises"
TableEmployees = "employees"
TableAnnualEvents = "annual_events"
TableEventParticipants = "event_participants"
TableEventTables = "event_tables"
TableEventSeats = "event_seats"
TableEventImportLogs = "event_import_logs"
TableEventSeatQueries = "event_seat_queries"
)
// 年会活动状态(annual_events.status,字符串枚举;仅 published 可查座)
const (
EventStatusDraft = "draft"
EventStatusPublished = "published"
EventStatusEnded = "ended"
)
// 自动排座模式(/event/admin/seat/auto 的 mode)
const (
SeatModeFill = "fill" // 增量补位:保留手工号,只填未分配员工到空位
SeatModeReorder = "reorder" // 全部重排:覆盖含手工号在内的编号并清空 is_manual
)
// 桌型(event_tables.table_type,N4)
//
// 0 普通桌 / 1 主桌 / 2 签到台 / 3 媒体席 / 4 备用桌 / 9 其他。
// 类型默认联动(前端 N4-10,后端中立存储;老数据缺列回退 0):
//
// 签到台/媒体席/备用桌 → capacity=0 + exclude_auto=1
// 普通桌 → capacity=10 + exclude_auto=0
// 主桌 → capacity=10 + exclude_auto=1
const (
TableTypeNormal = 0 // 普通桌
TableTypeMain = 1 // 主桌
TableTypeCheckin = 2 // 签到台
TableTypeMedia = 3 // 媒体席
TableTypeSpare = 4 // 备用桌
TableTypeOther = 9 // 其他
)
// 员工导入:行分类(预览 type,见 §8.4)
const (
ImportTypeNew = "new" // 新号码 → 新增
ImportTypeUpdate = "update" // 同手机号 → 视为同一人,更新
ImportTypeConflict = "conflict" // 同名不同号 → 需人工裁决
ImportTypeError = "error" // 缺手机号/格式非法 → 跳过(计入 skipped)
)
// 员工导入:冲突裁决动作(decisions.action,见 §8.4)
const (
ImportActionMerge = "merge" // 视为同一人·更新手机号
ImportActionCreate = "create" // 视为另一个人新增
ImportActionIgnore = "ignore" // 忽略
)
// 导入记录状态(event_import_logs.status,见 §8.4)
const (
ImportStatusDraft = "draft" // 预览中
ImportStatusCommitted = "committed" // 已入库
ImportStatusCancelled = "cancelled" // 已取消
)
// 导入行数上限(超出给明确提示;可用 settings 覆盖)
const (
SettingImportRowLimit = "event_import_row_limit" // settings 键
// DefaultImportRowLimit 单次导入行数上限兜底值
DefaultImportRowLimit = 2000
)
// 查座限流/冷却阈值(settings 键 + 缺省值;无 Redis,以 event_seat_queries 时间窗为准)
const (
SettingSeatRateLimitPerMin = "event.seat.rateLimitPerMin" // ≥N 次/分钟 → 4006
SettingSeatRateLimitPerHour = "event.seat.rateLimitPerHour" // ≥N 次/小时 → 4006
SettingSeatFailCooldownMinutes = "event.seat.failCooldownMinutes" // 冷却时长(分钟)
SettingSeatFailThreshold = "event.seat.failThreshold" // 连续失败达到该值触发冷却
DefaultSeatRateLimitPerMin = 5 // PRD-02 §4.2.3 建议值
DefaultSeatRateLimitPerHour = 20 // 建议值
DefaultSeatFailCooldownMinutes = 10 // 建议值
DefaultSeatFailThreshold = 5 // 建议值
)
// 查座审计结果枚举(event_seat_queries.result,架构 §3.2.9)
const (
SeatResultFail = 0 // 失败(未命中/未分配)
SeatResultSuccess = 1 // 成功(命中并返回本人桌座)
SeatResultAll = -1 // 审计列表筛选用:全部结果(仅用于入参,不落库)
)
// 笔记额度与提醒状态
const (
// NoteFreeLimit 免费用户活跃笔记数上限(总量上限,非每月重置)
NoteFreeLimit = 50
// notes.remind_status 状态枚举
NoteRemindNone = 0 // 未设置
NoteRemindPending = 1 // 待提醒
NoteRemindSent = 2 // 已提醒
NoteRemindFailed = 3 // 发送失败(含未发送·授权不足 / 模板未配置)
NoteRemindExpired = 4 // 已过期
)
// 笔记提醒过期设置(settings 表)
const (
SettingNoteRemindExpireHours = "note_remind_expire_hours" // 待提醒超过该小时数仍未发送则置「已过期」
)
// DefaultNoteRemindExpireHours 提醒过期兜底值:settings 未配置时使用。
const DefaultNoteRemindExpireHours = 24
// 订单类型:同一个下单/发货链路承载「会员」与「次数包」两种商品
const (
OrderTypeMember = 1 // 会员套餐(延长等级有效期)
OrderTypeQuota = 2 // 次数包(增加某工具的付费额度余额)
)
// ============================================================================
// 商业化域:优惠码 / 优惠券(迭代-2026-09-20)
// ============================================================================
// 表名(优惠码 / 券模板 / 用户持有券)
const (
TablePromoCodes = "promo_codes"
TableCoupons = "coupons"
TableUserCoupons = "user_coupons"
)
// 业务错误码:优惠凭证域
//
// 编号接续 4001–4009 / 4010–4014 段(架构 §4.5 统一裁定,解决两份 PRD 的冲突)。
const (
CodePromoInvalid = 4010 // 优惠码无效/过期/达上限/超单人限用/不适用;或同时传 promo_code 与 user_coupon_id
CodeCouponUnavailable = 4011 // 券不可用/已用/过期/作废;免单券并发占用失败
CodeLevelInvalid = 4012 // 等级不存在或已停用
CodePromoRateLimited = 4013 // 同一用户 1 分钟内优惠码错误次数超限
CodeProductPriceMismatch = 4014 // 服务端自检:settings 道具价格与 goodsPrice 预期不一致
)
// 业务错误码(续 4015–4018 段)
//
// 4015 属 T01(订单两步式):订单不存在 / 不属于当前用户 / 状态不允许该操作。
// 4016/4017(T02 头像上传)见紧随其后的独立区块;4018 属 T04(座次自动排座前置条件)。
const (
CodeOrderInvalid = 4015 // 订单不存在 / 不属于当前用户 / 状态不允许该操作(已支付、已关闭)
CodeSeatAutoInvalid = 4018 // 自动排座前置条件不满足(无可参与排座的桌)——fail-closed,message 列出被排除的桌
)
// 头像上传错误码(T02 / N3)
//
// 4017 上传失败**必须给可读中文提示**(如「头像上传失败,请重试」)且**原头像不变、不静默失败**(U1)。
const (
CodeUploadInvalid = 4016 // 上传校验失败:体积超限(>5MB)/ 格式不支持 —— 前端不发起上传
CodeUploadFailed = 4017 // 上传存储失败(服务端写盘 / 目录异常)—— 可读中文提示,原头像不变
)
// 券型(coupons.coupon_type / user_coupons.coupon_type)
//
// 1 免单券(走支付)/ 2 展示券(仅展示)。**新建默认 2**(fail-closed)。
const (
CouponTypeFreebie = 1
CouponTypeDisplay = 2
)
// 券面语义 kind(coupons.kind / user_coupons.kind):1 满减 / 2 免单
const (
CouponKindCut = 1
CouponKindFreebie = 2
)
// 用户券状态(user_coupons.status)
const (
UserCouponStatusUnused = 0 // 未使用
UserCouponStatusUsed = 1 // 已使用(占用或核销)
UserCouponStatusExpired = 2 // 已过期
UserCouponStatusVoid = 3 // 已作废
)
// 用户券来源(user_coupons.source)
const (
UserCouponSourceAdmin = 1 // 后台发放
UserCouponSourceClaim = 2 // 活动领取
UserCouponSourceSystem = 3 // 系统赠送
)
// 用户券占用/核销人(user_coupons.used_by)
const (
UserCouponUsedBySystem = "system" // 订单占用
UserCouponUsedByAdmin = "admin" // 人工核销
)
// 订单使用凭证类型(member_orders.promo_kind)
const (
PromoKindNone = 0 // 未使用凭证
PromoKindCode = 1 // 优惠码
PromoKindCoupon = 2 // 免单券
)
// 适用范围(promo_codes.scope / coupons.scope):0 全部 / 1 指定会员套餐 / 2 指定次数包
const (
PromoScopeAll = 0
PromoScopePlan = 1
PromoScopePack = 2
)
// 优惠凭证域 settings 键(后台可配,不硬编码进 config.yaml)
const (
SettingFreebieProductId = "pay.freebie.product_id" // 免单道具 ID
SettingFreebiePriceCents = "pay.freebie.price_cents" // 免单道具价格(分)
SettingPromoFailLimitPerMin = "promo.fail.limitPerMin" // 同一用户 1 分钟错误上限
SettingCouponLockTtlSeconds = "coupon.lock.ttlSeconds" // 免单券占用自动释放时长(秒)
)
// 优惠凭证域 settings 兜底值(settings 未配置时使用)
const (
DefaultFreebieProductId = "admin_1"
DefaultFreebiePriceCents = 1
DefaultPromoFailLimitPerMin = 6
DefaultCouponLockTtlSeconds = 900
)
// 额度来源(扣减时实际命中的池)
const (
QuotaSourceFree = "free" // 免费额度
QuotaSourceMember = "member" // 会员额度
QuotaSourcePaid = "paid" // 付费额度(次数包)
QuotaSourceNone = "none" // 已耗尽
)
// 额度设置键(settings 表)
const (
SettingQuotaPeriodDays = "quota_period_days" // 全局默认额度重置周期(天)
)
// DefaultQuotaPeriodDays 额度周期兜底值:settings 与 config 都未配置时使用。
// 后台「额度设置」可改,用户维度与会员套餐维度可各自覆盖。
const DefaultQuotaPeriodDays = 7
// 会员订单状态
const (
OrderStatusPending = 0 // 待支付
OrderStatusDelivered = 1 // 已支付并发货(会员权益已生效)
OrderStatusRefunded = 2 // 已退款
OrderStatusClosed = 3 // 已关闭(超时未支付)
)
// 虚拟支付环境与支付渠道
const (
XPayEnvProd = 0 // env 固定填 0(现网)
XPayCurrency = "CNY" // 币种固定 CNY
XPayMode = "short_series_goods" // 道具直购
PayChannelWx = "wxpay" // 微信虚拟支付(Android 等)
PayChannelIOS = "apple" // Apple IAP
XPayNotifyEvent = "xpay_goods_deliver_notify" // 发货推送事件名
XPayRefundNotifyEvent = "xpay_refund_notify" // 退款结果推送事件名
)
// 退款单状态(member_orders.refund_status,独立于订单状态 status)
const (
RefundStatusNone = 0 // 未发起退款
RefundStatusProcessing = 1 // 退款中(已调用平台退款接口,等待回调/查单确认)
RefundStatusSuccess = 2 // 退款成功(含手工退款登记)
RefundStatusFailed = 3 // 退款失败(可重新发起)
)
// ============================================================================
// 软件开发服务域(迭代-2026-09-29)
// ============================================================================
// 表名(服务商品 / 上传附件登记 / 服务介绍页模块 / 作品案例 / 报价申请 / 站内信 / 开发项目)
const (
TableGoods = "goods"
TableAttachments = "attachments"
TableServiceBlocks = "service_blocks"
TableServiceWorks = "service_works"
TableServiceQuotes = "service_quotes"
TableUserMessages = "user_messages"
TableServiceProjects = "service_projects"
TableProjectStages = "service_project_stages"
TableProjectNodes = "service_project_nodes"
TableProjectFiles = "service_project_files"
)
// 服务介绍页模块类型(service_blocks.kind,预设排版布局)
const (
ServiceBlockHero = "hero" // 头图横幅:大标题(支持 **高亮**)+ 标语
ServiceBlockRich = "rich" // 富文本:Markdown 图文混排
ServiceBlockImageText = "image_text" // 图文:图 + 说明文字
ServiceBlockImageList = "image_list" // 图片列表:多图网格(点击预览)
ServiceBlockFlow = "flow" // 服务流程:时间轴,每行「标题|描述」
ServiceBlockChecklist = "checklist" // 交付清单:勾选卡片,每行一项
ServiceBlockPrice = "price" // 价格卡:每行「名称|描述|价格」
ServiceBlockDeveloper = "developer" // 开发者简介:头像 + 名字 + 介绍
ServiceBlockWorks = "works" // 作品模块入口(进作品列表)
ServiceBlockGoods = "goods" // 服务与价格清单入口(进服务列表)
)
// 报价申请状态(service_quotes.status)
const (
QuoteStatusPending = 0 // 待处理
QuoteStatusQuoted = 1 // 已报价
QuoteStatusDone = 2 // 已成交
QuoteStatusClosed = 3 // 已关闭
)
// QuoteStatusNameOf 报价状态 → 展示名
func QuoteStatusNameOf(s int) string {
switch s {
case QuoteStatusPending:
return "待处理"
case QuoteStatusQuoted:
return "已报价"
case QuoteStatusDone:
return "已成交"
case QuoteStatusClosed:
return "已关闭"
}
return "未知"
}
// 站内信类型(user_messages.type)
const (
MsgTypeOrderDelivered = "order_delivered" // 会员开通 / 次数包到账(订单生效)
MsgTypeQuoteSubmitted = "quote_submitted" // 报价申请已提交(回执)
MsgTypeQuoteHandled = "quote_handled" // 报价处理结果(已报价/成交/关闭)
MsgTypeFeedbackReplied = "feedback_replied" // 反馈收到官方回复
MsgTypeProjectUpdate = "project_update" // 开发项目进度更新(阶段/交付物/绑定/交付)
MsgTypeSystem = "system" // 系统通知
)
// 开发项目状态(service_projects.status)
const (
ProjectStatusRunning = 1 // 进行中
ProjectStatusDone = 2 // 已交付
ProjectStatusClosed = 3 // 已关闭
)
// ProjectStatusNameOf 项目状态 → 展示名
func ProjectStatusNameOf(s int) string {
switch s {
case ProjectStatusRunning:
return "进行中"
case ProjectStatusDone:
return "已交付"
case ProjectStatusClosed:
return "已关闭"
}
return "未知"
}
// 项目阶段状态(service_project_stages.status)
const (
StageStatusPending = 0 // 未开始
StageStatusDoing = 1 // 进行中
StageStatusDone = 2 // 已完成
)
// 项目分期触发节点(service_project_pays.trigger)
const (
PayTriggerStart = "start" // 开工前(定金)
PayTriggerMidDemo = "mid_demo" // 中期演示确认后
PayTriggerOnlineBefore = "online_before" // 上线前(尾款)
PayTriggerOnlineAfter = "online_after" // 上线后(尾款)
)
// PayTriggerNameOf 分期触发节点 → 展示名
func PayTriggerNameOf(t string) string {
switch t {
case PayTriggerStart:
return "开工前(定金)"
case PayTriggerMidDemo:
return "中期演示确认后"
case PayTriggerOnlineBefore:
return "上线前"
case PayTriggerOnlineAfter:
return "上线后"
}
return t
}
// 项目分期状态(service_project_pays.status)
const (
PayStatusPending = 0 // 待支付
PayStatusPaid = 1 // 已支付
)
// 表名追加(开发项目四期:维护模板 / 付款分期模板 / 项目分期)
const (
TableMaintainPlans = "service_maintain_plans"
TablePayPlans = "service_pay_plans"
TableProjectPays = "service_project_pays"
)
// StageStatusNameOf 阶段状态 → 展示名
func StageStatusNameOf(s int) string {
switch s {
case StageStatusDoing:
return "进行中"
case StageStatusDone:
return "已完成"
}
return "未开始"
}
// 软件开发服务分类(goods.category)
const (
GoodsCategoryWebsite = 1 // 网站开发系列
GoodsCategoryMiniProgram = 2 // 小程序开发系列
GoodsCategoryApp = 3 // App 开发系列
GoodsCategoryDesktop = 4 // 桌面程序开发系列
)
// GoodsCategoryNameOf 分类值 → 展示名(未知值回落「其他」)
func GoodsCategoryNameOf(cat int) string {
switch cat {
case GoodsCategoryWebsite:
return "网站开发"
case GoodsCategoryMiniProgram:
return "小程序开发"
case GoodsCategoryApp:
return "App 开发"
case GoodsCategoryDesktop:
return "桌面程序开发"
}
return "其他"
}
// 附件上传场景(attachments.scene)
const (
AttachmentSceneAvatar = "avatar" // 用户头像(小程序端上传)
AttachmentSceneCover = "cover" // 服务封面(管理端上传)
AttachmentSceneAttachment = "attachment" // 通用附件(管理端上传)
)
// 站点 settings 键
const (
SettingSiteBaseUrl = "site.base_url" // 图片等静态资源的对外拼接前缀;留空 = 按请求 Host 动态拼接
SettingHomeFeatureCovers = "home.feature.covers" // 首页便捷功能卡背景图(JSON:featureKey → 相对路径),经 /app/config 下发
)