ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CRM需求文档深度解析:从J2EE老系统到现代重构的权限与流程设计

CRM需求文档深度解析:从J2EE老系统到现代重构的权限与流程设计

简介:这是面向企业CRM系统建设与产品设计人员的需求分析设计文档,围绕客户关系管理中的销售自动化、客户信息整合与数据分析支持等目标展开,适合产品经理、需求分析师和开发团队参考。文档以1个doc文件呈现,压缩包大小1.66MB,内容涵盖项目背景、用户组织结构、业务流程、功能需求及性能指标,并附有详尽的用例描述、概念结构图、业务流程图和界面原型预览,便于从需求到设计一体化理解。全文约60页,结构按引言、任务概述、功能需求、性能需求等模块划分,既可帮助团队统一术语与需求口径,也可作为后续系统设计与开发验收的基线。目前已有188人学习下载,对正在规划或实施CRM项目的人员具有直接借鉴价值。

1. 这份CRM客户关系管理需求文档,值得每个ToB开发者读一遍

刚接触CRM客户关系管理项目的开发者,最容易把需求分析文档当成“一堆没用的流程描述”。直到我接手一个老系统的二次开发,领导丢来一份六十页的需求规格说明书,才意识到这些用例描述才是项目最值钱的部分——它定义了完整的三类角色、九大业务域、三十多个功能用例,还有精确到按钮级的界面交互规则。这套客户关系管理系统文档解决的是典型的中小型企业客户管理问题:销售部门管客户、就业部门盯合作企业、主管做审批、系统管理员控权限。适合正在做CRM相关项目、需要写需求文档、或者想看懂老项目设计逻辑的开发者和产品经理。

2. 需求文档里的技术栈盘点:J2EE + SQL Server 2000时代的CRM该看什么

2.1 从运行环境反推技术选型:WebSphere、SQL Server与JDK 1.5的组合

这份文档在任务概述部分给出了清晰的运行环境约束,这些信息对于拆解老项目至关重要。数据库采用Microsoft SQL Server 2000,应用服务器是IBM WebSphere Application Server 6.1,编译程序是JDK 1.5,操作系统是Windows XP SP2。这一套组合是2008年前后典型的J2EE企业级应用标准配置,也决定了代码层面会遇到的两个关键约束。

第一个约束是JDK 1.5时代的语法限制。那时候还没有Lambda表达式、Stream API和Optional,写循环基本靠for和Iterator,集合操作靠的是Collections工具类。这意味着如果你打算把这段历史资产翻新成Spring Boot项目,几乎所有业务层的代码都要重写,而不是简单调整依赖。

第二个约束是WebSphere的部署特性。WebSphere 6.1对JSP编译、类加载顺序和数据源配置都有自己的一套玩法,和今天嵌入式Tomcat的开发体验差别很大。文档里提到的“单点登录功能,但不使用AD或其他LDAP系统”,在当年的技术背景下一般会采用Session共享加自定义用户上下文的方式实现,也就是登录成功后把用户信息塞进Session,再通过Filter拦截请求做身份校验。

# 典型的老J2EE部署环境 JDK 1.5 IBM WebSphere Application Server 6.1 Microsoft SQL Server 2000 (SP4) Windows Server 2003 / Windows XP SP2

这套组合决定了两个事实:第一,系统内部大量使用JSP/Servlet/JavaBean的分层方式,页面和业务逻辑耦合较重;第二,数据库访问走JDBC或早期ORM框架,SQL语句直接写在Java代码里或配置文件里的情况很常见。

2.2 系统目标与角色划分:三类角色、三个业务域、一条审批链

文档在功能需求开头明确说了系统由三类角色组成:系统管理员、主管(销售部门/就业部门)、业务员(销售部门/就业部门)。这个三角色模型是理解整个CRM系统的钥匙。

业务员负责记录客户信息、通过和客户交流记录客户表达的意愿和建议。主管通过业务员记录的客户信息和日程安排来把握客户的整体意向动态,主管也可以直接和客户沟通,还可以通过邮件系统进行双方交流。系统管理员负责客户分类、用户信息管理与数据安全。

