fix(mp): T40 UI 审计修复 — 28 项设计系统合规 + 安全加固 + 讨论记录

T40 UI 审计修复(60 页面全覆盖):
- 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码
- 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件)
- 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页)
- 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加)
- statusTag 移除硬编码布局值,改用 SCSS mixin 控制
- 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect)
- 移除 action-inbox 未使用 import

安全 P0 修复:
- JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化
- 速率限制增强:滑动窗口 + 暴力破解防护
- analytics handler 错误处理完善

文档:
- T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK)
- 5 份 DevTools/性能审计讨论记录
- wiki 症状导航 + 小程序章节更新
This commit is contained in:
iven
2026-05-14 23:12:54 +08:00
parent 447126b6c5
commit 8f353946e1
90 changed files with 2089 additions and 830 deletions

View File

@@ -25,6 +25,8 @@ export class BLEManager {
private scanTimer: ReturnType<typeof setTimeout> | null = null;
private onConnectionChange?: (state: BLEConnectionState) => void;
private onReadings?: (readings: NormalizedReading[]) => void;
private connChangeHandler: ((res: any) => void) | null = null;
private charChangeHandler: ((res: any) => void) | null = null;
constructor(config?: Partial<BLEManagerConfig>) {
this.config = { ...DEFAULT_CONFIG, ...config };
@@ -143,13 +145,22 @@ export class BLEManager {
timeout: 10000,
});
// 移除旧监听器,避免多次 connect 累积
if (this.connChangeHandler) {
Taro.offBLEConnectionStateChange(this.connChangeHandler);
}
if (this.charChangeHandler) {
Taro.offBLECharacteristicValueChange(this.charChangeHandler);
}
// 监听断连
Taro.onBLEConnectionStateChange((res: any) => {
this.connChangeHandler = (res: any) => {
if (res.deviceId === device.deviceId && !res.connected) {
this.updateState('disconnected', '设备断开连接');
this.connection = null;
}
});
};
Taro.onBLEConnectionStateChange(this.connChangeHandler);
// 发现服务
const servicesRes = await Taro.getBLEDeviceServices({ deviceId: device.deviceId });
@@ -174,7 +185,7 @@ export class BLEManager {
}
// 监听数据通知
Taro.onBLECharacteristicValueChange((res: any) => {
this.charChangeHandler = (res: any) => {
if (res.deviceId !== device.deviceId) return;
const newReadings = device.adapter!.parseNotification(
res.serviceId,
@@ -186,7 +197,8 @@ export class BLEManager {
this.dataBuffer.push(newReadings);
this.onReadings?.(newReadings);
}
});
};
Taro.onBLECharacteristicValueChange(this.charChangeHandler);
this.connection = { ...this.connection, state: 'connected', connectedAt: Date.now() };
this.updateState('connected');
@@ -273,6 +285,17 @@ export class BLEManager {
if (!this.connection) return;
const { deviceId } = this.connection;
// 移除 BLE 监听器,防止断开后仍收到回调
if (this.connChangeHandler) {
Taro.offBLEConnectionStateChange(this.connChangeHandler);
this.connChangeHandler = null;
}
if (this.charChangeHandler) {
Taro.offBLECharacteristicValueChange(this.charChangeHandler);
this.charChangeHandler = null;
}
try {
await Taro.closeBLEConnection({ deviceId });
} catch {
@@ -326,5 +349,3 @@ export class BLEManager {
}
}
}
export default new BLEManager();

View File

@@ -16,6 +16,8 @@ const DEFAULT_CONFIG: Required<DataBufferConfig> = {
storageKeyPrefix: 'ble_buffer',
};
const MAX_BUCKETS = 20;
/** 离线数据缓冲 — 分桶持久化到 Storage支持去重和容量管理 */
export class DataBuffer {
private config: Required<DataBufferConfig>;
@@ -76,9 +78,14 @@ export class DataBuffer {
let total = 0;
let idx = 0;
while (true) {
while (idx < MAX_BUCKETS) {
const key = `${this.config.storageKeyPrefix}_${idx}`;
const raw = Taro.getStorageSync(key) as string;
let raw: string;
try {
raw = Taro.getStorageSync(key) as string;
} catch {
break;
}
if (!raw) break;
try {
const parsed: NormalizedReading[] = JSON.parse(raw);
@@ -138,9 +145,14 @@ export class DataBuffer {
private clearStorage(): void {
let idx = 0;
while (true) {
while (idx < MAX_BUCKETS) {
const key = `${this.config.storageKeyPrefix}_${idx}`;
const raw = Taro.getStorageSync(key) as string;
let raw: string;
try {
raw = Taro.getStorageSync(key) as string;
} catch {
break;
}
if (!raw) break;
try { Taro.removeStorageSync(key); } catch { /* ignore */ }
idx++;