platform.ciyuanfun.cn

API v1.0.0 · PX兑换枢纽 · 统一身份认证 · 语音合成 · 图片/视频生成 · 渠道商管理 · 自定义模型(BYOK) · ← 返回首页

快速开始

所有 API 通过 HTTPS 访问,认证方式:Bearer Token(用户API)或 HMAC-SHA256 签名(开放平台 API)。

# Bearer Token 示例
curl -H "Authorization: Bearer <token>" \
  https://platform.ciyuanfun.cn/api/v1/org/stories

# HMAC-SHA256 签名(开放平台 /open/v1/*)
# 参见下方"签名算法"

🔐 认证鉴权

POST/api/v1/auth/register注册新用户详情
Body: {"username","password","display_name"}
curl -X POST https://platform.ciyuanfun.cn/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"123456","display_name":"测试"}'
POST/api/v1/auth/login登录获取 Token Pair详情
Body: {"username","password"}
curl -X POST https://platform.ciyuanfun.cn/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"test","password":"123456"}'
POST/api/v1/auth/refresh刷新 Access Token详情
GET/api/v1/users/profile当前用户信息(Bearer)详情
POST/api/v1/users/logout登出撤销 Token
GET/oauth/authorizeOAuth 2.0 授权码
POST/oauth/tokenOAuth Token 端点
GET/.well-known/openid-configurationOIDC 发现文档
GET/api/v1/.well-known/jwks.jsonJWT 公钥
GET/health服务健康检查

🎤 声音克隆 & TTS

POST/api/v1/org/voices/clone阿里云声音复刻详情
上传音频 → 克隆声音 → 获取 voice_id。时长 10-60 秒。
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/voices/clone \
  -H "Authorization: Bearer <token>" \
  -F "audio=@sample.wav" \
  -F "name=my_voice"
POST/api/v1/org/voices/upload上传音频(≤10MB)
GET/api/v1/org/voices音色列表
POST/api/v1/org/voices创建音色
PUT/api/v1/org/voices/:id更新音色
DEL/api/v1/org/voices/:id删除音色
POST/api/v1/org/tts/synthesize文本合成语音详情
模型: qwen3-tts-vc-2026-01-22
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/tts/synthesize \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"text":"你好世界","voice_id":"xxx"}'
GET/api/v1/org/tts/voices可用 TTS 音色
GET/api/v1/org/voices/file/:filename音频文件服务

🖼️ AI 图片生成(文生图)

POST/api/v1/org/generate/image文生图(火山 Seedream 直连 + 阿里 Qwen-image,同步返回,扣积分+失败自动退款)详情
Body: {"prompt","model","quality","size","category"};category 可选(general/character/scene/prop/voice),声明这张图的用途,落库进 cy_assets.category,缺省 general;
模型(括号内为档位/积分):seedream-5.0-flash(2k·3k·4k / 2 分,最便宜,默认)、seedream-5.0-lite(2k·3k·4k / 3 分)、seedream-4.5(2K·4K / 3 分)、seedream-4.0(标准档 / 3 分)、qwen-image-3.0(标准档 1k / 3 分,阿里百炼 DashScope);
历史别名 qwen-image 仍兼容,服务端归一为 seedream-5.0-flash;
各档价格 = ceil(厂价×1.2×10),以 GET /org/pricing/image-models 实际返回为准;size 支持 1:1/4:3/3:4/16:9/9:16/3:2/2:3。返回 asset_id + 图片 URL;生成或保存失败自动退款。
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/generate/image \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"prompt":"海边日落","model":"seedream-4.5","quality":"2K","size":"16:9"}'
GET/api/v1/org/assets素材库列表(分页)详情
参数: page/size/type(image/audio/video)/cat(general/character/scene/prop/voice/none);
返回 data 素材数组(id/name/asset_type/file_size/tags/channel_id 等)。
分类口径:cat 过滤 category 列(2026-11-02 起分类独立成列,不再解析 tags JSON;AI 生成图的历史 tags 是数组,原先永远归不了类);none = category IS NULL。
返回体含 category 字段,前端可直接渲染分类徽标/下拉。
文件经 /org/assets/file/:id 访问。
GET/api/v1/org/assets/file/:id素材文件服务(图片/音频/视频)详情
直接返回文件字节,可用于 <img>/<video> 直链;
支持 Range(返回 206),视频可拖进度条;
若入库 MIME 为空或 application/octet-stream,服务端按扩展名兜底(如 .mp4→video/mp4),避免视频播不出来;
带签名参数 ?sig=&exp= 时按签名校验(供第三方 AI 拉图)。
POST/api/v1/org/assets上传素材(图片/音频/视频)详情
multipart/form-data:file 文件、name 名称、asset_type(image/audio/video,缺省按 MIME 嗅探与扩展名推断,且实际类型优先:客户端传错会被纠正,如 mp4 标成 image 会落 video)、category(general/character/scene/prop/voice,可选;不传则回退解析 tags.$.category)、tags JSON 串;
返回 data.id 与 download_url(即 /org/assets/file/:id),可直接用于分镜视频挂载;
单文件受 nginx client_max_body_size 50m 限制,超限会返回 413(HTML,非 JSON)。
PATCH/api/v1/org/assets/:id修改素材(名称 / 描述 / 分类)详情
Body(三者皆可选,至少给一个):{"name":"...","description":"...","category":"scene"};
只能改自己的资产:非本人返回 403,不存在返回 404;
category 限 general/character/scene/prop/voice,传空串 "" 表示清除分类(回到「未分类」),传其它值返回 400;
前端素材卡片上的分类下拉就是调这个接口,改完立即生效,无需重启。
返回 {"data":{"id":N,"category":"scene"}}。
curl -X PATCH https://platform.ciyuanfun.cn/api/v1/org/assets/100 \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"category":"scene"}'
GET/api/v1/org/pricing/image-models图片模型与档位定价详情
返回启用中的图片模型列表(id/name/qualities[2K/3K/4K 各档价格]/sizes[支持尺寸]),前端据此渲染生图模型下拉与尺寸选项。
GET/api/v1/org/pricing/video-models视频模型与分辨率定价详情
返回视频模型列表(id/name/rule[480p/720p/1080p 每秒积分]/sizes[尺寸]/modes[模式: text2video/multi_ref/first_last_frame]/durations[时长]),前端据此渲染视频工坊模型/清晰度/尺寸/时长下拉与费用预估。

🔌 模型通道管理

2026-11-01(P192–P193)落地:把模型配置从代码搬进数据库。一条通道 = 某个模型的一条实际出路(厂商 + 接入点 + 密钥 + 协议模板 + 启停 + 优先级)。停用即拒单,不回落硬编码;新增模型时勾「自动算价」可一条龙建价。管理端入口:admin.ciyuanfun.cn → 定价/模型 → 通道管理。