从功能划分来看,系统包含九个模块:日程安排(日常事务、日日程、周日程、月日程)、客户信息管理、指派员工与指派目标(主管才能操作)、邮件系统(写邮件、收件箱、发件箱、废件箱、查询邮件、邮件帐号配置、删除邮件、群发邮件)、审批管理(我的审批、我的申请)、短信管理(发送短信、查询短信、删除短信)、组织结构管理(部门管理、员工管理)、系统管理(客户分类管理、数据库操作、设置用户权限)、组织管理(添加修改删除部门、添加修改删除人员)。

-- 角色权限的核心逻辑:数据归属决定可见范围 -- 业务员只能操作自己的客户、日程和邮件 -- 主管可以查看下属业务员的数据并进行审批 SELECT u.user_id, u.user_name, r.role_name, d.dept_name FROM sys_user u JOIN sys_user_role ur ON u.user_id = ur.user_id JOIN sys_role r ON ur.role_id = r.role_id LEFT JOIN sys_dept d ON u.dept_id = d.dept_id WHERE u.user_id = ? -- 当前登录用户

这段SQL是权限模型的核心思路:用户、角色、部门三张表关联。业务层再根据角色类型做数据行级过滤。

2.3 功能模块全景:十二个用例模块的优先级与归属

文档用表格列举了三十二个用例,并且标注了优先级。这里有一个值得注意的细节:用例编号并不是统一连贯的——客户信息管理是CRM201,日程安排是CRM101,邮件系统是CRM107,审批管理是CRM114,短信管理是CRM116,组织结构是CRM119,系统管理是CRM301。这说明文档在编写过程中经历了多次功能增补,编号体系并不严格。

优先级标注也很有意思。客户分类管理、数据库操作、设置用户权限的优先级都达到了四分以上,客户管理五分、日程安排五分,说明文档编制者认为客户和日程是最核心的业务。而邮件系统的用例分散在五到七的编号区间,短信管理则相对边缘。

从这些优先级数据中,我一般会把模块划分为三个建设批次:

批次模块理由
P0(必须)客户信息管理、日程安排客户管理是系统存在的根本,日程是销售日常工作记录
P1(重要)审批管理、邮件系统、用户权限审批支撑业务流转,邮件是内外沟通管道,权限是安全底线
P2(增强)短信管理、组织管理、数据库操作短信是补充通知手段,数据库备份还原属于运维功能

这个划分方式可以直接用于排期和资源分配。如果你在设计自己的CRM系统,也可以借鉴这种按优先级分层的方法,把有限的开发资源先投入核心链路。

3. 核心模块拆解:日程安排、审批流与邮件管理的用例链路

3.1 日程安排的用例转换:从“点击新增”到数据库记录

文档中CRM1.01“日程安排”用例是描述最详尽的一个,包含了日日程、周日程、月日程三种粒度。基本事件流是:用户点击日程安排页面,看到四个菜单——日常事务、日日程、周日程、月日程,然后分别进行新增、修改、删除、查看操作。

把这段需求转成后端逻辑,其实就是一张日程表加上增删改查接口。关键在于“新增日程”对话框中有一个“关联客户”字段,这个字段把日程和客户信息绑定起来。

// 日程表核心结构(根据需求推演) CREATE TABLE crm_schedule ( schedule_id INT PRIMARY KEY IDENTITY(1,1), -- 日程ID schedule_type TINYINT NOT NULL, -- 1=日 2=周 3=月 subject NVARCHAR(200) NOT NULL, -- 主题 start_time DATETIME NOT NULL, -- 启始时间 content NVARCHAR(2000), -- 内容 customer_id INT, -- 关联客户ID owner_id INT NOT NULL, -- 归属人(业务员ID) dept_id INT NOT NULL, -- 归属部门 create_time DATETIME DEFAULT GETDATE(), update_time DATETIME DEFAULT GETDATE(), deleted_flag TINYINT DEFAULT 0 );

这张表的设计思路是:schedule_type区分日程粒度,customer_id建立与客户信息的关联,owner_id和dept_id配合实现数据隔离。业务规则中明确规定“销售人员只能对自己的日程安排信息进行查询、添加、修改、删除”,所以每次查询都要带上owner_id条件。

// 查询日程列表的核心SQL(按归属人过滤) SELECT schedule_id, schedule_type, subject, start_time, content, customer_id FROM crm_schedule WHERE owner_id = ? AND deleted_flag = 0 AND schedule_type = ? -- 日/周/月 ORDER BY start_time DESC;

