admin-migration-status.md 23 KB

Angular 管理后台与云函数迁移状态

已确认的迁移基线

  • 旧 Web 交付物是 ASP.NET Core 发布包,没有 .sln、.csproj 与原始 Razor 管理页面源码。
  • ZoomLaCMS.dll 可恢复 79 个后台控制器、至少 1,042 个公开动作;详细清单见 legacy-admin-inventory.generated.md。
  • 线上 Parse 应用为 7pIbDBJmKx_main,当前可见 109 个 Schema,其中 96 个为 SQL Server 规范化迁移目标类。
  • 核心数据已存在:CommonModel 92,435、VocabularyWord 89,620、NodeAuth 5,935、PracticeRecord 816、CourseAppointment 345、DailyStudyRecord 108、LessonRecord 82。
  • 迁移开始前 Function 表为空;本阶段已部署 9 个云函数。

本阶段已实现

  • Angular 18 独立管理端路由 /admin,包含独立登录、后台布局、概览、动态 Schema 表格、动态编辑表单、分页、搜索、软删除和迁移状态页。
  • 后台迁移状态页直接读取线上 xiaoshu.app.gateway/migration_status,动态显示已实现 action 数量及完整阻塞原因清单,不再使用前端硬编码阻塞计数。
  • 后台“全部数据类”目录由管理员云函数实时返回白名单,所有已开放规范化类均可从界面进入动态列表/编辑页;_Session、Function 等敏感系统类不进入目录。
  • 动态编辑器对 Object/Array 执行 JSON 类型校验,错误格式会阻止保存;Relation、File、GeoPoint 等无法安全通用写入的 Parse 类型由前后端共同设为只读,避免结构化字段被字符串覆盖。
  • 动态资源表按 objectId/name/title/username 等业务字段稳定优先展示,总页数采用向上取整;只读类使用查看抽屉且不显示保存/删除操作。
  • _User 详情抽屉已接入专用 resetPassword 管理员云函数,执行 8 位最小长度与二次确认;新密码不进入通用资源字段或前端持久化存储。
  • _User 新建入口已改为专用 createUser 管理员云函数:帐套内校验邀请账号,事务锁原子分配 legacyUserId,固定默认普通组 1,并初始化旧版余额/经验数据;请求不接受管理员或角色字段,开户密码也不进入通用资源草稿。
  • 旧 User_API 的锁定、解锁和移动用户组已迁为专用 userBatch 管理员云函数:支持每批 1–100 人、强制帐套边界,原子同步 isDisabled/legacyUserData.State 或 legacyGroupId/legacyUserData.GroupID;停用会撤销全部 Parse 会话,普通管理员不能停用自己或管理其他管理员。旧版账号登录也会拒绝已停用/已删除用户。
  • _User 的状态、旧用户 ID、旧用户组与旧资料 JSON 已从通用字段编辑中移除;密码重置和用户删除新增管理员目标保护并撤销目标会话,避免绕过专用一致性流程。
  • 当前线上 Group 表为空;用户组移动只接受当前帐套中真实存在的 Group.groupId,界面会明确提示先在“全部数据类”建立用户组,不会把不存在的默认编号伪装成可选组。
  • 旧 GroupAdd/GroupAdd_Submit 已迁为专用 saveGroup:新增组使用 PostgreSQL 事务锁在帐套内原子分配只读 groupId,校验同名组、父组归属与层级循环;通用保存不能绕过。删除组前会拒绝仍被用户或下级组引用的记录。
  • 旧 ContentManage_Status、ContentManage_Del、回收站恢复与 ContentMove_Submit 已迁为专用 contentBatch:支持每批 1–100 条内容、限定旧状态 -3/-2/-1/0/99、校验目标节点帐套并保留 addon 关联。CommonModel 的 generalId/modelId/nodeId/itemId/tableName/status/sourceKey/orderId 改为通用只读;新增按钮关闭,避免只写主表却遗漏模型附表。
  • 旧 NodeAdd_Submit、Node_API 与 UnionNode_Move 的核心栏目生命周期已迁为专用 saveNode/nodeBatch:新增栏目以临时来源键完成 Parse 初始写入,再使用 PostgreSQL 事务锁原子分配 nodeId/orderId 并替换为旧 sourceKey;保存会校验同级名称/目录重复、父栏目帐套和回收状态,移动会阻断自身/下级循环并递归修正所有后代深度。回收、恢复和每批 1–100 个栏目迁移已接入 Angular;永久删除仅在没有内容、下级栏目、节点权限和模型模板引用时开放,通用保存与删除不能绕过。
  • 旧 SpecialAdd_Save 与 UnionSpecial_Move 已迁为专用 saveSpecial/specialBatch:新增专题使用临时来源键和 PostgreSQL 事务锁原子分配 specId/orderId,保存时校验全帐套名称/目录唯一、父专题归属与层级循环;删除会拒绝仍有下级专题的记录。旧 UnionSpecial_Merge 被明确列为数据阻塞:CommonModel.specialId 只存在于 Parse Schema、PostgreSQL 物理列缺失,商品主表也未迁移,无法安全改写内容/商品专题关系。
  • 旧 ModelManage/Field/Order_Submit 的安全部分已迁为 saveModelMetadata/saveModelFieldMetadata/modelFieldOrder:现有模型可编辑名称、条目文案、图标、说明和内容模板元数据,现有字段可编辑别名、提示、显示/搜索配置,并可在同模型内批量排序;modelId/modelType/tableName/fieldId/modelId/fieldName/fieldType/sourceKey 等结构字段通用只读。新增、复制、删除模型或字段会显式返回 501,因为旧实现同时执行 PostgreSQL 建表/改列、标签生成和模板文件写入,目标托管环境没有该 DDL/旧模板运行时。
  • 旧 Guest/MsgList/ReplyGuest/MsgShow/Msg_API 已迁为 saveGuestbook/guestbookReply/guestbookBatch:留言列表支持帐套分页、标题搜索和每批 1–100 条审核/取消审核/回收/恢复;详情可白名单编辑标题与正文,管理员回复继承原分类和父 gid,再以事务锁原子分配新 gid/sourceKey。永久删除会拒绝仍有回复的留言;普通留言新增继续只走已上线 H5 留言云函数。gid/parentid/cateid/userid/status/sourceKey/gdate/ip 均通用只读。
  • 旧 Guest/Bar 共用的分类新增、编辑、推荐与删除已迁为 saveGuestCategory/guestCategoryBatch:新增分类用事务锁原子分配 cateid/orderId/sourceKey,gtype=0 留言分类固定顶级,gtype=1 贴吧分类支持父子结构并阻断循环;同父同类型名称防重,删除前同时检查下级分类、留言与帖子引用。贴吧发帖频率仍依赖旧 GuestConfig 文件,版块清空与用户级贴吧授权也尚未开放,因此对应动作保守标为部分实现。
  • 旧 Bar/TieList/TieDetail/TieMove/Tie_API 的帖子工作流已迁为 barBatch:每批 1–100 条支持审核/取消审核、回收/恢复、全局/版块置顶、取消排序、沉底、精华/取消精华和移动贴吧分类;移动会验证目标为同帐套 gtype=1 分类。GuestBar 在通用资源中改为结构只读,避免直接篡改帖子 ID、作者、父回复、状态、排序和精华标记。旧“勋章”动作依赖未迁移奖章关系表,继续保守标为部分实现而不伪造成功。
  • 旧 BarAuthSet/BarAuthSet_Submit 经逐浪官方源码模型复核,依赖独立 ZL_Guest_BarAuth 用户版块关系(Uid/BarID/Look/Send/Reply),并非仅靠 Guestcate.needLog/postAuth 即可等价实现;目标 109 个 Parse Schema 中只有 GuestBar/Guestcate,没有该权限关系或 ZL_Guest_Medals 勋章关系。管理员网关新增 barAuthorization/barMedal 显式 501 响应,避免界面或调用方误以为保存成功,并将两项授权动作列入最终数据阻塞清单。
  • 旧考试后台 Question_Class_Manage/AddQuestion_Class/Question_Class_GetChild/Question_Class_Add 已按官方 ZL_Exam_Class 模型迁到目标 ExamClass:saveExamClass 用 PostgreSQL 帐套事务锁原子分配 cId/cOrderBy/sourceKey,校验名称唯一、父分类归属和层级循环;examClassChildren 保留旧按父编号取子项能力,删除会拒绝仍有下级分类或 ExamSysQuestions.pClass 试题引用的记录。Angular 新增“试题分类”导航和结构表单;旧展开树视觉布局尚未复刻,因此列表动作仍保守标为部分实现。
  • 旧考试后台 ExamPointManage/AddExamPoint/ExamPoint_Add 已按官方 ZL_ExamPoint 模型迁到目标 ExamPoint:saveExamPoint 使用帐套事务锁原子分配 id/orderBy/sourceKey,自动记录创建管理员与时间,并校验名称唯一、上级考点归属和层级循环;删除拒绝仍有下级考点的记录。Angular 新增“考点管理”导航及父级/排序结构表单,通用保存不能绕过专用一致性规则。
  • 新版考试后台 KnowledgeManage/AddKnowledge/Knowledge_Add/Knowledge_GetChild 已单独迁到 QuestionsKnowledge,不再与早期把 ExamPoint 称作“知识点”的页面混用:saveKnowledge 原子分配 kId/kOrderBy/sourceKey,要求绑定同帐套真实 ExamClass,父知识点必须同科目,并阻断名称重复和层级循环;knowledgeChildren 保留科目内子级读取,删除会检查下级知识点及 ExamSysQuestions.pKnowledge 引用。Angular 新增知识点导航和科目/父级/排序结构表单;旧展开树视觉仍标为部分实现。
  • 旧考试后台 ExTeacherManage/AddExTeacher/Teacher_Add 已迁到 ExTeacher:saveExamTeacher 仅白名单保存姓名、岗位、授课、头像路径和简介,使用帐套事务锁原子分配旧 id/sourceKey,服务端记录创建管理员与时间,并校验任教 ExamClass 属于同一帐套。Angular 新增“考试教师”入口和任教分类结构控件;线上 Schema 扫描未发现指向 ExTeacher 的 Pointer 或明确数字外键,删除仍保留帐套隔离和审计。
  • 旧数据字典 DicCateList/DictCate_API/DicCateSel/DicItemList/DicItem_API 已按官方 ZL_Datadiccategory/ZL_Datadic 模型迁为 saveDictionaryCategory/saveDictionaryItem/dictionaryCategories/dictionaryBatch:分类与字典项分别分配旧 DicCateID/DicID,校验帐套内分类名称和分类内字典项名称防重,支持每批 1–100 条启用、停用和删除;分类仍有字典项时禁止删除。线上这两类的业务字段由迁移兼容层从登记数据还原,Parse Schema 虽声明字段但 PostgreSQL 物理表没有对应列,因此编号改为经 Parse 读取全局最大值并依靠唯一 sourceKey 冲突重试,未伪装成不可执行的物理列事务;新分类编号还会高于所有既有 diccate 引用,避免错误接管历史字典项。Angular 新增分类、字典项导航和帐套隔离分类选择控件。
  • 旧多级数据字典 GradeCateManage/GCateAdd/GCateAdd_Submit/GCate_API/GradeOption/GOptionAdd/GOptionAdd_Submit/GOption_API 已按官方 ZL_GradeCate/ZL_Grade 模型迁为 saveGradeCategory/saveGradeOption/gradeCategories/gradeOptions/gradeBatch:分类维护至少两级的 gradeField 层级别名,选项按分类和父选项自动推导 grade,校验同级名称防重、父级归属和最大层级;编辑选项只允许改名,不能篡改 cate/parentId/grade。分类和选项分别保护仍有选项、下级选项的删除,Angular 提供按分类及父选项查看当前层、专用新建表单与批量删除。GradeCate 的兼容层计数会错误返回 0,因此编号和读取同样使用 Parse 行结果与唯一 sourceKey 冲突重试,不依赖失真的 count 或缺失物理业务列。
  • 旧商城扩展 Currency/Currency_API 的现有货币管理已迁到目标 Currency:saveCurrency 白名单更新货币名称、2–10 位大写代码、符号、正数当前汇率与备注,并校验帐套内名称及代码唯一;currencyBatch 支持每批 1–100 条帐套隔离删除。CurrencyAdd/CurrencyAdd_Submit 明确列为基础设施阻塞:旧数字字段就叫 id,与 Parse SDK 对象保留 id 冲突,兼容层物理表又没有可写业务列,实测以 sourceKey 新增后重新读取仍无法得到旧数字 ID;Angular 已隐藏新增入口,云函数对新增返回 501,避免产生坏数据。
  • 旧系统 Config/Holiday/Holiday_API 的现有节假日管理已迁到 SysHoliday:saveHoliday 只允许更新名称、公/农历类型、MM月DD日 日期、影响天数、状态与展示文本,保护旧 ID、创建人、创建时间和来源键;holidayBatch 支持每批 1–100 条帐套隔离删除。HolidayAdd/HolidayAdd_Submit 因 SysHoliday.id 与 Parse 保留字段冲突且物理表缺少可写旧 ID 列而明确列为基础设施阻塞,Angular 隐藏新增入口,新增请求返回 501。目标 CourseAppointment/LessonRecord 均无节假日外键,未伪造删除级联。
  • 旧 Config/SearchFunc/SearchFunc_API/SearchFuncAdd/SearchFuncAdd_Submit 已映射到目标 Search(后台快捷入口,非内容搜索日志):searchNavigations 按管理后台/用户中心类型、启停和推荐状态筛选,saveSearchNavigation 白名单维护名称、站内路径、图标、打开方式、移动端、尺寸、颜色、用户组和推荐级别,searchNavigationBatch 覆盖启停、推荐/取消推荐、排序和删除。目标兼容层只能在不指定字段时还原旧数据;可回滚的临时创建—读取—删除实测证实 sourceKey 无法还原保留名为 id 的数字业务键,因此 Angular 隐藏新增,云函数返回 501。旧 starturl 依赖不存在的 SiteConfig.SiteOption.Admin_StartUrl 持久层,同样明确 501,未以浏览器本地存储伪造全局配置。
  • 旧 Config/APIInfo/ReadFile 页面的文档功能已迁为 Angular 云函数接口目录:apiCatalog 从实时 Function 元数据安全返回名称、路径、描述、参数、响应类型和更新时间,不暴露 code;界面可搜索并复制路径。FontIcon 替换为完整 Lucide 图标库的搜索、分页和点击复制页。旧 API 密钥/IP 策略、Swagger 开关、协议文件、进程关闭、KeyboardConfig/Hotkey XML 快捷键与 PrizeConfig/PrizeTask 奖励规则在目标 Schema/云函数运行时均无等价执行层;legacyConfigBlocker 对全部 11 个旧请求返回逐项 501 原因,避免伪保存。
  • 旧广告后台 Extend/AD/ZoneManage/Zone_API/InfoManage/Info_API 已映射到目标 AdZone/AdInfo:adZones/adInfos 支持帐套隔离、关键词、广告位类型/归属和启停/审核状态筛选;saveAdZone/saveAdInfo 对现有记录白名单维护版位、素材、链接、有效期、权重和状态,adZoneBatch/adInfoBatch 覆盖每批 1–100 条启停、审核、取消审核和删除,删除广告位前会阻止仍被广告内容引用。Angular 新增广告位、广告内容导航和当前生效广告预览;旧 HTML 仅作为源码文本展示,不在管理端执行脚本。可回滚创建探测证明 AdZone.id/AdInfo.id 被 Parse 当作保留字段拒绝,省略 id 又不会生成旧编号,因此两个新增分支明确返回 501,现有编辑分支继续可用。
  • 旧字体图后台的 Shape/ShapeInfo/ShapeInfo_Submit/Shape_API/ShapeType/ShapeTypeInfo/ShapeTypeInfo_Submit/ShapeType_API 已映射到目标 FontPicShape/FontPicShapeType:专用列表支持帐套、关键词和分类筛选;现有素材可维护名称、备注、分类并安全预览原路径,现有分类可维护名称和备注,删除分类前检查素材引用,素材/分类批量删除限制每次 1–100 条。可回滚探测证明省略 id 创建不会生成旧编号,因此新增素材/分类明确 501。图片替换也明确列为基础设施阻塞:线上实测 Parse.File SDK 与 /parse/files REST 端点均返回“要求 mongodb:// 或 mongodb+srv:// 连接串”,说明当前 PostgreSQL 部署的 Parse 文件存储适配器配置错误;Angular 不展示失效上传控件,云函数收到 file 时返回 501,避免伪成功。线上完整 Schema 核验只有 FontPicShape/FontPicShapeType/UserSIcon,其中 UserSIcon 是积分流水而非字体图;旧 FontPic 成品图和 FontPicDraft 草稿主表及数据均不存在,其 8 个动作由 legacyFontPicBlocker 返回逐项数据阻塞原因。
  • 旧问卷后台 SurveyManage/Survey_API/SurveyAdd/SurveyAdd_Submit/SurveyItemList/SurveyItemAdd/SurveyItemAdd_Submit/SurveyItem_API/SurveyResultChart/SurveyResultList 已按真实兼容模型 DesignAsk/DesignQuestion/DesignAnswer 迁移:问卷支持状态和关键词筛选、启停与删除,题目按问卷分页、编辑、排序和删除,答卷只读展示并可删除,结果统计从旧 answer JSON 聚合各选项计数且单独报告不可解析历史答卷。目标 SurveyItem/SurveyLog 属于新的考试测评模型,没有错误拼接到旧问卷。兼容层的 count 对 DesignAsk 错报 0,专用云函数改为读取实际行后分页;线上实际存在 2 份问卷、96 道题和 105 份答卷。可回滚 sourceKey 创建探测证明无法生成旧数字 ID,因此新增问卷和题目明确 501,现有记录编辑继续可用。Angular 新增问卷、题目、答卷入口、问卷范围筛选、批量工具和安全统计视图。
  • 旧客服后台 Default/Seat/SeatAdd/SeatAdd_Submit/CodeAdd/CodeAdd_Submit/Seat_API/CodeList 已映射到 ServiceSeat 与 Temp(useType=12):serviceSeats/saveServiceSeat 支持帐套隔离列表、现有编辑、绑定同帐套有效账号、默认席位互斥、排序与原子生成 sId/sourceKey,serviceCodes/saveServiceCode 只读取并白名单维护客服欢迎语的名称、HTML 内容和主题,serviceBatch 覆盖席位及欢迎语删除。Angular 新增客服席位、客服欢迎语入口、账号绑定控件和批量删除。旧逻辑在用户名不存在时以固定密码 123456 自动开户,该弱密码行为不迁移:云函数明确 501,并要求先走现有安全开户流程。Temp.id 与 Parse 保留字段冲突且省略 ID 无法生成旧编号,因此欢迎语新增明确阻塞。目标 Schema 完全缺少 ChatMsg 或等价消息类,MsgEx/MsgInfo/MsgList 以及 Seat_API.chat_del 由 legacyServiceChatBlocker 返回逐项数据阻塞原因。
  • _User、CommonModel、Node 与 Special 表格已补齐当前页全选和批量工具栏,可直接批量停用/解锁/移动用户组,审核、回收、恢复、移动内容节点,回收、恢复、移动栏目,以及移动专题;翻页或重新查询会清空选择,避免误操作隐藏页记录。
  • 迁移状态页已接入从反编译基线自动生成的 1,042 项旧后台动作台账,可按模块、action、原因与状态搜索筛选;当前保守登记为 86 项已实现、54 项部分实现、126 项数据阻塞、203 项基础设施阻塞、573 项待逐项核验。生成器和 admin-coverage:check 会阻止台账与源码基线漂移。
  • Parse 登录只在浏览器保存当前 sessionToken;masterKey 只在部署进程中使用。
  • 云函数网关强制校验管理员身份、company 帐套、类白名单、字段白名单和敏感字段过滤。
  • _Session 不开放,Function 源码不开放通用查询或编辑;用户密码只允许专用重置操作。
  • 8 个管理/CMS 函数均验证未认证请求返回 401;临时帐套管理员完成真实只读冒烟测试后已删除。
  • 第 9 个 xiaoshu.app.gateway 已覆盖旧 H5 源码的全部 91 个 action:28 个已映射,源码清单内 63 个显式返回 501 阻塞原因;动态调用的兼容 action content_add_zt 也已映射,product_add、product_upd 仍显式阻塞,因此线上为 29 个已实现、65 个阻塞项。经典注册/资料更新、课程词库进度、六类余额/积分账本读取、学习次数/生词状态、直属团队/VIP 统计、陪练预约学员分组、15 段复习计划、7 类规范化内容新增/更新、预约状态/课时账本/抗遗忘批量创建及旧版统计公式均已完成;缺主表的商品读取、缺短信校验的密码重置及其他缺副表模型继续按请求明确 501。详见 wxapp-cloud-action-matrix.md。
  • 旧数据的 isDeleted 在部分物理表与 Parse Schema 不一致,把它放入 Parse 查询条件会返回 500;网关改为公司条件查询后在结果侧兼容过滤。
  • Angular H5 已开始按页回归切换:账号密码登录、版本、栏目、公开内容、8 类 addon 联表,以及学习记录详情、预约详情、生词本、21 天抗遗忘列表可走云函数;私有读取只在真实 Parse 会话下切换,其余 action 仍由显式迁移路由保留旧端点。

