Skip to content

行政区划

行政区划模块管理全国省/市/区/街道四级区域数据,支持树形结构查询、级联选择器、拼音自动生成等功能。用户表通过 province_code/city_code/district_code/street_code 四个字段关联城市表的 area_code。

模块结构 ​

src/modules/city/
├── models.py     # 城市模型
├── schema.py     # 表单验证(WTForms)
├── repository.py # 数据访问层
├── service.py    # 业务逻辑层(继承 BaseService)
└── router.py     # 路由控制器

城市模型 ​

python
# src/modules/city/models.py
class City(BaseModel, BaseDB):
    """
    城市区域模型

    支持省、市、县区、街道四级区域结构。
    """
    __tablename__ = DB_PREFIX + "city"
    __table_comment__ = "城市表"

    # 城市区号
    city_code = db.Column(db.String(6), nullable=False, index=True, comment="城市区号")
    # 行政编码(用于用户表关联)
    area_code = db.Column(db.String(20), nullable=False, index=True, comment="行政编码")
    # 上级行政编码
    parent_code = db.Column(db.String(20), nullable=False, index=True, comment="上级行政编码")
    # 邮政编码
    zip_code = db.Column(db.String(6), nullable=False, comment="邮政编码")
    # 城市级别:0-省份 1-城市 2-县区 3-街道 4-社区
    level = db.Column(db.Integer, default=0, server_default=text('0'), index=True,
                      comment="城市级别:0-省份 1-城市 2-县区 3-街道 4-社区")
    # 上级城市ID
    pid = db.Column(db.Integer, default=0, server_default=text('0'), index=True, comment="上级城市ID")
    # 城市名称
    name = db.Column(db.String(150), nullable=False, comment="城市名称")
    # 城市简称
    short_name = db.Column(db.String(150), nullable=False, index=True, comment="城市简称")
    # 城市全称(沿 pid 链拼接,如"台湾省,云林,元长,华鹿")
    full_name = db.Column(db.String(150), nullable=True, comment="城市全称")
    # 城市拼音(由简称自动生成,如"TaiWan")
    pinyin = db.Column(db.String(150), nullable=True, comment="城市拼音")
    # 经纬度
    lng = db.Column(db.String(150), nullable=True, comment="城市经度")
    lat = db.Column(db.String(150), nullable=True, comment="城市纬度")

服务层特色 ​

城市服务继承 BaseService,并通过钩子实现自动派生字段:

python
# src/modules/city/service.py
class CityService(BaseService):
    repository = CityRepository
    form = CityForm
    model_name = "城市"

    @classmethod
    def before_add(cls, form):
        """新增前钩子:自动补全 full_name / pinyin"""
        data = form.data
        return {
            "full_name": cls._build_full_name(data.get("pid") or 0, data.get("name")),
            "pinyin": cls._build_pinyin(data.get("shortName") or data.get("name")),
        }

    @classmethod
    def before_delete(cls, ids):
        """删除前校验:检查是否存在子级城市"""
        for cid in ids:
            if CityRepository.count_children(cid, parent_field='pid') > 0:
                raise BizError(f"城市ID {cid} 存在子级城市,请先删除子级城市")

    @classmethod
    def _detail_dict(cls, obj):
        """详情序列化:追加 parentName(父级城市名称)"""
        data = model_to_dict(obj)
        parent_id = getattr(obj, 'pid', None)
        if parent_id:
            parent = CityRepository.get_by_id(parent_id)
            data['parentName'] = parent.name if parent else None
        else:
            data['parentName'] = None
        return data

自动生成字段 ​

字段生成规则示例
full_name沿 pid 链向上拼接,逗号分隔台湾省,云林,元长,华鹿
pinyin由简称生成无音调拼音,每音节首字母大写TaiWan、HuaLu

删除前校验 ​

城市采用树形结构(pid 字段),删除前检查是否存在子级城市:

广东省 (pid=0)
├── 深圳市 (pid=广东省.id)
│   ├── 南山区 (pid=深圳市.id)
│   └── 福田区 (pid=深圳市.id)
└── 广州市 (pid=广东省.id)

删除「深圳市」前,会检查其下是否存在「南山区」「福田区」等子级城市。

树形数据查询 ​

城市模块提供树形数据接口,用于前端级联选择器:

python
def get_city_tree():
    """获取城市树形数据,构建省-市-区三级树形结构"""
    all_cities = CityRepository.list_all()
    city_dicts = models_to_list(all_cities)
    return _build_city_tree(city_dicts)


def _build_city_tree(city_list, parent_id=0):
    """递归构建城市树形结构"""
    tree = []
    for city in city_list:
        if city.get("pid") == parent_id:
            node = {
                "id": city.get("id"),
                "name": city.get("name"),
                "code": city.get("area_code"),
                "level": city.get("level"),
                "children": _build_city_tree(city_list, city.get("id")),
            }
            if not node["children"]:
                del node["children"]
            tree.append(node)
    return tree

与用户模块的关联 ​

用户表通过四个字段关联城市表:

python
# src/modules/system/user/models.py
class User(BaseModel, BaseDB):
    # 省份编码
    province_code = db.Column(db.String(30), nullable=False, comment="省份编码")
    # 城市编码
    city_code = db.Column(db.String(30), nullable=False, comment="城市编码")
    # 县区编码
    district_code = db.Column(db.String(30), nullable=False, comment="县区编码")
    # 街道编码
    street_code = db.Column(db.String(30), nullable=False, comment="街道编码")
    # 省市区信息(拼接后的可读文本,如"广东省深圳市南山区")
    city_info = db.Column(db.String(255), nullable=True, comment="省市区信息")

用户新增/更新时,前端通过级联选择器传入 [省编码, 市编码, 区编码, 街道编码] 数组,Service 层拆分到独立字段:

python
# src/modules/system/user/service.py
def _apply_city(create_data, city):
    """将行政区划数组拆分到对应的字段"""
    create_data["province_code"] = city[0]
    create_data["city_code"] = city[1]
    create_data["district_code"] = city[2]
    create_data["street_code"] = city[3]

API 接口 ​

方法路径权限节点说明
GET/api/v1/city/listsys:city:list城市列表(支持 pid/name 筛选)
GET/api/v1/city/detail/<id>sys:city:detail城市详情
GET/api/v1/city/child/<code>-子级城市列表(级联选择器用)
GET/api/v1/city/tree-城市树形数据
POST/api/v1/city/addsys:city:add新增城市
PUT/api/v1/city/updatesys:city:update编辑城市
DELETE/api/v1/city/delete/<id>sys:city:delete删除城市

数据字典 ​

字典编码用途字典项示例
city_level城市级别名称0=省份, 1=城市, 2=县区, 3=街道, 4=社区

总结 ​

行政区划模块采用树形结构(pid 字段),支持省/市/区/街道四级管理。before_add 钩子自动补全 full_name(沿 pid 链拼接)和 pinyin(由简称生成),before_delete 钩子校验子级城市引用。用户表通过 area_code 字段关联城市表,前端使用级联选择器。

小蚂蚁云团队 · 提供技术支持