参数说明:第一个问号是当前登录用户的ID,第二个问号是日程类型(日、周、月)。这看似简单的设计,恰恰解决了销售管理中最常见的越权查看问题。

3.2 审批流的状态机:我的申请与我的审批

CRM1.04“我的申请”和CRM1.03“我的审批”构成了一个完整的审批闭环。销售人员发起申请,主管进行审批,审批结果只有“同意”或“不同意”两种。文档中有一个特殊需求值得注意:“审批通过后就不能对结果进行删除、修改,只能有查看的功能”。

这个需求说明审批流需要状态机。申请从创建到结束会经历:待审核、已同意、已拒绝、已撤回几个状态。其中只有待审核状态的申请允许修改和删除,已通过的申请锁定。

// 申请状态枚举 public enum ApplyStatus { PENDING(0, "待审核"), APPROVED(1, "已同意"), REJECTED(2, "已拒绝"), CANCELED(3, "已撤回"); private final int code; private final String desc; ApplyStatus(int code, String desc) { this.code = code; this.desc = desc; } }

审批流实现的关键在于:销售人员添加申请时必须选择一个审批人(文档明确要求),申请内容里必须有主题和内容,且两项不能为空。审批人在“我的审批”页面看到待审批列表,点击后查看细节并提交审批意见。审批结束后,系统根据审批结果更新申请状态,同时可以触发后续动作,比如通知申请者或者记录日志。

// 审批处理的核心逻辑伪代码 public void approve(Long applyId, Long approverId, boolean approveResult) { // 1. 校验审批人身份 Apply apply = applyMapper.selectById(applyId); if (!apply.getApproverId().equals(approverId)) { throw new BizException("只能审批指派给自己的申请"); } // 2. 判断状态是否允许审批 if (apply.getStatus() != ApplyStatus.PENDING.getCode()) { throw new BizException("该申请已处理,不能重复审批"); } // 3. 更新状态 apply.setStatus(approveResult ? ApplyStatus.APPROVED.getCode() : ApplyStatus.REJECTED.getCode()); apply.setApproveTime(new Date()); applyMapper.updateById(apply); }

逻辑说明:审批前必须校验身份和状态双条件,这是防止越权审批和重复审批的基本保障。参数approveResult是布尔值,true代表同意,false代表不同意。

3.3 邮件系统的四箱模型:收件箱、发件箱、废件箱与查询

邮件系统是这份文档里用例最多的模块,覆盖了写邮件、收件箱、发件箱、废件箱、查询邮件、邮件帐号配置、删除邮件、群发邮件八个功能。核心玩法是四箱模型:收件箱存客户来信,发件箱存已发出的邮件,废件箱放删除的邮件,再加上一个查询功能做全局搜索。

四箱模型在数据表设计上有一个经典做法:不物理分区,而是在邮件表上加状态字段。

-- 邮件系统的表结构设计推演 CREATE TABLE crm_mail ( mail_id INT PRIMARY KEY IDENTITY(1,1), mail_to NVARCHAR(200) NOT NULL, -- 收件人地址 mail_from NVARCHAR(200) NOT NULL, -- 发件人地址 subject NVARCHAR(500), -- 主题 content NTEXT, -- 邮件正文 box_type TINYINT NOT NULL, -- 1=收件箱 2=发件箱 3=废件箱 owner_id INT NOT NULL, -- 邮件归属人 send_time DATETIME, read_flag TINYINT DEFAULT 0, -- 是否已读 deleted_flag TINYINT DEFAULT 0, -- 是否逻辑删除 permanent_flag TINYINT DEFAULT 0 -- 是否永久删除 );

参数说明:box_type是核心字段,决定了邮件显示在哪个箱体。收件箱和发件箱删除邮件时,不会物理删行,而是把box_type改成3移到废件箱;废件箱清空时才是物理删除或标记permanent_flag。这个设计的好处是误删可恢复,符合文档中“恢复邮件”“清空废件箱”的用例要求。

另外,发件箱支持“再次发送”,收件箱支持“导出Excel”“打印”,这些功能在实现时都很直接——导出用POI生成Excel,打印用浏览器的window.print()。

