# 承销商管理 — 技术方案 > 依据:同目录 `承销商管理功能需求.md`。承销商主档 **CRUD** + **逻辑删除** + **编号自动生成(2 位)** + **销售去向**;列表编号/名称 **模糊**筛选。 --- ## 1. 技术架构 | 项 | 说明 | | --- | --- | | **后端** | RuoYi **v3.9.2**(**springboot2** 分支):JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid | | **数据库** | MySQL **5.7.39**,InnoDB,`utf8mb4` | | **前端** | 若依 Vue2;字段 **小驼峰**(本方案不展开页面) | **分层**:`Controller` → `Service`(`DistributorValidation`、**编号生成**、**分配账号**)→ `Mapper`/XML → `domain`。 **代码包**:`com.ruoyi.web.modules.trading` **外部依赖**:`ISysUserService`、`TradePartnerAccountAssigner`(与供应商共用) **业务摘要** | 场景 | 行为 | | --- | --- | | 新增 | 校验通过后 `DistributorCodeGenerator` 分配 `distributor_code`(`01`~`99`);`del_flag=0` | | 修改 | **不可改** `distributor_code` | | 列表/详情 | 仅 `del_flag=0`;排序 `distributor_code ASC, id ASC` | | 列表筛选 | `distributor_code`、`distributor_name` **模糊**匹配(`LIKE %keyword%`) | | 删除 | 逻辑删除 `del_flag=2`;存在**未支付**订单时 `ServiceException`;已分配账号联动删用户 | | 分配账号 | `account_assigned=0`;角色 **104**;登录名拼音首字母+`id` | --- ## 2. 数据库设计 ### 2.1 表 `biz_distributor`(承销商) | 字段 | 类型 | 非空 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | `id` | `bigint(20)` | Y | 自增 | 主键 | | `distributor_code` | `char(2)` | Y | — | 承销商编号 `01`~`99`,**全局唯一** | | `distributor_name` | `varchar(64)` | Y | — | 承销商名称(**1~20** 字) | | `distributor_type` | `tinyint(4)` | Y | — | `1` 个体户 `2` 企业 | | `identity_no` | `varchar(32)` | Y | — | 身份证 / 统一社会信用代码 | | `contact_phone` | `varchar(20)` | Y | — | 手机号 | | `business_location` | `varchar(50)` | N | NULL | 经营地点(≤50 字) | | `sales_destination` | `tinyint(4)` | Y | — | `1` 本市 `2` 本省 `3` 外省 | | `account_assigned` | `tinyint(4)` | Y | `0` | `0` 未分配 `1` 已分配 | | `sys_user_id` | `bigint(20)` | N | NULL | 关联用户 ID | | `assigned_login_name` | `varchar(64)` | N | NULL | 已分配登录名 | | `del_flag` | `char(1)` | Y | `'0'` | `0` 存在 `2` 删除 | | `create_by` / `create_time` / `update_by` / `update_time` | 若依惯例 | — | — | 审计 | | `remark` | `varchar(500)` | N | NULL | 备注 | **索引**:`PRIMARY KEY (id)`;`UNIQUE KEY uk_distributor_code (distributor_code)`;`KEY idx_distributor_name (distributor_name)`;`KEY idx_identity_no (identity_no)`;`KEY idx_sales_destination (sales_destination)`;`KEY idx_del_flag (del_flag)`。 > **唯一性**:`distributor_name`、`identity_no` 在 `del_flag=0` 范围内由 Service 校验唯一。 ### 2.2 DDL(MySQL 5.7) ```sql CREATE TABLE `biz_distributor` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `distributor_code` char(2) NOT NULL COMMENT '承销商编号01-99', `distributor_name` varchar(64) NOT NULL COMMENT '承销商名称', `distributor_type` tinyint(4) NOT NULL COMMENT '1个体户 2企业', `identity_no` varchar(32) NOT NULL COMMENT '身份证号或统一社会信用代码', `contact_phone` varchar(20) NOT NULL COMMENT '联系方式', `business_location` varchar(50) DEFAULT NULL COMMENT '经营地点', `sales_destination` tinyint(4) NOT NULL COMMENT '1本市 2本省 3外省', `account_assigned` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0未分配1已分配', `sys_user_id` bigint(20) DEFAULT NULL COMMENT '关联用户ID', `assigned_login_name` varchar(64) DEFAULT NULL COMMENT '已分配登录名', `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '0存在 2删除', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`), UNIQUE KEY `uk_distributor_code` (`distributor_code`), KEY `idx_distributor_name` (`distributor_name`), KEY `idx_identity_no` (`identity_no`), KEY `idx_sales_destination` (`sales_destination`), KEY `idx_del_flag` (`del_flag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='承销商'; ``` ### 2.3 枚举 **承销商类型 `distributor_type`** | 值 | 标签 | | --- | --- | | 1 | 个体户 | | 2 | 企业 | **销售去向 `sales_destination`** | 值 | 标签 | | --- | --- | | 1 | 本市 | | 2 | 本省 | | 3 | 外省 | **删除标记 `del_flag`** | 值 | 含义 | | --- | --- | | 0 | 正常 | | 2 | 已逻辑删除 | **账号分配**:`account_assigned` 同供应商;默认角色 **`role_id=104`**(承销商)。 ### 2.4 编号生成(`DistributorCodeGenerator`) 与供应商模块一致:`MAX(distributor_code)` + 1,格式 `%02d`;上限 `99`;与插入同事务防并发重复。 ### 2.5 分配账号 与供应商共用 `TradingPartnerLoginNameGenerator`、`TradePartnerAccountAssigner`;`distributor_name` + `id` 生成登录名;`DistributorRules.ROLE_ID_DISTRIBUTOR = 104`。 --- ## 3. 接口设计 **统一响应**:`AjaxResult` / `TableDataInfo`。 **权限标识**:`tradeMarket:distributor:list|query|add|edit|remove|assignAccount` **Base Path**:`/tradeMarket/distributor` | # | 说明 | Method | URI | 权限 | | --- | --- | --- | --- | --- | | 3.1 | 分页列表 | GET | `/tradeMarket/distributor/list` | `list` | | 3.2 | 详情 | GET | `/tradeMarket/distributor/{id}` | `query` | | 3.3 | 新增 | POST | `/tradeMarket/distributor` | `add` | | 3.4 | 修改 | PUT | `/tradeMarket/distributor` | `edit` | | 3.5 | 删除 | DELETE | `/tradeMarket/distributor/{ids}` | `remove` | | 3.6 | 分配账号 | POST | `/tradeMarket/distributor/assignAccount/{id}` | `assignAccount` | #### 3.1.1 列表 Query | 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `pageNum` / `pageSize` | int | N | 默认 `1` / `20` | | `distributorCode` | string | N | 承销商编号**模糊**:`distributor_code LIKE %value%` | | `distributorName` | string | N | 承销商名称**模糊**:`distributor_name LIKE %value%` | **列表行扩展(驼峰)**:`distributorTypeName`、`salesDestinationName` + `accountAssigned`、`assignedLoginName`。 **排序**:`distributor_code ASC, id ASC`。 #### 3.2.1 详情 `data` 全业务字段(驼峰)+ 类型/销售去向中文名 + 审计字段。 #### 3.3.1 新增 / 修改 Body(驼峰) | 字段 | 新增 | 修改 | 说明 | | --- | --- | --- | --- | | `id` | — | Y | 主键 | | `distributorCode` | — | — | **禁止传入**;仅响应带回 | | `distributorName` | Y | Y | 1~20 字,trim | | `distributorType` | Y | Y | `1` / `2` | | `identityNo` | Y | Y | 身份证 / USCC | | `contactPhone` | Y | Y | 手机号 | | `businessLocation` | N | N | ≤50 字 | | `salesDestination` | Y | Y | `1` / `2` / `3` | | `remark` | N | N | ≤500 字 | #### 3.4 服务端校验(`DistributorValidation`) | 项 | 规则 | | --- | --- | | 名称 | trim 后 1~20 字;未删除记录内**名称唯一** | | 类型 + 身份号 | `1` 身份证;`2` 18 位 USCC;**身份号唯一** | | 手机 | 项目统一手机号规则 | | 销售去向 | 必须为 `1`/`2`/`3` | | 修改 | 禁止改 `distributor_code`;记录存在且未删 | | 删除 | 未删;`DistributorReferenceChecker.hasUnpaidOrder(id)` 为 false(`biz_trade_order` 待支付且未删) | #### 3.5 删除 - `DELETE /tradeMarket/distributor/{ids}`,前端单条传一个 id。 - `del_flag='2'`;存在未支付订单时失败,msg「存在未支付订单,无法删除」。 #### 3.6 Mapper 约定 - 条件均带 `del_flag='0'`。 - 模糊筛选示例: `AND distributor_code LIKE CONCAT('%', #{distributorCode}, '%')` `AND distributor_name LIKE CONCAT('%', #{distributorName}, '%')` - 逻辑删除:`UPDATE … SET del_flag='2' WHERE id=? AND del_flag='0'`。 --- ## 4. 菜单与权限(示例) | 类型 | 名称 | 权限标识 | | --- | --- | --- | | 菜单 | 承销商管理 | `tradeMarket:distributor:list` | | 按钮 | 查询 | `tradeMarket:distributor:query` | | 按钮 | 新增 | `tradeMarket:distributor:add` | | 按钮 | 修改 | `tradeMarket:distributor:edit` | | 按钮 | 删除 | `tradeMarket:distributor:remove` | | 按钮 | 分配账号 | `tradeMarket:distributor:assignAccount` | 组件路径:`tradeMarket/distributor/index`(「交易市场平台」目录下)。 --- ## 5. 与供应商模块差异(实施对照) | 项 | 供应商 `biz_supplier` | 承销商 `biz_distributor` | | --- | --- | --- | | 列表筛选 | 编号/名称**精确** | **模糊** `LIKE` | | 特色字段 | `fee_method` 及费率 | `sales_destination` | | Base Path | `/tradeMarket/supplier` | `/tradeMarket/distributor` | | 分配账号角色 | **103** | **104** | | 分配账号接口 | `POST …/supplier/assignAccount/{id}` | `POST …/distributor/assignAccount/{id}` | --- ## 6. 交付清单 - [x] `sql/biz_distributor.sql`、`sql/alter_biz_supplier_distributor_assign_account.sql` - [x] `BizDistributor`、Mapper/XML、Service、Controller - [x] `DistributorValidation`、`DistributorCodeGenerator`、`DistributorRules` - [x] 共用 `TradePartnerAccountAssigner`、`TradingPartnerLoginNameGenerator` - [x] 单元测试 / MockMvc(含 `assignAccount`) --- ## 7. 修订记录 | 版本 | 说明 | | --- | --- | | 1.0 | 初稿:`biz_distributor`;CRUD+逻辑删除;2 位编号;销售去向;列表模糊;`tradeMarket/distributor` | | 1.1 | 移除 `vehicle_plates` 字段及 API `vehiclePlates`(对齐修订草稿) | | 1.2 | 分配账号:三字段、`POST assignAccount`、角色 104、拼音首字母+id、删除联动 | | 1.3 | 删除约束:`DistributorReferenceChecker.hasUnpaidOrder`;`biz_trade_order` 待支付拦截 |