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:
@@ -5,6 +5,7 @@ export type ProjectRow = {
|
||||
name: string;
|
||||
brand: string;
|
||||
main_group_member_ids: string;
|
||||
owner_userid: string;
|
||||
status: string;
|
||||
note: string;
|
||||
created_at: string;
|
||||
@@ -18,6 +19,7 @@ export type ProjectSummary = {
|
||||
note: string;
|
||||
createdAt: string;
|
||||
mainGroupMemberIds: string[];
|
||||
ownerUserid: string;
|
||||
taskCount: number;
|
||||
groupChatCount: number;
|
||||
};
|
||||
@@ -95,7 +97,7 @@ function parseJsonArray(value: string): string[] {
|
||||
export async function listProjects(db: DatabaseClient): Promise<ProjectSummary[]> {
|
||||
const result = await db
|
||||
.prepare(
|
||||
`SELECT p.id, p.name, p.brand, p.main_group_member_ids, p.status, p.note, p.created_at,
|
||||
`SELECT p.id, p.name, p.brand, p.main_group_member_ids, p.owner_userid, p.status, p.note, p.created_at,
|
||||
(SELECT COUNT(*) FROM tasks t WHERE t.project_id = p.id) AS task_count,
|
||||
(SELECT COUNT(*) FROM project_group_chats g WHERE g.project_id = p.id) AS group_chat_count
|
||||
FROM projects p
|
||||
@@ -110,6 +112,7 @@ export async function listProjects(db: DatabaseClient): Promise<ProjectSummary[]
|
||||
note: row.note,
|
||||
createdAt: row.created_at,
|
||||
mainGroupMemberIds: parseJsonArray(row.main_group_member_ids),
|
||||
ownerUserid: row.owner_userid ?? "",
|
||||
taskCount: Number(row.task_count),
|
||||
groupChatCount: Number(row.group_chat_count),
|
||||
}));
|
||||
@@ -122,7 +125,7 @@ export async function getProjectDetail(
|
||||
const id = text(projectId);
|
||||
const project = await db
|
||||
.prepare(
|
||||
`SELECT id, name, brand, main_group_member_ids, status, note, created_at
|
||||
`SELECT id, name, brand, main_group_member_ids, owner_userid, status, note, created_at
|
||||
FROM projects WHERE id = ?`,
|
||||
)
|
||||
.bind(id)
|
||||
@@ -170,6 +173,7 @@ export async function getProjectDetail(
|
||||
note: project.note,
|
||||
createdAt: project.created_at,
|
||||
mainGroupMemberIds: parseJsonArray(project.main_group_member_ids),
|
||||
ownerUserid: project.owner_userid ?? "",
|
||||
taskCount: tasks.results.length,
|
||||
groupChatCount: chats.results.length,
|
||||
},
|
||||
@@ -192,6 +196,7 @@ export type SaveProjectInput = {
|
||||
name?: unknown;
|
||||
brand?: unknown;
|
||||
mainGroupMemberIds?: unknown;
|
||||
ownerUserid?: unknown;
|
||||
note?: unknown;
|
||||
};
|
||||
|
||||
@@ -204,6 +209,7 @@ export async function saveProject(
|
||||
const brand = text(input.brand);
|
||||
const note = text(input.note ?? "");
|
||||
const memberIds = parseMemberIds(input.mainGroupMemberIds);
|
||||
const ownerUserid = text(input.ownerUserid).slice(0, 64);
|
||||
if (!name) throw new ProjectError("请填写项目名称");
|
||||
|
||||
const now = new Date().toISOString();
|
||||
@@ -212,10 +218,10 @@ export async function saveProject(
|
||||
const result = await db
|
||||
.prepare(
|
||||
`UPDATE projects
|
||||
SET name = ?, brand = ?, main_group_member_ids = ?, note = ?
|
||||
SET name = ?, brand = ?, main_group_member_ids = ?, owner_userid = ?, note = ?
|
||||
WHERE id = ?`,
|
||||
)
|
||||
.bind(name, brand, JSON.stringify(memberIds), note, id)
|
||||
.bind(name, brand, JSON.stringify(memberIds), ownerUserid, note, id)
|
||||
.run();
|
||||
if (result.meta.changes === 0) throw new ProjectError("项目不存在", 404);
|
||||
} else {
|
||||
@@ -223,14 +229,15 @@ export async function saveProject(
|
||||
await db
|
||||
.prepare(
|
||||
`INSERT INTO projects
|
||||
(id, name, brand, main_group_member_ids, status, note, created_at)
|
||||
VALUES (?, ?, ?, ?, 'active', ?, ?)`,
|
||||
(id, name, brand, main_group_member_ids, owner_userid, status, note, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, 'active', ?, ?)`,
|
||||
)
|
||||
.bind(
|
||||
projectId,
|
||||
name,
|
||||
brand,
|
||||
JSON.stringify(memberIds),
|
||||
ownerUserid,
|
||||
note,
|
||||
now,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user