'新增计划字段+持续同步功能'

This commit is contained in:
zelong 2026-01-12 19:38:33 +08:00
parent ecf9ccbc0b
commit 7db5d87a94
5 changed files with 216 additions and 66 deletions

View File

@ -10,7 +10,7 @@ docid = dcOsT3czWy0YEDg38vlDqwVCTjv0kzwC_GU2XmT9wSZctQ0ZJQUAV7vMQ3ljZx-n_NqxzEEY
[Schedule]
# 同步频率(分钟)
sync_interval = 1
sync_interval = 30
[wework]
# 企业微信应用ID

View File

@ -1,4 +1,4 @@
{
"access_token": "LsTV1Z6Ye4h2c77s1oKx4zasisAn4QiTbqXmZRUYchZRzp6Q_igpVWp22oEhv8rTHuYyB1LKfqhLjQ9qvgwU13fZns6t-aNBDMw00BBf76V2XqoKKXPHNQZGKQly8RH9NgFMoqfYiKeDnoCdbYnvRjgn8oYoc0AvnCu_qU9TdP8XJgLO01-inoLGSEdTLZnWXf90uf4RztU8xH1a7nuM1p31w58AT7iV3h8Uv9aPOe8",
"fetch_time": 1767954012.568217
"access_token": "9-OFdr2nbGo-1sSAYBujyvTtP5v88W0MjLSy3BzHayMEw0tX0tQ8qsbYXshNdHURsLqjXh4dVaRs2HIxvy2gXbWglBg90YtJU_3Yxfz-EXkCUT6Yyt1U1Z6ojUzWkwG6esUX9rWndHWK7hdXusZj5vpHBqVyQpY1Pi3eVKpB28puC_7IXsgbUaW5-JenDP3C7AnAs_x7jbsECXD8Y2XjZymhBKEwf_jZH-iK1Ojnvxk",
"fetch_time": 1768214302.441097
}

View File