GET/api/v1/admin/model-channels通道列表详情
参数: kind(image/video,可选);
返回 data.list:id/kind/model_key/platform_alias/provider/mode/base_url/model_id/endpoint_id/credential_id/protocol_id/enabled/priority/price_hint/extra;
platform_alias 是对外请求用的模型名(如 seedream-5.0-flash),model_key 是定价主键,两者可能不同。
POST/api/v1/admin/model-channels新增 / 更新通道(一条龙建价)详情
Body(带 id 即更新,否则新增):
{
  "kind": "image",                       // 必填 image|video
  "model_key": "seedream-5.0-flash",     // 必填,定价主键
  "platform_alias": "seedream-5.0-flash",// 对外模型名
  "provider": "volc",                    // 必填
  "mode": "native",                      // 缺省 native
  "model_id": "doubao-seedream-5-0-flash-260915", // 必填
  "endpoint_id": "ep-2026...",           // 火山接入点
  "base_url": "https://ark.cn-beijing.volces.com/api/v3",
  "credential_id": 3,
  "protocol_id": 1,                      // 必填,须与 kind 同模态
  "enabled": 1, "priority": 0,
  "price_hint": 0.12,
  "extra": {"key_env":"VOLC_IMG_API_KEY_FLASH","test_quality":"2k"},
  "auto_price": true,                    // 勾了才建价
  "factory_cost": 0.12,                  // 厂价(元/张),auto_price=true 时必填
  "pricing_name": "Seedream 5.0 Flash",  // 定价表显示名
  "qualities": [{"value":"2k","credits_per_image":2}]
}
自动算价:公布价(积分) = ceil(厂价(元) × 1.2 × 10),例 0.12 元→2 分、0.22 元→3 分;写入/更新定价表后返回 auto_credits。
校验顺序:kind 合法 → 三要素必填 → auto_price 必须有厂价 → 建价 → 有路无价检查 → 协议模板存在且同模态 → 密钥存在。
返回 {"data":"ok","id":N,"auto_credits":2}。
DEL/api/v1/admin/model-channels/:id删除通道(默认只停用)详情
不带参数:软停用,置 enabled=0,立即拒单;
带 ?purge=1:硬删除。若 cy_assets.channel_id 仍引用该通道,返回 409 并附 assets 数量,提示改为停用;
两种情况都会清空通道缓存,无需重启。
POST/api/v1/admin/model-channels/:id/test测试连通(真发一次请求,会产生费用)详情
Body(全部可选):{"prompt":"一只橘猫,写实摄影","ratio":"1:1","quality":"2k"};
quality 默认取通道 extra.test_quality,再兜底 2k —— seedream 5.0-lite / 4.5 上游要求 size ≥ 3686400 像素,写死 standard(1024²) 必被拒,会误判「通道坏了」;4.0 这类老模型则要在 extra 里写 test_quality:"standard"。
限流:同一通道 60 秒一次(429);本期只支持 kind=image,video 返回 400。
返回实际出图 URL、耗时、宽高。
GET/api/v1/admin/channel-protocols协议模板列表详情
返回可用协议模板:id/code/name/kind/is_builtin + submit(提交 URL/方法/headers/body 模板 + 超时)、submit_parse(响应字段映射,如 data[0].url)、query/query_parse(异步查询,图片同步为空)。
内置模板:volc_image「火山方舟 文生图(同步)」→ {{base_url}}/images/generations。
GET/api/v1/admin/model-catalog厂商 & 模型预置清单详情
新增模型弹窗第 1 段的数据源。参数 kind(image/video) 可过滤。
返回 data.list:[{code,label,base_url,protocol_code,models:[{kind,platform_alias,display_name,model_id,endpoint_id,factory_cost,published_credits,qualities,size_options,key_env,fallback_key_env,credential_note,test_quality,note}]}]
同返回 pricing_rule:markup=1.2、credits_per_yuan=10、formula 与算例,前端据此实时显示「厂价 → 公布价」。
只登记实测跑通的模型;百炼 qwen-image-3.0 仅登记(协议未适配)。
GET/api/v1/admin/model-credentials密钥列表(只回掩码)详情
返回 id/name/provider/masked/enabled/created_at;不返回明文,只给 masked(如 ark-5a08****)供辨认。
POST/api/v1/admin/model-credentials新增 / 轮换密钥(AES-256-GCM 加密入库)详情
Body: {"name":"volc-flash","provider":"volc","secret":"ark-xxxx"},三者必填;
name 唯一:同名即覆盖并重新启用(用于轮换密钥,不必先删);
加密用 MAM_VERIFY_KEK_FILE 指向的 KEK,缺失会返回 500 并提示检查该环境变量;
返回 {"data":{"name":...,"masked":...}}。密钥落库后通道按 credential_id 引用,业务代码不接触明文。
DEL/api/v1/admin/model-credentials/:id删除密钥详情
被任一通道引用时拒绝删除(先改通道的 credential_id);删除后不可恢复。

三张表:cy_model_channels(路)、cy_model_credentials(密钥,secret_enc 密文 + masked 掩码)、cy_channel_protocols(协议模板)。
缓存:增删改通道后服务端自动 InvalidateChannelCache(),无需重启;但 gateway_allocations 类配置仍需重启。

🎬 视频生成

POST/api/v1/org/videos视频生成(文生/多图参考/首尾帧)详情
异步任务,通过 List 查询状态。
模型: seedance-2.0 / seedance-2.0-fast / seedance-2.0-mini(火山方舟,支持文生/多图参考/首尾帧)、happyhorse(阿里百炼,仅多图参考);
模式 mode: text2video文生视频(默认)/ multi_ref多图参考 / first_last_frame首尾帧;
分辨率: resolution 480p/720p/1080p;时长: duration 2-12 秒整数(选几秒出几秒);比例: ratio 16:9/9:16/1:1/4:3/3:4/21:9/adaptive(HappyHorse 仅固定比例,adaptive 自动转 16:9)。
图生视频(multi_ref / first_last_frame)传 ref_images(公网 HTTP 图片 URL:多图参考 1-9 张;首尾帧第 1 张=首帧必填、第 2 张=尾帧可选;如素材库 /api/v1/org/assets/file/:id)。
状态: queued排队 / running生成中 / succeeded完成 / failed失败(含原因)
扣费按 模型单价×时长;失败自动退款(余额回加+流水 video_gen_refund)。
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/videos \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"海浪拍打礁石","model":"seedance-2.0-mini","resolution":"480p","duration":4,"ratio":"16:9"}'
GET/api/v1/org/videos视频任务列表
DEL/api/v1/org/videos/:id删除视频

📥 导入解析(图片 OCR / 音视频转写 / 扫描件 PDF)

POST/api/v1/org/import/extractAI 解析提取(Bearer)详情
multipart/form-data,字段名 file;文件类型由 magic bytes 判定,不认扩展名。
限额:图片 10MB(png/jpg)/ 视频 50MB(mp4/mov)/ 音频 20MB(m4a/mp3/wav)/ 视频时长 600s / 每用户每日 30 次 / 单用户 1 并发。
模型:图片 qwen-vl-ocr(base64 data URL,prompt 固定为 "OCR");音视频 paraformer-v2(DashScope 临时 OSS 上传 + 异步任务,ffmpeg 抽 16k 单声道音轨)。
响应:成功 {"success":true,"kind":"image|video|audio","text":"...","chars":79};无人声 {"success":true,"noSpeech":true}。
错误码:415 UNSUPPORTED_TYPE / 413 TOO_LARGE / 400 NO_AUDIO / 429 BUSY / 429 RATE_LIMIT / 400 USE_FRONTEND_PDF。
# 图片 OCR
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/import/extract \
  -H "Authorization: Bearer <token>" \
  -F "file=@cover.png"

# 视频转写
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/import/extract \
  -H "Authorization: Bearer <token>" \
  -F "file=@clip.mp4"

📄 本地解析(前端完成,不占额度)

LOCALTXT / RTF / DOCX / PDF(文本层)MaM 导入页内置解析器详情
TXT/RTF 按编码直读;DOCX 解包 word/document.xml;PDF 用 pdf.js 抽文字层,文字量 ≥20 字判定为文本 PDF。
若文字层为空(扫描件),前端把每页渲染到 canvas(scale=2 → JPEG 0.85)后逐页调用上面的 /extract,每批 10 页。

📖 故事 & 分镜(含自动配音合成)

POST/api/v1/org/stories创建故事(自动解析剧本)详情
剧本格式:"角色名"对白内容 自动拆分为分镜
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/stories \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"title":"新故事","script":"场景1描述...\\n\\"我\\"这是我的对白"}'
GET/api/v1/org/stories故事列表
GET/api/v1/org/stories/:id故事详情(含分镜)
PUT/api/v1/org/stories/:id/panels/:pid更新分镜
POST/api/v1/org/stories/:id/panels新增分镜
POST/api/v1/org/stories/:id/panels/insert-video插入分镜视频(素材库选片 / 本地上传)详情
两种入参二选一:
① 本地上传(multipart/form-data):video=视频文件、after=分镜序号;
② 素材库选片(application/json):{"video_url":"/api/v1/org/assets/file/26","after":1},只引用 URL 不转存。
只接受视频:video_url 仅放行 http(s):// 与站内 / 开头地址(拦截 javascript:/data:///外域),否则 400;
校验作品归属,非本人故事返回 403;
成功后在 after 指定分镜之后新建分镜并挂该视频,返回新分镜 id/panel_no/video_url。
curl -X POST https://platform.ciyuanfun.cn/api/v1/org/stories/17/panels/insert-video \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"video_url":"/api/v1/org/assets/file/26","after":1}'
DEL/api/v1/org/stories/:id/panels/:pid删除分镜
PUT/api/v1/org/stories/:id/panels/reorder重排分镜顺序
GET/api/v1/org/stories/:id/video🎬 生成故事视频(异步)详情
提交全部分镜到 Seedance 生成视频,后台自动配音
支持参数: duration(自动/5/10/15)、ratio(16:9/9:16/1:1)
curl "https://platform.ciyuanfun.cn/api/v1/org/stories/17/video?duration=auto&ratio=16:9&resolution=720p" \
  -H "Authorization: Bearer <token>"
GET/api/v1/org/stories/:id/video/status视频生成状态
GET/api/v1/org/stories/:id/video/concat🎬 分镜拼接(本地 ffmpeg 合并,不耗 Seedance token)详情
按分镜顺序把各分镜视频合并为成片;有自定义视频的分镜优先用自定义视频,否则取该分镜的生成视频。
支持参数: panel_nos(逗号分隔的分镜序号,控制拼接顺序,如 4,3,2,1 倒序;缺省按序号升序)
curl "https://platform.ciyuanfun.cn/api/v1/org/stories/22/video/concat?panel_nos=1,2,3,4" \
  -H "Authorization: Bearer <token>"
