本件回答两个问题:这一大堆东西怎么切开,让每一块都能独立卖、独立交付、独立演进; 以及本轮新增的五项能力(分会场 / 3D 展位 / 物料裂变 / AI 中台 / 现场端侧)在已核实事实下该怎么定规格。 00 节先给出四项判断,其中一项是劝退;02 节是解耦的完整工程方案。
你这次给的约束条件(百人内小会场、礼品百元内)把上一份文档的抽奖合规结论整个改写了。 结论:那条最大的风险消失了,但换成了另外两条原本不起眼的风险。此外本轮新查出三条红线, 它们看似分散在三个毫不相干的领域,却全部发生在同一个技术环节——这是 07 节 AI 中台必须内置合规网关的直接依据。
| # | 判断 | 依据与后果 | 结论 |
|---|---|---|---|
| 1 | 抽奖的风险等级整个降级,但风险换了位置 | 礼品 ≤100 元 vs 法定最高奖 5 万元上限 —— 相差 500 倍,第三项完全无压力。 但《反不正当竞争法》第十条还有第(一)项「有奖销售信息不明确」和第(二)项「谎称有奖或内定人员中奖」, 这两项恰恰是现场小奖最容易犯的。 系统要做的从「金额校验」变成「可审计」。 |
风险改位 |
| 2 | 「学员发朋友圈换礼品」是广告行为,不是打卡 | 《互联网广告管理办法》第九条第三款:通过体验分享等形式推销商品或服务,并附加购物链接等购买方式的, 应当显著标明「广告」——你的物料恰恰都带链接。发布者是学员本人,学员即「广告发布者」(该办法第四条明列自然人适用)。 | 必须改造 |
| 3 | 佣金必须单层,且要在代码里硬限制 | 「层级在三级以上 + 三十人以上」即可追究组织领导传销活动的刑事责任(2013 年公通字 37 号)。
以销售业绩为依据的单层佣金完全合法(马哥给的报名费 10% 就是这一形态); 一旦允许「下线业绩给上线提成」,性质立刻变成团队计酬——司法解释的问题 Giving 见 06.3。 |
硬编码约束 |
| 4 | 3D 展位不要做成「拍照重建」 | 3D 高斯泼溅的几何精度约 7.82cm 平均误差,做不了面积计量;而「准确面积」恰恰是你最重要的字段
(面积决定价格)。面积只能来自场馆的 CAD 平面图。 且空宴会厅每次布置都不同,重建一次复用价值有限。 |
方案调整 |
当时我把「最高奖不得超过 5 万元」列为抽奖的头号风险,并据此设计了系统的硬校验。
你补充「礼品都是 100 元以下」之后,这条退居次要 —— 100 元 vs 5 万元,差 500 倍,永远不会触碰。
替换上来的两条是:信息不明确(第十条第一项)与 内定中奖(第十条第二项)。
它们的处罚依同一法条(第二十二条,四万元以上二十万元以下罚款);
更重要的是这两条在系统上的答案完全不同——不是「校验一个数字」,而是「让抽奖过程可复现、可举证」。详见 04.4。
| 事项 | 核实到的原文要点 | 来源 | 可信度 |
|---|---|---|---|
| 有奖销售 | 《反不正当竞争法》第十条:经营者进行有奖销售不得存在下列情形:(一) 所设奖的种类、兑奖条件、奖金金额或者奖品等有奖销售信息不明确,影响兑奖;(二) 采用谎称有奖或者故意让内定人员中奖的欺骗方式进行有奖销售;(三) 抽奖式的有奖销售,最高奖的金额超过五万元。 | 法律原文 | 已核实 |
| 朋友圈分享 | 《互联网广告管理办法》第九条第三款 + 第四条(利用互联网为广告主发布广告的自然人适用广告发布者规定)+ 第十六条(平台经营者义务) | 市场监管总局令第 72 号,中国政府网 | 已核实 |
| 分销层级 | 2013 年《关于办理组织领导传销活动刑事案件适用法律若干问题的意见》:层级三级以上且人员三十人以上追究刑责;第五条:以销售业绩为计酬依据的单纯「团队计酬」不作为犯罪处理,但实质以发展人员数量为计酬依据的以组织领导传销活动罪论处 | 最高人民法院公报 | 已核实 |
| 证书称谓 | 人社部函〔2022〕25 号:重点审核违规使用「中华人民共和国」「中国」「中华」「国家」「全国」「职业资格」「执业资格」「岗位合格(凭证)」「专业技术职务」「职业技能鉴定」「职业技能等级」等字样;禁用「包过」「上岗必须」「X 天拿证」「挂靠」「高薪入职」等宣传词 | 人社部官网 | 已核实 |
| 3D 重建精度 | 高斯泼溅平均几何误差约 7.82cm;摄影测量配合控制点可达 1~3cm;两者定位「可视化 vs 测量」 | 第三方技术评测 | 量级 |
| 3DGS 自采成本 | 国内公开测算:手机/微单采集 + 本地处理,单套综合成本约 ¥80~160,含 GPU 算力约 ¥16 | 公开技术博客测算 | 仅参考 |
广告标识义务、传销层级限制、证书禁用词 —— 分属三部完全不同的法规,看似互不相干。
但它们有一个共同点:全部作用在「内容被生成出来的那一刻」。
物料是 AI 生成的;推广链接是系统按模板拼的;课程宣传语很可能也是 AI 写的。
也就是说,违规内容不是人写出来的,是系统产出的 —— 而你要的恰恰是让它自动批量产出。
这意味着合规不能靠「运营审核一遍」这种人工环节兜住 —— 产量一大必然漏。
唯一可行的位置是在生成/分发的管道上做一道强制拦截。这就是 07 节的「合规网关」。
它不是外挂的审核功能,是 AI 中台的出口闸门:内容不出网关,就不允许被发布。
你说「会议系统、AI 中台都可以独立运作,也可以 OEM 给园区、会展中心、酒店」。
这句话成立与否,取决于一件事:「独立」能不能被验收。
如果它只是一个形容词,那么三个月后一定会变成「改一个抽奖字段要回归测试整套 CRM」。
所以先把标准定死 —— 下列五条,缺一条就不许宣称独立。
| # | 条件 | 可验收的定义 | 为什么这条不能省 |
|---|---|---|---|
| 1 | 数据自洽 | 该产品拥有的全部数据表,与其他产品的表之间没有外键约束。删除该产品的全部数据后,其余产品功能不受任何影响。 | 最常见也最难改。共享外键一上手就赢不了,后期只能靠数据迁移才能分开。 |
| 2 | 部署独立 | 可以只对它发一次版:npm run deploy --pkg=booth,其余产品不停机、不重启、不需同步发布。 |
做不到全量发布,就永远不敢在会前一周改动 —— 而会场类产品恰恰最需要会前改动。 |
| 3 | 依赖单向 | 允许依赖下层内核,禁止依赖兄弟产品。产品之间不认识对方的存在。这条在 CI 里用静态检查卡住(见 02.4)。 | 今天是 lateral 依赖,明天就是循环依赖。循环依赖出现之日就是单体退化之日。 |
| 4 | 契约通信 | 跨产品只能通过「 API 调用」或「领域事件」两种形式,禁止跨库 JOIN、禁止 import 对方的内部类。 | 这是把「物理拆分」推后也能保持逻辑解耦的唯一办法 —— 你可以先不拆服务,但必须先拆契约。 |
| 5 | 账单可算 | 该产品能独立核算:独立定价、独立统计用量、独立算出毛利率。 | 算不出账的产品,被砍掉时无法判断得失 —— 结果就是什么都不敢砍,需求无限膨胀。 |
条件 1~3 是能不能拆,条件 4 是拆完之后会不会重新长回去,条件 5 是商业上值不值得拆。
绝大多数听起来很美的「微服务」死在条件 4 —— 代码拆了,但彼此 import 对方的内部实现,改一处两边一起崩。
| 产品 | ①数据自洽 | ②部署独立 | ③依赖单向 | ④契约通信 | ⑤账单可算 | 备注 |
|---|---|---|---|---|---|---|
| M1 智能会场 | ◯ | ◯ | ◯ | ◯ | ◯ | 天然独立,学员数据自成一域 |
| M2 3D 展位 | ◯ | ◯ | ◯ | △ | ◯ | ④ 有风险:很容易直接读写 M1 的场次表 |
| M3 裂变分发 | ◯ | ◯ | △ | ◯ | ◯ | ③ 有风险:天然想依赖 M2 的展商数据 |
| M4 AI 中台 | ◯ | ◯ | ◯ | ◯ | ◯ | 独立性最强,且是唯一可反向输出的 |
| M5 现场端侧 | △ | ◯ | ◯ | ◯ | △ | 离线数据同步是难点;建议单卖硬件+服务包 |
| M6 线索总线 | ◯ | ◯ | ◯ | ◯ | ◯ | 可做成完全独立的小服务 |
| M7 保本测算 | ◯ | ◯ | ◯ | ◯ | ◯ | 上一个文档已论证:独立性最好的切入点 |
| M8 获客客服 | △ | ◯ | △ | ◯ | ◯ | 基于开源件二次封装,注意许可证边界 |
◯ = 设计上可达 | △ = 需要在 02 节用手段强制 | ✕ = 需要重构。 当前八项全部可达 ◯,前提是把 02 节的两处薄弱点先补掉。
两个产品都需要「场次」,于是共用一个 Entity 类 —— 半年后给 A 加字段,B 崩了。 正确做法:各自存自己关心的那几个字段(往往是 id 和名称),靠事件同步。重复三行代码,换回十年不耦合。
园区客户要求改个字段、改个流程 —— 一行 if (tenant == 'xxx'),就是最终长出几十个分支的开端。
正确做法:一切差异必须是配置或插件,内核只读。改不了内核,你就知道自己该拒绝这个需求,而不是接下一个外包项目。
最热闹的失败方式:起了八个 Kubernetes 服务,但它们连同一个库同一个表 —— 于是获得了分布式的全部缺点,没得到任何解耦好处。 正确做法:数据先按 01.1 第 1 条切干净,服务怎么部署是后面再说的事。
你这套系统里有两套隔离维度经常被混为一谈:多租户隔离,和产品边界隔离。 它们不是一回事,搞混是这类平台最常见的结构性错误。
| # | 规则 | 具体定义 | 违反的后果 |
|---|---|---|---|
| 1 | 一个产品一个 schema | P1 每个产品独占 PostgreSQL 一个 schema(venue / booth / viral / aihub ...),产品名称即 schema 名。 |
共享一个 schema,就等于把删除权交给了所有人 |
| 2 | 禁止跨 schema 外键 | 表之间只能用「弱引用」—— 存对方 id(UUID),不加 FOREIGN KEY 约束。引用完整性由应用层保证,不做级联删除。 | 有了外键,删一条展位订单会连带级联删掉别的模块的记录 |
| 3 | 禁止跨 schema JOIN | 查询只能在本 schema 内进行。需要别的产品数据,要么调 API,要么订阅事件后冗余存储到自己库里(推荐后者)。 | 一次 JOIN 让两个产品的发布绑死在一起 |
| 4 | 每个表必须有 tenant_id | 除系统级字典表外,业务表一律带 tenant_id UUID NOT NULL,并启用行级安全策略。缺失该列的表,迁移脚本直接报错。 |
这是唯一一种「一次疏忽就是数据泄露」的错误 |
| 形式 | 什么时候用 | 约束 |
|---|---|---|
| 查询 API | 需要即时读到对方的数据(如展位页要显示场次名称) | 只读,且必须由被调用方提供稳定契约;调用方要把结果缓存或落地,避免强依赖可用性。 |
| 命令 API | 需要对方做事(如「冻结这个展位」) | 幂等,必须带幂等键;失败可重试。 |
| 领域事件 | 通知「某件事已发生」(如 attendee.checked_in) |
首选方式。单向广播,发布者不知道谁在听 —— 这是最松的耦合。采用事务发件箱模式保证不丢。 |
它不是架构洁癖,是解耦力度不一样:
调用 API 时,A 知道 B 的存在,B 挂了 A 就得做降级;发事件时,A 根本不知道有没有人在听,B 挂了 A 完全不受影响。
具体到你的场景:学员签到是一个事件。AI 中台可以听(做人群画像)、线索总线可以听(给展商推通知)、
会场大屏可以听(更新到场率)——三个订阅方随时可以加,会场系统一行代码都不用改。
这就是「 _____ —— 这正是你要的「}}}
换成三个 await NotificationService.xxx() 调用,每加一个订阅方,签到服务就得改一次。
规则写在文档里等于没有。必须让它在 CI 里跑起来,违反就红。
用 dependency-cruiser 或自定义 ESLint 规则:P1 产品模块只能 import @core/*,禁止 import 其他产品模块的内部文件。
扫描迁移文件与查询语句,正则匹配 JOIN <其他schema>. 与缺 tenant_id 的建表语句,直接报错。
产品对外 API 与事件 schema 变更必须通过兼容性检查;不兼容变更需走「新版本号 → 双写过渡 → 下线旧版」三步。
CI 里每周一次的流水线:只构建并部署某一个产品到测试环境,其余产品镜像不变。跑不通说明耦合了。
// .dependency-cruiser.js —— 放进 CI,违反即构建失败 module.exports = { forbidden: [ { name: 'no-cross-product-import', comment: 'P1 产品之间禁止互相引用内部实现,只能走 @core 或事件', from: { path: '^src/products/([^/]+)/' }, to: { path: '^src/products/([^/]+)/', pathNot: '^src/products/$1/', }, }, { name: 'no-deep-import-into-core', comment: '只能通过 @core/xxx 的公开入口,禁止直接摸内核内部文件', from: { path: '^src/products/' }, to: { path: '^src/core/(?!index|public)' }, }, ], };
-- 每个产品一个 schema;禁止跨 schema 外键,只做弱引用 CREATE SCHEMA booth; CREATE TABLE booth.slot ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, -- 垂直切:租户 event_ref UUID NOT NULL, -- 弱引用 venue.session.id,无外键 code TEXT NOT NULL, area_sqm NUMERIC(8,2) NOT NULL, -- 来自场馆图纸,不是 3D 重建结果 price_cny NUMERIC(12,2) NOT NULL, geom JSONB NOT NULL, -- 摊位多边形顶点,业务图层的真相来源 status TEXT NOT NULL, -- available / held / sold / blocked UNIQUE (tenant_id, event_ref, code) ); -- 行级安全:漏写 tenant_id 过滤也不会越权 ALTER TABLE booth.slot ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON booth.slot USING (tenant_id = current_setting('app.tenant_id')::UUID);
你最终可能确实需要拆,但顺序不能错:先按上面的规则把数据和契约切干净,
将来某个产品真的需要独立扩容或被单独交付给 OEM 客户时,再把它抽出去单独部署 —— 那时的成本是改配置,不是重构。
反过来(先拆服务后拆数据)会得到分布式系统的全部缺点,却得不到解耦的任何好处。
同一套 booth.slot 表,卖给会展中心和卖给酒店,卖的不是同一个东西。
OEM 最常见的失败就是把「换 logo + 换域名」当成全部工作,结果客户问「这对我有什么用」时答不上来。
| 买家 | 他的真实痛点 | 他要买的 | 交付形态 | 定价方式 | 优先级 |
|---|---|---|---|---|---|
| 大型会展中心 | 场租单价被压、淡季空置、展后用完即走拿不到数据;展商年年流失 | 场地的数字化销售前台:把每一个厅、每一块区域变成可在线看位、选位、下单的商品 | 私有化 + 其域名下嵌 iframe | 年费 + 成交额抽成(低) | 首选 |
| 大型酒店(宴会厅) | 宴会部靠电话和熟人;婚宴公司-会议品类的线上成交几乎没有;客人问「你们厅多大」要看 PDF | 宴会厅的在线选位与预订,尤其是培训/会务这类非婚宴场景 | SaaS 为主,PMS 对接 | 月费按门店数 | 首选 |
| 地方园区 / 政府机构 | 要给入驻企业做「增值服务」但手上没东西;需要数字化政绩 | 园区企业服务门户里的「活动与培训」板块 | 私有化,数据不出园区 | 项目制 + 年运维 | 跟进 |
| 行业主办方 / 主播 | 前面几份文档论证过:跑得动一场,跑不动第二场 | 整套办会操作系统(M1+M2+M3+M7) | SaaS 多租户 | 按场次 / SaaS 订阅 | 自有为主战场 |
| 活动公司 / 承办方 | 接单靠关系,交付靠 Excel,和客户对账靠微信截图 | 给客户看的进度透明工具(这是他拿单的武器) | SaaS,多项目隔离 | 低月费或免费,靠 GMV 反哺 | 渠道角色 |
会展中心话语权强、决策链长、往往已有成熟系统和长期供应商; 而酒店的痛更具体、更没人管 —— 宴会部至今基本靠电话和微信。 更重要的是:酒店同时也是你自己的供给侧。 卖给酒店一套系统,顺带把这家酒店变成了你可以随时调用的会场资源 —— 卖出一个客户,同时解决一个供给。 这在别人那里是两件事,在你这里是一件事。这是你的结构性优势。
| 层次 | 可调整的内容 | 实现方式 | 目标覆盖率 | 说明 |
|---|---|---|---|---|
| 品牌层 | 名称、Logo、域名、主色、字体、邮件/短信签名、小程序名称 | 配置文件 + 主题变量 | 100% | 必须全部可配,且不能改代码 |
| 业务层 | 模块开关、字段是否必填、审批流节点、计价规则、角色权限模板 | 功能开关 + 流程模板 | 80% | 剩下的 20% 用插件接口,不改内核 |
| 数据层 | 数据存在谁那里、是否能导出、留存多久 | 部署形态(SaaS / VPC / 本地) | 100% | 每层必须有明确选项,不能口头承诺 |
| 内核层 | 数据库结构、核心领域逻辑、认证与权限机制 | 不可协商 | ||
内核一旦对某个客户开放修改,你就获得了一个客户和一份不可复用的代码。
判断标准依旧是那一条:新增一个客户的边际交付工时是否趋近于零。
实操建议:当客户提出一个必须改内核才能满足的需求时,先算账 ——
若这批需求可以沉淀成一个通用插件,且预估有 3 个以上客户会用,则做;否则明确告知「这条我们不做,但可以这样绕过去」。
拒绝一个需求,比接下一个项目更有价值。
前面《后端选型与供给侧引擎》里论证过给客户部署开源件的最大风险是版本碎片化。OEM 完全同理 —— 而且更贵,因为 OEM 客户是大客户,他会打电话。
| 手段 | 做法 | 不可妥协的程度 |
|---|---|---|
| 单一版本轨道 | 所有 OEM 实例跑同一个版本号,不维护多个长期支持分支 | 高 |
| 自动升级 | OEM 实例默认开启自动升级,窗口可协商但不能无限推迟 | 高 |
| 遥测与健康回传 | 版本、错误率、关键指标回传;写入合同的运维条款 | 中(先谈清楚,避免事后争议) |
| 集中管控台 | 一侧能看见所有 OEM 实例健康状态,出问题比你先知道 | 高 |
| 升级前自动备份与回滚 | 每次升级前快照,一键回滚 | 高 |
做不到「一台服务器上一键安装 + 后台点一下升级」之前,不要卖 OEM。 否则你卖的不是产品,是一份没有出口的运维债务。
前几份文档查实过:Dify 的 Modified Apache 2.0 附加条款限制多租户供给;Chatwoot 的 SSO/白标/权限在企业版包里。 外购/外挂的 AI 能力,你无法给它任何许可证承诺。自建的部分反过来 —— 每一行代码都是你的。
OEM 客户买会场系统时,往往会顺带问「你们那个 AI 能不能也给我用」。 此刻 M4 就是一个完全独立的商品:知识库 + 问答 + 合规网关,不依赖任何会场功能。
这也是它必须满足 01.1 那五条独立性标准的第二个理由 —— 第一次是因为红线就压在它这里(架构上必须隔离),第二次是因为它自己就是一个独立商品(商业上独立核算)。
你说的那段现场观察里,最关键的判断是这句:「抽奖只在百人以内的小会场,大会场不做」。 这句话的产品含义远大于它看起来 —— 因为它意味着分会场不是主会场的附属品,它是一个独立的经营单元: 有自己的主题、自己的赞助商、自己的礼品预算、自己的学员名单。
| 你的原话痛点 | 对应功能 | 载体 | 说明 |
|---|---|---|---|
| 学员找不到自己的会场 | 分会场导航页:全部分会场主题、时间、地点、导师、当前状态 | 小程序 + 蓝牙信标 | 走到哪个区域页面自动切换(蓝牙信标方案已在上一份文档核实) |
| 不知道主题会场具体内容和流程 | 会程详情页:议题、发言单位、时间节点、讲义下载 | H5 | 发言单位就是赞助商,天然是招商位 |
| 不知道哪些学员进来了 | 分会场扫码签到 | 二维码 + 小程序 | 这是整套数据资产的源头 |
| 咨询互动 | 场内提问墙:匿名提问、点赞排序、主持端投影 | H5 + 大屏 | 提问即需求采集,见 04.5 |
| 抽奖 | 可审计抽奖 | 小程序 | 见 04.4,重点变了 |
| 收集用户需求 | 需求登记:勾选式需求标签 + 留资授权开关 | H5 | 勾选比开放填空填写率高得多 |
| 分摊礼品经费 / 给服务商做推广 | 分会场招商位与礼品认领 | 后台 | 见 06 节与 09 节 |
| 结束后我们不知道谁进了哪个场 | 分会场复盘报表 | 后台 | 这是给赞助商看的交付物 |
-- 分会场:一个独立经营单元,有自己的主题、场地容量、赞助归属 CREATE TABLE venue.sub_session ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, event_ref UUID NOT NULL, -- 弱引用主会场 topic TEXT NOT NULL, room_label TEXT, starts_at TIMESTAMPTZ, capacity INT, beacon_key TEXT, -- 蓝牙信标标识,用于自动识别到场 sponsor_ref UUID, -- 弱引用该分会场的冠名赞助商 status TEXT NOT NULL ); -- 签到记录:每条都要能回答「谁、几点、进的哪个场、授权的什么」 CREATE TABLE venue.sub_checkin ( id BIGSERIAL PRIMARY KEY, tenant_id UUID NOT NULL, session_ref UUID NOT NULL, attendee_ref UUID NOT NULL, via TEXT NOT NULL, -- qr / beacon / manual checked_in_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (tenant_id, session_ref, attendee_ref) ); -- 需求勾选:结构化,便于二次销售与课程设计 CREATE TABLE venue.demand_tag ( id BIGSERIAL PRIMARY KEY, tenant_id UUID NOT NULL, session_ref UUID NOT NULL, attendee_ref UUID NOT NULL, tag_code TEXT NOT NULL, lead_consent BOOLEAN NOT NULL DEFAULT false, -- 是否同意转给服务商 created_at TIMESTAMPTZ NOT NULL DEFAULT now() );
既然《反不正当竞争法》第十条第(三)项(最高奖 5 万元)对百元礼品不构成压力, 系统真正要防的是第(一)项「信息不明确」和第(二)项「内定中奖」。
| 法定要求 | 系统落地做法 | 为什么这样做才有效 |
|---|---|---|
| 信息明确(种类、兑奖条件、奖品) | 抽奖前展示规则卡:奖项名称、数量、奖品、参与条件、开奖时间、兑换方式。规则卡生成版本号,每次展示都可回溯到当时那一版。 | 纠纷发生时,你能拿出的不是「我们当时说了」,是一份带时间戳的规则快照 |
| 不得内定 | 开奖瞬间冻结名单快照(写哈希);随机数种子公开;抽奖结果 = f(快照哈希, 种子)。任何人可复核重算,得到同一批中奖者。 | 把「我们没作弊」从辩解变成可验算的数学 |
| 奖品来源可追 | 记录每件奖品的提供方(主办方 / 某服务商),并在规则卡上明示。 | 奖品由服务商提供时,明确谁是经营者,避免责任归属不清 |
// 抽奖结果必须可复现:同样的输入,任何时候重算都得到同一个人 async function draw(req) { // 1. 冻结名单并落哈希 —— 之后名单再变也不影响已开出的结果 const pool = await freezePool(req.sessionRef, req.ruleVersion); const poolHash = sha256(pool.attendeeIds.join(',')); // 2. 种子:由规则版本号 + 冻结时间戳 + 可选的现场公证输入构成 const seed = sha256(`${req.ruleVersion}:${pool.frozenAt}:${req.liveInput ?? ''}`); // 3. 确定性洗牌 —— 不用 Math.random(),它不可回放 const rng = makeSeededRandom(seed); const winners = await pickWithoutReplacement(pool, rng, req.prizeCount); // 4. 存证:这条记录就是日后用于举证的完整材料 await saveEvidence({ sessionRef: req.sessionRef, ruleVersion: req.ruleVersion, poolHash, seed, liveInput: req.liveInput ?? null, winners, operatorId: req.operatorId, drawnAt: new Date(), }); return winners; // 事后调用 verify(poolHash, seed) 可复算,结果必然一致 }
对开发来说这只是几十行代码,价值在于它把举证责任从「人怎么说」转移到了「记录怎么算」:
学员质疑「是不是内定了」时,答复不再是道歉或口头保证,而是「这是当时的名单哈希、种子和算法,你可以自己重算」。
对主办方而言,这种可举证能力本身就是产品的一部分 —— 它是别家抽奖软件给不了的东西。
开放填空题的填写率通常远低于勾选;而结构化标签才能汇总成商品。 「我想找储能项目甲方」和一段自由文本,前者能直接卖给服务商,后者不能。
勾需求是免费的;「同意把我的联系方式给相关服务商」必须是独立开关,默认关闭。 这是《个人信息保护法》下最稳妥的做法,也是后续能否变现的前提 —— 未经授权的线索一分钱都不值。
需求标签汇总后有三条变现路径:给主办方(下次办什么课)、给服务商(精准线索,按条或打包计费)、 给产品研发(哪些主题值得做)。第一条免费,第二三条收费,而它们的成本是同一份数据。
你想的是「拍照实现 3D 效果 → 展位规划(准确 3D 位置、价格、面积)→ 线上预览 → 预定 → 线上装修」。
这条链里,第一步到第二步之间断开了:
拍照重建出来的东西做不了面积和尺寸的法定依据,而面积恰恰是定价的基础。
但 3D 拍照本身不是没用 —— 它在另一个场景里价值极高(见 05.5)。
结论不是放弃 3D,是把它放回正确的位置。
| 维度 | 3D 高斯泼溅(你设想的方向) | 摄影测量(Mesh) | 谁更适合 |
|---|---|---|---|
| 几何精度 | 平均误差约 7.82cm | 配合控制点可达 1~3cm | 摄影测量 |
| 渲染速度 | 浏览器实时 60fps | 需预处理 | 高斯泼溅 |
| 复杂材质 | 玻璃、反光、水表现优秀 | 容易出网格瑕疵 | 高斯泼溅 |
| 可编辑性 | 有限(不能像 mesh 那样改特定的面) | 完全可编辑 | 摄影测量 |
| 模型体积 | 压缩后约 8~30MB,加载 3 秒内 | 纹理图导致体积更大 | 高斯泼溅 |
| 专业定位 | 「可视化」对战「测量」 —— 这是两者的本质区别 | — | |
来源:第三方评测机构 2026 年对比数据。说明:7.82cm 是平均几何误差,用于观看完全足够,用于「这个摊位 36.5㎡」则不可。
你提到「预定后线上装修成自己公司的宣传效果,上传可以分享给客户的公司网站、APP、方案文档」。 这一句里包含两种不同的东西,容易做成成本黑洞。
| 诉求 | ❌ 过度设计的做法 | ✅ 正确的做法 | 理由 |
|---|---|---|---|
| 装修成宣传效果 | 让展商在 3D 里拖拽搭建 3D 展台、换材质 | 上传背景图 + Logo + 主色,自动生成谈二维码页 | 展商的目的是「有人扫的时候有东西看」,不是玩 3D |
| 上传公司网站、APP、方案文档 | 做一个站内 CMS 让用户重新录入一遍 | 挂载外链 + 文件直传,学员扫码后一站式获取 | 重新录入的填写率极低,且立刻变成过时信息 |
| 能分享给客户 | 复杂的私域系统 | 一个短链 + 一张带二维码的海报 | 展商真正会用的是扫码那一刻能不能顺畅拿到资料 |
这个子系统的价值不是「3D 展位图」,而是 「数字化展包」 ——
展商买了摊位,拿到的是一个能扫码带走资料的页面,而不是一块物理空间。
这也解释了为什么之前几份文档反复强调「数据和结构化 Ask 才是资产」:
物理摊位一天就拆了,数字展包能长期存在,而且能被统计访问量。它能按访问量向展商收费。
-- 面积、价格来自第 2 层;3D 底图只是可选的可视化资源 ALTER TABLE booth.slot ADD COLUMN visual_ref TEXT; -- 3D 资源地址,可空 ALTER TABLE booth.slot ADD COLUMN walk_desc TEXT; -- 位置描述:距入口/主动线/邻近业态 ALTER TABLE booth.slot ADD COLUMN source TEXT NOT NULL DEFAULT 'blueprint'; -- 数字展包:这才是展商真正购买并长期使用的东西 CREATE TABLE booth.digital_kit ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, slot_ref UUID NOT NULL, exhibitor_ref UUID NOT NULL, theme_json JSONB, -- 背景/主色/Logo,而非 3D 模型 links_json JSONB, -- 外挂的官网/APP/文档链接 docs_json JSONB, -- 直传文件清单 short_code TEXT UNIQUE, -- 摊位二维码指向的固定短码 view_count BIGINT NOT NULL DEFAULT 0 -- 可计量,可作为加价依据 );
| 场景 | 3D 拍照的价值 | 是否做 | 说明 |
|---|---|---|---|
| 自己办的培训会 (临时布置的宴会厅) |
低。每次布置都不同,重建一次只能用一场;而且是临时场地,没人看 | 不做 | 用第 2 层矢量图 + 几张现场照片足够 |
| OEM 给会展中心 (固定场馆) |
高。一次扫描,承接的所有活动都能复用;本来就有「虚拟展馆」招标需求 | 做 | 分摊到几十场活动后,单场成本近乎为零 |
| OEM 给酒店 (固定宴会厅) |
中高。宴会厅布置变化不大,客户决策周期长多次查看 | 做 | 尤其适合婚宴之外的会议场景 |
同一套代码,技术组件是可选安装的,而不是必须。
自用时关掉 3D 也能跑(节省 ¥80~160/场 × N 场);
卖给会展中心时打开它,它从「我自己用不上」变成对方的采购理由。
这也再一次说明 01 节的判断:产品有多条不同的销售路径,才值得去谈迁移。只有一条用途的功能,做了就是浪费。