diff --git a/tems/imgs/1.png b/tems/imgs/1.png deleted file mode 100644 index 6fb49bd6..00000000 Binary files a/tems/imgs/1.png and /dev/null differ diff --git a/tems/imgs/2.png b/tems/imgs/2.png deleted file mode 100644 index 5c839ae7..00000000 Binary files a/tems/imgs/2.png and /dev/null differ diff --git a/tems/imgs/3.png b/tems/imgs/3.png deleted file mode 100644 index e01e0b80..00000000 Binary files a/tems/imgs/3.png and /dev/null differ diff --git a/tems/test1.js b/tems/test1.js deleted file mode 100644 index fac0ecd8..00000000 --- a/tems/test1.js +++ /dev/null @@ -1,563 +0,0 @@ - $s = class { - constructor() { - gl(this, "root_url", "https://www.cloudtentacles.com"); - gl(this, "cache", new Map) - } - static getInstance() { - return $s.instance || ($s.instance = new $s) - } - PemToArrayBuffer(t) { - const r = t.split(` -`).filter(s => s.trim() && !s.includes("---")).join("") - , a = window.atob(r) - , o = new Uint8Array(a.length); - for (let s = 0; s < a.length; s++) - o[s] = a.charCodeAt(s); - return o.buffer - } - async encryptWithPublicKey(t) { - for (let s in t) - (t[s] === void 0 || t[s] === null || t[s] === "") && delete t[s]; - const n = Date.now() - , r = Math.random().toString(16) - , a = "ct-client" - , o = JSON.stringify(Object.assign({ - t: n, - r, - s: a - }, t)); - try { - if (window.crypto && window.crypto.subtle && window.crypto.subtle.importKey) { - const i = await window.crypto.subtle.importKey("spki", this.PemToArrayBuffer($s.PublicKey), { - name: "RSA-OAEP", - hash: { - name: "SHA-256" - } - }, !0, ["encrypt"]) - , u = new TextEncoder().encode(o) - , c = await window.crypto.subtle.encrypt({ - name: "RSA-OAEP" - }, i, u) - , f = new Uint8Array(c); - let p = ""; - for (let h = 0; h < f.byteLength; h++) - p += String.fromCharCode(f[h]); - return { - covert: window.btoa(p), - t: n, - r - } - } - console.warn("\u5F53\u524D\u5904\u4E8E\u975E\u5B89\u5168\u73AF\u5883\u6216\u6D4F\u89C8\u5668\u4E0D\u652F\u6301 WebCrypto\uFF0C\u5DF2\u56DE\u9000\u5230 forge \u52A0\u5BC6"); - const l = Td.pki.publicKeyFromPem($s.PublicKey).encrypt(o, "RSA-OAEP", { - md: Td.md.sha256.create(), - mgf1: { - md: Td.md.sha256.create() - } - }); - return { - covert: Td.util.encode64(l), - t: n, - r - } - } catch (s) { - throw console.error("\u52A0\u5BC6\u5931\u8D25:", s), - "\u52A0\u5BC6\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6D4F\u89C8\u5668\u73AF\u5883" - } - } - cacheCheck(t, n) { - if (n > 0) { - const r = this.cache.get(t); - if (r && Date.now() - r.time <= n) - return JSON.parse(JSON.stringify(r.value)) - } - return null - } - parametersToKey(t, n) { - let r = t; - for (let a in n) - a !== "token" && (r += `${a}:${n[a]}`); - return r - } - async httpProto(t, n, r) { - const a = this.parametersToKey(t, n) - , o = this.cacheCheck(a, r.interval || 0); - if (o) - return o; - const s = await B6({ - url: t, - data: n, - method: r.type - }) - , {data: l, code: i, message: u, token: c} = s.data; - switch (i) { - case bu.AlreadyExpired: - throw hs().setToken(""), - ei.replace("/login"), - "\u767B\u5F55\u5DF2\u8FC7\u671F"; - case bu.UpdateSuccess: - hs().setToken(c); - case bu.Success: - return r.interval && r.interval > 0 && this.cache.set(a, { - time: Date.now(), - value: JSON.parse(JSON.stringify(l)) - }), - l; - case bu.Fail: - throw u; - default: - throw "\u672A\u77E5\u9519\u8BEF" - } - } - async https(t, n={}, r={}) { - return await this.httpProto(t, n, Object.assign({}, $s.default_header, r)) - } - async getVerifCode(t, n) { - const r = await this.encryptWithPublicKey({ - account: t, - phone: n - }); - await this.https("/public/verif_code", r) - } - async logIn(t, n, r, a) { - const o = await this.encryptWithPublicKey({ - account: t, - password: cu(n), - phone: r, - code: a - }); - return await this.https("/public/login", o) - } - async getUserInfo() { - return await this.https("/user/info", {}, { - load: !1 - }) - } - async getAsset() { - return await this.https("/user/get_asset", {}, { - type: "GET", - load: !1 - }) - } - async getPermission() { - return await this.https("/user/get_permission", {}, { - type: "GET", - load: !1 - }) - } - async getRecordRange(t, n, r) { - return await this.https("/record/range", { - record_code: t, - start_date: n, - end_date: r - }, { - load: !1 - }) - } - async getSkuList() { - return await this.https("/sku/list", {}, { - type: "GET", - interval: 6e4 - }) - } - async selectSkuList(t) { - return await this.https("/sku/select_list", { - id: t - }) - } - async getCategoryList() { - return await this.https("/categories/get", {}, { - type: "GET", - interval: 6e4 - }) - } - async getSubList() { - return await this.https("/user/sub", {}, { - type: "GET" - }) - } - async updatePwd(t, n) { - const r = await this.encryptWithPublicKey({ - o_password: cu(t), - n_password: cu(n) - }); - return await this.https("/user/modify_pwd", r) - } - async giveAmount(t, n, r) { - return await this.https("/user/give", { - amount: t, - sub_id: n, - notes: r - }) - } - async giveSku(t, n, r, a) { - return await this.https("/user/give_sku", { - sku_id: t, - sub_id: n, - count: r, - notes: a - }) - } - async recoveryAmount(t, n, r) { - return await this.https("/user/recovery", { - amount: t, - sub_id: n, - notes: r - }) - } - async getUserPreferentialPolicies() { - return await this.https("/sku/user_pref_poli", {}, { - type: "GET", - interval: 6e4 - }) - } - async buyGoods(t, n) { - return await this.https("/sku/buy", { - id: t, - count: n - }) - } - async getKnapsack() { - const t = await this.https("/user/get_knapsack", {}, { - type: "GET" - }); - return R8e().setKnapsackInfo(t), - t - } - async use(t) { - return await this.https("/sku/use", t) - } - async getIdleVirtualNumber(t) { - return await this.https("/vn/list", { - key: t - }) - } - async getVirtualNumberBindUrl(t, n) { - return await this.https("/vn/bind_url", { - id: t, - key: n - }) - } - async getVirtualNumberGameUserName(t, n) { - return await this.https("/vn/bind_info", [{ - id: t, - key: n - }]) - } - async inquiryUID(t, n) { - return await this.https("/vn/inquiry", { - uid: t, - key: n - }) - } - async applyVirtualNumber(t, n) { - return await this.https("/vn/appoint", { - key: t, - phone: n - }) - } - async returnVirtualNumber(t, n) { - return await this.https("/vn/back", { - key: t, - id: n - }) - } - async generateLoginCode(t, n) { - return await this.https("/vn/generate_login_code", { - key: t, - id: n - }) - } - async verifyLoginCode(t, n, r) { - return await this.https("/vn/verify_login_code", { - key: t, - id: n, - code: r - }) - } - async vnVerifCode(t, n) { - return await this.https("/public/vn_verif_code", { - key: t, - phone: n - }) - } - async getRecordList(t, n, r, a, o) { - return await this.https("/record/paging", { - record_code: t, - page: n, - size: r, - start_date: a, - end_date: o - }) - } - async sendEMailCode(t, n, r) { - return await this.https("/vn/send_email_code", { - id: t, - key: n, - uid: r - }) - } - async verifyEMailBindGame(t, n, r, a) { - return await this.https("/vn/verify_email_code", { - id: t, - key: n, - uid: r, - code: a - }) - } - async direct_bind(t, n, r) { - return await this.https("/vn/direct_bind", { - id: t, - key: n, - uid: r - }) - } - async getUserAuthority(t) { - return await this.https("/admin/get_user_permissions", { - id: t - }) - } - async updateUserAuthority(t) { - return await this.https("/admin/modify_user_permissions", t) - } - async modifyUser(t) { - return await this.https("/admin/modify_user", t) - } - async getUserList() { - return await this.https("/user/list", {}, { - type: "GET" - }) - } - async getRelationshipList() { - return await this.https("/relationship/list", {}, { - type: "GET" - }) - } - async addRelationshipList(t, n) { - return await this.https("/relationship/add", { - user_id: t, - sub_id: n - }) - } - async delRelationshipList(t, n) { - return await this.https("/relationship/del", { - user_id: t, - sub_id: n - }) - } - async editRelationship(t, n) { - return await this.https("/relationship/modify", { - id: t, - open_repay: n - }) - } - async getCategoryAllList() { - return await this.https("/admin/get_categories_list", {}, { - type: "GET" - }) - } - async getSkuAllList() { - return await this.https("/admin/get_sku_list", {}, { - type: "GET" - }) - } - async getPreferentialPoliciesAll() { - return await this.https("/preferential/list", {}, { - type: "GET" - }) - } - async getUserPreferentialPoliciesToID(t) { - return await this.https("/preferential/get_relationship", { - id: t - }) - } - async createUser(t) { - const n = cu(t.password); - return await this.https("/admin/creator_user", Object.assign(t, { - password: n - })) - } - async updateUserPassword(t, n) { - const r = await this.encryptWithPublicKey({ - account: t, - n_password: cu(n) - }); - return await this.https("/admin/modify_pwd", r) - } - async updateUserStatus(t, n) { - return await this.https("/admin/ban_user", { - id: t, - expire: n - }) - } - async createSku(t) { - return await this.https("/admin/add_sku", t) - } - async createCategory(t) { - return await this.https("/admin/add_category", t) - } - async createPreferentialPolicies(t) { - return await this.https("/preferential/add", t) - } - async addUserPreferentialPolicies(t, n, r, a) { - return await this.https("/preferential/add_relationship", { - p_id: t, - u_id: n, - show_time: r, - hide_time: a - }) - } - async addRelationshipLow(t) { - return await this.https("/preferential/add_relationship_low", t) - } - async batchRelationshipLow(t) { - return await this.https("/preferential/batch_relationship_low", t) - } - async delUserPreferentialPolicies(t, n) { - return await this.https("/preferential/del_Relationship", { - p_id: t, - u_id: n - }) - } - async updateCategory(t) { - return await this.https("/admin/modify_category", t) - } - async updateSku(t) { - return await this.https("/admin/modify_sku", t) - } - async updatePreferentialPolicies(t) { - return await this.https("/preferential/modify", t) - } - async upsertCustom(t) { - return await this.https("/record/edit_custom", t) - } - async delCustom(t) { - return await this.https("/record/remove_custom", { - id: t - }) - } - async subRangeRecord(t) { - return await this.https("/record/sub_range", { - ids: t - }) - } - async transaction_record() { - return await this.https("/record/transaction_record") - } - async recipient_record() { - return await this.https("/record/recipient_record") - } - async observerSalesRecords(t, n, r) { - return await this.https("/observer/sales_records", { - ids: t, - start_date: n, - end_date: r - }) - } - async observerTransactionRecord(t) { - return await this.https("/observer/transaction_record", { - ids: t - }) - } - async observerHoldConsume(t) { - return await this.https("/observer/hold_consume", { - ids: t - }) - } - async observerPaging(t, n, r, a, o, s) { - return await this.https("/observer/paging", { - ids: t, - record_code: n, - page: r, - size: a, - start_date: o, - end_date: s - }) - } - async observerSub(t) { - return await this.https("/observer/sub", { - ids: t - }) - } - async getYeZiList() { - return await this.https("/utils/list") - } - async applyYeZiPhone(t) { - return await this.https("/utils/apply", { - id: t - }) - } - async releaseYeZiPhone(t) { - return await this.https("/utils/release", { - phone: t - }) - } - async messageYeZiPhone(t) { - return await this.https("/utils/message", { - phone: t - }) - } - async getProjectList() { - return await this.https("/utils/project_list") - } - async getHaoZhuList() { - return await this.https("/utils/list_hz") - } - async applyHaoZhuPhone(t) { - return await this.https("/utils/apply_hz", { - id: t + "" - }) - } - async releaseHaoZhuPhone(t) { - return await this.https("/utils/release_hz", { - phone: t - }) - } - async messageHaoZhuPhone(t) { - return await this.https("/utils/message_hz", { - phone: t - }) - } - async getHaoZhuProjectList() { - return await this.https("/utils/project_list_hz") - } - async returnOfGoods(t) { - return await this.https("/admin/return_of_goods", t) - } - async customRepay(t) { - return await this.https("/admin/custom_repay", t) - } - async upsertSKU(t) { - return await this.https("/sku/upsert", { - data: t - }) - } - async upsertCDK(t) { - await this.https("/admin/upsert_cdk", { - data: t - }) - } - async getCDKList(t, n) { - return await this.https("/admin/cdk", { - page: t, - size: n - }) - } -} -; -let Sr = $s; -gl(Sr, "instance"), -gl(Sr, "default_header", { - type: "POST", - interval: 0 -}), -gl(Sr, "PublicKey", `-----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSzjk+9st2f8IpYqYaM5 - F7K27ZR2IvzxTr2w4zgZAS2+BRdgMvXUOITdFQT+ytYF/r3pIgTkL7MF9UQuylmb - S57DiqGBLNBS7FzKyYzWTOc9yYA3+KrarbvpUn/Bm3UK+KKB17viPy+0pnNAHysJ - pzqadmAtQhI1ZDujJ2sW+FkV/pC8KIFahiyPIHjncy1ZG/BbRF3BDhvDcr6O7scO - GsHstGf9XsZBIITS5LYmlai7OdY4ZIVxUtQbfWzibihO+YDJJzFgt47drX0Ajo85 - PfQTPNA++KsXtwRX9M6Re3vkTDRsutIIWKtj8jqhUbbYS3vzS8GJnAWavUFVkR15 - 0wIDAQAB - -----END PUBLIC KEY-----`), -gl(Sr, "LocalCacheKey", "YCJ-LocalCacheKey"); \ No newline at end of file diff --git a/前后端现状分析.md b/前后端现状分析.md deleted file mode 100644 index 6a20138a..00000000 --- a/前后端现状分析.md +++ /dev/null @@ -1,354 +0,0 @@ -# 前后端现状分析 - -## 当前结论 - -整体架构方向没有跑偏。 - -- 后端主干仍然是 `routes -> services -> repositories` -- 前端后台管理也已经从“单页巨石”逐步转成“薄页面 + composable + 子组件 + 分领域 service” - -和最初相比,当前最重要的变化有两点: - -1. 平台配置这条前后端竖线,第一阶段拆分已经基本完成 -2. 真正还需要优先处理的,已经转向后端运行时巨石模块 - -所以现在不适合再把精力平均分散到全项目,而应该把已经拆开的区域收口,把还没拆开的大文件优先解决。 - -## 已完成 - -### 1. 平台配置页前端拆分 - -这部分已经不再是最初判断里的“一个页面承载四套完整平台流”。 - -- `8e1a4be` 拆分前端后台管理 API 客户端 - - `apps/frontend/src/services/admin.ts` 已改成聚合入口 - - 后台 API 已按领域拆到 `apps/frontend/src/services/admin/` - -- `dff7da7` 拆分平台配置页脚本逻辑 - - `apps/frontend/src/views/admin/AdminPlatformShopsView.vue` 的平台状态和行为已经拆到 composable - - 已有: - - `useAdminAgisoPlatform.ts` - - `useAdminNinetyonePlatform.ts` - - `useAdminKuaishouEticketPlatform.ts` - - `useAdminCloudtentaclesPlatform.ts` - -- `4650c3e` 抽离平台配置页共享样式 - - 共享样式已统一收口到 `apps/frontend/src/styles/admin-platform-shops.css` - -- `60c2d91`、`254a031`、`8f6a72f` 完成平台区块拆分 - - 四个平台区块已经分别下沉为独立组件: - - `AdminPlatformAgisoSection.vue` - - `AdminPlatformNinetyoneSection.vue` - - `AdminPlatformKuaishouEticketSection.vue` - - `AdminPlatformCloudtentaclesSection.vue` - -- `58f25fa` 抽离平台配置结果信息卡片 - - 新增 `AdminResultCard.vue` - - `快手核销` / `cloudtentacles` 的重复结果卡片已统一 - -### 2. `session-proof.js` 已完成第一轮拆分 - -这条线已经不是“下一步待开始”,而是已经落地完成。 - -当前 `apps/backend/src/services/session/` 下已经拆出: - -- `session-proof-mode.js` -- `session-proof-paths.js` -- `session-proof-result-writer.js` -- `session-proof-beijing-time.js` -- `session-proof-renderer.js` -- `session-proof-html.js` -- `session-proof-constants.js` - -`apps/backend/src/services/session/session-proof.js` 现在已经收敛成门面层,文件体量约 `145` 行,风险比之前明显下降。 - -### 3. 后台平台配置服务已完成按平台拆分 - -这条线也已经不是最初的 `admin-platform-config-service.js` 巨石形态了。 - -最近这波提交已经完成: - -- `d629bc9` 整理后台平台配置服务目录结构 -- `d11879f` 下沉后台履约规则校验编排逻辑 -- `8c9d487` 下沉后台云触手会话结果组装逻辑 -- `dad646b` 抽离后台平台配置写入归一化逻辑 -- `3a7c311` 按平台拆分后台平台配置服务 - -当前 `apps/backend/src/services/admin/platform-config/` 已形成按职责拆分的目录: - -- `service.js` - - 只保留统一导出入口 -- `agiso-service.js` -- `ninetyone-service.js` -- `kuaishou-eticket-service.js` -- `cloudtentacles-service.js` -- `fulfillment-bindings-service.js` -- `kuaishou-cloud-fulfillment-service.js` -- 配套 helper / mapper / validation / test 文件 - -其中 `service.js` 已经从原来的 600+ 行缩到 53 行,平台配置后端竖线的第一阶段拆分已经完成。 - -## 已过时的判断 - -下面这些判断不应该再按旧版本理解。 - -### 1. `AdminPlatformShopsView.vue` 是前端第一优先级 - -这个判断已经过时。 - -原因: - -- 四个平台模板已经拆开 -- 主要行为已经下沉到 composable -- 剩余问题更多是收口、少量复用和测试,而不是继续大拆页面 - -新判断: - -- 这条线仍可继续小步优化 -- 但优先级已经明显低于后端 `session.js`、`claim-session-service.js`、`admin-write-service.js` - -### 2. `admin-platform-config-service.js` 是后端当前主战场 - -这个判断也已经过时。 - -原因: - -- 这条线已经迁到 `apps/backend/src/services/admin/platform-config/` -- 统一入口 `service.js` 已经很薄 -- 大部分公共逻辑也已经拆到 `context.js`、`writes.js`、`validation.js`、`mappers.js`、`fulfillment.js` 等模块 - -新判断: - -- 平台配置后端已经从“继续大拆”阶段,进入“适度收口 + 补测试 + 控制 cloudtentacles 继续膨胀”阶段 - -### 3. 下一刀应该从 `session-proof.js` 开始 - -这个判断也已过时。 - -原因: - -- `session-proof.js` 第一轮拆分已经完成 -- 现在更该处理的是仍然明显偏大的运行时主线文件 - -## 仍然优先拆分 / 优先优化 - -### 1. `apps/backend/src/services/admin/admin-write-service.js` - -这是现在最胖、最值得优先处理的大文件之一,当前约 `1710` 行。 - -它更像“后台万能动作总线”,跨域过多: - -- 库存 -- claim -- webhook -- cloudtentacles -- 快手核销 -- 腾讯 session -- task 手工动作 - -建议方向: - -- `admin-inventory-write` -- `admin-task-actions` -- `admin-claim-actions` -- `admin-webhook-actions` -- `admin-manual-redeem-actions` - -如果继续放大,这类文件会成为后续所有后台动作改动的冲突中心。 - -### 2. `apps/backend/src/services/claim/claim-session-service.js` - -当前约 `1255` 行,仍然是后端运行时第二优先级。 - -问题主要在: - -- 管理态 / 用户态创建逻辑平行复制 -- claim token、浏览器会话、任务同步、库存释放、自动发货收尾混在一起 - -建议方向: - -- `claim-context` -- `claim-session-lifecycle` -- `claim-task-sync` -- `claim-fulfillment-finalizer` - -这条线和下单、兑换、任务状态强相关,越晚拆越难动。 - -### 3. `apps/backend/src/services/session/session.js` - -当前约 `566` 行,虽然比前两项小,但仍然是高风险运行时核心。 - -它同时承担: - -- Playwright 浏览器生命周期 -- 会话内存状态 -- 二维码抓取 -- 登录态判断 -- 角色信息同步 -- 兑换执行 -- 自动关闭 - -好消息是:`session-proof.js`、`session-qq.js`、`session-wx.js`、`session-redeem.js`、`session-page.js`、`session-state.js` 等子模块已经存在,说明这条线并不是没拆过。 - -当前更合适的目标不是“从零拆分”,而是继续把 `session.js` 剩余的编排职责下沉,最终把它收敛成真正的 orchestrator。 - -### 4. 平台配置竖线的收口项 - -虽然平台配置的大拆分已经完成,但这条线还有一些第二优先级问题: - -- `apps/backend/src/services/admin/platform-config/cloudtentacles-service.js` - - 当前约 `250` 行 - - 已经比原来小很多,但仍同时承载登录、会话校验、商品目录、虚拟号调试、完整 flow 调试 - - 如果后面 cloudtentacles 功能继续增加,可以进一步拆成: - - `cloudtentacles-auth-service` - - `cloudtentacles-catalog-service` - - `cloudtentacles-virtual-number-service` - -- `apps/frontend/src/views/admin/AdminPlatformShopsView.vue` - - 当前约 `402` 行 - - 已经不是巨石模板问题,剩下主要是 overview 卡片和页面级加载编排还在父层 - -- `apps/frontend/src/services/admin/platform-config.ts` - - 当前约 `373` 行 - - 还可以进一步按平台拆成更细的 `services/admin/platform-config/` 子文件 - - 但这属于可做项,不是最高优先级 - -### 5. `apps/backend/src/services/webhook/webhook-service.js` - -这条线仍然适合改成流水线。 - -建议拆成: - -- `webhook-parse` -- `webhook-verify` -- `webhook-ignore-policy` -- `webhook-enrich` -- `webhook-process` -- `webhook-replay` - -它不是眼下最大的文件,但属于典型“流程很多、分支很多、回归成本高”的区域。 - -### 6. `apps/backend/src/config/runtime.js` - -仍需要“配置声明化”优化。 - -目前问题不是功能错,而是长期可维护性差: - -- env override 越堆越长 -- 新平台配置接入成本越来越高 -- 难测、难查、难对照 - -建议后期改成: - -- 配置 schema -- env 映射表 -- 默认值与来源声明 - -## 质量防线现状 - -这个判断仍然成立,而且现在重要性更高。 - -当前风险: - -- 后端高风险模块仍然存在 `@ts-nocheck` -- 平台配置页和后台平台配置服务最近连续经历了多轮重构 -- 当前主要还是依赖 `typecheck + test + build` 防回归 - -这能挡住: - -- 类型问题 -- 引用错误 -- 明显的构建错误 -- 一部分纯函数退化 - -但仍挡不住: - -- 页面条件渲染退化 -- 管理台复杂交互异常 -- 任务状态流转串线 -- 外部平台调试链路断裂 - -建议至少补两层最小防线: - -1. 前端 - - 平台配置页基础渲染 smoke 测试 - - 关键按钮存在性 / 条件渲染测试 - -2. 后端 - - `claim-session-service.js` 关键状态流转测试 - - `admin-write-service.js` 高风险动作 smoke 测试 - -## 暂时不用急着拆 - -### 腾讯浏览器前端链路 - -- `TencentBrowserView.vue` -- `session-qq.js` -- `session-wx.js` - -这些区域当前模式相对健康,仍然可以作为其它模块拆分时的参考。 - -## 下一步计划 - -### 已完成阶段 - -1. 拆分前端后台管理 API 客户端 -2. 拆分平台配置页脚本逻辑 -3. 抽离平台配置页共享样式 -4. 拆分 Agiso / 91卡券 / 快手核销 / cloudtentacles 四个平台区块 -5. 抽离结果信息卡片 -6. 拆分 `session-proof.js` -7. 重构后台 `platform-config` 目录并完成按平台拆分 - -### 下一阶段建议 - -#### Phase 1:先处理 `admin-write-service.js` - -目标: - -- 优先拆掉当前最大的后台动作总线 -- 先按职责切片,不改路由公开接口 -- 把高频变更区域从一个文件拆成多个动作模块 - -建议步骤: - -1. 先按动作域分段 - - inventory - - task - - claim - - webhook - - redeem - -2. 把纯组装和副作用编排分开 - -3. 保留 `admin-write-service.js` 作为门面导出层 - -#### Phase 2:再处理 `claim-session-service.js` - -前提: - -- `admin-write-service.js` 的动作边界更清楚 -- claim / task / inventory 之间的依赖关系已经更容易梳理 - -#### Phase 3:继续收口 `session.js` - -重点: - -- 不是重写 -- 而是继续把剩余运行时编排下沉到已有 session 子模块 - -#### Phase 4:补平台配置页与后台动作的最小 smoke 测试 - -这一步不一定最先做,但应该尽早插入,避免后续继续重构时缺乏防线。 - -## 当前建议 - -平台配置这条前后端竖线现在可以先收口,不建议继续把它当主战场。 - -更合适的下一步是: - -1. 开始拆 `apps/backend/src/services/admin/admin-write-service.js` -2. 同步梳理 `claim-session-service.js` 的职责边界 -3. 尽快补一层最小 smoke 测试 - -如果继续执行,下一刀最值得从 `admin-write-service.js` 开始。