<reponame>SuperOpenDevOps/sodo
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ = Lyon

import traceback
import math
from base.auth import login_required
from api.status import Code, Message
from lib.dt import dt
from docs import RequestHandler, define_api, Param
from base.db.mysql import async_db, Func
from apps.rbac.rbac import (
    RbacUser,
    RbacRole,
    RbacUserRole,
    RbacPermission,
    RbacRolePermission,
    RbacMenu,
    RbacMenuPermission,
)

"""
用户相关
"""


class RbacUserLogin(RequestHandler):
    """
    {
        "return_code":0,
        "return_data":{
        },
        "return_msg":"成功"
    }
    """

    @define_api("/rbac/user/login", [
        Param('phone', True, 'str', '', '手机号'),
        Param('password', True, 'str', '', '用户昵称'),
    ], desc="登录")
    async def post(self):
        phone = self.get_arg('phone')
        password = self.get_arg('password')
        user = await RbacUser.async_get(phone=phone, password=password, is_delete=RbacUser.DELETE_NO)
        if not user:
            return self.write_fail(Code.User.USERNAME_OR_PASSWORD_INVALID, Message.User.USERNAME_OR_PASSWORD_INVALID)
        if user.is_using == RbacUser.USING_NO:
            return self.write_fail(Code.User.MENU_CANNOT_USE, Message.User.MENU_CANNOT_USE)

        data = {
            'token': user.encode_token()
        }

        return self.write_success(data)


class RbacUserMenu(RequestHandler):
    """
    {
        "return_code":0,
        "return_data":{
        },
        "return_msg":"成功"
    }
    """

    @define_api("/rbac/user/menu", [
    ], desc="获取菜单")
    @login_required
    async def get(self):

        permissions = await RbacUser.async_sql("SELECT p.id id "
                                               "FROM rbac_user_role ur "
                                               "INNER JOIN rbac_role_permission rp ON ur.user_id = %s AND ur.role_id = rp.role_id "
                                               "INNER JOIN rbac_permission p ON p.id = rp.permission_id ",
                                               self.current_user_id)
        permissions = [p.id for p in permissions]
        menus = []
        if permissions:
            menus = await RbacMenu.async_sql("SELECT m.id id, m.name name, m.pid pid, m.route route, m.icon icon, "
                                             "p.alias alias "
                                             "FROM rbac_menu m "
                                             "INNER JOIN rbac_menu_permission mp ON m.id = mp.menu_id "
                                             "INNER JOIN rbac_permission p ON p.id = mp.permission_id "
                                             "AND mp.permission_id IN %s ORDER BY m.pid",
                                             tuple(permissions))
        menus_data = {}
        for menu in menus:
            if menu.pid in menus_data:
                if menu.id in menus_data[menu.pid]['children']:
                    menus_data[menu.pid]['children'][menu.id]['permission'].append(menu.alias)
                else:
                    menus_data[menu.pid]['children'][menu.id] = {
                        'name': menu.name,
                        'route': menu.route,
                        'icon': menu.icon,
                        'permission': [menu.alias],
                        'children': []
                    }
            else:
                menus_data[menu.id] = {
                    'name': menu.name,
                    'route': menu.route,
                    'icon': menu.icon,
                    'children': {}
                }
        data = []
        for m in menus_data:
            menus_data[m]['children'] = list(menus_data[m]['children'].values())
            data.append(menus_data[m])

        return self.write_success(data)


class RbacUserAdd(RequestHandler):
    """
    {
        "return_code":0,
        "return_data":{
        },
        "return_msg":"成功"
    }
    """

    @define_api("/rbac/user/add", [
        Param('nickname', True, 'str', '', '用户昵称'),
        Param('phone', True, 'str', '', '手机号'),
        Param('email', True, 'str', '', '邮箱'),
        Param('roles', True, 'array', '[1]', '角色id数组'),
    ], desc="添加用户")
    @login_required
    async def post(self):
        nickname = self.get_arg('nickname')
        phone = self.get_arg('phone')
        email = self.get_arg('email')
        roles = self.get_arg_json('roles')

        async with async_db.atomic_async() as tran:
            user = await RbacUser.async_create(
                nickname=nickname,
                phone=phone,
                password=phone[5:],
                email=email,
                is_using=RbacUser.USING_IS
            )

            for role_id in roles:
                await RbacUserRole.async_create(
                    user_id=user.id,
                    role_id=role_id
                )

        return self.write_success()


class RbacUserDelete(RequestHandler):
    """
    {
        "return_code":0,
        "return_data":{
        },
        "return_msg":"成功"
    }
    """

    @define_api("/rbac/user/delete", [
        Param('user_id', True, 'int', '', '用户id')
    ], desc="删除用户")
    @login_required
    async def post(self):
        user_id = self.get_arg_int('user_id')
        user = await RbacUser.async_get(id=user_id, is_delete=RbacUser.DELETE_NO)
        if not user:
            return self.write_fail(Code.User.USER_NOE_EXIST, Message.User.USER_NOE_EXIST)

        await user.asycn_delete()

        return self.write_success()


