Compare commits

...

2 Commits

Author SHA1 Message Date
991d713777 feat(backend): 新增留言通知 — add_task_record 自动推送给任务负责人
触发条件:
- 有人对任务添加流转记录(留言/备注)
- 任务有 assignee_id
- 留言人 != 任务负责人 (不给自己发通知)

通知内容:
- title: 💬 收到新留言
- content: 产品[SN]的「任务名」有新留言:{前30字摘要}
- type: COMMENT

额外:
- Notification 模型新增 NOTIFY_COMMENT 常量
- 前端 notify 页新增 COMMENT 图标(💬)和标题(收到新留言)
2026-08-11 18:17:52 +08:00
7e4d796ce3 perf(app): OTA下载进度节流 — 20%步进轻提示根治卡顿
问题: plus.nativeUI.showWaiting 每次下载回调都触发原生等待框更新,
      高频调用导致主线程阻塞产生无响应/掉帧。

修复:
1. 废除 plus.nativeUI.showWaiting 全系列调用
2. 改用 uni.showToast + position:'top' 顶部轻提示
3. 核心: 节流阀 lastProgress — 每跨越20%才触发一次UI更新
   整个下载过程最多5次toast (20/40/60/80/100%),根治跨端卡顿
4. 初始toast '已转入后台下载...' 给用户明确反馈
5. 安装/重启逻辑保持不变(静默安装+2s自动重启)
2026-08-11 18:01:17 +08:00
5 changed files with 46 additions and 14 deletions

View File

@ -308,4 +308,4 @@ async def add_task_record_endpoint(
current_user: dict = Depends(get_current_user),
):
"""追加进度记录(备注+图片),不改变任务状态"""
return await task_service.add_task_record(db, uuid.UUID(task_id), data)
return await task_service.add_task_record(db, uuid.UUID(task_id), data, current_user)

View File

@ -11,6 +11,7 @@ from app.core.time_utils import get_beijing_time
# 通知类型常量
NOTIFY_TRANSFER = "TRANSFER" # 新任务派发/转交
NOTIFY_REJECT = "REJECT" # 品质驳回
NOTIFY_COMMENT = "COMMENT" # 留言提醒
class Notification(Base):

View File

@ -1042,7 +1042,8 @@ async def create_subtask(
# ============================================================
async def add_task_record(
db: AsyncSession, task_id: uuid.UUID, data: TaskRecordCreate
db: AsyncSession, task_id: uuid.UUID, data: TaskRecordCreate,
current_user: dict | None = None,
) -> TaskResponse:
"""追加进度记录(备注+图片),不改变任务状态"""
import json
@ -1055,6 +1056,32 @@ async def add_task_record(
images=json.dumps(data.images) if data.images else None,
)
db.add(record)
await db.flush()
# 🚀 留言通知:给任务当前负责人发送提醒(不给自己发)
if (
current_user
and task.assignee_id
and task.assignee_id != current_user.get("username", "")
and data.remark
):
# 截取留言内容前 30 字作为摘要
remark_text = data.remark.strip()
short_content = remark_text[:30] + ("..." if len(remark_text) > 30 else "")
# 查询产品条码
product_result = await db.execute(
select(Product).where(Product.id == task.product_id)
)
product = product_result.scalar_one_or_none()
product_sn = product.serial_number if product else "未知"
db.add(Notification(
user_id=task.assignee_id,
title="💬 收到新留言",
content=f"产品 [{product_sn}] 的「{task.task_name}」有新留言:{short_content}",
type="COMMENT",
task_id=task.id,
))
await db.commit()
await db.refresh(record)

View File

@ -109,35 +109,32 @@ export default {
success: (modalRes) => {
if (!modalRes.confirm) return;
// 🚀 使用原生等待框,原地更新文字,杜绝闪烁
plus.nativeUI.showWaiting("正在下载 0%");
// 初始反馈:告知用户已转入后台
uni.showToast({ title: '已转入后台下载...', icon: 'none', position: 'top' });
const downloadTask = uni.downloadFile({
url: wgtUrl,
success: (downloadRes) => {
if (downloadRes.statusCode !== 200) {
plus.nativeUI.closeWaiting();
uni.showToast({ title: "下载失败", icon: "none" });
return;
}
// 安装阶段 — 原生等待框直接更新文字,无闪烁
plus.nativeUI.showWaiting("正在安装...");
// 安装阶段 — toast 轻提示
uni.showToast({ title: "正在安装...", icon: "none", position: "top" });
plus.runtime.install(
downloadRes.tempFilePath,
{ force: true },
() => {
plus.nativeUI.closeWaiting();
console.log("[OTA] WGT 安装成功");
// 🚀 静默重启toast 提示后自动重启,无需用户杀后台
// 🚀 静默重启toast 提示后自动重启
plus.nativeUI.toast("新版本已就绪,即将重启...");
setTimeout(() => {
plus.runtime.restart();
}, 2000);
},
(err) => {
plus.nativeUI.closeWaiting();
console.error("[OTA] 安装失败:", err.message);
uni.showToast({
title: "更新失败: " + (err.message || "未知错误"),
@ -148,18 +145,24 @@ export default {
);
},
fail: (err) => {
plus.nativeUI.closeWaiting();
console.error("[OTA] 下载失败:", err.errMsg);
uni.showToast({ title: "下载失败,请检查网络", icon: "none" });
},
});
// 下载进度 — 稳定更新原生等待框文字,不会闪烁
// 🚀 核心性能优化:节流阀 — 每跨越 20% 才触发一次轻提示
let lastProgress = 0;
if (downloadTask && downloadTask.onProgressUpdate) {
downloadTask.onProgressUpdate((res) => {
const pct = res.progress;
if (pct < 100) {
plus.nativeUI.showWaiting(`正在下载 ${pct}%`);
if (pct - lastProgress >= 20 && pct < 100) {
lastProgress = pct;
uni.showToast({
title: `新版本下载中 ${pct}%`,
icon: "none",
position: "top",
duration: 1200,
});
}
});
}

View File

@ -51,6 +51,7 @@ import { getNotifications, markNotificationRead } from "../../api/notification";
const TYPE_CONFIG = {
TRANSFER: { icon: "🟢", title: "新任务派发" },
REJECT: { icon: "🔴", title: "品质驳回提醒" },
COMMENT: { icon: "💬", title: "收到新留言" },
};
const notifications = ref([]);