跳转到主要内容

FSD M6 · 组织协同

← 总则 | 附录 → | 上游:PRD §4 M6 实现落点:backend/internal/domain/org/(model 308/service 775/repo 209/repo_read 389/repo_groups 55/perms 53/code 24 单包多文件)+ cmd/server/org_handlers.go(460 行 29 handler)+ cmd/server/routes.go:103-136(挂载)。事实基准=Go 代码;冻结版 qmtv/server/index.js:513-597(组织段 9)+enterprise.js(20)仅差异对拍参照。 域内要点:四零域(零上传/零扣费/零 SSE/零通知,无 BodyLimit——routes.go:105-106);无组织分流四态谱:me·dashboard ok(null)|folders·groups·perm-groups·material-perms·shared-projects ok([])|folder create·assign·folder delete 400「您尚未加入企业」|allocation-mode 404「未加入组织」——逐端点归属各异勿统一;owner-严格谱系(invites/remove/role/groups/perm-groups/assign 六面 ownerOnly vs material-perms 唯一 adminOrOwner 面);「管理员」文案 vs owner 严格判定(PRD ⑩);projectRank/memberRank 驼峰 wire 双键(同 M2 shotCount 族勿修正);permGroups/materialPerms 响应经 map 重出(perms json:"-" 串解析为数组)。PRD §4 M6 已登记差异 ①~⑪+十九续勘正批注(prd-qmtv-v1.md:172-178)引用不重立。

M6 组织协同

