feat(oss): 补齐开源声明体系(MIT + 第三方声明 + 贡献/安全/行为准则)
* LICENSE —— MIT
* THIRD-PARTY-NOTICES —— 依赖清单、再分发合规说明(含随仓库分发的
Apache ECharts 5.6.0 / Apache-2.0)与自查清单
* CONTRIBUTING.md —— 开发环境、五层验证、必须遵守的不变量、提交规范
* SECURITY.md —— 漏洞私有报告渠道、已有措施、已知非目标
* CODE_OF_CONDUCT.md —— 改编自 Contributor Covenant 2.1
* .github/ —— Bug 报告 / 功能建议表单 + PR 模板
* .editorconfig —— 与 .gitattributes 保持一致
* 全部 Python / Shell 源文件加 SPDX-License-Identifier: MIT 头
* README 增加「开源与许可」章节与许可标识
这个提交包含在:
+33
@@ -0,0 +1,33 @@
|
||||
# 统一的编辑器约定。与 .gitattributes(* text=auto eol=lf)保持一致。
|
||||
# 这样无论谁在什么系统上编辑,提交进来的都是 LF 与 UTF-8。
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{html,css,js,json,yml,yaml,svg}]
|
||||
indent_size = 2
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
max_line_length = 100
|
||||
|
||||
[*.md]
|
||||
# Markdown 里行尾两个空格是有意义的分行,别自动删掉
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.sh]
|
||||
indent_size = 4
|
||||
# 容器 entrypoint 必须是 LF,否则报 exec format error / no such file or directory
|
||||
end_of_line = lf
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.{png,jpg,jpeg,gif,ico,woff,woff2,sqlite}]
|
||||
insert_final_newline = false
|
||||
@@ -0,0 +1,83 @@
|
||||
name: Bug 报告
|
||||
description: 报告一个可复现的问题
|
||||
title: "[Bug] "
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢反馈。提交前请先确认:
|
||||
|
||||
1. 你已经读过 [FAQ](https://git.iwali.top/wangchuanli/workbuddy-portal/src/branch/main/docs/FAQ.md) 与 [部署排错](https://git.iwali.top/wangchuanli/workbuddy-portal/src/branch/main/docs/DEPLOYMENT.md);
|
||||
2. **不要贴真实 Cookie、secret_key 或真实用量数据**——需要复现请用 `python tools/demo_data.py` 生成的示例数据。
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本 / 提交号
|
||||
description: 例如 1.1.0,或 `git rev-parse --short HEAD` 的输出
|
||||
placeholder: 1.1.0 / 118e27f
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: deploy
|
||||
attributes:
|
||||
label: 部署方式
|
||||
options:
|
||||
- Docker Compose(命名卷,推荐)
|
||||
- Docker Compose + hostdir 叠加层(绑定挂载)
|
||||
- 裸机 waitress
|
||||
- 裸机 Flask 开发服务器
|
||||
- 其他(请在补充说明里写)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: env
|
||||
attributes:
|
||||
label: 运行环境
|
||||
description: 操作系统与 Python 版本
|
||||
placeholder: Windows 11 + Docker Desktop 4.3x / Ubuntu 24.04 + Python 3.13
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what
|
||||
attributes:
|
||||
label: 现象
|
||||
description: 发生了什么?预期是什么?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: 越具体越好。涉及数据口径的问题请说明用的是示例数据还是真实数据。
|
||||
placeholder: |
|
||||
1. `docker compose up -d --build`
|
||||
2. 打开 /dashboard
|
||||
3. 点击「30 天」
|
||||
4. 看到 …
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 日志 / 报错
|
||||
description: |
|
||||
相关日志。**请先脱敏**(抹掉 Cookie、域名、内网 IP、真实模型名)。
|
||||
大屏类问题请附浏览器 Console 的报错。
|
||||
render: text
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: checks
|
||||
attributes:
|
||||
label: 已做过的自查
|
||||
description: 例如是否跑过 `tools/check_live.py`、是否换过浏览器、是否重启过容器
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 使用问题 / 部署排错
|
||||
url: https://git.iwali.top/wangchuanli/workbuddy-portal/src/branch/main/docs/FAQ.md
|
||||
about: 常见问题、Cookie 获取、时区与调度、备份恢复等,先看 FAQ 与部署文档
|
||||
- name: 安全漏洞
|
||||
url: https://git.iwali.top/wangchuanli/workbuddy-portal/src/branch/main/SECURITY.md
|
||||
about: 请勿公开提交可直接利用的漏洞细节,按 SECURITY.md 走私有渠道
|
||||
@@ -0,0 +1,47 @@
|
||||
name: 功能建议
|
||||
description: 提出一个新功能或改进
|
||||
title: "[Feature] "
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
在提建议前,请先看一眼 [架构说明](https://git.iwali.top/wangchuanli/workbuddy-portal/src/branch/main/docs/ARCHITECTURE.md)
|
||||
里的「已知边界」——有些能力是**刻意不做**的(例如不引入 APScheduler、不新增第三方依赖)。
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 你想解决什么问题
|
||||
description: 先说场景与痛点,再说方案。这样更容易判断有没有更简单的做法。
|
||||
placeholder: 我在做 … 的时候,必须手动 … ,很费时。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: 你期望的做法
|
||||
description: 如果有具体的接口/页面/参数设计,写在这里
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 考虑过的替代方案
|
||||
description: 以及为什么它们不够好
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: constraints
|
||||
attributes:
|
||||
label: 约束自查
|
||||
options:
|
||||
- label: 该功能不需要新增第三方依赖(或已在下方说明理由)
|
||||
required: false
|
||||
- label: 该功能不破坏「SQLite 单写者」这一前提
|
||||
required: false
|
||||
- label: 若涉及列表类接口,我不会在其中返回 `prompt` 全文
|
||||
required: false
|
||||
@@ -0,0 +1,46 @@
|
||||
## 这个 PR 做了什么
|
||||
|
||||
<!-- 一句话说清。若是修 bug,请写清根因,而不是只写「修了个 bug」。 -->
|
||||
|
||||
## 关联 Issue
|
||||
|
||||
<!-- 例如 Closes #12 -->
|
||||
|
||||
## 改动类型
|
||||
|
||||
- [ ] Bug 修复
|
||||
- [ ] 新功能
|
||||
- [ ] 重构(不改变外部行为)
|
||||
- [ ] 文档
|
||||
- [ ] 构建 / 部署 / CI
|
||||
|
||||
## 改动清单
|
||||
|
||||
<!-- 文件路径 + 做了什么,便于快速 review。例:
|
||||
- `workbuddy_portal/query.py` —— bundle() 里 daily 改为全量下发
|
||||
- `docs/API.md` —— 同步说明 daily 的范围语义
|
||||
-->
|
||||
|
||||
## 验证情况
|
||||
|
||||
<!-- 请贴出实际输出,不要只写「已测试」。至少覆盖第 2 层。 -->
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| `python -m compileall -q workbuddy_portal manage.py tools` | |
|
||||
| `python tools/smoke.py` | `RESULT: ok=?? fail=0` |
|
||||
| `python tools/check_live.py`(如起了服务) | `RESULT: ok=?? fail=0` |
|
||||
| `python tools/shots.py`(如改了前端) | 无 JS 报错 |
|
||||
| `docker compose up -d --build`(如改了容器相关) | 容器 healthy |
|
||||
|
||||
## 破坏性变更 / 需要部署方做的事
|
||||
|
||||
<!-- 有就写:环境变量、数据迁移、需要重建镜像、需要改反向代理配置…;没有就写「无」 -->
|
||||
|
||||
## 自查确认
|
||||
|
||||
- [ ] 没有提交任何凭据、`data/usage.sqlite`、`logs/*`、`instance.json`(已用 `git check-ignore -v` 复核过忽略规则)
|
||||
- [ ] 没有在列表类接口里新增返回 `prompt` 全文
|
||||
- [ ] 若涉及数据口径变更,我已用**独立聚合**与页面结果逐项比对
|
||||
- [ ] 新增的第三方资源已登记到 `THIRD-PARTY-NOTICES.md`
|
||||
- [ ] 文档(README / docs / CHANGELOG)已同步更新
|
||||
@@ -0,0 +1,59 @@
|
||||
# 行为准则(Code of Conduct)
|
||||
|
||||
## 我们的承诺
|
||||
|
||||
为了营造开放、友善的协作环境,我们作为贡献者与维护者承诺:**让每个人参与本项目的体验都不受骚扰**,
|
||||
无论其年龄、体型、可见或不可见的残障、族裔、性别认同与表达、经验水平、教育程度、
|
||||
社会经济状况、国籍、外貌、种族、宗教,或性取向与身份认同。
|
||||
|
||||
## 我们的标准
|
||||
|
||||
**有助于营造积极环境的行为:**
|
||||
|
||||
- 对他人展现同理心与善意
|
||||
- 尊重不同的意见、观点与经验
|
||||
- 给出并优雅地接受建设性的反馈
|
||||
- 承担责任、向被我们影响的人致歉,并从中学习
|
||||
- 关注对整个社区最有利的事,而不只是对我们个人
|
||||
|
||||
**不可接受的行为:**
|
||||
|
||||
- 性化的言语或图像,以及任何形式的性关注或挑逗
|
||||
- 挑衅、侮辱或贬损性评论,以及人身攻击或政治攻击
|
||||
- 公开或私下的骚扰
|
||||
- 未经明确许可,公布他人的私人信息(如真实姓名、住址、邮箱、凭据等)
|
||||
- 其他在专业场合中可被合理视为不当的行为
|
||||
|
||||
> 与本项目技术定位直接相关的一条:**请勿在 Issue、PR、讨论或截图中提交真实的账号凭据、
|
||||
> 云端 Cookie、`secret_key` 或真实用户的用量数据**。需要复现时请用 `tools/demo_data.py`
|
||||
> 生成的示例数据,或自行脱敏。这类内容会被立即删除。
|
||||
|
||||
## 维护者的责任
|
||||
|
||||
维护者负责澄清并执行上述标准,对任何被视为不当、威胁、冒犯或有害的行为,
|
||||
有权采取适当且公平的纠正措施,包括删除、编辑或拒绝评论、提交、代码与 Issue,
|
||||
并在必要时临时或永久禁止任何贡献者参与。
|
||||
|
||||
## 适用范围
|
||||
|
||||
本准则适用于所有项目空间,也适用于个人在**公开场合代表本项目**时的言行。
|
||||
|
||||
## 报告与执行
|
||||
|
||||
如遇滥用、骚扰或其他不可接受的行为,请通过维护者在代码托管平台上公布的联系方式私下报告
|
||||
(参见 [SECURITY.md](SECURITY.md) 中的私有渠道)。所有投诉都会被及时、公正地审查与处理。
|
||||
维护者有义务尊重报告者的隐私与安全。
|
||||
|
||||
## 执行准则
|
||||
|
||||
维护者将按下述梯度决定后果:
|
||||
|
||||
1. **更正** — 私下书面警告,说明违规性质并解释为何不当。必要时要求公开致歉。
|
||||
2. **警告** — 在一段时间内禁止与相关人员互动;违反将导致临时或永久封禁。
|
||||
3. **临时封禁** — 在指定期限内禁止任何形式的公开或私下互动。
|
||||
4. **永久封禁** — 永久禁止在项目内进行任何形式的公开互动。
|
||||
|
||||
## 致谢
|
||||
|
||||
本准则改编自 [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 版
|
||||
(原文以 CC BY 4.0 发布)。执行准则参考其「Enforcement Guidelines」部分。
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
# 贡献指南(Contributing)
|
||||
|
||||
感谢你有兴趣改进 WorkBuddy Portal。这是一个**单进程 Flask + SQLite** 的轻量项目,
|
||||
刻意保持了很小的依赖面与很平的目录结构——请先花两分钟读完本文,你的改动会更顺利被接受。
|
||||
|
||||
---
|
||||
|
||||
## 一、开发环境
|
||||
|
||||
| 项 | 要求 |
|
||||
|---|---|
|
||||
| Python | **3.13**(Docker 镜像用的就是 3.13-slim;3.11+ 一般也可) |
|
||||
| 操作系统 | Linux / macOS / Windows 均可,但**注意**下面「Windows 特有坑」一节 |
|
||||
| 依赖 | `pip install -r requirements.txt` |
|
||||
| 可选 | Playwright + Chromium(只有跑界面截图才需要) |
|
||||
|
||||
起步:
|
||||
|
||||
```bash
|
||||
git clone <你的仓库地址> workbuddy-portal
|
||||
cd workbuddy-portal
|
||||
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
python manage.py init # 建表 + 建管理员(默认 admin/admin123)
|
||||
python manage.py serve --port 8848 # 或 docker compose up -d --build
|
||||
```
|
||||
|
||||
## 二、用示例数据开发,别用真实数据
|
||||
|
||||
`tools/demo_data.py` 会生成一份**完全合成**的数据集(假模型名、假 Prompt、偏斜的积分分布),
|
||||
放在 `data/demo/` 下(该目录已在 `.gitignore` 内):
|
||||
|
||||
```bash
|
||||
python tools/demo_data.py # 默认 data/demo,管理员 admin/admin123
|
||||
WB_DATA_DIR=$PWD/data/demo python manage.py serve --port 8849 --no-scheduler
|
||||
```
|
||||
|
||||
这样你既能有一个「看起来像真的」的界面来调试,也不会把任何真实用量带进仓库或截图。
|
||||
|
||||
## 三、改动前请先跑一遍验证
|
||||
|
||||
项目有一层层递进的验证,**代价从低到高**,改完至少跑到第 2 层:
|
||||
|
||||
| # | 命令 | 覆盖什么 | 需要什么 |
|
||||
|---|---|---|---|
|
||||
| 1 | `python -m compileall -q workbuddy_portal manage.py tools` | 语法 | — |
|
||||
| 2 | `python tools/smoke.py` | **99 项**离线断言:全页面只读渲染、模板残留检测、历史缺陷防回归、静态资源、CSS 类名对账 | 无(用 Flask test_client,不启服务) |
|
||||
| 3 | `python tools/check_live.py --base http://127.0.0.1:8848` | **56 项**真实 HTTP 断言,含登录/CSRF/开放重定向 | 一个运行中的服务 |
|
||||
| 4 | `python tools/shots.py --base http://127.0.0.1:8849` | 登录后逐页截图并收集 `console`/`pageerror` | Playwright + Chromium |
|
||||
| 5 | `docker compose up -d --build && docker compose ps` | 容器化路径 | Docker |
|
||||
|
||||
> `tools/shots.py` 是**最有价值的一层**:项目曾经出过「大屏整页全白」的 bug,
|
||||
> 只有它抓到了(`smoke` 与 `check_live` 都放过了)。改前端务必跑。
|
||||
|
||||
这三支脚本都会打印 `RESULT: ok=N fail=0`,`fail` 不为 0 时退出码是 1,可直接接进 CI。
|
||||
|
||||
## 四、必须遵守的几条不变量
|
||||
|
||||
这些是踩过坑之后定下来的,破坏它们会在生产上以很隐蔽的方式出问题:
|
||||
|
||||
1. **SQLite 只允许一个写者。** 任何采集动作都要走 `collect._Lock()`(`data/collect.lock`)。
|
||||
不要起多个带调度的进程;多实例部署时其余实例设 `WB_DISABLE_SCHEDULER=1`。
|
||||
2. **列表类接口默认不返回 `prompt` 全文。** 它占原始体积约 80%。只有 `/api/top` 与
|
||||
`/api/records` 带,且都做截断。新增接口请沿用这个约定。
|
||||
3. **两种字段命名契约不要互相「统一」**:`/api/bundle` 用短键(`d/c/k/m/cl/t/px`,大屏页依赖),
|
||||
`/api/records` 用可读全名。改错会让大屏静默渲染成空白。
|
||||
4. **流式响应里不要复用 `db.get_db()`。** Flask 在响应迭代开始前就会关掉请求上下文里的连接,
|
||||
生成器一读库就 `Cannot operate on a closed database`。要在生成器内部自建连接并 `finally` 关闭。
|
||||
5. **Docker 部署用命名卷,不要退回绑定挂载。** Windows + Docker Desktop 走 9p,
|
||||
宿主进程碰过 WAL 库之后容器会永久打不开数据库(详见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md))。
|
||||
6. **`.gitignore` 不支持行尾注释**——规则后跟 `# 注释` 会让整行失效。注释必须单独占一行,
|
||||
改完用 `git check-ignore -v <file>` 逐条确认命中。
|
||||
|
||||
## 五、代码风格
|
||||
|
||||
- 遵循 PEP 8;行宽 100。
|
||||
- **注释与文档字符串用中文**,说明「为什么这么做」而不是「这行在做什么」。
|
||||
- 提交前用 `python -m compileall` 与 `python tools/smoke.py` 自查。
|
||||
- 不要引入新的第三方依赖,除非有充分理由并在 PR 里说明——这个项目的卖点之一就是依赖少。
|
||||
如果确实新增了,请同步登记到 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)。
|
||||
|
||||
## 六、提交与 PR
|
||||
|
||||
提交信息用 **Conventional Commits**,一句话说清「改了什么」即可,正文可写动机:
|
||||
|
||||
```
|
||||
fix(docker): 数据改用 Docker 命名卷,修容器打不开数据库的问题
|
||||
feat(api): 新增 /api/export 支持按筛选条件导出 CSV
|
||||
docs: 补充反向代理部署示例
|
||||
```
|
||||
|
||||
PR 请包含:
|
||||
|
||||
- **动机**:解决什么问题,或复现步骤
|
||||
- **改动**:涉及哪些文件、有没有破坏性变更
|
||||
- **验证**:贴出 `smoke` / `check_live` 的 `RESULT` 行;改前端再贴截图脚本的输出
|
||||
- 若改动影响数据口径(聚合、去重、时区),请**额外写一份独立聚合与之比对**,
|
||||
不要只靠肉眼看页面
|
||||
|
||||
## 七、Windows 特有坑(若你在 Windows 上开发)
|
||||
|
||||
- 宿主 Windows 进程访问过 `data/usage.sqlite` 后,**容器内**会打不开同一个库。
|
||||
用命名卷部署时,宿主侧跑 CLI 请一律走 `docker compose exec portal python manage.py …`。
|
||||
- Git 的 `/tmp` 会被解析成 `C:\tmp`,`git commit -F /tmp/msg.txt` 会失败——用仓库内路径。
|
||||
- 本机若开着 HTTP 代理,`curl http://127.0.0.1:…` 会被代理拦成 502,探测本地服务要加 `--noproxy '*'`。
|
||||
- 行尾:仓库用 `.gitattributes` 锁死 `eol=lf`,`docker/*.sh` 若带 CRLF 会在容器里报
|
||||
`exec format error`。
|
||||
|
||||
---
|
||||
|
||||
有任何不确定的地方,先在 Issue 里问,比写完再返工更省事。
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -14,9 +14,10 @@
|
||||
| 部署 | Docker Compose / 裸机 `waitress`;镜像可推 Gitea 容器注册表 |
|
||||
| 鉴权 | 全站登录 + CSRF + 角色(管理员 / 普通用户),凭证存库、页面只回掩码 |
|
||||
| 版本 | v1.1.0 |
|
||||
| **许可证** | **MIT**(第三方组件与再分发资源见 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)) |
|
||||
|
||||
**目录**:[核心特性](#核心特性) · [架构](#架构一图) · [快速开始](#快速开始) · [命令一览](#命令一览) ·
|
||||
[页面一览](#页面一览) · [接口一览](#接口一览) · [文档导航](#文档导航) · [安全须知](#安全须知)
|
||||
[页面一览](#页面一览) · [接口一览](#接口一览) · [文档导航](#文档导航) · [安全须知](#安全须知) · [开源与许可](#开源与许可)
|
||||
|
||||
---
|
||||
|
||||
@@ -245,6 +246,11 @@ workbuddy-portal/
|
||||
| [docs/API.md](docs/API.md) | 开发 / 集成 | 接口参考:路径、参数、返回结构、错误码 |
|
||||
| [docs/FAQ.md](docs/FAQ.md) | 所有人 | 常见问题:采集为空、Cookie 失效、时区、性能、权限 |
|
||||
| [docs/CHANGELOG.md](docs/CHANGELOG.md) | 所有人 | 变更日志 |
|
||||
| [CONTRIBUTING.md](CONTRIBUTING.md) | 贡献者 | 贡献指南:开发环境、验证分层、必须遵守的不变量、提交规范 |
|
||||
| [SECURITY.md](SECURITY.md) | 运维 / 安全 | 安全策略:漏洞私有报告渠道、已有措施、已知非目标 |
|
||||
| [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md) | 合规 | 第三方组件清单与许可证(含随仓库再分发的 ECharts) |
|
||||
| [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) | 所有人 | 行为准则 |
|
||||
| [LICENSE](LICENSE) | 所有人 | MIT 许可证全文 |
|
||||
|
||||
---
|
||||
|
||||
@@ -259,3 +265,42 @@ workbuddy-portal/
|
||||
- **开放重定向防护**:登录跳转的 `next` 只接受站内相对路径,`//evil.com` 这类协议相对 URL 一律回落到 `/`。
|
||||
- **登录限速**:同 IP 连续失败 5 次锁定 10 分钟;失败计数表有上限与 TTL。
|
||||
- **不进版本库的文件**:`data/instance.json`(含 `secret_key`)、`data/usage.sqlite`、`logs/`、`.env`(含明文密码)。
|
||||
|
||||
---
|
||||
|
||||
## 开源与许可
|
||||
|
||||
本项目以 **MIT 许可证**发布,全文见 [LICENSE](LICENSE)。你可以自由使用、修改、商用与再分发,
|
||||
只需保留版权声明与许可声明。
|
||||
|
||||
### 第三方组件(务必看一眼)
|
||||
|
||||
用量大屏**随仓库再分发了 Apache ECharts 5.6.0**(`workbuddy_portal/web/static/dashboard/vendor/echarts.min.js`,
|
||||
Apache-2.0 许可)——之所以内置而不走 CDN,是为了让大屏在局域网内离线可用。
|
||||
按 Apache-2.0 第 4 条,再分发时需保留其许可证与版权声明(该文件头部已自带)。
|
||||
其余运行期依赖(Flask / waitress / openpyxl)不在本仓库内,由使用方安装时获取。
|
||||
|
||||
完整的依赖清单、许可证对照表与**合规自查清单**见 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)。
|
||||
|
||||
### 参与贡献
|
||||
|
||||
- 想改代码?先读 [CONTRIBUTING.md](CONTRIBUTING.md) —— 里面有**必须遵守的几条不变量**
|
||||
(SQLite 单写者、列表接口不回 `prompt` 全文、两种字段命名契约不要互相「统一」……),
|
||||
以及从 `compileall` 到容器验证的五层自检该怎么跑。
|
||||
- 有想法但手上没有真实数据?`python tools/demo_data.py` 会生成一份**完全合成**的示例库,
|
||||
写到 `data/demo/`(已在 `.gitignore` 内),可直接拿来调试界面与截图。
|
||||
- 发现安全漏洞?**请不要开公开 Issue**,按 [SECURITY.md](SECURITY.md) 走私有渠道。
|
||||
- 参与本项目即表示你同意遵守 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。
|
||||
|
||||
### 文档里的数据都是合成的
|
||||
|
||||
`docs/images/` 的全部界面截图与 `docs/` 中的 JSON 示例**均为合成数据**:
|
||||
模型名统一为 `demo-*`,客户端为 `vscode` / `webconsole` / `sdk`,Prompt 为通用示例文本,
|
||||
审计 IP 取自 RFC 5737 的文档专用网段(`192.0.2.0/24`)。
|
||||
生成方式是 `tools/demo_data.py`,所以任何人不需要真实账号就能复现整套文档。
|
||||
|
||||
### 致谢
|
||||
|
||||
- 交互大屏依赖 [Apache ECharts](https://echarts.apache.org/)
|
||||
- Web 框架 [Flask](https://flask.palletsprojects.com/),生产服务器 [waitress](https://github.com/Pylons/waitress)
|
||||
- 行为准则框架来自 [Contributor Covenant](https://www.contributor-covenant.org/)
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# 安全策略(Security Policy)
|
||||
|
||||
## 支持范围
|
||||
|
||||
本项目按「自托管、局域网内使用」的定位开发。安全修复只针对当前主分支与最新发布版本。
|
||||
|
||||
| 版本 | 是否接受安全修复 |
|
||||
|---|---|
|
||||
| `1.1.x`(当前) | ✅ |
|
||||
| `< 1.1` | ❌ 请先升级 |
|
||||
|
||||
## 如何报告漏洞
|
||||
|
||||
**请不要在公开 Issue 里贴出可直接利用的细节**(含真实 Cookie、`secret_key`、可复现的绕过步骤)。
|
||||
|
||||
请通过以下任一私有渠道联系维护者:
|
||||
|
||||
<!-- TODO(维护者):首次公开发布前,把下面这行替换为真实可达的安全联系邮箱 -->
|
||||
- 邮件:`<安全联系邮箱>`(占位,待维护者补全)
|
||||
- 或通过代码托管平台(Gitea)的站内私信联系仓库管理员
|
||||
|
||||
请在报告里尽量包含:
|
||||
|
||||
1. 受影响的版本 / 提交号
|
||||
2. 复现步骤与最小复现(可脱敏)
|
||||
3. 影响范围(能读到什么、能改到什么)
|
||||
4. 如果有,你建议的修复方向
|
||||
|
||||
我们会在 **7 天内**确认收到,并在修复发布后于 CHANGELOG 里致谢(除非你希望匿名)。
|
||||
|
||||
## 设计上已有的安全措施
|
||||
|
||||
理解这些边界,有助于你判断某个现象是「设计如此」还是「真的漏洞」:
|
||||
|
||||
| 项 | 做法 | 位置 |
|
||||
|---|---|---|
|
||||
| 全站鉴权 | 每个页面都有 `@login_required`,每个 `/api/*` 未登录返回 401 JSON | `workbuddy_portal/security.py`、`web/views.py` |
|
||||
| CSRF | 所有写请求必须带 `X-CSRF-Token`,页面注入 `window.WB_CSRF`,服务端统一拦截 | `security.check_csrf` |
|
||||
| 会话签名 | Flask `secret_key` 由 `data/instance.json` 持有,首次启动随机生成 | `workbuddy_portal/config.py` |
|
||||
| 口令存储 | 加盐哈希,不存明文 | `security.hash_password` |
|
||||
| 凭据脱敏 | 云端 **Cookie 是账号凭证**:入库后页面与接口**都不回传全文**,只给「N 字符,结尾 …xxxx」 | `config.SECRET_KEYS`、`web/views.py` |
|
||||
| 开放重定向 | 登录后的 `next` 只允许站内相对路径 | `web/views.py` |
|
||||
| TLS 校验 | 默认开启,**不提供「关掉校验」的快捷开关**(Cookie 不该裸奔) | `settings.ssl_verify` |
|
||||
| 容器权限 | 运行层非 root(uid/gid 1000 `app`) | `Dockerfile` |
|
||||
|
||||
**绝不入库**:`data/instance.json`(含 `secret_key`)、`data/usage.sqlite`、`logs/*`、`.env`。
|
||||
`.gitignore` 已覆盖;改动忽略规则后请用 `git check-ignore -v <file>` 逐条复核。
|
||||
|
||||
## 已知的**非**目标(部署方需自行处理)
|
||||
|
||||
本项目刻意不做下面这些,请按你的环境补齐:
|
||||
|
||||
- **没有多租户与细粒度权限**:登录用户即为管理员,能改配置、能看全部数据。
|
||||
- **没有限流与账号锁定**:公网暴露前请置于反向代理的 rate limit 之后。
|
||||
- **没有强制 HTTPS**:请由反向代理(nginx/Caddy)终止 TLS。
|
||||
- **没有备份机制**:备份策略需要你自己定(见 `docs/DEPLOYMENT.md`)。
|
||||
- **不建议直接暴露到公网**:设计前提是局域网或 VPN 内使用。
|
||||
- **Cookie 的获取方式由使用者负责**:手动从浏览器复制。请勿把该 Cookie 分享给他人,
|
||||
它的权限等同于你的账号;轮换后记得在「配置管理」页更新。
|
||||
|
||||
## 部署前的最小检查清单
|
||||
|
||||
- [ ] 已修改默认管理员口令(`WB_ADMIN_PASSWORD`),不再是 `admin123`
|
||||
- [ ] `data/` 与 `logs/` 目录的权限只对服务账号可读写
|
||||
- [ ] 前面有反向代理并启用了 HTTPS
|
||||
- [ ] 确认 `data/instance.json` 没有被提交到任何仓库
|
||||
- [ ] 已规划备份(SQLite 库是唯一正本)
|
||||
@@ -0,0 +1,78 @@
|
||||
# 第三方组件与许可声明(Third-Party Notices)
|
||||
|
||||
本项目(WorkBuddy Portal)自身以 [MIT 许可证](LICENSE) 发布。
|
||||
但它**依赖**、并在个别位置**再分发**了若干第三方组件。这些组件的著作权归各自作者所有,
|
||||
其许可条款独立于本项目的 MIT 条款。本文件汇总这些依赖,供合规审查与二次分发时参考。
|
||||
|
||||
---
|
||||
|
||||
## 一、运行期依赖(`requirements.txt`)
|
||||
|
||||
这些包不在本仓库内,由使用方安装时获取。
|
||||
|
||||
| 组件 | 版本要求 | 许可证 | 用途 |
|
||||
|---|---|---|---|
|
||||
| [Flask](https://flask.palletsprojects.com/) | `>=3.0` | BSD-3-Clause | Web 框架(路由、Jinja 模板、会话) |
|
||||
| [waitress](https://github.com/Pylons/waitress) | `>=3.0` | ZPL-2.1 | 生产级纯 Python WSGI 服务器 |
|
||||
| [openpyxl](https://openpyxl.readthedocs.io/) | `>=3.1` | MIT | 仅 `manage.py import-xlsx` 读 Excel |
|
||||
|
||||
Python 标准库(`sqlite3`、`urllib`、`http`、`threading` 等)按 PSF-2.0 许可,随 Python 分发。
|
||||
|
||||
> 项目**刻意不依赖** `APScheduler`(调度自实现)与 `requests`(用标准库 `urllib`),
|
||||
> 因此这两者的许可证与本项目无关。
|
||||
|
||||
## 二、随仓库再分发的第三方资源
|
||||
|
||||
这是需要特别注意的一类:文件**物理存在于本仓库中**。
|
||||
|
||||
### Apache ECharts 5.6.0 — Apache License 2.0
|
||||
|
||||
- **位置**:`workbuddy_portal/web/static/dashboard/vendor/echarts.min.js`
|
||||
- **著作权**:Copyright © 2017-2025 Apache Software Foundation 及 ECharts 贡献者
|
||||
- **许可证**:Apache License, Version 2.0(全文见 <https://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- **为何内置**:用量大屏要在局域网内离线可用,不能依赖公网 CDN
|
||||
- **未修改**:文件按官方发行版原样保留,其头部已包含 Apache 许可证声明与版权信息
|
||||
|
||||
按 Apache-2.0 第 4 条要求,再分发时需保留许可证与版权声明——该 `.min.js` 文件头部已自带,
|
||||
本声明构成附加的显著声明。
|
||||
|
||||
> **替换说明**:如需升级,从 <https://echarts.apache.org/> 下载对应版本覆盖同名文件即可,
|
||||
> 大屏页通过 `/static/dashboard/vendor/echarts.min.js` 引用,无需改代码。
|
||||
|
||||
### 项目自有资源
|
||||
|
||||
以下文件由本项目创作,同样按 MIT 发布,**不属第三方**:
|
||||
|
||||
- `workbuddy_portal/web/static/favicon.svg`
|
||||
- `workbuddy_portal/web/static/css/app.css`
|
||||
- `workbuddy_portal/web/static/js/app.js`
|
||||
- `workbuddy_portal/web/static/dashboard/index.html`
|
||||
- `docs/images/*.png`(界面截图,**使用合成示例数据**渲染,见下节)
|
||||
|
||||
## 三、开发期工具(非运行依赖)
|
||||
|
||||
| 组件 | 许可证 | 用途 |
|
||||
|---|---|---|
|
||||
| [Playwright for Python](https://playwright.dev/python/) | Apache-2.0 | `tools/shots.py` 登录后逐页截图 |
|
||||
| [Pillow](https://python-pillow.org/) | MIT-CMU | 人工压缩文档配图时使用,未入库 |
|
||||
|
||||
这些工具**不会**被打进 Docker 运行镜像的依赖里,也不影响部署方的义务。
|
||||
|
||||
## 四、文档与截图中的数据
|
||||
|
||||
`docs/images/` 下的界面截图与 `docs/` 中的 JSON 示例**全部使用合成数据**,
|
||||
由 `tools/demo_data.py` 生成:模型名统一为 `demo-*`,客户端为 `vscode`/`webconsole`/`sdk`,
|
||||
Prompt 为通用示例文本,审计 IP 取自 RFC 5737 的文档专用网段(`192.0.2.0/24`)。
|
||||
**不含任何真实账号、真实用量或第三方受版权保护的内容。**
|
||||
|
||||
---
|
||||
|
||||
## 五、合规自查清单
|
||||
|
||||
二次分发或商用前,建议逐项确认:
|
||||
|
||||
- [ ] `LICENSE` 与本文档随发行物一并提供
|
||||
- [ ] `vendor/echarts.min.js` 的头部许可证声明未被剥离或压缩掉
|
||||
- [ ] 若替换了 ECharts,同步更新本文档中的版本号
|
||||
- [ ] 若新增了第三方文件到仓库,在此登记其许可证
|
||||
- [ ] 若将本项目的界面截图用于宣传,确认其中不含真实业务数据
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
# =============================================================================
|
||||
# workbuddy-portal 容器入口
|
||||
# 1) 幂等初始化数据库(建表 + 默认配置 + 首个管理员)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""容器健康检查。
|
||||
|
||||
只用标准库:slim 镜像里没有 curl。`/login` 是唯一免登录页面,拿到 200 即认为
|
||||
|
||||
+16
-1
@@ -22,16 +22,31 @@
|
||||
- **容器环境变量**:`WB_HOST` `WB_PORT` `WB_DATA_DIR` `WB_LOG_DIR` `WB_DB`
|
||||
`WB_ADMIN_USER` `WB_ADMIN_PASSWORD` `WB_DISABLE_SCHEDULER` `WB_IMPORT_CREDS` `WB_IMPORT_XLSX`
|
||||
- **文档体系** `docs/`:
|
||||
[用户使用手册](USER-GUIDE.md)(含 9 张界面截图)、
|
||||
[用户使用手册](USER-GUIDE.md)(含 9 张界面截图,**全部用合成示例数据渲染**)、
|
||||
[部署与运维指南](DEPLOYMENT.md)(含推镜像到 Gitea 注册表的完整流程)、
|
||||
[架构与设计说明](ARCHITECTURE.md)、
|
||||
[接口参考](API.md)、
|
||||
[常见问题](FAQ.md)
|
||||
- **开源声明体系**(仓库根目录):[LICENSE](../LICENSE)(MIT)、
|
||||
[THIRD-PARTY-NOTICES.md](../THIRD-PARTY-NOTICES.md)(依赖清单与再分发合规自查)、
|
||||
[CONTRIBUTING.md](../CONTRIBUTING.md)(含「必须遵守的不变量」与五层自检方法)、
|
||||
[SECURITY.md](../SECURITY.md)、[CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md)、
|
||||
`.github/` 下的 Issue 表单与 PR 模板、`.editorconfig`,
|
||||
以及给全部 Python / Shell 源文件加 `SPDX-License-Identifier: MIT` 头
|
||||
- **`tools/demo_data.py`**:生成**完全合成**的示例库(假模型名 / 假 Prompt / 偏斜的积分分布),
|
||||
写入 `data/demo/`(已在 `.gitignore` 内)。文档截图与本地调试都基于它,
|
||||
任何人不需要真实账号就能复现整套界面
|
||||
- **`.gitattributes`**:强制 `*.sh` / `Dockerfile` / 各类源码为 LF
|
||||
(带 CRLF 的 `.sh` 在容器里会报 `exec format error`,极难定位)
|
||||
|
||||
### 变更
|
||||
|
||||
- **文档数据脱敏**:9 张界面截图全部改用合成示例数据重拍;
|
||||
`docs/API.md`、`docs/DEPLOYMENT.md` 示例响应里的真实模型名与真实统计数字一并替换为示例口径。
|
||||
此前截图中含**真实 Prompt 全文**、本机路径与本机用户名,属于不该公开的内容
|
||||
- **`.gitignore` 补强**:只写 `data/*.sqlite` 会漏掉子目录,改为同时保留 `data/**/*.sqlite`
|
||||
等规则,并新增 `data/demo/` 忽略——否则 `tools/demo_data.py` 的产物会被误提交
|
||||
|
||||
- **项目定名**:`wb_usage_portal` → **`workbuddy-portal`**;
|
||||
Python 包 `wb_usage` → **`workbuddy_portal`**;会话 cookie
|
||||
`wb_usage_sid` → `workbuddy_portal_sid`(升级后需要重新登录)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""WorkBuddy Portal —— 统一命令行入口。
|
||||
|
||||
采集 / 存储 / 呈现三件事都由本项目承担,不再依赖外部计划任务或自动化。
|
||||
|
||||
+4
-1
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""端到端验收:对**运行中的**服务发真实 HTTP 请求,走完整登录/CSRF/API 链路。
|
||||
|
||||
与 tests 里用 Flask test_client 的冒烟测试互补——这里验证的是「真的起起来了、
|
||||
@@ -7,7 +10,7 @@
|
||||
|
||||
用法:
|
||||
python tools/check_live.py # 默认 http://127.0.0.1:8848
|
||||
python tools/check_live.py --base http://10.0.0.5:8848
|
||||
python tools/check_live.py --base http://192.168.1.50:8848 # 换成你的部署主机
|
||||
python tools/check_live.py -u admin -p 你的密码
|
||||
python tools/check_live.py --from 2026-09-08 --to 2026-09-14
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
# =============================================================================
|
||||
# 用 Gitea Access Token 一次性完成:推代码 + 推镜像
|
||||
#
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""界面实检:登录后逐页截图,用来目视确认「统一美化」是否真的落地。
|
||||
|
||||
用法:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""离线回归:用 Flask test_client 对**真实库**做全页面只读渲染 + 缺陷防回归断言。
|
||||
|
||||
与 tools/check_live.py 的分工:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""WorkBuddy Portal —— 独立 Flask 项目。
|
||||
|
||||
一个程序管全部:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""云端用量接口客户端(纯 urllib,不依赖 requests/浏览器)。
|
||||
|
||||
接口:POST {api_base}/billing/meter/get-user-request-usage
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""采集主流程:云端增量 -> SQLite(去重、断点、漂移校验、运行记录)。
|
||||
|
||||
与原 fetch_usage.py 的差别:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""基础配置。
|
||||
|
||||
刻意保持「薄」:凡是运行期要改的东西(cookie、调度周期、采集参数)都放数据库
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""SQLite 访问层。
|
||||
|
||||
并发约定(重要):
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""SQL 聚合层:所有统计都在 SQLite 里算完再出去,页面不再搬运全量明细。
|
||||
|
||||
返回结构刻意与旧版 dashboard/data/*.json 的字段保持一致(d/c/k/fc/bc/m/h、
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""进程内调度器(手写,不依赖 APScheduler)。
|
||||
|
||||
为什么不引 APScheduler:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""密码哈希、登录装饰器、CSRF。
|
||||
|
||||
局域网可访问 ⇒ 必须有鉴权。这里用 Werkzeug 自带的 PBKDF2,不引第三方依赖。
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""Web 层包:蓝图注册。"""
|
||||
from . import api, views
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""JSON API —— ECharts 大屏与后台页面的数据入口。
|
||||
|
||||
约定:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Copyright (c) 2026 Wang Chuanli
|
||||
|
||||
"""页面路由(Jinja 模板)。
|
||||
|
||||
分工:
|
||||
|
||||
在新工单中引用
屏蔽一个用户