Files
soul/app/api/ckb/match/route.ts
卡若 b60edb3d47 feat: 完整重构小程序匹配功能 + 修复UI对齐 + 文章数据API
主要更新:
1. 按H5网页端完全重构匹配功能(match页面)
   - 4种匹配类型: 创业合伙/资源对接/导师顾问/团队招募
   - 资源对接等类型弹出手机号/微信号输入框
   - 去掉重新匹配按钮,改为返回按钮

2. 修复所有卡片对齐和宽度问题
   - 目录页附录卡片居中
   - 首页阅读进度卡片满宽度
   - 我的页面菜单卡片对齐
   - 推广中心分享卡片统一宽度

3. 修复目录页图标和文字对齐
   - section-icon固定40rpx宽高
   - section-title与图标垂直居中

4. 更新真实完整文章标题(62篇)
   - 从book目录读取真实markdown文件名
   - 替换之前的简化标题

5. 新增文章数据API
   - /api/db/chapters - 获取完整书籍结构
   - 支持按ID获取单篇文章内容
2026-01-21 15:49:12 +08:00

132 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { type NextRequest, NextResponse } from "next/server"
import crypto from "crypto"
// 存客宝API配置
const CKB_API_KEY = process.env.CKB_API_KEY || "fyngh-ecy9h-qkdae-epwd5-rz6kd"
const CKB_API_URL = "https://ckbapi.quwanzhi.com/v1/api/scenarios"
// 生成签名 - 根据文档实现
function generateSign(params: Record<string, any>, apiKey: string): string {
// 1. 移除 sign、apiKey、portrait 字段
const filteredParams = { ...params }
delete filteredParams.sign
delete filteredParams.apiKey
delete filteredParams.portrait
// 2. 移除空值字段
const nonEmptyParams: Record<string, any> = {}
for (const [key, value] of Object.entries(filteredParams)) {
if (value !== null && value !== "") {
nonEmptyParams[key] = value
}
}
// 3. 按参数名升序排序
const sortedKeys = Object.keys(nonEmptyParams).sort()
// 4. 拼接参数值
const stringToSign = sortedKeys.map(key => nonEmptyParams[key]).join("")
// 5. 第一次 MD5
const firstMd5 = crypto.createHash("md5").update(stringToSign).digest("hex")
// 6. 拼接 apiKey 再次 MD5
const sign = crypto.createHash("md5").update(firstMd5 + apiKey).digest("hex")
return sign
}
// 匹配类型映射
const matchTypeMap: Record<string, string> = {
partner: "创业合伙",
investor: "资源对接",
mentor: "导师顾问",
team: "团队招募",
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { matchType, phone, wechat, userId, nickname, matchedUser } = body
// 验证必填参数 - 手机号或微信号至少一个
if (!phone && !wechat) {
return NextResponse.json({ success: false, message: "请提供手机号或微信号" }, { status: 400 })
}
// 生成时间戳(秒级)
const timestamp = Math.floor(Date.now() / 1000)
// 构建请求参数不包含sign
const requestParams: Record<string, any> = {
timestamp,
source: `创业实验-找伙伴匹配`,
tags: `找伙伴,${matchTypeMap[matchType] || "创业合伙"}`,
siteTags: "创业实验APP,匹配用户",
remark: `用户发起${matchTypeMap[matchType] || "创业合伙"}匹配`,
}
// 添加联系方式
if (phone) requestParams.phone = phone
if (wechat) requestParams.wechatId = wechat
if (nickname) requestParams.name = nickname
// 生成签名
const sign = generateSign(requestParams, CKB_API_KEY)
// 构建最终请求体
const requestBody = {
...requestParams,
apiKey: CKB_API_KEY,
sign,
portrait: {
type: 4, // 互动行为
source: 0, // 本站
sourceData: {
action: "match",
matchType: matchType,
matchLabel: matchTypeMap[matchType] || "创业合伙",
userId: userId || "",
matchedUserId: matchedUser?.id || "",
matchedUserNickname: matchedUser?.nickname || "",
matchScore: matchedUser?.matchScore || 0,
device: "webapp",
timestamp: new Date().toISOString(),
},
remark: `找伙伴匹配-${matchTypeMap[matchType] || "创业合伙"}`,
uniqueId: `soul_match_${phone || wechat}_${timestamp}`,
},
}
// 调用存客宝API
const response = await fetch(CKB_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
})
const result = await response.json()
if (result.code === 200) {
return NextResponse.json({
success: true,
message: "匹配记录已上报",
data: result.data,
})
} else {
console.error("CKB Match API Error:", result)
// 即使上报失败也返回成功,不影响用户体验
return NextResponse.json({
success: true,
message: "匹配成功",
})
}
} catch (error) {
console.error("存客宝匹配API调用失败:", error)
// 即使出错也返回成功,不影响用户体验
return NextResponse.json({ success: true, message: "匹配成功" })
}
}