29 端点全量(组织面 9+enterprise 面 20,两口径重合;#8 share 物理路由冻结在 index.js:574 非 director.js——routes.go:106-107 归组注记)。

F6.01 组织申请、档案与分账模式(applications/me/allocation-mode)

对应 PRD:§4 M6 · POST /api/organizations/applications、GET /api/organizations/me、PUT /api/organizations/me/credit-allocation-mode | 状态:实现 | 实现落点:org_handlers.go:18-68、service.go:59-106、repo.go:128-132/:178-181、repo_read.go:16-54 | UI:EnterpriseApplyView.vue(申请)/OrganizationView.vue(档案)/分账模式前端零消费 | 场景挂钩:无专项 e2e(OrgChain 第九链集成锚替位;E2E-NAV-01 间接)

1 功能定义

提交建组织申请、读取本人组织档案四联文档(组织+成员+邀请+积分流水)、切换组织积分分账模式(死端点照抄)。

2 触发条件与前置状态

申请仅登录无权限判定(重复申请不查重双侧同形);me 无组织返 null 非 404(service.go:72-74→handlers:46-48);allocation-mode 经 orgOf 判定,无成员行 → 404「未加入组织」(service.go:98——四态谱第四态独一份),无 role 门槛 member 可改。

3 输入与校验

申请 body company/contact/reason(company 空 → 400「公司名称必填」service.go:61;三者不 slice 不 trim——差异⑤ VARCHAR(128) strict 溢出报错 vs 冻结 SQLite 照收);allocation-mode body mode 可选缺省 “"。

4 处理流程

申请:oa_ id(ids.go:63)→ InsertOrgApplication 五列单写,status 走 DDL 默认 ‘pending’(repo.go:128-132)。me:OrgDetail 四查串行零写——组织+成员(repo_read.go:16-35)→invites(:38-43,无 status 过滤 pending+used 全回 quirk)→credit_records(:46-54,LIMIT 50 task_id 尾插)。分账:UPDATE organizations SET credit_allocation_mode=?,mode 非 independent 一律归 pooled(service.go:102-104——死端点:前端零消费实证 grep allocation 零命中,PRD ①)。

5 输出与结果状态

申请 {"id"}(oa_);me 无组织 data:null,有组织 MeDoc 键序 id,name,owner_id,status,credit_allocation_mode,credits,created_at,my_role,members,invites,credit_records(model.go:204-222);members 行 id,org_id,user_id,role,quota,group_id,perm_group_id,material_perms,created_at,email,nickname(group_id/perm_group_id NULLIF 还原 null、material_perms 为 JSON 字符串形态非数组、email/nickname omitempty);分账 {"mode"}。

6 状态流转

org_applications 插行(审批写侧归 M12 F12.09);organizations.credit_allocation_mode 单列覆写(差异⑦:冻结无读写消费方空转照抄)。

7 边界与异常
条件系统行为提示/码
company 空400「公司名称必填」
me 无组织200data:null(四态谱第一态)
allocation 无成员行404「未加入组织」(冻结 index.js:530 同 404 非 400)
bind 失败400err.Error() 原文(echo 库消息,14 处同制)
8 权限与数据规则

quirk:分账端点无 role 门槛(member 可改组织级配置);冻结侧差异①登记死端点照抄。

F6.02 邀请与加入(invites/join)

对应 PRD:§4 M6 · POST /api/organizations/me/invites、POST /api/organizations/join | 状态:实现 | 实现落点:org_handlers.go:71-103、service.go:110-142、code.go:14-24、repo.go:135-157 | UI:OrganizationView.vue(成员 tab)| 场景挂钩:OrgChain(invite→join→三哨兵)

1 功能定义

owner 生成 8 位邀请码(crypto/rand 大写 base36——差异⑥形态等价);凭码加入组织(三写禁事务)。

2 触发条件与前置状态

invites:orgOf 无行或非 owner → 双分支同哨兵 ErrOwnerOnlyInvite 403(service.go:113-120——无组织并入 403 非 404)。join 仅登录;码查 code=? AND status='pending' 无行 → 400;无「已入组」防重(双侧同形,重复 join 产生重复 org_members 行)。

3 输入与校验

invites body email 可选缺省 “"(无格式校验双侧同形);join body code 可选缺省 “"。

4 处理流程

invites:genInviteCode(code.go:14-24)→ inv_ id → InsertInvite 四列单写。join 三写逐条禁事务(service.go:127-142=冻结 :546-548 同序):MarkInviteUsed(status→‘used’)→ InsertOrgMember(om_,role 恒 ‘member’)→ SetUserOrg(users.org_id 回填);任一失败即中断前写不回滚(三条铁律之三)。

5 输出与结果状态

invites {"id","code"};join data:true。

6 状态流转

org_invites pending→used;org_members 新增 member 行;users.org_id ← inv.org_id(跨三表写序固定)。

7 边界与异常
条件系统行为提示/码
非 owner/无组织403「仅组织管理员可邀请」(文案「管理员」vs 判定 role=='owner' 严格——PRD ⑩)
码无效/已用400「邀请码无效或已使用」
8 权限与数据规则

ownerOnly 严格;join 无防重 quirk 照抄(幂等性缺失=冻结原样)。

F6.03 成员移除与角色(members DELETE/role PUT)

对应 PRD:§4 M6 · DELETE、PUT /api/organizations/me/members/:memberId(/role) | 状态:实现 | 实现落点:org_handlers.go:106-132、service.go:146-198、repo.go:114-124/:160-175 | UI:OrganizationView.vue(成员 tab)| 场景挂钩:OrgChain(移除双态合并断言)

1 功能定义

owner 移除成员(两写:删行+users.org_id 清空)、调整成员角色(非 ‘admin’ 一律归 ‘member’ 双态收敛)。

2 触发条件与前置状态

两端点 orgOf 无行/非 owner → 各自同哨兵 403;role 面 target 无行 → 404「成员不存在」(service.go:186-192);remove 面 target 查 MemberByID(org 隔离 WHERE id=? AND org_id=?)。

3 输入与校验

路径 memberId;role body role 可选(service.go:196-198 非 admin 归 member——与 allocation-mode 同构双态收敛)。

4 处理流程

remove:target.Role==‘owner’ → 400「无法移除」→ 两写 DeleteOrgMember + ClearUserOrg(UPDATE users SET org_id=''——冻结 SET org_id=NULL 的 Go 空串语义等价,repo.go:159 注)。role:owner target 拒 → 归一 → SetMemberRole 单写。

5 输出与结果状态

remove data:true;role {"id","role"}(归一后值)。

6 状态流转

org_members 删行/role 覆写;users.org_id 清空(’’)。

7 边界与异常
条件系统行为提示/码
target 不存在 或 target 是 owner400「无法移除」(双态合并同一哨兵 ErrCannotRemove——PRD ⑨,非 404 quirk)
role target 无行404「成员不存在」
role target 是 owner400「不能修改所有者角色」(文案「所有者」与 invites「管理员」不同源——冻结 :564 vs :537 原样差异)
非 owner403「仅组织所有者可调整角色」
8 权限与数据规则

owner 无法被移除也无法被改角色(owner 调用时 target 即 owner);org 隔离谓词防跨组织 memberId 枚举。

F6.04 项目共享与共享列表(share/shared-projects)

对应 PRD:§4 M6 · POST /api/director3d/:id/share、GET /api/organizations/shared-projects | 状态:实现 | 实现落点:org_handlers.go:135-165、service.go:203-232、repo.go:196-209、repo_read.go:167-180 | UI:Director3DView.vue(分享开关)/OrganizationView.vue(共享列表)| 场景挂钩:OrgChain(share/search 面);E2E-NAV-01 间接

1 功能定义

导演项目所有者切换 org_shared 开关(跨 /director3d 前缀归本域——冻结 index.js:574);组织内读取共享项目列表(两级子查询计数+全剧本下发)。

2 触发条件与前置状态

share:判定序属主先于组织(ProjectOwned 不属主 → 404 英文 “not found”;再 orgOf 无行 → 400「您尚未加入企业,无法共享」——冻结 :575-578 同序);shared-projects:orgOf 无行 → ok([]) 非 404(四态谱第二态)。

3 输入与校验

share body shared bool(冻结 truthy 强转 ? 1 : 0 vs Go bool 严格 bind);shared-projects 无输入。

4 处理流程

share:存在性 COUNT → orgOf → UPDATE director_projects SET org_shared=? 单写(repo.go:202-209)。shared-projects:dp. 显式 10 列+owner_name/owner_email 尾插+episodes/videos 两级 COUNT 子查询(video_id IS NOT NULL 即止);WHERE org_shared=1 AND user_id IN(...);ORDER BY created_at DESC 无 LIMIT。

5 输出与结果状态

share {"id","org_shared"}(值 int64 0/1 数值非 bool);shared-projects=ShareRow 数组,行键 id,user_id,name,style_id,face_style,status,kind,org_shared,script_text,created_at,owner_name,owner_email,episodes,videos(model.go:110-125——script_text 全剧本下发即冻结 dp.* 语义,十九续 C1 投影补齐)。

6 状态流转

director_projects.org_shared ← 0|1。

7 边界与异常
条件系统行为提示/码
非属主/项目不存在404“not found”(英文原样,ErrNotFoundEN 共享哨兵)
未入组织400「您尚未加入企业,无法共享」
8 权限与数据规则

仅属主可共享本人项目(orgOf 仅判存在性——member 亦可共享);member 可见全组织共享面。

F6.05 团队看板(dashboard)

对应 PRD:§4 M6 · GET /api/organizations/me/dashboard | 状态:实现 | 实现落点:org_handlers.go:167-178、service.go:240-317、repo_read.go:57-138 | UI:OrganizationView.vue(团队看板 tab)| 场景挂钩:OrgChain(available==balance 钉死断言)

1 功能定义

组织级积分总览(五聚合)+近 14 天消耗趋势+项目/人员双排行(全站最密 quirk 节)。

2 触发条件与前置状态

orgOf 无行 → data:null(四态谱第一态);无 role 门槛。

3 输入与校验

无输入(GET 零参数)。

4 处理流程

MemberIDsOfOrg → 五聚合逐查(balance/recharged=approved 充值/consumed=负额取反/frozen=eval_projects status=‘running’ SUM)→ trend 14 天循环(i:13→0,SQL 方言 DATE(DATE_SUB(NOW(), INTERVAL ? DAY))——repo_read.go:5-6 冻结 date('now',?,'localtime') 等价改写)→ projectRank TOP10(谓词 task_id IS NOT NULL——十九续 A4 勘误还原,空串聚合行「独立生成 / 工具」上榜)→ memberRank TOP20(name=nickname‖email)。

5 输出与结果状态

DashboardDoc 键序 overview,trend,projectRank,memberRank;overview 键序 balance,available,frozen,recharged,consumed;行键 rank,name,kind,amount(kind 单词键——十九续 A1 曾误写 amount-kind 连字符已修,wire 键集断言守卫自此入惯例)与 rank,name,email,amount;trend 行 label,amount。projectRank/memberRank 驼峰 wire 双键(同 M2 shotCount 族照抄勿修正)。

6 状态流转

纯读零写。

7 边界与异常

仅 401/503/ok(null) 面;空组织 ids → 聚合占位符空串照常返 0(repo_read.go:71-76)。

8 权限与数据规则

available==balance 谎言钉死(service.go:312 Available: balance 直接复制无独立查询——PRD ②);projectRank dir_ 死分支(service.go:287-290 HasPrefix “dir_” 永不命中——实产 dpr_(ids.go:25)且 TaskID[4:] 偏移随前缀长度错位,PRD ② 照抄);frozen 公式=M11 语义同形(M11 已落地后 running 可非零——「语义冻结谎言」勿写「恒 0」时态断言)。

F6.06 作品墙(showcase 三件)

对应 PRD:§4 M6 · GET/POST /api/showcase、DELETE /api/showcase/:id | 状态:实现 | 实现落点:org_handlers.go:182-223、service.go:320-346、repo_read.go:184-218 | UI:WorkShowcaseView.vue | 场景挂钩:E2E-NAV-01 间接;OrgChain(showcase 恒 ok 断言)

1 功能定义

全站作品展示列表(跨组织跨用户)、发布 done 成片为作品、删除本人作品。

2 触发条件与前置状态

列表仅登录无组织判定(全表);发布前置 VideoDoneOfUser(id=? AND user_id=? AND status='done' 三谓词)无行 → 404;删除无任何前置校验。

3 输入与校验

发布 body video_id(语义必填缺省 "” 查无 404)+title 可选(>60 rune 截 60、空串缺省「未命名作品」——冻结 String(title||'未命名作品').slice(0,60) 同形)。

4 处理流程

列表 Works 单读(7 列+author 尾插,ORDER BY created_at DESC LIMIT 60 跨组织全表);发布 wk_ id → InsertWork 五列单写(cover_path 不写走 DDL 默认 ‘’);删除 DELETE ... WHERE id=? AND user_id=? 不查行数。

5 输出与结果状态

WorkRow 行键 id,user_id,title,video_id,cover_path,video_path,created_at,author(cover_path *string NULLIF 还原 null);发布 {"id"};删除 true。

6 状态流转

works 插行(video_id/video_path 冗余固化自 videos)/删行。

7 边界与异常
条件系统行为提示/码
非本人/未完成/不存在成片404「成片不存在或未完成」(三态合并单哨兵)
删除他人/不存在作品200恒 true 静默 0 行(恒 ok quirk PRD ⑧)
8 权限与数据规则

列表不分组织不看本人(冻结 e.js:62 无 WHERE quirk 照抄);删除 user_id=? 谓词限本人——他人作品删不掉只是不报。

F6.07 素材夹目录树(folders 三件)

对应 PRD:§4 M6 · GET/POST /api/organizations/me/material/folders、DELETE …/folders/:id | 状态:实现 | 实现落点:org_handlers.go:224-265、service.go:349-407、repo_read.go:222-251 | UI:OrganizationView.vue(素材 tab)| 场景挂钩:OrgChain(folders 面)

1 功能定义

组织素材夹目录树(含计数)读、建、删(先摘后删)。

2 触发条件与前置状态

GET 无行 → ok([])(四态谱第二态);POST 无行 → 400「您尚未加入企业」(四态谱第三态——冻结 e.js:89 同 400);DELETE 无行 → 400,夹不存在 → 404 英文。

3 输入与校验

POST body name(trim 必填 400、>30 rune 截 30)+type 可选——六类白名单 character,scene,prop,image,video,audio,白名单校验先于 name 必填判定(service.go:374-376 先于 :377),不在白名单缺省 ‘character’。

4 处理流程

GET AssetFolders 单读(6 列+asset_count 子查询,ORDER BY type,created_at);POST fd_ id → 五列单写;DELETE 两写先摘后删:DetachFolderAssets(UPDATE asset_library SET folder_id=''——冻结 NULL 的 ’’ 语义)→ DeleteFolder(DELETE by id 无 org 谓词——id 已隔离校验过,冻结 :103 同形)。

5 输出与结果状态

AssetFolderRow 行键 id,org_id,user_id,type,name,created_at,asset_count;POST {"id"};DELETE true。

6 状态流转

asset_library.folder_id 批量清空 + asset_folders 删行(两写无事务)。

7 边界与异常
条件系统行为提示/码
未入组织(POST/DELETE)400「您尚未加入企业」
name 空400「文件夹名称必填」
夹不存在404“not found”(英文共享哨兵)
8 权限与数据规则

任何组织成员皆可建夹/删夹(无 owner 门槛——删除他人所建夹亦放行只要同组织,冻结同形)。

F6.08 素材列表与配额估算(materials)

对应 PRD:§4 M6 · GET /api/organizations/me/materials | 状态:实现 | 实现落点:org_handlers.go:266-277、service.go:411-449、repo_read.go:254-299 | UI:OrganizationView.vue(素材 tab)| 场景挂钩:OrgChain

1 功能定义

组织共享素材列表(type/folder 双可选过滤)+存储配额估算法(80KB/2MB 系数硬编码)。

2 触发条件与前置状态

orgOf 无行 → {assets:[],storage:null}(非 404 非 400——四态谱第二态变体)。

3 输入与校验

query type(!="" && !="全部" 才过滤)+folder_id(非空才过滤)——双可选。

4 处理流程

动态 SQL(显式 9 列+三 JOIN 尾插 image_url/owner_name/folder_name,WHERE user_id IN(...) AND org_shared=1,LIMIT 200)→ 存储三查(图片 SUM+NULL 计数双列/音频 SUM/视频 COUNT)→ 估算:used=img.Sum+img.Nulls×80KB+aud+vidCount×2MB,quota=10GB,percent=min(100, Round(×100×10)/10)(toFixed(1) 正数域等价——差异④)。

5 输出与结果状态

{assets,storage};AssetRow 行键 id,user_id,name,type,description,image_id,org_shared,folder_id,created_at,image_url,owner_name,folder_name(description/folder_id/folder_name/image_url NULL→null);storage 键序 used,quota,percent,plan,plan 恒「基础档」。

6 状态流转

纯读零写。

7 边界与异常

无业务异常面(仅 401/503)。

8 权限与数据规则

配额系估算法非实测(80KB/2MB 假设值——PRD ④);素材面=组织全员 org_shared=1 资产(含他人);视频按条数不按字节估。

F6.09 素材归夹(material/assign)

对应 PRD:§4 M6 · POST /api/organizations/me/material/assign | 状态:实现 | 实现落点:org_handlers.go:278-295、service.go:453-471、repo_read.go:302-311 | UI:OrganizationView.vue(素材 tab)| 场景挂钩:OrgChain

1 功能定义

本人素材移入/移出文件夹(folder_id 可空清除)。

2 触发条件与前置状态

orgOf 无行 → 400「您尚未加入企业」(四态谱第三态);AssetOwned(id=? AND user_id=? 仅本人)不在 → 404。

3 输入与校验

body asset_id + folder_id *string(指针捕获「缺失/null」vs「空串」——nil 或 "” 最终都落 '' 清除);不校验 folder_id 存在性(双侧同形,悬空 folder_id 可写入)。

4 处理流程

orgOf → AssetOwned → UPDATE asset_library SET folder_id=? WHERE id=? 单写。

5 输出与结果状态

data:true。

6 状态流转

asset_library.folder_id ← folder_id|’’(空串即清除语义,读侧 NULLIF 还原 null)。

7 边界与异常
条件系统行为提示/码
未入组织400「您尚未加入企业」
非本人/不存在素材404「素材不存在」
8 权限与数据规则

仅本人素材可归夹(组织共享面读、本人面写的不对称)。

F6.10 成员组(groups 三件)

对应 PRD:§4 M6 · GET/POST /api/organizations/me/groups、DELETE …/groups/:id | 状态:实现 | 实现落点:org_handlers.go:296-337、service.go:475-529、repo_read.go:315-323、repo_groups.go:12-28 | UI:OrganizationView.vue(成员组 tab)| 场景挂钩:OrgChain

1 功能定义

组织成员组列表、创建(leader 可空)、删除(先摘后删)。

2 触发条件与前置状态

GET 无行 → ok([]);POST/DELETE orgOf 无行或非 owner → 各自同哨兵 403(无组织并入 403 非 404);DELETE 无 404 面(PRD ⑧——不查组存在性)。

3 输入与校验

POST body name(trim 必填 400、截 30)+leader_id *string(nil→’’ 落库;不校验 leader 是否本组织成员双侧同形)。

4 处理流程

GET 单读(5 列+leader_id NULLIF+leader_name LEFT JOIN+member_count 子查询);POST mg_ id → 四列单写;DELETE 两写先摘后删:DetachGroupMembers(UPDATE org_members SET group_id='' WHERE group_id=?——按 group_id 全局摘无 org 谓词,冻结 :167 同形)→ DeleteMemberGroup(id=? AND org_id=? org 隔离)。

5 输出与结果状态

MemberGroupRow 行键 id,org_id,name,leader_id,created_at,leader_name,member_count(leader_id/leader_name NULL→null);POST {"id"};DELETE true。

6 状态流转

member_groups 插行/删行;org_members.group_id 批量清空。

7 边界与异常
条件系统行为提示/码
非 owner(POST)403「仅组织所有者可创建成员组」
name 空400「组名必填」
非 owner(DELETE)403「仅组织所有者可删除」
删不存在/他组织 id200恒 true(恒 ok 死分支 PRD ⑧——403 后无 404 面)
8 权限与数据规则

ownerOnly 严格双分支;DELETE 无行数校验(PRD ⑧)。

F6.11 权限组 CRUD(perm-groups 四件)

对应 PRD:§4 M6 · GET/POST /api/organizations/me/perm-groups、PUT/DELETE …/perm-groups/:id | 状态:实现 | 实现落点:org_handlers.go:338-401、service.go:532-627+747-775、repo_read.go:333-340、repo_groups.go:31-55 | UI:OrganizationView.vue(权限组 tab)| 场景挂钩:OrgChain(material-perms 联动)

1 功能定义

权限组(四键 perms 白名单 view/edit/delete/download)列表/建/改(COALESCE 部分更新)/删。

2 触发条件与前置状态

GET 无行 → ok([]);POST/PUT/DELETE owner 严格 → 各自 403(文案各异 model.go:26-28);PUT 夹不存在 → 404 英文;DELETE 无 404 面(PRD ⑧)。

3 输入与校验

POST body name(trim 必填截 30)+description(不 trim截 100)+perms []string(FilterPermKeys 白名单保输入序,perms.go:42-53);PUT body 三键全 *string/[]string——hasPerms=req.Perms != nil(perms 缺省/null 沿用旧值;name 传 nil 或空串均沿用 COALESCE 双闸;description 传 "” 为清空)。

4 处理流程

POST pg_ id → 五列单写(perms=JSON 串);PUT PermGroupByID → name=COALESCE(?,name), description=COALESCE(?,description), perms=?(perms 恒写,repo_groups.go:39-43);DELETE 两写先摘后删:DetachPermGroupMembers(perm_group_id=’’)→ DeletePermGroup(org 隔离)。

5 输出与结果状态

GET 行经 permGroupDoc map 重出六键 id,org_id,name,description,perms,created_at——perms 由 json:"-" 串解析重出为数组(空/坏串/nil 一律 [],service.go:763-775);POST {"id","perms"};PUT {"perms"};DELETE true。

6 状态流转

perm_groups 插行/三列部分更新/删行;org_members.perm_group_id 批量清空。

7 边界与异常
条件系统行为提示/码
非 owner(三写面各自)403「仅组织所有者可创建/修改/删除权限组」(文案各异)
name 空(POST)400「权限组名称必填」
PUT 组不存在404“not found”
DELETE 不存在200恒 true(PRD ⑧)
8 权限与数据规则

ownerOnly 四处;COALESCE 三闸差异(name 空串沿用 vs description 空串清空 vs perms 恒写)——part-update 语义三态勿统一。

F6.12 成员指派(members/:memberId/assign PUT)

对应 PRD:§4 M6 · PUT /api/organizations/me/members/:memberId/assign | 状态:实现 | 实现落点:org_handlers.go:402-419、service.go:645-671、repo.go:184-187 | UI:OrganizationView.vue(成员 tab)| 场景挂钩:OrgChain

1 功能定义

owner 将成员指派到成员组/权限组(双列可空清除)。

2 触发条件与前置状态

orgOf 无行/非 owner → 403;MemberByID 无行 → 404「成员不存在」;不校验 group_id/perm_group_id 存在性(双侧同形,悬空可写)。

3 输入与校验

body group_id/perm_group_id 双 *string(nil→"" 空串语义=清除)。

4 处理流程

判权 → MemberByID → UPDATE org_members SET group_id=?, perm_group_id=? WHERE id=? 单写双列。

5 输出与结果状态

{"group_id","perm_group_id"}——回 "" 而非 null(缺省时冻结回 null,Go 响应面未走读侧 NULLIF——已登记 PRD ⑪,前端 assign 后重拉非回归)。

6 状态流转

org_members.group_id/perm_group_id 双列覆写(传 nil 即清空)。

7 边界与异常
条件系统行为提示/码
非 owner403「仅组织所有者可指派」
target 无行404「成员不存在」
8 权限与数据规则

ownerOnly;无 owner-target 豁免判定(可对 owner 行写 group_id——冻结同形)。

F6.13 素材权限矩阵(material-perms 两件)

对应 PRD:§4 M6 · GET /api/organizations/me/material-perms、PUT …/material-perms/:memberId | 状态:实现 | 实现落点:org_handlers.go:421-449、service.go:674-725+686-696、perms.go:7-39、repo.go:190-193 | UI:OrganizationView.vue(素材权限 tab)| 场景挂钩:OrgChain(联动归一断言)

1 功能定义

组织全员素材权限矩阵读、单成员五键权限设置(联动归一幂等)。

2 触发条件与前置状态

GET 无行 → ok([]),无 role 门槛(全员可读矩阵);PUT orgOf 无行/!adminOrOwner → 403(本域唯一 admin 可用面,service.go:50-53=冻结 e.js:240);target 无行 → 404;target 是 owner → 400「所有者默认拥有全部权限」。

3 输入与校验

PUT body perms []string——NormalizeMaterialPerms 内联归一(perms.go:14-39):白名单过滤→upload/download/edit/delete 任一自动补 view→delete 自动补 edit→无 view 则整体清空返 []→按 view,upload,download,edit,delete 序输出。不传(nil)→ 归一空集 []。

4 处理流程

GET MaterialPermRows 单读(COALESCE(material_perms,'[]')+perm_group_id NULLIF,org_members JOIN users 全员行)→ service map 重出;PUT 归一 → 单写 JSON 串。

5 输出与结果状态

GET 行 map 六键 member_id,role,nickname,email,perms,perm_group_id(perms 数组重出);PUT {"perms"}(归一后数组)。无分页(全员行)。

6 状态流转

org_members.material_perms ← JSON 串(如 ["view","edit"])。

7 边界与异常
条件系统行为提示/码
非 owner 非 admin403「仅组织管理员可设置素材权限」(adminOrOwner 唯一面)
target 无行404「成员不存在」
target 是 owner400「所有者默认拥有全部权限」
8 权限与数据规则

toggleMatPerm 联动 quirk(perms.go:12-13 注:取消 view 即取消全部——归一幂等与冻结逐字同形故 PRD 未单列);owner target 不可设(恒全权)。

F6.14 全站搜索(search)

对应 PRD:§4 M6 · GET /api/search | 状态:实现 | 实现落点:org_handlers.go:451-459、service.go:728-741、repo_read.go:353-371 | UI:App.vue 全局搜索框(:259 /search?q=)| 场景挂钩:OrgChain(share/search 面)

1 功能定义

全局搜索——剧本库(三列 LIKE TOP8)+编剧档案(三列 LIKE TOP6)。

2 触发条件与前置状态

仅登录;无组织判定(任何登录用户搜全站;归 M6 组系冻结 enterprise.js 物理位置)。

3 输入与校验

query q——TrimSpace 后空 → 直接回空双数组不查库。

4 处理流程

SearchScripts(title/description/tags 三列 %q% LIKE,enabled=1,ORDER BY rating DESC LIMIT 8,tier CASE 表达式内联)→ SearchWriters(name/bio/genres 三列 LIKE,LIMIT 6,published 子查询计数)串行两查。

5 输出与结果状态

{scripts,writers};ScriptHit 行键 id,title,genre,market,form,rating,cover_path,description,tier(cover_path/description NULL→null,tier 恒在 ‘精品’|‘普通’);WriterHit 行键 id,user_id,name,bio,genres,works,published。

6 状态流转

纯读零写。

7 边界与异常

无业务异常面;空 q 返 {scripts:[],writers:[]}。

8 权限与数据规则

quirk:% 通配未转义(q 含 %/_ 充当通配符——双侧同形);跨域读 script_library/writer_profiles(repo.go:91-93 注释登记)。


册尾端点覆盖对账(29/29)

对账 backend/cmd/server/routes.go:108-136 亲数 29 条 ↔ 本册 F6.01~F6.14。口径脚注:冻结侧两口径重合无错位——routes.go:104 注「index.js 组织段 9 + enterprise.js 20」,物理分组 :108-116 恰 9 条(=冻结 index.js:513-597 亲数 9)+ :117-136 恰 20 条(=enterprise.js router.* 亲数 20);#8 share 物理路由冻结在 index.js:574 非 director.js(routes.go:106-107 归组注记)。

#端点(routes.go:line)节#端点(routes.go:line)节
1POST /organizations/applications(:108)F6.0116POST /organizations/me/material/folders(:122)F6.07
2GET /organizations/me(:109)F6.0117DELETE /organizations/me/material/folders/:id(:123)F6.07
3PUT /organizations/me/credit-allocation-mode(:110)F6.0118GET /organizations/me/materials(:124)F6.08
4POST /organizations/me/invites(:111)F6.0219POST /organizations/me/material/assign(:125)F6.09
5POST /organizations/join(:112)F6.0220GET /organizations/me/groups(:126)F6.10
6DELETE /organizations/me/members/:memberId(:113)F6.0321POST /organizations/me/groups(:127)F6.10
7PUT /organizations/me/members/:memberId/role(:114)F6.0322DELETE /organizations/me/groups/:id(:128)F6.10
8POST /director3d/:id/share(:115,冻结 index.js:574)F6.0423GET /organizations/me/perm-groups(:129)F6.11
9GET /organizations/shared-projects(:116)F6.0424POST /organizations/me/perm-groups(:130)F6.11
10GET /organizations/me/dashboard(:117)F6.0525PUT /organizations/me/perm-groups/:id(:131)F6.11
11GET /showcase(:118)F6.0626DELETE /organizations/me/perm-groups/:id(:132)F6.11
12POST /showcase(:119)F6.0627PUT /organizations/me/members/:memberId/assign(:133)F6.12
13DELETE /showcase/:id(:120)F6.0628GET /organizations/me/material-perms(:134)F6.13
14GET /organizations/me/material/folders(:121)F6.0729PUT /organizations/me/material-perms/:memberId(:135)F6.13
15GET /api/search(:136)F6.14

差异纪律:PRD §4 M6 ①~⑪+十九续勘正批注(A1 wire 键/A4 谓词还原/C1 script_text 投影)全引用不重立;本册新立 DIF-31-1(members/ShareRow/assets 行 created_at 键序位次三处同族——键集相等序不同,前端按键取值无感,M4 ① 同族先例整批登记)/DIF-31-2(perm-groups/material-perms 响应 map 字母序输出+PUT perms 传非数组时冻结 200 沿用旧值 vs Go bind 400 类型宽容差)入 99-附录 B。