GET/api/v1/org/stories/:id/tts生成故事配音
GET/api/v1/org/stories/:id/audio查询配音状态
POST/api/v1/org/stories/:id/audio/mix配音混入视频

🗂️ 项目组(作品分组)

把「我的作品」里的故事按系列 / 客户归入项目组。参照 aigc iw_comic_groups 的分组语义,只补分组层,不搬漫剧四步向导;一个作品只属于一个组,删除项目组只解绑,绝不删作品。P222 起项目组升级为协作单元:全站成员可自由加入,组内成员互见素材与作品,详见「🧩 项目组协作」。

GET/api/v1/org/project-groups我的项目组列表(page / page_size / keyword / status,含 story_count)
POST/api/v1/org/project-groups新建项目组(name 必填;description / cover 可选)
GET/api/v1/org/project-groups/:groupId项目组详情
PUT/api/v1/org/project-groups/:groupId重命名 / 改描述 / 改封面 / 归档(status=0) 或恢复(status=1)
DEL/api/v1/org/project-groups/:groupId删除项目组:软删组 + 解绑其下作品,返回 ungrouped 数量
GET/api/v1/org/project-groups/:groupId/stories组内作品列表
PUT/api/v1/org/project-groups/:groupId/stories/:storyId作品归入 / 移动到该组(校验作品归属)
DEL/api/v1/org/project-groups/:groupId/stories/:storyId把作品移出该组(变未分组)
GET/api/v1/admin/project-groups后台项目组列表(X-Mam-Admin-Key / Bearer;page / page_size / keyword / user_id / status)
GET/api/v1/admin/project-groups/:groupId/stories后台查看组内作品
POST/api/v1/admin/project-groupsP221 超管代建组(user_id / name / description)
PUT/api/v1/admin/project-groups/:groupIdP221 超管改名 / 描述 / 归档恢复(name / description / status)
DEL/api/v1/admin/project-groups/:groupIdP221 超管删组:软删 + 解绑作品 + 组内素材回私有,绝不删作品/素材
GET/api/v1/admin/project-groups/:groupId/assetsP221 组内六类素材 + 已归组作品
GET/api/v1/admin/project-groups/:groupId/membersP222 后台成员清单(列表字段含 member_count)
POST/api/v1/admin/project-groups/:groupId/membersP222 后台添加成员 {user_id}
DEL/api/v1/admin/project-groups/:groupId/members/:userIdP222 后台移除成员(不删资产)

数据模型与约定

cy_project_groups(group_id = cg_YYYYMMDD_xxxx、user_id、name、description、cover、status、is_deleted)+ cy_project_group_items(uk_story 唯一键保证一作品一组)+ P222 cy_project_group_members(成员关系)。不改 cy_stories 表结构,避免打断它的 _bak_* 备份表。

GET /api/v1/org/stories 新增可选 group_id / ungrouped=1 / limit≤100(默认仍 LIMIT 20),每条返回 group_id / group_name / owner_id / shared;POST /api/v1/org/stories 可带 group_id 建完直接归组(P222 放宽为「我创建或已加入的组」)。

所有查询恒带 user_id,跨用户一律 404;封面 URL 只放行站内 / 或 https。用户端入口:我的作品 → 新建项目组 / 分组条 / 卡片「移动到项目组」;后台入口:项目组(P221 起完整管理;P222 增成员查 / 加 / 移除)。

🧩 项目组协作(成员可见)

项目组由首提者建立并任组长,全站登录成员可自由选择加入;组内成员可见彼此归到该组的六类素材 + 作品(只读可见可复用,写 / 删仍限属主)。创建素材 / 故事时在编辑框里直接选归属,默认「私有」,下拉分「我的项目组」(我创建 / 已加入)与「全站项目组」(选中后自动加入)两组,右侧「+」可就地新建组;故事表单只列私有 / 项目组(作品无 global 语义)。P223 起全站正常项目组在创建弹窗里直接可选,选中即自动加入并归属。

GET/api/v1/org/project-groups-discover全站项目组浏览(keyword;返回 joined / role / member_count / is_owner / owner_name)
POST/api/v1/org/project-groups/:groupId/join自由加入(幂等;归档组 400;首提者自动为 owner)
DEL/api/v1/org/project-groups/:groupId/leave退出(组长 400;只把本人归到该组的素材回私有、作品解绑)
GET/api/v1/org/project-groups/:groupId/members成员列表(仅有效组员可见,非组员 404)
DEL/api/v1/org/project-groups/:groupId/members/:userId组长移除成员(不能移组长;同样只回收该成员自己的归属)
GET/api/v1/admin/project-groups/:groupId/members后台成员清单(X-Mam-Admin-Key / Bearer)
POST/api/v1/admin/project-groups/:groupId/members后台添加成员 {user_id}(幂等;校验用户与组存在)
DEL/api/v1/admin/project-groups/:groupId/members/:userId后台移除成员(不能移组长,返回最新成员列表)

数据模型与口径

cy_project_group_members(uk_group_user(group_id,user_id)、role=owner/member、status)独立成表,不改 cy_project_groups / cy_stories / 任何素材表;建组即补 owner 行,存量组按 user_id 幂等回填。GET /org/asset-scopes/options 的 groups 仍=我创建的 ∪ 我已加入的(供归属筛选 / 单条 / 批量弹窗消费),并返回 role;P223 新增 joinable =全站 status=1、非我创建且我未加入的组(含 member_count / owner_name),只在创建弹窗渲染。选中未加入组时前端先 POST /join(幂等+审计)再落归属,故事创建链路同样前置 join。P224 起,所有创建弹窗在打开瞬间强制刷新候选组,登录 / 建组 / 加入后的状态变化无需刷新页面即可在下拉里生效。

列表可见性 = (自己名下) OR (scope='global') OR (scope='group' AND 我 status=1 在该组成员表);作品列表按 cy_project_group_items JOIN 成员表,返回 owner_id / shared。只有 global 与 group 改变可见性,work 仍是归类筛选。文件读取对 group 素材要求属主 / 有效组员 / sig 签名链接,其余维持历史行为。

归档组不可加入、组长不可退出、组长不可被移除;退出 / 移除不删任何资产,只把当事人自己的归组映射与作品解绑。所有写操作落 audit_logs:project_group_join / leave / member_remove / member_admin_add / member_admin_remove。后台入口:🗂️ 项目组 → 成员(查 / 加 / 移除)。

🎯 素材归属与可见范围(六类全覆盖)

素材库 6 个 tab(角色库 / 场景库 / 道具库 / 音色库 / 视频库 / 素材超市)与作品统一支持四级互斥单选归属:private 私有(默认)、global 全局(全站登录用户可见)、group 项目组、work 作品。P222 起 global 与 group 都改变可见性(global=全站登录可见;group=组内有效成员可见);work 仍是归类与筛选维度。

归属独立存 cy_asset_scopes(uk_asset(asset_kind,asset_id)),不改任何原素材表结构;无映射记录即私有,因此存量素材零改动、零曝光。存量归属由组创建者 / 属主自主决定,系统不做批量迁移。

GET/api/v1/org/asset-scopes/options候选:我的项目组 groups + 我的作品 stories
GET/api/v1/org/asset-scopes单条归属(kind / id;返回 scope / group_id / story_id / editable)
GET/api/v1/org/asset-scopes/list归属总览(我名下 + 全站 global;page / size / kind / scope)
PUT/api/v1/org/asset-scopes/:kind/:id设置单条归属(scope 必填;group 带 group_id、work 带 story_id;仅属主可写)
POST/api/v1/org/asset-scopes/batch批量设置(1~100 条;返回 ok / fail,非本人素材计入 fail)
GET/api/v1/org/asset-scopes?…六类列表统一支持 scope / group_id / story_id 筛选
GET/api/v1/org/assets/file/:id文件读取改 OptionalAuth:global 未登录 403、登录或 ?token= 200;保留 sig 签名链接给第三方 AI 拉图
GET/api/v1/admin/asset-scopes超管全站归属清单(kind / scope / group_id / user_id / page_size)
PUT/api/v1/admin/asset-scopes/:kind/:id超管设为 global 或下架回 private(项目组/作品归属只能属主决定)
DEL/api/v1/admin/asset-scopes/:kind/:id超管清除映射(等同回默认私有,不删素材本身)

数据模型与约定

asset_kind 六值:asset / character / scene / prop / voice / video;scope 四值:private / global / group / work。列表可见性 = (自己名下) OR (scope='global') OR (scope='group' AND 我是该组有效成员),再叠加 scope 筛选条件;子查询别名统一 ascope。

所有归属写操作落 audit_logs:asset_scope_set / asset_scope_reset / project_group_admin_create / project_group_admin_update / project_group_admin_delete。跨用户设置一律 404;group 校验「组属于该属主 或该属主已加入该组」、work 校验作品属于该属主;options 的候选组=我创建的 ∪ 已加入的。

