fix(mobile): converge work report confirmation
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- **2026-07-29,Android 文件选择、麦克风拒绝降级与上传路径修复,源码候选升级为 `0.1.16 (116)`,尚无签名 APK**:真机确认 `0.1.13` 在允许麦克风时可完成原生录音和生产 ASR,但拒绝后的降级误用 App 不支持的 `uni.chooseFile`。Android 非媒体文件现改用系统 `ACTION_OPEN_DOCUMENT`,通过 `ParcelFileDescriptor + FileChannel.transferFrom` 复制到应用专属外部缓存,避免 Native.js `byte[]` 参数兼容问题;图片/视频改走 App `uni.chooseMedia`,页面生命周期会保留系统选择器中的待完成任务,录音前统一检查麦克风权限。vivo Android 15 真机在 HBuilderX 5.22 标准调试基座完成取消、重复打开、`100,542` 字节 WAV 精确复制、multipart 上传、生产 ASR 和成果草稿整理,未点击确认投稿。移动端 206 项测试、类型检查、App 发布资源预检和 App-Plus 构建通过;调查用 `0.1.14/0.1.15` APK 已删除。DCloud 当日云打包额度耗尽,最终 `0.1.16` 签名 APK、深检和同包真机复验仍未完成,因此当前仍为 No-Go,未发布移动 H5、原生 App、后端、管理端或数据库。
|
||||
- **2026-07-29,Android 文件选择、麦克风拒绝降级与上传路径修复,源码候选升级为 `0.1.16 (116)`,尚无签名 APK**:真机确认 `0.1.13` 在允许麦克风时可完成原生录音和生产 ASR,但拒绝后的降级误用 App 不支持的 `uni.chooseFile`。Android 非媒体文件现改用系统 `ACTION_OPEN_DOCUMENT`,通过 `ParcelFileDescriptor + FileChannel.transferFrom` 复制到应用专属外部缓存,避免 Native.js `byte[]` 参数兼容问题;图片/视频改走 App `uni.chooseMedia`,页面生命周期会保留系统选择器中的待完成任务,录音前统一检查麦克风权限。vivo Android 15 真机在 HBuilderX 5.22 标准调试基座完成取消、重复打开、`100,542` 字节 WAV 精确复制、multipart 上传和生产 ASR。首次成果投稿验证实际只进入追问态,当时并未出现“确认投稿”,此前“未点击确认投稿”的表述已纠正;继续补充时线上整理请求耗时 `94,965ms`,超过客户端 `45,000ms` 门限。当前源码改为仅首轮调用 AI,后续原话并入当前草稿后交由既有服务端本地复核,真机复验后续整理耗时 `1ms`,确认卡及“确认投稿”按钮均已显示但未点击,未形成正式投稿。移动端 206 项测试、类型检查、App 发布资源预检和 App-Plus 构建通过;调查用 `0.1.14/0.1.15` APK 已删除。DCloud 当日云打包额度耗尽,最终 `0.1.16` 签名 APK、深检和同包真机复验仍未完成,因此当前仍为 No-Go,未发布移动 H5、原生 App、后端、管理端或数据库。
|
||||
- **2026-07-29,资料处理中心普通文档/图片单文件 500MB 已发布并完成生产验证**:管理端批量导入、后端异步队列、ZIP 子文件和 Tika 输入流统一允许受支持文件 `≤500MiB`,沿用生产 Spring/Undertow `500MB/520MB` 传输层配置;仍保留 ZIP 最多 1000 个子文件、解压总量 ≤2GB、禁止嵌套 ZIP 等安全边界,同步 SOP 上传和公司文件继续保持 100MB。生产实测发现首个候选虽已放宽页面和队列校验,但 Tika 内仍残留独立 `100MiB` 输入流上限,导致首批队列 `1194/1195` 上传成功后加工失败;补齐解析器上限及回归断言后,11 项定向后端测试和后端生产打包通过。管理端发布前备份整站到 `/opt/wygj/backups/www-20260729213203`,线上 `processing-BjvQ3qpr.js` 与本地产物 SHA-256 均为 `6ace59477519d6c53cbfe207a229518a7e2f7568e05961cfea28d669e1eeb305`,移动 H5 树哈希保持 `ba4aa656d64fc301141280357c204a67141d1e6b2e2a26a0be21c0dd48a25712` 未变。最终后端 JAR SHA-256 为 `be16c229cfc255e4cdf5ec32df047994b54d7d2c31b6c729dde6d949e191e53d`,旧包备份在 `/opt/wygj/backups/backend-20260729214901-5ef5dd484b5d`;整包及内嵌 `ruoyi-aihr-5.6.2.jar` 远近端哈希一致,服务、公开接口、64/64 schema 和生产禁运行时 DDL 预检均通过。已登录生产 Chrome 验收批次 `b-c2560e95-0cf7-46e0-9bdc-7690ea86ffd7` 中,`4、巡检表(事业部颗粒归仓专用).xlsx`(101.6MB,队列 `1196`、附件 `2036`)完成 5 个片段和 5 个向量,`新员工培训-社区发展中心-社区资源.pptx`(147.9MB,队列 `1197`、附件 `2037`)完成 7 个片段和 7 个向量;两项均显示“已完成/已入库”,数据库片段均含 embedding。资料总数从 1032 增至 1034,目标空间从 22 增至 24 个文件,处理中和需处理均为 0。本次只定向发布后端与管理端,未重发移动 H5、未执行 schema 迁移,不代表当前工作树与线上完整包全部匹配。
|
||||
- **2026-07-29,资料处理媒体任务恢复已发布并完成生产验证**:修复历史图片/视频在视觉输入不兼容或未提取到文字时长期停在“等待解析”的问题;图片在送入视觉模型前会按方向纠正、限制像素和体积并统一编码,异步队列只有生成至少一个片段才进入完成态,零片段会保留为可重试失败。新增 `POST /api/knowledge/doc/processing-tasks/{attachmentId}/retry`,可从当前租户原 OSS 对象重新暂存并入队;正式迁移 `aihr_20260729_media_reprocess_mysql8.sql` 增加 `source_attach_id` 和 `(tenant_id, source_attach_id, id)` 索引。生产已备份 schema、后端和完整站点,发布管理端与后端 JAR `5376b67b33b171b5c85e22cd6a2f20bbb0bc527fc50a223603d0aec3f6587bac`,移动 H5 未重发;发布脚本在重启后等待公开端点真实就绪再执行预检,服务为 `active`,根站、租户和移动首页均返回 HTTP 200。原卡住任务 `1133/1175/1308/1405` 从原文件重试为队列 `1185–1188`,四项均完成、各生成一个片段且向量存在;资料处理页显示“待重试 0”、Tika `1028/1028`、可引用资料 `1028`。该证据证明本次资料恢复和定向发布成功,不代表移动 H5 与当前工作树完整匹配,也不替代 Android 真机或正式试点验收。
|
||||
- **2026-07-29,新增移动端 UI token 与渲染态审计,未发布静态资源**:`audit:ui-tokens` 以 `mobile-uni/src/styles/base.css` 为源扫描颜色、字阶、间距和圆角,`audit:ui-rendered` 在 390×844 的真实 Chrome 中审计 H5 构建的六个默认路由并保存截图。两项均把报告写入 Git 已忽略的 `output/ui-ux/`;本地实跑覆盖 53 个源码文件和 6 个渲染路由,渲染路由均无横向溢出或控制台错误,同时如实记录现有 palette 外颜色、孤立字号和非 token 圆角,不能把“脚本执行成功”解释为视觉债务已清零。
|
||||
|
||||
@@ -54,7 +54,7 @@ Android 每个人工步骤完成后,可在不改变设备状态的前提下保
|
||||
|
||||
同一真机随后使用生产现有在职 APP 主体和固定码完成页面登录;账号、固定码和令牌未进入日志、截图或仓库。`0.1.13` 允许麦克风后,原生按住录音、上传和生产 ASR 已跑通,识别结果进入真实“问”主路径。拒绝麦克风后的文件降级同时暴露两个 App-Plus 缺陷:`uni.chooseFile` 在 App 不受支持,且 Native.js 无法可靠构造并传递 Java `byte[]`。调查包 `0.1.14/0.1.15` 仅用于定位,生成的 APK 已删除,不得分发。
|
||||
|
||||
当前源码候选已升级为 `0.1.16 (116)`:Android 非媒体文件通过系统 `ACTION_OPEN_DOCUMENT` 选择,以 `ParcelFileDescriptor + FileChannel.transferFrom` 复制到应用专属外部缓存;页面生命周期会区分系统选择器暂时离开和真实离页,图片/视频改走 App 支持的 `uni.chooseMedia`,麦克风拒绝在启动录音前统一进入降级。vivo 真机使用 HBuilderX 5.22 标准调试基座完成选择器打开、取消、重复打开及选择 `100,542` 字节 WAV;复制后字节数一致,并继续完成 multipart 上传、生产 ASR 和成果投稿草稿整理,识别测试语句后只生成追问草稿,未点击“确认投稿”、未形成正式投稿。当前 206/206 项移动端测试、类型检查、App 发布资源预检和 App-Plus 构建通过。
|
||||
当前源码候选已升级为 `0.1.16 (116)`:Android 非媒体文件通过系统 `ACTION_OPEN_DOCUMENT` 选择,以 `ParcelFileDescriptor + FileChannel.transferFrom` 复制到应用专属外部缓存;页面生命周期会区分系统选择器暂时离开和真实离页,图片/视频改走 App 支持的 `uni.chooseMedia`,麦克风拒绝在启动录音前统一进入降级。vivo 真机使用 HBuilderX 5.22 标准调试基座完成选择器打开、取消、重复打开及选择 `100,542` 字节 WAV;复制后字节数一致,并继续完成 multipart 上传和生产 ASR。首次成果投稿验证只生成追问草稿,实际尚未显示“确认投稿”,不是已经出现按钮但未点击。复查又确认每轮补充重新调用模型会继续生成追问,且一次线上整理耗时 `94,965ms`,超过客户端 `45,000ms` 超时门限。当前源码改为首轮 AI 整理、后续原话并入当前草稿后交由既有服务端本地复核;同一真机复验后续整理耗时 `1ms`,已显示成果投稿确认卡和“确认投稿”按钮,但没有点击、没有形成正式投稿。当前 206/206 项移动端测试、类型检查、App 发布资源预检和 App-Plus 构建通过。
|
||||
|
||||
由于 DCloud 当日免费云打包次数已用尽,`0.1.16` 的最终签名 APK 尚未生成,标准调试基座证据不能冒充签名包验收。最后一个可重复校验的签名包仍是 `0.1.13 (113)`,但它不包含上述系统选择器修复;额度恢复或购买次数后必须重新云打包、深检并在同一真机复跑麦克风拒绝、选择器取消/选择和 ASR。当前结论因此仍为 No-Go。
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
同日后续已获得独立授权并通过 `release-backend.sh` 完成生产后端定向发布;生产 JAR SHA-256 为 `5376b67b33b171b5c85e22cd6a2f20bbb0bc527fc50a223603d0aec3f6587bac`,服务、三个公开入口和后端 + schema 发布后预检通过,后端发布不再是 8 月 1 日 APK 的阻断项。同批发布资料处理媒体恢复 schema 和管理端,移动 H5 未重发;这不改变正式场景/题目、五通道处理人、受控分发与真机设备门禁的 No-Go 状态。
|
||||
|
||||
同日真机继续确认 `0.1.13` 的麦克风允许和生产 ASR 可用,但拒绝后的降级误用 App 不支持的 `uni.chooseFile`。当前源码已升级为 `0.1.16 (116)`,改用 Android 系统文档选择器、`ParcelFileDescriptor + FileChannel.transferFrom`、应用专属外部缓存和 App 媒体选择器,并保留系统选择器暂时离开时的待完成任务。vivo 真机标准调试基座完成取消、重复打开、`100,542` 字节 WAV 选择/复制/上传/生产 ASR 和无提交草稿整理;206 项测试、类型检查、App 发布资源预检和 App-Plus 构建通过。调查用 `0.1.14/0.1.15` APK 已删除,DCloud 当日云打包额度耗尽,`0.1.16` 签名 APK 尚不存在,不能进入分发。
|
||||
同日真机继续确认 `0.1.13` 的麦克风允许和生产 ASR 可用,但拒绝后的降级误用 App 不支持的 `uni.chooseFile`。当前源码已升级为 `0.1.16 (116)`,改用 Android 系统文档选择器、`ParcelFileDescriptor + FileChannel.transferFrom`、应用专属外部缓存和 App 媒体选择器,并保留系统选择器暂时离开时的待完成任务。vivo 真机标准调试基座完成取消、重复打开、`100,542` 字节 WAV 选择/复制/上传和生产 ASR。首次成果投稿验证只到追问态,没有出现“确认投稿”;继续补充又暴露整理请求 `94,965ms`、超过客户端 `45,000ms` 的问题。当前源码已改为仅首轮调用 AI,后续补充走服务端本地合并复核,同机复验耗时 `1ms` 并显示确认卡和“确认投稿”按钮,但未点击、未形成正式投稿。206 项测试、类型检查、App 发布资源预检和 App-Plus 构建通过。调查用 `0.1.14/0.1.15` APK 已删除,DCloud 当日云打包额度耗尽,`0.1.16` 签名 APK 尚不存在,不能进入分发。
|
||||
|
||||
生活顾问调研已转成 `docs/content-candidates/aug1-life-advisor-content-candidates-v0.1.json`:包含 5 个禁用场景候选和 30 道待审政策题,内容候选校验脚本通过。由于当前只收到 1/24 份访谈,且正式来源、业务审核和 Rubric 签认尚未完成,正式可发布场景与正式有据标准答案均为 `0`;交付包中的候选文件只供审核,不得直接导入或启用。
|
||||
|
||||
|
||||
@@ -251,6 +251,7 @@ import {
|
||||
isCurrentWorkReportRequest,
|
||||
isDefinitiveWorkReportRejection,
|
||||
organizeWorkReport,
|
||||
prepareWorkReportOrganizeInput,
|
||||
stableWorkReportRequestId,
|
||||
submitWorkReport,
|
||||
uploadWorkReportAttachment,
|
||||
@@ -430,6 +431,7 @@ const organizeInput = async (
|
||||
const transcript = text.trim();
|
||||
if (!transcript || organizing.value || draftLocked.value || submittedReport.value) return;
|
||||
const previousDraft = draftPayload();
|
||||
const organizeRequest = prepareWorkReportOrganizeInput(transcript, previousDraft);
|
||||
pushMessage<UserMessage>({
|
||||
role: 'user',
|
||||
text: transcript,
|
||||
@@ -443,8 +445,8 @@ const organizeInput = async (
|
||||
speechPlayback.stop();
|
||||
try {
|
||||
const result = await organizeWorkReport({
|
||||
transcript,
|
||||
currentDraft: previousDraft,
|
||||
transcript: organizeRequest.transcript,
|
||||
currentDraft: organizeRequest.currentDraft,
|
||||
attachmentOssId: attachment.value?.ossId,
|
||||
attachmentName: attachment.value?.fileName
|
||||
});
|
||||
|
||||
@@ -52,6 +52,29 @@ export const isCurrentWorkReportRequest = (
|
||||
currentAccountKey: string
|
||||
) => generation === currentGeneration && accountKey === currentAccountKey;
|
||||
|
||||
export const prepareWorkReportOrganizeInput = (
|
||||
transcript: string,
|
||||
currentDraft?: WorkReportDraft
|
||||
): { transcript: string; currentDraft?: WorkReportDraft } => {
|
||||
const addition = transcript.trim();
|
||||
if (!currentDraft) return { transcript: addition };
|
||||
|
||||
const currentContent = currentDraft.content.trim();
|
||||
const content = !currentContent
|
||||
? addition
|
||||
: !addition || currentContent === addition
|
||||
? currentContent
|
||||
: `${currentContent}\n${addition}`;
|
||||
|
||||
return {
|
||||
transcript: '',
|
||||
currentDraft: {
|
||||
...currentDraft,
|
||||
content
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const workReportTypes: Array<{ value: WorkReportType; label: string; desc: string; icon: string }> = [
|
||||
{ value: 'CASE', label: '优秀案例', desc: '现场处理得好的真实案例', icon: '⭐' },
|
||||
{ value: 'VIDEO', label: '操作视频', desc: '值得推广的操作演示视频', icon: '🎬' },
|
||||
|
||||
@@ -36,7 +36,8 @@ test('语音转写、文字和附件补充都进入无状态整理', async () =>
|
||||
assert.match(page, /transcribeSpeechFile/);
|
||||
assert.match(page, /organizeWorkReport\(/);
|
||||
assert.match(page, /const previousDraft = draftPayload\(\)/);
|
||||
assert.match(page, /currentDraft:\s*previousDraft/);
|
||||
assert.match(page, /prepareWorkReportOrganizeInput\(transcript, previousDraft\)/);
|
||||
assert.match(page, /currentDraft:\s*organizeRequest\.currentDraft/);
|
||||
assert.match(page, /attachmentOssId:\s*attachment\.value\?\.ossId/);
|
||||
assert.match(page, /void organizeInput\(text, \{ voice: false \}\)/);
|
||||
assert.match(page, /void addMediaAttachment\(\)/);
|
||||
@@ -171,4 +172,26 @@ test('幂等键与异步账号守卫是可执行行为,不只是源码字符
|
||||
assert.equal(state.isCurrentWorkReportRequest(3, 3, 'account-a', 'account-a'), true);
|
||||
assert.equal(state.isCurrentWorkReportRequest(2, 3, 'account-a', 'account-a'), false);
|
||||
assert.equal(state.isCurrentWorkReportRequest(3, 3, 'account-a', 'account-b'), false);
|
||||
|
||||
const firstTurn = state.prepareWorkReportOrganizeInput(' 首轮原话 ');
|
||||
assert.deepEqual(firstTurn, { transcript: '首轮原话' });
|
||||
|
||||
const existingDraft = {
|
||||
suggestedType: 'CASE',
|
||||
title: '真机验收',
|
||||
content: '已完成文件选择和 ASR'
|
||||
};
|
||||
const followUp = state.prepareWorkReportOrganizeInput(' 补充设备和结果 ', existingDraft);
|
||||
assert.deepEqual(followUp, {
|
||||
transcript: '',
|
||||
currentDraft: {
|
||||
suggestedType: 'CASE',
|
||||
title: '真机验收',
|
||||
content: '已完成文件选择和 ASR\n补充设备和结果'
|
||||
}
|
||||
});
|
||||
assert.equal(existingDraft.content, '已完成文件选择和 ASR');
|
||||
|
||||
const duplicate = state.prepareWorkReportOrganizeInput(' 已完成文件选择和 ASR ', existingDraft);
|
||||
assert.equal(duplicate.currentDraft.content, '已完成文件选择和 ASR');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user