@ -227,27 +227,25 @@ class SmartSheetSync:
# 直接使用任务一的 update_records 方法已添加debug=1
return self.api.update_records(sheet_id, update_records)
def get_records_with_tapd_link(self, sheet_id: str) -> List[Dict]:
def get_records_with_tapd_link(self, sheet_id: str,
all_records: List[Dict] = None) -> List[Dict]:
"""
获取所有包含TAPD链接的记录
获取所有包含TAPD链接的记录同步状态为空
Args:
sheet_id: 子表ID
all_records: 可选已获取的所有记录列表避免重复获取
Returns:
List[Dict]: 包含TAPD链接的记录列表每条记录包含
- record: 原始记录对象
- record_id: 记录ID
- tapd_link: TAPD链接
- story_id: 解析出的需求单号如果解析成功
- parse_success: 链接解析是否成功
- parse_error: 解析失败的错误信息
List[Dict]: 包含TAPD链接的记录列表
"""
print(f"正在获取包含TAPD链接的记录...")
print(f"正在获取包含TAPD链接的新记录...")
if all_records is None:
all_records = self.get_all_records(sheet_id)
all_records = self.get_all_records(sheet_id)
records_with_link = []
skipped_synced_count = 0 # 统计跳过的已同步记录数
skipped_synced_count = 0
for record in all_records:
tapd_link = self.extract_tapd_link(record)
@ -313,6 +311,68 @@ class SmartSheetSync:
FIELD_PLAN: self.api.get_field_value_by_title(record, FIELD_PLAN),
}
def get_synced_records_for_update(self, sheet_id: str,
terminal_statuses: List[str],
all_records: List[Dict] = None) -> List[Dict]:
"""
获取需要持续同步的已同步记录
筛选条件
- 同步状态 = "成功"
- TAPD状态 不在终态列表中
Args:
sheet_id: 子表ID
terminal_statuses: 终态列表 ['已完成', '取消']
all_records: 可选已获取的所有记录列表避免重复获取
Returns:
List[Dict]: 需要持续同步的记录列表
"""
print(f"正在获取需要持续同步的记录...")
if all_records is None:
all_records = self.get_all_records(sheet_id)
records_for_update = []
skipped_terminal_count = 0
for record in all_records:
# 检查同步状态是否为"成功"
sync_status = self.api.get_field_value_by_title(record, FIELD_SYNC_STATUS)
if sync_status != "成功":
continue
# 检查TAPD链接是否存在
tapd_link = self.extract_tapd_link(record)
if not tapd_link:
continue
# 检查TAPD状态是否为终态
tapd_status = self.api.get_field_value_by_title(record, FIELD_TAPD_STATUS)
if tapd_status in terminal_statuses:
skipped_terminal_count += 1
continue
# 解析链接获取story_id
success, result, link_type = parse_tapd_link(tapd_link)
if not success:
continue
record_info = {
"record": record,
"record_id": record.get('record_id', ''),
"tapd_link": tapd_link,
"story_id": result,
"current_status": tapd_status,
}
records_for_update.append(record_info)
print(f" ✓ 找到 {len(records_for_update)} 条需要持续同步的记录")
if skipped_terminal_count > 0:
print(f" - 跳过终态记录: {skipped_terminal_count}")
return records_for_update
def process_sheet(api: SmartSheetSync, sheet_id: str, sheet_title: str) -> Dict:
"""

View File

@ -20,7 +20,7 @@ sys.path.insert(0, str(project_root))
from src.token_manager import TokenManager
from src2.config import Task2ConfigManager
from src2.logger import get_task2_logger
from src2.tapd_api import TAPDStoryApi
from src2.tapd_api import TAPDStoryApi, TERMINAL_STATUSES
from src2.smartsheet_sync import SmartSheetSync, REQUIRED_FIELDS
@ -181,65 +181,76 @@ class SyncService:
print(f" ⚠ 跳过此子表: {sheet_result['skip_reason']}")
return sheet_result
# 2. 获取包含TAPD链接的记录
records_with_link = self.smartsheet.get_records_with_tapd_link(sheet_id)
# 2. 获取所有记录(只获取一次,供新记录同步和持续同步共用)
print(f"正在获取所有记录...")
all_records = self.smartsheet.get_all_records(sheet_id)
# 3. 获取包含TAPD链接的新记录同步状态为空
records_with_link = self.smartsheet.get_records_with_tapd_link(
sheet_id, all_records=all_records
)
sheet_result["records_with_link"] = len(records_with_link)
if not records_with_link:
print(f" 没有包含TAPD链接的记录")
return sheet_result
# 4. 处理新记录
if records_with_link:
print(f"\n--- 新记录同步 ---")
success_records = [] # 成功同步的记录
failed_record_ids = [] # 失败的记录ID列表
# 3. 处理每条记录
success_records = [] # 成功同步的记录(包含业务字段 + 同步状态)
failed_record_ids = [] # 失败的记录ID列表
for record_info in records_with_link:
record_result = self._process_record(record_info)
sheet_result["details"].append(record_result)
for record_info in records_with_link:
record_result = self._process_record(record_info)
sheet_result["details"].append(record_result)
if record_result["success"]:
sheet_result["records_synced"] += 1
if record_result["success"]:
sheet_result["records_synced"] += 1
if record_result["update_record"]:
success_records.append(record_result["update_record"])
sheet_result["records_updated"] += 1
else:
sheet_result["records_failed"] += 1
failed_record_ids.append(record_info["record_id"])
if record_result["update_record"]:
success_records.append(record_result["update_record"])
sheet_result["records_updated"] += 1
else:
sheet_result["records_failed"] += 1
failed_record_ids.append(record_info["record_id"])
# 收集失败记录的详细信息用于推送
failed_record = {
"sheet_title": sheet_title,
"record_id": record_info["record_id"],
"tapd_link": record_info.get("tapd_link", "(无链接)"),
"error_message": record_result.get("error_message", "未知错误")
}
sheet_result["failed_records"].append(failed_record)
# 收集失败记录的详细信息用于推送
failed_record = {
"sheet_title": sheet_title,
"record_id": record_info["record_id"],
"tapd_link": record_info.get("tapd_link", "(无链接)"),
"error_message": record_result.get("error_message", "未知错误")
}
sheet_result["failed_records"].append(failed_record)
# 4. 批量回写成功记录
if success_records:
print(f"\n正在回写 {len(success_records)} 条成功记录...")
try:
self.smartsheet.batch_update_records(sheet_id, success_records)
print(f" ✓ 成功记录回写完成")
except Exception as e:
print(f" ✗ 成功记录回写失败: {e}")
# 4. 批量回写成功记录(业务字段 + 同步状态=成功)
if success_records:
print(f"\n正在回写 {len(success_records)} 条成功记录...")
try:
self.smartsheet.batch_update_records(sheet_id, success_records)
print(f" ✓ 成功记录回写完成")
except Exception as e:
print(f" ✗ 成功记录回写失败: {e}")
# 5. 批量回写失败记录
if failed_record_ids:
print(f"\n正在回写 {len(failed_record_ids)} 条失败记录的状态...")
try:
failed_updates = [
self.smartsheet.build_update_record(
record_id=record_id,
sync_status="失败"
)
for record_id in failed_record_ids
]
self.smartsheet.batch_update_records(sheet_id, failed_updates)
print(f" ✓ 失败记录状态回写完成")
except Exception as e:
print(f" ✗ 失败记录状态回写失败: {e}")
else:
print(f" 没有新记录需要同步")
# 5. 批量回写失败记录(只写入同步状态=失败)
if failed_record_ids:
print(f"\n正在回写 {len(failed_record_ids)} 条失败记录的状态...")
try:
failed_updates = [
self.smartsheet.build_update_record(
record_id=record_id,
sync_status="失败"
)
for record_id in failed_record_ids
]
self.smartsheet.batch_update_records(sheet_id, failed_updates)
print(f" ✓ 失败记录状态回写完成")
except Exception as e:
print(f" ✗ 失败记录状态回写失败: {e}")
# 7. 持续同步:更新已同步记录的最新状态
print(f"\n--- 持续同步 ---")
update_result = self._process_synced_records_update(sheet_id, all_records=all_records)
sheet_result["continuous_sync"] = update_result
sheet_result["total_records"] = len(records_with_link)
@ -371,6 +382,82 @@ class SyncService:
return False
def _process_synced_records_update(self, sheet_id: str,
all_records: List[Dict] = None) -> Dict[str, Any]:
"""
处理已同步记录的状态更新持续同步
Args:
sheet_id: 子表ID
all_records: 可选已获取的所有记录列表
Returns:
Dict: 更新结果统计
"""
result = {
"checked": 0,
"updated": 0,
"failed": 0,
}
# 获取需要持续同步的记录
records = self.smartsheet.get_synced_records_for_update(
sheet_id, TERMINAL_STATUSES, all_records=all_records
)
if not records:
print(f" 没有需要持续同步的记录")
return result
result["checked"] = len(records)
updates = []
for record_info in records:
try:
# 查询TAPD最新状态
story_info = self.tapd_api.get_story(record_info["story_id"])
# 提取最新字段值
new_status = story_info.get('status', '')
new_owner = story_info.get('owner', '')
new_begin = story_info.get('begin', '')
new_due = story_info.get('due', '')
plan_id = story_info.get('custom_plan_field_1', '')
new_plan = self.tapd_api.map_plan_id_to_name(plan_id)
# 获取当前值并比较
current = self.smartsheet.get_current_field_values(record_info["record"])
needs_update = self._check_needs_update(
current, new_status, new_owner, new_begin, new_due, new_plan
)
if needs_update:
update_record = self.smartsheet.build_update_record(
record_id=record_info["record_id"],
status=new_status,
owner=new_owner,
begin_date=new_begin,
due_date=new_due,
plan=new_plan
)
updates.append(update_record)
except Exception as e:
result["failed"] += 1
if self.test_mode:
print(f" ✗ 记录 {record_info['record_id']} 更新失败: {e}")
# 批量更新
if updates:
print(f" 正在更新 {len(updates)} 条记录...")
self.smartsheet.batch_update_records(sheet_id, updates)
result["updated"] = len(updates)
print(f" ✓ 持续同步更新完成")
else:
print(f" ✓ 所有记录状态均未变化")
return result
def _send_failure_notification(self, failed_records: List[Dict]) -> None:
"""
发送同步失败通知

View File

@ -28,6 +28,9 @@ STATUS_MAPPING = {
"status_13": "待评审",
}
# 需求终态列表(这些状态不需要持续同步)
TERMINAL_STATUSES = ['已完成', '取消']
def map_status(status_code: str) -> str:
"""