SQL 视图 / 函数替代状态

旧对象 云函数替代 状态 说明
ZL_StripeHtmlTag、ZL_StripeTrimstr cms.content-normalizer 已实现 保守转换为纯文本,覆盖脚本、样式、实体和空白
ZL_EX_UserView cms.users.extended 已实现 过滤密码、哈希、会话、Token 与 Secret
ZL_Exam_ClassView cms.exams.classes 已实现基础投影 当前目标类缺少可证明的学校/年级 Pointer 时不伪造关联
ZL_Guest_BarView cms.guest.bar 已实现基础投影 支持帐套与分页
ZL_SearchView cms.search 已实现 搜索 CommonModel 并清理输出 HTML
ZL_User_PlatView cms.users.platform 已实现 使用合并进 _User 的 legacy 平台资料
ZL_User_WXView cms.users.wechat 已实现 使用合并进 _User 的微信资料并过滤密钥
ZL_CartProView — 数据阻塞 目标 Schema 无购物车类,且迁移登记没有可用行
ZL_Order_PayedView — 数据阻塞 目标 Schema 无订单/支付明细类
ZL_Order_ProSaleView — 数据阻塞 目标 Schema 无订单商品明细类
ZL_Order_ShareView — 数据阻塞 目标 Schema 无订单分享关系类
ZL_Plat_BlogView — 数据阻塞 目标 Schema 无博客正文/回复类

仍需逐页专用迁移

动态 Schema CRUD 覆盖的是规范化业务数据管理能力,不代表旧后台 1,042 个动作已逐页复刻。考试组卷、微信配置、订单支付、模板编辑、数据库备份、站点集群、PowerShell、文件管理等必须逐项判断:

  1. 有目标业务类且仍属当前产品范围:新增专用 Angular 页面和专用云函数。
  2. 需要第三方密钥或基础设施权限:取得新系统凭据后再实现,前端不得存储密钥。
  3. 依赖未迁移表、IIS、Windows 文件系统或旧 Zoomla 运行时:列入最终无法迁移清单,不以空成功响应代替。

管理员启用前置条件

线上 435 个 _User 在核验时没有任何 isAdmin=true、role=admin 或 adminRoleKey 管理员。系统因此默认不可登录管理端。上线前必须由持有 Parse masterKey 的部署人员为明确指定的现有用户设置:

  • isAdmin: true
  • roles: ["admin"]
  • company: Pointer<Company>

跨帐套管理员才可设置 adminRoleKey: "super-admin";不允许从浏览器执行管理员提权。