通讯录管理
通讯录管理模块提供企业联系人信息的查询、搜索、统计等功能,支持公开访问和管理员操作。
接口概览
公开接口(无需认证)
| 方法 | 路径 | 描述 | 权限要求 |
|---|---|---|---|
| GET | /api/v1/public/contacts | 获取公开通讯录列表 | 无 |
| GET | /api/v1/public/contacts/{id} | 获取公开通讯录详情 | 无 |
| GET | /api/v1/public/contacts/search | 搜索通讯录 | 无 |
| GET | /api/v1/public/contacts/stats | 获取通讯录统计 | 无 |
| GET | /api/v1/public/departments | 获取部门列表 | 无 |
管理员接口
| 方法 | 路径 | 描述 | 权限要求 |
|---|---|---|---|
| POST | /api/v1/admin/contacts | 创建通讯录记录 | 管理员 |
| PUT | /api/v1/admin/contacts/{id} | 更新通讯录记录 | 管理员 |
| DELETE | /api/v1/admin/contacts/{id} | 删除通讯录记录 | 管理员 |
| POST | /api/v1/admin/contacts/sync/{employee_id} | 同步员工信息到通讯录 | 管理员 |
| POST | /api/v1/admin/contacts/batch-sync | 批量同步员工信息 | 管理员 |
获取公开通讯录列表
获取公开的企业通讯录列表,支持分页和筛选。
接口信息
- URL:
/api/v1/public/contacts - 方法:
GET - 认证: 无需认证
查询参数
| 参数 | 类型 | 必填 | 描述 | 默认值 |
|---|---|---|---|---|
| page | int | 否 | 页码 | 1 |
| page_size | int | 否 | 每页数量 | 20 |
| department | string | 否 | 部门筛选 | - |
| search | string | 否 | 搜索关键词(姓名、职位) | - |
| is_public | bool | 否 | 是否公开(默认公开) | true |
请求示例
bash
GET /api/v1/public/contacts?page=1&page_size=20&department=技术部&search=张响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取成功",
"data": {
"contacts": [
{
"id": 1,
"name": "张三",
"position": "技术总监",
"department": "技术部",
"email": "zhangsan@company.com",
"phone": "13800138000",
"mobile": "13900139000",
"avatar": "https://example.com/avatar.jpg",
"office": "A座 1001室",
"is_public": true,
"created_at": "2024-01-01T12:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"pages": 1
}
},
"timestamp": "2024-01-01T12:00:00Z"
}获取公开通讯录详情
获取指定联系人的详细信息。
接口信息
- URL:
/api/v1/public/contacts/{id} - 方法:
GET - 认证: 无需认证
路径参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| id | int | 是 | 联系人ID | 1 |
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取成功",
"data": {
"id": 1,
"name": "张三",
"position": "技术总监",
"department": "技术部",
"email": "zhangsan@company.com",
"phone": "13800138000",
"mobile": "13900139000",
"fax": "010-12345678",
"avatar": "https://example.com/avatar.jpg",
"office": "A座 1001室",
"address": "北京市朝阳区xxx街道xxx号",
"is_public": true,
"employee_id": 10,
"company": {
"id": 1,
"name": "源丰科技有限公司",
"logo": "https://example.com/company-logo.png"
},
"created_at": "2024-01-01T12:00:00Z",
"updated_at": "2024-01-01T12:00:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}搜索通讯录
在通讯录中搜索联系人。
接口信息
- URL:
/api/v1/public/contacts/search - 方法:
GET - 认证: 无需认证
查询参数
| 参数 | 类型 | 必填 | 描述 | 默认值 |
|---|---|---|---|---|
| q | string | 是 | 搜索关键词 | - |
| page | int | 否 | 页码 | 1 |
| page_size | int | 否 | 每页数量 | 20 |
| department | string | 否 | 部门筛选 | - |
请求示例
bash
GET /api/v1/public/contacts/search?q=技术总监&department=技术部响应示例
成功响应 (200)
json
{
"code": 200,
"message": "搜索成功",
"data": {
"contacts": [
{
"id": 1,
"name": "张三",
"position": "技术总监",
"department": "技术部",
"email": "zhangsan@company.com",
"phone": "13800138000",
"is_public": true
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 1,
"pages": 1
},
"search_query": "技术总监"
},
"timestamp": "2024-01-01T12:00:00Z"
}获取通讯录统计
获取通讯录的统计信息。
接口信息
- URL:
/api/v1/public/contacts/stats - 方法:
GET - 认证: 无需认证
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取成功",
"data": {
"total_contacts": 150,
"public_contacts": 120,
"private_contacts": 30,
"by_department": [
{
"department": "技术部",
"count": 45
},
{
"department": "销售部",
"count": 35
},
{
"department": "市场部",
"count": 25
}
],
"by_position": [
{
"position": "工程师",
"count": 60
},
{
"position": "经理",
"count": 25
}
]
},
"timestamp": "2024-01-01T12:00:00Z"
}获取部门列表
获取所有部门列表。
接口信息
- URL:
/api/v1/public/departments - 方法:
GET - 认证: 无需认证
响应示例
成功响应 (200)
json
{
"code": 200,
"message": "获取成功",
"data": {
"departments": [
{
"id": 1,
"name": "技术部",
"description": "负责技术研发和系统维护",
"employee_count": 45
},
{
"id": 2,
"name": "销售部",
"description": "负责产品销售和客户关系",
"employee_count": 35
},
{
"id": 3,
"name": "市场部",
"description": "负责市场推广和品牌建设",
"employee_count": 25
}
]
},
"timestamp": "2024-01-01T12:00:00Z"
}创建通讯录记录(管理员)
管理员创建新的通讯录记录。
接口信息
- URL:
/api/v1/admin/contacts - 方法:
POST - 内容类型:
application/json - 认证: 需要管理员权限
请求参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| name | string | 是 | 联系人姓名 | "张三" |
| position | string | 是 | 职位 | "技术总监" |
| department | string | 是 | 部门 | "技术部" |
| string | 否 | 邮箱地址 | "zhangsan@company.com" | |
| phone | string | 否 | 办公电话 | "010-12345678" |
| mobile | string | 否 | 手机号码 | "13800138000" |
| fax | string | 否 | 传真号码 | "010-87654321" |
| office | string | 否 | 办公室 | "A座 1001室" |
| address | string | 否 | 办公地址 | "北京市朝阳区xxx街道xxx号" |
| is_public | bool | 否 | 是否公开 | true |
| employee_id | int | 否 | 关联员工ID | 10 |
请求示例
json
{
"name": "张三",
"position": "技术总监",
"department": "技术部",
"email": "zhangsan@company.com",
"phone": "010-12345678",
"mobile": "13800138000",
"office": "A座 1001室",
"is_public": true,
"employee_id": 10
}响应示例
成功响应 (201)
json
{
"code": 201,
"message": "创建成功",
"data": {
"id": 1,
"name": "张三",
"position": "技术总监",
"department": "技术部",
"email": "zhangsan@company.com",
"phone": "010-12345678",
"mobile": "13800138000",
"office": "A座 1001室",
"is_public": true,
"employee_id": 10,
"created_at": "2024-01-01T12:00:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}同步员工信息到通讯录(管理员)
将员工信息同步到通讯录。
接口信息
- URL:
/api/v1/admin/contacts/sync/{employee_id} - 方法:
POST - 认证: 需要管理员权限
路径参数
| 参数 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| employee_id | int | 是 | 员工ID | 10 |
请求参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| is_public | bool | 否 | 是否公开 | true |
| override_existing | bool | 否 | 是否覆盖已存在的记录 | false |
请求示例
json
{
"is_public": true,
"override_existing": false
}响应示例
成功响应 (200)
json
{
"code": 200,
"message": "同步成功",
"data": {
"contact_id": 1,
"employee_id": 10,
"synced_fields": ["name", "position", "department", "email", "phone"],
"created": true
},
"timestamp": "2024-01-01T12:00:00Z"
}批量同步员工信息(管理员)
批量将多个员工信息同步到通讯录。
接口信息
- URL:
/api/v1/admin/contacts/batch-sync - 方法:
POST - 内容类型:
application/json - 认证: 需要管理员权限
请求参数
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
| employee_ids | int[] | 是 | 员工ID列表 | [10, 11, 12] |
| is_public | bool | 否 | 是否公开 | true |
| override_existing | bool | 否 | 是否覆盖已存在的记录 | false |
请求示例
json
{
"employee_ids": [10, 11, 12],
"is_public": true,
"override_existing": false
}响应示例
成功响应 (200)
json
{
"code": 200,
"message": "批量同步完成",
"data": {
"total": 3,
"success": 3,
"failed": 0,
"results": [
{
"employee_id": 10,
"contact_id": 1,
"status": "success",
"action": "created"
},
{
"employee_id": 11,
"contact_id": 2,
"status": "success",
"action": "created"
},
{
"employee_id": 12,
"contact_id": 3,
"status": "success",
"action": "created"
}
]
},
"timestamp": "2024-01-01T12:00:00Z"
}数据模型
ContactResponse
typescript
interface ContactResponse {
id: number; // 联系人ID
name: string; // 姓名
position: string; // 职位
department: string; // 部门
email?: string; // 邮箱地址
phone?: string; // 办公电话
mobile?: string; // 手机号码
fax?: string; // 传真号码
avatar?: string; // 头像URL
office?: string; // 办公室
address?: string; // 办公地址
is_public: boolean; // 是否公开
employee_id?: number; // 关联员工ID
company?: CompanyInfo; // 公司信息
created_at: string; // 创建时间
updated_at: string; // 更新时间
}DepartmentResponse
typescript
interface DepartmentResponse {
id: number; // 部门ID
name: string; // 部门名称
description?: string; // 部门描述
employee_count: number; // 员工数量
}ContactStats
typescript
interface ContactStats {
total_contacts: number; // 总联系人数
public_contacts: number; // 公开联系人数
private_contacts: number; // 私有联系人数
by_department: Array<{ // 按部门统计
department: string;
count: number;
}>;
by_position: Array<{ // 按职位统计
position: string;
count: number;
}>;
}错误代码
| 错误代码 | 描述 | 解决方案 |
|---|---|---|
| 400 | 请求参数错误 | 检查请求参数格式和必填字段 |
| 401 | 未认证 | 检查 JWT token 是否有效 |
| 403 | 无权限 | 确认用户具有管理员权限 |
| 404 | 联系人不存在 | 检查联系人ID是否正确 |
| 409 | 联系人已存在 | 检查是否重复创建 |
| 500 | 服务器内部错误 | 联系系统管理员 |
集成示例
JavaScript 联系人搜索组件
javascript
import React, { useState, useEffect } from 'react';
const ContactSearch = () => {
const [contacts, setContacts] = useState([]);
const [loading, setLoading] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [selectedDepartment, setSelectedDepartment] = useState('');
const searchContacts = async (query = '', department = '') => {
setLoading(true);
try {
const params = new URLSearchParams();
if (query) params.append('q', query);
if (department) params.append('department', department);
const response = await fetch(`/api/v1/public/contacts/search?${params}`);
const data = await response.json();
if (data.code === 200) {
setContacts(data.data.contacts);
}
} catch (error) {
console.error('搜索失败:', error);
} finally {
setLoading(false);
}
};
const getDepartments = async () => {
try {
const response = await fetch('/api/v1/public/departments');
const data = await response.json();
return data.data.departments;
} catch (error) {
console.error('获取部门列表失败:', error);
return [];
}
};
useEffect(() => {
searchContacts();
}, []);
const handleSearch = () => {
searchContacts(searchQuery, selectedDepartment);
};
return (
<div className="contact-search">
<h2>通讯录搜索</h2>
<div className="search-form">
<input
type="text"
placeholder="搜索联系人..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<select
value={selectedDepartment}
onChange={(e) => setSelectedDepartment(e.target.value)}
>
<option value="">所有部门</option>
{/* 部门选项 */}
</select>
<button onClick={handleSearch} disabled={loading}>
{loading ? '搜索中...' : '搜索'}
</button>
</div>
<div className="contact-list">
{contacts.map(contact => (
<div key={contact.id} className="contact-item">
<div className="contact-avatar">
{contact.avatar ? (
<img src={contact.avatar} alt={contact.name} />
) : (
<div className="avatar-placeholder">
{contact.name.charAt(0)}
</div>
)}
</div>
<div className="contact-info">
<h3>{contact.name}</h3>
<p className="position">{contact.position}</p>
<p className="department">{contact.department}</p>
<div className="contact-details">
{contact.email && (
<div className="detail-item">
📧 {contact.email}
</div>
)}
{contact.phone && (
<div className="detail-item">
📞 {contact.phone}
</div>
)}
{contact.mobile && (
<div className="detail-item">
📱 {contact.mobile}
</div>
)}
{contact.office && (
<div className="detail-item">
🏢 {contact.office}
</div>
)}
</div>
</div>
</div>
))}
</div>
</div>
);
};
export default ContactSearch;Python 联系人管理脚本
python
import requests
import csv
from typing import List, Optional, Dict, Any
class ContactManager:
def __init__(self, base_url: str):
self.base_url = base_url
def search_contacts(self, query: str, department: Optional[str] = None,
page: int = 1, page_size: int = 20) -> Dict[str, Any]:
"""搜索联系人"""
params = {
'q': query,
'page': page,
'page_size': page_size
}
if department:
params['department'] = department
response = requests.get(
f'{self.base_url}/api/v1/public/contacts/search',
params=params
)
response.raise_for_status()
return response.json()
def get_contact_stats(self) -> Dict[str, Any]:
"""获取通讯录统计"""
response = requests.get(f'{self.base_url}/api/v1/public/contacts/stats')
response.raise_for_status()
return response.json()
def get_departments(self) -> List[Dict[str, Any]]:
"""获取部门列表"""
response = requests.get(f'{self.base_url}/api/v1/public/departments')
response.raise_for_status()
return response.json()['data']['departments']
def export_contacts_to_csv(self, filename: str, department: Optional[str] = None):
"""导出联系人到CSV文件"""
all_contacts = []
page = 1
while True:
data = self.search_contacts('', department, page, 100)
contacts = data['data']['contacts']
if not contacts:
break
all_contacts.extend(contacts)
page += 1
# 写入CSV文件
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
if not all_contacts:
print("没有找到联系人数据")
return
fieldnames = ['name', 'position', 'department', 'email', 'phone', 'mobile', 'office']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for contact in all_contacts:
writer.writerow({
'name': contact['name'],
'position': contact['position'],
'department': contact['department'],
'email': contact.get('email', ''),
'phone': contact.get('phone', ''),
'mobile': contact.get('mobile', ''),
'office': contact.get('office', '')
})
print(f"已导出 {len(all_contacts)} 个联系人到 {filename}")
# 使用示例
def main():
contact_manager = ContactManager('http://localhost:8080')
# 获取统计信息
stats = contact_manager.get_contact_stats()
print(f"总联系人数: {stats['data']['total_contacts']}")
print(f"公开联系人: {stats['data']['public_contacts']}")
# 搜索技术部的联系人
tech_contacts = contact_manager.search_contacts('', '技术部')
print(f"技术部联系人: {len(tech_contacts['data']['contacts'])}")
# 导出联系人到CSV
contact_manager.export_contacts_to_csv('contacts.csv')
if __name__ == '__main__':
main()