1+ import axios from 'axios' ;
2+ import { AliTaskResponse , BaseAliyunImageGenerator } from './base.aliyun.image-generator' ;
3+
4+ /**
5+ * 阿里云海报生成模型参数接口
6+ */
7+ interface WanxPosterGenOptions {
8+ title : string ; // 必选,主标题,最多30个字符
9+ sub_title ?: string ; // 可选,副标题,最多30个字符
10+ body_text ?: string ; // 可选,正文,最多50个字符
11+ prompt_text_zh ?: string ; // 可选,中文提示词
12+ prompt_text_en ?: string ; // 可选,英文提示词
13+ generate_mode : "generate" | "sr" | "hrf" ; // 必选,生成模式
14+ generate_num ?: 1 | 2 | 3 | 4 ; // 可选,生成数量,仅在 generate 模式下有效
15+ auxiliary_parameters ?: string ; // 可选,sr或hrf模式下的辅助参数
16+ wh_ratios ?: "横版" | "竖版" ; // 可选,默认横版
17+ lora_name ?: "2D插画1" | "2D插画2" | "浩瀚星云" | "浓郁色彩" |
18+ "光线粒子" | "透明玻璃" | "剪纸工艺" | "折纸工艺" |
19+ "中国水墨" | "中国刺绣" | "真实场景" | "2D卡通" |
20+ "儿童水彩" | "赛博背景" | "浅蓝抽象" | "深蓝抽象" |
21+ "抽象点线" | "童话油画" ; // 可选,预设背景风格
22+ lora_weight ?: number ; // 可选,lora权重,范围0-1,默认0.8
23+ ctrl_ratio ?: number ; // 可选,留白效果权重,范围0-1,默认0.7
24+ ctrl_step ?: number ; // 可选,留白步数比例,范围0-1,默认0.7
25+ creative_title_layout ?: boolean ; // 可选,是否启用创意排版,默认false
26+ }
27+
28+ /**
29+ * 阿里云海报生成器实现类
30+ */
31+ export class AliyunWanxPosterGenerator extends BaseAliyunImageGenerator {
32+ constructor ( ) {
33+ super ( ) ;
34+ this . baseUrl = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis" ;
35+ this . model = "wanx-poster-generation-v1" ;
36+ }
37+
38+ async generate ( options : WanxPosterGenOptions ) : Promise < string > {
39+ // 参数验证
40+ this . validateOptions ( options ) ;
41+
42+ try {
43+ // 提交任务
44+ const taskResponse = await this . submitTask < AliTaskResponse > ( {
45+ input : {
46+ title : options . title ,
47+ sub_title : options . sub_title ,
48+ body_text : options . body_text ,
49+ prompt_text_zh : options . prompt_text_zh ,
50+ prompt_text_en : options . prompt_text_en ,
51+ generate_mode : options . generate_mode ,
52+ generate_num : options . generate_mode === 'generate' ? ( options . generate_num || 1 ) : undefined ,
53+ auxiliary_parameters : options . auxiliary_parameters ,
54+ lora_name : options . lora_name ,
55+ wh_ratios : options . wh_ratios || '横版' ,
56+ lora_weight : this . clampValue ( options . lora_weight , 0 , 1 , 0.8 ) ,
57+ ctrl_ratio : this . clampValue ( options . ctrl_ratio , 0 , 1 , 0.7 ) ,
58+ ctrl_step : this . clampValue ( options . ctrl_step , 0 , 1 , 0.7 ) ,
59+ creative_title_layout : options . creative_title_layout
60+ } ,
61+ parameters : { }
62+ } ) ;
63+
64+ // 等待任务完成
65+ return await this . waitForCompletion ( taskResponse . output . task_id ) ;
66+ } catch ( error ) {
67+ if ( axios . isAxiosError ( error ) ) {
68+ throw new Error ( `阿里云API调用失败: ${ error . response ?. data ?. message || error . message } ` ) ;
69+ }
70+ throw error ;
71+ }
72+ }
73+
74+ private validateOptions ( options : WanxPosterGenOptions ) : void {
75+ if ( ! options . title ) {
76+ throw new Error ( '标题是必需的' ) ;
77+ }
78+
79+ // 裁剪标题
80+ if ( options . title . length > 30 ) {
81+ console . warn ( `标题超过30个字符,已自动裁剪。原文: ${ options . title } ` ) ;
82+ options . title = options . title . slice ( 0 , 30 ) ;
83+ }
84+
85+ // 裁剪副标题
86+ if ( options . sub_title && options . sub_title . length > 30 ) {
87+ console . warn ( `副标题超过30个字符,已自动裁剪。原文: ${ options . sub_title } ` ) ;
88+ options . sub_title = options . sub_title . slice ( 0 , 30 ) ;
89+ }
90+
91+ // 裁剪正文
92+ if ( options . body_text && options . body_text . length > 50 ) {
93+ console . warn ( `正文超过50个字符,已自动裁剪。原文: ${ options . body_text } ` ) ;
94+ options . body_text = options . body_text . slice ( 0 , 50 ) ;
95+ }
96+
97+ // 处理提示词
98+ if ( ! options . prompt_text_zh && ! options . prompt_text_en ) {
99+ throw new Error ( '中文或英文提示词至少需要提供一个' ) ;
100+ }
101+
102+ const promptLength = ( options . prompt_text_zh ?. length || 0 ) + ( options . prompt_text_en ?. length || 0 ) ;
103+ if ( promptLength > 50 ) {
104+ console . warn ( '中英文提示词总长度超过50个字符,将按比例裁剪' ) ;
105+ if ( options . prompt_text_zh && options . prompt_text_en ) {
106+ const ratio = 50 / promptLength ;
107+ const zhLen = Math . floor ( options . prompt_text_zh . length * ratio ) ;
108+ const enLen = Math . floor ( options . prompt_text_en . length * ratio ) ;
109+ options . prompt_text_zh = options . prompt_text_zh . slice ( 0 , zhLen ) ;
110+ options . prompt_text_en = options . prompt_text_en . slice ( 0 , enLen ) ;
111+ } else if ( options . prompt_text_zh ) {
112+ options . prompt_text_zh = options . prompt_text_zh . slice ( 0 , 50 ) ;
113+ } else if ( options . prompt_text_en ) {
114+ options . prompt_text_en = options . prompt_text_en . slice ( 0 , 50 ) ;
115+ }
116+ }
117+
118+ // 处理生成模式
119+ if ( ! [ 'generate' , 'sr' , 'hrf' ] . includes ( options . generate_mode ) ) {
120+ console . warn ( `生成模式 "${ options . generate_mode } " 无效,将使用默认值 "generate"` ) ;
121+ options . generate_mode = 'generate' ;
122+ }
123+
124+ // 处理sr或hrf模式下的辅助参数
125+ if ( options . generate_mode !== 'generate' && ! options . auxiliary_parameters ) {
126+ console . warn ( `${ options . generate_mode } 模式下需要auxiliary_parameters,将切换为generate模式` ) ;
127+ options . generate_mode = 'generate' ;
128+ }
129+
130+ // 处理生成数量
131+ if ( options . generate_mode === 'generate' && options . generate_num ) {
132+ if ( ! [ 1 , 2 , 3 , 4 ] . includes ( options . generate_num ) ) {
133+ console . warn ( `生成数量 ${ options . generate_num } 无效,将使用默认值 1` ) ;
134+ options . generate_num = 1 ;
135+ }
136+ }
137+
138+ // 处理宽高比
139+ if ( options . wh_ratios && ! [ '横版' , '竖版' ] . includes ( options . wh_ratios ) ) {
140+ console . warn ( `宽高比 "${ options . wh_ratios } " 无效,将使用默认值 "横版"` ) ;
141+ options . wh_ratios = '横版' ;
142+ }
143+
144+ // 处理lora名称
145+ const validLoraNames = [
146+ '2D插画1' , '2D插画2' , '浩瀚星云' , '浓郁色彩' , '光线粒子' ,
147+ '透明玻璃' , '剪纸工艺' , '折纸工艺' , '中国水墨' , '中国刺绣' ,
148+ '真实场景' , '2D卡通' , '儿童水彩' , '赛博背景' , '浅蓝抽象' ,
149+ '深蓝抽象' , '抽象点线' , '童话油画'
150+ ] ;
151+
152+ if ( options . lora_name && ! validLoraNames . includes ( options . lora_name ) ) {
153+ console . warn ( `lora_name "${ options . lora_name } " 无效,将移除此参数` ) ;
154+ delete options . lora_name ;
155+ }
156+
157+ // 处理数值范围参数
158+ if ( options . lora_weight !== undefined ) {
159+ if ( options . lora_weight < 0 || options . lora_weight > 1 ) {
160+ console . warn ( `lora_weight ${ options . lora_weight } 超出范围[0-1],将使用默认值 0.8` ) ;
161+ options . lora_weight = 0.8 ;
162+ }
163+ }
164+
165+ if ( options . ctrl_ratio !== undefined ) {
166+ if ( options . ctrl_ratio < 0 || options . ctrl_ratio > 1 ) {
167+ console . warn ( `ctrl_ratio ${ options . ctrl_ratio } 超出范围[0-1],将使用默认值 0.7` ) ;
168+ options . ctrl_ratio = 0.7 ;
169+ }
170+ }
171+
172+ if ( options . ctrl_step !== undefined ) {
173+ if ( options . ctrl_step < 0 || options . ctrl_step > 1 ) {
174+ console . warn ( `ctrl_step ${ options . ctrl_step } 超出范围[0-1],将使用默认值 0.7` ) ;
175+ options . ctrl_step = 0.7 ;
176+ }
177+ }
178+ }
179+ }
0 commit comments