@@ -167,11 +167,16 @@ class NotifyTask(Base):
167167 user_id = Column (Integer , ForeignKey ('users.id' ), nullable = False , comment = "用户ID" )
168168 title = Column (String (200 ), nullable = False , comment = "通知标题" )
169169 content = Column (Text , nullable = False , comment = "通知内容" )
170- channel = Column (Enum (NotifyChannel , values_callable = lambda obj : [e .value for e in NotifyChannel ]), nullable = False , comment = "通知渠道" )
170+ channel = Column (Enum (NotifyChannel , values_callable = lambda obj : [e .value for e in NotifyChannel ]), nullable = True , comment = "通知渠道(单渠道模式) " )
171171 scheduled_time = Column (DateTime , nullable = False , comment = "计划发送时间" )
172172
173173 # 渠道配置(JSON格式字符串)
174- channel_config = Column (Text , nullable = False , comment = "渠道配置信息" )
174+ channel_config = Column (Text , nullable = True , comment = "渠道配置信息(单渠道模式)" )
175+
176+ # 多渠道支持
177+ channels_json = Column (Text , nullable = True , comment = "通知渠道数组(JSON格式,多渠道模式)" )
178+ channels_config_json = Column (Text , nullable = True , comment = "渠道配置映射(JSON格式,多渠道模式)" )
179+ send_results = Column (Text , nullable = True , comment = "各渠道发送结果(JSON格式)" )
175180
176181 # 状态相关
177182 status = Column (Enum (NotifyStatus , values_callable = lambda obj : [e .value for e in NotifyStatus ]), default = NotifyStatus .PENDING , comment = "发送状态" )
@@ -201,12 +206,28 @@ def to_dict(self):
201206 channel_config = ast .literal_eval (self .channel_config ) if self .channel_config else {}
202207 except :
203208 channel_config = {}
209+
210+ # 安全解析多渠道字段
211+ try :
212+ channels = json .loads (self .channels_json ) if self .channels_json else None
213+ except (json .JSONDecodeError , TypeError ):
214+ channels = None
215+
216+ try :
217+ channels_config = json .loads (self .channels_config_json ) if self .channels_config_json else None
218+ except (json .JSONDecodeError , TypeError ):
219+ channels_config = None
220+
221+ try :
222+ send_results = json .loads (self .send_results ) if self .send_results else None
223+ except (json .JSONDecodeError , TypeError ):
224+ send_results = None
204225
205- return {
226+ result = {
206227 'id' : self .id ,
207228 'title' : self .title ,
208229 'content' : self .content ,
209- 'channel' : self .channel .value ,
230+ 'channel' : self .channel .value if self . channel else None ,
210231 'scheduled_time' : self .scheduled_time .isoformat () if self .scheduled_time else None ,
211232 'status' : self .status .value ,
212233 'sent_time' : self .sent_time .isoformat () if self .sent_time else None ,
@@ -217,6 +238,14 @@ def to_dict(self):
217238 'channel_config' : channel_config ,
218239 'external_uid' : self .external_uid
219240 }
241+
242+ # 添加多渠道字段(如果存在)
243+ if channels :
244+ result ['channels' ] = channels
245+ result ['channels_config' ] = channels_config
246+ result ['send_results' ] = send_results
247+
248+ return result
220249
221250
222251# 数据库配置
@@ -240,13 +269,82 @@ def init_db():
240269 except Exception :
241270 print ("Migrating: Adding calendar_token to users table..." )
242271 conn .execute (text ("ALTER TABLE users ADD COLUMN calendar_token VARCHAR(64)" ))
272+ conn .commit ()
243273
244274 # 2. 检查 notify_tasks.external_uid
245275 try :
246276 conn .execute (text ("SELECT external_uid FROM notify_tasks LIMIT 1" ))
247277 except Exception :
248278 print ("Migrating: Adding external_uid to notify_tasks table..." )
249279 conn .execute (text ("ALTER TABLE notify_tasks ADD COLUMN external_uid VARCHAR(255)" ))
280+ conn .commit ()
281+
282+ # 3. 检查 notify_tasks.channels_json(多渠道支持)
283+ try :
284+ conn .execute (text ("SELECT channels_json FROM notify_tasks LIMIT 1" ))
285+ except Exception :
286+ print ("Migrating: Adding multi-channel support fields to notify_tasks table..." )
287+ conn .execute (text ("ALTER TABLE notify_tasks ADD COLUMN channels_json TEXT" ))
288+ conn .execute (text ("ALTER TABLE notify_tasks ADD COLUMN channels_config_json TEXT" ))
289+ conn .execute (text ("ALTER TABLE notify_tasks ADD COLUMN send_results TEXT" ))
290+ conn .commit ()
291+
292+ # 4. 移除 channel 和 channel_config 的 NOT NULL 约束(多渠道模式需要)
293+ # SQLite 不支持直接修改列约束,需要重建表
294+ try :
295+ # 检查是否已经迁移(通过尝试插入 channel=NULL 的记录)
296+ result = conn .execute (text ("SELECT sql FROM sqlite_master WHERE type='table' AND name='notify_tasks'" ))
297+ table_schema = result .fetchone ()[0 ]
298+
299+ # 如果表结构中 channel 字段仍有 NOT NULL 约束
300+ if 'channel' in table_schema and 'channel TEXT NOT NULL' in table_schema :
301+ print ("Migrating: Removing NOT NULL constraint from channel and channel_config..." )
302+
303+ # 创建临时表
304+ conn .execute (text ("""
305+ CREATE TABLE notify_tasks_new (
306+ id INTEGER PRIMARY KEY AUTOINCREMENT,
307+ user_id INTEGER NOT NULL,
308+ title VARCHAR(200) NOT NULL,
309+ content TEXT NOT NULL,
310+ channel TEXT,
311+ scheduled_time DATETIME NOT NULL,
312+ channel_config TEXT,
313+ channels_json TEXT,
314+ channels_config_json TEXT,
315+ send_results TEXT,
316+ status TEXT DEFAULT 'pending',
317+ sent_time DATETIME,
318+ error_msg TEXT,
319+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
320+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
321+ is_recurring BOOLEAN DEFAULT 0,
322+ cron_expression VARCHAR(100),
323+ external_uid VARCHAR(255),
324+ FOREIGN KEY (user_id) REFERENCES users(id)
325+ )
326+ """ ))
327+
328+ # 复制数据
329+ conn .execute (text ("""
330+ INSERT INTO notify_tasks_new
331+ SELECT id, user_id, title, content, channel, scheduled_time,
332+ channel_config, channels_json, channels_config_json, send_results,
333+ status, sent_time, error_msg, created_at, updated_at,
334+ is_recurring, cron_expression, external_uid
335+ FROM notify_tasks
336+ """ ))
337+
338+ # 删除旧表
339+ conn .execute (text ("DROP TABLE notify_tasks" ))
340+
341+ # 重命名新表
342+ conn .execute (text ("ALTER TABLE notify_tasks_new RENAME TO notify_tasks" ))
343+
344+ conn .commit ()
345+ print ("Migration completed: channel and channel_config are now nullable" )
346+ except Exception as e :
347+ print (f"Channel nullable migration info: { e } " )
250348 except Exception as e :
251349 print (f"Migration warning: { e } " )
252350
0 commit comments