class RbacUserEdit(RequestHandler):
    """
    {
        "return_code":0,
        "return_data":{
        },
        "return_msg":"成功"
    }
    """

    @define_api("/rbac/user/edit", [
        Param('user_id', True, 'int', '', '用户id'),
        Param('nickname', True, 'str', '', '用户昵称'),
        Param('phone', True, 'str', '', '手机号'),
        Param('email', True, 'str', '', '邮箱'),
        Param('roles', True, 'array', '[1]', '角色id数组'),
    ], desc="编辑用户")
    @login_required
    async def post(self):
        user_id = self.get_arg_int('user_id')
        nickname = self.get_arg('nickname')
        phone = self.get_arg('phone')
        email = self.get_arg('email')
        roles = set(self.get_arg_json('roles'))

        user = await RbacUser.async_get(id=user_id, is_delete=RbacUser.DELETE_NO)
        db_roles = await RbacUserRole.async_select(user_id=user_id, is_delete=RbacUserRole.DELETE_NO)
        async with async_db.atomic_async() as tran:
            await user.async_update(
                nickname=nickname,
                phone=phone,
                email=email
            )

            for dr in db_roles:
                # 要删除的
                if dr.id not in roles:
                    await dr.async_delete()
                # 是不用管的
                else:
                    roles.remove(dr.id)
            # 是需要新增的
            for role_id in roles:
                await RbacUserRole.async_create(
                    user_id=user.id,
                    role_id=role_id
                )

        return self.write_success()


class RbacUserList(RequestHandler):
    @define_api("/rbac/user/list", [
        Param('nickname', False, 'str', '', '用户昵称'),
        Param('phone', False, 'str', '', '手机号'),
        Param('email', False, 'str', '', '邮箱'),
        Param('page', False, 'int', '', '页码'),
        Param('page_count', False, 'int', '', '数量'),
    ], desc="用户列表")
    @login_required
    async def get(self):
        nickname = self.get_arg('nickname')
        phone = self.get_arg('phone')
        email = self.get_arg('email')
        page = self.get_arg_int('page', 1)
        page_count = self.get_arg_int('page_count', 20)

        cons = {
            'is_delete': RbacUser.DELETE_NO
        }
        if nickname:
            cons['nickname'] = Func.like('%{}%'.format(nickname))
        if phone:
            cons['phone'] = Func.like('%{}%'.format(phone))
        if phone:
            cons['email'] = Func.like('%{}%'.format(email))

        users = await RbacUser.async_select(**cons, paginate=(page, page_count))
        total_count = await RbacUser.async_count(**cons)

        data = {
            'data_list': [
                {
                    'id': user.id,
                    'nickname': user.nickname,
                    'phone': user.phone,
                    'email': user.email,
                    'create_time': dt.dt_to_str(user.create_time)
                } for user in users
            ],
            'total_count': total_count,
            'page': page,
            'page_size': math.ceil(total_count / page_count)
        }

        return self.write_success(data)


"""
角色相关
"""


class RbacRoleAdd(RequestHandler):
    @define_api("/rbac/role/add", [
        Param('name', True, 'str', '', '角色名称'),
        Param('permissions', True, 'array', '[1]', '权限id数组'),
    ], desc="添加角色")
    @login_required
    async def post(self):
        name = self.get_arg('name')
        permissions = self.get_arg_json('permissions')

        async with async_db.atomic_async() as tran:
            role = await RbacRole.async_create(
                name=name
            )

            for pid in permissions:
                await RbacRolePermission.async_create(
                    role_id=role.id,
                    permission_id=pid
                )

        return self.write_success()


class RbacRoleDelete(RequestHandler):
    @define_api("/rbac/role/delete", [
        Param('role_id', True, 'int', '', '角色id')
    ], desc="删除用户")
    @login_required
    async def post(self):
        role_id = self.get_arg_int('role_id')
        role = await RbacRole.async_get(id=role_id, is_delete=RbacUser.DELETE_NO)
        if not role:
            return self.write_fail(Code.User.ROLE_NOE_EXIST, Message.User.ROLE_NOE_EXIST)

        await role.asycn_delete()

        return self.write_success()


class RbacRoleEdit(RequestHandler):
    @define_api("/rbac/role/edit", [
        Param('role_id', True, 'int', '', '角色id'),
        Param('name', True, 'str', '', '角色名称'),
        Param('permissions', True, 'array', '[1]', '权限id数组'),
    ], desc="编辑角色")
    @login_required
    async def post(self):
        role_id = self.get_arg_int('role_id')
        name = self.get_arg('name')
        permissions = set(self.get_arg_json('permissions'))

        role = await RbacRole.async_get(id=role_id, is_delete=RbacUser.DELETE_NO)
        db_permissions = await RbacRolePermission.async_select(user_id=role_id, is_delete=RbacUserRole.DELETE_NO)
        async with async_db.atomic_async() as tran:
            await role.async_update(
                name=name,
            )

            for dp in db_permissions:
                # 要删除的
                if dp.id not in permissions:
                    await dp.async_delete()
                # 是不用管的
                else:
                    permissions.remove(dp.id)
            # 是需要新增的
            for permission_id in permissions:
                await RbacRolePermission.async_create(
                    role_id=role.id,
                    permission_id=permission_id
                )

        return self.write_success()


class RbacRoleList(RequestHandler):
    @define_api("/rbac/role/list", [
        Param('name', False, 'str', '', '角色名称'),
        Param('page', False, 'int', '', '页码'),
        Param('page_count', False, 'int', '', '数量'),
    ], desc="角色列表")
    @login_required
    async def get(self):
        name = self.get_arg('name')
        page = self.get_arg_int('page', 1)
        page_count = self.get_arg_int('page_count', 20)

        cons = {
            'is_delete': RbacUser.DELETE_NO
        }
        if name:
            cons['name'] = Func.like('%{}%'.format(name))
        roles = await RbacRole.async_select(**cons, paginate=(page, page_count))
        total_count = await RbacRole.async_count(**cons)

        data = {
            'data_list': [
                {
                    'id': role.id,
                    'nickname': role.nickname,
                    'create_time': dt.dt_to_str(role.create_time)
                } for role in roles
            ],
            'total_count': total_count,
            'page': page,
            'page_size': math.ceil(total_count / page_count)
        }

        return self.write_success(data)