4. 从文档到落地:权限边界、数据模型与接口要求

4.1 权限设计:系统管理员、主管、业务员的数据隔离方案

文档反复强调三类角色的差异:系统管理员负责客户分类、用户信息管理、数据安全;主管能查看业务员记录的客户信息和日程安排,能做审批,能指派人员和目标;业务员只能管理自己的数据和日程。

这个权限模型要落地,需要解决两个层面的问题。第一是功能权限,什么人能进入什么菜单、点什么按钮。第二是数据权限,业务员查客户列表时只能看到自己名下或自己部门范围内的客户。

功能权限在J2EE时代常见做法是用Filter加上角色标识进行URL拦截。数据权限则在SQL层处理,比如客户信息管理模块中“销售部门只能对自己管理范围内的系统进行管理”,意味着客户表必须有归属人字段和归属部门字段。

// 数据权限过滤的三种策略(从简单到复杂) // 策略1:仅限本人(业务员) WHERE c.owner_id = #{currentUserId} // 策略2:本部门(主管查看部门下属业务员) WHERE c.dept_id IN ( SELECT dept_id FROM sys_dept WHERE parent_id = #{currentUserDeptId} OR dept_id = #{currentUserDeptId} ) // 策略3:全部数据(系统管理员) -- 不加过滤条件

我一般会在MyBatis的拦截器里根据当前用户角色动态拼接这些条件,而不是在业务代码里分散判断。这样做的原因是权限规则集中管理,后续调整管控级别只需要改拦截器逻辑。

4.2 数据模型设计:从ER图到业务表的推演

文档中提到了“数据概念结构图(实体—关系图)”,虽然正文没有给出具体图例,但结合用例描述可以反推核心实体:用户、角色、部门、客户、日程、邮件、申请审批、短信、客户分类。

这些实体之间的关系可以这样理解:一个部门有多个用户,一个用户拥有多个客户,一个客户有多个日程和邮件,一个用户发起多个申请、审批多个申请。客户分类是树形结构,系统管理员可以添加修改删除分类列表节点。

-- 客户信息表(核心业务表) CREATE TABLE crm_customer ( customer_id INT PRIMARY KEY IDENTITY(1,1), customer_name NVARCHAR(200) NOT NULL, -- 客户名称 customer_type TINYINT, -- 客户类型(关联客户分类表) contact_name NVARCHAR(100), -- 联系人 contact_phone NVARCHAR(50), -- 联系电话 owner_id INT NOT NULL, -- 归属人(业务员) dept_id INT NOT NULL, -- 归属部门 level TINYINT DEFAULT 1, -- 客户级别 source NVARCHAR(100), -- 客户来源 remark NVARCHAR(1000), -- 备注 create_time DATETIME DEFAULT GETDATE() );

客户信息管理是文档强调的重点模块,“贯穿于整个项目”,所以这张表的设计直接决定了后续所有关联功能的数据基础。

4.3 非功能性需求:安全性、界面要求与故障处理

文档的性能需求章节提到了数据精确度、时间特性、适应性,运行需求部分涉及安全性、用户界面、接口要求、故障处理。这类非功能性需求虽然不如用例生动,但在实施评审时是必选项。

界面要求部分特别有意思,文档列出了二十多个界面的操作细节,比如“添加发送短信”“查看已接收短信列表”“删除已接收短信”“查看审批列表”“添加审批信息反馈”“查看我的申请列表”等等。界面层的需求文档通常包含按钮位置、表单字段、列表展示方式。

安全性需求在文档中没有展开,但结合“系统管理员对用户权限进行设置”这个用例,可以推断系统需要支持用户密码管理、会话超时、操作日志记录。接口要求提到通过标准的WebService为其他软件系统提供统一的权限管理服务,这意味着系统不是一个孤立应用,而是未来CRM平台的一个基础服务。

5. 避坑与排查:老CRM需求文档落地时的5个经典翻车现场

5.1 用例编号混乱导致需求追溯困难

现象:需求评审时,开发按用例编号追踪功能,发现CRM119是部门管理、CRM301是客户分类管理,编号没有任何连续性,而且客户信息管理同时出现CRM201和CRM1.01两种写法。

原因:文档在多次迭代中由不同人补充需求,编号体系没有统一维护,导致追踪矩阵失效。

