文件结构:
app | 业务功能逻辑 | |
bootstrap | 框架启动入口,autoload.php比较重要,读环境变量,以匹配对应的配置文件,里面的cache目录需要被php进程可读写,否则框架运行有问题。 app.php里的$app->conigureMonologUsing() 配置了monolog日志,影响全局的日志 Log::error/info/debug,值得详细看一下 | autoload.php还生成了唯一的请求ID, 做完这些动作后, require了vendor/autoload.php这个文件,最终require_once了vender/composer/autoload_real.php, 这里面有一串类似于"99020184a024a1be726c1b1d********" 这个类(32位字符串),其实是为了保证应用的安全性,这里面加载了所有的class,要么是vendor/composer/autoload_static.php下面的,要么是vendor/composer/autoload_classmap.php或者vendor/composer/autoload_namespaces.php, 然后调用register方法进行注册,然后把每个class对应的类文件通过require逐个加载进来.
app.php中使用singleton方法加载了App\Http\Kernel, App\Console\Kernel, App\Exceptions\Handler这几个单例,同时引入了Monolog库,format, logHandle, processor对应日志格式,日志文件,上下文处理等数据 |
config | 这里放置了整个项目的配置项 可以自定义的配置全部放到env目录中,并根据环境变量找对应的 .env.{APP_ENV},其他配置不要做自定义修改 | app.php: 一些应用的配置如时区地点语言日志,还有一些将被自动加载的provider类(Auth,Broadcasting,Cookie,Hashing,Mail,Queue,Redis,Session,Validation,View等), alias别名等 cache.php: 缓存的配置项, 默认的缓存driver, session缓存等 database.php: 数据库的配置项, 读写,host, port, 库名, user, password, 前缀, 严格模式等 session.php: session的配置项 queue.php: 队列的配置项 ... |
database | 目前没用,可以把数据库的变更放这里管起来,尽量用laravel的migration | |
public | web根目录,index.php是总的入口 | 注意执行业务逻辑脚本时,当前目录getcwd()即为public的目录,另外laravel中提供了很多目录的辅助函数 https://laravel.com/docs/5.6/helpers#method-app-path 这里 require __DIR__ . '/../bootstrap/autoload.php';//注册自动加载器 $app = require_once __DIR__ . '/../bootstrap/app.php'; //开灯,生成应用实例 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); //让应用跑起来,返回结果,终止进程
$response = $kernel->handle($request = Illuminate\Http\Request::capture());
$response->send();
$kernel->terminate($request, $response); |
resources | lang目录多语言没有用上,views目录保存了所有的邮件和短信模板 | AtsMail::makeContent() 是一个用模板+数据生成邮件内容的例子 |
routes | 没用上,基本都走模块里面的路由 | php artisan route:list 可以看到所有的路由,跑起来比较慢,因为子模块已经很多了 |
storage | 基本没用上,本地化里用来存上传的文件和图片,替代dfs | |
tests | 单元测试,好像有其他同事用了,我这里没用上 | |
vendor | 第三方库,composer包管理的内容 | 工程里带上了composer.phar文件,不用另行安装composer工具,如php composer.phar install 即可安装composer.json中的第三方包 |
app目录需要重点说一下:
Common | Base: 各个db连接的基础model,全局controller 全局service 全局exception的基类 Constants: 全局配置,基本从be沿用来的 Events/Jobs/Listeners/: 默认的,没用上,在模块里有另外在用的 Helpers: 我没用上,不太了解 Services:ES目录是同步基础数据到es搜索,Remote目录是各种rpc的client封装 Support/Utils:是各种全局的辅助类 VO:存放封装参数的数据结构 | |
Console | 命令行工具,很多不用的可以删了 LoadBatchData: ats4月份上线时使用的导数据脚本,具体的导数据逻辑都在TobData模块下,Load*的脚本基本都是导数据时候用的 EsClient: 配合es导数据使用的,里面dataDelivery/dataCandidate/dataStep仍然在,需要批量刷es数据时可使用 DbTool: 为了批量给tob_ats_*库执行sql,只有开发环境在用,运维不需要这个 Tool/ToolCommon: php artisan tool:common list 可以看到可用的命令,我自己在用的一些小工具 Temp/*:配合上线变更的临时脚本 | 命令行工具有时间迭代最好清理一下,不用的删掉,在用的归归类 |
Exceptions | Handler 很重要,全局的异常处理,封装后输出 | 有优化空间: 1 检查和屏蔽不应暴露给前端的异常信息 2 优化异常的日志,确保方便查问题 |
Events/Jobs/Mail/Traits | 没啥用 | |
Providers | 框架层面全局注册的一些类 EventServiceProvider: debug打开时,记录所有执行的sql CollectionServiceProvider: 往laravel的Collection注入自定义的方法,这里只是个举例,deprecated | |
Http | Kernel.php很重要,注入了所有路由的middleware,这里几乎没使用laravel的middleware,主要用到的路由现在都在模块TobAts的Http/Middleware/下面,值得重点看下 | 路由这里随着业务演进,不断增加新的进来,有整理优化空间 |
模块:
在app/Modules/*
TobAccount 账号相关 | AccountController 获取账号列表和组织结构 AccountSettingController 获取账号设置相关 | |
TobApp 对接沈旭的App端的接口 | | 面试签到等对接了,其他很多没用上,也有一些对接接口没写在这里 有优化空间 |
TobAts 流程相关 | Events/Subscribers 业务触发的事件,流程是(举例)
- app/Modules/TobAts/Providers/ModuleServiceProvider.php 中注册 EventServiceProvider
- EventServiceProvider中注册业务的AtsSubscriber
- AtsSubscriber中绑定了event和对应的handle方法
- 这里触发event和event的处理是同步的
Helpers
- AtsConfig/DeliverySourceConfig/EstimateConfig 流程相关配置
- AtsException 子模块自定义的异常
- AtsHelper 一些辅助函数
Http Jobs 分两类
- 一类配合定时任务,检查发邮件之类
- 一类是发起异步任务,通过消息队列供长进程在后台处理,如同步es搜索、主投评分之类
|
- 配置很多从be代码里照搬,可能有一些用不到的或者重复定义的,有整理和优化的空间。
- AtsSubscriber中注册的处理event的handle方法也写在这个类里了,可以重构优化到独立的handler类。
- laravel中event和job的概念有点容易混淆,我们这个工程中event更偏业务概念,如做完一个流程,触发发放积分的动作。因为event是同步,需注意事务的范围。
- 定时任务经常需要轮询tob_ats_*的数据库,轮询在基类AtsCronJob中,需要注意通过AtsMap::listByType(),防止在通用中访问到定制,或定制中访问到通用的数据库
- 处理异步任务的长进程,需要注意:1 异常处理,不应被异常导致退出。2 资源泄漏,至少包括内存和文件句柄。
|
TobCampusAts | 校招,不熟悉 | |
TobCandidate | 候选人管理,不太熟悉 | |
TobData | 数据迁移的业务逻辑。数据迁移因表结构和各部分业务逻辑差异较大,不能简单的用sql转换数据导入。大致概念是 1 从be和其他老的库,数据转为业务上抽象的model 2 按model再插入回新库 遇到的问题是 1 插入效率,按单个model逻辑上比较清晰,但数据量大时必须批量处理,此时model的批量处理脚本写起来比较繁琐 | 业务数据在业务之外的作用: 1 反映了系统运行的状况 2 为迭代扩展的功能提供支撑 3 成后续功能迭代的累赘和负担 |
TobForm | 面试登记表,包含了一个表单设计器,对应表 form_tpl_*, form_module_*, form_field_* - 一个module包括多个field - tpl为表单模板,一个tpl包括多个module - 具体结合业务后的数据存在 form_user_* - form_user_tpl 为从默认的tpl生成客户自己使用的表单 - form_user_tpl_data 为从表单保存的数据 | |
TobHomepage | 主页,不熟悉 | |
TobPosition | 职位模块 | |
TobResume | 简历模块 | |
代码风格
分库的设计 | 1 tobusiness.ats_map 定义了tob_ats_*业务库 2 tobusiness.account_map 定义了账号和业务库的对应关系,新用户第一次登录时在这里新增记录 3 ats_map目前只包括了通用和绿城,定制线其他项目好像有别的方式管理 |
请求处理的一般过程: | 1 请求的url匹配模块的Routes/api.php 2 对应的middleware处理认证和鉴权等 3 走对应的controller,controller中只处理参数校验,然后调service完成业务逻辑 4 调service,service调用其他service和model来处理主要业务逻辑 5 model映射到表,model可有自定义的方法处理自身表范围内的逻辑,但不可调用其他service |
参数校验 (抄阿里的) | 下列情形,需要进行参数校验: 1) 调用频次低的方法。 2) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。 3) 需要极高稳定性和可用性的方法。 4) 对外提供的开放接口,不管是 RPC/API/HTTP 接口。 5) 敏感权限入口。 下列情形,不需要进行参数校验: 1) 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。 2) 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底 层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所 以 DAO 的参数校验,可以省略。 3) 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数。 |
参数封装 |
- controller的处理请求的函数参数都从Request或FormRequest及继承类开始
- service的函数参数不允许直接传入外层的Requst,需根据业务逻辑传入明确的参数
- 参数数量过多时,为service的参数单独封装RequestVO
|
middleware相关 | 中间件这里跟安全通常有很大关系,也会影响到一些业务逻辑
- 所有的API接口应在各个模块的route.php中使用相应场景下的middleware
- 几个比较重要的middleware:
-
- TobAuth 要求用户经过登录认证
- TobCsrf 对post请求进行csrf校验,这里逻辑是登录时在browser记录csrf token,前端框架里封装所有的post请求时都会http header里带上,然后供后端比对校验
- TobToken 邮件、短信打开链接时使用,逻辑是业务场景生成链接时包含一个根据当前上下文生成的token,在TobToken中根据token查找当时的上下文,用于判断访问权限和业务逻辑
- TobInterviewAssistant/TobWechatReviewAuth 这两个是微信场景的,我不太熟
- noLoginAuth 直接信任链接中传入的user_id,并授权。一般为内部系统之间的调用
- 存在的问题:
- 登录日志审计目前好像是跨部门,可定期确认安全性上是否有疏忽
- TobCsrf没有对所有子模块使用,有安全隐患
- TobToken存在滥用,如从token打开的链接所包含的列表之类,目前也没对存token数据的表审计和过期清理,有优化空间。
- TobToken中增加独立的session维护token上下文
- 考虑某些场景,相同的上下文的token可允许被重复使用,减少token的生成
- 审计和过期清理线上的token表数据
- noLoginAuth 有安全隐患,应尽量往内部rpc上迁移,有很大的优化空间
|
日志的管理 | 1 日志使用了laravel+monolog,配置在bootstrap/app.php中,日志格式需要对照配置看下 2 本项目日志级别包括 debug/info/warning/error, debug日志和其他日志分开 3 记日志并没有很要求规范,总结一个大致的要求:
- 处理一个完整请求(web的、计划任务的、异步任务长进程的、命令行工具的),无论正常还是异常都应有日志,包括输入输出的概要、处理时长、上下文(请求来源/登录用户等)。
- 发生外部调用时(包括rpc和其他方式的外部调用)
- 发生数据库写操作时
- 发生比较耗时的操作,如批量读数据时
- 代码中比较重要的条件分支(如:if(缓存命中){返回缓存}else{执行其他逻辑})
- 业务场景屏蔽异常时
4 日志方面本项目当前以能够查找问题为基准,很有优化空间 |
Service | 本项目中Service大概分为三类:
- 对接controller调用,完成业务逻辑
- 供其他模块调用,作为全局公共service
- 供同模块其他service调用,作为模块内的公共service
这个分类并不严格,一个service类可能同时提供这些功用。一些大致的原则:
- 单个函数不能太长,需充分考虑业务逻辑的含义,划分子函数调用
- 注意子函数调用的层次关系
- 该设为private的设为private
- 重复逻辑该抽成公共函数的抽成公共函数
- 可能发生变化的地方抽出来成为函数
- 用laravel的依赖注入处理service之间的依赖关系,需注意循环依赖问题,因为脚本语言的限制,出现此问题时报错并不明显,暂时只能人为检查避免
|
数据库相关 | |
异常的管理: |
- 全局异常的基类在 app/Common/Base/TobException.php ,定义了一些全局通用的错误码
- 按子模块定义自己的异常,举例:TobAts的 app/Modules/TobAts/Helpers/AtsException.php ,定义自己的错误码和内容
- 调用 error 方法,用错误码、错误内容、自定义的错误内容,构造异常并抛出
- 在全局的 app/Exceptions/Handler.php render() 中会统一处理异常,这里对异常的日志处理有点繁琐,包括异常的文件、行号、栈信息等,有优化空间,直接调 Log::error($exception) 可能会更好
- 一些屏蔽异常的场景 ,也应Log::error($e),日志记录完整异常
|
转载于:https://my.oschina.net/u/3412738/blog/3009278