openxiangda-skill-kit 2.0.0-alpha.39 → 2.0.0-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/architecture/school-contact-head-teacher-v2.md +10 -0
- package/docs/architecture/workflow-participant-surface-v2.md +198 -0
- package/docs/data-authz.md +1 -1
- package/docs/school-contact-relations.md +21 -1
- package/package.json +2 -2
- package/skills/openxiangda-v2/SKILL.md +3 -0
- package/skills/openxiangda-v2-backend/SKILL.md +4 -0
- package/skills/openxiangda-v2-data-authz/SKILL.md +5 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# School-contact head-teacher contract v2
|
|
2
|
+
|
|
3
|
+
- Problem evidence: DingTalk returns head-teacher status only as `teacher` membership `feature.is_adviser`; the platform stores it but the Native school-contact API currently exposes only guardian/student relations and the three broad platform identities.
|
|
4
|
+
- Capability owner: the platform organization service remains the sole owner of synchronized membership facts and `platformRoleCodes`. A 2.0 application reads them through the typed NestJS platform client and exposes its own App API; React never calls the platform endpoint directly.
|
|
5
|
+
- Stable invariants: `SCHOOL_HEAD_TEACHER` is additive to `SCHOOL_TEACHER`; it means the user heads at least one current class and cannot identify which class. Exact class membership comes from `querySchoolContactTeachers` and `isHeadTeacher`. No application-local copy or fourth DingTalk source role is introduced.
|
|
6
|
+
- Affected contracts: `openxiangda-nest` adds typed teacher/class query, record and page contracts plus `querySchoolContactTeachers`. Skills, docs and the standard template explain the global-role versus class-relation boundary. Existing guardian/student APIs are unchanged.
|
|
7
|
+
- Failure and concurrency: the client is a bounded read using the verified authorization and RoleSession. It adds no retry, cache, queue, mutable state or browser credential exposure; platform sync retains its existing complete-snapshot transaction and tenant lock.
|
|
8
|
+
- Security and resource bounds: the platform enforces tenant, app, RoleSession and all/self/class scope. Responses contain platform user ID, DingTalk userid, name and nullable mobile; applications return only fields needed by their App API and must not log full responses. Page size remains capped at 100.
|
|
9
|
+
- Rollback boundary: the Nest method, docs and template are independently removable. The additive platform identity and migration may remain after an SDK rollback. Existing 1.x applications and other 2.0 methods ignore the new contract.
|
|
10
|
+
- Falsifiable verification: typed tests must prove the exact `/school-contact/teachers` request, RoleSession headers and boolean serialization; package checks and `pnpm verify:affected` pass; Skills validation names both `SCHOOL_HEAD_TEACHER` and `teachers.list`/`querySchoolContactTeachers` without suggesting browser direct access.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Workflow participant surface v2
|
|
2
|
+
|
|
3
|
+
Status: accepted for implementation on 2026-08-20.
|
|
4
|
+
|
|
5
|
+
## Problem evidence
|
|
6
|
+
|
|
7
|
+
- The standard purchase form already persists the selected department and
|
|
8
|
+
submits `departmentId` as a workflow fact, while the workflow definition also
|
|
9
|
+
asks the initiator to choose a submission department. The confirmation
|
|
10
|
+
surface therefore repeats a decision the user has already made and exposes a
|
|
11
|
+
second "resolve approval path" click.
|
|
12
|
+
- Department supervisor resolution returns valid user identifiers without
|
|
13
|
+
display names. A node with four supervisors is rendered as four identical
|
|
14
|
+
unresolved placeholders even though assignment succeeded.
|
|
15
|
+
- Workflow preview already contains its canonical end node. Admin, desktop user,
|
|
16
|
+
and mobile user renderers append another synthetic completion node.
|
|
17
|
+
- Task surfaces project only a subset of their owning instance, omitting
|
|
18
|
+
`startedAt` and `completedAt`. The timeline endpoint returns command logs only,
|
|
19
|
+
so a newly started instance has no visible process history and no future-node
|
|
20
|
+
preview.
|
|
21
|
+
- A department supervisor can be frozen into a workflow task by user identity
|
|
22
|
+
without owning an application role membership. Native RoleSession bootstrap
|
|
23
|
+
stops at `unassigned` before the Workflow Kernel can apply its existing
|
|
24
|
+
task/instance participant authorization.
|
|
25
|
+
|
|
26
|
+
## Capability owners
|
|
27
|
+
|
|
28
|
+
- Native RoleSession remains the only owner of the active human runtime
|
|
29
|
+
identity. It may expose one reserved `workflow_participant` subject when the
|
|
30
|
+
user is an initiator or participant in this application's environment.
|
|
31
|
+
- Workflow Kernel remains the only owner of task assignment, instance access,
|
|
32
|
+
commands, node state, timestamps, and immutable operation logs.
|
|
33
|
+
- Native Data remains the owner of physical business records. Workflow Surface
|
|
34
|
+
may request one revision-bound record for an already-authorized instance and
|
|
35
|
+
project only fields visible under the workflow field policy.
|
|
36
|
+
- Workflow Definition and Binding remain the only owners of path shape and
|
|
37
|
+
assignee resolution. Renderers do not infer workflow transitions.
|
|
38
|
+
- Admin and user renderers own only presentation and automatic re-prepare after
|
|
39
|
+
all required answers are present.
|
|
40
|
+
|
|
41
|
+
## Stable invariants and affected contracts
|
|
42
|
+
|
|
43
|
+
1. A business form's explicit department fact is used directly by a
|
|
44
|
+
`department_supervisor` binding. The reference workflow does not ask for a
|
|
45
|
+
second organization-context department.
|
|
46
|
+
2. `prepare-start` remains revision-bound and side-effect free. When genuine
|
|
47
|
+
requirements remain, changing a complete answer set automatically requests a
|
|
48
|
+
new preparation; starting still requires a ready preparation token and one
|
|
49
|
+
explicit confirmation.
|
|
50
|
+
3. Candidate identifiers and assignment semantics do not change. The platform
|
|
51
|
+
adds directory display names when they are available, and renderers fall back
|
|
52
|
+
to a concise assignee count instead of repeating unresolved labels.
|
|
53
|
+
4. A canonical end node is the sole completion step. A synthetic completion
|
|
54
|
+
step is added only when a preview has no end node.
|
|
55
|
+
5. Workflow timeline adds a deterministic `flow` projection containing submit,
|
|
56
|
+
planned nodes, node visit state, assignees, and lifecycle timestamps. Existing
|
|
57
|
+
immutable command logs remain available as `items`.
|
|
58
|
+
6. Task Surface returns the complete public instance lifecycle projection and a
|
|
59
|
+
field-policy-filtered `presentation.businessData` record when the DataRef is
|
|
60
|
+
valid. The client does not need ordinary Data API read capability to render
|
|
61
|
+
an assigned approval.
|
|
62
|
+
7. The reserved `workflow_participant` RoleSession has zero application
|
|
63
|
+
capabilities and no data scopes. It does not create an application role or
|
|
64
|
+
membership. Workflow endpoints still authorize every task, instance,
|
|
65
|
+
work-center row, timeline, and command from stored participation.
|
|
66
|
+
8. `workflow_participant` cannot prepare or start a workflow and cannot call
|
|
67
|
+
ordinary Data/App APIs. Existing membership and super-admin behavior is
|
|
68
|
+
unchanged.
|
|
69
|
+
9. OpenXiangda 1.x, unrelated tenants, and existing production AppVersions do
|
|
70
|
+
not consume these 2.0 contracts.
|
|
71
|
+
|
|
72
|
+
## Failure, concurrency, security, and resource bounds
|
|
73
|
+
|
|
74
|
+
- Automatic re-prepare is generation-guarded; a later answer change wins and a
|
|
75
|
+
stale response cannot replace the current preparation token.
|
|
76
|
+
- RoleSession creation and switching keep the existing serializable transaction,
|
|
77
|
+
single-active-session rule, expiry, Head revision binding, and CAS behavior.
|
|
78
|
+
- The reserved subject exists only while a matching workflow instance or task
|
|
79
|
+
participation row exists in the same tenant, application, environment, and
|
|
80
|
+
user identity. It is invalidated by the normal bootstrap/context checks when
|
|
81
|
+
that relation disappears.
|
|
82
|
+
- Workflow instance, task, timeline, and business-data reads fail closed unless
|
|
83
|
+
the caller is the initiator, a stored participant, or the application super
|
|
84
|
+
admin. Completed participants retain read-only history access; only an active
|
|
85
|
+
task assignment receives command operations.
|
|
86
|
+
- Timeline planning uses the activated immutable definition and frozen fact
|
|
87
|
+
snapshot. It is bounded by the existing 200-step planner limit. Business-data
|
|
88
|
+
projection reads exactly one referenced record and never enables list queries.
|
|
89
|
+
|
|
90
|
+
## Rollback boundary
|
|
91
|
+
|
|
92
|
+
- Platform rollback uses the previous platform-server image plus the down
|
|
93
|
+
section of the additive RoleSession subject-kind SQL migration.
|
|
94
|
+
- Frontend and contract rollback uses the previous independently published
|
|
95
|
+
OpenXiangda 2.0 package versions.
|
|
96
|
+
- Reference application rollback uses the previous immutable preproduction
|
|
97
|
+
AppVersion. No compatibility flag or duplicate authorization store remains.
|
|
98
|
+
|
|
99
|
+
## Falsifiable verification
|
|
100
|
+
|
|
101
|
+
1. Reference workflow tests prove that the department selected in the business
|
|
102
|
+
form is the only department input and resolves the configured platform
|
|
103
|
+
supervisor.
|
|
104
|
+
2. Admin, desktop user, and mobile user tests prove answer changes re-prepare
|
|
105
|
+
automatically, real assignee names render, and previews contain one terminal
|
|
106
|
+
node.
|
|
107
|
+
3. Platform tests prove task Surface carries start/completion timestamps and
|
|
108
|
+
field-policy-filtered business data, while timeline returns submit, complete,
|
|
109
|
+
active, and waiting node states plus immutable command logs.
|
|
110
|
+
4. A user with no application membership but an assigned department-supervisor
|
|
111
|
+
task receives a `workflow_participant` RoleSession, can open and operate that
|
|
112
|
+
task, can view the instance history after completion, and cannot prepare a
|
|
113
|
+
new workflow or read ordinary Data API records.
|
|
114
|
+
5. Users with no application role and no workflow relationship remain
|
|
115
|
+
`unassigned`; membership, multi-role selection, and super-admin tests remain
|
|
116
|
+
unchanged.
|
|
117
|
+
6. Platform OpenXiangda v2 release verification, toolchain affected/release
|
|
118
|
+
verification, reference application generate/check/test/build, and desktop
|
|
119
|
+
plus mobile Chromium approval paths pass before preproduction activation.
|
|
120
|
+
|
|
121
|
+
## Release-gate correction: local department supervisor resolution
|
|
122
|
+
|
|
123
|
+
The first formal packed-distribution run failed in the fresh PostgreSQL
|
|
124
|
+
application at `department-review` with
|
|
125
|
+
`WORKFLOW_V2_APPROVER_RESOLUTION_EMPTY`. Production correctly delegates this
|
|
126
|
+
provider to platform organization data, but the local platform adapter still
|
|
127
|
+
expected every approval binding to carry an application `roleCode`; the
|
|
128
|
+
standard `department_supervisor` binding intentionally has none.
|
|
129
|
+
|
|
130
|
+
- Ownership and contracts remain unchanged: production organization data owns
|
|
131
|
+
configured supervisors, while the local adapter only emulates that provider
|
|
132
|
+
from the fixture's `department_manager` membership and department approval
|
|
133
|
+
scope.
|
|
134
|
+
- The local adapter must require the binding's `departmentIdFrom` fact and a
|
|
135
|
+
matching `department` scope grant with `approve` (or `*`). Missing facts,
|
|
136
|
+
roles, or scope continue to fail closed with the existing resolution error.
|
|
137
|
+
- No new state store, public API, environment branch, concurrency behavior, or
|
|
138
|
+
production authorization path is introduced. Rollback is the previous
|
|
139
|
+
`openxiangda-local-platform` package.
|
|
140
|
+
- Unit verification covers both a matching and a non-matching department, and
|
|
141
|
+
the packed fresh-application PostgreSQL lifecycle must resolve the same
|
|
142
|
+
`department-review` node before this candidate can be published.
|
|
143
|
+
|
|
144
|
+
## Release-gate correction: persistent delegation policy
|
|
145
|
+
|
|
146
|
+
The next packed-distribution run resolved the department supervisor correctly,
|
|
147
|
+
but PostgreSQL start rejected the direct assignment with
|
|
148
|
+
`WORKFLOW_V2_PREPARATION_ASSIGNMENT_CHANGED`. The local assignment owner had
|
|
149
|
+
correctly ignored an unrelated long-term delegation, while the transaction
|
|
150
|
+
guard still queried delegation rules for every provider.
|
|
151
|
+
|
|
152
|
+
- The activated workflow binding remains the sole owner of whether long-term
|
|
153
|
+
delegation applies. The local platform passes that resolved policy into the
|
|
154
|
+
PostgreSQL transaction; the store does not infer provider semantics.
|
|
155
|
+
- Delegatable nodes retain the existing delegator lock, active-rule query, and
|
|
156
|
+
frozen participant comparison so a concurrent rule change still invalidates
|
|
157
|
+
preparation. Non-delegatable nodes validate a direct primary participant and
|
|
158
|
+
deliberately do not consult or lock unrelated delegation rules.
|
|
159
|
+
- No public contract, persisted document, production provider, application
|
|
160
|
+
identity, or business-data path changes. The rollback boundary is the prior
|
|
161
|
+
`openxiangda-local-platform` package, and 1.x applications, other tenants,
|
|
162
|
+
and already deployed AppVersions remain outside the blast radius.
|
|
163
|
+
- Verification must keep an active department-manager delegation present while
|
|
164
|
+
a `department_supervisor` preparation starts directly in the real local
|
|
165
|
+
PostgreSQL lifecycle. Its add-sign target remains a second explicit,
|
|
166
|
+
in-scope department-manager identity; the test must not widen the current
|
|
167
|
+
node to a finance role. Existing delegatable-node concurrency and snapshot
|
|
168
|
+
tests must continue to pass.
|
|
169
|
+
|
|
170
|
+
## Release-gate correction: department supervisor participant targets
|
|
171
|
+
|
|
172
|
+
The formal packed-distribution lifecycle then reached the explicit after-add-sign
|
|
173
|
+
command, but the local platform rejected the second department-manager identity
|
|
174
|
+
with `WORKFLOW_V2_TARGET_ROLE_SUBJECT_INVALID`. Initial assignee resolution
|
|
175
|
+
already derived the implicit `department_manager` role and required the selected
|
|
176
|
+
department's approval scope; participant-target validation still read only an
|
|
177
|
+
explicit binding `roleCode`, which `department_supervisor` intentionally omits.
|
|
178
|
+
|
|
179
|
+
- The activated Workflow binding remains the only owner of target eligibility.
|
|
180
|
+
The local adapter must apply the same implicit `department_manager` role,
|
|
181
|
+
`departmentIdFrom` fact, and `department` `approve` (or `*`) scope check to
|
|
182
|
+
transfer, delegate, and add-sign targets that it applies to the original
|
|
183
|
+
department-supervisor assignment.
|
|
184
|
+
- Missing department facts, a different application role, an out-of-scope
|
|
185
|
+
department, an expired local target, or an unknown RoleSubject continues to
|
|
186
|
+
fail closed with the existing target-validation error. No fallback role or
|
|
187
|
+
second resolver is introduced.
|
|
188
|
+
- Public Workflow contracts, remote provider behavior, participant persistence,
|
|
189
|
+
task/instance CAS, idempotency receipts, and concurrency behavior are
|
|
190
|
+
unchanged. Validation remains a bounded scan of declared local fixture roles
|
|
191
|
+
and scope grants and does not add remote calls or mutable state.
|
|
192
|
+
- The rollback boundary is the prior `openxiangda-local-platform` package.
|
|
193
|
+
OpenXiangda 1.x, other tenants, production platform authorization, and already
|
|
194
|
+
deployed AppVersions remain outside the blast radius.
|
|
195
|
+
- A focused local-platform test must accept a same-role target for the selected
|
|
196
|
+
department and reject the same target for another department. The full packed
|
|
197
|
+
PostgreSQL lifecycle must complete add-sign, restart recovery, and subsequent
|
|
198
|
+
approvals before this candidate can be published.
|
package/docs/data-authz.md
CHANGED
|
@@ -72,7 +72,7 @@ authz: {
|
|
|
72
72
|
|
|
73
73
|
`roleCodes` 把一条数据规则限定到当前 `RoleSession` 选中的角色。同一用户同时是申请人、学院管理员时,切换到学院管理员后,`applicant` 的 `current_user` 规则不再参与 OR 计算。多角色应用的角色特有规则必须声明 `roleCodes`;未声明时表示该规则对所有活动角色通用。编译器会拒绝空数组、重复或未声明的角色代码。
|
|
74
74
|
|
|
75
|
-
平台家校身份是另一条只读维度:使用 `platformRoleCodes`(`SCHOOL_GUARDIAN`、`SCHOOL_STUDENT`、`SCHOOL_TEACHER
|
|
75
|
+
平台家校身份是另一条只读维度:使用 `platformRoleCodes`(`SCHOOL_GUARDIAN`、`SCHOOL_STUDENT`、`SCHOOL_TEACHER`、`SCHOOL_HEAD_TEACHER`)表达家长、学生、老师、班主任,不把它们塞进应用 `roleCodes`。班主任同时保留老师身份;`SCHOOL_HEAD_TEACHER` 只表达“至少担任一个班的班主任”,具体班级必须通过老师班级关系的 `isHeadTeacher` 判断。这些编码由平台家校同步维护并在可信 Principal/RoleSession context 中返回;默认不增加数据限制,应用只有在明确需要时才在 App API 服务端做身份条件判断。浏览器请求中的同名字段不是授权凭据,不能用于放行。
|
|
76
76
|
|
|
77
77
|
角色成员、学院范围授权和“用户—仪器”关系授权属于环境运行数据,不进入不可变应用包。它们通过 Native 管理 API 维护,部署新版本不会删除。所有写操作要求 UUID `operationId`、目标资源 revision 和活动管理 RoleSession;平台在数据库事务内完成权限复核、幂等回执、CAS、相关会话撤销与审计。
|
|
78
78
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
| 家长 | `SCHOOL_GUARDIAN` | 家校成员 `guardian` |
|
|
14
14
|
| 学生 | `SCHOOL_STUDENT` | 家校成员 `student` |
|
|
15
15
|
| 老师 | `SCHOOL_TEACHER` | 家校成员 `teacher` |
|
|
16
|
+
| 班主任 | `SCHOOL_HEAD_TEACHER` | 至少一个老师班级成员带 `is_adviser=true` |
|
|
16
17
|
|
|
17
18
|
身份可组合,同一用户可以同时拥有多个编码。2.0 的 `NativePrincipal` / `Principal` 和 `RoleSessionContext` 以可选的 `platformRoleCodes` 暴露这些编码;它们只用于业务身份判断,不能替代当前应用角色、不能参与 `roleCodes` 的切换,也不能由浏览器提交。权限默认不限制,应用若需要按身份限制页面或 App API,应在服务端使用这些可信编码显式校验。
|
|
18
19
|
|
|
@@ -21,6 +22,8 @@ const isTeacher =
|
|
|
21
22
|
request.openxiangda?.principal.platformRoleCodes?.includes('SCHOOL_TEACHER')
|
|
22
23
|
```
|
|
23
24
|
|
|
25
|
+
`SCHOOL_HEAD_TEACHER` 表示此人至少在一个当前班级担任班主任,并不能说明具体班级。班主任同时保留 `SCHOOL_TEACHER`。需要判断具体班级时,使用下方 `querySchoolContactTeachers` 返回的 `isHeadTeacher`。
|
|
26
|
+
|
|
24
27
|
应用开发者不直接写 `roles` 绑定,也不在本地复制同步结果;下一次完整同步会自动增删平台身份绑定。
|
|
25
28
|
|
|
26
29
|
平台同步钉钉家校通讯录后,以只读关系返回监护人、学生和班级。关系两端都包含:
|
|
@@ -94,7 +97,7 @@ import {
|
|
|
94
97
|
OpenXiangdaOperation,
|
|
95
98
|
OpenXiangdaPlatformClient,
|
|
96
99
|
} from 'openxiangda-nest';
|
|
97
|
-
import { Controller, Get, Req } from '@nestjs/common';
|
|
100
|
+
import { Controller, Get, Param, Req } from '@nestjs/common';
|
|
98
101
|
import { appOperations } from '@app/contracts';
|
|
99
102
|
|
|
100
103
|
@Controller('/api/family')
|
|
@@ -111,18 +114,35 @@ export class FamilyController {
|
|
|
111
114
|
{ page: 1, pageSize: 50 }
|
|
112
115
|
);
|
|
113
116
|
}
|
|
117
|
+
|
|
118
|
+
@Get('/classes/:classId/head-teachers')
|
|
119
|
+
@OpenXiangdaOperation(appOperations.familyMine)
|
|
120
|
+
async headTeachers(
|
|
121
|
+
@Req() request: OpenXiangdaHttpRequest,
|
|
122
|
+
@Param('classId') classId: string,
|
|
123
|
+
) {
|
|
124
|
+
const context = request.openxiangda!;
|
|
125
|
+
return await this.platform.querySchoolContactTeachers(
|
|
126
|
+
context.authorization,
|
|
127
|
+
context.roleSessionId!,
|
|
128
|
+
{ classId, isHeadTeacher: true, page: 1, pageSize: 100 }
|
|
129
|
+
);
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
132
|
```
|
|
116
133
|
|
|
117
134
|
可用方法:
|
|
118
135
|
|
|
119
136
|
- `querySchoolContactRelations(authorization, roleSessionId, query)`:按平台用户 ID、手机号、钉钉 userid、姓名、班级或关系类型分页检索;
|
|
137
|
+
- `querySchoolContactTeachers(authorization, roleSessionId, query)`:按老师的平台用户 ID、手机号、钉钉 userid、姓名、班级和 `isHeadTeacher` 分页检索老师班级成员;
|
|
120
138
|
- `schoolContactChildren(..., userId, query)`:查询指定监护人的学生;
|
|
121
139
|
- `schoolContactGuardians(..., userId, query)`:查询指定学生的监护人;
|
|
122
140
|
- `mySchoolContactFamily(...)`:同时查询当前用户作为监护人和学生的两个方向。
|
|
123
141
|
|
|
124
142
|
应用前端用 `useOpenXiangdaAdmin().appApi()` 调用上面的 App API。不要把登录 token、RoleSession ID、平台 URL 或完整关系快照放入浏览器缓存;手机号只展示给确有业务需要的页面,日志不得记录手机号和完整返回体。
|
|
125
143
|
|
|
144
|
+
老师成员返回 `teacher`(`userId`、`dingtalkUserId`、`name`、可空 `mobile`)、`class`、布尔值 `isHeadTeacher`、`syncedAt` 和 `syncState`。查询某班班主任使用 `{ classId, isHeadTeacher: true }`;不要只用 `SCHOOL_HEAD_TEACHER` 推断班级。
|
|
145
|
+
|
|
126
146
|
## 失效与降级
|
|
127
147
|
|
|
128
148
|
- `sync.state = disabled`:租户未开启后续同步,历史关系仍可能存在;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda-skill-kit",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.41",
|
|
4
4
|
"description": "Validation and deterministic packaging for OpenXiangda 2.0 AI skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"openxiangda-devkit-core": "2.0.0-alpha.
|
|
24
|
+
"openxiangda-devkit-core": "2.0.0-alpha.29"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsx": "4.23.12",
|
|
@@ -43,6 +43,9 @@ For a new repository, link it to the target platform and let an authorized platf
|
|
|
43
43
|
`all` without a school-contact scope capability; narrower self/class access
|
|
44
44
|
must be an explicit authorization decision. The App API still requires its
|
|
45
45
|
normal business-operation capability.
|
|
46
|
+
- Treat `SCHOOL_HEAD_TEACHER` as a global platform identity only. Resolve the
|
|
47
|
+
exact class through `querySchoolContactTeachers(..., { isHeadTeacher: true })`;
|
|
48
|
+
never infer a class from the role code or call the platform from React.
|
|
46
49
|
|
|
47
50
|
## Delivery Gate
|
|
48
51
|
|
|
@@ -24,6 +24,10 @@ Build a normal NestJS service in `apps/server`. The platform deploys one contain
|
|
|
24
24
|
the NestJS backend with the verified authorization and RoleSession. Expose
|
|
25
25
|
only an application App API to the browser; never call the platform relation
|
|
26
26
|
endpoint from React.
|
|
27
|
+
10. A head teacher keeps `SCHOOL_TEACHER` and additionally receives
|
|
28
|
+
`SCHOOL_HEAD_TEACHER`. Use `querySchoolContactTeachers` and its
|
|
29
|
+
`isHeadTeacher` field for the exact class; the global role alone is not a
|
|
30
|
+
class authorization fact.
|
|
27
31
|
|
|
28
32
|
## Runtime Path
|
|
29
33
|
|
|
@@ -35,10 +35,13 @@ Model permissions as capability checks plus contextual data predicates. A role g
|
|
|
35
35
|
business-operation capability and rely on the platform query boundary, not
|
|
36
36
|
browser filtering.
|
|
37
37
|
- The synchronized platform identities `SCHOOL_GUARDIAN`, `SCHOOL_STUDENT`,
|
|
38
|
-
and `
|
|
38
|
+
`SCHOOL_TEACHER`, and `SCHOOL_HEAD_TEACHER` are additive `platformRoleCodes` on the trusted
|
|
39
39
|
Principal/RoleSession context, not selectable application `roleCodes`.
|
|
40
40
|
They are maintained by the platform sync and are unrestricted by default;
|
|
41
41
|
use them only for an explicit server-side identity check.
|
|
42
|
+
- `SCHOOL_HEAD_TEACHER` means the user heads at least one current class and is
|
|
43
|
+
additive to `SCHOOL_TEACHER`. Authorize the exact class from the platform
|
|
44
|
+
teacher-membership result `isHeadTeacher`, never from the global role alone.
|
|
42
45
|
- Apply field policies consistently to queries, search controls, forms, CSV
|
|
43
46
|
transfer and restricted transactions.
|
|
44
47
|
- Use the bounded server-side aggregate endpoint for charts and summaries. Only
|
|
@@ -55,4 +58,4 @@ Run `openxiangda check` and `openxiangda test` after every policy or schema chan
|
|
|
55
58
|
|
|
56
59
|
Read [Data and Authorization](../../docs/data-authz.md) for policy examples and
|
|
57
60
|
[School contact relations](../../docs/school-contact-relations.md) for the
|
|
58
|
-
guardian/student
|
|
61
|
+
guardian/student and teacher/class contracts.
|