解决:落地前先建立需求追踪矩阵RTM,把所有用例重新编号,与模块、页面、测试用例一一对应。不要沿用文档里的原始编号作为代码注释或接口命名依据。

5.2 “能做成下拉列表的一定做成下拉列表”被过度解读

现象:文档里明确写着“能做成下来列表的选项一定做成下拉列表。方便用户”,结果前端把所有文本框都改成了下拉框,客户名称、邮件内容这种自由文本也被做成下拉框,数据录入效率反而变低。

原因:需求描述过于口语化,没有明确哪些字段适合下拉、哪些字段适合自由输入。

解决:整理一份枚举字段清单。客户分类、客户类型、日程类型、优先级、审批状态这些有限取值的字段做下拉;客户名称、日程主题、邮件正文这种无限取值的字段保留文本输入。下拉框数据来源统一从数据字典表读取。

5.3 数据权限只在页面隐藏,没在接口层管控

现象:销售业务员通过修改URL参数,访问到了其他业务员的客户详情页,越权数据暴露。

原因:页面按钮根据角色做了显示隐藏,但后端接口没有做数据行级过滤,接口层是无条件信任前端传参的。

解决:在Service层增加数据权限校验,查询客户详情时必须校验当前用户是否有权操作该客户。原则是前端控制可见性,后端控制数据边界。

5.4 邮件附件存储方式没提前规划

现象:邮件支持导出、打印、再次发送,但文档没有提及附件处理,上线后用户反馈发送含附件的邮件速度极慢,服务器磁盘占用飙升。

原因:附件直接存在数据库表里,大文件读写频繁导致性能瓶颈。

解决:附件走文件存储路径,数据库只存文件路径等信息。经验教训是这类隐含需求最好在需求评审阶段主动追问。

5.5 逻辑删除与物理删除混用导致数据混乱

现象:废件箱执行“清空”操作后,部分邮件还能在收件箱里出现,恢复功能时数据错乱。

原因:有的模块用deleted_flag做逻辑删除,有的模块直接执行DELETE语句物理删除,标准不统一。

解决:统一所有删除操作走逻辑删除,永久删除只允许在废件箱阶段执行,且需要二次确认。数据表增加deleted_flag和permanent_flag双字段,代码层用枚举约束删除行为。

6. 二次开发改造:把2009年的需求文档翻译成现代技术栈

如果你拿到的就是这份类似的需求文档,要把它改造成可运行的现代系统,我的建议是先别急着写代码,按照下面三个步骤走。

先把六十页文档重新拆成用户故事和验收标准。这个阶段的工作量大约占整个改造周期的百分之三十。把每个用例转换成“作为某角色,我希望执行某操作,以便达到某目标”,然后给每个用户故事标注优先级和依赖关系。日程安排、审批流、邮件系统这几个用例必须保持原有业务语义,因为销售流程一旦改得面目全非,业务方是不会买账的。

技术栈迁移方面,我一般会把原有的J2EE结构翻译成Spring Boot加MyBatis Plus的经典组合。JDK 1.5的代码全部重写,WebSphere的WebService接口改用RESTful方式暴露。数据库方面,保留客户、日程、邮件、审批四张核心表的结构设计,SQL Server 2000的语法需要转换到MySQL 8或PostgreSQL,比如分页方式要改、NTEXT类型要换成TEXT或JSONB。

邮件模块的改造值得单独讲一下。老系统的邮件收发是基于JavaMail直接对接邮件服务器的,现在更推荐用Spring Boot的邮件自动配置,配合OAuth2协议对接企业邮箱。文档中“废件箱可恢复”的需求,建议用软删除加回收站机制实现,不要物理删行。

权限这块我的经验是直接换用Spring Security加Sa-Token这类成熟框架,不建议自己写Filter拦截器。文档中“系统管理员可以在任何机构下创建用户,机构管理员可以在自己机构下新建用户”这个需求,翻译到现代框架里,就是基于RBAC模型的用户和机构管理,用数据权限注解加部门数据范围控制就能搞定。

从那以后,我每次拿到老旧的需求文档,都会强制走一遍“拆用例、列名词、画边界、定状态”四个步骤再动手写代码,这样既能保证需求不丢失,又不会让老文档的过时设计拖累新技术方案。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进