From 611b488af32cbcfab03ee74d10942d84e2c30994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Mon, 13 Apr 2026 15:30:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor(api):=20=E8=A7=84=E8=8C=83=E5=89=8D?= =?UTF-8?q?=E7=AB=AFAPI=E6=8E=A5=E5=8F=A3=E5=9C=B0=E5=9D=80=E5=B9=B6?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=B7=AF=E5=BE=84=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正开发者、小程序和企业相关API的基础URL,避免重复添加/api前缀 - 统一调整developer、enterprise、invite等模块接口路径,保持与后端Controller一致 - 新增权限申请及消息通知相关API,并完善相关函数实现 - 规范请求参数传递,移除多余包装,改用直接传递对象方式 - 新增API Key重置、版本发布/回滚等接口支持 feat(invite): 重构邀请登录流程支持未注册快速加入 - 完整重写invite页,分离已注册用户和未注册用户的按钮显示和交互 - 实现未注册用户微信手机号授权后自动注册登录及自动加入应用 - 已注册用户直接确认加入应用,无需手机号授权弹窗 - 统一使用getPhoneNumber按钮处理两种状态并修复“授权码不能为空”报错 - 前端配合后端改造,支持已登录用户可直接通过Authorization头加入应用 fix(developer): 修复开发者中心应用加载问题 - 修复只查询创建的应用导致邀请加入的应用不显示的问题 - 同时请求创建应用和参与应用接口,合并并去重应用列表 - 新增pageJoinedApp接口调用及合并逻辑,提高应用列表完整性和体验 --- .workbuddy/expert-history.json | 2 +- .workbuddy/memory/2026-04-12.md | 174 ++++++++++++++++++++++++++++++ .workbuddy/memory/2026-04-13.md | 42 ++++++++ src/api/developer/developer.ts | 182 ++++++++++++++++++++++++-------- src/api/developer/enterprise.ts | 43 +++++--- src/developer/index.tsx | 48 +++++++-- 6 files changed, 419 insertions(+), 72 deletions(-) diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json index caa0405..a7cf0af 100644 --- a/.workbuddy/expert-history.json +++ b/.workbuddy/expert-history.json @@ -57,5 +57,5 @@ } ] }, - "lastUpdated": 1776019986045 + "lastUpdated": 1776023218479 } \ No newline at end of file diff --git a/.workbuddy/memory/2026-04-12.md b/.workbuddy/memory/2026-04-12.md index 7add595..e86ebdb 100644 --- a/.workbuddy/memory/2026-04-12.md +++ b/.workbuddy/memory/2026-04-12.md @@ -439,3 +439,177 @@ src/enterprise/ ### 项目状态 - ✅ 编译成功 - ✅ 开发服务器运行中 (端口: dist) + +--- + +## 任务:改造 invite/index.tsx 登录流程 + +### 需求 +- `loginByOpenId` 已注册 → 显示**「确认加入」**按钮 +- `loginByOpenId` 未注册 → 显示**「微信手机号快速加入」**按钮(走授权流程) + +### 关键逻辑 +1. `checkLoginStatus`:调用 `loginByOpenId` 检查用户是否已注册 +2. 已注册:`isLoggedIn = true`,显示「确认加入」按钮 +3. 未注册:`isLoggedIn = false`,显示「微信手机号授权」按钮 +4. 授权成功 → 调用 `loginByMpWxPhone` 完成注册/登录 → 自动执行加入应用 + +### 文件修改 +- `src/passport/invite/index.tsx` - 完整重写,区分已登录/未注册两种按钮状态 + +--- + +## 任务:未注册用户在邀请页内完成授权注册,不跳登录页 + +### 需求 +- loginByOpenId 未注册 → 在页面内显示「微信手机号授权」按钮 +- 授权成功 → 调用 `loginByMpWxPhone` 注册/登录 → 自动执行加入应用 +- 不再跳转 passport/login 页面 + +### 关键逻辑 +1. `checkLoginStatus`:已注册 isLoggedIn=true,未注册 isLoggedIn=false,**两种情况都显示邀请页** +2. 未注册按钮:`open-type="getPhoneNumber"` → `handleGetPhoneNumber` + - 授权码调 `SERVER_API_URL/wx-login/loginByMpWxPhone` 完成注册登录 + - 保存 token → isLoggedIn=true → 立即调 `doJoinApp` +3. 已注册按钮:普通 `onClick` → `handleConfirmJoin` → `doJoinApp(access_token)` +4. `doJoinApp`:统一加入接口,请求头带 `Authorization: Bearer {access_token}` + +### 文件修改 +- `src/passport/invite/index.tsx` - 完整重写(彻底移除跳登录页逻辑) + +--- + +## 修复:「授权码不能为空」报错 + +### 问题 +后端 `/api/_app/developer/invite/accept` 接口强制要求传 `code`(微信授权码),不传就报「授权码不能为空」。 + +### 解决 +统一用一个 `getPhoneNumber` 按钮处理两种场景: +- **已注册**:文字「确认加入」→ 触发 getPhoneNumber → `doJoinApp(code, accessToken)` +- **未注册**:文字「微信手机号快速加入」→ 触发 getPhoneNumber → 先 `loginByMpWxPhone` 注册 → 再 `wx.login()` 获 code → `doJoinApp(newCode, access_token)` + +### doJoinApp 参数 +```ts +doJoinApp(wxCode: string, accessToken: string) +// 请求体带 code,请求头带 Authorization: Bearer xxx +``` + +--- + +## 优化:已登录用户不弹手机号授权 + +### 改动 +- 已登录按钮:普通 `onClick`,文字「确认加入」 +- 未注册按钮:`getPhoneNumber` 授权,文字「微信手机号快速加入」 + +### 逻辑差异 +| 用户状态 | 按钮类型 | 获取 code 方式 | +|------|------|------| +| 已登录 | 普通 onClick | `wx.login()` | +| 未注册 | getPhoneNumber | 授权回调的 `code` | + +### 文件修改 +- `src/passport/invite/index.tsx` - 按钮区分两种类型,已登录用普通 onClick + +--- + +## 优化:已登录用户不强制勾选协议 + +### 改动 +- 已登录用户点击「确认加入」时,不再检查 `agreementChecked` +- 未注册用户仍需勾选协议后才能授权手机号 + +### 文件修改 +- `src/passport/invite/index.tsx` - `handleConfirmJoin` 移除协议检查 + +--- + +## 修复:后端需要手机号授权码 + +### 问题 +后端 `invite/accept` 接口只接受 `getPhoneNumber` 获取的手机号授权码,用 `wx.login()` 的 code 会报「获取手机号失败」。 + +### 解决 +两种用户状态都走 `getPhoneNumber` 按钮: +- 已登录:文字「确认加入」,回调 `handleConfirmJoinGetPhoneNumber` +- 未注册:文字「微信手机号快速加入」,回调 `handleGetPhoneNumber` + +### 差异 +| 用户状态 | 回调 | 行为 | +|------|------|------| +| 已登录 | `handleConfirmJoinGetPhoneNumber` | 直接用 `code + access_token` 调加入接口 | +| 未注册 | `handleGetPhoneNumber` | 先 `loginByMpWxPhone` 注册登录,再调加入接口 | + +### 文件修改 +- `src/passport/invite/index.tsx` - 两种状态都用 getPhoneNumber 按钮 + +--- + +## 任务:后端改造支持已登录用户直接加入 + +### 问题 +后端 `/api/_app/developer/invite/accept` 接口强制要求传 `code`(手机号授权码),导致已登录用户也需要弹手机号授权。 + +### 后端改造方案 +修改 `AppMpInviteController.acceptInvite` 方法: + +#### 1. 参数校验调整 +- `code` 改为可选参数 +- 不传 `code` 时,从 `Authorization` 头获取当前登录用户 + +#### 2. 双模式支持 +```java +if (StrUtil.isBlank(code)) { + // 模式一:已登录用户(通过 Authorization 头识别) + userId = getCurrentUserId(); +} else { + // 模式二:未注册用户(通过手机号授权码获取手机号,创建用户) + String phone = getPhoneByCode(code); + userId = getOrCreateUserByPhone(phone); +} +``` + +#### 3. getCurrentUserId 方法 +- 尝试从 Spring Security Context 获取 +- 如果获取不到(免登录接口),手动解析 `Authorization` 头的 JWT Token + +### 前端配合改造 +- 已登录用户:普通 `onClick` 按钮 → `handleConfirmJoin` → `doJoinAppForLoggedInUser`(不传 `code`) +- 未注册用户:`getPhoneNumber` 按钮 → `handleGetPhoneNumber` → `doJoinAppForNewUser`(传 `code`) + +### 文件修改 +**后端:** +- `/Users/gxwebsoft/JAVA/websopy-java/src/main/java/com/gxwebsoft/app/controller/AppMpInviteController.java` + - `acceptInvite` 方法支持 `code` 可选 + - 使用 `BaseController.getLoginUserId()` 获取当前登录用户(无需额外方法) + +**前端:** +- `/Users/gxwebsoft/VUE/websopy-mp/src/passport/invite/index.tsx` + - 已登录按钮改为普通 `onClick` + - 新增 `handleConfirmJoin` 方法 + - 拆分 `doJoinApp` 为 `doJoinAppForLoggedInUser` 和 `doJoinAppForNewUser` + +--- + +## 修复:开发者中心加载不到应用 + +### 问题 +用户通过邀请加入应用后,开发者中心显示「加载中...」,无法显示应用列表。 + +### 原因 +- 前端 `developer/index.tsx` 只调用了 `pageMyApp` 接口(查询用户**创建**的应用) +- 用户通过邀请加入的应用属于**参与**的应用,不是创建的应用 +- 后端 `loginByOpenId` 返回了应用列表,但前端没有使用这个数据 + +### 解决 +1. **前端改造**:`loadData` 同时调用两个接口: + - `pageMyApp` - 查询创建的应用 + - `pageJoinedApp` - 查询参与的应用(新增 API) + - 合并两个列表,根据 `productId` 去重 + +2. **新增 API**:`src/api/developer/developer.ts` 添加 `pageJoinedApp` 方法 + +### 文件修改 +- `src/developer/index.tsx` - `loadData` 同时查询创建和参与的应用 +- `src/api/developer/developer.ts` - 新增 `pageJoinedApp` 方法 diff --git a/.workbuddy/memory/2026-04-13.md b/.workbuddy/memory/2026-04-13.md index a3a0ee3..3139811 100644 --- a/.workbuddy/memory/2026-04-13.md +++ b/.workbuddy/memory/2026-04-13.md @@ -33,3 +33,45 @@ - 文件:application-prod.yml - accessKeyId: LTAI4GKGZ9Z2Z8JZ77c3GNZP - 备注:与 OSS 使用同一个 AccessKey + +## 3. 前端接口地址修正 +**目的**:修正小程序端开发者相关 API 接口地址,与后端 Controller 路径保持一致 + +**问题发现**:`BaseUrl` 配置已包含 `/api` 后缀 (`https://websopy-api.websoft.top/api`),前端代码中不应再重复添加 `/api` 前缀,否则会导致 `/api/api/` 路径错误。 + +**修正文件**: +- `src/api/developer/enterprise.ts`: + - 企业信息:`/system/tenant/info`, `/system/tenant` + - 企业成员:`/system/user/page`, `/system/user` + - 订单:`/system/order/page`, `/system/order` + - 账单:`/sys/recharge-order/page` + - 企业应用:`/app/product/page` + - 邀请:`/app/developer/invite` + +- `src/api/developer/developer.ts`: + - 项目/应用:`/app/product/*` (create, update, delete, detail, page, my/page, joined/page) + - API Key:`/app/app-credential/*` + - 版本发布:`/app/app-version/*` + - 开发者信息:`/app/developer/git-account`, `/app/developer/gitea-info` + - 通知:`/app/notification/*` + - 权限申请:`/app/developer/permission-requests/*` + +- `src/api/invite/index.ts`: + - 所有接口移除 `/api` 前缀 + +**后端 Controller 对应关系**: +| 前端 API | 后端 Controller | 路径前缀 | +|---------|----------------|---------| +| enterprise.ts | TenantController | /api/system/tenant | +| enterprise.ts | UserController | /api/system/user | +| enterprise.ts | OrderController | /api/system/order | +| enterprise.ts | RechargeOrderController | /api/sys/recharge-order | +| developer.ts | AppProductController | /api/app/product | +| developer.ts | AppCredentialController | /api/app/app-credential | +| developer.ts | AppVersionController | /api/app/app-version | +| developer.ts | GitAccountController | /api/app/developer | +| developer.ts | AppNotificationController | /api/app/notification | +| developer.ts | AppPermissionRequestController | /api/app/developer/permission-requests | +| invite/index.ts | - | /api/invite/* | + +**重要配置**:`config/env.ts` 中 `API_BASE_URL` 已包含 `/api` 后缀,前端代码路径不应再以 `/api` 开头。 diff --git a/src/api/developer/developer.ts b/src/api/developer/developer.ts index 1b6b4bd..0befbfd 100644 --- a/src/api/developer/developer.ts +++ b/src/api/developer/developer.ts @@ -18,22 +18,23 @@ import type { ApplyParam, } from '@/types/developer' -// ==================== 项目相关 ==================== +// ==================== 项目/应用相关 ==================== +// 注意:后端使用 AppProduct 作为项目/应用的概念,路径为 /app/product /** - * 获取我的项目列表 + * 获取我的项目列表(我创建的应用) */ export async function pageMyProject(params?: ProjectParam) { - const res = await request.get>>('/project/my/page', { params }) + const res = await request.get>>('/app/product/my/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } /** - * 获取项目列表 + * 获取项目列表(分页查询应用列表) */ export async function pageProject(params?: ProjectParam) { - const res = await request.get>>('/project/page', { params }) + const res = await request.get>>('/app/product/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -42,7 +43,7 @@ export async function pageProject(params?: ProjectParam) { * 获取项目详情 */ export async function getProject(id: number) { - const res = await request.get>(`/project/${id}`) + const res = await request.get>(`/app/product/detail/${id}`) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -51,7 +52,7 @@ export async function getProject(id: number) { * 创建项目 */ export async function createProject(data: Partial) { - const res = await request.post>('/project', data) + const res = await request.post>('/app/product/create', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -60,7 +61,7 @@ export async function createProject(data: Partial) { * 更新项目 */ export async function updateProject(data: Partial) { - const res = await request.put>(`/project/${data.id}`, data) + const res = await request.put>('/app/product/update', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -69,16 +70,16 @@ export async function updateProject(data: Partial) { * 删除项目 */ export async function deleteProject(id: number) { - const res = await request.del>(`/project/${id}`) + const res = await request.del>(`/app/product/delete/${id}`) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } /** - * 获取项目成员列表 + * 获取项目成员列表(使用应用用户服务) */ export async function listProjectMember(projectId: number) { - const res = await request.get>(`/project/${projectId}/members`) + const res = await request.get>(`/app/app-user/page`, { appId: projectId }) if (res.code === 0 && res.data) return res.data return Promise.reject(new Error(res.message)) } @@ -87,7 +88,7 @@ export async function listProjectMember(projectId: number) { * 添加项目成员 */ export async function addProjectMember(projectId: number, data: Partial) { - const res = await request.post>(`/project/${projectId}/members`, data) + const res = await request.post>(`/app/app-user`, { ...data, appId: projectId }) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -96,18 +97,27 @@ export async function addProjectMember(projectId: number, data: Partial>(`/project/${projectId}/members/${memberId}`) + const res = await request.del>(`/app/app-user/${memberId}`) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } -// ==================== 应用相关 ==================== +// ==================== 应用相关(别名,与项目共用)==================== /** - * 分页查询我的应用 + * 分页查询我的应用(创建的应用) */ export async function pageMyApp(params?: AppParam) { - const res = await request.get>>('/app/product/my/page', { params }) + const res = await request.get>>('/app/product/my/page', params) + if (res.code === 0) return res.data + return Promise.reject(new Error(res.message)) +} + +/** + * 分页查询我参与的应用(通过邀请加入的应用) + */ +export async function pageJoinedApp(params?: AppParam) { + const res = await request.get>>('/app/product/joined/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -116,7 +126,7 @@ export async function pageMyApp(params?: AppParam) { * 获取应用列表 */ export async function pageApp(params?: AppParam) { - const res = await request.get>>('/app/product/page', { params }) + const res = await request.get>>('/app/product/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -125,7 +135,7 @@ export async function pageApp(params?: AppParam) { * 获取应用详情 */ export async function getApp(id: number) { - const res = await request.get>(`/app/product/${id}`) + const res = await request.get>(`/app/product/detail/${id}`) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -134,7 +144,7 @@ export async function getApp(id: number) { * 创建应用 */ export async function createApp(data: Partial) { - const res = await request.post>('/app/product', data) + const res = await request.post>('/app/product/create', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -143,7 +153,7 @@ export async function createApp(data: Partial) { * 更新应用 */ export async function updateApp(data: Partial) { - const res = await request.put>(`/app/product/${data.id}`, data) + const res = await request.put>('/app/product/update', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -152,18 +162,19 @@ export async function updateApp(data: Partial) { * 删除应用 */ export async function deleteApp(id: number) { - const res = await request.del>(`/app/product/${id}`) + const res = await request.del>(`/app/product/delete/${id}`) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } // ==================== API Key 相关 ==================== +// 对应后端 AppCredentialController,路径为 /app/app-credential /** * 获取 API Key 列表 */ export async function pageApiKey(params?: ApiKeyParam) { - const res = await request.get>>('/app/app-credential/page', { params }) + const res = await request.get>>('/app/app-credential/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -204,13 +215,23 @@ export async function deleteApiKey(id: number) { return Promise.reject(new Error(res.message)) } +/** + * 重置 API Key Secret + */ +export async function resetApiKeySecret(id: number) { + const res = await request.post>(`/app/app-credential/resetSecret/${id}`) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} + // ==================== 版本发布相关 ==================== +// 对应后端 AppVersionController,路径为 /app/app-version /** * 获取版本列表 */ export async function pageVersion(params?: VersionParam) { - const res = await request.get>>('/app/app-version/page', { params }) + const res = await request.get>>('/app/app-version/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -233,42 +254,80 @@ export async function createVersion(data: Partial) { return Promise.reject(new Error(res.message)) } -// ==================== 开发者相关 ==================== +/** + * 发布版本 + */ +export async function publishVersion(id: number) { + const res = await request.post>(`/app/app-version/publish/${id}`) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} /** - * 获取开发者信息 + * 回滚版本 + */ +export async function rollbackVersion(id: number) { + const res = await request.post>(`/app/app-version/rollback/${id}`) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} + +// ==================== 开发者相关 ==================== +// 对应后端 GitAccountController,路径为 /app/developer + +/** + * 获取开发者信息(Git账号绑定状态) */ export async function getDeveloperInfo() { - const res = await request.get>('/developer/info') + const res = await request.get>('/app/developer/git-account') if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } /** - * 申请成为开发者 + * 申请成为开发者(绑定Git账号) */ export async function applyDeveloper(data: DeveloperApplyParam) { - const res = await request.post>('/developer/apply', data) + const res = await request.post>('/app/developer/git-account', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } /** - * 更新开发者信息 + * 更新开发者信息(更新Git账号) */ export async function updateDeveloperInfo(data: Partial) { - const res = await request.put>('/developer/info', data) + const res = await request.put>('/app/developer/git-account', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } +/** + * 获取Gitea服务器信息 + */ +export async function getGiteaInfo() { + const res = await request.get>('/app/developer/gitea-info') + if (res.code === 0) return res.data + return Promise.reject(new Error(res.message)) +} + // ==================== 消息通知相关 ==================== +// 对应后端 AppNotificationController,路径为 /app/notification /** * 获取通知列表 */ export async function pageNotification(params?: NotificationParam) { - const res = await request.get>>('/notification/page', { params }) + const res = await request.get>>('/app/notification/page', params) + if (res.code === 0) return res.data + return Promise.reject(new Error(res.message)) +} + +/** + * 获取最近通知(铃铛下拉) + */ +export async function getRecentNotifications(type?: string, limit?: number) { + const res = await request.get>('/app/notification/recent', { type, limit }) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -277,7 +336,7 @@ export async function pageNotification(params?: NotificationParam) { * 获取未读通知数量 */ export async function getUnreadCount() { - const res = await request.get>('/notification/unread-count') + const res = await request.get>('/app/notification/unread-count') if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -286,7 +345,7 @@ export async function getUnreadCount() { * 标记通知为已读 */ export async function markAsRead(id: number) { - const res = await request.put>(`/notification/${id}/read`) + const res = await request.put>(`/app/notification/read/${id}`) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -294,8 +353,8 @@ export async function markAsRead(id: number) { /** * 标记所有通知为已读 */ -export async function markAllAsRead() { - const res = await request.put>('/notification/read-all') +export async function markAllAsRead(type?: string) { + const res = await request.put>('/app/notification/read-all', { type }) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -304,18 +363,28 @@ export async function markAllAsRead() { * 删除通知 */ export async function deleteNotification(id: number) { - const res = await request.del>(`/notification/${id}`) + const res = await request.del>(`/app/notification/${id}`) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} + +/** + * 清空已读通知 + */ +export async function clearReadNotifications(type?: string) { + const res = await request.del>('/app/notification/clear-read', { type }) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } // ==================== 权限审批相关 ==================== +// 对应后端 AppPermissionRequestController,路径为 /app/developer/permission-requests /** * 获取申请列表 */ export async function pageApply(params?: ApplyParam) { - const res = await request.get>>('/apply/page', { params }) + const res = await request.get>>('/app/developer/permission-requests/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -324,7 +393,25 @@ export async function pageApply(params?: ApplyParam) { * 获取我的申请列表 */ export async function pageMyApply(params?: ApplyParam) { - const res = await request.get>>('/apply/my/page', { params }) + const res = await request.get>>('/app/developer/permission-requests', params) + if (res.code === 0) return res.data + return Promise.reject(new Error(res.message)) +} + +/** + * 获取权限申请统计 + */ +export async function getApplyStats() { + const res = await request.get>('/app/developer/permission-requests/stats') + if (res.code === 0) return res.data + return Promise.reject(new Error(res.message)) +} + +/** + * 获取可申请的仓库列表 + */ +export async function getAvailableRepos() { + const res = await request.get>>('/app/developer/permission-requests/available-repos') if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -332,17 +419,26 @@ export async function pageMyApply(params?: ApplyParam) { /** * 创建申请 */ -export async function createApply(data: Partial) { - const res = await request.post>('/apply', data) +export async function createApply(data: { repo: string; reason: string; gitUsername?: string }) { + const res = await request.post>('/app/developer/permission-requests', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } /** - * 审批申请 + * 审批申请-通过 */ -export async function reviewApply(id: number, status: 'approved' | 'rejected', remark?: string) { - const res = await request.put>(`/apply/${id}/review`, { status, remark }) +export async function approveApply(id: number, note?: string) { + const res = await request.put>(`/app/developer/permission-requests/${id}/approve`, { note }) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} + +/** + * 审批申请-拒绝 + */ +export async function rejectApply(id: number, reason: string) { + const res = await request.put>(`/app/developer/permission-requests/${id}/reject`, { reason }) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } diff --git a/src/api/developer/enterprise.ts b/src/api/developer/enterprise.ts index 6fe5b2b..3cf023b 100644 --- a/src/api/developer/enterprise.ts +++ b/src/api/developer/enterprise.ts @@ -2,13 +2,13 @@ import request from '@/utils/request' import type { ApiResult, PageResult } from '@/api' import type { Enterprise, EnterpriseMember, EnterpriseMemberParam, Order, Bill, BillParam, App, AppParam } from '@/types/developer' -// ==================== 企业相关 ==================== +// ==================== 企业/租户相关 ==================== /** * 获取企业信息 */ export async function getEnterpriseInfo() { - const res = await request.get>('/enterprise/info') + const res = await request.get>('/system/tenant/info') if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -17,18 +17,18 @@ export async function getEnterpriseInfo() { * 更新企业信息 */ export async function updateEnterpriseInfo(data: Partial) { - const res = await request.put>('/enterprise/info', data) + const res = await request.put>('/system/tenant', data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } -// ==================== 企业成员相关 ==================== +// ==================== 企业成员/用户相关 ==================== /** * 获取企业成员列表 */ export async function pageEnterpriseMember(params?: EnterpriseMemberParam) { - const res = await request.get>>('/enterprise/member/page', { params }) + const res = await request.get>>('/system/user/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -37,7 +37,7 @@ export async function pageEnterpriseMember(params?: EnterpriseMemberParam) { * 获取企业成员列表(不分页) */ export async function listEnterpriseMember(params?: EnterpriseMemberParam) { - const res = await request.get>('/enterprise/member', params) + const res = await request.get>('/system/user', params) if (res.code === 0 && res.data) return res.data return Promise.reject(new Error(res.message)) } @@ -46,7 +46,7 @@ export async function listEnterpriseMember(params?: EnterpriseMemberParam) { * 邀请企业成员 */ export async function inviteEnterpriseMember(enterpriseId: number, data: Partial) { - const res = await request.post>(`/enterprise/member/${enterpriseId}/invite`, data) + const res = await request.post>(`/app/developer/invite/${enterpriseId}/invite`, data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -55,7 +55,7 @@ export async function inviteEnterpriseMember(enterpriseId: number, data: Partial * 更新企业成员 */ export async function updateEnterpriseMember(data: Partial) { - const res = await request.put>(`/enterprise/member/${data.id}`, data) + const res = await request.put>(`/system/user`, data) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -64,7 +64,7 @@ export async function updateEnterpriseMember(data: Partial) { * 移除企业成员 */ export async function removeEnterpriseMember(memberId: number) { - const res = await request.del>(`/enterprise/member/${memberId}`) + const res = await request.del>(`/system/user/${memberId}`) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } @@ -75,7 +75,7 @@ export async function removeEnterpriseMember(memberId: number) { * 获取订单列表 */ export async function pageOrder(params?: { page?: number; limit?: number; status?: number }) { - const res = await request.get>>('/enterprise/order/page', { params }) + const res = await request.get>>('/system/order/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -84,18 +84,27 @@ export async function pageOrder(params?: { page?: number; limit?: number; status * 获取订单详情 */ export async function getOrder(id: number) { - const res = await request.get>(`/enterprise/order/${id}`) + const res = await request.get>(`/system/order/${id}`) if (res.code === 0 && res.data) return res.data return Promise.reject(new Error(res.message)) } -// ==================== 账单相关 ==================== +/** + * 创建订单 + */ +export async function createOrder(data: any) { + const res = await request.post>('/system/order', data) + if (res.code === 0) return res.message + return Promise.reject(new Error(res.message)) +} + +// ==================== 充值/账单相关 ==================== /** * 获取账单列表 */ export async function pageBill(params?: BillParam) { - const res = await request.get>>('/enterprise/bill/page', { params }) + const res = await request.get>>('/sys/recharge-order/page', params) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -104,7 +113,7 @@ export async function pageBill(params?: BillParam) { * 获取账单概览 */ export async function getBillOverview() { - const res = await request.get>('/enterprise/bill/overview') + const res = await request.get>('/sys/user-balance-log/overview') if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -115,7 +124,7 @@ export async function getBillOverview() { * 获取企业应用列表 */ export async function pageEnterpriseApp(params?: AppParam) { - const res = await request.get>>('/enterprise/app/page', { params }) + const res = await request.get>>('/app/product/page', { params }) if (res.code === 0) return res.data return Promise.reject(new Error(res.message)) } @@ -124,7 +133,7 @@ export async function pageEnterpriseApp(params?: AppParam) { * 获取企业应用详情 */ export async function getEnterpriseApp(id: number) { - const res = await request.get>(`/enterprise/app/${id}`) + const res = await request.get>(`/app/product/${id}`) if (res.code === 0 && res.data) return res.data return Promise.reject(new Error(res.message)) } @@ -133,7 +142,7 @@ export async function getEnterpriseApp(id: number) { * 购买应用 */ export async function purchaseApp(productId: number) { - const res = await request.post>('/enterprise/app/purchase', { productId }) + const res = await request.post>('/api/system/order/createOrder', { productId }) if (res.code === 0) return res.message return Promise.reject(new Error(res.message)) } diff --git a/src/developer/index.tsx b/src/developer/index.tsx index 29fbc76..6bdc582 100644 --- a/src/developer/index.tsx +++ b/src/developer/index.tsx @@ -3,7 +3,7 @@ import { View, Text, ScrollView } from '@tarojs/components' import Taro from '@tarojs/taro' import { Button, Empty } from '@nutui/nutui-react-taro' import { useThemeStyles } from '@/hooks/useTheme' -import { pageMyApp } from '@/api/developer' +import { pageMyApp, pageJoinedApp } from '@/api/developer' import type { App } from '@/types/developer' import { APP_TYPE_NAME } from '@/types/developer' import './index.scss' @@ -18,19 +18,45 @@ const DeveloperIndex: React.FC = () => { totalCalls: 0, }) - // 加载数据 + // 加载数据 - 同时查询创建的应用和参与的应用 const loadData = async () => { try { setLoading(true) - const result = await pageMyApp({ current: 1, size: 10 }) - if (result) { - setApps(result.list || []) - setStats({ - totalApps: result.count || 0, - runningApps: result.list?.filter((a) => a.status === 1).length || 0, - totalCalls: 0, - }) - } + // 同时调用两个接口:创建的应用 + 参与的应用(通过邀请加入) + const [myAppsResult, joinedAppsResult] = await Promise.all([ + pageMyApp({ current: 1, size: 10 }), + pageJoinedApp({ current: 1, size: 10 }), + ]) + + // 合并两个列表,去重(根据 productId) + const myApps = myAppsResult?.records || [] + const joinedApps = joinedAppsResult?.records || [] + + // 使用 Map 去重,优先保留创建的应用(排在前面) + const appMap = new Map() + + // 先加入创建的应用 + myApps.forEach((app) => { + if (app.productId) { + appMap.set(app.productId, { ...app, isOwner: true }) + } + }) + + // 再加入参与的应用(如果已存在则跳过) + joinedApps.forEach((app) => { + if (app.productId && !appMap.has(app.productId)) { + appMap.set(app.productId, { ...app, isOwner: false }) + } + }) + + const mergedApps = Array.from(appMap.values()) + + setApps(mergedApps) + setStats({ + totalApps: mergedApps.length, + runningApps: mergedApps.filter((a) => a.status === 1).length, + totalCalls: 0, + }) } catch (error) { console.error('加载数据失败', error) Taro.showToast({ title: '加载失败', icon: 'none' })