diff --git a/api/api_v1/endpoints/interview.py b/api/api_v1/endpoints/interview.py index 393d39a..cd70c83 100644 --- a/api/api_v1/endpoints/interview.py +++ b/api/api_v1/endpoints/interview.py @@ -1,20 +1,27 @@ import operator import os import re +from typing import Any + import pandas as pd import pymongo from fastapi import APIRouter, Depends, Request, File, UploadFile +from fastapi.security import OAuth2PasswordRequestForm from motor.motor_asyncio import AsyncIOMotorDatabase from api import deps -from utils.dingding import get_redis_alluid, send_dates +# from utils.dingding import get_redis_alluid, send_dates,unionid,get_alluid_list +from core import security +from core.config import settings +from core.security import get_password_hash +from utils.dingding import * from utils.jianli import get_resume import crud, schemas from datetime import datetime from core.configuration import * from db import get_database from db.ckdb import get_ck_db, CKDrive - +from datetime import timedelta from models.interview_zsgc import InterviewDo from utils import get_time, qujian_time, Download_xlsx, send_str_mail @@ -245,19 +252,19 @@ async def interview_insert( if data_in.time_type == 'now': # 查询返回的数据一共多少条 len_sql = f"""select uid from HR.resumes where {where} and toDate(star_time) == '{times}' ORDER BY event_time""" - sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid, + sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid,interview_id, feedback,interview_name from HR.resumes where {where} and toDate(star_time) == '{times}' ORDER BY event_time LIMIT 10 OFFSET {(data_in.pages - 1) * 10}""" # 明天及之后的面试 elif data_in.time_type == 'tomorrow': len_sql = f"""select uid from HR.resumes where {where} and toDate(star_time) > '{times}' ORDER BY event_time""" - sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid, + sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid,interview_id, feedback,interview_name from HR.resumes where {where} and toDate(star_time) > '{times}' ORDER BY event_time LIMIT 10 OFFSET {(data_in.pages - 1) * 10}""" # 昨天及以前的面试 else: len_sql = f"""select uid from HR.resumes where {where} and toDate(star_time) < '{times}' ORDER BY event_time""" - sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid, + sql = f"""select interview_round,interview_type,star_time,end_time,name,phone,job_names,hr_name,uid,interview_id, feedback,interview_name from HR.resumes where {where} and toDate(star_time) < '{times}' ORDER BY event_time LIMIT 10 OFFSET {(data_in.pages - 1) * 10}""" if where == '': @@ -291,6 +298,7 @@ async def interview_insert( date1['type'] = int(df['feedback'][i]) date1['name'] = df['interview_name'][i] dates['type'] = date1 + dates['interview_id'] = df['interview_id'][i] datas.append(dates) data = {'lens': len_date, 'data': datas @@ -826,8 +834,145 @@ async def event_edit( """批量修改已读状态""" # 面试记录 if data_in.type == 'interview': - await crud.interview_record.up_interview(db,data_in) + await crud.interview_record.up_interview(db, data_in) # 邮件,反馈 else: await crud.email_record.up_hint(db, data_in) return schemas.Msg(code=200, msg='ok', data='') + + +@router.post("/login") +async def login( + data_in: schemas.Login, + data: OAuth2PasswordRequestForm = Depends(), + db: AsyncIOMotorDatabase = Depends(get_database) +) -> Any: + """ + OAuth2兼容令牌登录,获取将来令牌的访问令牌 + """ + user_id = Unionid(data_in.unionid) + user_list = get_alluid_list() + if user_id not in user_list: + return schemas.Msg(code=-1, msg='密码或用户名错误') + user = await crud.user.gets_user(db, user_id=user_id) + if user.state == 1: + return schemas.Msg(code=-1, msg='您的账号已被锁定,请联系管理员解锁') + access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + await crud.user.update_login_time(db, user.id) # 更新最后一次登录时间 + return { + 'data': { + 'name': user.name, # 名字 + 'email': user.email, # 邮箱 + 'tel': user.tel, # 电话 + 'user_id': user.id, # 钉钉id + 'rank': user.rank, # 区分hr和面试官 + 'token': security.create_access_token( + expires_delta=access_token_expires, user_id=user.user_id, email=user.email, + tel=user.tel, name=user.name), + "token_type": "bearer"}, + 'access_token': security.create_access_token( + expires_delta=access_token_expires, user_id=user.user_id, email=user.email, + tel=user.tel, name=user.name + ), + "token_type": "bearer", + 'code': 200, + 'msg': 'success', + } + + +@router.post("/reset_password") +async def reset_password(request: Request, + data_in: schemas.UserRestPassword, + db: AsyncIOMotorDatabase = Depends(get_database), + #current_user: schemas.User = Depends(deps.get_current_user) + ) -> Any: + """ + 修改其他人密码 + """ + try: + await crud.user.reset_password(db, data_in) + except Exception as e: + return schemas.Msg(code=-9, msg='修改失败', data={'username': data_in}) + return schemas.Msg(code=200, msg='ok') + + +@router.post("/reset_my_password") +async def reset_password(request: Request, + data_in: schemas.UserRestMyPassword, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.User = Depends(deps.get_current_user) + ) -> Any: + """ + 修改自己的密码 + """ + await crud.user.reset_password(db, + schemas.UserRestPassword(user_id=current_user.user_id, password=data_in.password)) + return schemas.Msg(code=200, msg='ok') + + +@router.post("/add_account") +async def all_account( + request: Request, + data_in: schemas.Createuser, + db: AsyncIOMotorDatabase = Depends(get_database), + # current_user: schemas.User = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 创建新账号 + """ + user_id = Unionid(data_in.unionid) + user_list = get_alluid_list() + if user_id not in user_list: + return schemas.Msg(code=-9, msg="不是本公司的员工") + if is_exists := await crud.user.exists(db, {'user_id': user_id}): + return schemas.Msg(code=-9, msg='已创建该账号') + else: + new_account = schemas.UserCreate(name=data_in.name, hashed_password=get_password_hash('123'), + unionid=data_in.unionid, + rank=data_in.rank, email=data_in.email, tel=data_in.tel, user_id=user_id) + await crud.user.create(db, new_account) # 创建账号 + + return schemas.Msg(code=200, msg='创建成功', data='') + + +@router.post("/forbid_login") +async def forbid_lojin(request: Request, + data_in: schemas.Get_userid, + db: AsyncIOMotorDatabase = Depends(get_database), + #current_user: schemas.User = Depends(deps.get_current_user) + ) -> schemas.Msg: + """ + 禁止/解禁用户登录功能 + """ + if data_in.type == 1: + await crud.user.forbid_lojin(db, data_in,1) + elif data_in.type == 0: + await crud.user.forbid_lojin(db, data_in, 0) + return schemas.Msg(code=200, msg='ok', data='') + +@router.post("/owner_list") +async def event_list( + request: Request, + data_in: schemas.Getdate, + db: AsyncIOMotorDatabase = Depends(get_database), + #current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """获取基本信息列表""" + try: + res = await crud.basic_data.one_owner(db, data_in) + except Exception as e: + return schemas.Msg(code=-9, msg='查无数据', data='') + + return schemas.Msg(code=200, msg='ok', data=res) + + +@router.post("/owner_edit") +async def event_edit( + request: Request, + data_in: schemas.Ownername, + db: AsyncIOMotorDatabase = Depends(get_database), + #current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """新增,删除基本信息""" + await crud.basic_data.update(db, data_in) + return schemas.Msg(code=200, msg='ok', data='') \ No newline at end of file diff --git a/api/deps.py b/api/deps.py index 560cbc2..a0d4a79 100644 --- a/api/deps.py +++ b/api/deps.py @@ -14,14 +14,12 @@ from db import get_database from db.ckdb import CKDrive, get_ck_db reusable_oauth2 = OAuth2PasswordBearer( - tokenUrl=f"{settings.API_V1_STR}/user/login" + tokenUrl=f"{settings.API_V1_STR}/itr/login" ) def get_current_user(token: str = Depends(reusable_oauth2) ) -> schemas.UserDB: - # def get_current_user(token: str - # ) -> schemas.UserDBBase: try: payload = jwt.decode( token, settings.SECRET_KEY, algorithms=[security.ALGORITHM] diff --git a/core/config.py b/core/config.py index 4be3132..a05e95d 100644 --- a/core/config.py +++ b/core/config.py @@ -21,7 +21,7 @@ class Settings(BaseSettings): DEFAULT_PASSWORD = '123456' - ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 + ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 # 登录后一天过期 SECRET_KEY: str = 'ZaFX6EypK6PtuhGv11q4DLRvAb0csiLx4dbKUwLwCe8' REDIS_CONF = { diff --git a/crud/__init__.py b/crud/__init__.py index c467ad4..99dd674 100644 --- a/crud/__init__.py +++ b/crud/__init__.py @@ -30,3 +30,4 @@ from .crud_interview_record import interview_record from .crud_worker_info import worker_info from .crud_owner_info import owner_info from .crud_count_in_worker import count_in_worker +from .crud_basic_data import basic_data diff --git a/crud/crud_basic_data.py b/crud/crud_basic_data.py new file mode 100644 index 0000000..a17d473 --- /dev/null +++ b/crud/crud_basic_data.py @@ -0,0 +1,28 @@ +from motor.motor_asyncio import AsyncIOMotorDatabase +import schemas +from crud.base import CRUDBase + +__all__ = 'basic_data', + + +class CRUDOwnername(CRUDBase): + # 获取所有的基本信息数据 + async def all_owner(self, db: AsyncIOMotorDatabase): + return await self.find_many(db, {}, {'_id': 0, 'chinese': 0}) + + # 获取对应的基础数据 + async def one_owner(self, db: AsyncIOMotorDatabase, data_in: schemas.Getdate): + return await self.find_one(db, {'name': data_in.name}, {'_id': 0, 'chinese': 0}) + + # 修改、添加渠道数据 + async def update(self, db: AsyncIOMotorDatabase, data_in: schemas.Ownername): + await self.update_one(db, {'name': data_in.name}, {'$set': {'date': data_in.date}}) + + # 插入数据 + # async def create(self, db: AsyncIOMotorDatabase, data_in: schemas.ProjectnumberInsert): + # # await self.update_one(db, {'xiangmu': data_in.xiangmu}, {'$set': data_in.dict()}, upsert=True) + # await self.update_one(db, {data_in.game, data_in.ditch}, upsert=True) + # + + +basic_data = CRUDOwnername('basic_data') diff --git a/crud/crud_user.py b/crud/crud_user.py index 3dd2474..1bb1e64 100644 --- a/crud/crud_user.py +++ b/crud/crud_user.py @@ -21,31 +21,31 @@ class CRUDUser(CRUDBase): res = await db[self.coll_name].find_one({'name': name}) return res + # 获取用户信息 + async def get_user(self, db: AsyncIOMotorDatabase, user_id: str): + res = await db[self.coll_name].find_one({'user_id': user_id}) + return res + async def edit_profile(self, db: AsyncIOMotorDatabase, data_id: schemas.UserProfileEdit, user_id): if data_id.nickname: await self.update_one(db, {'_id': user_id}, {'$set': {'nickname': data_id.nickname}}) if data_id.tel: await self.update_one(db, {'_id': user_id}, {'$set': {'tel': data_id.tel}}) - async def update_login_time(self, db, name): - await self.update_one(db, {'name': name}, + async def update_login_time(self, db, id): + await self.update_one(db, {'id': id}, {'$set': {'last_login_ts': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}}) - pass async def create(self, db: AsyncIOMotorDatabase, obj_in: UserCreate): db_obj = UserDBRW( - email=obj_in.email, - hashed_password=get_password_hash(obj_in.password), - name=obj_in.name, - is_superuser=obj_in.is_superuser, - nickname=obj_in.nickname, + **obj_in.dict(), _id=get_uid() ) await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) - return db_obj.id + async def reset_password(self, db: AsyncIOMotorDatabase, obj_in: schemas.UserRestPassword): hashed_password = get_password_hash(obj_in.password) - await self.update_one(db, {'name': obj_in.username}, {'$set': {'hashed_password': hashed_password}}) + await self.update_one(db, {'user_id': obj_in.user_id}, {'$set': {'hashed_password': hashed_password}}) async def authenticate(self, db: AsyncIOMotorDatabase, name: str, password: str): user_obj = await self.get_by_user(db, name=name) @@ -63,14 +63,23 @@ class CRUDUser(CRUDBase): res = await self.find_many(db, *args, **kwargs) return schemas.Users(data=res) - async def get_all_users(self,db,where): + async def get_all_users(self, db, where): return await self.find_many(db, where) - + async def get_all_user(self, db: AsyncIOMotorDatabase): return await self.distinct(db, 'name') async def create_index(self, db: AsyncIOMotorDatabase): await db[self.coll_name].create_index('name', unique=True) + # 登录时获取用户的基本信息 + async def gets_user(self, db: AsyncIOMotorDatabase, user_id: str): + user_obj = await self.get_user(db, user_id=user_id) + user_obj = UserDBRW(**user_obj) + return user_obj + # 修改登录权限 + async def forbid_lojin(self, db: AsyncIOMotorDatabase, obj_in: schemas.Get_userid,state): + await self.update_one(db, {'user_id': obj_in.user_id}, {'$set': {'state': state}}) + user = CRUDUser('user') diff --git a/liwei_接口文档.md b/liwei_接口文档.md index c8cebcc..94a140d 100644 --- a/liwei_接口文档.md +++ b/liwei_接口文档.md @@ -252,4 +252,54 @@ api:/api/v1/itr/hint api:/api/v1/itr/up_hint 请求方式:post 参数: - hint_id: List[str] # 要修改已读的数据id \ No newline at end of file + hint_id: List[str] # 要修改已读的数据id + +#登录 +api:/api/v1/itr/login +请求方式:post +参数: + unionid: str # 通过钉钉扫码获取的unionid + +#修改其他人密码 +api:/api/v1/itr/reset_password +请求方式:post +参数: + user_id: str = ... + password: str = ... + +#修改自己的密码 +api:/api/v1/itr/reset_my_password +请求方式:post +参数: + password: str = ... + +#创建新账号 +api:/api/v1/itr/add_account +请求方式:post +参数: + unionid: str # 通过钉钉扫码获取的unionid + rank: int # 判断是hr还是面试官 + email: str # 邮箱 + tel: str # 手机号 + name: str # 名字 + +#禁止/解禁用户登录功能 +api:/api/v1/itr/forbid_login +请求方式:post +参数: + user_id: str # 钉钉的唯一标识 + type: int # 0为解禁1为禁止 + +#获取基本信息列表 +api:/api/v1/itr/owner_list +请求方式:post +参数: + name: str # 基本资料的名称 + +#新增,删除基本信息 +api:/api/v1/itr/owner_edit +请求方式:post +参数: + date: List[str] # 各种基本资料 + name: str # 基本资料的名称 现有渠道owner_name,工作经验job_exp, + 职能类别function_type,职位级别job_rank,学历要求education,部门sector \ No newline at end of file diff --git a/schemas/__init__.py b/schemas/__init__.py index 4d6ce63..3521ada 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -36,3 +36,4 @@ from .interview_record import * from .worker import * from .owner_info import * from .count_in_worker import * +from .basic_data import * diff --git a/schemas/basic_data.py b/schemas/basic_data.py new file mode 100644 index 0000000..582f530 --- /dev/null +++ b/schemas/basic_data.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel +from typing import List + + +class Ownername(BaseModel): + date: List[str] # 各种基本资料 + name: str # 基本资料的名称 + + +class Basic_data(BaseModel): + date: List[str] # 各种基本资料 + name: str # 基本资料的名称 + chinese: str # 中文名 + + +class Getdate(BaseModel): + name: str # 基本资料的名称 diff --git a/schemas/interview_plan.py b/schemas/interview_plan.py index 88d2f96..265e962 100644 --- a/schemas/interview_plan.py +++ b/schemas/interview_plan.py @@ -85,5 +85,23 @@ class Up_hint(BaseModel): hint_id: List[str] # 要修改已读的数据id type: str + class Get_hr(BaseModel): hr_name: str + + +class Login(BaseModel): + unionid: str # 通过钉钉扫码获取的unionid + + +class Createuser(BaseModel): + unionid: str # 通过钉钉扫码获取的unionid + rank: int # 判断是hr还是面试官 + email: str # 邮箱 + tel: str # 手机号 + name: str # 名字 + + +class Get_userid(BaseModel): + user_id: str # 钉钉的唯一标识 + type: int # 0为解禁1为禁止 diff --git a/schemas/user.py b/schemas/user.py index f8af986..4e629f2 100644 --- a/schemas/user.py +++ b/schemas/user.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Optional, List, Any from schemas.base import DBBase @@ -20,7 +21,7 @@ class UserProfileEdit(BaseModel): class User(UserBase): - name: str + user_id: str class Users(BaseModel): @@ -37,7 +38,7 @@ class UserLogin(BaseModel): class UserRestPassword(BaseModel): - username: str = ... + user_id: str = ... password: str = ... @@ -46,8 +47,13 @@ class UserRestMyPassword(BaseModel): class UserCreate(UserBase): - password: str - name: str + hashed_password: str + name: str # 名字 + unionid: str # 通过钉钉扫码获取的unionid + rank: int # 判断是hr还是面试官 + email: str # 邮箱 + tel: str # 手机号 + user_id: str # 钉钉的用户id # **************************************************************************** @@ -64,5 +70,14 @@ class UserDB(DBBase): data_where: dict = dict() -class UserDBRW(UserDB): - hashed_password: str +class UserDBRW(DBBase): + hashed_password: str # 密码 + unionid: str # 通过钉钉扫码获取的unionid + rank: int # 判断是hr还是面试官 + email: str # 邮箱 + tel: str # 手机号 + name: str # 名字 + last_login_ts: str = '尚未登录' # 登录时间 + create_date: datetime = datetime.now() # 创建账号的时间 + state: int = 0 # 默认刚创账号是不锁定的 + user_id:str # 钉钉里面的用户id diff --git a/utils/dingding.py b/utils/dingding.py index 2c85aac..2f2673a 100644 --- a/utils/dingding.py +++ b/utils/dingding.py @@ -1,17 +1,20 @@ import json +import pprint import redis import requests from core.config import Settings -Settings=Settings() -host=Settings.REDIS_CONF.get('host') -port=Settings.REDIS_CONF.get('port') -db=Settings.REDIS_CONF.get('db') -password=Settings.REDIS_CONF.get('password') -#redisdb = redis.Redis(host=host, port=port, db=db,password=password) + +Settings = Settings() +host = Settings.REDIS_CONF.get('host') +port = Settings.REDIS_CONF.get('port') +db = Settings.REDIS_CONF.get('db') +password = Settings.REDIS_CONF.get('password') +# redisdb = redis.Redis(host=host, port=port, db=db,password=password) redisdb = redis.Redis(host=host, port=port, db=db) + def get_token(): """ 获取钉钉token @@ -171,7 +174,7 @@ def get_all_uid(): def get_redis_alluid(): """ 获取redis中存的所有部门钉钉用户id,如没有重新获取再存数据库中,再获取 - :return: list + :return: list #包含有所在部门 """ redisuid = redisdb.get('user_id') if redisuid == None: @@ -179,10 +182,11 @@ def get_redis_alluid(): user_list = json.dumps(user_list) redisdb.set(name='user_id', value=user_list, ex=86400) # 存一天时间 redisuid = redisdb.get('user_id') - user=redisuid.decode() + user = redisuid.decode() return json.loads(user) -def send_dates(content,userid_list): + +def send_dates(content, userid_list): """ 发送消息至钉钉的通用模板 :param content: 需要发送的内容 @@ -209,6 +213,62 @@ def send_dates(content,userid_list): # 发送消息到钉钉 requests.post(url=url, params=query, data=json_data) + +def Unionid(unionid): + """ + 根据unionid获取用户userid + :param unionid: 企业账号范围内的唯一标识 + :return: 用户userid + """ + url = "https://oapi.dingtalk.com/topapi/user/getbyunionid" + query = { + 'access_token': get_redistoken() + } + data = {'unionid': unionid} + r = requests.post(url=url, params=query, data=data) + datas = r.json() + return datas.get('result').get('userid') + +def user_details(userid): + """ + 根据userid获取用户详情 + :param userid: 钉钉用户userid + :return: + """ + url = "https://oapi.dingtalk.com/topapi/v2/user/get" + query = { + 'access_token': get_redistoken() + } + data = {'userid': userid} + r = requests.post(url=url, params=query, data=data) + datas = r.json() + return datas +def get_user_list(): + """ + 获取所有部门钉钉用户id,list返回 + """ + user=get_redis_alluid() + user_list = [] + for i in user: + for ii in i.get('user_id'): + user_list.append(ii.get('userid')) + return user_list + +def get_alluid_list(): + """ + 获取redis中存的所有部门钉钉用户id,如没有重新获取再存数据库中,再获取 + :return: list #直接列表里面包含user_id,没有其他的 + """ + redisuid = redisdb.get('user_id_list') + if redisuid == None: + user_list = get_user_list() + user_list = json.dumps(user_list) + redisdb.set(name='user_id_list', value=user_list, ex=86400) # 存一天时间 + redisuid = redisdb.get('user_id_list') + user = redisuid.decode() + return json.loads(user) + if __name__ == '__main__': - a=get_redis_alluid() - print(a) \ No newline at end of file + #a = user_details('16371426094531014') + a=get_alluid_list() + pprint.pprint(a)