Advance UIKit rewrite with AMap integration and core UI modules.
Integrate高德 SDK with simulator-safe build flags, add map views for operating area and punch points, refactor main tabs and key feature screens to UIKit with Diffable lists, and document Swift concurrency defaults in AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
471
Scripts/polish_swift_doc_comments.py
Normal file
471
Scripts/polish_swift_doc_comments.py
Normal file
@ -0,0 +1,471 @@
|
||||
#!/usr/bin/env python3
|
||||
"""修正自动生成的低质量 Swift 文档注释。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1] / "suixinkan_ios"
|
||||
|
||||
METHOD_OVERRIDES: dict[str, str] = {
|
||||
"startIfNeeded": "条件满足时启动轮询任务。",
|
||||
"runLoop": "按间隔持续轮询队列数据。",
|
||||
"pollOnce": "执行一次队列数据拉取与播报判断。",
|
||||
"pollIntervalSeconds": "根据前后台状态返回轮询间隔秒数。",
|
||||
"selectedSpotId": "读取用户当前选中的打卡点 ID。",
|
||||
"resetSnapshot": "重置播报状态与上次轮询快照。",
|
||||
"beginBackgroundTask": "申请后台任务,保证进入后台时仍可短时轮询。",
|
||||
"endBackgroundTask": "结束后台任务。",
|
||||
"resumePending": "恢复所有等待播报结束的 continuation。",
|
||||
"enqueue": "将语音文本加入播报队列,可选择替换待播内容。",
|
||||
"registerNavigationBridge": "向 UIKit 导航桥注册当前 Tab 的 NavigationController。",
|
||||
"syncFromRouterPath": "根据 Router 路径深度补齐或回退导航栈。",
|
||||
"syncRouterPathFromStack": "导航栈变化后裁剪 Router 路径,保持双向同步。",
|
||||
"observeContextChanges": "监听权限与账号上下文变化并刷新菜单。",
|
||||
"rebuildMenus": "按当前角色权限重建首页菜单与常用应用。",
|
||||
"menuItem": "按 URI 解析并返回可用菜单项。",
|
||||
"startCountdownTimerIfNeeded": "在线状态下启动位置上报倒计时。",
|
||||
"scenicTapped": "点击景区名称,跳转景区选择页。",
|
||||
"reminderTapped": "选择位置上报提前提醒时间。",
|
||||
"locationReportTapped": "跳转位置上报页面。",
|
||||
"paymentTapped": "跳转立即收款页面。",
|
||||
"taskCreateTapped": "跳转提交任务页面。",
|
||||
"onlineTapped": "切换在线/离线状态并更新倒计时。",
|
||||
"setupTopBar": "搭建首页顶部景区选择栏。",
|
||||
"setupTableView": "配置首页分组列表布局与注册 Cell。",
|
||||
"makeStatusCard": "构建在线状态与倒计时卡片。",
|
||||
"makeLocationCard": "构建位置上报提醒卡片。",
|
||||
"makeStoreCard": "构建门店信息卡片。",
|
||||
"makeQuickActionsRow": "构建快捷操作按钮行。",
|
||||
"configureViews": "初始化子视图与约束。",
|
||||
"refreshPresentation": "根据当前状态刷新展示或隐藏动画。",
|
||||
"showBanner": "展示 Toast 横幅动画。",
|
||||
"hideBanner": "隐藏 Toast 横幅动画。",
|
||||
"loadAnimation": "加载 Lottie 动画资源。",
|
||||
"route": "解析推送载荷并执行路由跳转。",
|
||||
"navigateHomeRoute": "切换至首页 Tab 并 Push 目标路由。",
|
||||
"hexString": "将二进制数据转为十六进制字符串。",
|
||||
"receiveLoop": "持续接收 WebSocket 消息并分发处理。",
|
||||
"stringValue": "安全地将任意值转为字符串。",
|
||||
"homeRouteURI": "将首页路由编码为 URI 字符串。",
|
||||
"make": "按路由参数创建 ViewController。",
|
||||
"fail": "记录校验失败并返回 false。",
|
||||
"normalizedError": "将后端错误信息归一化为用户可读中文。",
|
||||
"amountText": "格式化金额展示文本。",
|
||||
"summaryCard": "创建统计摘要卡片视图。",
|
||||
"selectPeriod": "切换统计周期并重新加载数据。",
|
||||
"reload": "加载或刷新页面数据。",
|
||||
"loadMore": "加载下一页列表数据。",
|
||||
"updateFilterTitle": "更新筛选条件标题展示。",
|
||||
"updateSummary": "刷新钱包摘要区域。",
|
||||
"setupHeader": "搭建页面头部区域。",
|
||||
"updateTitle": "刷新导航或页面标题。",
|
||||
"setupFormHeader": "搭建表单页头部说明区域。",
|
||||
"wireViewModel": "绑定 ViewModel 变更回调并触发列表刷新。",
|
||||
"bindViewModel": "绑定 ViewModel 数据变更并刷新 UI。",
|
||||
"handleRefresh": "下拉刷新触发,重新加载页面数据。",
|
||||
"mountChild": "按登录阶段挂载对应子控制器。",
|
||||
"updateScenicTitle": "刷新顶部景区名称展示。",
|
||||
}
|
||||
|
||||
LINE_REPLACEMENTS: dict[str, str] = {
|
||||
"/// speechSynthesizer 方法实现。": "/// 语音播报生命周期回调。",
|
||||
"/// 处理相关事件。": "/// 更新轮询结果并根据队列变化触发语音播报。",
|
||||
"/// tableView 方法实现。": "/// UITableView 数据源或代理回调。",
|
||||
"/// 启动IfNeeded流程。": "/// 条件满足时启动轮询任务。",
|
||||
"/// 执行Loop循环或任务。": "/// 按间隔持续轮询队列数据。",
|
||||
"/// 轮询Once数据。": "/// 执行一次队列数据拉取与播报判断。",
|
||||
"/// 轮询IntervalSeconds数据。": "/// 根据前后台状态返回轮询间隔秒数。",
|
||||
"/// 重置Snapshot状态。": "/// 重置播报状态与上次轮询快照。",
|
||||
"/// 恢复Pending流程。": "/// 恢复所有等待播报结束的 continuation。",
|
||||
"/// 更新ScenicTitle状态。": "/// 刷新顶部景区名称展示。",
|
||||
"/// 绑定ViewModel回调或数据。": "/// 绑定 ViewModel 数据变更并刷新 UI。",
|
||||
"/// 响应lineTapped事件。": "/// 切换在线/离线状态并更新倒计时。",
|
||||
"/// observeContextChanges 方法实现。": "/// 监听权限与账号上下文变化并刷新菜单。",
|
||||
"/// rebuildMenus 方法实现。": "/// 按当前角色权限重建首页菜单与常用应用。",
|
||||
"/// menuItem 方法实现。": "/// 按 URI 解析并返回可用菜单项。",
|
||||
"/// 启动CountdownTimerIfNeeded流程。": "/// 在线状态下启动位置上报倒计时。",
|
||||
"/// scenicTapped 方法实现。": "/// 点击景区名称,跳转景区选择页。",
|
||||
"/// reminderTapped 方法实现。": "/// 选择位置上报提前提醒时间。",
|
||||
"/// locationReportTapped 方法实现。": "/// 跳转位置上报页面。",
|
||||
"/// paymentTapped 方法实现。": "/// 跳转立即收款页面。",
|
||||
"/// taskCreateTapped 方法实现。": "/// 跳转提交任务页面。",
|
||||
"/// selectedSpotId 方法实现。": "/// 读取用户当前选中的打卡点 ID。",
|
||||
"/// beginBackgroundTask 方法实现。": "/// 申请后台任务,保证进入后台时仍可短时轮询。",
|
||||
"/// endBackgroundTask 方法实现。": "/// 结束后台任务。",
|
||||
}
|
||||
|
||||
METHOD_LINE = re.compile(r"^(?P<indent>\s*)/// (?P<name>\w+) 方法实现。\s*$")
|
||||
FUNC_LINE = re.compile(r"^\s*(?:@\w+(?:\([^)]*\))?\s+)*(?:override\s+|private\s+|static\s+)*func\s+(?P<name>\w+)")
|
||||
|
||||
WORD_HINTS: dict[str, str] = {
|
||||
"Tapped": "点击",
|
||||
"Setup": "初始化",
|
||||
"Configure": "配置",
|
||||
"Update": "更新",
|
||||
"Reload": "刷新",
|
||||
"Load": "加载",
|
||||
"Fetch": "请求",
|
||||
"Create": "创建",
|
||||
"Delete": "删除",
|
||||
"Clear": "清空",
|
||||
"Copy": "复制",
|
||||
"Show": "展示",
|
||||
"Hide": "隐藏",
|
||||
"Start": "启动",
|
||||
"Stop": "停止",
|
||||
"Cancel": "取消",
|
||||
"Confirm": "确认",
|
||||
"Apply": "提交",
|
||||
"Parse": "解析",
|
||||
"Format": "格式化",
|
||||
"Decode": "解码",
|
||||
"Encode": "编码",
|
||||
"Validate": "校验",
|
||||
"Submit": "提交",
|
||||
"Toggle": "切换",
|
||||
"Refresh": "刷新",
|
||||
"Build": "构建",
|
||||
"Make": "创建",
|
||||
"Present": "弹出",
|
||||
"Dismiss": "关闭",
|
||||
"Select": "选择",
|
||||
"Filter": "筛选",
|
||||
"Handle": "处理",
|
||||
"Check": "检查",
|
||||
"Request": "请求",
|
||||
"Register": "注册",
|
||||
"Bind": "绑定",
|
||||
"Sync": "同步",
|
||||
"Mount": "挂载",
|
||||
"Embed": "嵌入",
|
||||
"Generate": "生成",
|
||||
"Edit": "编辑",
|
||||
"Add": "添加",
|
||||
"Reset": "重置",
|
||||
"Save": "保存",
|
||||
"Open": "打开",
|
||||
"Close": "关闭",
|
||||
"Login": "登录",
|
||||
"Logout": "登出",
|
||||
"Complete": "完成",
|
||||
"Continue": "继续",
|
||||
"Consume": "消费",
|
||||
"Dedupe": "去重",
|
||||
"Scanner": "扫码",
|
||||
"Camera": "相机",
|
||||
"Permission": "权限",
|
||||
"Avatar": "头像",
|
||||
"Header": "头部",
|
||||
"Card": "卡片",
|
||||
"Row": "行",
|
||||
"Field": "字段",
|
||||
"Timer": "计时器",
|
||||
"Countdown": "倒计时",
|
||||
"Queue": "排队",
|
||||
"Order": "订单",
|
||||
"Task": "任务",
|
||||
"Project": "项目",
|
||||
"Scenic": "景区",
|
||||
"Store": "门店",
|
||||
"Wallet": "钱包",
|
||||
"Payment": "支付",
|
||||
"Album": "相册",
|
||||
"Schedule": "排班",
|
||||
"Invite": "邀请",
|
||||
"Message": "消息",
|
||||
"Location": "位置",
|
||||
"Live": "直播",
|
||||
"Flyer": "飞手",
|
||||
"Punch": "打卡",
|
||||
"Detail": "详情",
|
||||
"List": "列表",
|
||||
"Summary": "摘要",
|
||||
"Amount": "金额",
|
||||
"Code": "验证码",
|
||||
"QR": "二维码",
|
||||
"URL": "链接",
|
||||
"Text": "文本",
|
||||
"Title": "标题",
|
||||
"Form": "表单",
|
||||
"Keyboard": "键盘",
|
||||
"Business": "营业",
|
||||
"Time": "时间",
|
||||
"Date": "日期",
|
||||
"Folder": "文件夹",
|
||||
"File": "文件",
|
||||
"Broadcast": "播报",
|
||||
"Voice": "语音",
|
||||
"Background": "后台",
|
||||
"Foreground": "前台",
|
||||
"Remote": "远程",
|
||||
"Scan": "扫码",
|
||||
"Verify": "验证",
|
||||
"Collection": "集合",
|
||||
"Table": "列表",
|
||||
"View": "视图",
|
||||
"Model": "模型",
|
||||
"Data": "数据",
|
||||
"Payload": "载荷",
|
||||
"Snapshot": "快照",
|
||||
"Runtime": "运行时",
|
||||
"Settings": "设置",
|
||||
"Menu": "菜单",
|
||||
"Route": "路由",
|
||||
"Navigation": "导航",
|
||||
"Tab": "Tab",
|
||||
"Badge": "角标",
|
||||
"Filter": "筛选",
|
||||
"Search": "搜索",
|
||||
"Upload": "上传",
|
||||
"Download": "下载",
|
||||
"Image": "图片",
|
||||
"Video": "视频",
|
||||
"Audio": "音频",
|
||||
"Map": "地图",
|
||||
"Spot": "打卡点",
|
||||
"Ticket": "票号",
|
||||
"Stats": "统计",
|
||||
"Report": "上报",
|
||||
"Audit": "审核",
|
||||
"Settlement": "结算",
|
||||
"Withdrawal": "提现",
|
||||
"Deposit": "押金",
|
||||
"Refund": "退款",
|
||||
"WriteOff": "核销",
|
||||
"Pilot": "飞手",
|
||||
"Certification": "认证",
|
||||
"RealName": "实名",
|
||||
"Account": "账号",
|
||||
"Switch": "切换",
|
||||
"Profile": "个人中心",
|
||||
"Auth": "认证",
|
||||
"Session": "会话",
|
||||
"Token": "令牌",
|
||||
"Push": "推送",
|
||||
"Notification": "通知",
|
||||
"Socket": "WebSocket",
|
||||
"Client": "客户端",
|
||||
"Provider": "提供者",
|
||||
"Factory": "工厂",
|
||||
"Helper": "工具",
|
||||
"Extension": "扩展",
|
||||
"IfNeeded": "(按需)",
|
||||
"And": "并",
|
||||
"Or": "或",
|
||||
"From": "从",
|
||||
"To": "至",
|
||||
"For": "用于",
|
||||
"With": "附带",
|
||||
"Without": "不带",
|
||||
"All": "全部",
|
||||
"Current": "当前",
|
||||
"Selected": "选中",
|
||||
"Pending": "待处理",
|
||||
"Active": "活跃",
|
||||
"Empty": "空",
|
||||
"Default": "默认",
|
||||
"Custom": "自定义",
|
||||
"Local": "本地",
|
||||
"Remote": "远程",
|
||||
"Legacy": "兼容旧版",
|
||||
"Normalized": "规范化",
|
||||
"Coerced": "强制转换",
|
||||
"Lossy": "宽松",
|
||||
"Deduplicated": "去重",
|
||||
"Sorted": "排序",
|
||||
"Changed": "变更",
|
||||
"Interval": "间隔",
|
||||
"Threshold": "阈值",
|
||||
"Positive": "正数",
|
||||
"Decimal": "小数",
|
||||
"Int": "整数",
|
||||
"String": "字符串",
|
||||
"Bool": "布尔",
|
||||
"Value": "值",
|
||||
"Object": "对象",
|
||||
"JSON": "JSON",
|
||||
"LatLng": "经纬度",
|
||||
"LngLat": "经纬度",
|
||||
"Pair": "坐标对",
|
||||
"LooksLike": "判断是否符合",
|
||||
"Fill": "填充",
|
||||
"Cycle": "循环切换",
|
||||
"ApplyViewModel": "应用 ViewModel 状态",
|
||||
"Wire": "连接",
|
||||
"Dispose": "释放订阅",
|
||||
"Embed": "嵌入",
|
||||
"Labeled": "带标签",
|
||||
"LongValid": "长期有效",
|
||||
"ClearListAndDetail": "清空列表与详情",
|
||||
"ClearQueueData": "清空排队数据",
|
||||
"ClearMessages": "清空消息",
|
||||
"ClearFolders": "清空文件夹",
|
||||
"CheckPermissionAndStart": "检查权限并启动",
|
||||
"ConsumePendingRemoteCalledTickets": "消费待处理的远程叫号",
|
||||
"ConsumePendingScanCodeIfNeeded": "按需消费待处理扫码结果",
|
||||
"FillFormIfNeeded": "按需回填表单",
|
||||
"CopyOrderNumber": "复制订单号",
|
||||
"CopyDownloadLink": "复制下载链接",
|
||||
"CopyCode": "复制验证码",
|
||||
"CopyURL": "复制链接",
|
||||
"ConfirmVerify": "确认验证码",
|
||||
"CreatePunchPoint": "创建打卡点",
|
||||
"CreateTask": "创建任务",
|
||||
"CreateProject": "创建项目",
|
||||
"AddSchedule": "添加排班",
|
||||
"ApplyAmount": "提交金额",
|
||||
"BusinessTime": "营业时间",
|
||||
"BusinessTimePayload": "构建营业时间请求体",
|
||||
"AvatarPlaceholder": "生成头像占位图",
|
||||
"FormatQueueTime": "格式化排队时间",
|
||||
"FromJSONObject": "从 JSON 对象解析",
|
||||
"GenerateQRCode": "生成二维码",
|
||||
"Int64Value": "解析 Int64 值",
|
||||
"IntValue": "解析 Int 值",
|
||||
"DecimalValue": "解析小数值",
|
||||
"EmptyToZero": "空值转零",
|
||||
"FileType": "解析文件类型",
|
||||
"DecodeLossyBool": "宽松解码布尔值",
|
||||
"DecodeLossyDouble": "宽松解码浮点数",
|
||||
"DecodeLossyInt": "宽松解码整数",
|
||||
"DecodeLossyString": "宽松解码字符串",
|
||||
"LiveDecodeLossyInt": "直播模块宽松解码整数",
|
||||
"LiveDecodeLossyString": "直播模块宽松解码字符串",
|
||||
"CoercedBroadcastInterval": "规范化播报间隔",
|
||||
"CoercedCountdownThreshold": "规范化倒计时阈值",
|
||||
"LegacyPositiveInt": "兼容旧版正整数解析",
|
||||
"DeduplicatedAndSorted": "去重并排序",
|
||||
"CompleteLogin": "完成登录流程",
|
||||
"LoginTapped": "点击登录按钮",
|
||||
"LogoutTapped": "点击登出",
|
||||
"CancelTapped": "点击取消",
|
||||
"ConfirmTapped": "点击确认",
|
||||
"CloseTapped": "点击关闭",
|
||||
"ContinueTapped": "点击继续",
|
||||
"EditTapped": "点击编辑",
|
||||
"DetailTapped": "点击详情",
|
||||
}
|
||||
|
||||
|
||||
def split_camel(name: str) -> list[str]:
|
||||
parts = re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", name).split()
|
||||
return parts
|
||||
|
||||
|
||||
def describe_method(name: str) -> str:
|
||||
if name in METHOD_OVERRIDES:
|
||||
return METHOD_OVERRIDES[name]
|
||||
if name in WORD_HINTS:
|
||||
return f"{WORD_HINTS[name]}。"
|
||||
|
||||
# 尝试从最长前缀/后缀匹配组合词
|
||||
if name.endswith("Tapped"):
|
||||
base = name[:-6]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"点击{hint}的处理逻辑。"
|
||||
if name.endswith("IfNeeded"):
|
||||
base = name[:-8]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"按需{hint}。"
|
||||
if name.startswith("make"):
|
||||
base = name[4:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"创建{hint}。"
|
||||
if name.startswith("setup"):
|
||||
base = name[5:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"初始化{hint}。"
|
||||
if name.startswith("update"):
|
||||
base = name[6:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"更新{hint}。"
|
||||
if name.startswith("load"):
|
||||
base = name[4:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"加载{hint}。"
|
||||
if name.startswith("clear"):
|
||||
base = name[5:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"清空{hint}。"
|
||||
if name.startswith("copy"):
|
||||
base = name[4:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"复制{hint}。"
|
||||
if name.startswith("create"):
|
||||
base = name[6:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"创建{hint}。"
|
||||
if name.startswith("decode"):
|
||||
base = name[6:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"解码{hint}。"
|
||||
if name.startswith("format"):
|
||||
base = name[6:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"格式化{hint}。"
|
||||
if name.startswith("live"):
|
||||
base = name[4:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"直播{hint}相关逻辑。"
|
||||
if name.startswith("flyer"):
|
||||
base = name[5:]
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in split_camel(base)) or base
|
||||
return f"飞手{hint}相关逻辑。"
|
||||
|
||||
parts = split_camel(name)
|
||||
if len(parts) == 1:
|
||||
return f"{name} 业务逻辑。"
|
||||
hint = "".join(WORD_HINTS.get(p, p) for p in parts)
|
||||
return f"{hint}相关逻辑。"
|
||||
|
||||
|
||||
def process_file(path: Path) -> bool:
|
||||
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
|
||||
output: list[str] = []
|
||||
changed = False
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
stripped = line.rstrip("\n")
|
||||
if stripped in LINE_REPLACEMENTS:
|
||||
indent = re.match(r"^(\s*)", line).group(1)
|
||||
output.append(f"{indent}{LINE_REPLACEMENTS[stripped]}\n")
|
||||
changed = True
|
||||
continue
|
||||
|
||||
match = METHOD_LINE.match(stripped)
|
||||
if match:
|
||||
indent = match.group("indent")
|
||||
name = match.group("name")
|
||||
output.append(f"{indent}/// {describe_method(name)}\n")
|
||||
changed = True
|
||||
continue
|
||||
|
||||
if stripped.startswith("/// ") and i + 1 < len(lines):
|
||||
func_match = FUNC_LINE.match(lines[i + 1])
|
||||
if func_match:
|
||||
name = func_match.group("name")
|
||||
if name in METHOD_OVERRIDES and ("方法实现" in stripped or name in stripped):
|
||||
indent = re.match(r"^(\s*)", line).group(1)
|
||||
output.append(f"{indent}/// {METHOD_OVERRIDES[name]}\n")
|
||||
changed = True
|
||||
continue
|
||||
|
||||
output.append(line)
|
||||
|
||||
if changed:
|
||||
path.write_text("".join(output), encoding="utf-8")
|
||||
return changed
|
||||
|
||||
|
||||
def main() -> int:
|
||||
target = Path(sys.argv[1]) if len(sys.argv) > 1 else ROOT
|
||||
count = sum(1 for path in sorted(target.rglob("*.swift")) if process_file(path))
|
||||
print(f"Polished {count} files")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user