## GitHub 同步说明 | 项 | 内容 | |:---|:---| | 仓库 | https://github.com/fnvtk/cunkebao_v3 | | 分支 | develop | | 方向 | 本地 → GitHub(单向 push) | | 是否拉取远程 | 否(未 fetch / pull / merge) | | 是否改本地源文件 | 否(仅 git add/commit,未编辑业务/文档正文) | | 远程基准 | origin/develop @b5b40e8b| | 本批工作区变更 | 999 files, +47622 / -4007 lines | ## 一并推送的本地已有 commit(此前未 push) 1.3aa7ecf8deploy: 官网 ckb.quwanzhi.com 宝塔上线 DNS+SSL+验收文档 2.16a70045fix(官网): 桌面导航仅保留一个留资 CTA 并重新上线 3.dbe7b7aafix(官网): Hero 改为主 CTA 按钮并去掉重复 1200+ 数据条 4.ac82726echore(部署): 五端上线验收46项并统一留资与缓存版本 ## 本 commit 目录统计 | 目录 | 文件数 | 说明 | |:---|---:|:---| | 开发文档/ | 639 | 10、项目管理、未完成项跨仓汇总、五端需求/部署/接口 | | Server/ | 129 | 后端 API、迁移、部署相关 | | Touchkebao/ | 80 | PC 工作台、获客、算力 | | 官网/ | 61 | ckb.quwanzhi.com 静态站与 CTA 迭代 | | Cunkebao/ | 49 | 移动端场景/设置/流量池 | | SuperAdmin/ | 26 | 超管后台 | | .cursor/ | 11 | 规则与 Skill | | 其他 | 14 | docker-compose、.obsidian、.codegraph 等 | ## 重点文件(本地为准) - 开发文档/1、需求/修改/未完成项与跨仓验收汇总.md - 开发文档/10、项目管理/账号密码与登录环境一览.md - 开发文档/10、项目管理/(新建项目管理目录与截图) - 官网/ 全站 + 宝塔上线文档 - 五端上线验收 46 项(见 ac82726e) ## 刻意未上传 - node_modules/、.DS_Store、.smart-env/ - .开发文档_nested_git_backup/ - **/__pycache__/ ## 验收 - 提交页(含本说明): https://github.com/fnvtk/cunkebao_v3/commit/HEAD - 分支树: https://github.com/fnvtk/cunkebao_v3/tree/develop Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import { apiRequest, ApiResponse } from './api-utils';
|
|
|
|
export interface DeviceItem {
|
|
id: number;
|
|
memo: string;
|
|
imei: string;
|
|
phone: string;
|
|
model: string;
|
|
brand: string;
|
|
alive: number;
|
|
createTime?: number | string;
|
|
}
|
|
|
|
export async function postDeviceQrCode(params: {
|
|
accountId?: number;
|
|
companyId?: number;
|
|
}): Promise<ApiResponse<{ qrCode: string; config: Record<string, unknown> }>> {
|
|
return apiRequest('/devices/qrcode', 'POST', params);
|
|
}
|
|
|
|
export async function getDeviceAddResult(
|
|
accountId: number
|
|
): Promise<ApiResponse<{ added: boolean; device?: DeviceItem }>> {
|
|
return apiRequest(`/devices/add-results?accountId=${accountId}`);
|
|
}
|
|
|
|
export async function transferDevice(params: {
|
|
deviceId: number;
|
|
targetCompanyId?: number;
|
|
targetProjectId?: number;
|
|
}): Promise<ApiResponse> {
|
|
return apiRequest('/devices/transfer', 'POST', params);
|
|
}
|
|
|
|
export async function getCompanyOptions(): Promise<
|
|
ApiResponse<{ list: { id: number; name: string; companyId: number; primaryTag?: string; primaryTagLabel?: string }[] }>
|
|
> {
|
|
return apiRequest('/company/options?limit=500');
|
|
}
|
|
|
|
// §6.3 批量迁移设备到指定项目
|
|
export interface DeviceMigratePreviewItem {
|
|
deviceId: number;
|
|
name: string;
|
|
imei: string;
|
|
fromCompanyId: number;
|
|
fromName: string;
|
|
imeiConflict: boolean;
|
|
sameProject: boolean;
|
|
}
|
|
|
|
export async function previewDeviceMigrate(
|
|
deviceIds: number[], targetCompanyId: number,
|
|
): Promise<ApiResponse<{ targetCompanyId: number; targetName: string; total: number; conflicts: number; alreadyThere: number; items: DeviceMigratePreviewItem[] }>> {
|
|
return apiRequest(`/devices/migrate/preview?deviceIds=${deviceIds.join(',')}&targetCompanyId=${targetCompanyId}`);
|
|
}
|
|
|
|
export async function migrateDevices(
|
|
deviceIds: number[], targetCompanyId: number,
|
|
): Promise<ApiResponse<{ requested: number; migrated: number; okDeviceIds: number[]; failed: { deviceId: number; reason: string }[] }>> {
|
|
return apiRequest('/devices/migrate', 'POST', { deviceIds, targetCompanyId });
|
|
}
|
|
|
|
export async function getDeviceList(params: {
|
|
page?: number;
|
|
limit?: number;
|
|
companyId?: number;
|
|
keyword?: string;
|
|
alive?: number;
|
|
groupByProject?: boolean;
|
|
includeArchived?: boolean;
|
|
}): Promise<ApiResponse<any>> {
|
|
const q = new URLSearchParams();
|
|
q.append('page', String(params.page ?? 1));
|
|
q.append('limit', String(params.limit ?? 20));
|
|
if (params.companyId) q.append('companyId', String(params.companyId));
|
|
if (params.keyword) q.append('keyword', params.keyword);
|
|
if (params.alive !== undefined && params.alive >= 0) q.append('alive', String(params.alive));
|
|
if (params.groupByProject) q.append('groupByProject', '1');
|
|
if (params.includeArchived) q.append('includeArchived', '1');
|
|
return apiRequest(`/devices/list?${q.toString()}`);
|
|
}
|
|
|
|
/** 从工作手机 s2 全量同步设备到 ck_device */
|
|
export async function syncAllDevices(): Promise<
|
|
ApiResponse<{ synced: number; totalActive: number; totalArchived: number; totalAll?: number; s2Eligible?: number }>
|
|
> {
|
|
return apiRequest('/devices/sync-all', 'POST', {});
|
|
}
|
|
|
|
export async function syncDevices(params: {
|
|
projectId?: number;
|
|
companyId?: number;
|
|
}): Promise<ApiResponse<{ synced: number; companyId: number }>> {
|
|
return apiRequest('/devices/sync', 'POST', params);
|
|
}
|