youruo 56fba389e4 feat: 每日进销存增加定时截图通知
设置(仅管理员)
- 可配置是否启用、通知时间(默认 01:00 Asia/Shanghai)、多名通知用户
- 收件人在后端规范化:去空、去重、限长限量,配置持久化到 system_config
- 接口不返回通知中心密钥与对象存储密钥,只给是否已配置与脱敏预览

定时任务
- 单实例锁(MySQL GET_LOCK),按配置时间触发,命中后 30 分钟内失败最多补跑 3 次
- 服务端签发 15 分钟短时效 JWT 作为专用身份,令牌只写入浏览器 localStorage
- 等数据真正渲染完成(有数据行、无加载中、无错误提示)才截图,展开滚动容器后整页截图
- 对象键形如 daily-inventory/2026-09-25/daily-inventory-20260925-010000.png
- 截图上传成功后才逐用户发通知,单人失败不影响他人;正式推送按「业务日期+收件人」幂等
- 立即测试走同一链路但幂等键带随机数,不影响次日正式推送
- 每次执行记录时间、截图地址、通知对象、成功状态与失败原因

测试
- 覆盖收件人去重、通知时间校验、幂等键、业务日期与对象键、重试退避(17 项)
2026-09-24 11:22:51 +08:00

154 lines
2.6 KiB
CSS

.daily-inventory-page {
padding: 16px 20px 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
.daily-inventory-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.daily-inventory-header__title {
display: flex;
align-items: center;
gap: 4px;
}
.daily-inventory-header__help {
font-size: 13px;
}
.daily-inventory-header__meta {
display: flex;
flex-wrap: wrap;
gap: 16px;
font-size: 13px;
color: rgba(0, 0, 0, 0.55);
}
.daily-inventory-th-tip {
border-bottom: 1px dashed rgba(0, 0, 0, 0.35);
cursor: help;
white-space: nowrap;
}
.daily-inventory-table .ant-table-thead > tr > th {
white-space: nowrap;
font-size: 12px;
padding: 6px 8px;
}
.daily-inventory-table .ant-table-tbody > tr > td {
padding: 5px 8px;
font-size: 13px;
}
/* 运行时取数的列:加淡蓝底纹,与基准值区分 */
.daily-inventory-table .daily-inventory-col--runtime {
background: rgba(22, 119, 255, 0.04);
}
/* 暂无单据来源的列:淡灰底纹,提示该列待 DBA 补表 */
.daily-inventory-table .daily-inventory-col--unavailable {
background: rgba(0, 0, 0, 0.03);
}
.daily-inventory-cell--na {
color: rgba(0, 0, 0, 0.25);
}
.daily-inventory-warehouse {
white-space: nowrap;
}
.daily-inventory-cell--out {
color: #cf1322;
}
.daily-inventory-cell--in {
color: rgba(0, 0, 0, 0.85);
}
.daily-inventory-cell--zero {
color: rgba(0, 0, 0, 0.3);
}
.daily-inventory-row--total > td {
font-weight: 600;
background: rgba(0, 0, 0, 0.03);
}
/* 定时截图通知设置弹窗 */
.daily-inventory-notify-form {
display: flex;
flex-direction: column;
gap: 10px;
}
.daily-inventory-notify-row {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.daily-inventory-notify-label {
width: 120px;
flex: none;
color: rgba(0, 0, 0, 0.75);
}
.daily-inventory-notify-recipients {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-left: 128px;
min-height: 24px;
}
.daily-inventory-notify-meta {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
gap: 4px;
font-size: 13px;
}
.daily-inventory-notify-runs {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 4px;
font-size: 12px;
}
.daily-inventory-notify-run {
display: flex;
align-items: center;
gap: 10px;
color: rgba(0, 0, 0, 0.6);
}
.daily-inventory-footer {
font-size: 12px;
}
.daily-inventory-help p {
margin: 0 0 6px;
line-height: 1.7;
font-size: 13px;
white-space: pre-wrap;
}
.daily-inventory-help p.is-indent {
padding-left: 16px;
color: rgba(0, 0, 0, 0.65);
}