feat: 人物编辑弹窗改为CKB计划选择下拉框
- 新增 GET /api/admin/ckb/plans 获取存客宝获客计划列表 - 新增 GET /api/admin/ckb/plan-detail 获取计划详情 - PersonAddEditModal: 密钥字段改为可搜索的计划选择器 选择计划后自动覆盖 greeting/tips/设备/时间等参数 - 删除"修复 CKB 密钥"按钮 Made-with: Cursor
This commit is contained in:
File diff suppressed because one or more lines are too long
1
soul-admin/dist/assets/index-BHf8KXmF.css
vendored
Normal file
1
soul-admin/dist/assets/index-BHf8KXmF.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
soul-admin/dist/assets/index-D9IazBEm.css
vendored
1
soul-admin/dist/assets/index-D9IazBEm.css
vendored
File diff suppressed because one or more lines are too long
4
soul-admin/dist/index.html
vendored
4
soul-admin/dist/index.html
vendored
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>管理后台 - Soul创业派对</title>
|
||||
<script type="module" crossorigin src="/assets/index-DxdnfQve.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D9IazBEm.css">
|
||||
<script type="module" crossorigin src="/assets/index-A95wVqFr.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BHf8KXmF.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -53,4 +53,35 @@ export function getPersonDetail(personId: string) {
|
||||
return get<PersonDetailResponse>(`/api/db/person?personId=${encodeURIComponent(personId)}`)
|
||||
}
|
||||
|
||||
export interface CkbPlan {
|
||||
id: number | string
|
||||
name: string
|
||||
apiKey?: string
|
||||
sceneId?: number
|
||||
scenario?: number
|
||||
enabled?: boolean
|
||||
greeting?: string
|
||||
tips?: string
|
||||
remarkType?: string
|
||||
remarkFormat?: string
|
||||
addInterval?: number
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
deviceGroups?: (number | string)[]
|
||||
}
|
||||
|
||||
export interface CkbPlansResponse {
|
||||
success?: boolean
|
||||
error?: string
|
||||
plans?: CkbPlan[]
|
||||
total?: number
|
||||
}
|
||||
|
||||
export function getCkbPlans(params?: { page?: number; limit?: number; keyword?: string }) {
|
||||
const search = new URLSearchParams()
|
||||
if (params?.page) search.set('page', String(params.page))
|
||||
if (params?.limit) search.set('limit', String(params.limit))
|
||||
if (params?.keyword?.trim()) search.set('keyword', params.keyword.trim())
|
||||
const qs = search.toString()
|
||||
return get<CkbPlansResponse>(qs ? `/api/admin/ckb/plans?${qs}` : '/api/admin/ckb/plans')
|
||||
}
|
||||
|
||||
@@ -2454,37 +2454,17 @@ export function ContentPage() {
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-xs text-gray-500">添加人物时同步创建存客宝场景获客计划,配置与存客宝 API 获客一致</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="border-amber-600 text-amber-400 hover:bg-amber-900/30 bg-transparent"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const res = await post<{ success?: boolean; fixed?: number; total?: number; results?: { personId: string; name: string; apiKey?: string; error?: string }[] }>('/api/db/persons/fix-ckb', {})
|
||||
if (res?.success) {
|
||||
alert(`修复完成:${res.fixed}/${res.total} 个人物已补充 CKB 密钥`)
|
||||
loadPersons()
|
||||
} else {
|
||||
alert('修复失败')
|
||||
}
|
||||
} catch { alert('修复请求失败') }
|
||||
}}
|
||||
>
|
||||
修复 CKB 密钥
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
onClick={() => {
|
||||
setEditingPerson(null)
|
||||
setPersonModalOpen(true)
|
||||
}}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
添加
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
onClick={() => {
|
||||
setEditingPerson(null)
|
||||
setPersonModalOpen(true)
|
||||
}}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
添加
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-1 max-h-[400px] overflow-y-auto">
|
||||
{persons.length > 0 && (
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import toast from '@/utils/toast'
|
||||
import { getCkbDevices, type CkbDevice } from '@/api/ckb'
|
||||
import { getCkbDevices, getCkbPlans, type CkbDevice, type CkbPlan } from '@/api/ckb'
|
||||
|
||||
export interface PersonFormData {
|
||||
personId: string
|
||||
@@ -93,6 +93,10 @@ export function PersonAddEditModal({
|
||||
const [deviceOptions, setDeviceOptions] = useState<CkbDevice[]>([])
|
||||
const [deviceLoading, setDeviceLoading] = useState(false)
|
||||
const [deviceKeyword, setDeviceKeyword] = useState('')
|
||||
const [planOptions, setPlanOptions] = useState<CkbPlan[]>([])
|
||||
const [planLoading, setPlanLoading] = useState(false)
|
||||
const [planKeyword, setPlanKeyword] = useState('')
|
||||
const [planDropdownOpen, setPlanDropdownOpen] = useState(false)
|
||||
/** 必填项校验错误,用于红色边框与提示 */
|
||||
const [errors, setErrors] = useState<{
|
||||
name?: string
|
||||
@@ -126,10 +130,13 @@ export function PersonAddEditModal({
|
||||
}
|
||||
setErrors({})
|
||||
|
||||
// 懒加载设备列表:仅在第一次需要时加载
|
||||
// 懒加载设备列表和计划列表
|
||||
if (deviceOptions.length === 0) {
|
||||
void loadDevices('')
|
||||
}
|
||||
if (planOptions.length === 0) {
|
||||
void loadPlans('')
|
||||
}
|
||||
}
|
||||
}, [open, editingPerson])
|
||||
|
||||
@@ -149,6 +156,44 @@ export function PersonAddEditModal({
|
||||
}
|
||||
}
|
||||
|
||||
const loadPlans = async (keyword: string) => {
|
||||
setPlanLoading(true)
|
||||
try {
|
||||
const res = await getCkbPlans({ page: 1, limit: 100, keyword })
|
||||
if (res?.success && Array.isArray(res.plans)) {
|
||||
setPlanOptions(res.plans)
|
||||
} else if (res?.error) {
|
||||
toast.error(res.error)
|
||||
}
|
||||
} catch {
|
||||
toast.error('加载计划列表失败')
|
||||
} finally {
|
||||
setPlanLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const selectPlan = (plan: CkbPlan) => {
|
||||
const dg = Array.isArray(plan.deviceGroups) ? plan.deviceGroups.map(String).join(',') : ''
|
||||
setForm(f => ({
|
||||
...f,
|
||||
ckbApiKey: plan.apiKey || '',
|
||||
greeting: plan.greeting || f.greeting,
|
||||
tips: plan.tips || f.tips,
|
||||
remarkType: plan.remarkType || f.remarkType,
|
||||
remarkFormat: plan.remarkFormat || f.remarkFormat,
|
||||
addFriendInterval: plan.addInterval || f.addFriendInterval,
|
||||
startTime: plan.startTime || f.startTime,
|
||||
endTime: plan.endTime || f.endTime,
|
||||
deviceGroups: dg || f.deviceGroups,
|
||||
}))
|
||||
setPlanDropdownOpen(false)
|
||||
toast.success(`已选择计划「${plan.name}」,参数已覆盖`)
|
||||
}
|
||||
|
||||
const filteredPlans = planKeyword.trim()
|
||||
? planOptions.filter(p => (p.name || '').includes(planKeyword.trim()) || String(p.id).includes(planKeyword.trim()))
|
||||
: planOptions
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const nextErrors: {
|
||||
name?: string
|
||||
@@ -256,16 +301,74 @@ export function PersonAddEditModal({
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
{/* 左列:计划密钥与设备 */}
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-gray-400 text-xs">存客宝密钥(计划 apiKey)</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
placeholder="创建计划成功后自动回填,不可手动修改"
|
||||
value={form.ckbApiKey}
|
||||
readOnly
|
||||
/>
|
||||
<div className="space-y-1.5 relative">
|
||||
<Label className="text-gray-400 text-xs">选择存客宝获客计划</Label>
|
||||
<div className="flex gap-2">
|
||||
<div
|
||||
className="flex-1 flex items-center bg-[#0a1628] border border-gray-700 rounded-md px-3 py-2 cursor-pointer hover:border-[#38bdac]/60 text-sm"
|
||||
onClick={() => setPlanDropdownOpen(!planDropdownOpen)}
|
||||
>
|
||||
{form.ckbApiKey ? (
|
||||
<span className="text-white truncate">
|
||||
{planOptions.find(p => p.apiKey === form.ckbApiKey)?.name || `密钥: ${form.ckbApiKey.slice(0, 20)}...`}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-500">点击选择已有计划 / 新建时自动创建</span>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-gray-600 text-gray-200 shrink-0"
|
||||
onClick={() => { void loadPlans(planKeyword); setPlanDropdownOpen(true) }}
|
||||
disabled={planLoading}
|
||||
>
|
||||
{planLoading ? '加载...' : '刷新'}
|
||||
</Button>
|
||||
</div>
|
||||
{planDropdownOpen && (
|
||||
<div className="absolute z-50 top-full left-0 right-0 mt-1 bg-[#0b1828] border border-gray-700 rounded-lg shadow-xl max-h-64 flex flex-col">
|
||||
<div className="p-2 border-b border-gray-700/60">
|
||||
<Input
|
||||
className="bg-[#050c18] border-gray-700 text-white h-8 text-xs"
|
||||
placeholder="搜索计划名称..."
|
||||
value={planKeyword}
|
||||
onChange={e => setPlanKeyword(e.target.value)}
|
||||
onKeyDown={e => { if (e.key === 'Enter') void loadPlans(planKeyword) }}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{filteredPlans.length === 0 ? (
|
||||
<div className="text-center py-4 text-gray-500 text-xs">
|
||||
{planLoading ? '加载中...' : '暂无计划'}
|
||||
</div>
|
||||
) : filteredPlans.map(plan => (
|
||||
<div
|
||||
key={String(plan.id)}
|
||||
className={`px-3 py-2 cursor-pointer hover:bg-[#38bdac]/10 text-sm flex items-center justify-between ${form.ckbApiKey === plan.apiKey ? 'bg-[#38bdac]/20 text-[#38bdac]' : 'text-white'}`}
|
||||
onClick={() => selectPlan(plan)}
|
||||
>
|
||||
<div className="truncate">
|
||||
<span className="font-medium">{plan.name}</span>
|
||||
<span className="text-xs text-gray-500 ml-2">ID:{String(plan.id)}</span>
|
||||
</div>
|
||||
{plan.enabled ? (
|
||||
<span className="text-[10px] text-green-400 bg-green-400/10 px-1.5 rounded shrink-0 ml-2">启用</span>
|
||||
) : (
|
||||
<span className="text-[10px] text-gray-500 bg-gray-500/10 px-1.5 rounded shrink-0 ml-2">停用</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="p-2 border-t border-gray-700/60 flex justify-end">
|
||||
<Button type="button" size="sm" variant="ghost" className="text-gray-400 h-7 text-xs" onClick={() => setPlanDropdownOpen(false)}>关闭</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-gray-500">
|
||||
由存客宝计划详情接口返回的 apiKey,用于小程序 @人物 时推送到对应获客计划。
|
||||
选择计划后自动覆盖下方参数。新建人物时若不选择则自动创建新计划。
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
|
||||
@@ -306,3 +306,117 @@ func AdminCKBDevices(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// AdminCKBPlans GET /api/admin/ckb/plans 管理端-存客宝获客计划列表
|
||||
func AdminCKBPlans(c *gin.Context) {
|
||||
token, err := ckbOpenGetToken()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
pageStr := c.DefaultQuery("page", "1")
|
||||
limitStr := c.DefaultQuery("limit", "50")
|
||||
keyword := c.Query("keyword")
|
||||
|
||||
values := url.Values{}
|
||||
values.Set("page", pageStr)
|
||||
values.Set("limit", limitStr)
|
||||
if keyword != "" {
|
||||
values.Set("keyword", keyword)
|
||||
}
|
||||
planURL := ckbOpenBaseURL + "/v1/plans?" + values.Encode()
|
||||
req, err := http.NewRequest(http.MethodGet, planURL, nil)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "构造请求失败"})
|
||||
return
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求存客宝计划列表失败"})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
|
||||
var parsed map[string]interface{}
|
||||
if err := json.Unmarshal(b, &parsed); err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "解析计划列表失败"})
|
||||
return
|
||||
}
|
||||
|
||||
var listAny interface{}
|
||||
if dataVal, ok := parsed["data"].(map[string]interface{}); ok {
|
||||
listAny = dataVal["list"]
|
||||
if _, ok := parsed["total"]; !ok {
|
||||
if tv, ok := dataVal["total"]; ok {
|
||||
parsed["total"] = tv
|
||||
}
|
||||
}
|
||||
} else if la, ok := parsed["list"]; ok {
|
||||
listAny = la
|
||||
}
|
||||
|
||||
plans := make([]map[string]interface{}, 0)
|
||||
if arr, ok := listAny.([]interface{}); ok {
|
||||
for _, item := range arr {
|
||||
if m, ok := item.(map[string]interface{}); ok {
|
||||
plans = append(plans, map[string]interface{}{
|
||||
"id": m["id"],
|
||||
"name": m["name"],
|
||||
"apiKey": m["apiKey"],
|
||||
"sceneId": m["sceneId"],
|
||||
"scenario": m["scenario"],
|
||||
"enabled": m["enabled"],
|
||||
"greeting": m["greeting"],
|
||||
"tips": m["tips"],
|
||||
"remarkType": m["remarkType"],
|
||||
"remarkFormat": m["remarkFormat"],
|
||||
"addInterval": m["addInterval"],
|
||||
"startTime": m["startTime"],
|
||||
"endTime": m["endTime"],
|
||||
"deviceGroups": m["deviceGroups"],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total := 0
|
||||
switch tv := parsed["total"].(type) {
|
||||
case float64:
|
||||
total = int(tv)
|
||||
case int:
|
||||
total = tv
|
||||
case string:
|
||||
if n, err := strconv.Atoi(tv); err == nil {
|
||||
total = n
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "plans": plans, "total": total})
|
||||
}
|
||||
|
||||
// AdminCKBPlanDetail GET /api/admin/ckb/plan-detail?planId=xxx 管理端-存客宝获客计划详情
|
||||
func AdminCKBPlanDetail(c *gin.Context) {
|
||||
planIDStr := c.Query("planId")
|
||||
if planIDStr == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 planId"})
|
||||
return
|
||||
}
|
||||
planID, _ := strconv.ParseInt(planIDStr, 10, 64)
|
||||
if planID <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "planId 无效"})
|
||||
return
|
||||
}
|
||||
token, err := ckbOpenGetToken()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
apiKey, err := ckbOpenGetPlanDetail(token, planID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "apiKey": apiKey})
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,8 @@ func Setup(cfg *config.Config) *gin.Engine {
|
||||
admin.POST("/referral-settings", handler.AdminReferralSettingsPost)
|
||||
// 存客宝开放 API 辅助接口:设备列表(供链接人与事选择设备)
|
||||
admin.GET("/ckb/devices", handler.AdminCKBDevices)
|
||||
admin.GET("/ckb/plans", handler.AdminCKBPlans)
|
||||
admin.GET("/ckb/plan-detail", handler.AdminCKBPlanDetail)
|
||||
admin.GET("/author-settings", handler.AdminAuthorSettingsGet)
|
||||
admin.POST("/author-settings", handler.AdminAuthorSettingsPost)
|
||||
admin.PUT("/orders/refund", handler.AdminOrderRefund)
|
||||
|
||||
Binary file not shown.
7
开发文档/1、需求/修改/20260315内容功能13.md
Normal file
7
开发文档/1、需求/修改/20260315内容功能13.md
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
功能一:
|
||||
这里话是可以选择密钥,是选择存克宝。密钥同时是选择纯科宝的上面这个 API 获客的那个场景获客的计划,并且这个场景符合计划,可以搜索的,可以直接选择是哪个计划,选择完之后可以调用相应的参数直接覆盖掉,再存个把,那这里都快修复,存克宝密钥,这个直接关掉删除。不要这个按钮。
|
||||
你可以调用存克宝的接口,里面调用相应的那个计划,然后直接可以选择下拉框,可以选择匹配,嗯,也可以直接新建。
|
||||
BIN
开发文档/1、需求/修改/images/2026-03-15-23-04-54.png
Normal file
BIN
开发文档/1、需求/修改/images/2026-03-15-23-04-54.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
BIN
开发文档/1、需求/修改/images/2026-03-15-23-05-29.png
Normal file
BIN
开发文档/1、需求/修改/images/2026-03-15-23-05-29.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 298 KiB |
@@ -81,6 +81,7 @@ python3 scripts/feishu_wiki_upload.py --title "文档标题" --content "内容"
|
||||
| `FEISHU_APP_ID` / `FEISHU_APP_SECRET` | tenant_access_token(API 调用) |
|
||||
| `FEISHU_WEBHOOK_SOUL_TEAM` | Soul 彩民团队群 webhook |
|
||||
| `FEISHU_WEBHOOK_CONTENT` | 内容推送群 webhook |
|
||||
| `FEISHU_WEBHOOK_PARTY_AI` | 派对AI开发群 webhook(复盘/迭代汇总发这里) |
|
||||
| `FEISHU_WIKI_NODE_TOKEN` | 知识库目标节点 |
|
||||
|
||||
飞书凭证配置见 `项目AI/config/.env`,`.env.feishu` 也兼容(`scripts/.env.feishu`)。
|
||||
|
||||
@@ -19,7 +19,7 @@ updated: "2026-03-15"
|
||||
|
||||
| 端 | 当前版本 | 状态 | 最后更新 |
|
||||
|:---|:---|:---|:---|
|
||||
| 小程序(C端) | **v1.3.3** | 已上传微信平台,待设体验版/提审 | 2026-03-15 |
|
||||
| 小程序(C端) | **v1.2.6** | 已上传微信平台,待设体验版/提审 | 2026-03-15 |
|
||||
| API 后端 | soul-api | 运行中 | 2026-03-15 |
|
||||
| 管理端 | soul-admin | 运行中 | 2026-03-14 |
|
||||
|
||||
@@ -155,9 +155,7 @@ bash 从GitHub下载最新_devlop.sh
|
||||
|
||||
| 版本 | 日期 | 变更 |
|
||||
|:---|:---|:---|
|
||||
| v1.3.3 | 2026-03-15 | 修复上下篇溢出、删除退款按钮、地球一键复制文案、分享按钮纵向布局 |
|
||||
| v1.3.2 | 2026-03-15 | 修复@提及点击、@纯文本解析、上下篇排序 |
|
||||
| v1.3.1 | 2026-03-15 | 支付修复(代付分享/充值)、退款原路返回 |
|
||||
| v1.2.6 | 2026-03-15 | 支付全链路修复、@提及解析修复、上下篇排序修复、样式溢出修复、删除退款按钮、地球一键复制文案、分享按钮纵向布局 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -128,7 +128,22 @@ Soul创业实验AI(魂AI)
|
||||
- 已完成需求:`开发文档/1、需求/已完成/`
|
||||
- 完成后由魂AI 负责将文档移到 `已完成/`
|
||||
|
||||
### 7.4 与卡若AI 的协同
|
||||
### 7.4 飞书群复盘推送(强制)
|
||||
|
||||
每次完成开发迭代后,**必须将复盘推送到派对AI开发群**:
|
||||
|
||||
- **派对AI开发群 Webhook**:`https://open.feishu.cn/open-apis/bot/v2/hook/c558df98-e13a-419f-a3c0-7e428d15f494`
|
||||
- 格式:飞书 post 富文本,包含完成清单、技术成果、版本状态、下一步
|
||||
- 所有派对AI开发的功能完成后的复盘、迭代汇总,统一发到此群
|
||||
|
||||
推送命令模板:
|
||||
```bash
|
||||
curl -s -X POST "https://open.feishu.cn/open-apis/bot/v2/hook/c558df98-e13a-419f-a3c0-7e428d15f494" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"msg_type":"post","content":{"post":{"zh_cn":{"title":"标题","content":[[{"tag":"text","text":"内容"}]]}}}}'
|
||||
```
|
||||
|
||||
### 7.5 与卡若AI 的协同
|
||||
|
||||
- 派对AI 跑通的确定性能力,沉淀到卡若AI 经验库:`卡若AI/02_卡人(水)/水溪_整理归档/经验库/待沉淀/`
|
||||
- 可复制的模式(CLI上传小程序、Go后端部署、支付链路等)供卡若AI 其他项目复用
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
- **获取**:
|
||||
1. 飞书群 → 设置 → 群机器人 → 添加机器人 → 自定义机器人
|
||||
2. 复制 Webhook 地址
|
||||
- **配置键**:`FEISHU_WEBHOOK_SOUL_TEAM`(Soul团队群)、`FEISHU_WEBHOOK_CONTENT`(内容推送群)
|
||||
- **当前默认**:`https://open.feishu.cn/open-apis/bot/v2/hook/34b762fc-5b9b-4abb-a05a-96c8fb9599f1`
|
||||
- **配置键**:`FEISHU_WEBHOOK_SOUL_TEAM`(Soul团队群)、`FEISHU_WEBHOOK_CONTENT`(内容推送群)、`FEISHU_WEBHOOK_PARTY_AI`(派对AI开发群)
|
||||
- **Soul团队群**:`https://open.feishu.cn/open-apis/bot/v2/hook/34b762fc-5b9b-4abb-a05a-96c8fb9599f1`
|
||||
- **派对AI开发群(主群,复盘/迭代汇总发这里)**:`https://open.feishu.cn/open-apis/bot/v2/hook/c558df98-e13a-419f-a3c0-7e428d15f494`
|
||||
|
||||
### 1.3 Wiki Node Token(长期有效)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user