Skip to content

[Feature] 线下抽奖增加签字弹窗 #1

Description

@buduan

背景

线下抽奖现有链路:

  1. apps/web/src/views/lottery.vuelottery_mode === 'offline' 时需管理员登录,调用 POST /lottery/activities/:id/offline-draw
  2. 后端 apps/service/src/routes/lottery.js 创建 lottery_records(含 operator_id),返回中奖结果
  3. 前端弹出结果 Dialog,5 秒后自动关闭
  4. 活动详情 activityDetail.vue 展示抽奖记录,目前没有签字字段

系统设置页 settings.vuesuperSettings.vue 目前几乎是空壳。GET /system/config 是硬编码,config/system.json 只存安装元数据,没有 COS 配置,也没有写配置接口。

目标

  1. 仅线下抽奖:抽奖完成后弹出签字弹窗,是否开启在抽奖配置中决定
  2. 签字图片由后端上传到腾讯云 COS,不要把 SecretKey 下发到浏览器
  3. 未配置 COS 时,组织在抽奖配置中勾选打开签字功能
  4. 将 COS 对象键 / 访问信息写回对应 lottery_records
  5. 超级管理员可在系统设置中配置 COS:SecretIdSecretKeyBucketbucketname-appid)、Region,可选自定义域名 / 对象前缀
  6. 活动详情的抽奖记录可查看签字状态和预览## 建议流程
管理员输入抽奖码 → offline-draw 成功
  → 展示抽奖结果
  → 打开签字弹窗(取消现有 5 秒自动关闭,避免打断签字)
  → Canvas 导出 PNG
  → POST 到后端(record_id + 图片)
  → 后端 putObject 到 COS
  → 更新 lottery_records.signature_*
  → 记录页可预览

签字必须走后端上传。COS 官方也建议密钥只放服务端,并优先子账号最小权限。

签字库依赖

这里不做强制指定,但如果你没做过,可以看看!

方案 说明
推荐 A * signature_padszimek/signature_pad 行业标准 Canvas 库,星标多、TypeScript 友好,可包一层 Vue 组件,和现有 shadcn-vue Dialog 最贴合
推荐 B @selemondev/vue3-signature-pad Vue 3 + TS 封装,2026 仍在维护,底层同样是 signature_pad
推荐 C vue3-signature 中文社区常用封装,底层 signature_pad 5.x

不建议 vue-signature-pad(偏 Vue 2)。项目已有 @vueuse/corereka-ui Dialog,没有现成签字能力,需要新增依赖。

签字信息存储

腾讯云 COS:

  • 官方包:cos-nodejs-sdk-v5(要求 Node.js ≥ 9,本仓库 Node ≥ 20 满足)
  • 文档:快速入门接口文档
  • 签字图很小,用 putObject + Body: Buffer 即可,不必上分块上传
  • 配置项:SecretIdSecretKeyBucket(格式 name-appid)、Region(如 ap-guangzhou
  • 对象键建议:lottery-signatures/{activityId}/{recordId}/{timestamp}.png

可能的改造点

以下内容为 AI 根据项目已有情况进行整理,具体请根据实际改造需求进行改造,

前端改造点

  1. 新建签字组件(如 SignatureDialog.vue
    复用现有 Dialog:画布、清空、撤销、确认。触摸屏要可用(线下现场常见平板)。

  2. lottery.vue

    • offline 模式、抽奖成功后弹出签字
    • 去掉或延长结果 Dialog 的 5 秒自动关闭
    • offlineDraw 返回的 lottery_record.id 作为上传关联 ID
    • 处理未配置 COS、上传失败、用户拒签
  3. 系统设置

    • 建议做在 superSettings.vue(超级管理员),settings.vue 也可入口但需鉴权
    • 表单:SecretId / SecretKey / Bucket / Region,可选 Path Prefix、自定义域名
    • SecretKey 回显脱敏,留空表示不修改
    • 可选「测试连接」按钮
  4. API / 类型

    • apps/web/src/api.ts:上传签字、读写 COS 配置
    • types/api.tsLotteryRecord 增加 signature_url / signed_at
  5. activityDetail.vue 记录表
    增加「签字」列:未签 / 已签预览(Dialog 或新窗口)。DataTable 已支持自定义 render

  6. i18napps/web/src/i18n/locales/zh.json 补文案。

后端改造点

  1. 数据表 lottery_recordsscripts/install.js + 已有库的迁移)建议字段:

    • signature_key VARCHAR:COS Object Key
    • signature_url VARCHAR:访问 URL(或只存 key,读取时再签临时链)
    • signed_at DATETIME
    • 可选 signature_statusunsigned / signed
  2. ModelLotteryRecord.jscreateRecordfindByActivityactivities.jsGET /admin/activities/:id/records 的精简返回要带上签字字段。

  3. 签字上传接口(需管理员鉴权)
    建议:POST /lottery/activities/:id/records/:recordId/signature

    • 校验记录属于该活动、是线下抽奖、调用者有权限
    • 接收 multipart 或 PNG base64,限制大小(如 1–2MB)和 MIME
    • 上传 COS 成功后再写库
    • COS 未配置时返回明确错误
  4. COS 系统配置接口requireSuperAdmin

    • GET /system/cos-config:SecretKey 脱敏
    • PUT /system/cos-config:更新;SecretKey 空则保留原值
    • 可选 POST /system/cos-config/testgetService 或对测试 key 做 headObject/putObject
    • 存储位置实施者自定:扩展 config/system.json,或独立配置文件 / 表。密钥不要进 git,不要明文打日志。
  5. COS 客户端封装
    例如 apps/service/src/utils/cosClient.js,按 官方示例 初始化,统一 putObject / getObjectUrl。存储桶建议私有,预览用预签名 URL。

  6. 测试
    扩展 tests/routes/lottery.test.jsoffline-draw:上传成功、未配置 COS、非线下记录、无权限。配置接口测超级管理员权限和脱敏。

  7. OpenAPI:同步 apps/service/openapi.json

验收

  • 线下抽奖成功后可弹出签字板,能写、清空、提交
  • 线上抽奖不出现签字弹窗
  • 图片出现在配置的 COS 桶中,记录行能对应到该对象
  • 活动详情能看到签字状态并预览
  • 超级管理员能保存 COS 配置,普通管理员不能改密钥
  • 未配置 COS 时有明确错误,不丢抽奖结果
  • 密钥不出现在前端响应、日志、仓库里

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions