feat: 项目负责人+内部任务群+portal 内部身份领取+群主链接归属
- 迁移 0012:projects.owner_userid / tasks.task_chat_id / claims.owner_partner_id - lib/task-group-service.ts:懒建 appchat 任务群(首个内部群主领取时创建,成员 diff 补员),播报/卡片文案 - wecom-client 新增 appchat create/update/get/send 封装 - OAuth ticket 增可选 uid(v:1 兼容),内部成员领取按 wecom_userid 解析群主身份 - 外部 OAuth 领取人 upsert 打 partner_type='group_owner' - portal 链接带 ?owner= 穿透 OAuth 往返,claims 记录归属群主 - 后台任务卡「群主链接」面板:复制链接/推送内部群主/任务群状态 - 催办与超期升级同步播报进任务群
This commit is contained in:
@@ -7,6 +7,11 @@ const routeSource = readFileSync(
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const portalSource = readFileSync(
|
||||
new URL("../koc-portal/app/page.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
test("partner route accepts identityTicket for wecom-backed claim", () => {
|
||||
assert.match(routeSource, /identityTicket\?: string/);
|
||||
assert.match(routeSource, /verifyWecomIdentityTicket/);
|
||||
@@ -33,6 +38,72 @@ test("claim with ticket upserts wecom_external_user_id for reminders", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("internal identity claim resolves the group_owner partner by wecom_userid", () => {
|
||||
assert.match(
|
||||
routeSource,
|
||||
/WHERE wecom_userid = \? AND partner_type = 'group_owner'/,
|
||||
);
|
||||
assert.match(routeSource, /未找到你的群主身份/);
|
||||
assert.match(
|
||||
routeSource,
|
||||
/claimantName = ownerPartner\.name \|\| internalUserid/,
|
||||
);
|
||||
});
|
||||
|
||||
test("external wecom claim upserts partner_type group_owner", () => {
|
||||
assert.match(
|
||||
routeSource,
|
||||
/wecom_external_user_id, partner_type\)\s*\n\s*VALUES \(\?, \?, \?, '外部KOC', 0, 0, \?, 'group_owner'\)/,
|
||||
);
|
||||
assert.match(
|
||||
routeSource,
|
||||
/partner_type = 'group_owner'`/,
|
||||
);
|
||||
});
|
||||
|
||||
test("claim records owner_partner_id after validating a group_owner partner", () => {
|
||||
assert.match(routeSource, /owner\?: string/);
|
||||
assert.match(
|
||||
routeSource,
|
||||
/SELECT id FROM partners WHERE id = \? AND partner_type = 'group_owner'/,
|
||||
);
|
||||
assert.match(
|
||||
routeSource,
|
||||
/INSERT INTO claims\s*\n\s*\(\s*id, task_id, partner_id, owner_partner_id, claimant_name, claim_token, quantity\)/,
|
||||
);
|
||||
});
|
||||
|
||||
test("claim schedules task group creation and broadcast in background", () => {
|
||||
assert.match(routeSource, /ensureTaskChatForMembers/);
|
||||
assert.match(routeSource, /taskClaimBroadcastText/);
|
||||
assert.match(routeSource, /taskChatIntroText/);
|
||||
assert.match(routeSource, /runInBackground\(/);
|
||||
assert.match(
|
||||
routeSource,
|
||||
/internalUserid \? \{ addUserids: \[internalUserid\] \} : \{\}/,
|
||||
);
|
||||
});
|
||||
|
||||
test("recover resolves internal identity via wecom_userid as well", () => {
|
||||
assert.match(
|
||||
routeSource,
|
||||
/wecomIdentity\?\.userId[\s\S]{0,200}WHERE wecom_userid = \? AND partner_type = 'group_owner'/,
|
||||
);
|
||||
});
|
||||
|
||||
test("portal preserves owner param through the oauth round trip and sends it with claim", () => {
|
||||
assert.match(portalSource, /owner: string;/);
|
||||
assert.match(portalSource, /compact\.set\("o", params\.owner\)/);
|
||||
assert.match(portalSource, /fallback\.set\("o", params\.owner\)/);
|
||||
assert.match(portalSource, /parsed\.get\("o"\) \?\? ""/);
|
||||
assert.match(portalSource, /params\.get\("owner"\) \?\? ""/);
|
||||
assert.match(
|
||||
portalSource,
|
||||
/ownerPartnerId \? \{ owner: ownerPartnerId \} : \{\}/,
|
||||
);
|
||||
assert.match(portalSource, /userType === "internal"/);
|
||||
});
|
||||
|
||||
test("recover builds dynamic partner candidates including wecom identity", () => {
|
||||
assert.match(
|
||||
routeSource,
|
||||
|
||||
Reference in New Issue
Block a user