diff --git a/backend/app/api/v1/system_settings.py b/backend/app/api/v1/system_settings.py index 7abd302..870771b 100644 --- a/backend/app/api/v1/system_settings.py +++ b/backend/app/api/v1/system_settings.py @@ -41,10 +41,7 @@ class DingtalkConfigResponse(BaseModel): class EmployeeSyncConfigUpdate(BaseModel): - """员工同步配置更新请求(钉钉 API 方式)""" - corp_id: Optional[str] = Field(None, description="钉钉企业 CorpId") - client_id: Optional[str] = Field(None, description="应用 ClientId (AppKey)") - client_secret: Optional[str] = Field(None, description="应用 ClientSecret (AppSecret)") + """员工同步配置更新请求(复用钉钉免密登录配置)""" enabled: Optional[bool] = Field(None, description="是否启用自动同步") @@ -291,7 +288,7 @@ async def get_employee_sync_config( db: AsyncSession = Depends(get_db), ) -> ResponseModel: """ - 获取员工同步配置(钉钉 API 方式) + 获取员工同步配置(复用钉钉免密登录配置) 仅限管理员访问 """ @@ -299,31 +296,22 @@ async def get_employee_sync_config( tenant_id = await get_or_create_tenant_id(db) - # 从数据库获取钉钉 API 配置 - corp_id = await get_system_config(db, tenant_id, 'employee_sync', 'CORP_ID') - client_id = await get_system_config(db, tenant_id, 'employee_sync', 'CLIENT_ID') - client_secret = await get_system_config(db, tenant_id, 'employee_sync', 'CLIENT_SECRET') + # 从 dingtalk 配置组读取(与免密登录共用) + corp_id = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_CORP_ID') + app_key = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_APP_KEY') + app_secret = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_APP_SECRET') enabled = await get_feature_switch(db, tenant_id, 'employee_sync') + dingtalk_enabled = await get_feature_switch(db, tenant_id, 'dingtalk_login') - # 脱敏处理 client_secret - client_secret_masked = None - if client_secret: - if len(client_secret) > 8: - client_secret_masked = client_secret[:4] + '****' + client_secret[-4:] - else: - client_secret_masked = '****' - - # 检查配置是否完整 - configured = bool(corp_id and client_id and client_secret) + # 检查钉钉配置是否完整 + configured = bool(corp_id and app_key and app_secret) return ResponseModel( message="获取成功", data={ - "corp_id": corp_id, - "client_id": client_id, - "client_secret_masked": client_secret_masked, "enabled": enabled, "configured": configured, + "dingtalk_enabled": dingtalk_enabled, # 免密登录是否启用 } ) @@ -335,7 +323,7 @@ async def update_employee_sync_config( db: AsyncSession = Depends(get_db), ) -> ResponseModel: """ - 更新员工同步配置(钉钉 API 方式) + 更新员工同步配置(仅开关,API 凭证复用钉钉免密登录) 仅限管理员访问 """ @@ -344,15 +332,6 @@ async def update_employee_sync_config( tenant_id = await get_or_create_tenant_id(db) try: - if config.corp_id is not None: - await set_system_config(db, tenant_id, 'employee_sync', 'CORP_ID', config.corp_id) - - if config.client_id is not None: - await set_system_config(db, tenant_id, 'employee_sync', 'CLIENT_ID', config.client_id) - - if config.client_secret is not None: - await set_system_config(db, tenant_id, 'employee_sync', 'CLIENT_SECRET', config.client_secret) - if config.enabled is not None: await set_feature_switch(db, tenant_id, 'employee_sync', config.enabled) @@ -381,7 +360,7 @@ async def test_employee_sync_connection( db: AsyncSession = Depends(get_db), ) -> ResponseModel: """ - 测试钉钉 API 连接 + 测试钉钉 API 连接(复用免密登录配置) 仅限管理员访问 """ @@ -389,15 +368,15 @@ async def test_employee_sync_connection( tenant_id = await get_or_create_tenant_id(db) - # 获取钉钉配置 - corp_id = await get_system_config(db, tenant_id, 'employee_sync', 'CORP_ID') - client_id = await get_system_config(db, tenant_id, 'employee_sync', 'CLIENT_ID') - client_secret = await get_system_config(db, tenant_id, 'employee_sync', 'CLIENT_SECRET') + # 从 dingtalk 配置组读取(与免密登录共用) + corp_id = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_CORP_ID') + client_id = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_APP_KEY') + client_secret = await get_system_config(db, tenant_id, 'dingtalk', 'DINGTALK_APP_SECRET') if not all([corp_id, client_id, client_secret]): return ResponseModel( code=400, - message="钉钉 API 配置不完整,请先填写 CorpId、ClientId、ClientSecret" + message="请先在「钉钉免密登录」页签配置 CorpId、AppKey、AppSecret" ) try: diff --git a/backend/app/services/employee_sync_service.py b/backend/app/services/employee_sync_service.py index c72c5a5..8d6109a 100644 --- a/backend/app/services/employee_sync_service.py +++ b/backend/app/services/employee_sync_service.py @@ -29,17 +29,18 @@ class EmployeeSyncService: self._dingtalk_config = None async def _get_dingtalk_config(self) -> Dict[str, str]: - """从数据库获取钉钉 API 配置""" + """从数据库获取钉钉 API 配置(复用免密登录配置)""" if self._dingtalk_config: return self._dingtalk_config try: + # 从 dingtalk 配置组读取(与免密登录共用) result = await self.db.execute( text(""" SELECT config_key, config_value FROM tenant_configs WHERE tenant_id = :tenant_id - AND config_group = 'employee_sync' + AND config_group = 'dingtalk' """), {"tenant_id": self.tenant_id} ) @@ -47,7 +48,13 @@ class EmployeeSyncService: config = {} for key, value in rows: - config[key] = value + # 转换 key 名称以匹配 DingTalkService 需要的格式 + if key == 'DINGTALK_CORP_ID': + config['CORP_ID'] = value + elif key == 'DINGTALK_APP_KEY': + config['CLIENT_ID'] = value + elif key == 'DINGTALK_APP_SECRET': + config['CLIENT_SECRET'] = value self._dingtalk_config = config return config diff --git a/frontend/src/views/admin/system-settings.vue b/frontend/src/views/admin/system-settings.vue index ff0d65b..6f7f595 100644 --- a/frontend/src/views/admin/system-settings.vue +++ b/frontend/src/views/admin/system-settings.vue @@ -105,13 +105,13 @@ @@ -124,50 +124,20 @@ 启用后将每日自动从钉钉同步员工数据 - 钉钉应用配置 - - - - 在钉钉管理后台-设置中查看 - - - - - 钉钉开发者后台-应用凭证 - - - - - - 当前值: {{ syncForm.client_secret_masked }} - - - - + {{ syncForm.configured ? '已配置' : '未配置' }} + + 请先在「钉钉免密登录」页签配置 API 凭证 + 保存配置 - + 测试连接 @@ -206,14 +176,10 @@ const dingtalkForm = reactive({ corp_id: '', }) -// 员工同步配置表单(钉钉 API 方式) +// 员工同步配置表单(复用钉钉免密登录配置) const syncForm = reactive({ enabled: false, - corp_id: '', - client_id: '', - client_secret: '', - client_secret_masked: '', - configured: false, + configured: false, // 钉钉 API 是否已配置 }) // 表单验证规则 @@ -229,14 +195,7 @@ const dingtalkRules = reactive({ ] }) -const syncRules = reactive({ - corp_id: [ - { required: false, message: '请输入企业 CorpId', trigger: 'blur' } - ], - client_id: [ - { required: false, message: '请输入应用 ClientId', trigger: 'blur' } - ] -}) +const syncRules = reactive({}) /** * 加载钉钉配置 @@ -308,7 +267,7 @@ const saveDingtalkConfig = async () => { } /** - * 加载员工同步配置(钉钉 API 方式) + * 加载员工同步配置(复用钉钉免密登录配置) */ const loadSyncConfig = async () => { syncLoading.value = true @@ -316,10 +275,6 @@ const loadSyncConfig = async () => { const response = await request.get('/api/v1/settings/employee-sync') if (response.code === 200 && response.data) { syncForm.enabled = response.data.enabled || false - syncForm.corp_id = response.data.corp_id || '' - syncForm.client_id = response.data.client_id || '' - syncForm.client_secret = '' - syncForm.client_secret_masked = response.data.client_secret_masked || '' syncForm.configured = response.data.configured || false } } catch (error: any) { @@ -330,23 +285,14 @@ const loadSyncConfig = async () => { } /** - * 保存员工同步配置(钉钉 API 方式) + * 保存员工同步配置(仅开关) */ const saveSyncConfig = async () => { syncSaving.value = true try { - const updateData: any = { + const response = await request.put('/api/v1/settings/employee-sync', { enabled: syncForm.enabled, - corp_id: syncForm.corp_id, - client_id: syncForm.client_id, - } - - // 只有输入了新密码才传递 - if (syncForm.client_secret) { - updateData.client_secret = syncForm.client_secret - } - - const response = await request.put('/api/v1/settings/employee-sync', updateData) + }) if (response.code === 200) { ElMessage.success('配置保存成功') await loadSyncConfig()