feat: 任务发布到企微客户群(企业群发)与资源库单条新增
- 任务中心新增「发布到企微群」:同步客户群清单(wecom_group_chats)、 按群主分组创建企业群发任务(add_msg_template)、发送记录落库 wecom_group_pushes,迁移 mysql/0009;lib/wecom-client.ts 补 listCustomerGroupChats/createGroupMsgTemplate - KOC 资源库支持单条新增:app/api/resources-insert + lib/resource-write, 拆出资源写入公共逻辑供导入复用;0008 补合作方外部联系人字段 - 环境变量示例补企微凭证与 SEED_DEMO_DATA;next.config 增加 allowedDevOrigins;CLAUDE.md 补充项目说明 - .gitignore 排除 .codegraph/ 与 .ipynb_checkpoints/
This commit is contained in:
@@ -337,6 +337,59 @@ function normalizeRows(rows: string[][]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export type ManualResourceInput = {
|
||||
profileUrl: string;
|
||||
nickname?: string;
|
||||
publicAccountId?: string;
|
||||
ipLocation?: string;
|
||||
followers?: string;
|
||||
gender?: string;
|
||||
bio?: string;
|
||||
tags?: string | string[];
|
||||
cooperationSource?: string;
|
||||
};
|
||||
|
||||
export function buildManualRow(input: ManualResourceInput): ResourceImportRow {
|
||||
const rawProfileUrl = (input.profileUrl ?? "").trim();
|
||||
const profileUrl = normalizeProfileUrl(rawProfileUrl);
|
||||
const platform = platformFromProfileUrl(profileUrl);
|
||||
const parsedFollowers = parseResourceFollowers(input.followers ?? "");
|
||||
const parsedGender = normalizeResourceGender(input.gender ?? "");
|
||||
const tags = normalizeResourceTags(input.tags ?? "");
|
||||
const ipLocation = (input.ipLocation ?? "").trim();
|
||||
const errors: string[] = [];
|
||||
if (!rawProfileUrl) errors.push("账号主页不能为空");
|
||||
else if (!profileUrl) errors.push("账号主页链接格式不正确");
|
||||
else if (!platform) errors.push("当前自动解析仅支持小红书或抖音账号主页");
|
||||
if (!parsedFollowers.valid) {
|
||||
errors.push("粉丝数格式不正确,请填写数字或如 1.3万、10+");
|
||||
}
|
||||
if (!parsedGender.valid) {
|
||||
errors.push("性别格式不正确,请填写男、女或留空");
|
||||
}
|
||||
if (tags.length > 5) {
|
||||
errors.push("单个账号最多填写 5 个标签,请用逗号分隔");
|
||||
}
|
||||
if (ipLocation && /^\d+$/.test(ipLocation)) {
|
||||
errors.push("IP属地格式不正确,请填写省份、地区或国家名称");
|
||||
}
|
||||
return {
|
||||
rowNumber: 1,
|
||||
platform,
|
||||
nickname: (input.nickname ?? "").trim(),
|
||||
publicAccountId: (input.publicAccountId ?? "").trim(),
|
||||
profileUrl,
|
||||
ipLocation,
|
||||
followers: parsedFollowers.value,
|
||||
followersResolved: parsedFollowers.resolved,
|
||||
gender: parsedGender.value,
|
||||
bio: (input.bio ?? "").trim(),
|
||||
tags: tags.slice(0, 5),
|
||||
cooperationSource: (input.cooperationSource ?? "").trim(),
|
||||
errors,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseResourceImportFile(fileName: string, bytes: Uint8Array) {
|
||||
const extension = fileName.toLocaleLowerCase().split(".").pop();
|
||||
const workbooks =
|
||||
|
||||
Reference in New Issue
Block a user