小程序一些功能更新

This commit is contained in:
李琦
2026-09-29 10:57:01 +08:00
parent 0cb70a7618
commit 3bb7fa8db3
96 changed files with 13890 additions and 213 deletions

View File

@@ -11,13 +11,44 @@ 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 // 额度用尽(免费/会员/付费额度都已耗尽)
CodePayFail = 4101 // 下单/支付参数异常
CodeLocked = 4001 // 模块未授权(等级锁定)
CodeQuotaExhausted = 4002 // 额度用尽(免费/会员/付费额度都已耗尽)
CodeNoteLimit = 4003 // 笔记数量超上限(免费版最多 50 条)
CodeNoteNotFound = 4004 // 笔记不存在/无权
CodeEventUnavailable = 4005 // 活动不存在或未发布
CodeSeatRateLimited = 4006 // 查座频率超限
CodeSeatCooling = 4007 // 查座连续失败冷却中
CodeImportConflict = 4008 // 导入存在未处理冲突
CodeLayoutConflict = 4009 // 布局版本冲突(CAS 失败)
CodePayFail = 4101 // 下单/支付参数异常
)
// 默认等级:会员到期后回落的目标等级
@@ -32,6 +63,7 @@ const (
TableUsageLogs = "tool_usage_logs"
TableWorkbench = "user_workbench"
TableAdminUsers = "admin_users"
TableAdminAuditLog = "admin_audit_logs"
TableFeedbacks = "feedbacks"
TableMemberPlans = "member_plans"
TableMemberOrders = "member_orders"
@@ -39,14 +71,229 @@ const (
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" // 免费额度