V3.2 送达者=用户 + 用户邮箱必填

- users 表新增 email 列:所有用户(含 admin)必须有邮箱,新建/编辑用户强制校验
- 项目送达者从'邮箱字符串'改为'绑定用户(deliver_user_id)':创建项目必须选择平台用户
- 送达通知实时读取用户最新邮箱:用户改邮箱 → 其送达的所有项目自动跟随,无需改项目
- 项目进行中可随时更换送达者用户(编辑项目 → 送达者下拉)
- 迁移:存量用户自动补 username@tphai.com 默认邮箱;旧项目按 deliver_email 匹配回填 deliver_user_id
- 新增 /api/users 轻量用户列表接口(登录即可,供选送达者);deliver_email 降级为快照缓存
- 前端:项目表单送达者改为用户下拉选择;用户管理增加邮箱列;交付页显示送达者用户名+邮箱
This commit is contained in:
2026-08-13 16:21:35 +08:00
parent 1f37344d03
commit 8be01b5af7
6 changed files with 173 additions and 57 deletions
+15 -5
View File
@@ -290,11 +290,21 @@ def _email_body(project, extra=''):
return '\n'.join(lines)
def deliver_email_of(project):
"""送达者邮箱:优先取绑定的用户(deliver_user_id)最新邮箱;无绑定则回退旧快照"""
uid = project.get('deliver_user_id')
if uid:
u = db.q('SELECT email FROM users WHERE id=?', (uid,), one=True)
if u and (u.get('email') or '').strip():
return u['email'].strip()
return (project.get('deliver_email') or '').strip()
def notify_deliverer(project, subject, text, attach=None, base_url=''):
"""发邮件给送达者,写交付记录。返回 (ok, msg)"""
email = (project.get('deliver_email') or '').strip()
email = deliver_email_of(project)
if not email:
return False, '项目未配置送达者邮箱'
return False, '项目未配置送达者(请在编辑项目中指定送达者用户)'
ok, msg = send_mail(email, subject, text, attachments=[attach] if attach else None)
if ok:
db.w('UPDATE projects SET delivered_at=? WHERE id=?', (db.now(), project['id']))
@@ -303,7 +313,7 @@ def notify_deliverer(project, subject, text, attach=None, base_url=''):
def notify_blocker(project, task_title, detail):
"""项目遇到无法绕开的难关 → 及时邮件通知送达者(同项目限频防打扰)"""
email = (project.get('deliver_email') or '').strip()
email = deliver_email_of(project)
if not email:
return
# 去重:同项目 30 分钟内只提醒一次(detail 带项目标记)
@@ -329,9 +339,9 @@ def notify_blocker(project, task_title, detail):
def notify_project_complete(project, base_url=''):
"""项目完成 → 打包 + 邮件送达(含 Demo 链接与附件)。返回 (ok, msg)"""
email = (project.get('deliver_email') or '').strip()
email = deliver_email_of(project)
if not email:
return False, '项目未配置送达者邮箱'
return False, '项目未配置送达者(请在编辑项目中指定送达者用户)'
# 网页交付物:确保已部署 Demo
if project.get('deliver_type') == 'web' and not project.get('demo_url'):
try: