资讯动态

Java OA自动化办公系统源码落地:从部署到审批流跑通

发布时间:2026/10/8 11:42:08 来源:尧图企业网站定制
简介这是一份采用Maven管理、基于Spring Boot与MySQL构建的Java OA办公自动化系统源码OA是员工及管理者使用频率最高的应用系统该源码面向需要快速搭建企业内部办公平台或系统学习Spring Boot项目的开发者。压缩包共1031个文件大小5.49MB内容涵盖237个Java业务类、152个FTL模板、85个JavaScript脚本、56个CSS样式表、39个HTML页面以及PNG、GIF、JPG等大量静态资源呈现完整的前后端交互结构同时提供SQL初始化脚本、配置文件、JSP入口文档等项目素材便于导入数据库后启动运行也方便按模块阅读调试。资源已有1899人学习下载。借助完整源码可理解OA系统如何将请假、报销、文档管理等事务线上化并学习Spring Boot分层架构、Maven项目管理以及模板引擎渲染等工程实现对积累真实企业项目经验有直接帮助。目录结构按功能模块划分方便按需查阅与二次开发。1. 为什么一份OA自动化办公系统源码包比Hello World难十倍拿到一份Java开发OA自动化办公系统源码.zip别指望解压后扔进Tomcat就能跑起来。OA的复杂度不在单表CRUD而在组织架构、审批流、表单权限这三条数据链怎么串联。对初级Java开发它是难得的完整工程样本对想自研OA的团队它比从零搭框架省一半时间。但它的坑同样多数据库版本、字符集、流程引擎、前端缓存任何一个都能让系统在启动阶段翻车。这也是“OA系统”常出现在java面试题里的原因——它考核的不只是增删改查而是对多模块工程、事务、状态机的综合理解。下面按我实际落地路径把这个zip包从解压到能审批、能考勤、能自动化的过程拆开讲。新手能跟步骤走熟手也能对参数边界和踩坑位置有参照。2. 拆解OA源码包目录结构、数据库脚本与三个关键配置拿到zip包先别急着导入IDE。源码包是一个黑匣子第一步是用命令行把它拆开确认它长什么样。这一章的目标是让你在写第一行启动命令前就对整个工程的骨架和命脉有数。2.1 先看目录Maven父子工程还是单体分包解压并查看目录树是所有操作的前提。用命令行解压比双击zip再拖文件夹更可控也方便后续在服务器上重复操作。unzip Java开发OA自动化办公系统源码.zip -d oa-project cd oa-project tree -L 2 -F | head -50unzip的-d参数指定解压目录避免把一堆文件散落在当前目录。tree -L 2只显示两层目录-F在目录名后面加斜杠方便识别目录和文件。如果系统没有tree可以用find . -maxdepth 2 -type d | sort代替。一个典型的OA源码包常采用Maven多模块结构主目录下会有oa-common、oa-dao、oa-service、oa-web、oa-admin。判断是多模块还是单体关键是看根目录的pom.xml里有没有modules元素。如果有这就是多模块工程如果没有所有Java文件可能都在一个src/main/java下。多模块是OA这种业务复杂度的必然选择组织、权限、考勤、审批、通知、日程各有边界模块化能强制依赖方向——web依赖serviceservice依赖daocommon谁都不依赖。一旦边界划清后面替换或修改一个模块就不至于牵一发动全身。很多老OA是单体分包也能跑但改到后面包与包之间的循环依赖会让人怀疑人生。查看目录时还要注意隐藏文件。执行ls -la如果看到mvnw和mvnw.cmd说明源码包带了Maven Wrapper那不需要你本机装Maven也能构建。如果只有pom.xml则依赖本机Maven环境。另外.gitignore文件能帮你判断哪些目录是生成产物比如target/、out/。2.2 数据库脚本从权限表到流程表的初始化顺序OA源码包必带SQL脚本一般放在doc/sql或db目录。这里要做的第一件事是把所有SQL文件按文件名排序找出它们的依赖顺序。find . -path */sql/* -name *.sql | sort | head -20常见命名约定是01_schema.sql、02_data.sql、03_flow.sql。01_schema创建所有表和索引02_data插入管理员账号、菜单、部门种子数据03_flow初始化流程定义和审批节点。顺序不能乱因为03_flow里会引用01_schema中的表。这里有个血泪经验不要用source一次性把多个SQL文件灌进去。你应该逐个执行观察每个文件的输出。MySQL命令行客户端默认每个语句成功与否都会回显如果你加了--force建表失败会继续最后留下半成品表排查起来非常痛苦。执行前还要确认字符集。OA是中文场景库表如果不是utf8mb4中文乱码和表情存储失败是必然的。我一般会先手工建库指定字符集再导入脚本mysql -uroot -p -e CREATE DATABASE oa_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -uroot -p oa_db doc/sql/01_schema.sql mysql -uroot -p oa_db doc/sql/02_data.sql mysql -uroot -p oa_db doc/sql/03_flow.sql这里的oa_db要和后面配置文件里spring.datasource.url中的库名完全一致。很多人栽在“脚本执行了但连的库不对”——往A库导数据应用连B库启动照样报“Table doesnt exist”。OA的权限模型通常是RBACsys_user、sys_role、sys_menu、rel_role_user、rel_role_menu。你可以先查一下初始化数据里有没有管理员账号SELECT user_id, user_code, user_name, status FROM sys_user WHERE user_code admin;这条SQL能确认02_data.sql是否真的执行成功。如果查不到说明导入时被某条语句中断了需要回头查看执行日志。记住OA系统没有管理员账号后面一切都白搭。2.3 三个关键配置数据源、Redis、文件上传路径启动一个OA系统真正需要改的配置通常只有三处数据源、Redis、文件上传路径。找到application.yml或application-local.yml重点看下面这些行。spring: datasource: url: jdbc:mysql://localhost:3306/oa_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: database: 0 oa: upload: path: /data/oa/uploadspring.datasource.url里必须带serverTimezoneAsia/ShanghaiMySQL 8.x驱动默认时区是UTC不指定会直接报错。password留空表示Redis无密码如果设置了密码这里填明文即可。oa.upload.path是文件上传的物理路径Windows下写成D:/oa/uploadLinux下用绝对路径注意目录要有写权限。这三个配置就是OA系统启动前的“三座大山”。文件上传路径最容易被忽视——启动时可能不报错但一上传附件预览就出现404因为Spring Boot没有把上传目录映射成可访问的静态资源。这个坑在第5章会专门给解。还需要确认日志配置。在application.yml里搜logginglogging: level: root: info com.oa: debug file: name: logs/oa.logcom.oa包名根据源码实际包路径来改。开发阶段把com.oa调成debug能打印SQL和事务日志排查审批流程时很有用。但生产环境要调回info否则日志膨胀很快。这里我一般会在启动前就设置好而不是等出问题再回头加。3. 把审批流跑起来从数据表到Java接口的实现路径审批流是OA系统的灵魂。这一章把数据模型、接口实现和流程引擎选型讲透让你顺着源码的脉络把一条请假审批跑通。搞清楚这一块OA就不再是黑匣子。3.1 审批流的数据模型流程实例、任务节点、审批记录不管源码用不用工作流引擎审批流的数据模型都绕不开三张核心表流程实例表、任务节点表、审批记录表。以自研状态机的实现为例表设计通常长这样。CREATE TABLE oa_process_instance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, biz_type VARCHAR(32) NOT NULL COMMENT 业务类型leave/travel/purchase, biz_id BIGINT NOT NULL COMMENT 业务表单ID, current_node VARCHAR(64) NOT NULL COMMENT 当前节点编码, status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1审批中 2通过 3驳回, creator_id BIGINT NOT NULL, create_time DATETIME NOT NULL ); CREATE TABLE oa_process_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, instance_id BIGINT NOT NULL, node_code VARCHAR(64) NOT NULL, assignee_id BIGINT NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待办 1已办, start_time DATETIME, end_time DATETIME ); CREATE TABLE oa_process_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, instance_id BIGINT NOT NULL, task_id BIGINT, operator_id BIGINT NOT NULL, action VARCHAR(16) NOT NULL COMMENT agree/reject/transfer, comment VARCHAR(500), create_time DATETIME NOT NULL );一张请假单发起后在oa_process_instance生成一条记录类型是leave然后在oa_process_task给部门经理生成一张待办经理点击同意在oa_process_record插入一条agree记录再给下一节点生成待办。biz_type和biz_id把流程实例和具体的请假单、报销单关联起来这是一种“多态关联”设计避免为每种业务各建一套流程表。如果你接手的源码包用了Activiti或Flowable表名会是ACT_RE_*、ACT_RU_*、ACT_HI_*开头。那你的重点会变成BPMN XML建模。但大多数轻量OA源码会选择自研状态机因为OA审批链相对固定不需要BPMN那么重。查询一个用户的待办列表是OA最常用的SQL之一SELECT t.id, t.node_code, i.biz_type, i.biz_id, i.create_time FROM oa_process_task t LEFT JOIN oa_process_instance i ON t.instance_id i.id WHERE t.assignee_id #{userId} AND t.status 0 ORDER BY t.start_time DESC;这个查询把任务表与实例表做关联status 0表示待办。很多新手只查任务表不带出biz_type和biz_id导致前端无法跳转对应的业务表单这也是待办列表“点不进去”的常见原因。3.2 一个请假审批的后端接口实现以请假申请为例看从Controller到Service的完整链路。这是OA最典型的业务代码。RestController RequestMapping(/oa/leave) public class LeaveController { Autowired private LeaveService leaveService; PostMapping(/apply) public Result apply(RequestBody LeaveApplyDTO dto) { if (StringUtils.isBlank(dto.getReason())) { return Result.error(请假原因不能为空); } return Result.ok(leaveService.apply(dto)); } PostMapping(/approve) public Result approve(RequestBody ApproveDTO dto) { return Result.ok(leaveService.approve(dto)); } }LeaveApplyDTO接收前端传来的startDate、endDate、reason、leaveType等字段。approve接口接收instanceId、taskId、action和comment。两个接口都返回统一Result包装前端根据code判断是否成功。Service层的关键是事务和状态流转。看apply方法Service public class LeaveService { Autowired private LeaveMapper leaveMapper; Autowired private ProcessInstanceMapper processInstanceMapper; Autowired private ProcessTaskMapper processTaskMapper; Transactional(rollbackFor Exception.class) public Integer apply(LeaveApplyDTO dto) { Leave leave new Leave(); BeanUtils.copyProperties(dto, leave); leave.setStatus(0); // 草稿状态 leaveMapper.insert(leave); ProcessInstance instance new ProcessInstance(); instance.setBizType(leave); instance.setBizId(leave.getId()); instance.setCurrentNode(dept_approve); instance.setStatus(1); // 审批中 processInstanceMapper.insert(instance); ProcessTask task new ProcessTask(); task.setInstanceId(instance.getId()); task.setNodeCode(dept_approve); task.setAssigneeId(dto.getDeptManagerId()); task.setStatus(0); processTaskMapper.insert(task); return leave.getId(); } }apply方法干三件事插入请假主表记录创建流程实例给第一个审批人生成待办任务。Transactional保证这三步要么全部成功要么全部回滚不会出现“表单有了但流程没发起”的脏数据。注意这里有个设计细节流程实例的status置为1请假单自己的status还是0。两个状态分开是因为请假单支持撤回、修改再提交流程实例的状态则独立维护。很多初次做OA的开发者只用一个状态字段最后要么撤回逻辑混乱要么查询列表时不知道以哪个状态为准。approve方法会先校验任务归属再更新任务状态、写审批记录、根据action决定流程走向。骨架如下Transactional public void approve(ApproveDTO dto) { ProcessTask task processTaskMapper.selectById(dto.getTaskId()); Assert.isTrue(task.getAssigneeId().equals(dto.getUserId()), 不是当前审批人); ProcessRecord record new ProcessRecord(); record.setInstanceId(task.getInstanceId()); record.setTaskId(task.getId()); record.setOperatorId(dto.getUserId()); record.setAction(dto.getAction()); record.setComment(dto.getComment()); processRecordMapper.insert(record); task.setStatus(1); task.setEndTime(new Date()); processTaskMapper.updateById(task); if (approve.equals(dto.getAction()) dept_approve.equals(task.getNodeCode())) { // 创建下一节点任务 ProcessTask nextTask new ProcessTask(); nextTask.setInstanceId(task.getInstanceId()); nextTask.setNodeCode(hr_approve); nextTask.setAssigneeId(dto.getHrManagerId()); nextTask.setStatus(0); processTaskMapper.insert(nextTask); } else { // 流程结束更新实例状态 ProcessInstance instance processInstanceMapper.selectById(task.getInstanceId()); instance.setStatus(approve.equals(dto.getAction()) ? 2 : 3); processInstanceMapper.updateById(instance); } }这个逻辑把“审批动作”和“节点跳转”耦合在一起节点一多就会膨胀。更优雅的做法是用策略模式但源码包能跑通是第一位。你要做的是顺着这个逻辑把当前源码里的节点判断梳理清楚。3.3 流程引擎选型自研状态机还是集成Flowable很多Java开发拿到OA源码后第一反应是把自研状态机换成Flowable或Activiti。我的建议是除非团队有BPMN建模能力否则先别动。自研状态机适合审批链固定、分支简单的线性流程代码可读性高任何一个Java开发都能顺着if/else读懂。Flowable的优势在会签、或签、条件网关、任意跳转但集成成本不只是Jar包还要迁移历史数据、改前端接口。维度自研状态机Activiti/Flowable学习成本低Java/XML即可高需要学会BPMN建模会签/或签实现麻烦原生支持动态路由自己写条件判断条件网关配置历史数据迁移无需需要迁移或兼容适合项目固定审批链、中小OA复杂流程、BPM驱动型系统即使你去看泛微、通达这类商业oa系统的建模引擎它们的核心节点模型也还是那套“节点-连线-角色”的逻辑。自研状态机学的是这套思想切换到商业产品或Flowable只是换个表达方式。我一般建议先用源码自带状态机跑通业务等真遇到会签需求再引入Flowable并保留biz_typebiz_id映射表做兼容。4. 本地部署一套OAJDK、Maven与Tomcat的配合这一章解决“怎么把源码包变成能访问的系统”。版本匹配、打包方式、启动参数每一样都能让新手卡半个小时。走完这一章你就能在浏览器里看到登录页。4.1 版本匹配JDK 8/11/17与Tomcat的坑先从pom.xml里找源码编译版本。打开根目录的pom.xml定位到properties段。properties java.version1.8/java.version spring-boot.version2.3.12.RELEASE/spring-boot.version /propertiesjava.version如果是1.8就老老实实用JDK 8或JDK 11不要用JDK 17。Spring Boot 2.x在JDK 17下跑容易出现反射相关的IllegalAccessError因为JDK强模块化限制了深反射。如果java.version是17那源码基于Spring Boot 3.x环境必须JDK 17。先用命令确认当前环境java -version mvn -version如果mvn报错找不到JAVA_HOME需要配置环境变量。Linux下可以临时设置export JAVA_HOME/usr/lib/jvm/java-1.8.0 export PATH$JAVA_HOME/bin:$PATH但临时设置换终端就失效。我会把这两行写进~/.bashrc这样每次登录自动生效。Maven 3.6对JDK 8和11都兼容低于3.3的版本解析Spring Boot依赖时可能会非常慢。注意如果java.version是17源码里却大量使用javax.servlet包那打war包时不要丢进Tomcat 10因为Tomcat 10把javax换成了jakarta会直接类找不到。用内嵌Tomcat的java -jar方式更省心。4.2 用Maven打包并初始化数据库部署前先把数据库准备好。第2章里已经给了建库和导入SQL的命令如果还没执行现在补上。接着进行Maven打包。mvn clean package -DskipTests -Plocal-DskipTests跳过单元测试避免测试类连测试数据库失败。-Plocal激活名为local的Maven profile决定加载application-local.yml还是application-dev.yml。如果不确定有哪些profile看pom.xml里profiles段。打包过程中Maven会下载依赖。OA项目依赖动辄上百个第一次构建可能要5到10分钟。网络慢时可以用阿里云镜像加速编辑~/.m2/settings.xml加一个mirrorsettings mirrors mirror idaliyun/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/central/url /mirror /mirrors /settings这个镜像只对central生效不干扰源码包自带的私服配置。构建成功后产物在oa-web/target/oa-web.jar。多模块项目可以先在根目录执行mvn clean install -DskipTests让公共模块装到本地仓库再进oa-web单独打包。4.3 启动参数与访问路径启动Java应用得分配内存。OA系统启动时要加载菜单、权限缓存内存分配太小会频繁Full GC。我一般用java -Xms512m -Xmx1024m -jar oa-web.jar --spring.profiles.activelocal-Xms512m是初始堆大小-Xmx1024m是最大堆大小。--spring.profiles.activelocal是Spring的profile参数通常命令行写出来更明确避免同时存在多个环境配置文件时猜错。启动后看日志找到“Started Application”就说明成功。端口默认是8080访问路径取决于server.servlet.context-pathserver: port: 8088 servlet: context-path: /oa如果配了context-path: /oa登录页就是http://localhost:8088/oa/login没配就是http://localhost:8088/login。很多新手对着别的文章找/login结果一直404就是这个前缀的差别。启动过程中如果端口被占用用下面命令排查lsof -i:8088 # Linux/macOS netstat -ano | findstr 8088 # Windows启动成功后再验证一下健康状态。如果源码集成了Spring Boot Actuator访问http://localhost:8088/oa/actuator/health会返回JSON没有集成Actuator就直接访问登录页看能不能打开。我不建议跳过验证直接进系统因为有些错误是在登录之后的第一次页面跳转才暴露的。5. 避坑OA源码落地时的5个常见问题与排查源码包这行当跑通不难但“顺利跑通”很难。这里总结5个高频问题每条都按现象、原因、解决三个步骤写你可以直接对照排查。5.1 现象登录页能看到但验证码不显示登录页加载出来了用户名密码框都在唯独验证码图片裂了或空白。看浏览器Network验证码接口返回500日志里多半是Redis连接失败。原因OA的验证码不存Session而是存Redis生成验证码时后端调用redis.set(key, code, 60)。Redis没启动或spring.redis.host/port配置不对验证码服务就不可用。解决先启动Redis。Linux下redis-server /etc/redis/redis.confWindows下运行redis-server.exe。然后确认连接参数redis-cli -h localhost -p 6379 ping返回PONG就正常。同时检查database参数如果源码包用的是database: 1而你连的是0也会出现“能连接但读不到数据”的情况。5.2 现象启动时报“Table doesnt exist”但SQL脚本已执行启动应用时控制台报错Table oa_db.oa_user doesnt exist但你明明执行了SQL脚本。原因数据库连接串指向的库名和导入脚本的库名不一致。比如脚本导入到了oa_db配置文件里却写的是oa_dev。解决先确认应用连的库mysql -uroot -p -e SHOW DATABASES; mysql -uroot -p oa_db -e SHOW TABLES;如果库名一致但表确实没有那就是SQL脚本导入失败。回看第2章的导入步骤逐条执行注意观察有没有报错被--force吞掉。另外多模块项目可能不止一个数据源注意看MapperScan扫描的包是不是连到了别的库。5.3 现象审批流程发起后卡在“待提交”没有进入下一步用户在OA前端填完请假单点“提交”列表里这条单子一直是“待提交”点进去也没有审批按钮。原因前端点击“提交”调用的接口是/oa/leave/save保存草稿而不是/oa/leave/apply提交审批。这是源码里最常见的职责混淆保存草稿和提交审批共用一个接口或者前端把提交类型传丢了。解决打开浏览器F12看Network里提交请求的URL。如果是调用了save改前端submitForm的提交地址。如果是apply但状态没变检查LeaveService.apply里有没有把流程实例状态置为1以及事务有没有回滚。最直接的办法是在apply方法入口打日志确认deptManagerId是否传入。很多提交失败是因为找不到审批人assigneeId被设成null数据库外键约束让插入失败。日志里看到SQLException: Column assignee_id cannot be null就能定位。5.4 现象文件上传成功但预览404上传附件显示成功文件在服务器磁盘上也存在但点预览链接返回404。原因Spring Boot没把上传目录映射为静态资源。上传物理路径是/data/oa/upload前端访问的URL是/files/xxx.jpg没有对应的处理器。解决加一个WebMvcConfigurer实现把/files/**映射到本地目录。Configuration public class WebConfig implements WebMvcConfigurer { Value(${oa.upload.path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceLocations(file: uploadPath /); } }这段代码的作用是当URL以/files/开头时Spring Boot从本地uploadPath目录下查找文件。注意file:前缀不能丢否则Spring会以为是classpath下的路径。改完配置重启应用再访问预览地址就通了。5.5 现象系统时间和本地差8小时新部署的OA系统里所有待办时间和日志时间都比本地慢8小时数据库里的create_time也不对。原因应用时区和数据库时区不一致。MySQL驱动默认用serverTimezoneUTCJava进程默认时区可能是Etc/UTC而你的本地是东八区。解决修改连接串和启动参数双管齐下。spring: datasource: url: jdbc:mysql://localhost:3306/oa_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai同时启动时加时区参数java -Duser.timezoneAsia/Shanghai -jar oa-web.jar如果已经跑起来的数据错了修正时区后需要手动改历史数据的create_time这个没办法自动补救。所以第一次部署就把这两个参数写对比事后后悔药有效得多。6. 给OA源码加一个自动化能力表单联动与定时任务的进阶技巧源码包跑通只是起点真正让你觉得“这系统能用”的是自动化细节。这里给你两个我常用的进阶技巧改动都不大但用户感知非常明显。6.1 用Spring Schedule实现考勤日报自动推送考勤模块是OA标配但大多数源码只做了打卡和查询日报得靠人工。加一个定时任务每天固定时间汇总前一天的打卡记录推送给部门主管这个功能很加分。Component EnableScheduling public class AttendanceReportJob { Autowired private AttendanceMapper attendanceMapper; Scheduled(cron 0 0 9 * * *) public void sendDailyReport() { ListAttendance yesterdayList attendanceMapper.selectYesterday(); // 按部门分组生成统计内容通过站内信或邮件发送 MapString, ListAttendance groupByDept yesterdayList.stream() .collect(Collectors.groupingBy(Attendance::getDeptId)); // 略拼装消息并调用通知服务 } }Scheduled(cron 0 0 9 * * *)表示每天9点执行。cron从左到右是秒、分、时、日、月、周所以0 0 9是9点整。如果要每个工作日执行用0 0 9 * * MON-FRI。注意定时任务时区跟随JVM启动时没指定-Duser.timezone这个任务会按系统UTC时间触发早上9点变下午5点。这是第5.5节同一个坑的另一种表现形式。6.2 在表单渲染层做“筛选框隐藏字段”联动前端表单的联动逻辑在很多源码包里是缺失的。比如请假类型选了“年假”就显示“剩余年假天数”选了“事假”就隐藏该字段。这种“根据筛选框隐藏字段”的需求在泛微、通达这类商业OA里要靠建模引擎去配在自研OA里直接写jQuery就行。$(#leaveType).on(change, function() { var type $(this).val(); if (type annual) { $(#annualDaysRow).show(); $(#noticeRow).hide(); } else if (type personal) { $(#annualDaysRow).hide(); $(#noticeRow).show(); } else { $(#annualDaysRow).show(); $(#noticeRow).show(); } });这段逻辑放在页面底部script里或源码包自带的JS文件中。注意change事件用on(change)而不是on(click)否则某些浏览器会在打开下拉框时就触发。页面初始加载时也要执行一次保证编辑场景下回显能正确显示或隐藏字段。做源码项目最忌讳的就是解压后什么都不看直接启动。我去年接手一个OA源码包先花了大半天读SQL脚本把流程表的关系画在纸上后面的部署、改需求、加定时任务都顺了。看似多花了时间实际省了后面几个晚上的排查。源码包是一个黑匣子但你只要把数据库表和三个配置点摸透里面的代码就是纸老虎。上面这些坑我踩过大半也有看同事翻车总结出来的希望你部署自己的OA时能少走这一圈希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