openapi: 3.0.3
info:
  title: RBAC 数据权限过滤系统 API
  description: |
    提供账号、角色、权限管理和数据权限过滤功能的 RESTful API。
    
    **核心功能**:
    - 账号管理(CRUD)
    - 角色管理(CRUD)
    - 权限管理(CRUD)
    - 账号-角色关联管理
    - 角色-权限关联管理
    - 基于 owner_id 的自动数据权限过滤
    
  version: 1.0.0
  contact:
    name: API Support
    email: support@example.com

servers:
  - url: http://localhost:8080/api/v1
    description: 开发环境
  - url: https://api.example.com/api/v1
    description: 生产环境

tags:
  - name: accounts
    description: 账号管理
  - name: roles
    description: 角色管理
  - name: permissions
    description: 权限管理
  - name: account-roles
    description: 账号-角色关联管理
  - name: role-permissions
    description: 角色-权限关联管理

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 使用 JWT Token 进行认证(格式: Bearer <token>)

  schemas:
    # 通用响应结构
    SuccessResponse:
      type: object
      required:
        - code
        - message
        - data
        - timestamp
      properties:
        code:
          type: integer
          description: 响应码(0表示成功)
          example: 0
        message:
          type: string
          description: 响应消息
          example: success
        data:
          type: object
          description: 响应数据
        timestamp:
          type: string
          format: date-time
          description: 响应时间戳(ISO 8601)
          example: "2025-11-17T15:30:00+08:00"

    ErrorResponse:
      type: object
      required:
        - code
        - message
        - data
        - timestamp
      properties:
        code:
          type: integer
          description: 错误码(非0表示错误)
          example: 1001
        message:
          type: string
          description: 错误消息
          example: 参数验证失败
        data:
          type: object
          nullable: true
          description: 错误详情
        timestamp:
          type: string
          format: date-time
          description: 响应时间戳(ISO 8601)
          example: "2025-11-17T15:30:00+08:00"

    PaginationResponse:
      type: object
      required:
        - total
        - page
        - size
        - items
      properties:
        total:
          type: integer
          format: int64
          description: 总记录数
          example: 100
        page:
          type: integer
          description: 当前页码(从1开始)
          example: 1
        size:
          type: integer
          description: 每页记录数
          example: 20
        items:
          type: array
          description: 数据列表
          items:
            type: object

    # 账号相关
    Account:
      type: object
      required:
        - id
        - created_at
        - updated_at
        - username
        - phone
        - user_type
        - status
        - creator
        - updater
      properties:
        id:
          type: integer
          format: uint
          description: 账号ID
          example: 1
        created_at:
          type: string
          format: date-time
          description: 创建时间
          example: "2025-11-17T15:30:00+08:00"
        updated_at:
          type: string
          format: date-time
          description: 更新时间
          example: "2025-11-17T15:30:00+08:00"
        username:
          type: string
          description: 用户名
          example: "admin"
        phone:
          type: string
          description: 手机号
          example: "13800138000"
        user_type:
          type: integer
          description: 用户类型(1=root, 2=平台, 3=代理, 4=企业)
          enum: [1, 2, 3, 4]
          example: 1
        shop_id:
          type: integer
          format: uint
          nullable: true
          description: 店铺ID
          example: 10
        parent_id:
          type: integer
          format: uint
          nullable: true
          description: 上级账号ID
          example: null
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1
        creator:
          type: integer
          format: uint
          description: 创建人ID
          example: 1
        updater:
          type: integer
          format: uint
          description: 更新人ID
          example: 1

    CreateAccountRequest:
      type: object
      required:
        - username
        - phone
        - password
        - user_type
      properties:
        username:
          type: string
          minLength: 3
          maxLength: 50
          description: 用户名
          example: "testuser"
        phone:
          type: string
          pattern: '^\d{11}$'
          description: 手机号(11位)
          example: "13800138000"
        password:
          type: string
          minLength: 6
          maxLength: 50
          description: 密码(6-50字符)
          example: "password123"
        user_type:
          type: integer
          description: 用户类型(1=root, 2=平台, 3=代理, 4=企业)
          enum: [1, 2, 3, 4]
          example: 2
        shop_id:
          type: integer
          format: uint
          nullable: true
          description: 店铺ID
          example: 10
        parent_id:
          type: integer
          format: uint
          nullable: true
          description: 上级账号ID(非root账号必填)
          example: 1
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          default: 1
          example: 1

    UpdateAccountRequest:
      type: object
      properties:
        username:
          type: string
          minLength: 3
          maxLength: 50
          description: 用户名
          example: "newusername"
        phone:
          type: string
          pattern: '^\d{11}$'
          description: 手机号(11位)
          example: "13900139000"
        password:
          type: string
          minLength: 6
          maxLength: 50
          description: 新密码(6-50字符)
          example: "newpassword123"
        shop_id:
          type: integer
          format: uint
          nullable: true
          description: 店铺ID
          example: 20
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1

    # 角色相关
    Role:
      type: object
      required:
        - id
        - created_at
        - updated_at
        - role_name
        - role_type
        - status
        - creator
        - updater
      properties:
        id:
          type: integer
          format: uint
          description: 角色ID
          example: 1
        created_at:
          type: string
          format: date-time
          description: 创建时间
          example: "2025-11-17T15:30:00+08:00"
        updated_at:
          type: string
          format: date-time
          description: 更新时间
          example: "2025-11-17T15:30:00+08:00"
        role_name:
          type: string
          description: 角色名称
          example: "系统管理员"
        role_desc:
          type: string
          description: 角色描述
          example: "拥有系统所有权限"
        role_type:
          type: integer
          description: 角色类型(1=超级, 2=代理, 3=企业)
          enum: [1, 2, 3]
          example: 1
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1
        creator:
          type: integer
          format: uint
          description: 创建人ID
          example: 1
        updater:
          type: integer
          format: uint
          description: 更新人ID
          example: 1

    CreateRoleRequest:
      type: object
      required:
        - role_name
        - role_type
      properties:
        role_name:
          type: string
          minLength: 2
          maxLength: 50
          description: 角色名称
          example: "代理管理员"
        role_desc:
          type: string
          maxLength: 255
          description: 角色描述
          example: "代理商管理员角色"
        role_type:
          type: integer
          description: 角色类型(1=超级, 2=代理, 3=企业)
          enum: [1, 2, 3]
          example: 2
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          default: 1
          example: 1

    UpdateRoleRequest:
      type: object
      properties:
        role_name:
          type: string
          minLength: 2
          maxLength: 50
          description: 角色名称
          example: "新角色名称"
        role_desc:
          type: string
          maxLength: 255
          description: 角色描述
          example: "新角色描述"
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1

    # 权限相关
    Permission:
      type: object
      required:
        - id
        - created_at
        - updated_at
        - perm_name
        - perm_code
        - perm_type
        - sort
        - status
        - creator
        - updater
      properties:
        id:
          type: integer
          format: uint
          description: 权限ID
          example: 1
        created_at:
          type: string
          format: date-time
          description: 创建时间
          example: "2025-11-17T15:30:00+08:00"
        updated_at:
          type: string
          format: date-time
          description: 更新时间
          example: "2025-11-17T15:30:00+08:00"
        perm_name:
          type: string
          description: 权限名称
          example: "用户管理"
        perm_code:
          type: string
          description: 权限编码(唯一)
          example: "user:manage"
        perm_type:
          type: integer
          description: 权限类型(1=菜单, 2=按钮)
          enum: [1, 2]
          example: 1
        url:
          type: string
          description: URL路径
          example: "/admin/users"
        parent_id:
          type: integer
          format: uint
          nullable: true
          description: 上级权限ID
          example: null
        sort:
          type: integer
          description: 排序
          example: 0
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1
        creator:
          type: integer
          format: uint
          description: 创建人ID
          example: 1
        updater:
          type: integer
          format: uint
          description: 更新人ID
          example: 1

    CreatePermissionRequest:
      type: object
      required:
        - perm_name
        - perm_code
        - perm_type
      properties:
        perm_name:
          type: string
          minLength: 2
          maxLength: 50
          description: 权限名称
          example: "订单管理"
        perm_code:
          type: string
          minLength: 2
          maxLength: 100
          description: 权限编码(唯一)
          example: "order:manage"
        perm_type:
          type: integer
          description: 权限类型(1=菜单, 2=按钮)
          enum: [1, 2]
          example: 1
        url:
          type: string
          maxLength: 255
          description: URL路径
          example: "/admin/orders"
        parent_id:
          type: integer
          format: uint
          nullable: true
          description: 上级权限ID
          example: 1
        sort:
          type: integer
          minimum: 0
          description: 排序
          default: 0
          example: 10
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          default: 1
          example: 1

    UpdatePermissionRequest:
      type: object
      properties:
        perm_name:
          type: string
          minLength: 2
          maxLength: 50
          description: 权限名称
          example: "新权限名称"
        url:
          type: string
          maxLength: 255
          description: URL路径
          example: "/admin/new-path"
        sort:
          type: integer
          minimum: 0
          description: 排序
          example: 20
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1

    # 账号-角色关联
    AccountRole:
      type: object
      required:
        - id
        - account_id
        - role_id
        - status
      properties:
        id:
          type: integer
          format: uint
          description: 关联ID
          example: 1
        account_id:
          type: integer
          format: uint
          description: 账号ID
          example: 10
        role_id:
          type: integer
          format: uint
          description: 角色ID
          example: 5
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1

    AssignRolesToAccountRequest:
      type: object
      required:
        - role_ids
      properties:
        role_ids:
          type: array
          items:
            type: integer
            format: uint
          description: 角色ID列表
          example: [1, 2, 3]

    # 角色-权限关联
    RolePermission:
      type: object
      required:
        - id
        - role_id
        - perm_id
        - status
      properties:
        id:
          type: integer
          format: uint
          description: 关联ID
          example: 1
        role_id:
          type: integer
          format: uint
          description: 角色ID
          example: 5
        perm_id:
          type: integer
          format: uint
          description: 权限ID
          example: 10
        status:
          type: integer
          description: 状态(0=禁用, 1=启用)
          enum: [0, 1]
          example: 1

    AssignPermsToRoleRequest:
      type: object
      required:
        - perm_ids
      properties:
        perm_ids:
          type: array
          items:
            type: integer
            format: uint
          description: 权限ID列表
          example: [1, 2, 3, 4, 5]

  responses:
    UnauthorizedError:
      description: 未授权(缺少认证令牌或令牌无效)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 1002
            message: 缺少认证令牌
            data: null
            timestamp: "2025-11-17T15:30:00+08:00"

    ForbiddenError:
      description: 禁止访问(无权限)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 1005
            message: 禁止访问
            data: null
            timestamp: "2025-11-17T15:30:00+08:00"

    NotFoundError:
      description: 资源未找到
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 1006
            message: 资源未找到
            data: null
            timestamp: "2025-11-17T15:30:00+08:00"

    ValidationError:
      description: 参数验证失败
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 1001
            message: 参数验证失败
            data:
              field: username
              error: 用户名长度必须在 3-50 个字符之间
            timestamp: "2025-11-17T15:30:00+08:00"

    InternalServerError:
      description: 服务器内部错误
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            code: 2001
            message: 内部服务器错误
            data: null
            timestamp: "2025-11-17T15:30:00+08:00"

