Skip to content

Commit 87aaaae

Browse files
committed
feat:新增用户管理批量导入功能
1 parent 399beca commit 87aaaae

File tree

9 files changed

+415
-20
lines changed

9 files changed

+415
-20
lines changed

dash-fastapi-backend/module_admin/controller/user_controller.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ async def change_system_user_profile_info(request: Request, edit_user: AddUserMo
153153

154154
@userController.patch("/user/profile/resetPwd", response_model=CrudUserResponse, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
155155
@log_decorator(title='个人信息', business_type=2)
156-
async def reset_system_user_password(request: Request, reset_user: ResetUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
156+
async def reset_system_user_password(request: Request, reset_user: ResetUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
157157
try:
158158
if not reset_user.user_id and reset_user.old_password:
159159
reset_user.user_id = current_user.user.user_id
@@ -172,6 +172,33 @@ async def reset_system_user_password(request: Request, reset_user: ResetUserMode
172172
return response_500(data="", message=str(e))
173173

174174

175+
@userController.post("/user/importData", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))])
176+
@log_decorator(title='用户管理', business_type=6)
177+
async def batch_import_system_user(request: Request, user_import: ImportUserModel, query_db: Session = Depends(get_db), current_user: CurrentUserInfoServiceResponse = Depends(get_current_user)):
178+
try:
179+
batch_import_result = UserService.batch_import_user_services(query_db, user_import, current_user)
180+
if batch_import_result.is_success:
181+
logger.info(batch_import_result.message)
182+
return response_200(data=batch_import_result, message=batch_import_result.message)
183+
else:
184+
logger.warning(batch_import_result.message)
185+
return response_400(data="", message=batch_import_result.message)
186+
except Exception as e:
187+
logger.exception(e)
188+
return response_500(data="", message=str(e))
189+
190+
191+
@userController.post("/user/importTemplate", dependencies=[Depends(CheckUserInterfaceAuth('system:user:import'))])
192+
async def export_system_user_template(request: Request, query_db: Session = Depends(get_db)):
193+
try:
194+
user_import_template_result = UserService.get_user_import_template_services()
195+
logger.info('获取成功')
196+
return streaming_response_200(data=bytes2file_response(user_import_template_result))
197+
except Exception as e:
198+
logger.exception(e)
199+
return response_500(data="", message=str(e))
200+
201+
175202
@userController.post("/user/export", dependencies=[Depends(CheckUserInterfaceAuth('system:user:export'))])
176203
@log_decorator(title='用户管理', business_type=5)
177204
async def export_system_user_list(request: Request, user_query: UserQueryModel, query_db: Session = Depends(get_db)):

dash-fastapi-backend/module_admin/entity/vo/user_vo.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ class DeleteUserModel(BaseModel):
213213
update_time: Optional[str]
214214

215215

216+
class ImportUserModel(BaseModel):
217+
"""
218+
批量导入用户模型
219+
"""
220+
url: str
221+
is_update: bool
222+
223+
216224
class CrudUserResponse(BaseModel):
217225
"""
218226
操作用户响应模型

dash-fastapi-backend/module_admin/service/user_service.py

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from module_admin.entity.vo.user_vo import *
22
from module_admin.dao.user_dao import *
33
from utils.pwd_util import *
4-
from utils.common_util import export_list2excel
4+
from utils.common_util import *
55

66

77
class UserService:
@@ -173,6 +173,98 @@ def reset_user_services(cls, result_db: Session, page_object: ResetUserModel):
173173

174174
return CrudUserResponse(**result)
175175

176+
@classmethod
177+
def batch_import_user_services(cls, result_db: Session, user_import: ImportUserModel, current_user: CurrentUserInfoServiceResponse):
178+
"""
179+
批量导入用户service
180+
:param user_import: 用户导入参数对象
181+
:param result_db: orm对象
182+
:param current_user: 当前用户对象
183+
:return: 批量导入用户结果
184+
"""
185+
header_dict = {
186+
"部门编号": "dept_id",
187+
"登录名称": "user_name",
188+
"用户名称": "nick_name",
189+
"用户邮箱": "email",
190+
"手机号码": "phonenumber",
191+
"用户性别": "sex",
192+
"帐号状态": "status"
193+
}
194+
filepath = get_filepath_from_url(user_import.url)
195+
df = pd.read_excel(filepath)
196+
df.rename(columns=header_dict, inplace=True)
197+
add_error_result = []
198+
count = 0
199+
try:
200+
for index, row in df.iterrows():
201+
count = count + 1
202+
if row['sex'] == '男':
203+
row['sex'] = '0'
204+
if row['sex'] == '女':
205+
row['sex'] = '1'
206+
if row['sex'] == '未知':
207+
row['sex'] = '2'
208+
if row['status'] == '正常':
209+
row['status'] = '0'
210+
if row['status'] == '停用':
211+
row['status'] = '1'
212+
add_user = UserModel(
213+
**dict(
214+
dept_id=row['dept_id'],
215+
user_name=row['user_name'],
216+
password=PwdUtil.get_password_hash('123456'),
217+
nick_name=row['nick_name'],
218+
email=row['email'],
219+
phonenumber=row['phonenumber'],
220+
sex=row['sex'],
221+
status=row['status'],
222+
create_by=current_user.user.user_name,
223+
update_by=current_user.user.user_name
224+
)
225+
)
226+
user_info = UserDao.get_user_by_info(result_db, UserModel(**dict(user_name=row['user_name'])))
227+
if user_info:
228+
if user_import.is_update:
229+
edit_user = UserModel(
230+
**dict(
231+
user_id=user_info.user_id,
232+
dept_id=row['dept_id'],
233+
user_name=row['user_name'],
234+
nick_name=row['nick_name'],
235+
email=row['email'],
236+
phonenumber=row['phonenumber'],
237+
sex=row['sex'],
238+
status=row['status'],
239+
update_by=current_user.user.user_name
240+
)
241+
).dict(exclude_unset=True)
242+
UserDao.edit_user_dao(result_db, edit_user)
243+
else:
244+
add_error_result.append(f"{count}.用户账号{row['user_name']}已存在")
245+
else:
246+
UserDao.add_user_dao(result_db, add_user)
247+
result_db.commit()
248+
result = dict(is_success=True, message='\n'.join(add_error_result))
249+
except Exception as e:
250+
result_db.rollback()
251+
result = dict(is_success=False, message=str(e))
252+
253+
return CrudUserResponse(**result)
254+
255+
@staticmethod
256+
def get_user_import_template_services():
257+
"""
258+
获取用户导入模板service
259+
:return: 用户导入模板excel的二进制数据
260+
"""
261+
header_list = ["部门编号", "登录名称", "用户名称", "用户邮箱", "手机号码", "用户性别", "帐号状态"]
262+
selector_header_list = ["用户性别", "帐号状态"]
263+
option_list = [{"用户性别": ["男", "女", "未知"]}, {"帐号状态": ["正常", "停用"]}]
264+
binary_data = get_excel_template(header_list=header_list, selector_header_list=selector_header_list, option_list=option_list)
265+
266+
return binary_data
267+
176268
@staticmethod
177269
def export_user_list_services(user_list: List):
178270
"""

dash-fastapi-backend/utils/common_util.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import pandas as pd
22
import io
3+
import os
4+
from openpyxl import Workbook
5+
from openpyxl.styles import Alignment, PatternFill
6+
from openpyxl.utils import get_column_letter
7+
from openpyxl.worksheet.datavalidation import DataValidation
8+
from config.env import CachePathConfig
39

410

511
def worship():
@@ -65,3 +71,76 @@ def export_list2excel(list_data: list):
6571
binary_data = binary_data.getvalue()
6672

6773
return binary_data
74+
75+
76+
def get_excel_template(header_list: list, selector_header_list: list, option_list: list[dict]):
77+
"""
78+
工具方法:将需要导出的list数据转化为对应excel的二进制数据
79+
:param header_list: 表头数据列表
80+
:param selector_header_list: 需要设置为选择器格式的表头数据列表
81+
:param option_list: 选择器格式的表头预设的选项列表
82+
:return: 模板excel的二进制数据
83+
"""
84+
# 创建Excel工作簿
85+
wb = Workbook()
86+
# 选择默认的活动工作表
87+
ws = wb.active
88+
89+
# 设置表头文字
90+
headers = header_list
91+
92+
# 设置表头背景样式为灰色,前景色为白色
93+
header_fill = PatternFill(start_color="ababab", end_color="ababab", fill_type="solid")
94+
95+
# 将表头写入第一行
96+
for col_num, header in enumerate(headers, 1):
97+
cell = ws.cell(row=1, column=col_num)
98+
cell.value = header
99+
cell.fill = header_fill
100+
# 设置列宽度为16
101+
ws.column_dimensions[chr(64 + col_num)].width = 12
102+
# 设置水平居中对齐
103+
cell.alignment = Alignment(horizontal='center')
104+
105+
# 设置选择器的预设选项
106+
options = option_list
107+
108+
# 获取selector_header的字母索引
109+
for selector_header in selector_header_list:
110+
column_selector_header_index = headers.index(selector_header) + 1
111+
112+
# 创建数据有效性规则
113+
header_option = []
114+
for option in options:
115+
if option.get(selector_header):
116+
header_option = option.get(selector_header)
117+
dv = DataValidation(type="list", formula1=f'"{",".join(header_option)}"')
118+
# 设置数据有效性规则的起始单元格和结束单元格
119+
dv.add(
120+
f'{get_column_letter(column_selector_header_index)}2:{get_column_letter(column_selector_header_index)}1048576')
121+
# 添加数据有效性规则到工作表
122+
ws.add_data_validation(dv)
123+
124+
# 保存Excel文件为字节类型的数据
125+
file = io.BytesIO()
126+
wb.save(file)
127+
file.seek(0)
128+
129+
# 读取字节数据
130+
excel_data = file.getvalue()
131+
132+
return excel_data
133+
134+
135+
def get_filepath_from_url(url: str):
136+
"""
137+
工具方法:根据请求参数获取文件路径
138+
:param url: 请求参数中的url参数
139+
:return: 文件路径
140+
"""
141+
file_info = url.split("?")[1].split("&")
142+
task_id = file_info[0].split("=")[1]
143+
file_name = file_info[1].split("=")[1]
144+
filepath = os.path.join(CachePathConfig.PATH, task_id, file_name)
145+
146+
return filepath

dash-fastapi-frontend/api/user.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ def reset_user_password_api(page_obj: dict):
4646
return api_request(method='patch', url='/system/user/profile/resetPwd', is_headers=True, json=page_obj)
4747

4848

49+
def batch_import_user_api(page_obj: dict):
50+
51+
return api_request(method='post', url='/system/user/importData', is_headers=True, json=page_obj)
52+
53+
54+
def download_user_import_template_api():
55+
56+
return api_request(method='post', url='/system/user/importTemplate', is_headers=True, stream=True)
57+
58+
4959
def export_user_list_api(page_obj: dict):
5060

5161
return api_request(method='post', url='/system/user/export', is_headers=True, json=page_obj, stream=True)

dash-fastapi-frontend/callbacks/login_c.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def login_auth(nClicks, username, password, input_captcha, session_id, image_cli
5252
None,
5353
None,
5454
dash.no_update,
55-
True,
55+
False,
5656
token,
5757
dcc.Location(
5858
pathname='/',

0 commit comments

Comments
 (0)