Files
nexus/Project/fonrey/PRD/登录管理/找回流程图.drawio

335 lines
22 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" modified="2026-04-25T00:00:00.000Z" agent="OpenCode" version="21.0.0">
<!-- ============================================================
5.4.1 找回用户名流程
============================================================ -->
<diagram id="recover-username" name="5.4.1 找回用户名流程">
<mxGraphModel dx="1200" dy="800" grid="1" gridSize="10" guides="1" tooltips="1"
connect="1" arrows="1" fold="1" page="1" pageScale="1"
pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- Title -->
<mxCell id="t1" value="5.4.1 找回用户名流程" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="175" y="20" width="500" height="40" as="geometry" />
</mxCell>
<!-- Start -->
<mxCell id="u1" value="用户点击「忘记用户名」" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="80" width="300" height="50" as="geometry" />
</mxCell>
<!-- Decision: Is email format valid? -->
<mxCell id="u2" value="展示「找回用户名」页面&#xa;(邮箱输入框 + 发送按钮)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="170" width="350" height="50" as="geometry" />
</mxCell>
<!-- User inputs email -->
<mxCell id="u3" value="用户输入邮箱并点击「发送」" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="260" width="350" height="50" as="geometry" />
</mxCell>
<!-- Decision: Email format valid? -->
<mxCell id="u4" value="邮箱格式校验通过?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="350" width="300" height="70" as="geometry" />
</mxCell>
<!-- Format invalid -->
<mxCell id="u5" value="提示「请输入有效的邮箱地址」" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="620" y="360" width="220" height="50" as="geometry" />
</mxCell>
<!-- Backend query -->
<mxCell id="u6" value="服务端查询邮箱是否绑定账号&#xa;(不向前端返回查询结果)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="470" width="350" height="60" as="geometry" />
</mxCell>
<!-- Unified response to frontend -->
<mxCell id="u7" value="统一响应前端:&#xa;「如该邮箱已绑定账号,您将收到邮件」&#xa;发送按钮进入 60 秒倒计时" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="580" width="350" height="70" as="geometry" />
</mxCell>
<!-- Decision: Email exists? -->
<mxCell id="u8" value="邮箱已绑定 Tenant Admin 账号?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="630" y="475" width="240" height="70" as="geometry" />
</mxCell>
<!-- Email found: send email -->
<mxCell id="u9" value="后台:异步发送邮件&#xa;(包含用户名、发送时间)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="640" y="580" width="220" height="60" as="geometry" />
</mxCell>
<!-- Email not found: silent -->
<mxCell id="u10" value="后台:静默处理&#xa;(不发送邮件,不报错)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="640" y="660" width="220" height="50" as="geometry" />
</mxCell>
<!-- Rate limit check -->
<mxCell id="u11" value="同一邮箱 1 小时内已发送 ≥ 3 次?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="630" y="700" width="240" height="70" as="geometry" />
</mxCell>
<!-- Rate limit exceeded -->
<mxCell id="u12" value="拒绝发送&#xa;(达到频率上限)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="900" y="715" width="160" height="50" as="geometry" />
</mxCell>
<!-- User receives email -->
<mxCell id="u13" value="用户查收邮件,获取用户名" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="640" y="810" width="220" height="50" as="geometry" />
</mxCell>
<!-- Return to login -->
<mxCell id="u14" value="点击「返回登录」&#xa;回到登录界面" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="700" width="300" height="50" as="geometry" />
</mxCell>
<!-- Edges -->
<mxCell id="e1" edge="1" source="u1" target="u2" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e2" edge="1" source="u2" target="u3" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e3" edge="1" source="u3" target="u4" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e4" value="否" edge="1" source="u4" target="u5" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="620" y="385" /></Array></mxGeometry>
</mxCell>
<mxCell id="e5" value="" edge="1" source="u5" target="u3" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="730" y="285" /></Array></mxGeometry>
</mxCell>
<mxCell id="e6" value="是" edge="1" source="u4" target="u6" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e7" edge="1" source="u6" target="u7" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e8" edge="1" source="u6" target="u8" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="600" y="500" /><mxPoint x="630" y="510" /></Array></mxGeometry>
</mxCell>
<mxCell id="e9" value="是" edge="1" source="u8" target="u11" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e10" value="否(普通员工邮箱或不存在)" edge="1" source="u8" target="u10" parent="1">
<mxGeometry relative="1" as="geometry"><mxPoint x="760" y="660" as="targetPoint" /></mxGeometry>
</mxCell>
<mxCell id="e11" value="否(未超限)" edge="1" source="u11" target="u9" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e12" value="是(超过 3 次)" edge="1" source="u11" target="u12" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="900" y="735" /></Array></mxGeometry>
</mxCell>
<mxCell id="e13" edge="1" source="u9" target="u13" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="e14" edge="1" source="u13" target="u14" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="750" y="835" /><mxPoint x="425" y="835" /><mxPoint x="425" y="750" /></Array></mxGeometry>
</mxCell>
<mxCell id="e15" edge="1" source="u7" target="u14" parent="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<!-- ============================================================
5.4.2 找回密码流程
============================================================ -->
<diagram id="recover-password" name="5.4.2 找回密码流程">
<mxGraphModel dx="1200" dy="800" grid="1" gridSize="10" guides="1" tooltips="1"
connect="1" arrows="1" fold="1" page="1" pageScale="1"
pageWidth="850" pageHeight="1300" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- Title -->
<mxCell id="title" value="5.4.2 找回密码流程" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="175" y="20" width="500" height="40" as="geometry" />
</mxCell>
<!-- ===== STEP 1 Header ===== -->
<mxCell id="step1hdr" value="步骤一:身份验证" style="text;html=1;strokeColor=none;fillColor=#dae8fc;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=1;fontSize=13;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="60" y="65" width="730" height="30" as="geometry" />
</mxCell>
<!-- Start -->
<mxCell id="p1" value="用户点击「忘记密码」" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=1;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="115" width="300" height="50" as="geometry" />
</mxCell>
<!-- Show form -->
<mxCell id="p2" value="展示「找回密码」页面Stepper&#xa;步骤一:用户名 + 绑定邮箱输入框" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="225" y="200" width="400" height="60" as="geometry" />
</mxCell>
<!-- User submits -->
<mxCell id="p3" value="用户输入用户名 + 邮箱,点击「下一步」" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="225" y="300" width="400" height="50" as="geometry" />
</mxCell>
<!-- Backend verification -->
<mxCell id="p4" value="服务端校验用户名与邮箱是否匹配" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="225" y="390" width="400" height="50" as="geometry" />
</mxCell>
<!-- Unified response -->
<mxCell id="p5" value="统一响应前端:&#xa;「如信息匹配,重置链接将发送至您的邮箱」" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="225" y="480" width="400" height="60" as="geometry" />
</mxCell>
<!-- Decision: Match? -->
<mxCell id="p6" value="用户名与邮箱匹配?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="650" y="395" width="220" height="70" as="geometry" />
</mxCell>
<!-- Rate limit check -->
<mxCell id="p7" value="同一账号 1 小时内已发 ≥ 3 次?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="650" y="490" width="220" height="70" as="geometry" />
</mxCell>
<!-- Matched: generate token -->
<mxCell id="p8" value="生成加密 Token&#xa;secrets.token_urlsafe(32),有效期 30 分钟)&#xa;异步发送重置邮件" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="650" y="590" width="240" height="70" as="geometry" />
</mxCell>
<!-- No match: silent -->
<mxCell id="p9" value="不匹配:静默处理&#xa;(不发邮件,不报错)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="900" y="405" width="180" height="60" as="geometry" />
</mxCell>
<!-- Rate limit exceeded -->
<mxCell id="p10" value="已超频率上限&#xa;静默处理" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="900" y="505" width="160" height="50" as="geometry" />
</mxCell>
<!-- ===== STEP 2 Header ===== -->
<mxCell id="step2hdr" value="步骤二:用户点击邮件重置链接" style="text;html=1;strokeColor=none;fillColor=#d5e8d4;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=1;fontSize=13;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="60" y="690" width="730" height="30" as="geometry" />
</mxCell>
<!-- User clicks link -->
<mxCell id="p11" value="用户点击邮件中的重置链接" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="740" width="300" height="50" as="geometry" />
</mxCell>
<!-- Validate token -->
<mxCell id="p12" value="服务端校验 Token 有效性&#xa;is_used=False AND expires_at 未过期)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="830" width="350" height="60" as="geometry" />
</mxCell>
<!-- Token decision -->
<mxCell id="p13" value="Token 有效?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="300" y="930" width="250" height="70" as="geometry" />
</mxCell>
<!-- Invalid token -->
<mxCell id="p14" value="提示「链接已过期或已使用,请重新申请」&#xa;提供「重新申请」按钮(跳回步骤一)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="620" y="940" width="260" height="60" as="geometry" />
</mxCell>
<!-- ===== STEP 3 Header ===== -->
<mxCell id="step3hdr" value="步骤三:输入并提交新密码" style="text;html=1;strokeColor=none;fillColor=#fff2cc;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=1;fontSize=13;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="60" y="1030" width="730" height="30" as="geometry" />
</mxCell>
<!-- Show reset form -->
<mxCell id="p15" value="展示「重置密码」表单&#xa;(新密码 + 确认新密码 + 密码强度指示)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="250" y="1080" width="350" height="60" as="geometry" />
</mxCell>
<!-- User submits new password -->
<mxCell id="p16" value="用户输入新密码并提交" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="1180" width="300" height="50" as="geometry" />
</mxCell>
<!-- Complexity check -->
<mxCell id="p17" value="密码复杂度校验&#xa;≥8位含字母+数字,两次一致)" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="1265" width="300" height="80" as="geometry" />
</mxCell>
<!-- Complexity fail -->
<mxCell id="p18" value="实时提示不满足的规则&#xa;(逐条红色 ✗ / 绿色 ✓ 视觉指引)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="640" y="1275" width="240" height="60" as="geometry" />
</mxCell>
<!-- History check -->
<mxCell id="p19" value="历史密码校验&#xa;(不得与最近 3 次历史密码相同)" style="rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="1380" width="300" height="80" as="geometry" />
</mxCell>
<!-- History fail -->
<mxCell id="p20" value="提示「不得与最近 3 次密码相同」" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="640" y="1395" width="240" height="50" as="geometry" />
</mxCell>
<!-- Success: update password -->
<mxCell id="p21" value="✅ 校验通过:&#xa;① 更新密码PBKDF2+SHA256 哈希存储)&#xa;② is_initial_password = False&#xa;③ 清除该账号所有有效 Session&#xa;④ 标记 Token 为 is_used = True" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="225" y="1500" width="400" height="100" as="geometry" />
</mxCell>
<!-- End -->
<mxCell id="p22" value="跳转登录界面&#xa;提示「密码已重置,请使用新密码登录」" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="275" y="1630" width="300" height="60" as="geometry" />
</mxCell>
<!-- Edges Step 1 -->
<mxCell id="ep1" edge="1" source="p1" target="p2" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep2" edge="1" source="p2" target="p3" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep3" edge="1" source="p3" target="p4" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep4" edge="1" source="p4" target="p5" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep5" edge="1" source="p4" target="p6" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="625" y="415" /><mxPoint x="650" y="430" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep6" value="否" edge="1" source="p6" target="p9" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="900" y="430" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep7" value="是" edge="1" source="p6" target="p7" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep8" value="是(超限)" edge="1" source="p7" target="p10" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="900" y="525" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep9" value="否(未超限)" edge="1" source="p7" target="p8" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<!-- Step 2 edges -->
<mxCell id="ep10" edge="1" source="p5" target="p11" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep11" edge="1" source="p11" target="p12" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep12" edge="1" source="p12" target="p13" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep13" value="否(无效/过期)" edge="1" source="p13" target="p14" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="620" y="965" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep14" value="重新申请" edge="1" source="p14" target="p2" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="960" y="970" /><mxPoint x="960" y="230" /><mxPoint x="625" y="230" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep15" value="是(有效)" edge="1" source="p13" target="p15" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<!-- Step 3 edges -->
<mxCell id="ep16" edge="1" source="p15" target="p16" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep17" edge="1" source="p16" target="p17" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep18" value="不通过" edge="1" source="p17" target="p18" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="640" y="1305" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep19" value="" edge="1" source="p18" target="p16" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="760" y="1205" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep20" value="通过" edge="1" source="p17" target="p19" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep21" value="不通过" edge="1" source="p19" target="p20" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="640" y="1420" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep22" value="" edge="1" source="p20" target="p16" parent="1">
<mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="760" y="1205" /></Array></mxGeometry>
</mxCell>
<mxCell id="ep23" value="通过" edge="1" source="p19" target="p21" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
<mxCell id="ep24" edge="1" source="p21" target="p22" parent="1"><mxGeometry relative="1" as="geometry" /></mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>