security:
  - BearerAuth: []

paths:
  # 账号管理
  /accounts:
    post:
      tags:
        - accounts
      summary: 创建账号
      description: |
        创建新账号。只有本级账号能创建下级账号。
        
        **业务规则**:
        - root 账号(user_type=1)的 parent_id 为 NULL
        - 非 root 账号必须提供 parent_id
        - parent_id 创建后不可更改
        - username 和 phone 必须唯一
      operationId: createAccount
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccountRequest'
      responses:
        '200':
          description: 创建成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Account'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    get:
      tags:
        - accounts
      summary: 账号列表(分页)
      description: |
        查询账号列表,支持分页和过滤。
        
        **数据权限过滤**:
        - root 账号(user_type=1)可以查看所有账号
        - 非 root 账号只能查看自己和所有下级账号
        - 基于 owner_id 字段自动过滤(未来添加)
      operationId: listAccounts
      parameters:
        - name: page
          in: query
          description: 页码(从1开始)
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          description: 每页记录数(最大100)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: username
          in: query
          description: 用户名(模糊查询)
          schema:
            type: string
        - name: user_type
          in: query
          description: 用户类型
          schema:
            type: integer
            enum: [1, 2, 3, 4]
        - name: status
          in: query
          description: 状态
          schema:
            type: integer
            enum: [0, 1]
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - $ref: '#/components/schemas/PaginationResponse'
                          - type: object
                            properties:
                              items:
                                type: array
                                items:
                                  $ref: '#/components/schemas/Account'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /accounts/{id}:
    get:
      tags:
        - accounts
      summary: 获取账号详情
      description: 根据账号ID获取账号详情。受数据权限过滤限制。
      operationId: getAccount
      parameters:
        - name: id
          in: path
          required: true
          description: 账号ID
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Account'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - accounts
      summary: 更新账号
      description: |
        更新账号信息。
        
        **限制**:
        - 不能修改 parent_id(创建后不可更改)
        - 不能修改 user_type
      operationId: updateAccount
      parameters:
        - name: id
          in: path
          required: true
          description: 账号ID
          schema:
            type: integer
            format: uint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAccountRequest'
      responses:
        '200':
          description: 更新成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Account'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - accounts
      summary: 删除账号(软删除)
      description: |
        软删除账号(设置 deleted_at 字段)。
        
        **业务规则**:
        - 软删除后,该账号的数据对上级仍然可见
        - 递归查询下级ID时包含已删除账号
      operationId: deleteAccount
      parameters:
        - name: id
          in: path
          required: true
          description: 账号ID
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 删除成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # 角色管理
  /roles:
    post:
      tags:
        - roles
      summary: 创建角色
      operationId: createRole
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateRoleRequest'
      responses:
        '200':
          description: 创建成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Role'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    get:
      tags:
        - roles
      summary: 角色列表(分页)
      operationId: listRoles
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: role_type
          in: query
          description: 角色类型
          schema:
            type: integer
            enum: [1, 2, 3]
        - name: status
          in: query
          description: 状态
          schema:
            type: integer
            enum: [0, 1]
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - $ref: '#/components/schemas/PaginationResponse'
                          - type: object
                            properties:
                              items:
                                type: array
                                items:
                                  $ref: '#/components/schemas/Role'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /roles/{id}:
    get:
      tags:
        - roles
      summary: 获取角色详情
      operationId: getRole
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Role'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - roles
      summary: 更新角色
      operationId: updateRole
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateRoleRequest'
      responses:
        '200':
          description: 更新成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Role'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - roles
      summary: 删除角色(软删除)
      operationId: deleteRole
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 删除成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # 权限管理
  /permissions:
    post:
      tags:
        - permissions
      summary: 创建权限
      operationId: createPermission
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePermissionRequest'
      responses:
        '200':
          description: 创建成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Permission'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    get:
      tags:
        - permissions
      summary: 权限列表(分页)
      operationId: listPermissions
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: perm_type
          in: query
          description: 权限类型
          schema:
            type: integer
            enum: [1, 2]
        - name: status
          in: query
          description: 状态
          schema:
            type: integer
            enum: [0, 1]
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        allOf:
                          - $ref: '#/components/schemas/PaginationResponse'
                          - type: object
                            properties:
                              items:
                                type: array
                                items:
                                  $ref: '#/components/schemas/Permission'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /permissions/{id}:
    get:
      tags:
        - permissions
      summary: 获取权限详情
      operationId: getPermission
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Permission'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    put:
      tags:
        - permissions
      summary: 更新权限
      operationId: updatePermission
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePermissionRequest'
      responses:
        '200':
          description: 更新成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Permission'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    delete:
      tags:
        - permissions
      summary: 删除权限(软删除)
      operationId: deletePermission
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 删除成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # 账号-角色关联
  /accounts/{account_id}/roles:
    post:
      tags:
        - account-roles
      summary: 为账号分配角色
      description: 批量为账号分配角色。如果已存在关联,则忽略。
      operationId: assignRolesToAccount
      parameters:
        - name: account_id
          in: path
          required: true
          description: 账号ID
          schema:
            type: integer
            format: uint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignRolesToAccountRequest'
      responses:
        '200':
          description: 分配成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    get:
      tags:
        - account-roles
      summary: 获取账号的所有角色
      operationId: getAccountRoles
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Role'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /accounts/{account_id}/roles/{role_id}:
    delete:
      tags:
        - account-roles
      summary: 移除账号的角色
      description: 软删除账号-角色关联记录
      operationId: removeRoleFromAccount
      parameters:
        - name: account_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
        - name: role_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 移除成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # 角色-权限关联
  /roles/{role_id}/permissions:
    post:
      tags:
        - role-permissions
      summary: 为角色分配权限
      description: 批量为角色分配权限。如果已存在关联,则忽略。
      operationId: assignPermsToRole
      parameters:
        - name: role_id
          in: path
          required: true
          description: 角色ID
          schema:
            type: integer
            format: uint
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssignPermsToRoleRequest'
      responses:
        '200':
          description: 分配成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

    get:
      tags:
        - role-permissions
      summary: 获取角色的所有权限
      operationId: getRolePermissions
      parameters:
        - name: role_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 查询成功
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessResponse'
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Permission'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /roles/{role_id}/permissions/{perm_id}:
    delete:
      tags:
        - role-permissions
      summary: 移除角色的权限
      description: 软删除角色-权限关联记录
      operationId: removePermFromRole
      parameters:
        - name: role_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
        - name: perm_id
          in: path
          required: true
          schema:
            type: integer
            format: uint
      responses:
        '200':
          description: 移除成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '500':
          $ref: '#/components/responses/InternalServerError'
