Rodger-Wang
|
c8765a7c31
|
清理:第 2 批,Go 后端零引用的包、未注册的 mcp tool 与无人读的配置段(约 1.2 万行)
- 整包删除(只有自身测试引用):lego/sys/{gin,timewheel,lghttp,sdk/bytedance/tos}、
lego/utils/crypto/{gm,gm_java,sra,base64}、lego/utils/container 根目录及 addr/ip/sortslice/version
(container/id 保留)、sys/{dify,deepseek,qweather,coze,axml,haifanwu,aliyun/sts,websearch/bingsearch,websearch/bravesearch}
- mcp 的 tool_finance / tool_spotify_music_* 从未 RegisterComp,连带 sys/juhe、sys/spotify 一起删;
sys/openai 测试里依赖 juhe 的 finance 工具用例随之去掉
- modules/timer/timer_uselog.go 从未装上;comm/const.go 四个零引用的 Module* 常量
- 配置模板去掉没人读的段:home 的 deepseek/ali_filetrans、mcp 的 juhe/spotify/bravesearch、
console 的 cos、gateway 白名单里没有对应路由的 public_info
验证:go build/vet 通过;go test 除两个本来就依赖本机 MySQL / 外部 HTTP 的集成测试
(lego/sys/mysql、modules/agents,改前同样失败)外全部通过。
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 990c09f066e63a31c483adff1a6d2e3c9ab72c3e)
|
2 weeks ago |
Rodger-Wang
|
0ccc978993
|
清理:第 1 批,移出误进 git 的构建产物与本机文件(约 31MB)
- apps/services/console(29MB go build 产物)、apps/admin/.nuxt(45 个 nuxt 生成文件)
- dump.rdb、5 个运行日志、10 个 .DS_Store、1 个 .pyc
- 只从索引移除,工作区文件不动;.gitignore 补上此前没覆盖到的几类
- 附上本轮清理的落地文档 docs/2026-09-24-无用代码清理-落地文档.md
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 264acd2c742617a971e918cd22a0d816bf0ad44b)
|
2 weeks ago |
Rodger-Wang
|
c12651e79c
|
语音纪要:录音语言自动识别、转写不翻译、翻译只作用于总结;可选总结模型;详情加 PPT 页签(仅界面)
客户端
- 首页标题「语音记录助手」→「语音纪要」,去掉顶栏右侧的算力摘要
- 「生成」弹窗:去掉录音语言(传空,交给识别服务自动判断);「目标语言」改为「翻译」,
默认「默认」= 不翻译、总结跟随录音语言,选了语言 = 总结/概览用该语言输出;
新增「总结模型」一行(echomeet_getsummaryservices 拉列表,记住上次选择,summary_svc_id 带进 starttask/summary)
- 重新总结选「默认」时传 tolanguage=auto(该接口空串历来表示沿用上次语言)
- 纪要详情在思维导图右边加「PPT」页签:示意图 + 「生成 PPT」按钮,点了提示即将上线
服务端(echomeet)
- 转写不再翻译:TranslateProcess 只做 Translate=Original;StartTask 不再选翻译服务
- 总结输出语言由 summaryLanguageInstruction 追加在模板 system prompt 末尾(空=跟随转写语言)
- 模板解析 ResolveSummaryTemplate:有目标语言先找该语种版本,否则用客户端选中的那条
- formlanguage / tolanguage 的 "auto" 与空串同义;Azure 批量转写不指定语言时带候选语种自动识别,被拒退回 en-US
- console「会议配置 → 会议总结」:默认模型之外可勾选「供用户选择的模型」,勾了即打开应用级「允许客户端选择」
⚠️ 行为变化:服务端部署后老版本 App 也不再翻译转写(原「英文录音→中文」现在是英文转写 + 中文总结)。
验证:dart analyze 无 error;flutter test 224 全过;go build/vet 通过,echomeet/console 测试通过;
admin npm run build 通过。已部署测试服(app/console/admin),release 包装到 ALN-AL80。
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
2 weeks ago |
Rodger-Wang
|
6c9e0d48eb
|
语音纪要:有算力却提示余额不足(客户端按停用的旧会议额度拦截);转写失败按扣费明细原路退还算力
客户端
- 生成总结前不再判旧「会议额度」meetintegral <= 0 就弹余额不足:那个桶已随会员与算力改造停用,
新账号恒为 0,于是有算力也被拦,请求都没发出去。准入交给服务端(没 VIP 回 VipRequired,
算力不足按后台闸门处理),同翻译那次的修法
- 语音纪要页顶栏:原来按旧额度显示剩余时长 + 进度条(恒为 00:00:00),改为显示算力(user_getcompute)
服务端
- 开始任务扣算力后把明细记在记录上(新列 computeuser / computedevices,AutoMigrate 自动加);
转写失败的八条路径与开始任务中途报错,一律经 refundRecordCompute / refundCharge 按明细原路退回:
设备部分减回 computeused、账户部分加回 computebalance;先在库里原子认领明细再退,保证只退一次;
超额部分从未扣过,不退
- 去掉短音频失败时 user.Meetintegral += 秒数 + 整行 updateuser(退进了停用的旧桶,整行写回还可能覆盖并发的算力变动)
- 新增流水类型 UserLogType.ComputeRefund = 6
- comm:ComputeResult 带每台设备的扣费明细(Cuts);新增 EncodeDeviceCharges / DecodeDeviceCharges / RefundComputeUsage,
编解码有单测(MAC 带冒号,按最后一个冒号切)
验证:go build/vet 通过,comm / echomeet / user 测试通过;dart analyze 无 error,flutter test 221 全过;
测试服已部署 app + console,两列已加上、五个服务正常。
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
2 weeks ago |
Rodger-Wang
|
d6892eb3d6
|
后台管理下载落地页;实名认证显隐与游客登录同一个口子
下载落地页(版本与分发页)
- 「下载与说明」区顶部新增「下载页」一行:编辑 / 保存 / 查看 + 地址输入框
- 编辑:CodeMirror 编辑线上 HTML,改动自动存浏览器草稿;保存才发布到分发桶
(服务端 PutObject,强制 text/html;charset=utf-8 + no-cache),地址写入 app_release.download_page
- 查看:右侧抽屉预览,可切「当前编辑内容 / 线上版本」、手机 / 电脑尺寸
- 地址只能改文件名,域名与 /<应用>/ 目录由服务端卡死(TestParseDistPageURL)
- 新接口 api_getdistpage / api_savedistpage;admin 新增 codemirror 依赖
实名认证显隐
- 能看到游客登录的那一版(送审版本)才显示实名认证并按实名限制功能;
其余版本实名整个隐藏,功能不受实名限制。不分 Android / iOS
- 服务端:comm.AppRelease.ShowIdVerify(= ShowTourists)→ DBChannelApp.idverify 下发
- 客户端:VersionUpdateService.showIdVerify → IdVerifyGuard 隐藏即放行;
「我的」页实名行、卡片「未认证」角标跟着显隐;老服务端无 idverify 时退回 tourists
- 后台「游客显隐控制」页说明改为同时控制游客登录与实名认证
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
2cf260eb05
|
服务端:音乐模块与腾讯云 COS 整体下线;新增分发桶(App 安装包与录音/固件分开放)
音乐:sys/music、sys/musicobj、modules/music、mcp/tool_music.go 全部删除,home 的
模块列表与 musicobj 初始化、两份 yaml 模板里的 musicobj 段一并去掉。起因是它的
ApiBaseUrl 指向上一代品牌域名;客户端的音乐模块本来就早已删除。
⚠️ 咪咕音乐(tool_migu_music + migu 服务)没有动,别一起当成音乐链路删了。
腾讯云 COS:sys/tencentyun 整个删除,home/api 的 cos.OnInit 与配置段、
comm.svcRuntimeSections 的 cos 托管一并去掉。头像上传改走阿里云 OSS(与
user_getuploadurl、console 后台直传同一个桶)。modules/api/api_getcostoken.go 是
孤儿(后台调的其实是 console 那个同名路由,客户端从不调),删除。后台两处的
腾讯云 COS 卡片/模板与巡检探针也删——服务端读存储配置的唯一入口只认
provider=aliyun_oss,留着是「配得上却不生效」的陷阱。存量 tencent_cos 行不动。
⚠️ 老客户端仍读业务库 config 表的 COS_* 直传腾讯云,那 5 个键别删;历史录音的
audiourl 是 COS 绝对地址,不会自动搬家。
分发桶:comm.AliyunOSSConf 加 DistBucket/DistDomain(留空用 ymai-dl /
dl.ymaikj.com),凭据与主桶共用。后台「上传 APK」走 api_getcostoken 带
target:"dist":key 不加 EAIMAR 根前缀(分发桶直接挂在 dl.ymaikj.com 上)、目录走
白名单、按应用分子目录(一个桶放多个应用,不分目录会静默互相覆盖)。
⚠️ Content-Type 由服务端按扩展名强制并回带给前端:浏览器对 .apk 给出的 File.type
往往是空串,前端照它发 PUT 会与预签名不符、OSS 回 403,而报错里完全看不出是类型
对不上。⚠️ 分发桶必须单独配 CORS(来源 console.ymaikj.com、方法 PUT),
浏览器直传打的是桶的 OSS endpoint 而不是自定义域名。
另含下载落地页 deploy/dl(含 OSS 部署教程)与各 deploy 脚本/README 的同步更新。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
9f105bf7c8
|
会议纪要分享:链接最后一段改成服务端签发的只读票据,不再放登录 JWT
链接格式 share.ymaikj.com/share/{serverHost}/ymaikj.com/{id}/{ticket}。最后一段
以前放的是 logintoken(完整登录 JWT)——谁拿到链接就拿到了整个账号,转发进群、
被截图、进浏览器历史都等于把账号交出去,而且除了改密码没有任何失效手段。
现在是 HMAC 签名的分享票据:绑定单条记录、只读、默认 7 天过期,密钥由
FIELD_ENCRYPT_KEY 派生(上线不需要新配置项)。
- echomeet_getsharedrecord 必须在 gateway 白名单里——打开链接的人本来就没登录,
漏了是 code:18、表现为「分享链接打开一片空白」。签发端 echomeet_createsharelink
反之,不要加进白名单。
- 落地页嵌在 gateway 二进制里(go:embed),路由 GET /share/*any 必须是 catch-all:
路径里的段是数据不是文件路径,gin.Static 会去磁盘找同名文件、一律 404。
- 落地页的 ALLOWED_HOSTS 白名单不能删:serverHost 来自 URL,不校验就等于「构造
一条链接让别人的浏览器带着票据去请求任意域名」。新增环境要加一行。
- 极简 Markdown 渲染器先 esc() 再套正则,块级语法要匹配转义后的形态(引用块匹配
的是 > 不是 >),写成 > 不报错、只会静默退化成普通段落。
客户端侧:分享前先申请票据并加载提示,拿不到就不分享出一条必然打不开的链接。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
4753d37c69
|
版本更新判定改口径:按平台分版本、最低版本留空=线上版本、渠道未上架不推、地址回退链
判定仍全在服务端算好下发,客户端只拿结论。规则:
1. Android / iOS 各一套版本号:app_release 加 ios_version / ios_min_version
(启动 ALTER ... ADD COLUMN IF NOT EXISTS 幂等),原 version/min_version 只管
Android,平台由渠道号推导(只有 GlobalApple=101 是 iOS)。iOS 没填时**不回退
Android 的**——回退等于又回到共用一个版本号:Android 先发 1.0.3 而 iOS 商店还是
1.0.2 时,iOS 用户会被指向一个商店里没有的版本。
2. 最低支持版本留空 = 线上版本:勾上强更不填最低版本,就是「所有低版本都强更」;
填了则只强更低于它的,介于两者之间的仍是推荐。后台校验相应改成「至少填一个
平台的线上版本」。
3. 渠道行状态不是「已上架」(审核中/已下架)时不推送推荐更新——商店里还是旧包。
强更不受此限,它的地址会由回退链换成官方包。
4. 下载地址回退链:Android 渠道 = 本渠道(须已上架)→ 官方渠道(100) → 默认下载
地址;iOS / Google 只认本渠道,没有就空——那两处如今放的是 apk 直链。
5. 算不出任何地址时两种更新都不提示,强更也不例外:一个点了没反应又关不掉的
弹窗等于把用户锁死。
客户端三处配套:推荐更新的「稍后提醒」记住版本号,同一版本 24 小时内冷启动不再弹
(强更与手动检查不受影响);设置页手动检查绕过 10 分钟缓存;Android 上服务端地址
打不开时回退网页下载页(iOS 不兜底,那页给的是 apk)。
另修后台上传安装包报「必须先选择应用」:X-App-Id 头来自登录态的 currentAppId,
只有绑定了应用的运营/代理账号才有;超管在页面顶部选的应用只是个 app_name 字符串,
从不进请求头。现在 uploadFile 把它放进请求体,服务端没有 X-App-Id 时按 name /
app_name 反查注册表补作用域。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
76aa2032bf
|
services: 梳理并修「账号 ↔ 实名记录」的绑定口径
useridverify 明细是权威,user.idverified/idverifiedtime 只是后台筛选用的派生
缓存,两边是两次独立写,此前会各自漂移。纯逻辑收进 idverify_binding.go:
- reconcileUserFlags:home 启动时双向校正派生标识,幂等。
- applyIdVerifyAttempt:已实名账号「修改认证」失败时,明细上的身份字段一律
不动。原实现先无条件覆盖姓名/掩码/指纹再看结果,一次填错就把账号上的实名
身份换成了没核验通过的人,verifytime 却还停在通过状态;再提交回原来那个人
又被当成换人重新计费。
- scrubCancelledUserIdVerify:注销同时清 idverified/idverifiedtime/gender。
原先只删明细行,账号在后台仍显示「已实名」、详情里却没有姓名证件。
- 换人/注销留痕到 useridverify_log,只存掩码+指纹,不存姓名(注销要清 PII);
留痕失败只告警不阻断注销。
- alignCollation:user 表是 utf8mb4_general_ci(老库导入),gorm 新建的表是
utf8mb4_0900_ai_ci,联查直接报 1267。启动时把实名两张表 CONVERT 对齐。
CLAUDE.md 补这一节,含两条未修、属产品决策的边界(实名跟 uid 走、按应用库隔离)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
811a008545
|
services: 语音纪要→拾忆→MCP 链路审计落地;修两个「什么都没发生」的既有 bug
审计与落地计划见 docs/语音纪要-拾忆-MCP链路审计与落地计划.md,已在阿龙测试环境
逐条验收通过(记录见该文档 §5.1)。
## 安全
echomeet 八个按 id 操作的接口**一处都没有比对 record.Uid 与会话 uid**,任何登录用户
改一个 id 就能读别人纪要全文、删改别人的记录、给别人的记录发起总结(扣自己算力,
但覆盖对方 summary 并触发对方的拾忆抽取)。统一改走带 uid 的查询:
- 「不存在」与「不是你的」回同一个错误码同一句话——分开回等于给出一个探测他人 id
的接口;用的还是改动前记录真不存在时的 DBError,合法用户行为无任何变化;
- 批量接口按交集处理、**不报错**:客户端轮询队列里本就可能留着已被别的设备删掉的 id,
报错会把整个轮询循环掀掉;delrecords 响应回实际删掉的那批;
- starttask 的校验排在扣算力之前,否则拒绝了还先把钱扣了;
- 回调接口是第三方来的、没有 session,不在此列。
- TestApisUseUidScopedQueries 扫源码守着:最容易复发的不是有人改回去,
而是新加接口时照旧写法抄一遍,那样既不报错也看不出来。
## gorm 的 default: 标签吞零值
mysql.Insert = db.Table().Create(model),gorm 对带 default: 的字段**一律把零值换成
默认值**。DBMemoryItem 的 date_certain(default:true) / remind_ahead(default:5) 因此
永远写不进 false/0:测试库 25 条记录全部落成 1/5,「待定日期」分组恒为空,
会议待办还会在 08:55 响提醒——与「会议待办默认不提醒」正好相反。
⚠️ 加 Select("*") **救不了**:替换在 ConvertToCreateValues 的 reflect.Struct 分支里
无条件做,只看值是不是零值,与 Select/Omit 无关(DryRun 实测两种写法生成的 VALUES
一模一样)。改成插入后把这两列按结构体现值补写回去,并把真值还给调用方——
gorm 连结构体上的字段一起改了,不还原的话 memory_add 回给客户端的也是错的。
存量由 migrate_defaults.go 幂等修正(只碰 source=meeting 且 user_edited=0)。
## 内容门槛
6 秒的测试通话、20 秒的单人自述照样被抽成「继续进行进一步的功能测试」这类废话待办,
再靠条数凑够周报阈值又触发一次 LLM 调用。门槛卡**转写正文**不卡纪要——内容太薄时
模型按模板兜底规则照样能写出六百多字。只拦抽取与 MCP 检索,**纪要照出**:
短语音备忘是合法用法,也不判成 SummarizFail(客户端把那个码显示成「请稍后重试」,
用户会一直点)。数据缺失时按通过处理,不因上游漏传一个字段就静默关掉抽取。
## 其余
- 报告补偿:原先的 requeueIfStale 由 memory_getreport 驱动,而客户端调无参版本 →
走 latestUnconfirmedReport,那条 SQL 只查 state=Done,pending/processing/failed
永远查不出来也就永远补不了。新增 ensureRecent 挂在 memory_today:漏建的补建
(过与 cron 同一道条数闸门)、卡住的复用 requeueIfStale、失败的至多重试 3 次
(计数寄在 error_msg 的 retry=N; 前缀,fail() 必须保住它,否则上限形同虚设)。
加 enqueueOnce 判重与按报告 id 的 SETNX 生成锁,cron 与 ensure 撞上只跑一次 LLM。
- 说话人标签:finishSyncTranscribe 拼的是「Speaker 0」(空格)、其余是「Speaker_0」,
抽出来的负责人就成了 Speaker2、[Speaker_0] 各种样。四条写入路径统一走 normalizeSpeakers,
顺带修掉没开说话人分离时拼出「Speaker_」空尾巴。
- 时区:LoadMemoryLocation 现在认 IANA / 固定偏移 / CST(按 Asia/Shanghai 解释,
兜住已落库那批),认不出来仍退服务器本地时区但**打 warn**——原来是全静默的。
- 删纪要连带删自动待办(只删 user_edited=0),查询带 uid:入参来自删除请求,
光信 source_id 等于把归属校验的成果又丢一次。
- 回调幂等:两个回调既不看状态也不抢锁,与轮询撞车就各翻译一遍、各入队一次。
失败分支同样要过闸门——一条已 Completed 的记录被迟到的失败回调打回 TranscribeFail,
用户看到的就是「纪要好端端地变成了转写失败」。挡下来仍回 SUCCESS,不让第三方重投。
SubmitAITask 保持不判重(用户点的「重新生成」悄悄跳过等于按钮失灵),
自动路径改走 submitAITaskOnce。
- 重复项统计:只展开闹钟。花销展开等于虚构金额,待办的 done/undone 按次拆不开。
## 顺带修的两个既有 bug(部署验收时挖出来的)
happen_date / period_start 都是 type:date 列 + DSN parseTime=True,读进 Go 的 string
是「2026-09-07T00:00:00+08:00」。SQL 比较靠 MySQL 转换还是对的,所以这个问题
**在任何日志任何报错里都看不见**,只在 Go 侧解析它的地方发作:
1. remind.go 的 expandItem 解析不了 → **memory_upcoming 恒返回 0 个提醒时刻,
整个拾忆提醒在服务端一直空转**(真机实测:建一条每天 08:00 的闹钟,slots 为 []);
2. 下发给客户端的 happen_date 一直是 RFC3339 而非协议约定的 YYYY-MM-DD,
客户端 _localToday() 的字符串相等比较恒不匹配、_mergeRange 在区间端点会重复。
这也是审计里「会议待办会在 08:55 响通知」只能是推演的原因:实际一条都不会响,
两个 bug 互相遮蔽。修法两层:ParseMemoryDate 容忍带时间的形式(安全网),
读路径出口统一归一(管对外格式),缺任一层都会留坑。
## 列表骨架化(同批)
echomeet_getallrecords 只回列表要用的骨架字段,original/translate/summary 占每条
97% 的字节(实测 59 条 ≈ 500KB,列表用得上的 15KB)。
⚠️ 骨架记录**绝不能写回库**:mysql.Save 是整行 UPDATE,三列会被清空。
StartTask 允许对已完成/已阅/失败的记录「重新转写」,那期间 state=Transcribing 而旧
纪要还在库里,用户切一下列表页旧转写和旧总结就没了。轮询前重新取整条再操作,
TestListEndpointNeverSavesBriefRecord 守着不许回退。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
5193b0e5e8
|
admin,console: 「会议模板」改成「会议配置」,一页管转写/总结/模板
后台恢复了会议转写段与总结段的配置入口——它们随「会议记录服务」页一起下线后,
只能直接改库(echomeet_orch),而那正是语音纪要整条流水线的服务选型来源。
- 路由 /meettemplates → /meetconfig,旧地址留重定向;**菜单 id 不变**:
存量后台账号的 access 清单里存的是这个串,改 id 等于让这些账号丢入口;
- 一页全展开不分标签页,顶部一个应用选择器三块共用——各拉各的会出现
「转写配的是 A、模板改的是 B」这种对不上的状态;
- 保存是声明式的,asr/llm 都是**指针**:缺哪个不动哪段。写成值类型的话
「保存会议总结」会带一个空的 asr 数组把转写编排整段清空;
- 翻译段(kind=2) 本页不编辑但保存时必须原样保留,跟着 reconcile 会让纪要
能转写、译文整体消失;
- 服务池查询是 app_name=? 不带 OR app_name='':作用域归一后没有全局默认层了,
照旧版写回退查询会让一个应用读到另一个应用的服务选型,且不报任何错。
段→服务类别的映射由 TestEchoKindSvcCats 钉住:类别编号是 comm / console / admin
三处同写的,漏改一处的表现是某一段的下拉空空如也且不报错。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
3 weeks ago |
Rodger-Wang
|
3709aa6e3b
|
services: 语音纪要 ASR 按优先级自动回退;翻译区分「语种不支持」与瞬时失败;OSS 配置在作用域归一后可兜底定位
- echomeet: SubmitTranscribeTask 首选 ASR 失败时按 Priority 依次尝试其它启用的识别服务,
并把 AsrSvcId 写回任务,回调才能找到对的服务;原先一次失败就 TranscribeFail
- user_translate: 阿里 10005/10033(语向不支持/语种拼写错)不再当成瞬时错误,
直接回「该语言暂不支持翻译」;挪威语 nb-NO/nn-NO/no-NO 映射到 no
- model_translate: 「跳过未接入 MT 服务」的告警去重,不再刷满日志
- ossconf: appName 为空时按指纹唯一匹配(作用域归一后全局行已不存在)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
263c72ad31
|
services: 机器翻译只在服务端接得上的 provider 里挑,别随手挑中没接的那家
线上表现:文本翻译 / 同传 / 面对面全部报「未配置机器翻译服务」,
而后台 EAIMAR 下 mt_alibaba / mt_azure / mt_volcano 三家都启用着、凭据也都填了。
根因:resolveTranslator 挑选时没有任何优先级,谁先从 postgres 返回就用谁,
这次挑中了 mt_volcano,而服务端只实现了阿里一家,于是落进「provider 不是
alibaba 就返回 nil」的分支。错误信息指向「没配置」,会把人引去后台反复检查
明明填好的凭据;而且它取决于数据库返回顺序,可能自己好、也可能自己坏。
- 新增 mtProviderPriority 白名单:只在真正接了的 provider 里挑,并给出稳定优先级。
- 挑选抽成纯函数 pickMTSvc(可脱离 postgres 测),被跳过的未接入服务打一条点名 warn。
- 缓存命中不再硬编码返回 "alibaba",改为记住实际 provider。
- model_translate_test.go 5 个用例:未接入的要跳过、全不可用返回 nil、
停用/非 MT 类别不参与、应用行优先、provider 大小写归一。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
461289b780
|
services,admin: 后台赠送只发 VIP 天数与算力;console 读系数走应用连接
- giftUserResource 入参改为 vipday + compute,后台不再按功能分桶发放分钟数
(那是 2026-09 停用的旧三桶模型);trademin/meetmin 保留为入参兼容,
老 admin 容器与历史脚本送上来的分钟数按当前系数折成算力,进同一个 addcompute。
代理余额只有 balance_vipday/trademin/meetmin 三列、没有算力列,这条路的边界在注释里写明。
- console 读换算系数改用按 X-App-Id 取出的连接(computeRatesOf)。
⚠️ console 是单例服务,InitSys 从不 mysql.OnInit,包级 comm.LoadComputeRates()
一调就 nil interface method call panic,被 gin.Recovery 兜成 HTTP 500,
前端只看得到状态码、拿不到任何业务错误;何况系数本来就按应用存在各自业务库。
新增 pkgdb_boundary_test.go 守这条边界。
- 后台上传 OTA 更新包放开 .upd 扩展名。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
e00b617025
|
services,admin,client: 服务配置按应用隔离、应用参数下线、客户端凭据走加密 v3
后台
- 服务配置去掉「全局默认」层,svc_config / svc_region_override / echomeet_orch
/ echomeet_orch_setting / echomeet_template 五张表统一按 app_name 隔离,
启动迁移 migrate_scope.go 幂等分配(首个应用原地改名保 id,其余拷贝);
写接口 app_name 必填(scope_check.go)。
- 类别落库 svc_category,可增删改;内置 11 个 id 不可改不可删,运行时语义仍只挂 comm.SvcCat*。
- 「应用参数」标签页下线:业务库 config 表不再随 user_getappconfig 下发,
migrate_appparams.go 把 AGENT_TYPE / MOBILE_ELF_* 迁到服务 llm_mobile_elf、
iapCustomerServiceQQ 迁到 app_params,COMPUTE_*/NEWUSER_GIFT_* 留在 config 表不下发,
无人读的音乐/公码/导航/COS_*/MeetServers 删除,其余键保留并在启动日志告警。
- 删除 console appcfg/* 与 api 模块的 get/add/update/delconfig、唤醒音四个接口(无调用方)。
- 修 getmcpservers 仍查全局层导致 MCP 类服务永远读不到。
客户端
- 凭据改走结构化通道:user_getappconfig_v3 整体 AES-CBC 加密(api_crypto.dart,
拦截器须排在 AuthInterceptor 之前),AppConfig.cred(svcId, field, envKey) 取值,
老键名只作兜底;lib 内 AppConfig.env( 已降到 0 处调用。
- 图片翻译的 ALIBABA_VL_MODEL / ENDPOINT 改挂 ast_alibaba。
启动日志
- 删除 sys/auth/firebase(google_auth 的重复实现,零调用方,且同一错误刷两行)。
- api 模块不再每次启动无条件插入默认超管(Duplicate entry 'admin')。
- sys/auth/* 五个包的 logger 名从复制来的 sys.tavily 各改各名;
google_auth 启动时即判断配置是否可用。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
268ce26bc9
|
services,admin: 凭据归一到 svc_config;后台服务配置菜单收敛;品牌商与公码下架
这一提交里有两条相对独立的线,落在同一批文件上(server.go / core.go / model.go /
CLAUDE.md 等被两边同时改到),按文件拆不开,故合并提交。
── 一、第三方凭据归一到 svc_config,后台「服务配置」从 11 项收敛到 6 项 ──
起因:凭据同时躺在两张扁平 key/value 表里——应用业务库的 config 与 console 库的
global_config,而合并下发时**全局那张赢**。同一个 AZURE_SPEECH_REGION /
OPENAI_API_KEY / VOLC_OPENSPEECH_APP_ID / XUNFEI 密钥在两边值不一样,运营在
「应用环境配置」里改半天没有任何效果,且不报错。global_config 还按区域整份复制
(14 区 × 31 键 = 473 行),实测其中 27 个键是各区同值。
- comm.SvcField 新增 EnvKey(后台字段表里的「下发键名」列):非空时该字段的明文值
同时以这个名字出现在 user_getappconfig 的 env 里。已发布的客户端只读 env,
靠它继续供货、无需发新包;新客户端直接读结构化的 thirdsvcs。
- env 覆盖层与 thirdsvcs 走同一份 VIP 过滤,否则 VIP 过期的用户虽拿不到 AST 条目,
却仍能从 env 里读出同一套通话翻译凭据,那道闸门就形同虚设。
- 同作用域内 env_key 不允许重名(checkSvcEnvKeys 在保存时拦):重名时下发结果取决于
服务 id 排序,表现为「凭据时对时不对」。
- 一次性迁移 migrate_envtosvc.go(console 每次启动跑、幂等):基础值取中国区现值,
只有与基础值不同的区域才写 svc_region_override,473 行收敛到几十行。
顺带修了两行填错列的历史数据(group 里是键名、key 里是值),正是它让
ALIBABA_BAILIAN_APP_ID/WORKSPACE_ID 恒为 null、客户端一直用代码里写死的兜底值。
不删 global_config 的行(不可逆的线上凭据,留作回退依据),确认跑稳后由人手工 DROP。
- 后台:「第三方服务配置」改名「服务与环境配置」,「应用环境配置」并为其第二个标签页;
「全局环境配置」下线只留重定向;Agent 配置、通话翻译配置、会议记录服务三页删除
(前两者对应的 agent_config、call_translate_rule 两表在测试机与正式机上都是 0 行)。
⚠️ 会议记录服务删的只是后台入口,echomeet_orch 的数据与运行时都还在(两台机各 4 行,
是语音纪要识别/翻译/总结的服务选型来源),要换服务商现在只能直接改库。
阿龙测试环境实测:迁移前后 env 逐键比对,48/54 键值完全一致,丢失的 8 个全部是预期
丢弃(ALIBABA_OSS_* 与 COMMENT_* 无人读取、两个填错列的垃圾键名),无一个值发生变化。
验证中发现并修掉四个都不报错的坑:空值不能下发成 env 键(Dart 的 ?? 只兜 null,
空串会把默认值顶掉)、区域覆盖里的空串会把基础值抹成空、服务 enable=false 等于凭据
一个都不下发、启动时的模板补字段会给实例加空的加密字段导致误报「缺凭据」且保存被拒。
── 二、品牌商(identity=3)与公码下架 ──
brand 表、公码 authcode 及其全部后台页面与接口移除;账号作用域不再有 brandId,
设备/结算的归属改由渠道商推导。这部分不是本次会话中由 Claude 编写的。
验证:go build ./... + go vet ./... 通过,comm/console/user/echomeet/api 各包测试全绿;
admin npm run build 通过。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
223f7d0a44
|
deploy: 移除本机连不上的 6 台服务器,顺带清掉测试里写死的下线机器凭据
云雁(yunyan)/灵谱(lingpu) 的部署目标从所有脚本移除,ENVS / PROD_ENVS / REGIONS
现在都只剩 along。那 6 台(云雁测试/上海/日本/新加坡、灵谱测试/正式)的 SSH 私钥路径
指向的是另一台开发机,本机一台都登不上(22 端口通、无可用密钥),留在交互菜单里只会误选。
- 删 deploy/{console,admin,app}/dev-deploy.sh 的 lingpu、yunyan 两段 env_profile
- 删 deploy/{console,admin}/prod-deploy.sh 的 yunyan、lingpu 两段
- 删 deploy/app/prod-deploy.sh 的 shanghai/japan/singapore 三段 region_profile
- 删 deploy/admin/deploy.sh:早期脚本,只指向灵谱且生产位留空,已被
dev-deploy/prod-deploy 取代(CLAUDE.md 里"要同步两份 deploy.sh"也是错的,
console 那份早就不存在)
- IP 与私钥路径不再记在文档里,需要时从 git 历史取回
镜像仓库刻意保留:registry.voitrans.net / registry.lingpu.net 都在,prod-build.sh
仍可选 yunyan|lingpu 推镜像(三家仓库本机都能读写,协作者仍从他那台机部署这两家)。
顺带修掉三处把已下线机器地址连明文数据库密码一起写死的测试:
- sys/nats/nats_test.go 有个真 bug——`url := "nats://<写死地址>"` 紧接着 `if url == ""`,
常量永远不为空,所谓"默认跳过"从来没生效:每次 go test 都在连那台早已废弃的机器,
白等 4.5 秒超时。现在真正读 NATS_URL 了。
- modules/{api,echomeet}/model_test.go 的 DSN 改走 API_TEST_DSN / ECHOMEET_TEST_DSN
另外给 app/prod-deploy.sh 加了一条明确报错:输入已移除的区域名(如 shanghai)原本会被
is_tag_like 当成版本号,报出"版本指定了两次"这种驴唇不对马嘴的错。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
37817f2e96
|
会员与算力:VIP 管准入、算力管计量;补上线落地文档
VIP 与算力两层权益各管一件事,判定与记账统一走 comm/compute.go:
- VIP 管功能准入(无 VIP 时 user_translate / echomeet_starttask 返回 VipRequired);
自购记 user.vipexptime,设备赋予记在 device_mac 行上(激活日 + 天数),
两者在 ResolveUserVip 合并成有效值下发,库里仍只记自购——以前绑定礼把天数
累加进用户表,解绑时拆不开。
- 算力管用量计量,按后台系数折算,先扣设备赠送再扣用户余额。
三个旧额度桶停用,存量余额由 home 启动时一次性折进 computebalance。
- 闸门 COMPUTE_GATE 默认关:user_usages 是用完才上报、没有预扣,
现在开闸门形同虚设。
另含本次「阿龙测试服 → 正式服」上线落地文档(docs/),
以及 admin 侧商品/设备/产品/用户页的配套改动。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
4 weeks ago |
Rodger-Wang
|
48e31cf605
|
MCP 加会议纪要检索工具;用户类工具改用专用短时令牌鉴权
EMAI 助手此前答不了「我上次开会讲了什么」——纪要在服务端,助手拿不到。
补 search_meeting_notes 工具,配套解决身份怎么传。
身份链路(关键取舍):
百炼是第三方,MCP 请求经它转发,所以不能把网关那把能开全站的 JWT 交出去。
新增 MCP 专用令牌:user_getmcptoken 由 home 签发(uid 从网关解出的会话取,
客户端说了不算),aud=mcp、TTL 2h,用独立的 MCP_TOKEN_KEY 签。
即使泄漏,能做的也只有读这一个用户的只读数据,且两小时后失效。
⚠️ 裸 uid 参数已不再被信任:谁都能填别人的 uid。ResolveUIDWithToken 要么拿到
Authorization 里的登录态,要么拿到这张令牌,两者都没有就直接拒。
auth_mcptoken_test.go 守着这条底线(含 aud 不符、过期、换密钥三种伪造)。
⚠️ 令牌必须按工具名嵌套传:真机实测百炼**不会**把 biz_params.user_defined_params
平铺进工具参数——那次调用服务端只收到 [limit]。按 {"工具名": {"auth_token": ...}}
嵌套才生效。这个形状文档没写死,是试出来的。
检索是三级回退:全文索引(按相关度排序) → LIKE(兜单字,ngram_token_size=2 时
单字进不了索引) → 最近几条。最后一级不能省:模型常把「最近的会议纪要」整句
当关键词传,纪要正文里不会有「最近」二字,LIKE 全落空,用户看到的就是
「查不到您的会议纪要」而库里明明有。回退结果带 fallback 标记,免得模型把
「最近一次会议」当成「你问的那次会议」来答。
文件头注释里记了三个「该换向量/知识库方案」的触发条件,量大了照着判断。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
3b1c11e02b
|
echomeet 加卡死兜底扫描;转写中间态对外降级;会议纪要建 ngram 全文索引
服务端原先只有「回调」和「客户端轮询」两条推进路径,两条都没到时记录会永远
卡在 Transcribing,没有任何自愈——真机上卡了 8 分钟,直到用户杀 App 重开。
业务在服务端跑,不该由客户端在不在线决定它推不推进。
兜底做两件事(每分钟一次 cron):
- 转写中且 60s 无人查询 → 主动 PollTranscribe;超 2h 未完成判 TranscribeFail,
否则第三方丢单的记录会被无限查下去;
- 停在待总结/总结中超 5 分钟且**不在 AI 队列里** → 重新入队。「不在队列里」
这个前提不能省,正在跑的重复入队 = 两次大模型计费且后者覆盖前者。
顺带修两处:
- getstucktranscribing 的时间条件补上 `lastquerytime IS NULL`。SQL 里 NULL 的
比较永远不为真,缺了这条,从未被查询过的记录一条都扫不到——正是最该救的那批。
- 三个拉取入口统一用 hideHalfDoneTranscribe:PollTranscribe 成功后先把 State 推到
AwaitSummarizing 落库、再异步写 Translate,于是响应里必然是「状态说转写完了、
Translate 还是空」的中间态。客户端据此 jsonDecode 会抛异常,把轮询循环整个掀掉。
只对本次真正轮询过的记录降级,否则 finishTranscribe 出错时会被永久钉住。
另加会议纪要的 ngram 全文索引(幂等、随启动自动建、失败只降级不阻断),
给 MCP 的 search_meeting_notes 用;默认解析器按空格切词,中文整段会变成一个
巨型 token 等于没索引,所以必须 WITH PARSER ngram。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
fc3f747dfb
|
拾忆:周期报告弹窗 + 存量 DBTask 迁移
周期报告弹窗(MemoryReportSheet)
- 一次只弹最近一份未确认的。用户一个月不开 App 会攒 4 份周报 + 1 份月报,
连弹 5 次没人受得了——服务端不带参数时就只返回最近一份。
- 不可关闭(需求 4 要求用户确认),但**确认失败也放人走**:把人扣在一个不可
关闭的弹窗里比漏记一次确认糟得多,下次进来会再弹。
- summary 为空时不播报、只显示统计。文案生成失败时服务端是刻意只回写统计的
(宁可少一句话也不让报告整个没有),这里对齐那个降级。
- 统计数字全部来自 stat_json,客户端不做任何加减——同一份数据在两处各算一遍,
早晚会出现两个不同的结果。花销按币种分行,不做汇率换算。
- 排在每日安排弹窗之后:两个都要播报,同时弹会互相盖住,而「今天要做什么」
比回顾更即时。
存量 DBTask → memory_item 迁移
模块 Start 时起 goroutine 跑一次,幂等(client_key="task:<id>" + 唯一索引)。
- **不删也不改 allhelp_task**。不可逆迁移的通例:出问题时旧表是唯一回退依据。
- **失败不阻断启动**。它是补数据不是启动前置;返回 error 会让 home 退出,
而五个服务同镜像、entrypoint 会连坐全杀。
- trigger_time 用 time.Parse 保留原时区再取日期,**不先转 UTC**——
那会让 00:30+08:00 这类凌晨任务整体退到前一天(有用例守着)。
- RemindAhead 置 0:旧体系「服务端仅记录不触发」,用户从没被这些任务提醒过,
迁过来默认开提醒等于给所有人补发一堆历史提醒。
- UserEdited 置 true:迁移来的是用户资产,不该被会议重新生成之类的清理逻辑碰到。
- TaskType_Cron 按一次性迁,不猜重复规则——猜错会让用户每天被一个他没设过的
闹钟吵醒。
- 标题与描述都空、没有 uid、既无触发时间也无创建时间的一律跳过,不往新表塞垃圾。
7 个映射用例覆盖:基本字段、凌晨不退日期、五种 TaskType、三种状态、
无触发时间退回创建日期、四类该跳过的、标题回退描述。
验证:go build / go vet 全通过,comm + memory + mcp 三个包测试通过;
dart analyze 零错误,flutter build apk --debug 通过。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
73eb2ef2ca
|
MCP 补上会话鉴权,并挂三个拾忆只读工具
## 先修鉴权:此前公网接口完全没有身份校验
authFromRequest 只把 Authorization 塞进 context,**全仓没有一处读回来,也没有
任何 JWT 校验**;三个用户工具(get_user_tasks / allhelp_task / cancel_user_task)
的 uid 都是 mcp.WithString("uid", Required()) —— 由大模型填进来的普通参数。
而 mcp 是独立进程、独立端口 7300,不经过 gateway 那套 parseToken + isInWhiteList
(那只管 /api/* 和 /web/*),又必须对百炼公网可达。所以这不是「内网接口没做鉴权」,
是公网接口完全没有鉴权:知道 uid 就能读改任意人的数据。
新增 modules/mcp/auth.go:
- 从 Authorization 解 JWT 取 uid,与 gateway/core.go 同一套口径(RegisteredClaims,
uid 在 ID 字段);兼容 "Bearer xxx" 前缀(gateway 收的是裸 token,标准 MCP
客户端会加前缀)。
- 参数里的 uid **只用于比对**,不一致直接拒。不静默改用会话 uid——调用方显然
误以为自己能指定用户,让它失败比让它以为成功了更安全。
- 解不出会话 uid 一律拒,**不回退到参数 uid**,那等于这层没做。
- TokenKey 未配置时拒绝所有用户数据类工具:配置漏了导致鉴权静默失效,
比工具不可用严重得多。TokenKey 必须与 gateway 同值,两边都读 GATEWAY_TOKEN_KEY。
存量三个工具同步改造,uid 参数保留但改为可选(兼容百炼后台已配好的工具定义)。
9 个测试守住这条线,其中三条是核心安全断言:参数 uid 与会话不符必须拒、
没有 token 必须拒而不是回退、未配 TokenKey 必须全拒。另有伪造签名、过期 token、
空 uid claim 的用例。
## 共享查询层:避免 home 与 mcp 各写一份
comm/memoryquery.go。mcp 从不 RpcCall、只裸查 MySQL,如果记忆项的过滤条件、
分类白名单、排序口径两边各写一遍,改了一边另一边不报错,只会让 EMAI 答的和
App 里显示的对不上——而且没人会发现。memory 模块的 listItems 也改走这里。
ApplyMemoryQuery 里 uid 为空时拼 "1 = 0" 而不是不加条件:少一个 uid 条件就是把
全库记忆项返回给调用方,而它有两个调用方,其中一个公网可达。
另提供 MemoryRelativeRange 把「本周/上月/最近7天」换算成日期区间给模型用——
模型对「今天几号」的认知来自对话上下文,很不可靠,让它自己算 start_date 经常
算错(SET_clock 把「下午 3:30」填成 03:30 是同一类问题)。
## 三个只读工具
get_memory_items / get_memory_stats / get_memory_report。
**只读是刻意的**:新增/修改/删除不放这里。写路径的校验、幂等(client_key)、
提醒时刻重算都在 home 的 memory 模块内,在 MCP 里复制一份必然漂移。
录入走端侧指令(tool_calls)、查询走 MCP,这个分工不变。
- 花销按币种分行返回,不做汇率换算;同时给「分」和「元」,模型念元不容易错,
留分是以免它想自己做加减时用浮点。
- get_memory_report 只返回已生成完的:还在生成中的 summary 是空的,
给出去只会让模型编一段话填空。
- 返回给模型的是精简字段,不是整行 DBMemoryItem——client_key/gen_round/extra
这些内部字段对回答问题没用,只占 token 还诱导模型去解释它们。
- date_certain=false 的项带上 date_is_inferred 标记,让模型别把推断出来的日期
当成用户确认过的安排来陈述。
- 模型填错分类时忽略该筛选当作「不限分类」,而不是整次查询失败——它多半会再填错一次。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
0b0ac16b3c
|
记忆中心后端:memory 模块 + 会议待办抽取 + 周报月报
按设计文档一~三期的后端部分落地。全量 go build / go vet 通过,新增 22 个测试。
memory 模块(modules/memory,由 home 装载,路径 /api/home/memory_*)
11 个接口:list / today / add / update / del / complete / upcoming / stats /
getreport / confirmreport / listreports。
表由 proto + gorm tag 生成(不是手写 DDL——本仓所有业务表都走
mysql.CreateTable + AutoMigrate)。protobuf 没有 DECIMAL/DATE/DATETIME/null,
逐个落成:金额 int64 存分(与 pay/结算同口径,绝不用 double)、happen_date
string + gorm type:date、next_remind_at int64 unix 秒(0=无)。
几处刻意的选择
- 建表失败只 Errorln 不外传。lego.Run 里 Init 返回非 nil 就 Panicf,而五个服务
同镜像、entrypoint 见任一 pid 退出就全杀,一次建表失败等于全站 502。
allhelp/model.go 那段用命名返回值 err,第二张表失败时会漏出去,是反例。
- client_key 服务端保证非空。它参与 (uid, client_key) 唯一索引,而 MySQL 的唯一
索引不像放行 NULL 那样放行空串,留空会让同一用户的第二条记录直接插不进去。
用 crypto/rand 生成:这个值是去重依据,可预测就可被抢占。
- 重复项的提醒改成「就地展开」。设计文档 v0.1 写的「remind_at 服务端算好客户端
直接用」对重复项不成立——一个字段表达不了「每天」,且没人在它过去后往前推。
现在 memory_upcoming 按项自己的 tz 展开成绝对时刻列表返回,不落库。
单条项按自己的 tz 算而不跟着请求方走:「今天有哪些事」是视图问题可以跟着人走,
「明早 8 点响」是承诺问题不能。
- 花销按币种分行统计,不做汇率换算。把 CNY 和 JPY 加成一个数是错的。
- 统计全部走 SQL,LLM 只把 stat_json 组织成一段适合朗读的话。明细截断到 20 条
(重度用户一周两百条未完成,全塞进 prompt 会顶爆上下文,播报也没人听得完)。
- 查询/删除/更新一律带 uid 作条件,报告确认校验 uid 归属。
- 更新字段走白名单而不是黑名单,漏挡一个 uid/id 就是越权改别人的数据。
选路走编排,不再写死服务商
新增 comm.IEchomeet.ChatLLM,由 echomeet 用它的 PickLLM/GetSummarizer 实现。
memory 的报告生成与会议抽取都经它选模型。allhelp/summary.go 直接 import
sys/doubao 是历史债:绕过后台编排,海外应用照样调国内豆包、后台换模型也换不掉。
新模块不复制这个写法。
会议纪要 → 待办抽取(第三路)
挂在 AIProcess 写完 summary、状态置 Completed、出队之后,另起 goroutine。
prompt 内置代码不放 echomeet_template(1189 条模板改不齐,且输出要被程序 parse)。
输入用已生成的 summary 而非转写全文。注入 <meeting_date>,服务端二次校验
due_date:解析不出或早于会议当天的一律只留 due_raw、落会议当天并置
date_certain=false。抽取失败只记日志,绝不回头改 rec.State。
- gen_round 由 memory 按 source_id 现有最大值 +1 推出,不由调用方传。
echomeet 没有地方存轮次计数器,硬编码成 1 会让第二次「重新生成」的清理条件
gen_round < 1 一条都匹配不上,旧待办从此永远留在库里。
- 会议归属日期只能用 creationtime(上传时间)——DBEchoMeetRecord 上没有「会议实际
发生日期」字段,也没有客户端时区。补录历史录音会算错,是已知偏差,注释已记。
周报月报
cron 建单 → Redis 队列 → N worker。锁与队列 key 都过 redissys.RKey 加应用前缀:
测试机多应用共用一个 Redis,锁 key 不带前缀会让 A 应用挡住 B 应用整周不生成报告,
而且安安静静什么都不报。
补偿比防重更要紧:当前 compose 是单副本,多副本重复是前瞻性风险;真会发生的是
00:30 跑到一半容器重启、cron 不补跑、报告永远停在 pending。所以 memory_getreport
里做超时重新入队(同 echomeet 用 PollTranscribe 兜「转写回调丢了」的思路)。
文案生成失败不算整体失败——统计是完整的,宁可少一句话不可让报告整个没有。
顺带修的既有缺陷
- user 模块 cancel()(账号注销)补上 memory_item / memory_report 两张表,
以及既有遗漏的 useridverify(身份证掩码 + 加盐指纹)。这个清单是硬编码的,
漏了不报错,只是个人数据静静留在库里。
- errorcode.proto 新开 5201-5206 段。复用 ReqParameterError 会让客户端无法分流,
「报告还没生成好」该转圈重试而不是弹错。
测试
comm/memory_test.go 8 个:周日在 Go 里是 0、Dart 里是 7 的换算(错了不报错,
只会让每周日的闹钟在周一响)、ISO 跨年周、闰年二月、非法时区必须回退 time.Local
而不是 UTC(否则静默偏移 8 小时)、client_key 非空且不重复。
modules/memory 14 个:一次性/每天/每周展开、锚点在一年前仍能算出未来时刻、
过期项不排、灵感花销不提醒、全天项按 9:00 不落 0:00、展开结果必须升序
(客户端要按 iOS 64 条上限截断,顺序错了会把明天的排掉留下一周后的)、
项按自己时区算、LLM 输出容错(代码块/前后解释文字)与真失败必须报错。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
8d580a545d
|
文件上传改走服务端签发预签名 URL,客户端直传阿里云 OSS
客户端不再持有对象存储的长期密钥。
旧实现用腾讯云 COS SDK 直连,bucket 与 SecretId/SecretKey 明文随
user_getappconfig 的 env 下发给每一个 App 用户(config 表的 COS_* 那几个键)。
抓包或反编译就能拿到这对长期密钥,而它对整个桶有读写权限——不只是自己那部分,
User/ 下所有人的录音都能被拉走或删掉。
现在:新增 user_getuploadurl,服务端签发 15 分钟有效、且只对某一个 object key
生效的阿里云 OSS 预签名 PUT URL,客户端拿着它直传。存储与 console 后台传固件/
产品图同一个桶(ymaioss),不再是两个云厂商两套存储。
服务端
- 新接口 modules/user/api_getuploadurl.go:
· object key 由服务端拼死 EAIMAR/User/<uid>/<scene>/<yyyy/MM/dd>/<12位随机>.<ext>,
uid 取自会话不采信客户端,客户端连自己传到谁的目录下都决定不了;
· scene 白名单 LocalAudio / TranslatAudio / ExternalAudio / ChatImages
——加新场景前要把客户端 addUpload 与 UploadOss.upload 的调用点全捞一遍,
少一个值就是那条链路整条报错,且只在真机跑到才发现;
· 扩展名白名单决定 Content-Type,不在表里直接拒,不回退成
application/octet-stream(那等于把桶变成任人上传的网盘);
· 随机名用 crypto/rand 而非 math/rand:key 可枚举就等于别人能猜到录音地址。
- 抽出 comm/ossconf.go:OSS 配置读取、上传根前缀、publicURL 拼接从 console 挪来,
console 改为调用它。两边必须落同一个桶,各写一份迟早漂移。
解密用 ${FIELD_ENCRYPT_KEY},console 与业务服务本就必须配同一把。
客户端
- upload_oss.dart 重写为「要 URL → dio 流式 PUT」,upload() 签名与返回值不变,
MeetingUploadService 不用改。用独立 Dio 实例:项目那个挂着 AuthInterceptor,
会加 token 与业务签名头、还会把响应按业务 JSON 解析,直传 OSS 时全是多余,
签名头还可能与预签名冲突。
- Content-Type 必须用服务端回带的值,它计入了签名,写别的会 403。
- agent 图片的 rootDir 从 'User/<uid>/ChatImages' 改成 'ChatImages':
前缀由服务端加,客户端再拼一层就是重复路径。
真机验证:录音落到
https://oss.ymaikj.com/EAIMAR/User/<uid>/LocalAudio/2026/09/04/howXMScPdsNP.wav,
下载 212524B 与库里 size 逐字节一致,Content-Type=audio/wav,文件头 RIFF/WAVE。
其余三个 scene 走同一段代码但尚未实跑。
⚠️ 新老客户端并存:老包仍读 COS_* 直传腾讯云,config 表里那 5 个键先别删;
历史录音的 audiourl 是 COS 绝对地址,不会自动搬家。
⚠️ 录音目前仍是公共可读的(bucket 公共读,实测匿名 GET 206、不存在的 key 404),
只靠 12 位随机 key 不可猜测来保护。要锁住得转私有桶 + 播放时也签临时读 URL,未做。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
24e17b3434
|
收拢积压改动:恒玄 OTA、发版配置拆表、实名认证、芯片厂商/代工厂管理等
把工作区里积压的多轮改动一次性入库。主要几块:
客户端
- 恒玄 OTA 打通:新增 local_plugins/bes_ota_manager(自包含 BES SDK,
Android Java + iOS ObjC,SPP OTA 2.0),路由切到 BesOtaUpgradeView /
BesOtaUpgradeController,配套下载服务;杰理那套文件保留但已不挂路由。
- 通话翻译新增阿里 3.5 档(alibaba35_language_config.dart,
qwen3.5-livetranslate-flash-realtime + 实时声音复刻)。
- 实名认证接入、游客登录改走服务端 api_sgin 的 Tourists 分支、
功能闸门由 GuestGuard 改为 IdVerifyGuard。
- 41 个语种文案同步(OTA 新增 28 个 key 等)。
- 默认主题改为浅色,不跟随系统。
服务端
- 发版类配置拆表:app_release(版本控制 / 游客显隐,一应用一行)与
channel_app(渠道级下载地址 / 上架状态 / 支付渠道)分家,
新增 comm/apprelease.go 与 console/api_apprelease.go。
- 授权码字符串整体移除:删除 utils/license 包,新增 utils/devcode
(MAC 规范化与 PID 反解),配套迁移 SQL 在 docs/migrations/。
- console 新增芯片厂商、代工厂、用户管理接口与对应 admin 页面。
- 身份证实名核验(sys/idverify)与第三方服务的服务端专用类别。
其它
- .gitignore 补挡 apps/services/lego/sys/gin/log-*.log:gin 子系统跑起来
会按时间戳滚动生成日志(多为 0 字节),与 apps/services/comm/log 同一
性质,不进版本管理。已跟踪的 log-2026-08-20 那个仍在库里,未动。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
d7fad60658
|
设备表合表:license_<pid> 分表 → 全局 device_mac,只按 MAC 校验
设备的身份是它自己的 MAC,本来就全局唯一,定位一台设备不需要 pid。
原先按产品切分表,pid 既是查询键、又得由客户端猜出来,猜错就报
"这台 MAC 没登记在该产品下"——提示指向数据缺失,实际数据好好的。
触发改造的事故:一台已正确导入的 DEEPVOICE 耳机(MAC 在 license_b022,
产品 45090,status=0 未绑过)连不上、弹"设备连接受限"。根因是产品 45090
的 devicetype 被配成 2(BLE双端)而不是 1(经典蓝牙):客户端
_resolveProductId 第一步就 where(devicetype==1) 把整个产品滤掉,候选只剩
Echo-one,名字互不包含 → 兜底退回 headsets.first.id = 45091,服务端拿错
pid 去 license_b023 查,自然查不到。而且就算 pid 猜对也过不去——服务端
只有 devicetype==1 才走 MAC 反查,2 会走 else 分支从空 code 反解 pid、
回退写死的 45058。
改动:
- 新增全局表 device_mac(comm.TableDeviceMac),主键 code = MAC;
productid 降为归属列,不再是查询键。TableLicense 标记废弃,只留给迁移读。
- registry.go 的 migrateLicenseToDeviceMac 每次启动跑、幂等:
① code := devicemac 修存量 → ② 按两表列名交集做 INSERT ... ON CONFLICT
DO NOTHING(不能 SELECT *:各分表建于不同时期,有的多 factoryid/probatch)
→ ③ productid 为空的用表名里的 pid 补上。孤儿分表里也是真实发出去的设备,
一并搬。搬完刻意不删旧表——不可逆数据,旧表是出问题时唯一的回退依据。
- binddevice / unbinddevice 改为先按 code 再按 MAC 全局定位,命中行的
productid 才是产品。req.Pid 不再采信。杰理链路也顺带不需要 devcode
反解 pid 了。
- 新增 comm.ScopeToProduct:合表后分表那层天然隔离没有了,每个"按产品"
的读写都必须显式带 productid,漏一处就跨产品操作——查询多列几行只是
难看,批量禁用 / 按批次删除漏掉就是删别人产品的数据。它会把调用方的
条件整体括起来,防止 "a=? or b=?" 让 or 把 productid 短路掉。
- 导入查重改成全局:同一个 MAC 出现在两个产品下会让按 MAC 反查出现歧义。
- 客户端删掉 _resolveProductId,binddevice 只报 MAC 不传 pid。
按需求明确不做应用隔离:MAC 存在即放行。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
90abb0ad42
|
身份证实名核验、翻译与新用户开户礼
本次会话开始前就已存在于工作区的改动,一并提交。主要是三块:
1) 身份证实名核验(sys/idverify + user 模块)
接入阿里云 Id2MetaVerify、腾讯云 IdCardVerification、创蓝三家 provider,
无状态工厂——配置在 svc_config 里按应用作用域存,调用时才解析。
客户端接口 user_idverify / user_getidverify。
凭据是云账号主 AK/SK,故新增服务端专用类别 comm.SvcCatIdVerify=11,
由 svcresolve.go 在下发口整条跳过,svcpool_serveronly_test.go 守着这条底线。
2) 翻译(sys/aliyun/translate + comm/lang.go)
语言码归一与阿里云翻译调用。
3) 新用户开户礼(newuser_gift.go)
配套 api_sgin 建号流程。
注:go test ./modules/user/ 里的 TestApiProbeAppConfigV3 会失败,但与本次改动无关
——那是打线上接口的联调探针,硬编码的域名 app-dev.voitrans.net 已废弃、JWT 也过期了,
该文件自 b2577e16 起未改动过。go test -short 会跳过它,其余全部通过。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
Rodger-Wang
|
db47e0f3f0
|
游客登录入口改为后台按版本控制
需求:后台一个开关控制游客登录按钮显隐,且只对指定版本号的客户端生效,
其余版本一律隐藏(应用商店送审场景:只给正在过审的那一版开)。
白名单语义(comm.ChannelApp.ShowTourists):
- tourists=false → 全部隐藏
- tourists=true 且 touristsversion 空 → 全部隐藏(没点名任何版本 = 谁都不放行)
- tourists=true 且 touristsversion=X → 只有版本号等于 X 的客户端可见
留空按"全隐藏"而非"全放行"是刻意选的失败方向:判宽的后果是不该露游客入口的版本
露了出去(过审风险、白嫖新用户开户礼),判严只是入口少显示。要全部隐藏就取消勾选,
后台 saveChannelApp 会拒绝"勾了开关却没填版本"这种等于没开的配置。
判定放在服务端,与 NeedForceUpdate/IsReviewing 同规矩:客户端在 user_getchannelapps
的请求里带 version,服务端把每条渠道配置的 tourists 换算成结论再下发。版本比较各端
各写一遍必然出现实现不一致,而这个开关直接关系到过审,判错代价高。
touristsversion 是 channel_app 的后加列,console 启动时用 ADD COLUMN IF NOT EXISTS
补上——CreateTable 对已存在的表跳过 AutoMigrate,不补就永远没这列。
注:db.proto / user_msg.proto / CLAUDE.md 里另有先于本次改动就存在的编辑,一并带上。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
1 month ago |
liwei1dao
|
bb3b1c7a3a
|
上传代码
|
2 months ago |
liwei1dao
|
b2577e16fa
|
上传最新的平台代码
|
2 months ago |
liwei1dao
|
af05ff12ce
|
上传代码
|
2 months ago |
liwei1dao
|
1639cf35a0
|
上传代码
|
2 months ago |
liwei1dao
|
8924a3cd3b
|
上传服务后台代码
|
2 months ago |
liwei1dao
|
554a4ab501
|
上传最新的版本代码
|
2 months ago |
liwei1dao
|
948e368996
|
上传代码
|
2 months ago |
liwei1dao
|
59e54eb575
|
上传发布版本
|
2 months ago |
liwei1dao
|
6f7cfe3bb2
|
上传主版本
|
2 months ago |
liwei1dao
|
7660f33258
|
上传最新代码
|
2 months ago |
liwei1dao
|
97ecbf9df5
|
上传新版本的管理后台
|
2 months ago |
liwei1dao
|
18ffb48466
|
上传代码
|
2 months ago |
liwei1dao
|
e270cf89b6
|
上传新版本管理后台
|
2 months ago |
liwei1dao
|
6e5da54cf8
|
上传代码
|
3 months ago |
liwei1dao
|
af9e210f9a
|
会议记录接入后台服务编排:三段独立选路+客户端可选总结模型,移除旧 yaml 四套凭据模式
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
3 months ago |
liwei1dao
|
a25b0c9fc0
|
上传最新的服务后台代码
|
3 months ago |
liwei1dao
|
040af15192
|
上传基础服务代码
|
3 months ago |
liwei1dao
|
197d544062
|
上传管理后台的系统升级和服务配置逻辑优化
|
3 months ago |
liwei1dao
|
99de0fa26f
|
上传代码修复
|
3 months ago |
liwei1dao
|
1390f50144
|
优化邮件登陆服务
|
3 months ago |
liwei1dao
|
eaecd00d20
|
上传基本版本
|
3 months ago |
liwei1dao
|
603dc89073
|
上传优化节点部署代码
|
3 months ago |