用户端入口:素材库任一 tab → 归属筛选条 / 卡片「归属」按钮 / 批量设置(切 tab 显式重置筛选);后台入口:🎯 素材归属(清单、设为全局、下架回私有、清除映射)+ 🗂️ 项目组(组内素材、完整管理)。

👤 角色 & 素材

POST/api/v1/org/characters创建角色
GET/api/v1/org/characters角色列表详情
除库内字段 gen_model、gen_ratio 外,另返回 asset_model / asset_size:按 image_url 关联 cy_assets,从资产名「摘要 · 模型 · 尺寸」解析出的实际出图模型与像素尺寸。
🔴 角色、场景(/stories/:id/scenes)、道具(/stories/:id/props)三个列表均已返回这两个字段。库内 gen_model 是前端下拉框当时的取值,换模型生成时不会回写,曾有 7 条与实际出图模型不符(库内记 seedream-5.0-flash,实际是 qwen-image 或 seedream-5.0-lite);前端展示以 asset_model 为准,取不到(老平台链接、上传图)再退回 gen_model,仍空则不显示该行。
🔴 P208(2026-11-06):① 上述 7 条 gen_model 已按资产名落库改正(角色 38/40/41、场景 14/15、道具 13/14);② 模型名统一用正名——早期记录里的旧别名 qwen-image / qwen-image-2.0 读取时归一为 qwen-image-3.0(与定价表主键、通道 platform_alias、前端下拉同名);③ 卡片文本一律显示「✨ 提示词:…」:取值以 gen_prompt 为准,历史记录没有 gen_prompt 的退回描述字段(角色 features / 场景·道具 overview),不再出现「描述」与「提示词」两种表述混显;素材超市卡片同样统一带「提示词:」标签。
PUT/api/v1/org/characters/:id更新角色
DEL/api/v1/org/characters/:id删除角色
GET/api/v1/org/stories/:id/scenes场景列表
POST/api/v1/org/stories/:id/scenes创建场景
GET/api/v1/org/stories/:id/props道具列表
POST/api/v1/org/stories/:id/props创建道具

🏢 渠道商自助(role=reseller,Bearer admin JWT)

GET/api/v1/admin/reseller/me/invite我的邀请信息(码/链接/二维码)
POST/api/v1/admin/reseller/me/invite/regenerate重新生成我的邀请码(旧链接失效)
GET/api/v1/admin/reseller/me/stats我的统计(积分/用户数/今日新增/累计消耗/今日消耗)
GET/api/v1/admin/reseller/me/credits-daily每日积分消耗趋势详情
参数: date_from/date_to(YYYY-MM-DD,默认近 14 天,区间 ≤366 天);返回每日 credits_consumed 列表与 total_consumed。渠道商只看自己名下用户。
GET/api/v1/admin/reseller/me/users名下用户列表(分页)
POST/api/v1/admin/reseller/me/credits补贴名下用户积分(从我的渠道商积分扣)详情
Body: {"user_id":68,"amount":10};校验用户归属,余额不足拒绝。
POST/api/v1/admin/reseller/me/password修改我的登录密码(校验旧密码)
GET/api/v1/qr邀请二维码(服务端本地渲染 PNG)详情
参数: data(待编码文本,≤512 字符)、size(80~512,默认 150);返回 image/png+Cache-Control: max-age=86400。公开接口,只把传入文本编码成图片、不读任何业务数据。
🔴 2026-11-06 起所有邀请/支付二维码改走本接口:原 api.qrserver.com 在境内实测 1540ms(本机 4.45s),抖动即空白,导致 admin 后台与渠道商面板的二维码「同时消失」。已用 jsQR 解回原文验证。

👥 用户管理(超管全量 / 渠道商限自己名下)

GET/api/v1/admin/users用户列表详情
参数: page/page_size/keyword/status/reseller_id;超管可按 reseller_id 筛选(0=none 未绑定),渠道商强制只看自己名下。返回含 credits 余额与 reseller_name。
GET/api/v1/admin/users/:id用户详情(含近 10 条积分流水)
POST/api/v1/admin/users创建用户详情
Body: {"phone":"13900000000","password":"123456","username":"可选","email":"可选","reseller_id":"超管可选"};自动生成钱包;渠道商创建自动归属自己。
PUT/api/v1/admin/users/:id更新用户(邮箱/手机/密码/用户名;超管可改归属)
PUT/api/v1/admin/users/:id/status启用/禁用用户 {status:0|1}
DEL/api/v1/admin/users/:id删除用户(禁用 status=0)
POST/api/v1/admin/users/:id/credits调整单个用户积分(仅超管,正加负扣)详情
Body: {"amount":100,"description":"活动赠送"};amount 为正加负扣,写 admin_adjust 流水(不计入消耗统计)。
POST/api/v1/admin/users/batch-credits批量调整积分(≤500 用户,仅超管)
POST/api/v1/admin/users/import批量导入用户(≤500 条)详情
Body: {"users":[{"phone":"13900000001","password":"123456","username":"可选","email":"可选"}]};返回 success_count/fail_list。

🏢 渠道商管理(仅超管 super)

GET/api/v1/admin/resellers渠道商列表(page/keyword/status)
GET/api/v1/admin/resellers/stats全站统计(渠道商数/用户数/系统积分/今日消耗等)
GET/api/v1/admin/resellers/:id渠道商详情(积分/用户数/今日新增/累计消耗)
POST/api/v1/admin/resellers创建渠道商(自动生成邀请码+同步登录账号)
PUT/api/v1/admin/resellers/:id更新渠道商(名称/备注/密码/启用禁用 status)
DEL/api/v1/admin/resellers/:id删除渠道商(软删)
POST/api/v1/admin/resellers/:id/credits调整渠道商积分(正加负扣)
GET/api/v1/admin/resellers/:id/credit-logs渠道商积分流水
GET/api/v1/admin/resellers/:id/invite查看指定渠道商邀请信息
POST/api/v1/admin/resellers/:id/invite/regenerate为指定渠道商重新生成邀请码
GET/api/v1/admin/resellers/:id/users渠道商名下用户列表

🎛️ 模型配置(仅超管 admin)

GET/api/v1/admin/pricing/image-models图片模型列表(含 sizes 尺寸/enabled 启用状态)
PUT/api/v1/admin/pricing/image-models/:id编辑图片模型详情
Body: {"name","enabled":0|1,"qualities":[...],"sizes":["1:1","16:9",...],"factory_cost","published_price"};enabled=0 停用后用户端不再显示该模型,sizes 控制生图尺寸选项。
GET/api/v1/admin/pricing/video-models视频模型列表(含 sizes/modes/durations/enabled)
PUT/api/v1/admin/pricing/video-models/:id编辑视频模型详情
Body: {"name","enabled":0|1,"credits_rule":{"720p":15},"sizes":[...],"modes":["text2video","multi_ref","first_last_frame"],"durations":[4..12],"factory_cost","published_price"};modes 控制该模型在视频工坊可选模式,sizes/durations 控制尺寸与时长选项,enabled 控制启用/停用。以上字段均支持独立提交(快捷启用/禁用按钮只传 enabled)。

🔐 统一认证 & 管理端(admin.ciyuanfun.cn)

统一认证生态(MaM + PX + 各合作平台)的管理入口。渠道商体系 2026-08-16 落地:admin.ciyuanfun.cn(root=/www/wwwroot/mam-admin)→ 302 → login.html(双 tab:🏢超管 / 🔑渠道商)→ 超管跳 index.html、渠道商跳 reseller-panel.html(独立控制台)。账号存 id_auth.cy_admins(role=super|reseller),创建/改密/删除渠道商自动同步该表。nginx 对 login/reseller-panel 加 no-cache 头。

POST/admin/login管理端登录(超管/渠道商)详情
Body: {"username","password"}。bcrypt 校验 + JWT,返回 role(super/reseller)、token、name。超管初始账号 admin/Admin@2026!。渠道商登录后仅可访问 /admin/reseller/me* 与 /admin/users*,其余 403。
GET/admin/me当前管理账号信息详情
返回当前登录管理员的 role/name/reseller_id 等;鉴权方式:Bearer admin JWT 优先,兼容 X-Mam-Admin-Key(超管)。
GET/admin/reseller/me/stats渠道商自助-统计详情
渠道商自己的用户数/消耗统计(消耗口径:cy_credit_logs consume 金额为正数且排除 ref_type='admin_adjust')。
GET/api/v1/admin/users/:id/px-exchange-total统计用户 PX 兑换获得积分累计(X-Mam-Admin-Key)详情
2026-08-25 新增:统计该用户通过 PX 兑换获得的积分累计(口径:ref_type='px_exchange' 或 ref_type='admin_adjust' 且描述含"PX兑换"、amount>0),排除现金充值。供 PX 端消费进度(/api/gateway/credits/{username})调用。返回 {"success":true,"user_id":5,"px_exchange_total":2}。鉴权:X-Mam-Admin-Key。
GET/admin/reseller/me/users渠道商自助-名下用户详情
分页返回渠道商名下用户(含 credits 余额),仅限自己名下。
POST/admin/reseller/me/credits渠道商自助-补贴积分详情
Body: {"user_id":N,"amount":N,"description":"可选"};校验用户归属自己名下 + 渠道商积分池余额充足,不足拒绝。
GET/admin/reseller/me/credits-daily渠道商自助-每日消耗明细详情
14 天默认 / 366 天上限。
POST/admin/reseller/me/invite渠道商自助-生成邀请码详情
POST/admin/reseller/me/invite/regenerate渠道商自助-重置邀请码详情
PUT/admin/reseller/me/password渠道商自助-改密详情
Body: {"old_password","new_password"};校验旧密码,双写 cy_admins/cy_resellers。
PUT/admin/resellers/:id超管-设渠道商初始密码详情
Body: {"password":"新密码"}。
POST/admin/resellers超管-创建渠道商详情
创建 cy_resellers + 自动同步 cy_admins(role=reseller)。
GET/admin/resellers超管-渠道商列表详情
DEL/admin/resellers/:id超管-删除渠道商详情
删除 cy_resellers + 自动同步 cy_admins。

