JAVA办公自动化系统
一、系统概述
本办公自动化系统基于Java EE平台开发,实现了企业日常办公的数字化管理。系统包含文档管理、流程审批、会议管理、日程安排、通讯录等核心功能模块,采用B/S架构设计,支持多用户协同工作。系统使用Spring Boot框架简化开发流程,结合MyBatis实现数据持久化,前端采用Vue.js构建现代交互界面,确保系统具有良好的可扩展性和用户体验。
二、系统架构设计
1. 技术选型
- 前端:Vue.js、Element UI
- 后端:Spring Boot、Spring Security、MyBatis
- 数据库:MySQL
- 开发工具:IntelliJ IDEA
- 部署环境:Docker、Nginx
2. 系统架构
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── officeautomation
│ │ │ ├── controller (控制器层)
│ │ │ ├── service (服务层)
│ │ │ ├── mapper (数据访问层)
│ │ │ ├── entity (实体类)
│ │ │ ├── dto (数据传输对象)
│ │ │ ├── config (配置类)
│ │ │ └── utils (工具类)
│ │ ├── resources
│ │ │ ├── mapper (MyBatis映射文件)
│ │ │ ├── application.yml (配置文件)
│ │ │ └── static (静态资源)
│ │ └── webapp
│ │ └── WEB-INF
三、核心代码实现
1. 数据模型设计
java
// User.java
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
private String realName;
private String email;
private String phone;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableField(exist = false)
private List<Role> roles;
}
// Document.java
@Data
@TableName("oa_document")
public class Document {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "文档标题不能为空")
private String title;
private String content;
private String filePath;
private String fileType;
private Long fileSize;
private Long creatorId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer status;
private String keywords;
@TableField(exist = false)
private User creator;
}
// ApprovalProcess.java
@Data
@TableName("oa_approval_process")
public class ApprovalProcess {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "流程名称不能为空")
private String processName;
private String processKey;
private Integer status;
private String description;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@TableField(exist = false)
private List<ApprovalNode> nodes;
}
// Meeting.java
@Data
@TableName("oa_meeting")
public class Meeting {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "会议主题不能为空")
private String title;
private String content;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String location;
private Long organizerId;
private Integer status;
private LocalDateTime createTime;
@TableField(exist = false)
private User organizer;
@TableField(exist = false)
private List<User> participants;
}
2. 数据库连接配置
java
// MyBatisConfig.java
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
// 设置MyBatis配置
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(true);
factoryBean.setConfiguration(configuration);
// 设置TypeAliases包
factoryBean.setTypeAliasesPackage("com.officeautomation.entity");
// 设置MapperLocations
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
return factoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3. 服务层实现
java
// UserServiceImpl.java
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Override
public User getUserById(Long id) {
User user = userMapper.selectById(id);
if (user != null) {
List<Role> roles = roleMapper.getRolesByUserId(id);
user.setRoles(roles);
}
return user;
}
@Override
public User getUserByUsername(String username) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
User user = userMapper.selectOne(wrapper);
if (user != null) {
List<Role> roles = roleMapper.getRolesByUserId(user.getId());
user.setRoles(roles);
}
return user;
}
@Override
public List<User> getAllUsers() {
List<User> userList = userMapper.selectList(null);
for (User user : userList) {
List<Role> roles = roleMapper.getRolesByUserId(user.getId());
user.setRoles(roles);
}
return userList;
}
@Override
public boolean saveUser(User user) {
if (user.getId() == null) {
// 新增用户
user.setCreateTime(LocalDateTime.now());
user.setUpdateTime(LocalDateTime.now());
user.setStatus(1);
// 加密密码
String password = user.getPassword();
user.setPassword(PasswordEncoderUtil.encode(password));
int result = userMapper.insert(user);
return result > 0;
} else {
// 更新用户
user.setUpdateTime(LocalDateTime.now());
// 如果密码不为空,则更新密码
if (!StringUtils.isEmpty(user.getPassword())) {
user.setPassword(PasswordEncoderUtil.encode(user.getPassword()));
} else {
// 不更新密码,移除该字段
user.setPassword(null);
}
int result = userMapper.updateById(user);
return result > 0;
}
}
@Override
public boolean deleteUser(Long id) {
int result = userMapper.deleteById(id);
// 删除用户角色关联
roleMapper.deleteUserRoleByUserId(id);
return result > 0;
}
}
// DocumentServiceImpl.java
@Service
@Transactional
public class DocumentServiceImpl implements DocumentService {
@Autowired
private DocumentMapper documentMapper;
@Autowired
private UserService userService;
@Override
public Document getDocumentById(Long id) {
Document document = documentMapper.selectById(id);
if (document != null) {
User creator = userService.getUserById(document.getCreatorId());
document.setCreator(creator);
}
return document;
}
@Override
public List<Document> getDocumentsByPage(Page<Document> page, DocumentQuery query) {
QueryWrapper<Document> wrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(query.getTitle())) {
wrapper.like("title", query.getTitle());
}
if (query.getCreatorId() != null) {
wrapper.eq("creator_id", query.getCreatorId());
}
if (query.getStatus() != null) {
wrapper.eq("status", query.getStatus());
}
if (query.getStartTime() != null) {
wrapper.ge("create_time", query.getStartTime());
}
if (query.getEndTime() != null) {
wrapper.le("create_time", query.getEndTime());
}
wrapper.orderByDesc("create_time");
Page<Document> documentPage = documentMapper.selectPage(page, wrapper);
List<Document> documentList = documentPage.getRecords();
// 设置创建人信息
for (Document document : documentList) {
User creator = userService.getUserById(document.getCreatorId());
document.setCreator(creator);
}
return documentList;
}
@Override
public boolean saveDocument(Document document, MultipartFile file) {
try {
if (document.getId() == null) {
// 新增文档
document.setCreateTime(LocalDateTime.now());
document.setUpdateTime(LocalDateTime.now());
document.setStatus(1);
// 处理上传文件
if (file != null && !file.isEmpty()) {
String filePath = FileUploadUtil.uploadFile(file, "documents");
document.setFilePath(filePath);
document.setFileName(file.getOriginalFilename());
document.setFileSize(file.getSize());
document.setFileType(file.getContentType());
}
int result = documentMapper.insert(document);
return result > 0;
} else {
// 更新文档
document.setUpdateTime(LocalDateTime.now());
// 处理上传文件
if (file != null && !file.isEmpty()) {
// 删除原有文件
Document oldDocument = documentMapper.selectById(document.getId());
if (oldDocument != null && !StringUtils.isEmpty(oldDocument.getFilePath())) {
FileUploadUtil.deleteFile(oldDocument.getFilePath());
}
String filePath = FileUploadUtil.uploadFile(file, "documents");
document.setFilePath(filePath);
document.setFileName(file.getOriginalFilename());
document.setFileSize(file.getSize());
document.setFileType(file.getContentType());
}
int result = documentMapper.updateById(document);
return result > 0;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean deleteDocument(Long id) {
// 删除文档前先删除关联文件
Document document = documentMapper.selectById(id);
if (document != null && !StringUtils.isEmpty(document.getFilePath())) {
FileUploadUtil.deleteFile(document.getFilePath());
}
int result = documentMapper.deleteById(id);
return result > 0;
}
}
4. 控制器层实现
java
// UserController.java
@RestController
@RequestMapping("/api/users")
@Api(tags = "用户管理")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
@ApiOperation("获取用户详情")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return Result.success(user);
}
@GetMapping
@ApiOperation("获取用户列表")
public Result<List<User>> listUsers() {
List<User> userList = userService.getAllUsers();
return Result.success(userList);
}
@PostMapping
@ApiOperation("新增用户")
@PreAuthorize("hasAuthority('sys:user:add')")
public Result<?> addUser(@RequestBody User user) {
boolean result = userService.saveUser(user);
if (result) {
return Result.success();
} else {
return Result.error("新增用户失败");
}
}
@PutMapping
@ApiOperation("更新用户")
@PreAuthorize("hasAuthority('sys:user:edit')")
public Result<?> updateUser(@RequestBody User user) {
boolean result = userService.saveUser(user);
if (result) {
return Result.success();
} else {
return Result.error("更新用户失败");
}
}
@DeleteMapping("/{id}")
@ApiOperation("删除用户")
@PreAuthorize("hasAuthority('sys:user:delete')")
public Result<?> deleteUser(@PathVariable Long id) {
boolean result = userService.deleteUser(id);
if (result) {
return Result.success();
} else {
return Result.error("删除用户失败");
}
}
}
// DocumentController.java
@RestController
@RequestMapping("/api/documents")
@Api(tags = "文档管理")
public class DocumentController {
@Autowired
private DocumentService documentService;
@GetMapping("/{id}")
@ApiOperation("获取文档详情")
public Result<Document> getDocument(@PathVariable Long id) {
Document document = documentService.getDocumentById(id);
return Result.success(document);
}
@GetMapping
@ApiOperation("获取文档列表")
public Result<PageInfo<Document>> listDocuments(
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
DocumentQuery query) {
Page<Document> page = new Page<>(pageNum, pageSize);
List<Document> documentList = documentService.getDocumentsByPage(page, query);
PageInfo<Document> pageInfo = new PageInfo<>(documentList);
pageInfo.setTotal(page.getTotal());
pageInfo.setPages((int) page.getPages());
return Result.success(pageInfo);
}
@PostMapping
@ApiOperation("新增文档")
@PreAuthorize("hasAuthority('oa:document:add')")
public Result<?> addDocument(@RequestBody Document document) {
boolean result = documentService.saveDocument(document, null);
if (result) {
return Result.success();
} else {
return Result.error("新增文档失败");
}
}
@PostMapping("/upload")
@ApiOperation("上传文档")
@PreAuthorize("hasAuthority('oa:document:add')")
public Result<?> uploadDocument(@RequestParam("file") MultipartFile file,
@RequestParam("title") String title,
@RequestParam("content") String content,
@RequestParam("creatorId") Long creatorId) {
Document document = new Document();
document.setTitle(title);
document.setContent(content);
document.setCreatorId(creatorId);
boolean result = documentService.saveDocument(document, file);
if (result) {
return Result.success();
} else {
return Result.error("上传文档失败");
}
}
@PutMapping
@ApiOperation("更新文档")
@PreAuthorize("hasAuthority('oa:document:edit')")
public Result<?> updateDocument(@RequestBody Document document) {
boolean result = documentService.saveDocument(document, null);
if (result) {
return Result.success();
} else {
return Result.error("更新文档失败");
}
}
@DeleteMapping("/{id}")
@ApiOperation("删除文档")
@PreAuthorize("hasAuthority('oa:document:delete')")
public Result<?> deleteDocument(@PathVariable Long id) {
boolean result = documentService.deleteDocument(id);
if (result) {
return Result.success();
} else {
return Result.error("删除文档失败");
}
}
}
5. 前端组件示例(Vue.js)
vue
<!-- DocumentList.vue -->
<template>
<div class="document-list">
<el-card class="box-card">
<template #header>
<div class="clearfix">
<span>文档列表</span>
<el-button style="float: right; padding: 3px 0" type="primary" @click="handleAdd">
新增文档
</el-button>
</div>
</template>
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
<el-form-item label="标题">
<el-input v-model="queryParams.title" placeholder="请输入标题"></el-input>
</el-form-item>
<el-form-item label="创建人">
<el-select v-model="queryParams.creatorId" placeholder="请选择创建人">
<el-option v-for="user in userList" :key="user.id" :label="user.realName" :value="user.id"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="queryParams.startTime"
type="date"
placeholder="开始日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="queryParams.endTime"
type="date"
placeholder="结束日期">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="documentList" stripe border fit highlight-current-row>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="title" label="标题"></el-table-column>
<el-table-column prop="creator.realName" label="创建人"></el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
<el-table-column prop="fileSize" label="文件大小" width="100">
<template #default="scope">
{{ formatFileSize(scope.row.fileSize) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="mini" @click="handleView(scope.row)">查看</el-button>
<el-button size="mini" type="warning" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog :visible.sync="dialogVisible" title="文档管理">
<el-form :model="formData" ref="formRef" label-width="120px">
<el-form-item label="标题" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
<el-input v-model="formData.title"></el-input>
</el-form-item>
<el-form-item label="内容">
<el-input type="textarea" v-model="formData.content" rows="4"></el-input>
</el-form-item>
<el-form-item label="上传文件">
<el-upload
class="upload-demo"
action="/api/documents/upload"
:on-success="handleUploadSuccess"
:before-upload="beforeUpload"
:show-file-list="false">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传PDF、Word、Excel文件,且不超过10MB</div>
</el-upload>
<div v-if="formData.fileName" class="mt-2">
<el-link :href="formData.filePath" type="primary" target="_blank">{{ formData.fileName }}</el-link>
<el-button size="mini" type="danger" @click="removeFile">删除文件</el-button>
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveDocument">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
documentList: [],
userList: [],
queryParams: {
title: '',
creatorId: null,
startTime: null,
endTime: null
},
currentPage: 1,
pageSize: 10,
total: 0,
dialogVisible: false,
formData: {},
isEdit: false
}
},
created() {
this.fetchDocumentList();
this.fetchUserList();
},
methods: {
// 获取文档列表
fetchDocumentList() {
this.$http.get('/api/documents', {
params: {
pageNum: this.currentPage,
pageSize: this.pageSize,
...this.queryParams
}
}).then(res => {
if (res.code === 200) {
this.documentList = res.data.list;
this.total = res.data.total;
} else {
this.$message.error(res.message);
}
}).catch(err => {
console.error(err);
this.$message.error('获取文档列表失败');
});
},
// 获取用户列表
fetchUserList() {
this.$http.get('/api/users').then(res => {
if (res.code === 200) {
this.userList = res.data;
} else {
this.$message.error(res.message);
}
}).catch(err => {
console.error(err);
this.$message.error('获取用户列表失败');
});
},
// 格式化文件大小
formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
// 分页相关
handleSizeChange(size) {
this.pageSize = size;
this.fetchDocumentList();
},
handleCurrentChange(page) {
this.currentPage = page;
this.fetchDocumentList();
},
// 查询与重置
handleQuery() {
this.currentPage = 1;
this.fetchDocumentList();
},
handleReset() {
this.queryParams = {
title: '',
creatorId: null,
startTime: null,
endTime: null
};
this.currentPage = 1;
this.fetchDocumentList();
},
// 新增文档
handleAdd() {
this.isEdit = false;
this.formData = {
creatorId: this.$store.state.user.id
};
this.dialogVisible = true;
},
// 查看文档
handleView(row) {
this.$router.push({ name: 'DocumentDetail', params: { id: row.id } });
},
// 编辑文档
handleEdit(row) {
this.isEdit = true;
this.formData = { ...row };
this.dialogVisible = true;
},
// 删除文档
handleDelete(row) {
this.$confirm('确定要删除该文档吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http.delete(`/api/documents/${row.id}`).then(res => {
if (res.code === 200) {
this.$message.success('删除成功');
this.fetchDocumentList();
} else {
this.$message.error(res.message);
}
}).catch(err => {
console.error(err);
this.$message.error('删除失败');
});
}).catch(() => {
// 取消操作
});
},
// 上传文件前的校验
beforeUpload(file) {
const isPdf = file.type === 'application/pdf';
const isWord = file.type === 'application/msword' || file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
const isExcel = file.type === 'application/vnd.ms-excel' || file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
const isLt10M = file.size / 1024 / 1024 < 10;
if (!(isPdf || isWord || isExcel)) {
this.$message.error('只能上传PDF、Word、Excel文件');
return false;
}
if (!isLt10M) {
this.$message.error('文件大小不能超过10MB');
return false;
}
return true;
},
// 上传成功处理
handleUploadSuccess(response, file) {
if (response.code === 200) {
this.$message.success('上传成功');
this.formData.fileName = file.originalName;
this.formData.filePath = response.data.filePath;
this.formData.fileSize = file.size;
} else {
this.$message.error(response.message);
}
},
// 删除已上传文件
removeFile() {
this.formData.fileName = null;
this.formData.filePath = null;
this.formData.fileSize = null;
},
// 保存文档
saveDocument() {
this.$refs.formRef.validate(valid => {
if (valid) {
const url = this.isEdit ? `/api/documents` : `/api/documents`;
const method = this.isEdit ? 'put' : 'post';
this.$http[method](url, this.formData).then(res => {
if (res.code === 200) {
this.$message.success(this.isEdit ? '更新成功' : '创建成功');
this.dialogVisible = false;
this.fetchDocumentList();
} else {
this.$message.error(res.message);
}
}).catch(err => {
console.error(err);
this.$message.error(this.isEdit ? '更新失败' : '创建失败');
});
} else {
return false;
}
});
}
}
}
</script>
<style scoped>
.upload-demo {
margin-bottom: 10px;
}
.mt-2 {
margin-top: 10px;
}
</style>
四、系统功能模块
1. 用户权限管理
- 用户管理:用户信息的增删改查
- 角色管理:角色信息的管理和权限分配
- 权限管理:系统功能权限的定义和分配
2. 文档管理
- 文档上传:支持多种格式文档上传
- 文档分类:自定义文档分类体系
- 文档检索:基于关键词的文档搜索
- 文档权限:细粒度的文档访问权限控制
3. 流程审批
- 流程设计:可视化流程设计器
- 审批管理:流程发起、审批、跟踪
- 统计分析:审批效率统计和分析
4. 会议管理
- 会议安排:会议计划和安排
- 会议通知:自动发送会议通知
- 会议纪要:会议记录和归档
5. 日程安排
- 个人日程:个人工作计划和安排
- 共享日程:团队成员之间的日程共享
- 日程提醒:定时提醒功能
五、系统部署与测试
1. 环境要求
- JDK 11+
- MySQL 8.0+
- Maven 3.6+
- Node.js 14+
2. 部署步骤
- 创建数据库并导入表结构
- 配置数据库连接信息
- 编译后端项目:
mvn clean package
- 编译前端项目:
npm install && npm run build
- 部署后端应用到服务器
- 配置Nginx代理前端静态资源
3. 测试用例
java
// UserServiceTest.java
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testGetUserById() {
User user = userService.getUserById(1L);
assertNotNull(user);
assertEquals("admin", user.getUsername());
}
@Test
void testGetUserByUsername() {
User user = userService.getUserByUsername("admin");
assertNotNull(user);
assertEquals("admin", user.getUsername());
}
@Test
void testSaveUser() {
User user = new User();
user.setUsername("testuser");
user.setPassword("123456");
user.setRealName("测试用户");
user.setEmail("[email protected]");
user.setPhone("13800138000");
boolean result = userService.saveUser(user);
assertTrue(result);
User savedUser = userService.getUserByUsername("testuser");
assertNotNull(savedUser);
assertEquals("测试用户", savedUser.getRealName());
}
@Test
void testDeleteUser() {
// 先创建一个用户
User user = new User();
user.setUsername("testuser");
user.setPassword("123456");
user.setRealName("测试用户");
user.setEmail("[email protected]");
user.setPhone("13800138000");
userService.saveUser(user);
User savedUser = userService.getUserByUsername("testuser");
assertNotNull(savedUser);
// 删除用户
boolean result = userService.deleteUser(savedUser.getId());
assertTrue(result);
// 验证用户已删除
User deletedUser = userService.getUserById(savedUser.getId());
assertNull(deletedUser);
}
}
六、毕业设计文档框架
1. 论文框架
- 引言
- 相关技术综述
- 系统需求分析
- 系统设计
- 系统实现
- 系统测试
- 总结与展望
2. 外文翻译
- 推荐翻译Java EE开发、办公自动化系统设计等相关的外文文献
- 翻译内容应与系统实现技术密切相关
七、总结
本办公自动化系统基于Java EE平台开发,实现了企业日常办公的数字化管理。系统采用前后端分离架构,结合Spring Boot、MyBatis和Vue.js等技术,具有良好的可扩展性和用户体验。通过本项目的开发,深入掌握了Java EE开发、数据库设计、前端开发等多项技术,为企业办公自动化提供了完整的解决方案。