# btm-project-manage **Repository Path**: BigTailMonkey/btm-project-manage ## Basic Information - **Project Name**: btm-project-manage - **Description**: No description available - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-13 - **Last Updated**: 2026-09-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # BTM 任务进度管理工具 一个 Java 17 + Spring Boot + MyBatis + Vue 3 的任务进度管理工具。开发时前后端分别启动,生产部署打包为单个 Docker 镜像。 ## 本地调试 本地开发需要 JDK 17。 后端端口固定为 `11000`: ```bash cd backend mvn spring-boot:run ``` 前端端口固定为 `5600`,`/api` 会代理到后端: ```bash cd frontend npm install npm run dev ``` 演示账号: - 管理员:`admin / admin123` - 项目负责人:`owner / owner123` - 成员:`member / member123` ## Docker 部署(MySQL) ```bash scripts/docker-build-amd64.sh ``` 该脚本会构建 `linux/amd64` 镜像,适合部署到 x86 架构 CPU 的服务器。需要自定义镜像名时: ```bash IMAGE_NAME=registry.example.com/btm-project-manager:latest scripts/docker-build-amd64.sh ``` ### 构建需要的 npm 私有源凭据 前端部分依赖(含直接依赖与传递依赖)发布在团队内部的私有 npm 源上,构建镜像时需要鉴权才能下载。Dockerfile 通过 BuildKit secret 把本机的 `~/.npmrc` 临时挂载进构建容器来完成鉴权: - `scripts/docker-build-amd64.sh` 会自动传入 `--secret id=npmrc,src="$HOME/.npmrc"`; - `docker compose build` 通过 `docker-compose.yml` 顶层 `secrets.npmrc` 读取 `${HOME}/.npmrc`。 因此在一台新机器上首次打包前,需要在该机器的 `~/.npmrc` 中配置私有源地址和访问凭据,格式遵循 npm 官方配置(registry 地址 + 对应域名下的 `_authToken`),具体地址和 token 向团队内部索取。 说明: - 该 secret 只在 `npm ci` 这一条构建命令执行期间临时挂载,**不会写入任何镜像层,也不会进入最终镜像**;凭据同样不应提交到 git。 - 不要在项目目录内(如 `frontend/`)放置带凭据的 `.npmrc`,以免被误提交。 - 若本机没有可用的 `~/.npmrc`,构建会因 `E401 Incorrect or missing password` 失败,配置好后重试即可。 - 若要改用公共源,需要重新生成 `frontend/package-lock.json` 使所有 `resolved` 指向公共源,否则 lock 文件中锁定的私有源地址仍会触发鉴权。 先在 MySQL 中执行整合脚本: ```bash mysql -u root -p < backend/src/main/resources/db/sql/mysql/000_full_init.sql ``` 然后启动 Docker 服务: ```bash docker run -p 11000:11000 \ -e SPRING_PROFILES_ACTIVE=prod,mysql \ -e DB_URL="jdbc:mysql://host.docker.internal:3306/btm_project_manager?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai" \ -e DB_USERNAME=btm \ -e DB_PASSWORD=btm \ -e APP_UPLOAD_DIR=/app/uploads \ -e AI_SKILL_STORAGE_DIR=/var/lib/btm-project-manager/skills \ -e APP_PUBLIC_BASE_URL="https://btm.example.com" \ -e DINGTALK_ROBOT_ENABLED=false \ -e DINGTALK_ROBOT_WEBHOOK="" \ -e DINGTALK_ROBOT_SECRET="" \ -e DINGTALK_ROBOT_DUTY_REMINDER_ENABLED=false \ -e DINGTALK_ROBOT_DUTY_REMINDER_CRON="0 0 8 * * *" \ -v /data/btm-project-manager/uploads:/app/uploads \ -v /data/btm-project-manager/skills:/var/lib/btm-project-manager/skills \ btm-project-manager ``` 任务说明富文本中的图片会先上传到 `/api/uploads/images`,页面中保存 `/uploads/...` 访问地址。Docker 部署时必须把 `APP_UPLOAD_DIR` 挂载到宿主机目录,例如上面的 `/data/btm-project-manager/uploads`;容器重启或重新部署时继续挂载同一个宿主机目录,历史图片才会一直可访问。 上传的 Skill 文件夹不会写入应用代码目录,也不会打进 Docker 镜像。服务将完整文件夹(包括 `SKILL.md` 和引用资源)写入 `AI_SKILL_STORAGE_DIR`。Docker 部署必须把该目录挂载为独立持久化卷,例如上面的 `/data/btm-project-manager/skills`。更新镜像、重新创建容器时继续挂载同一目录,系统只会在管理员上传、编辑或删除 Skill 时修改其中内容,镜像启动不会覆盖已有 Skill。 也可以使用 Compose: ```bash cp .env.example .env # 按实际 MySQL 地址、账号、密码修改 .env。 # SKILL_STORAGE_VOLUME 默认使用 btm-skill-storage 命名卷,也可改为宿主机绝对路径。 docker compose up -d --build ``` 或直接在命令行传入: ```bash DB_URL="jdbc:mysql://host.docker.internal:3306/btm_project_manager?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai" \ DB_USERNAME=btm \ DB_PASSWORD=btm \ docker compose up -d --build ``` AI 助手使用 OpenAI 兼容接口,可选配置: ```bash MAIN_MODEL_BASE_URL="https://api.deepseek.com" MAIN_MODEL_API_KEY="your-key" # 自建模型路由服务若要求非标准认证,可改为填写完整请求头值,例如 "Bearer your-token"。 # MAIN_MODEL_AUTHORIZATION="Bearer your-token" MAIN_MODEL_NAME="deepseek-v4-pro" # 部分思考模型仅允许 1,使用这类模型时改为 1.0。 MAIN_MODEL_TEMPERATURE=0.3 # 仅本地调试:原样打印包含 Authorization 的完整模型 HTTP 请求。 MAIN_MODEL_LOG_FULL_REQUESTS=false MAIN_MODEL_READ_TIMEOUT_SECONDS=600 ``` 默认会使用 `MAIN_MODEL_API_KEY` 发送 `Authorization: Bearer `。路由服务要求其他 Authorization 值时,配置 `MAIN_MODEL_AUTHORIZATION`;它会覆盖默认 Authorization 请求头,且此时可不配置 `MAIN_MODEL_API_KEY`。 钉钉群机器人通知为可选配置,默认关闭。启用后,系统会在站内信通知具体人员时同步发送钉钉消息,并按用户手机号 `@` 对应人员;自己通知自己的场景不会发送站内信或钉钉通知。 ```bash DINGTALK_ROBOT_ENABLED=true DINGTALK_ROBOT_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=your-token" DINGTALK_ROBOT_SECRET="SECxxxxxxxxxxxxxxxx" DINGTALK_ROBOT_TITLE_PREFIX="BTM项目管理" DINGTALK_ROBOT_DUTY_REMINDER_ENABLED=true DINGTALK_ROBOT_DUTY_REMINDER_CRON="0 0 8 * * *" DINGTALK_ROBOT_CONNECT_TIMEOUT_SECONDS=5 DINGTALK_ROBOT_READ_TIMEOUT_SECONDS=10 APP_PUBLIC_BASE_URL="https://btm.example.com" ``` 配置说明: - `DINGTALK_ROBOT_ENABLED`:是否启用钉钉通知,默认 `false`。 - `DINGTALK_ROBOT_WEBHOOK`:钉钉群自定义机器人 Webhook,启用时必填。 - `DINGTALK_ROBOT_SECRET`:机器人安全设置中的加签密钥;如果机器人未开启加签可留空。 - `DINGTALK_ROBOT_TITLE_PREFIX`:钉钉消息标题前缀,默认 `BTM项目管理`。 - `DINGTALK_ROBOT_DUTY_REMINDER_ENABLED`:是否启用每日值班提醒,默认 `false`;同时需要启用钉钉机器人。 - `DINGTALK_ROBOT_DUTY_REMINDER_CRON`:值班提醒的 Spring cron 表达式,使用上海时区,默认每天 08:00。 - `DINGTALK_ROBOT_CONNECT_TIMEOUT_SECONDS` / `DINGTALK_ROBOT_READ_TIMEOUT_SECONDS`:钉钉请求超时时间。 - `APP_PUBLIC_BASE_URL`:系统外部访问地址,用于在钉钉消息中生成完整跳转链接。 - 用户管理中需要维护手机号;未配置手机号的用户仍会收到站内信,但不会发送钉钉 `@` 通知。 ## 权限与安全 - 权限覆盖菜单、按钮/操作和后端接口。 - 前端按权限隐藏菜单和按钮,后端接口使用权限注解强制校验。 - 未登录打开 `/tasks/:id` 会跳转登录,登录成功后回到任务详情;无权限会进入无权限页。 - 用户 5 小时无操作会自动退出。 ### 系统间创建任务接口 在「权限管理 → 接口 Key」中新建“应用接口 Key”,为 Key 勾选 `INTEGRATION_TASK_CREATE`(创建任务)接口权限。完整 Key 只会在创建时展示一次,调用时通过 `X-API-Key` 请求头传递。 ```bash curl -X POST "https://btm.example.com/api/integration/tasks" \ -H "Content-Type: application/json" \ -H "X-API-Key: btm_app_xxx" \ -H "Idempotency-Key: task-业务系统唯一请求号" \ -d '{ "title": "核查门诊统计口径", "projectName": "BTM 项目管理工具", "assigneeId": 3, "taskType": "数据问题排查", "priority": "HIGH", "plannedStartDate": "2026-08-03", "plannedEndDate": "2026-08-04", "estimatedHours": 2 }' ``` `title`、`projectName` 和 `taskType` 必填。`projectName` 使用系统中进行中项目的中文名称,接口会自动转换为内部项目 ID;`taskType` 使用系统现有的中文类型名称,仅支持“数据问题排查、工单、需求、数据导出”。可选字段包括 `description`、`milestoneId`、`assigneeId`、`sqlRecordId`、`externalTaskUrl`、`priority`、计划日期和预估工时。当前创建的是 `APPLICATION` 类型 Key,数据结构已预留 `MCP` 类型,供后续 MCP Key 使用。创建和图片上传均必须携带长度不超过 128 字符的 `Idempotency-Key`;同一应用 Key 下重复提交相同键与相同请求会返回原结果,若请求内容不同则返回 409,客户端重试时必须复用原键。 调用创建接口前,可使用相同的应用接口 Key 查询当前环境可用的完整列表: ```bash curl -X GET "https://btm.example.com/api/integration/tasks/options" \ -H "X-API-Key: btm_app_xxx" ``` 返回的 `projectNames` 是数据库中所有进行中项目的中文名称;`taskTypes` 是当前接口允许的中文任务类型(数据问题排查、工单、需求、数据导出)。管理员也可在「项目管理」页面查看和维护最新项目名称。 ### 上传任务说明图片 为应用接口 Key 勾选 `INTEGRATION_IMAGE_UPLOAD`(上传任务图片)权限后,可上传图片并将返回的地址写入创建任务请求的 `description`。上传权限和创建任务权限可分别配置。 ```bash curl -X POST "https://btm.example.com/api/integration/uploads/images" \ -H "X-API-Key: btm_app_xxx" \ -H "Idempotency-Key: image-业务系统唯一请求号" \ -F "file=@/absolute/path/screenshot.png" ``` 响应包含临时图片 `data.id` 和系统内图片地址 `data.url`,例如 `/uploads/images/2026/08/xxx.png`。临时图片 24 小时内有效;创建任务时须同时使用 `description` 引用该地址,并传入 `"imageUploadIds":["临时图片ID"]`,任务成功后图片才会正式绑定。未绑定资产会在到期后由带租约的清理任务删除;删除失败会以退避间隔持续重试,已绑定任务的图片不会被清理。单张图片最大 10MB,建议使用 PNG、JPG、GIF 或 WEBP。 ## SQL 维护规范 MySQL 生产初始化脚本位于: ```text backend/src/main/resources/db/sql/mysql/000_full_init.sql ``` H2 脚本仅用于本地开发默认 profile。SQL 文件必须包含: - 创建时间 - 用途 - 影响对象 - 是否可重复执行 - 部署说明 业务代码只通过 Service 调用 MyBatis Mapper,不直接绑定 H2 或 MySQL。