💳 套餐 & 支付

GET/api/v1/org/packages/plans套餐列表
GET/api/v1/org/packages已购套餐
POST/api/v1/org/packages/buy购买套餐(微信支付)
GET/api/v1/org/packages/order查询订单

💰 钱包 & 积分充值

GET/api/v1/wallet/recharge/plans充值套餐与汇率详情
返回充值套餐列表(面额/积分/赠送)与人民币:积分汇率 rate(默认 1 元=1 积分)。
curl https://platform.ciyuanfun.cn/api/v1/wallet/recharge/plans \
  -H "Authorization: Bearer <token>"
POST/api/v1/wallet/recharge微信充值下单详情
创建微信支付 Native 订单。Body 二选一:plan_id(选套餐)或 amount_fen(自定义金额,单位分)。返回 out_trade_no、code_url(二维码内容)、credits。用 code_url 生成二维码供用户扫码。
curl -X POST https://platform.ciyuanfun.cn/api/v1/wallet/recharge \
  -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"plan_id":1}'
GET/api/v1/wallet/recharge/status充值订单状态详情
轮询参数 out_trade_no。返回 data.status:paid/SUCCESS=已支付,CLOSED/PAYERROR/REVOKED=失败。前端据此弹成功/失败框。
curl "https://platform.ciyuanfun.cn/api/v1/wallet/recharge/status?out_trade_no=RCGxxxx" \
  -H "Authorization: Bearer <token>"
GET/api/v1/wallet/balance钱包余额详情
返回当前用户积分余额 balance。
curl https://platform.ciyuanfun.cn/api/v1/wallet/balance \
  -H "Authorization: Bearer <token>"
GET/api/v1/wallet/account/income账户明细(积分获得流水)详情
积分收入明细,按 way_text 分为「充值 / 平台转入 / 其他转入」,含积分数值与获得时间(前端不展示人民币)。
curl https://platform.ciyuanfun.cn/api/v1/wallet/account/income \
  -H "Authorization: Bearer <token>"
GET/api/v1/wallet/account/overview账户详情三块(货币/积分)详情
返回 money(充值记录:金额/到账积分/退款状态 + 充值总额/已退/可退,人民币)与 credits(当前积分余额 + 全量流水:充值/消费/退款扣回/赠送等,含余额快照)。充值页「账户详情」数据源。
GET/api/v1/wallet/px/balancePX 积分余额详情
返回用户 PX 链上钱包余额(bound=是否已开通 PX 钱包,address,balance)。
POST/api/v1/wallet/px/exchangePX 双向兑换(Mam 积分 ↔ PX 积分)详情
Body: {"direction":"mam_to_px"|"px_to_mam","amount":N}。规则:① 双向 ② 比例 1:1(PX 1 = 人民币 1 元 = Mam 1 积分)③ 手续费 1% 以 Mam 计价,最低 1 Mam(mam_to_px 扣 amount+手续费;px_to_mam 到账 amount-手续费)④ 不做提现。底层走 PX 平台转账(平台 PX 资金池地址=market_config.platform_px_address),本地写 px_exchange 流水,转账失败自动回滚。

🧾 充值退款(admin,服务商模式微信原路退回)

GET/api/v1/admin/recharge/orders/:out_trade_no/refund-preview退款预检(只算不退)详情
返回 refundable、refund_fen(可退金额/分)、refund_credits(扣回积分)、reason(不可退原因)。规则:仅已支付且未退款订单;无消费全额退;有消费扣除净消费额 15%;消费占比 ≥85% 不予退款。
POST/api/v1/admin/recharge/orders/:out_trade_no/refund执行退款(微信原路退回+扣回积分)详情
Body: {"reason":"退款原因"}(必填)。微信受理成功后事务内扣回积分并写流水(type='recharge_refund');订单进入 refunding,由 60s 定时器查询微信退款单最终状态:SUCCESS→refunded,异常→自动回加积分+failed(可重试)。每单仅退一次。

🔁 PX 积分兑换枢纽(px-sh.tokenroute.net)

PX 作为统一认证生态的积分兑换枢纽/清算所:兑换(px→积分)=消费语义、无手续费;退款(积分→px)=退给自己、99% 到账、1% 手续费进 owner 资金池;支持 targetUsername 跨用户互换(对方付积分、你付 PX)。数据真相源为 users_backend.json 账本。2026-08-26 三模式路由:① api=伙伴端真实入账(MaM,调伙伴 API 真实发放积分);② gateway=平台内托管积分包(DeepSeek/文心/通义/智谱/hy3/Kimi 共 6 个大模型伙伴,scenarios 选包 → 扣 PX → 写入 gateway 托管积分 → 可在 AI 网关 /api/gateway/chat 消费);③ none=暂未接入(银行/电信/南航/麦当劳/莱美等生活积分,仅登记、暂不真实发放)。汇率口径:rate = 1 PX 兑换的目标平台 credit 数量(如 DeepSeek 1PX→30万 DS Credits、电信 1PX→100 积分);模式 C 按真实官方比例校准(中行500/电信100/南航10/麦当劳10/莱美10)。移动端"消费进度"综合 MaM 兑换 + gateway 托管积分(已获得/可用/已消费)。

