# 账号与佣金管理模块需求规划 > 文档版本:v1.2 > 创建时间:2026-01-21 > 更新时间:2026-01-21 > 状态:✅ 已确认所有核心问题 --- ## 一、总体概述 ### 1.1 模块范围 本次需求涵盖以下功能模块: | 模块 | 说明 | |------|------| | 账号管理-代理商(店铺)管理 | 查看代理商佣金信息、佣金提现记录、佣金明细 | | 账号管理-佣金提现 | 佣金提现申请审批流程(放款/拒绝) | | 佣金提现设置 | 全局提现规则配置(次数、金额、手续费) | | 账号管理-企业客户管理 | 企业CRUD、卡分配/回收、启用禁用 | | 账号管理-客户账号管理 | 代理商+企业客户的账号统一管理 | | 财务-我的账号 | 当前登录账号的佣金数据查询 | ### 1.2 核心概念说明 | 概念 | 系统模型 | 说明 | |------|----------|------| | 代理商/店铺 | `Shop` | 同一概念,代码中使用 Shop | | 代理账号 | `Account` (UserType=3) | 店铺下的员工账号 | | 店铺主账号 | `Account` (is_primary=true) | 创建店铺时同步创建的账号,每个店铺有且仅有一个 | | 企业客户 | `Enterprise` | B端企业客户 | | 企业账号 | `Account` (UserType=4) | 企业的登录账号 | | 佣金钱包 | `Wallet` (WalletType=commission) | 店铺级别的佣金钱包 | ### 1.3 数据权限规则 | 角色 | 数据可见范围 | |------|-------------| | 平台用户 | 全部数据 | | 代理商用户 | 自己店铺 + 下级店铺 + 归属的企业客户数据 | | 企业用户 | 仅自己企业数据 | --- ## 二、数据模型变更 ### 2.1 需要新增的字段 #### 2.1.1 `tb_commission_withdrawal_request` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `withdrawal_no` | varchar(50) | 提现单号(唯一标识,格式:W + 时间戳 + 随机数) | 是 | | `applicant_id` | uint | 申请人账号ID(店铺下哪个账号提交的) | 是 | | `shop_id` | uint | 店铺ID(冗余字段,方便查询) | 是 | | `fee_rate` | int64 | 手续费比率(基点,100=1%,记录申请时的费率快照) | 是 | | `payment_type` | varchar(20) | 放款类型(manual=人工打款) | 是 | | `processor_id` | uint | 处理人ID(审批/放款人) | 否 | | `processed_at` | timestamp | 处理时间 | 否 | | `remark` | text | 备注 | 否 | #### 2.1.2 `tb_account` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `is_primary` | boolean | 是否为店铺主账号(创建店铺时同步创建的账号) | 否,默认false | **说明**: - 每个店铺有且仅有一个主账号(`is_primary=true`) - 创建店铺时自动创建的账号设置为主账号 - 主账号不可删除,只能禁用 - 未来销售系统会通过账号关联佣金归属 #### 2.1.3 `tb_commission_record` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `shop_id` | uint | 店铺ID(冗余字段,佣金主要跟着店铺走) | 是 | **说明**: - 佣金本质上跟着店铺走 - 同时保留 `agent_id`(账号ID),未来可支持查看某销售的佣金情况 - 佣金明细查询主要基于 `shop_id` #### 2.1.4 `tb_commission_withdrawal_setting` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `daily_withdrawal_limit` | int | 每日提现次数限制(每个代理商) | 是 | #### 2.1.5 `tb_iot_card` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `shop_id` | uint | 店铺ID(冗余字段,owner_type=shop时等于owner_id) | 否 | #### 2.1.6 `tb_device` 表新增字段 | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `shop_id` | uint | 店铺ID(冗余字段,owner_type=shop时等于owner_id) | 否 | #### 2.1.7 `tb_commission_record` 表新增字段(补充) | 字段名 | 类型 | 说明 | 是否必填 | |--------|------|------|----------| | `balance_after` | int64 | 入账后佣金余额(分),创建时计算:本次佣金 + 历史累计佣金 | 是 | #### 2.1.8 `tb_iot_card` 和 `tb_device` 表 `owner_type` 统一 **当前值**:`platform`, `agent`, `user`, `device` **统一为**:`platform`, `shop` | 旧值 | 新值 | 说明 | |------|------|------| | `platform` | `platform` | 平台库存(不变) | | `agent` | `shop` | 代理商持有(统一命名) | | `user` | 废弃 | 不再使用 | | `device` | 废弃 | 不再使用 | **核心设计**: - 卡/设备只在平台和代理商之间流转 - `owner_type=shop` + `owner_id=店铺ID` 表示代理商持有 - 企业看到的卡是通过"授权"实现的,不改变归属 ### 2.2 需要新增的表 #### 2.2.1 `tb_enterprise_card_authorization` 企业-卡授权表(核心) 用于记录企业被授权可见的卡。**这是企业查看卡的唯一途径,不改变卡的归属**。 ```sql CREATE TABLE tb_enterprise_card_authorization ( id BIGSERIAL PRIMARY KEY, enterprise_id BIGINT NOT NULL, -- 企业ID iot_card_id BIGINT NOT NULL, -- 卡ID shop_id BIGINT NOT NULL, -- 卡所属店铺ID(冗余,方便查询和权限校验) authorized_by BIGINT NOT NULL, -- 授权人账号ID authorized_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),-- 授权时间 status INT DEFAULT 1, -- 1=有效, 0=已回收 creator BIGINT, updater BIGINT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), deleted_at TIMESTAMP WITH TIME ZONE, CONSTRAINT uk_enterprise_card UNIQUE(enterprise_id, iot_card_id) ); CREATE INDEX idx_eca_enterprise ON tb_enterprise_card_authorization(enterprise_id, status) WHERE deleted_at IS NULL; CREATE INDEX idx_eca_card ON tb_enterprise_card_authorization(iot_card_id) WHERE deleted_at IS NULL; CREATE INDEX idx_eca_shop ON tb_enterprise_card_authorization(shop_id) WHERE deleted_at IS NULL; ``` **核心设计说明**: - 卡的归属(owner)始终是代理商店铺,不会变成企业 - 企业通过授权表"看到"被授权的卡 - 授权是永久的,回收时更新 `status=0` - 设备通过卡的授权间接可见(不需要单独的设备授权表) #### 2.2.2 `tb_asset_allocation_record` 资产分配记录表 用于记录卡/设备在平台和代理商之间流转的历史。 ```sql CREATE TABLE tb_asset_allocation_record ( id BIGSERIAL PRIMARY KEY, allocation_no VARCHAR(50) NOT NULL UNIQUE, -- 分配单号 allocation_type VARCHAR(20) NOT NULL, -- 分配类型:allocate=分配, recall=回收 asset_type VARCHAR(20) NOT NULL, -- 资产类型:iot_card, device asset_id BIGINT NOT NULL, -- 资产ID asset_identifier VARCHAR(50) NOT NULL, -- 资产标识(ICCID或设备号,方便查询) from_owner_type VARCHAR(20), -- 原归属类型 from_owner_id BIGINT, -- 原归属ID to_owner_type VARCHAR(20) NOT NULL, -- 目标归属类型:platform/shop to_owner_id BIGINT NOT NULL, -- 目标归属ID related_device_id BIGINT, -- 关联设备ID(卡分配时如果绑定了设备) related_card_ids JSONB, -- 关联卡ID列表(设备分配时包含的卡) operator_id BIGINT NOT NULL, -- 操作人ID remark TEXT, -- 备注 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), deleted_at TIMESTAMP WITH TIME ZONE ); CREATE INDEX idx_asset_allocation_asset ON tb_asset_allocation_record(asset_type, asset_id); CREATE INDEX idx_asset_allocation_to_owner ON tb_asset_allocation_record(to_owner_type, to_owner_id); CREATE INDEX idx_asset_allocation_created ON tb_asset_allocation_record(created_at); ``` ### 2.3 卡/设备归属与授权体系(已确认) #### 2.3.1 核心设计原则 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 归属 vs 授权 - 核心区别 │ └─────────────────────────────────────────────────────────────────┘ 【归属流转】改变 owner_type/owner_id,涉及真实的资产转移: 平台 (platform) ──分配──→ 代理商 (shop) 【授权可见】不改变归属,只是让企业"能看到": 代理商的卡 ──授权──→ 企业可见(通过授权表) ``` **owner_type 只有两种值**: - `platform` - 平台库存 - `shop` - 代理商持有 **企业不拥有卡**: - 企业看到的卡仍然属于代理商 - 企业通过 `tb_enterprise_card_authorization` 表获得可见性 - 授权是永久的,回收时更新状态 #### 2.3.2 数据权限过滤修改 需要修改 `pkg/gorm/callback.go`,对 `tb_iot_card` 表做特殊处理: ```go // 企业用户查询 IotCard 时的特殊处理 if userType == constants.UserTypeEnterprise && tableName == "tb_iot_card" { enterpriseID := middleware.GetEnterpriseIDFromContext(ctx) if enterpriseID != 0 { // 企业用户只能看到被授权的卡 tx.Where("id IN (SELECT iot_card_id FROM tb_enterprise_card_authorization WHERE enterprise_id = ? AND status = 1 AND deleted_at IS NULL)", enterpriseID) } else { tx.Where("1 = 0") } return } // 代理用户查询 IotCard 时,使用 shop_id 字段过滤 if userType == constants.UserTypeAgent && tableName == "tb_iot_card" { // 使用新增的 shop_id 冗余字段 tx.Where("shop_id IN ?", subordinateShopIDs) return } ``` #### 2.3.3 分配/授权规则 | 场景 | 操作 | 说明 | |------|------|------| | 平台 → 代理商 | 修改 `owner_type=shop`, `owner_id=shop_id`, `shop_id=shop_id` | 真实的归属转移 | | 代理商 → 企业 | 创建 `tb_enterprise_card_authorization` 记录 | 只是授权可见,不改变归属 | | 企业 → 代理商回收 | 更新授权表 `status=0` | 取消授权 | | 代理商 → 平台回收 | 修改 `owner_type=platform`, `shop_id=NULL` | 同时清理相关授权记录 | #### 2.3.4 设备可见性(通过卡间接查询) 企业查询设备时,不直接查设备表,而是: 1. 查询企业被授权的卡 2. 通过 `DeviceSimBinding` 表关联到设备 3. 返回设备信息 ```sql -- 企业可见的设备(通过卡间接查询) SELECT DISTINCT d.* FROM tb_device d INNER JOIN tb_device_sim_binding dsb ON d.id = dsb.device_id AND dsb.bind_status = 1 INNER JOIN tb_enterprise_card_authorization eca ON dsb.iot_card_id = eca.iot_card_id WHERE eca.enterprise_id = ? AND eca.status = 1 AND eca.deleted_at IS NULL; ``` ### 2.4 卡/设备分配详细方案(已确认) #### 2.4.1 分配交互流程 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 卡分配给企业流程 │ └─────────────────────────────────────────────────────────────────┘ 用户选择ICCID列表 → 调用预检接口 → 展示分配预览 → 用户确认 → 执行分配 │ ▼ ┌─────────────────────┐ │ 检查每张卡是否绑定设备 │ └──────────┬──────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐ │ 未绑定设备 │ │ 绑定了设备 │ │ 卡不存在 │ │ │ │ │ │ /无权限 │ └────┬────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ 查询设备绑定的 │ │ │ │ 所有卡列表 │ │ │ └────────┬────────┘ │ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────┐ │ 返回分配预览结果 │ │ - 可直接分配的卡 │ │ - 需要整体分配的设备(含所有卡) │ │ - 失败的卡(不存在/无权限) │ └─────────────────────────────────────────┘ ``` #### 2.4.2 新增接口:分配预检 **接口路径**:`POST /api/admin/enterprises/:id/allocate-cards/preview` **接口说明**:预检要分配的卡,返回分配预览信息供用户确认 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | iccids | []string | 是 | 需要分配的ICCID列表 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "standalone_cards": [ // 可直接分配的卡(未绑定设备) { "iccid": "89860001234567890123", "msisdn": "1440012345678", "carrier_name": "中国移动" } ], "device_bundles": [ // 需要整体分配的设备包 { "device_no": "DEV001", "device_name": "测试设备1", "trigger_iccid": "89860001234567890124", // 触发整体分配的卡 "cards": [ // 设备下所有卡 { "iccid": "89860001234567890124", "msisdn": "1440012345679", "is_trigger": true // 是用户选择的卡 }, { "iccid": "89860001234567890125", "msisdn": "1440012345680", "is_trigger": false // 连带分配的卡 }, { "iccid": "89860001234567890126", "msisdn": "1440012345681", "is_trigger": false } ] } ], "failed_items": [ // 失败的卡 { "iccid": "89860001234567890127", "reason": "卡不存在" }, { "iccid": "89860001234567890128", "reason": "无权限操作该卡" } ], "summary": { "standalone_card_count": 1, "device_count": 1, "device_card_count": 3, "total_card_count": 4, // 将要分配的总卡数 "failed_count": 2 } } } ``` **前端交互建议**: 1. 用户选择ICCID后,先调用预检接口 2. 展示分配预览: - "将分配 1 张独立卡" - "将分配 1 台设备(含 3 张卡),因为您选择的卡 xxx 绑定在该设备上" - "2 张卡分配失败:xxx(卡不存在)、xxx(无权限)" 3. 用户确认后,调用正式分配接口 #### 2.4.3 授权确认接口调整 **接口路径**:`POST /api/admin/enterprises/:id/allocate-cards` **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | iccids | []string | 是 | 需要授权的ICCID列表(与预检相同) | | confirm_device_bundles | bool | 是 | 确认整体授权设备下所有卡(必须为true才执行) | **说明**: - 这是"授权"操作,不是"转移归属" - 授权后卡仍然属于代理商,企业只是能看到和操作 - `confirm_device_bundles=true` 表示用户已确认整体授权设备下所有卡 **接口逻辑调整**: 1. 验证企业存在且归属于当前代理商(或其下级) 2. 验证卡属于当前代理商(`shop_id` 在可见范围内) 3. 如果卡绑定了设备,获取设备下所有卡一起授权 4. **创建授权记录**到 `tb_enterprise_card_authorization` 表(不修改卡的 owner) 5. 返回授权结果 --- ## 三、接口设计 ### 3.1 账号管理-代理商(店铺)管理 #### 3.1.1 代理商分页列表查询 **接口路径**:`GET /api/admin/shops/commission-summary` **接口说明**:查询代理商列表及其佣金汇总信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20,最大100 | | shop_name | string | 否 | 店铺名称(模糊查询) | | username | string | 否 | 代理商账号用户名(模糊查询) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "shop_id": 1, "shop_name": "某某代理商", "shop_code": "SHOP001", "username": "agent001", // 店铺主账号用户名 "phone": "13800138000", // 店铺主账号手机号 "total_commission": 100000, // 总佣金(分) "withdrawn_commission": 50000, // 已提现佣金(分) "unwithdraw_commission": 50000, // 未提现佣金(分)= 总佣金 - 已提现 "frozen_commission": 10000, // 冻结中佣金(分) "withdrawing_commission": 5000, // 提现中佣金(分)= 待审批的提现申请金额 "available_commission": 35000 // 可提现佣金(分)= 未提现 - 冻结 - 提现中 } ], "total": 100, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 根据当前用户权限过滤店铺列表(平台看全部,代理看自己+下级) 2. 查询店铺基本信息 3. 关联查询店铺的主账号(`is_primary=true` 的账号) 4. 计算佣金汇总: - `total_commission`:从 `Wallet` (shop类型, commission钱包) 的 `balance + frozen_balance` + 已提现金额 - `withdrawn_commission`:从 `CommissionWithdrawalRequest` 统计 `status=2(已通过)` 的总金额 - `frozen_commission`:`Wallet.frozen_balance` - `withdrawing_commission`:从 `CommissionWithdrawalRequest` 统计 `status=1(待审批)` 的总金额 - `available_commission`:`Wallet.balance - withdrawing_commission` **主账号说明**: - 每个店铺有且仅有一个主账号(`is_primary=true`) - 创建店铺时自动创建的账号即为主账号 - 这里显示主账号的信息(username、phone) --- #### 3.1.2 佣金提现分页列表(代理商维度) **接口路径**:`GET /api/admin/shops/:shop_id/withdrawal-requests` **接口说明**:查询指定代理商的佣金提现记录 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | shop_id | uint | 是 | 店铺ID(路径参数) | | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | withdrawal_no | string | 否 | 提现单号(精确查询) | | start_time | string | 否 | 申请开始时间(ISO8601) | | end_time | string | 否 | 申请结束时间(ISO8601) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 1, "withdrawal_no": "W20260121143000001", // 提现单号 "shop_name": "某某代理商", "shop_hierarchy": "上上级代理_上级代理_某某代理商", // 层级路径:本身往上最多两层(不含平台) "applicant_name": "张三", // 申请人姓名(账号username) "amount": 10000, // 提现金额(分) "fee_rate": 100, // 手续费比率(基点,100=1%) "fee": 100, // 手续费金额(分) "actual_amount": 9900, // 实际到账金额(分) "status": 1, // 状态:1=待审批,2=已通过,3=已拒绝,4=已放款 "status_name": "待审批", "created_at": "2026-01-21T14:30:00+08:00", // 申请时间 "withdrawal_method": "alipay", // 收款类型 "account_name": "张三", // 收款人姓名 "account_number": "zhangsan@alipay.com", // 支付宝账号 "payment_type": "manual", // 放款类型 "payment_type_name": "人工打款", "processor_name": "管理员", // 处理人姓名 "processed_at": "2026-01-21T15:00:00+08:00", // 处理时间 "remark": "备注信息" } ], "total": 50, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 验证当前用户有权限查看该店铺 2. 查询 `CommissionWithdrawalRequest` 表,过滤 `shop_id` 3. 关联查询店铺信息、申请人信息、处理人信息 4. 计算店铺层级路径: - 从当前店铺往上最多查两层 - 格式:`上上级_上级_本身`(用下划线分隔) - 如果只有一层上级:`上级_本身` - 如果是一级代理(无上级):`本身` - 不包含平台 --- #### 3.1.3 佣金明细分页查询(代理商维度) **接口路径**:`GET /api/admin/shops/:shop_id/commission-records` **接口说明**:查询指定代理商的佣金入账明细 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | shop_id | uint | 是 | 店铺ID(路径参数) | | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | commission_type | string | 否 | 佣金类型:one_time/long_term | | iccid | string | 否 | ICCID(模糊查询) | | device_no | string | 否 | 设备号(模糊查询) | | order_no | string | 否 | 订单号(模糊查询) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 1, "shop_name": "某某代理商", "order_no": "ORD20260121001", // 订单号 "device_no": "DEV001", // 设备号(可能为空) "iccid": "89860001234567890123", // ICCID(可能为空) "order_created_at": "2026-01-20T10:00:00+08:00", // 下单时间 "commission_type": "one_time", // 佣金类型 "commission_type_name": "一次性佣金", "amount": 500, // 入账佣金(分) "balance_after": 10500, // 入账后佣金余额(分) "status": 3, // 状态:1=冻结,2=解冻中,3=已发放,4=已失效 "status_name": "已发放", "created_at": "2026-01-21T10:00:00+08:00" // 佣金记录创建时间 } ], "total": 200, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 验证当前用户有权限查看该店铺 2. 查询 `CommissionRecord` 表,直接通过 `shop_id` 过滤(已确认新增该字段) 3. 关联查询 `Order` 表获取订单号、下单时间 4. 通过 `Order.iot_card_id` 关联 `IotCard` 获取 ICCID 5. 通过 `Order.device_id` 关联 `Device` 获取设备号 6. `balance_after` 需要从 `WalletTransaction` 中获取 **数据关联说明**: - `CommissionRecord` 同时存储 `agent_id`(账号ID)和 `shop_id`(店铺ID) - 佣金明细查询主要基于 `shop_id` - 保留 `agent_id` 用于未来支持查看某销售的佣金情况 --- ### 3.2 账号管理-佣金提现 #### 3.2.1 佣金提现申请分页查询列表 **接口路径**:`GET /api/admin/commission/withdrawal-requests` **接口说明**:查询所有待处理的佣金提现申请(审批列表) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | status | int | 否 | 状态:1=待审批,2=已通过,3=已拒绝,4=已放款 | | withdrawal_no | string | 否 | 提现单号(精确查询) | | shop_name | string | 否 | 店铺名称(模糊查询) | | start_time | string | 否 | 申请开始时间(ISO8601) | | end_time | string | 否 | 申请结束时间(ISO8601) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 1, "withdrawal_no": "W20260121143000001", "shop_id": 5, "shop_name": "某某代理商", "shop_hierarchy": "上上级代理_上级代理_某某代理商", // 本身往上最多两层 "applicant_id": 10, "applicant_name": "张三", "amount": 10000, "fee_rate": 100, "fee": 100, "actual_amount": 9900, "status": 1, "status_name": "待审批", "created_at": "2026-01-21T14:30:00+08:00", "withdrawal_method": "alipay", "withdrawal_method_name": "支付宝", "account_name": "张三", "account_number": "zhangsan@alipay.com", "payment_type": "manual", "payment_type_name": "人工打款", "processor_id": null, "processor_name": null, "processed_at": null, "remark": null } ], "total": 10, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 根据当前用户权限过滤数据 2. 支持多条件组合查询 3. 按申请时间倒序排列 --- #### 3.2.2 审批通过 **接口路径**:`POST /api/admin/commission/withdrawal-requests/:id/approve` **接口说明**:审批通过提现申请(实际打款由人工线下完成) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 提现申请ID(路径参数) | | payment_type | string | 是 | 放款类型:manual=人工打款 | | amount | int64 | 否 | 修正后的提现金额(分),不传则使用原金额 | | withdrawal_method | string | 否 | 修正后的收款类型,不传则使用原值 | | account_name | string | 否 | 修正后的收款人姓名,不传则使用原值 | | account_number | string | 否 | 修正后的收款账号,不传则使用原值 | | remark | string | 否 | 备注 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "withdrawal_no": "W20260121143000001", "status": 2, "status_name": "已通过", "processed_at": "2026-01-21T15:00:00+08:00" } } ``` **接口逻辑**: 1. 验证提现申请存在且状态为待审批 2. 验证当前用户有审批权限 3. 如果修正了金额,重新计算手续费和实际到账金额 4. 更新提现申请状态为已通过(status=2) 5. 从店铺佣金钱包扣除对应金额(解冻并扣除) 6. 记录钱包交易流水 7. 记录处理人和处理时间 **审批流程说明**: - 审批只有一步:待审批(1) → 已通过(2) 或 已拒绝(3) - 审批通过后,系统自动扣除佣金 - 实际打款由人工在线下完成(目前只支持人工打款) - 状态流转:1(待审批) → 2(已通过) → 人工线下打款 --- #### 3.2.3 拒绝(审批拒绝) **接口路径**:`POST /api/admin/commission/withdrawal-requests/:id/reject` **接口说明**:拒绝提现申请 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 提现申请ID(路径参数) | | remark | string | 是 | 拒绝原因 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "withdrawal_no": "W20260121143000001", "status": 3, "status_name": "已拒绝", "processed_at": "2026-01-21T15:00:00+08:00" } } ``` **接口逻辑**: 1. 验证提现申请存在且状态为待审批 2. 验证当前用户有审批权限 3. 更新提现申请状态为已拒绝 4. 解冻店铺佣金钱包中的冻结金额 5. 记录钱包交易流水 6. 记录处理人、处理时间和拒绝原因 --- ### 3.3 佣金提现设置 #### 3.3.1 新增佣金提现设置 **接口路径**:`POST /api/admin/commission/withdrawal-settings` **接口说明**:新增全局佣金提现配置(新配置生效后旧配置自动失效) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | daily_withdrawal_limit | int | 是 | 每日提现次数限制(每个代理商) | | min_withdrawal_amount | int64 | 是 | 提现最低金额(分) | | fee_rate | int64 | 是 | 提现手续费比率(基点,100=1%) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "daily_withdrawal_limit": 3, "min_withdrawal_amount": 10000, "fee_rate": 100, "is_active": true, "creator_name": "管理员", "created_at": "2026-01-21T14:30:00+08:00" } } ``` **接口逻辑**: 1. 验证当前用户有配置权限(平台用户) 2. 将当前生效配置的 `is_active` 设为 false 3. 创建新配置,`is_active` 设为 true 4. 记录创建人 --- #### 3.3.2 分页查询设置记录 **接口路径**:`GET /api/admin/commission/withdrawal-settings` **接口说明**:查询佣金提现配置历史记录 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 2, "daily_withdrawal_limit": 3, "min_withdrawal_amount": 10000, "fee_rate": 100, "is_active": true, "creator_id": 1, "creator_name": "管理员", "created_at": "2026-01-21T14:30:00+08:00" }, { "id": 1, "daily_withdrawal_limit": 5, "min_withdrawal_amount": 5000, "fee_rate": 50, "is_active": false, "creator_id": 1, "creator_name": "管理员", "created_at": "2026-01-01T10:00:00+08:00" } ], "total": 2, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 查询所有配置记录,按创建时间倒序 2. 关联查询创建人姓名 --- #### 3.3.3 获取当前生效配置 **接口路径**:`GET /api/admin/commission/withdrawal-settings/current` **接口说明**:获取当前生效的提现配置 **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 2, "daily_withdrawal_limit": 3, "min_withdrawal_amount": 10000, "fee_rate": 100, "is_active": true, "creator_name": "管理员", "created_at": "2026-01-21T14:30:00+08:00" } } ``` --- ### 3.4 账号管理-企业客户管理 #### 3.4.1 新增企业 **接口路径**:`POST /api/admin/enterprises` **接口说明**:创建企业客户,同时自动创建企业账号 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | owner_shop_id | uint | 否 | 归属代理商ID,不填则为平台自营 | | enterprise_name | string | 是 | 企业名称 | | enterprise_code | string | 是 | 企业编号(唯一) | | legal_person | string | 否 | 法人代表 | | contact_name | string | 是 | 联系人姓名 | | contact_phone | string | 是 | 联系人电话 | | login_phone | string | 是 | 登录手机号(作为企业账号的登录账号) | | password | string | 是 | 登录密码 | | business_license | string | 否 | 营业执照号 | | province | string | 否 | 省份 | | city | string | 否 | 城市 | | district | string | 否 | 区县 | | address | string | 否 | 详细地址 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "enterprise": { "id": 1, "enterprise_name": "某某企业", "enterprise_code": "ENT001", "owner_shop_id": 5, "owner_shop_name": "某某代理商", "legal_person": "李四", "contact_name": "王五", "contact_phone": "13900139000", "business_license": "91110000...", "province": "北京市", "city": "北京市", "district": "朝阳区", "address": "某某路123号", "status": 1, "created_at": "2026-01-21T14:30:00+08:00" }, "account": { "id": 10, "username": "某某企业", "phone": "13800138000", "user_type": 4, "status": 1 } } } ``` **接口逻辑**: 1. 验证企业编号唯一性 2. 如果指定 `owner_shop_id`,验证店铺存在且当前用户有权限 3. 验证 `login_phone` 在账号表中不存在 4. 开启事务: - 创建企业记录 - 创建企业账号(UserType=4, EnterpriseID=企业ID, Phone=login_phone, Username=企业名称) 5. 提交事务 --- #### 3.4.2 分页查询企业客户 **接口路径**:`GET /api/admin/enterprises` **接口说明**:查询企业客户列表 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | enterprise_name | string | 否 | 企业名称(模糊查询) | | login_phone | string | 否 | 登录手机号(模糊查询) | | contact_phone | string | 否 | 联系人电话(模糊查询) | | owner_shop_id | uint | 否 | 归属代理商ID | | status | int | 否 | 状态:0=禁用,1=启用 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 1, "enterprise_name": "某某企业", "enterprise_code": "ENT001", "owner_shop_id": 5, "owner_shop_name": "某某代理商", // NULL时显示"平台自营" "contact_name": "王五", "contact_phone": "13900139000", "login_phone": "13800138000", // 从关联的Account获取 "province": "北京市", "city": "北京市", "district": "朝阳区", "address": "某某路123号", "status": 1, "status_name": "启用", "created_at": "2026-01-21T14:30:00+08:00" } ], "total": 50, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 根据当前用户权限过滤: - 平台用户:看全部 - 代理商用户:看 `owner_shop_id` 在自己+下级店铺范围内的企业 2. 关联查询企业账号获取 `login_phone` 3. 关联查询归属店铺名称 --- #### 3.4.3 编辑企业 **接口路径**:`PUT /api/admin/enterprises/:id` **接口说明**:编辑企业信息(不影响账号) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | owner_shop_id | uint | 否 | 归属代理商ID | | enterprise_name | string | 否 | 企业名称 | | enterprise_code | string | 否 | 企业编号 | | legal_person | string | 否 | 法人代表 | | contact_name | string | 否 | 联系人姓名 | | contact_phone | string | 否 | 联系人电话 | | business_license | string | 否 | 营业执照号 | | province | string | 否 | 省份 | | city | string | 否 | 城市 | | district | string | 否 | 区县 | | address | string | 否 | 详细地址 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "enterprise_name": "某某企业(新)", "enterprise_code": "ENT001", "updated_at": "2026-01-21T15:00:00+08:00" } } ``` **接口逻辑**: 1. 验证企业存在 2. 验证当前用户有权限编辑该企业 3. 如果修改了 `enterprise_code`,验证唯一性 4. 如果修改了 `owner_shop_id`,验证店铺存在且当前用户有权限 5. 更新企业信息 6. **注意**:修改联系人电话不影响账号的登录手机号 --- #### 3.4.4 分配卡给企业客户 **接口路径**:`POST /api/admin/enterprises/:id/allocate-cards` **接口说明**:将卡分配给企业客户 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | iccids | []string | 是 | 需要分配的ICCID列表 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "success_count": 10, "fail_count": 2, "failed_items": [ { "iccid": "89860001234567890123", "reason": "卡不存在" }, { "iccid": "89860001234567890124", "reason": "无权限操作该卡" } ], "allocated_devices": [ // 因卡分配而连带分配的设备 { "device_no": "DEV001", "card_count": 4 } ] } } ``` **接口逻辑**: 1. 验证企业存在且当前用户有权限 2. 遍历ICCID列表: - 验证卡存在 - 验证当前用户有权限操作该卡(卡的 owner 在用户可见范围内) - 检查卡是否绑定了设备 3. 如果卡绑定了设备: - 将整个设备及其所有绑定的卡一起分配 - 记录到返回的 `allocated_devices` 中 4. 更新卡/设备的 `owner_type=enterprise`, `owner_id=enterprise_id` 5. 创建分配记录到 `tb_asset_allocation_record` **⚠️ 待确认**: - 如果卡A绑定在设备X上,设备X还绑定了卡B/C/D,分配卡A时是否要连带分配整个设备和所有卡? - 我的理解是:是的,需要整体分配,否则会导致归属关系混乱 --- #### 3.4.5 从企业客户回收卡授权 **接口路径**:`POST /api/admin/enterprises/:id/recall-cards` **接口说明**:取消企业对卡的授权(卡仍属于代理商) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | iccids | []string | 是 | 需要回收授权的ICCID列表 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "success_count": 10, "fail_count": 1, "failed_items": [ { "iccid": "89860001234567890123", "reason": "该卡未授权给此企业" } ], "recalled_devices": [ { "device_no": "DEV001", "card_count": 4 } ] } } ``` **接口逻辑**: 1. 验证企业存在且当前用户有权限 2. 遍历ICCID列表: - 验证卡存在 - 验证卡已授权给该企业(授权表中有记录且 status=1) - 检查卡是否绑定了设备 3. 如果卡绑定了设备,设备下所有卡的授权一起回收 4. **更新授权记录** `status=0`(不是删除,不是修改卡的 owner) 5. 卡仍然属于代理商,只是企业不再能看到 --- #### 3.4.6 企业客户卡分页查询列表 **接口路径**:`GET /api/admin/enterprises/:id/cards` **接口说明**:查询企业被授权可见的卡列表 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | status | int | 否 | 卡状态 | | carrier_id | uint | 否 | 运营商ID | | iccid | string | 否 | ICCID(模糊查询) | | device_no | string | 否 | 设备号(模糊查询) | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 1, "iccid": "89860001234567890123", "msisdn": "1440012345678", // 接入号 "device_id": 10, "device_no": "DEV001", // 设备号(可能为空) "carrier_id": 1, "carrier_name": "中国移动", "package_id": 5, "package_name": "月租套餐30G", // 当前套餐名称 "status": 3, "status_name": "已激活", "network_status": 1, // 网络状态:0=停机,1=开机 "network_status_name": "开机" } ], "total": 100, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 验证企业存在且当前用户有权限 2. 通过授权表查询企业被授权的卡: ```sql SELECT c.* FROM tb_iot_card c INNER JOIN tb_enterprise_card_authorization eca ON c.id = eca.iot_card_id WHERE eca.enterprise_id = ? AND eca.status = 1 AND eca.deleted_at IS NULL ``` 3. 关联查询设备信息、运营商信息、当前套餐信息 #### 3.4.6.1 企业操作卡 - 停机 **接口路径**:`POST /api/admin/enterprises/:id/cards/:card_id/suspend` **接口说明**:企业对授权卡执行停机操作 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | card_id | uint | 是 | 卡ID(路径参数) | **接口逻辑**: 1. 验证企业存在 2. 验证卡已授权给该企业(授权表中有有效记录) 3. 调用运营商接口执行停机 4. 更新卡的 `network_status = 0` #### 3.4.6.2 企业操作卡 - 复机 **接口路径**:`POST /api/admin/enterprises/:id/cards/:card_id/resume` **接口说明**:企业对授权卡执行复机操作 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | card_id | uint | 是 | 卡ID(路径参数) | **接口逻辑**: 1. 验证企业存在 2. 验证卡已授权给该企业 3. 调用运营商接口执行复机 4. 更新卡的 `network_status = 1` --- #### 3.4.7 启用/禁用企业 **接口路径**:`PUT /api/admin/enterprises/:id/status` **接口说明**:启用或禁用企业 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | status | int | 是 | 状态:0=禁用,1=启用 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "status": 0, "status_name": "禁用" } } ``` **接口逻辑**: 1. 验证企业存在且当前用户有权限 2. 更新企业状态 3. **同步禁用/启用**企业关联的账号 --- #### 3.4.8 修改企业账号密码 **接口路径**:`PUT /api/admin/enterprises/:id/password` **接口说明**:重置企业账号的登录密码 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 企业ID(路径参数) | | password | string | 是 | 新密码 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "enterprise_name": "某某企业" } } ``` **接口逻辑**: 1. 验证企业存在且当前用户有权限 2. 查找企业关联的账号 3. 更新账号密码(bcrypt加密) --- ### 3.5 账号管理-客户账号管理 > 说明:统一管理代理商账号和企业账号(UserType=3或4) #### 3.5.1 分页查询客户账号 **接口路径**:`GET /api/admin/customer-accounts` **接口说明**:查询代理商和企业的账号列表 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | shop_id | uint | 否 | 代理商ID(筛选该代理商及其下级的账号) | | username | string | 否 | 账号名称(模糊查询) | | status | int | 否 | 账号状态:0=禁用,1=启用 | | user_type | int | 否 | 账号类型:3=代理,4=企业 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "list": [ { "id": 10, "username": "张三", "phone": "13800138000", "user_type": 3, "user_type_name": "代理账号", "shop_id": 5, "shop_name": "某某代理商", "enterprise_id": null, "enterprise_name": null, "status": 1, "status_name": "启用", "created_at": "2026-01-21T14:30:00+08:00" }, { "id": 11, "username": "某某企业", "phone": "13900139000", "user_type": 4, "user_type_name": "企业账号", "shop_id": null, "shop_name": null, "enterprise_id": 1, "enterprise_name": "某某企业", "status": 1, "status_name": "启用", "created_at": "2026-01-21T14:30:00+08:00" } ], "total": 100, "page": 1, "page_size": 20 } } ``` **接口逻辑**: 1. 过滤条件:`user_type IN (3, 4)` 2. 根据当前用户权限过滤: - 平台用户:看全部 - 代理商用户:看自己店铺+下级店铺的代理账号 + 归属企业的账号 3. 关联查询店铺名称、企业名称 --- #### 3.5.2 新增客户账号 **接口路径**:`POST /api/admin/customer-accounts` **接口说明**:为代理商新增账号(企业账号通过新增企业时创建) **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | shop_id | uint | 是 | 代理商ID | | username | string | 是 | 账号名称 | | phone | string | 是 | 登录手机号 | | password | string | 是 | 登录密码 | | status | int | 否 | 状态,默认1=启用 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 12, "username": "李四", "phone": "13700137000", "user_type": 3, "shop_id": 5, "shop_name": "某某代理商", "status": 1, "created_at": "2026-01-21T14:30:00+08:00" } } ``` **接口逻辑**: 1. 验证店铺存在且当前用户有权限 2. 验证手机号在账号表中不存在 3. 创建账号(UserType=3, ShopID=shop_id) --- #### 3.5.3 编辑客户账号 **接口路径**:`PUT /api/admin/customer-accounts/:id` **接口说明**:编辑客户账号信息 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 账号ID(路径参数) | | username | string | 否 | 账号名称 | | phone | string | 否 | 登录手机号 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 12, "username": "李四(新)", "phone": "13700137001", "updated_at": "2026-01-21T15:00:00+08:00" } } ``` **接口逻辑**: 1. 验证账号存在且类型为代理或企业(3或4) 2. 验证当前用户有权限编辑该账号 3. 如果修改了手机号,验证新手机号不存在 4. 更新账号信息 --- #### 3.5.4 修改客户账号密码 **接口路径**:`PUT /api/admin/customer-accounts/:id/password` **接口说明**:重置客户账号密码 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 账号ID(路径参数) | | password | string | 是 | 新密码 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 12, "username": "李四" } } ``` --- #### 3.5.5 启用/禁用客户账号 **接口路径**:`PUT /api/admin/customer-accounts/:id/status` **接口说明**:启用或禁用客户账号 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | uint | 是 | 账号ID(路径参数) | | status | int | 是 | 状态:0=禁用,1=启用 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 12, "status": 0, "status_name": "禁用" } } ``` --- ### 3.6 财务-我的账号(代理商端) #### 3.6.1 获取当前账号佣金概览 **接口路径**:`GET /api/admin/my/commission-summary` **接口说明**:获取当前登录代理账号所属店铺的佣金汇总 **返回参数**: ```json { "code": 0, "message": "success", "data": { "shop_id": 5, "shop_name": "某某代理商", "total_commission": 100000, "withdrawn_commission": 50000, "unwithdraw_commission": 50000, "frozen_commission": 10000, "withdrawing_commission": 5000, "available_commission": 35000 } } ``` **接口逻辑**: 1. 从当前用户上下文获取 `shop_id` 2. 计算佣金汇总(逻辑同3.1.1) --- #### 3.6.2 佣金提现申请 **接口路径**:`POST /api/admin/my/withdrawal-requests` **接口说明**:代理商发起佣金提现申请 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | amount | int64 | 是 | 提现金额(分) | | withdrawal_method | string | 是 | 收款类型:alipay | | account_name | string | 是 | 收款人姓名 | | account_number | string | 是 | 支付宝账号 | **返回参数**: ```json { "code": 0, "message": "success", "data": { "id": 1, "withdrawal_no": "W20260121143000001", "amount": 10000, "fee_rate": 100, "fee": 100, "actual_amount": 9900, "status": 1, "status_name": "待审批", "created_at": "2026-01-21T14:30:00+08:00" } } ``` **接口逻辑**: 1. 从当前用户上下文获取 `shop_id` 和 `account_id` 2. 获取当前生效的提现配置 3. 验证: - 提现金额 >= 最低提现金额 - 可提现余额 >= 提现金额 - 今日提现次数 < 每日提现次数限制 4. 计算手续费和实际到账金额 5. 创建提现申请记录 6. 冻结店铺佣金钱包中对应金额 7. 记录钱包交易流水 --- #### 3.6.3 我的提现记录 **接口路径**:`GET /api/admin/my/withdrawal-requests` **接口说明**:查询当前代理商的提现记录 **请求参数**: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | page | int | 否 | 页码,默认1 | | page_size | int | 否 | 每页数量,默认20 | | status | int | 否 | 状态筛选 | | start_time | string | 否 | 申请开始时间 | | end_time | string | 否 | 申请结束时间 | **返回参数**: 与 3.2.1 相同,但仅返回当前店铺的数据 --- #### 3.6.4 我的佣金明细 **接口路径**:`GET /api/admin/my/commission-records` **接口说明**:查询当前代理商的佣金入账明细 **请求参数**: 与 3.1.3 相同 **返回参数**: 与 3.1.3 相同,但仅返回当前店铺的数据 --- ## 四、接口路径汇总 | 模块 | 方法 | 路径 | 说明 | |------|------|------|------| | **代理商管理** | GET | /api/admin/shops/commission-summary | 代理商佣金列表 | | | GET | /api/admin/shops/:shop_id/withdrawal-requests | 代理商提现记录 | | | GET | /api/admin/shops/:shop_id/commission-records | 代理商佣金明细 | | **佣金提现审批** | GET | /api/admin/commission/withdrawal-requests | 提现申请列表 | | | POST | /api/admin/commission/withdrawal-requests/:id/approve | 审批通过(人工线下打款) | | | POST | /api/admin/commission/withdrawal-requests/:id/reject | 审批拒绝 | | **提现设置** | POST | /api/admin/commission/withdrawal-settings | 新增配置 | | | GET | /api/admin/commission/withdrawal-settings | 配置列表 | | | GET | /api/admin/commission/withdrawal-settings/current | 当前配置 | | **企业客户管理** | POST | /api/admin/enterprises | 新增企业 | | | GET | /api/admin/enterprises | 企业列表 | | | PUT | /api/admin/enterprises/:id | 编辑企业 | | | PUT | /api/admin/enterprises/:id/status | 启用禁用 | | | PUT | /api/admin/enterprises/:id/password | 修改密码 | | | POST | /api/admin/enterprises/:id/allocate-cards/preview | 授权预检 | | | POST | /api/admin/enterprises/:id/allocate-cards | 授权卡(确认) | | | POST | /api/admin/enterprises/:id/recall-cards | 回收授权 | | | GET | /api/admin/enterprises/:id/cards | 企业授权卡列表 | | | POST | /api/admin/enterprises/:id/cards/:card_id/suspend | 企业操作-停机 | | | POST | /api/admin/enterprises/:id/cards/:card_id/resume | 企业操作-复机 | | **客户账号管理** | GET | /api/admin/customer-accounts | 账号列表 | | | POST | /api/admin/customer-accounts | 新增账号 | | | PUT | /api/admin/customer-accounts/:id | 编辑账号 | | | PUT | /api/admin/customer-accounts/:id/password | 修改密码 | | | PUT | /api/admin/customer-accounts/:id/status | 启用禁用 | | **财务-我的账号** | GET | /api/admin/my/commission-summary | 我的佣金概览 | | | POST | /api/admin/my/withdrawal-requests | 发起提现 | | | GET | /api/admin/my/withdrawal-requests | 我的提现记录 | | | GET | /api/admin/my/commission-records | 我的佣金明细 | --- ## 五、已确认事项 ### 5.1 核心设计确认 | # | 问题 | 确认结果 | |---|------|----------| | 1 | CommissionRecord 存储什么ID? | **同时存储账号ID和店铺ID**。佣金主要跟着店铺走,但保留账号ID方便未来查看某销售的佣金。 | | 2 | 店铺主账号如何定义? | **新增 `is_primary` 字段标记**。创建店铺时同步创建的账号就是主账号。 | | 3 | 卡绑定设备后如何授权? | **整个设备及所有卡一起授权**。新增预检接口让用户确认。 | | 4 | 审批流程几步? | **只有一步**。审批通过后状态变为"已通过",实际打款由人工线下完成。 | | 5 | 代理商层级路径格式? | **本身往上最多两层**:`上上级_上级_本身` | ### 5.2 卡/设备归属与授权确认 | # | 问题 | 确认结果 | |---|------|----------| | 6 | 卡的归属流转范围? | **只在平台和代理商之间**。企业不拥有卡,只是被授权可见。 | | 7 | owner_type 统一? | **改为 `platform` / `shop`**。去掉 `agent`、`user`、`device`。 | | 8 | 企业看卡的机制? | **通过授权表** `tb_enterprise_card_authorization`,不改变卡的 owner。 | | 9 | 设备如何可见? | **通过卡间接查询**。不需要单独的设备授权表。 | | 10 | 授权有效期? | **永久授权**。回收时更新 `status=0`。 | | 11 | 企业能操作什么? | **能看、可以停机/复机**。 | ### 5.3 佣金明细确认 | # | 问题 | 确认结果 | |---|------|----------| | 12 | "入账后佣金"如何计算? | **本次佣金 + 历史累计佣金**。在 `CommissionRecord` 创建时计算并存储 `balance_after` 字段。 | ### 5.4 待确认事项 **暂无待确认事项**。如有遗漏请补充。 --- ## 六、后续规划提示 本文档仅涵盖账号和佣金相关功能。以下功能待后续规划: - 物联网卡管理(ICCID增删改查、状态管理、数据同步) - 设备管理(设备增删改查、SIM绑定管理) - 号卡管理(虚拟产品管理) - 订单管理(套餐订购、支付流程) - 数据统计(用量统计、业务报表) --- *文档结束*