GET/api/exchange/partners可兑换对象列表详情
返回 18 个积分种类(DeepSeek/文心/通义/豆包/智谱/OpenAI/Google/Anthropic/Meta/xAI/银行/电信/南航/麦当劳/Tencent/莱美商城/MaM AI 漫剧/Kimi)。MaM AI 漫剧 id=18 已启用(rate=1.0,apiEndpoint=MaM 管理积分接口 + apiKey)。6 个大模型伙伴恢复 apiKey 并支持 gateway 积分包:DeepSeek(id=1)/文心(id=2)/通义(id=3)/智谱(id=5)/Tencent-hy3(id=15)/Kimi(id=17),scenarios 字段含可选积分包(如 ds_free: 1PX→30万 DS Credits、hy_standard: 5PX→500万 hy_credits)。
POST/api/exchange/redeem兑换/退款/购包执行(三模式路由)详情
Body: {"username":"发起方","partnerId":N,"amount":N,"direction":"consume"|"refund","scenarioId":"ds_free"(仅 gateway),"targetUsername":"目标用户"}。
consume(兑换 px→积分)按伙伴能力三模式路由:① api(配 apiEndpoint,如 MaM):跨用户时查目标方积分余额(不足拒「目标用户积分不足」)→ 扣目标方积分 → 发起方得积分 → PX 双向过户 → 记 exchange(consume);② gateway(有 scenarios 包,如 DeepSeek/hy3/Kimi):校验 PX 余额 ≥ 包 pxCost → 扣 PX → GatewayController.addCredits 写入该用户平台托管积分 → 记 exchange(direction=buy, scenarioId, credits)(失败回滚 PX);2026-09-05 起支持任意数量购买:不传 scenarioId 时按 amount × rate 折算入账(quota=floor(amount×rate)),前端"兑换数量(px)"模式;传积分包仍要求 amount 恰等于包价;③ none:返回「该积分类型暂未接入」。refund 仅 api 模式支持。
示例-兑换 MaM:curl -X POST .../api/exchange/redeem -d '{"username":"花猫妹妹","partnerId":18,"amount":5,"direction":"consume","targetUsername":"oldworker"}' 示例-买 DeepSeek 包:curl -X POST .../api/exchange/redeem -d '{"username":"王五","partnerId":1,"amount":1,"direction":"consume","scenarioId":"ds_free"}'
GET/api/users/ledger-balance?address=0x...按地址查账本余额(公开)详情
返回 {"success":true,"username":"王五","balance":989}。账本(users_backend.json)是运营真相源;链上余额(/api/point/balance/{addr})另作补真账轮次。PX 前端 points.js 与 MaM 端 PXBalance 均以此接口为显示口径。
GET/api/gateway/credits/{username}消费进度(综合 MaM + gateway 托管积分)详情
2026-08-26 改造:综合统计两类——① MaM:调 /api/v1/admin/users/{id}/px-exchange-total 返回已兑换 Mam 积分(usedCredits);② gateway 伙伴:读平台托管积分(gateway_allocations.json)返回 totalCredits/usedCredits/可用。返回示例:{"success":true,"username":"王五","partners":[{"partnerName":"MaM AI 漫剧","creditUnit":"Mam","rate":1.0,"usedCredits":146},{"partnerName":"DeepSeek","creditUnit":"DS Credits","rate":300000,"totalCredits":300000,"usedCredits":0}]}。移动端"消费进度"卡片数据源。
GET/api/transactions?username=花猫妹妹用户交易记录详情
返回该用户全部交易(transfer/exchange/buy/issue/consume)。exchange 记录字段:direction(consume/refund)、fromUsername(发起方)、toName(目标用户)、partnerName(积分平台名)、amount(源数量)、credits(到账/获得)、rate、fee(仅退款)。
CAL2026-09-05 · 6 家 LLM 伙伴校准表(1 PX 应得额度,已全量更新)校准口径=按官方刊例价(元/百万 token)折算;前端兑换页已 gateway 优先 + 双模式(积分包/任意数量)+ 弹窗动态预览 v28详情
入口名    alias → 上游模型                      1 PX 应得    备注
DS        ds → deepseek-v4-flash                13 万      套餐 ds_free(1PX)/ds_standard(5PX)
WenXin    wenxin/ernie → ernie-4.5-turbo-128k    38 万      千帆 v2;输出上限 cap→12288;⚠️欠费停服(9/5 起勿测)
TongYi    al/qwen → qwen-plus                   58 万      al_free(1PX)
ZhiPu     gl/glm → glm-5                        7 万       glm_free(1PX);推理模型小 max_tokens 时 content 空属正常
Kimi      km/kimi → kimi-k2.6                   4.5 万     org RPM=3 限流;moonshot-v1-8k 等已下线
Tencent   hy(直连 hy3 preview)                  6.6 万     hy_standard(5PX)/hy_pro(10PX)
兑换执行:gateway 积分包(带 scenarioId) 或 任意数量(不带 scenarioId → amount×rate,9/5 起)
鉴权:POST /api/gateway/allocations 需 X-Admin-Token(9/5 堵白嫖);用户购买一律走 /api/exchange/redeem 真扣 PX

⚙️ 开发者控制台

GET/api/v1/org/apps应用列表
POST/api/v1/org/apps创建应用
GET/api/v1/org/logs调用日志
GET/api/v1/org/app-info应用信息

🔓 开放平台 (Open API /open/v1/*)

开放平台 API 使用 HMAC-SHA256 签名,详见签名算法 Tab。

GET/open/v1/health服务健康检查
GET/open/v1/user/credits剩余调用次数
POST/open/v1/image/generate图片生成
GET/open/v1/image/task/{taskId}图片任务状态
POST/open/v1/video/generate视频生成
GET/open/v1/works作品列表
GET/open/v1/works/{id}作品详情
POST/open/v1/asset/certify资产确权
GET/open/v1/asset/verify/{certId}验证资产证书

🔑 HMAC-SHA256 签名算法

开放平台 API(/open/v1/*)使用 HMAC-SHA256 签名鉴权:

sign_string = METHOD + "\n"
           + PATH + "\n"
           + BODY + "\n"
           + TIMESTAMP + "\n"
           + NONCE + "\n"
           + SECRET

signature = hmac_sha256(sign_string, SECRET)

请求头:

X-App-ID:    你的应用 ID
X-Timestamp: 当前 Unix 时间戳(秒)
X-Nonce:     随机字符串(防重放)
X-Sign:      HMAC-SHA256 签名(hex)

📐 开发约定(跨端一致性)

改动落在服务端/前端时,以下口径必须同批对齐,否则两端会出现「看着都对、拼起来不对」。

1. AI 产物命名

RULEcy_assets.name三段式,前端按 split(' · ') 拆行详情
图片:摘要 · 模型 · 宽x高 例:橘猫在窗台晒太阳 · seedream-5.0-flash · 2048x2048
视频:摘要 · 模型 · 分辨率 · 时长s 例:小鹿穿过晨雾森林 · seedance-2.0 · 720p · 5s
摘要:取提示词首行首句;中文 ≤14 字全取,超出截 12 字 + …;英文取前 6 词。
提示词全文存 description,不进 name。
服务端实现:buildImageAssetName() / imageAssetSummary()(generate_image.go)。第三方若自建资产,建议同规则命名,前端可零适配渲染。

2. 素材分类

RULEcy_assets.category独立列,不解析 tags JSON详情
取值:general / character / scene / prop / voice;未分类 = category IS NULL(列表接口用 cat=none 查)。
历史上分类塞在 tags.$.category,AI 生成图的 tags 是数组,$.category 取不到 ⇒ 永远归不了类,故独立成列。
修改走 PATCH /org/assets/:id(只能改自己的),category="" 表示清除。

3. 模型命名与计费

RULEpricing / channels主键=对外名,别名只做归一详情
图片模型对外主键即展示名:seedream-5.0-flash / seedream-5.0-lite / seedream-4.5 / seedream-4.0。
旧名 qwen-image、qwen-image-2.0 仅作入参别名,服务端 canonicalImageModel() 归一为 seedream-5.0-flash。
改主键必须同批改四处:定价表 id、cy_model_channels.model_key、前端下拉 value、{characters,props,scenes}.gen_model。
计费红线:查不到价一律拒单,不兜底收费;cost<=0 时图片返回 400、视频任务置 failed,绝不按 1 分或 25 分放行。
定价公式:公布价 = 厂价 × 1.2,1 积分 = 0.1 元 ⇒ 积分 = ceil(厂价 × 12)。

4. 前端与部署

RULEmam / nginx响应式断点与缓存策略详情
响应式:≤860px 顶部导航收为「☰ + 右侧抽屉」(#navDrawer),861px 起恢复桌面四链接。验收看 scrollWidth === innerWidth。
HTML 必须 no-store:改版随时生效。注意 id-auth.ciyuanfun.cn 的 location ~* \.html$ 正则优先级高于 /mam/ 前缀,会把首页罩成 30 天 immutable ⇒ 需显式 location = /mam/index.html。
静态媒体:/mam/showcase/(首页影像窗素材,webp + 无声 mp4)走 ^~ 前缀 + 30 天 immutable;换片只改 data-items,不必升版本。
JS/CSS:改文件必须升 ?v=,否则老缓存;页面 HTML 全部 no-store。

5. 素材文件服务

RULE/org/assets/file/:idMIME + Range 两项都要对详情
必须返回正确 Content-Type(入库为空或 octet-stream 时按扩展名兜底,Go 标准库不认 mp4/webm);
必须支持 Range 并返回 206,否则视频拖不动进度条;
上传受 nginx client_max_body_size 50m 限制,超限返 413(HTML 而非 JSON),前端需按状态码兜底提示。

6. 自定义通道(接第三方模型)

RULEcy_channel_protocols协议模板驱动,不发版即可接新厂商详情
内置模板(kind=image)
· openai_image —— OpenAI Images 兼容:POST {{base_url}}/images/generations,取 data[0].url(或 data[0].b64_json)
· bailian_image —— 阿里百炼 DashScope 同步:POST {{base_url}}/api/v1/services/aigc/multimodal-generation/generation,取 output.choices[0].message.content[0].image
· volc_image —— 火山方舟同步(现有四个 seedream 通道在用)

可用占位符:{{base_url}} {{api_key}} {{model}} {{model_id}} {{endpoint_id}} {{prompt}} {{size}} {{size_star}} {{ratio}} {{quality}} {{provider}}
{{size}} 形如 1280x720,{{size_star}} 是同一尺寸的星号写法 1280*720(百炼要求星号)。

通道级尺寸覆盖:extra.size_map 按比例(其次画质)命中,未命中回落 seedream 档位;extra.size_style="generic" 表示用通用 1k 档。各家尺寸规范不同,靠全局映射必然对不上。
{"size_map":{"16:9":"1280*720","1:1":"1024*1024"},"size_style":"generic"}
实测教训:百炼的 /compatible-mode/v1/images/generations 在本账号实测返回 404,必须走 DashScope 原生同步接口 —— 接新厂商一律以真跑结果为准,不以文档为准。

7. 自定义通道的安全边界

RULESSRF管理员可填任意 BaseURL ⇒ 必须三层防护详情
① 保存时校验:只允许 https;IP 直连必须是公网;域名解析后每一条 A/AAAA 都必须是公网(挡「域名指向 127.0.0.1」)。
② 调用时 Dial 层再校验,并直接用解析出的 IP 建连 ⇒ 校验用的 IP 就是连接用的 IP,DNS rebinding 没有时间窗。
③ 不跟随重定向(ErrUseLastResponse)⇒ 挡「公网地址 302 到 169.254.169.254」。
拦截范围:环回 / 私网(10、172.16-31、192.168)/ 链路本地 / 169.254 / 100.64 CGNAT / IPv6 环回 / 带账号密码的 URL。
验收判据:把 base_url 直改成 https://127.0.0.1:58080(绕过保存校验),调用必须报「目标 127.0.0.1 解析到内网地址,已拦截」。

8. 通道的权威性

RULE配置即真相停用即拒单,不回落硬编码详情
通道表有记录(enabled=0 也算「已迁移」)⇒ 停用后必须拒单,不回落旧路径,否则「后台停了却仍能出图」。
停通道实测:返回 503「该模型的调用通道已停用或配置不完整,暂不可用(未扣费)」,余额不变。
模型别名只能精确匹配:旧名 qwen-image 归一为 seedream-5.0-flash 时,禁止用前缀匹配 —— HasPrefix("qwen-image") 会把 qwen-image-3.0 一起吞掉,导致「配了百炼通道却拿火山 ep 出图」。别名表全项目只留一份(canonicalImageModel)。

9. 视频模型的通道分发

RULE按 channel_id 选路禁止用任务号或模型名前缀猜厂商详情
视频提交前必须查 cy_model_channels(kind=video),把命中的通道 id 落进任务的 channel_id;轮询器只认这个字段,不再靠任务号前缀判断厂商。
硬编码回落是事故源:endpoint 映射表查不到模型名时若静默回落默认项,会出现「扣 wan3.0 的钱、出 seedance 的片」且任务显示 success。查不到必须报错。
验收判据:看任务号的形态 —— 火山是 cgt- 前缀,阿里百炼是 UUID。选 wan3.0 却拿到 cgt- 即判定走错厂商。
展示名与主键可以不同名:定价表主键 wan3.0、展示名 wan3.0-video 时,提交入口须先归一到主键(CanonicalVideoModel:先按主键、再按名字反查),否则按展示名调 API 会被当成未知模型直接拒单。
停用即拒单:通道有记录但已停用,返回「该视频模型通道已停用或配置不完整(未扣费)」,余额不变;未迁移的模型才走旧硬编码。

10. 用户自带模型通道(BYOK)

BYOK私有通道与公共通道物理隔离两张私有表 + 单价快照 + 个体 5% 服务费 + 渠道商自建(群内可见)详情
用户在 MaM 生成面板的模型下拉里选「自定义模型」,填入自己的 API Key,即可把上游模型接进来。它不是公共通道的另一种配置,而是另一套账:cy_user_model_channels + cy_user_model_credentials,与 cy_model_channels、cy_model_credentials 零交集。
为什么不给公共表加 owner 列:LoadChannel(kind, alias) 是带 30 秒缓存的热路径,没有用户维度;一旦混表,后台列表、测试连通、轮询器、缓存键每一处都要补 WHERE owner IS NULL,漏一处就串号。
单价快照:创建时按「参照模型族」读一次公布价与厂价,算出 unit_price 与 service_rate 落库;之后后台调价不影响已建通道,对账时也能解释「他为什么是 0.20」。
只收 5% 服务费(byok_pricing_mode=service_only,五哥 11-05 定):单价 = 公布价 × 5%,不叠厂价。另一档 cost_plus(厂价+服务费)保留,改配置即切、不用发版。
取整到「分」:积分→人民币(1 积分 = 0.1 元)→ 四舍五入到分 → 折回积分。例:公布价 3 × 5% = 0.15 ⇒ ¥0.015 ⇒ ¥0.02 = 0.2 积分/张,所以单价必然落在 0.1 的整数倍上。
🔴 小数价绝不能复用 roundPrice:那个函数是 math.Ceil(公布价取整用),拿它算 BYOK 会把 0.15 抬成 1(费率放大 6.7 倍)。判据:3×5% 必须落 0.20,不得出现 0.15 / 1 / 2.31。 扣谁的额度(P204 定案,五哥 11-06):扣使用者本人。① 模型主人自用 ⇒ 只付 5% 服务费(单价快照);② 渠道商群里的其他用户 ⇒ 按全域公布价扣他自己的积分,流水 ref_id=uc_<通道>_u<使用者>;③ 同时向渠道商收服务费 unit_price(3 分价 ⇒ 0.2 积分 = ¥0.02),扣 cy_resellers.credits,落 cy_reseller_credit_logs(service_fee / 失败退回 fee_refund)。②③ 是并行两笔,不是二选一。失败时用户与渠道商双向退回,流水 type 只有 16 字符,写超长会静默丢凭证。
渠道商自建模型(P202,五哥 11-05 定):自定义模型由渠道商在控制台点「+ 新建自定义模型」建立(POST /api/v1/admin/reseller/me/models),归属落 owner_reseller_id —— 渠道商在 MaM 没有用户账号,所以 owner_user_id=0。效果:群内用户可见可用,扣用户本人积分(全域公布价);上游调用费用由渠道商自己的 Key 承担。配套接口:列表 GET .../reseller/me/models、启停 PATCH .../:id、参照族 GET .../ref-models;停用后用户端下拉立即消失。
用户端入口:生图面板的模型下拉选「自定义模型」直接弹凭据窗。弹窗 z-index=100001 —— 生图面板本身在 99999 的表单弹窗里,层级低一等就会被盖住,表现为「点了没反应」。
可见性:visibility=0 仅自己;=1 同 reseller_id 的渠道商用户群。跨群用户列表看不到、按名字调用也不会命中(回落公共模型),越权改删返回 403。
安全三件套:Key 走 AES-256-GCM 落库、接口只回 sk-ws-****LgSa;BaseURL 只允许 https 且必须解析到公网(Dial 层再拦一次);协议由后端按 provider 白名单指定,用户不能提供 submit JSON,否则等于开放任意 HTTP 代理。
资产归属:私有通道产出的图 channel_id 留空 —— 它和公共通道是两套 id 空间,混写必串号;归属靠 tags 里的「私有」与流水 ref 回溯。删除通道不影响历史资产。
画质档对齐厂家:qwen-image-3.0 档位改为 1K / 2K(厂家 size 允许 512×512–2048×2048、比例 1:8–8:1),1K 1280×1280、2K 2048×2048,同档同价。
视频 BYOK(P209,2026-11-07):已开。视频按分辨率分档 × 秒计价,unit_price 单值存不下,故三档价写进 extra 快照:price_rule(本人自用 = 公布价 × 5%)、ref_rule(公布价,渠道商群内用户按它扣本人)。代表值 unit_price 取 720p 中间档,真实扣费一律读 price_rule[resolution]。例:参照 seedance-2.0-mini(3/6/13 积分/秒)⇒ 0.2 / 0.3 / 0.7 积分/秒;参照 seedance-2.0(5/12/30)⇒ 0.3 / 0.6 / 1.5。
🔴 视频轮询的 id 空间(P209):cy_videos / cy_story_video_tasks 增加 channel_scope(0=cy_model_channels,1=cy_user_model_channels)。两张表 id 各自自增,同一个数字是两条不同通道,轮询必须走 LoadChannelByScope(id, scope);私有通道查不到时不回落公共(回落即串号)。
视频只放两家协议:volc(火山方舟)与 bailian(阿里百炼),对应 volc_video / bailian_video 模板;openai_compat 没有视频协议,建通道时明确拒绝。
渠道商分成(P209):cy_resellers.share_rate(0~1,默认 0.85),在 admin.ciyuanfun.cn → 渠道商 列表「分成」列与详情页设置,越界(<0 或 >1)直接拒绝、不夹紧。它和 5% 模型服务费是两件事:5% 是平台向渠道商收的通道服务费,share_rate 是平台与渠道商的营收分成比例。
🔴 视频 BYOK 的三个实测坑(P209b/c/d):① 选路必须排在定价表校验之前——私有通道的模型名是用户自建的,cy_video_model_pricing 里没有这一行,先查表会把合法私有模型一律判成「未启用」,BYOK 视频永远用不了(T8 端到端实测踩到);私有通道因此跳过 videoModelAvailable,该分辨率没配价时由 cost<=0 拒单,仍不兜底收费。② GET /api/v1/org/models/custom 默认 kind=image,查视频通道必须显式带 ?kind=video,否则视频 tab 恒为空。③ 分镜已支持(P209f):story.go 同样改成「先选路后校验」,vp-model 已放开自制模型,扣费按 extra.price_rule[resolution] × 时长,渠道商模型另收 5% 服务费,任务落 channel_scope=1。入口四处(P209g):① 视频工坊模型下拉末尾「+ 新建自定义模型…」② 分镜模型下拉同款 ③ 用户端「我的模型」弹窗的图片/视频两个页签 ④ 渠道商面板「+ 新建自定义模型」的模型类型下拉(参照价由 GET /api/v1/admin/reseller/me/ref-models?kind=video 给三档)。没有下拉入口=功能不存在:视频侧此前只有图片下拉带 __custom__,所以「视频自定义」在界面上等于没有。
渠道商侧入口:admin.ciyuanfun.cn 左侧菜单「🔌 自定义模型」,或渠道商面板 /reseller-panel.html 的卡片「渠道商自定义模型」→「+ 新建自定义模型」。卡片文案「渠道商自定义模型,群内用户可见可用,扣用户款」——扣的是使用者本人的款,不是渠道商的额度。
服务费已落地(P204,2026-11-06):早期「15%」的两种读法(服务费口径 / 分成口径)五哥已拍板 —— 按服务费口径、但费率仍是 5%:群内用户付公布价,渠道商另付 0.2 积分(¥0.02),两笔并行。服务费扣在渠道商积分额度上,人民币那一头归「网盟网」人民币账户,MaM 内不建人民币台账,只在流水备注里写明「折合¥0.02」供财务对账。渠道商额度不足时照扣并允许为负,不因此拦截用户(用户的钱已经收了,图必须给),欠费由运营侧补。 🔴 P210-P212 三个实测坑(2026-11-07):① 插校验先看变量声明位置 —— 渠道商面板「创建」按钮因 const kind 的 TDZ 抛 ReferenceError: Cannot access 'kind' before initialization,异常发生在 fetch 之前,按钮已 disabled 却无人复位,表现为「点创建一直卡住、服务端零请求」;修完必须用 DOM 桩单测覆盖「异常后按钮是否复位」。② 新建厂商通道要看协议模板真正读哪个变量 —— 火山视频提交体的 model 填的是 endpoint_id(ep-xxx)而不是模型名;缺 ep 时通道建得成、一跑必挂,现已在创建时强校验并落 extra.endpoint_id(百炼走 model 名字,故不受影响)。③ 标注误导会伪装成权限 bug —— 渠道商模型被标「他人共享 / 归属 - /(自制)」,看起来像别人的私有模型;实测六个账号、三处数据源(列表 / 定价 / 选路)visibility=0 只本人可见,权限本身正确,改标注即可。另 弹窗状态残留=隐式默认值:mm-vis(谁能用)在打开弹窗和创建成功后不复位,选过一次「渠道商用户群」后后续模型全被误建为共享,已在两处复位为 0。
项目组(P220,2026-11-09):参照 aigc iw_comic_groups 的分组语义,只读参照、不碰 aigc 代码与 inwork 库;新表一律 cy_ 前缀。分页必须用 LIMIT ? OFFSET ?(size, offset):写错成 LIMIT ?,? 会变成 offset 在前、第一页 list 恒空而 total 仍正确 ⇒ 只断言 total 会漏判,必须同时断言 list 长度(P220 实测踩到)。另 归组关系独立成表,因为 cy_stories 的 _bak_user_del / _bak_admin_del 用 INSERT ... SELECT * 备份,给它加列会整链断裂(铁律 58)。弹窗关闭也要复位按钮文案 / disabled:重命名弹窗残留「保存」会让下一次新建看起来像没生效(铁律 7,DOM 桩单测已覆盖)。
🔴 素材归属全覆盖(P221,2026-11-09):① 子查询别名不能和外层撞 —— ListScenes 外层别名是 s,可见性子查询也写 s 会让 s.asset_id=s.id 指向同一张表,筛选恒失效;道具库外层是 p 所以碰巧正常 ⇒ 统一改成 ascope,且六类逐条断言 list 长度和具体 id,不能只看 total / 200。② 历史文案会和新概念撞名 —— 场景/道具库 story_id=0 过去标「全局素材」(实为「不挂作品」、仍是私有),P221 引入真正全站可见的 global 后必须改成「独立素材」,否则标注误导会伪装成权限 bug(铁律 112)。③ 归属映射独立成表,别给 cy_assets 等原表加列(_bak_* 用 INSERT ... SELECT *,加列会断链,铁律 58/124)。④ 文件读取改 OptionalAuth 但要保留 sig 签名链接,否则分镜里已落库的第三方 AI 拉图 URL 会全断。⑤ 只读态别占用 busy 标志:他人 global 素材弹窗若把「不可编辑」写成 busy=true,关闭后不复位会让下次打开被守卫挡死;拆成独立 readonly 并在 close 全量复位(铁律 7/116)。⑥ admin 只能设 private/global,group/work 归属属主自决;删组必须同时把组内素材回私有,且绝不删素材或作品。
🔴 创建弹窗候选组必须「打开时强刷」(P224,2026-11-10):① api() 对 401 / 失败返回 {error} 不抛异常,asEnsureOptions 不判错就会把空候选永久缓存成 optsLoaded=true;未登录 boot → SPA 内登录不刷新页面 → 下拉永远只剩「私有 / 全局」,表现为「列表没改过来」。② 六个创建弹窗打开时只同步渲染当时的 asState,不主动拉候选(铁律 108:同功能多条链路要全覆盖,含复用 DOM 的上传弹窗与故事创作页)。③ 修法:asEnsureOptions(force) 失败即 optsLoaded=false;新增 asRefreshCreateOptions() =立即重绘+强制重拉+再重绘,七个入口逐一挂上。④ 接口单测打测试票、DOM 桩假设数据已在手,都覆盖不到「装载时序」⇒必须补页面级时序桩(未登录 boot → 登录 → 打开弹窗 → 断言 option 列表);只断渲染函数会得出「已修复」的假阳性。
🔴 新建素材下拉要覆盖全站可加入组(P223,2026-11-10):① 候选口径别只按「我已加入」过滤 —— options.groups 只返回自己创建 / 已加入的组,其他账号下拉就只剩「私有 / 全局」,与「全站成员自由加入」相悖;修法是另给 joinable,不要把全站组塞进 groups(会污染归属筛选条 / 单条 / 批量弹窗)。② 选中未加入组必须先 join 再落归属:applyScope / CreateStory 都用 pgGroupAvailable 校验,未加入会 400;自动加入走既有 /join,不在后端偷偷写成员表。③ 同功能几条提交链路都要改(铁律 108):六类创建走 asApplyCreated,故事走 createStory → pgCreateBody,两条都要前置 join。④ join 前置放在按钮 disabled 之前,否则失败 return 会留下卡死按钮(铁律 7)。⑤ 归档组不给候选:status=0 不可加入,列了只会点了报错。⑥ 加入成功后必须 optsLoaded=false 再拉 options,否则下拉还显示旧分组。
🔴 项目组协作(P222,2026-11-10):① 删组别把映射 UPDATE 成 private 留垃圾行 —— 无记录即私有,直接 DELETE FROM cy_asset_scopes WHERE group_id=?;本轮实测 UPDATE 会清空 group_id,导致后续按组清理匹配不到、残留 6 行(判据要与结论同向:守恒要断映射表行数)。② 验证脚本的 query 拼接:已有 ? 的路径用 &scope=,没有的用 ?scope=;写错会整批 404 假阴性。③ vm DOM 桩的 select:重写 innerHTML 后没有 selected 时浏览器默认选第一项,桩必须模拟,否则「组失效回落私有」会被判成失败;asApplyCreated 成功后还会刷一次映射 GET,断言要只数 PUT。④ 同一创建功能有几条提交链路都要改(保存按钮 + 生图面板 GPregister.save,铁律 108)。⑤ gin 路由静态段不能与通配段同层:/project-groups/discover 会撞 /:groupId,改用独立路径 /project-groups-discover。⑥ 只读可见不等于可写:组员只见不改,改 / 删仍限属主+超管;退出只回收当事人自己的归属,绝不碰别人资产。
失败任务也要留通道号(P213):cy_videos / cy_story_video_tasks 在 INSERT 时即写 channel_id/channel_scope,不再只在提交成功的 UPDATE 里补;拒单时 vch 为 nil 落 0。上游百炼 overdue-payment / Access denied 也已映射成中文可操作提示,不再直接吐英文给终端用户。
渠道商分成结算台账(P214):cy_reseller_settlements 按「账期 + 单笔用户流水」留痕,share_rate 取生成当期快照,gross_credits =名下用户当月 AI 生成类消费净额(consume 为正、credit 退款为负;白名单只认 video_gen / image_gen / byok_* 及对应 _refund —— 排除法会把「渠道商补贴用户」的内部转账算成负营收,P213 实测踩到),reseller_credits = gross × share_rate、platform_credits = gross - reseller。状态恒为 estimated:只留痕,不改任何余额、不产生实际转账;生成接口以 source_log_id 唯一键幂等,可按月重跑。渠道商余额低于 100 积分显示「余额偏低」、为负显示「余额为负」,扣费逻辑保持「照扣、允许为负」不变。