[关闭]
@andrewwang 2017-03-19T22:49:28.000000Z 字数 10172 阅读 2064

设计方案集合

方案


方案

登录注册

方案

  1. 登录注册的返回地址种类:
    平台内发起的请求,地址是内部跳转地址或者app的地址
    平台外发起的请求,是外部跳转地址
  2. 授权和手机注册后,用户就注册成功了。

注册方式

方式 默认信息要求 说明
手机号注册
app授权(微信QQ)
微信服务号授权

流程

  1. 用户未登录情况下点击了应用
  2. 携带应用号跳转到登录页面
  3. 登录,授权,注册。
  4. 如需要完善信息则跳转到完善信息页面(携带应用号)。如无必须的用户信息,则自动pass完善信息页面。
  5. 完善后跳转到应用页。地址流程如下:
    获取跳转应用页地址(动态地址通过后端获取)。前端处理本地址(iframe):
  1. if (非平台方应用 && !外部跳转地址)
  2. iframe

如是登录情况下,从第4步开始流程,开始前需提醒用户需补充的信息。

页面

登录注册页面:应用号,外部跳转地址(redirectUrl,外部发起的请求,无需页面头部和底部),内部跳转地址(url)
完善信息页面:同上。根据信息需求提示用户输入,已有的信息的不必显示。

后端接口

授权注册,手机注册
完善信息
用户信息符合检查:登录状态除外

账号合并

原则

  1. 手机和社交账号都是账户的信息,每个账户的每种类型信息都只有一个。
  2. 手机号只能绑定一个账户
  3. 支持账户合并:设置里可以账号绑定(提示要合并信息)
方式 手机号 说明
手机号注册
app授权(微信QQ)
微信服务号授权

系统内代付

产品设计

  1. 支持一个订单只有1个代付单(代付单和订单支付超时时间一致)。
  2. 如果代付发起后,请求方还可以发起微信或支付宝支付,同时,代付单会失效。
  3. 退款只能退款到支付账户中。
  4. 支付单的类型支持系统内代付。
  5. 代付单在“我的”里面,进入后可代付。

系统设计

代付单结构(BehalfPayOrder):请求方,代付方,代付金额,代付说明,请求方支付单,请求方订单号,代付方支付单,过期时间。
所需支持的表结构:请求方支付单(b),代付方支付单(p),代付单(df),请求方退款单(br),代付方退款单(pr)。
通过df能单向找到b和p,因为df是扩展功能。保证系统解耦。

流程

  • 发起代付:生成df(设置请求方订单号等)。
  • 代付去支付:无操作
  • 代付成功:生成b和p (状态都是支付成功),修改df(状态是完成, b和p的id)
  • 代付退款:正常退款流程(生成br和pr,修改订单状态)。
  • 发起后再自己支付:关闭df。

平台服务的设计

平台上每个服务都是个app。
app表结构:开发商(vendor),用户信息要求(userInfo),动态网址(isUrlDynamic),是否匿名,是否启用(isOn)。
开发商:开发商是平台的不用加iframe(平台开发的自带标准布局),其他都加iframe
用户信息要求:json格式。信息如mobile,idcard,address等。
每个app在使用前都会检查用户状态和信息要求。不符合则要完善。

广告多来源设计

广告项表结构:来源(custom, app),来源编号,网址,标题等
来源是custom,则使用广告项上的内容
来源是app,则用app的内容(如网址),广告项上的内容都无效。
后端接口:
app广告项的实现:返回应用号信息和应用url
app的动态网址获取:返回url

缓存

缓存key编号规则

规则:分类_参数。例如:
实体:id是1000的活动,event_1000
搜索结果:s_news-lastIndex-1-10-keyword。s_news-1200-1-10-iphone,新闻搜索结果,起始点是id是1200的新闻,第一页,每页是10个,关键字是iphone

读写机制

写:数据库
读:读缓存,没有读数据库(同时保存到缓存)

更新机制(废弃,用不到,缓存会自动过期)

多用于搜索结果。更新的最小时间单位minTime(如5分钟),更新的最小条数minCount(如十条)。缓存有效期是最大时间单位(如1小时)

  1. if(time < minTime)
  2. refresh = false;
  3. else if (count > minCount)
  4. refresh = true;
  5. // 其他情况下是缓存过期,会自动触发

消息&通知的持久化和推送

所有的都是消息,通知是因用户交互产生的(下单,订阅)
推送是种信息传输方式,有短信,邮件,app,微信等

通知推送原则:
1. 推送渠道
1.1. 有来源渠道(推送回来源渠道),比如微信下单,微信通知。需业务记录渠道号(如订单记录渠道是微信)
1.2. 来源渠道没有时,推送到常用渠道(通过记录用户登录和日常使用,来获取)
3. 部分通知必须同时短信通知。如重要的,后续用户需要使用到的。

短信短地址的处理:
短地址对应的页面:有打开app、下载app、关注APP。
打开app时会将业务参数传入app,这样app打开时自动到了特定业务页面。

持久化:
每个消息有持久化模板,通过消息表持久化(用户编号,消息分类,已读,status,标题,内容,网址)
逻辑:
if(持久化) {
消息.标题 = format(持久化模板)
save 消息 to 消息表
}

异常处理

  1. 处理包括页面和API接口
  2. 异常界面/提示的错误显示:
    1. 前端显示返回码的值
    2. 非生产环境所有错误,需显示错误信息(message)
    3. 非生产环境系统错误(繁忙和失败)和系统异常(繁忙),需显示错误详细信息(verbose)
  3. 返回码定义:
    1-499是通用系统错误
    500-999是通用业务错误
    1000以上是自定义业务错误
分类 名称 编码 说明 前端行为 后端 业务处理
系统 成功 SUCCESS 0 自定义 N
系统 繁忙 BUSY -1 系统异常 ? N
系统 失败 FAIL 1 各种错误原因 联系客服 N
系统 lock失败 LOCK_FAIL 2 资源锁定失败 系统繁忙,重新操作 N
系统 NEW操作频繁 OPERATION_FAST 3 操作太快了,在指定时间内(默认1秒)重复提交 Y
业务-通用 验证码错误 CAPTCHA_ERROR 300 自定义 Y
业务-通用 NEW验证码发送频繁 CAPTCHA_SEND_FAST 301 自定义 Y
业务-通用 NEW地址解析错误 ADDRESS_PARSE_ERROR 302 Y
业务-接口 数据不存在 NOT_EXIST 400 管理系统使用,RUD 自定义 Y
业务-接口 数据已存在 EXIST 401 管理系统使用,CREATE 自定义 Y
业务-接口 参数错误 PARAM_ERROR 410 比其他错误多一个错误参数编码(field) ? 错误参数编码(如bankCard),message(如为空/超过范围) Y
业务-接口 接口废弃 DEPRECATED 411 使用annotation实现 需升级app N
业务-接口 已完成 FINISHED 412 请求已完成,属于重复提交。先lock,再判断重复。 已完成 Y
业务-账户 登录失败 LOGIN_FAIL 500 自定义 Y
业务-账户 需登录 NEED_LOGIN 501 自定义 N
业务-账户 账户冻结 ACCOUNT_FROZEN 503 冻结警告 提供自定义提示信息 Y
业务-账户 账号警告 ACCOUNT_WARNING 506 多次登录失败警告 提供自定义提示信息 Y
业务-电商 没有地址 NO_ADDRESS 2001 Y

重复点击(后端)

将提交内容的md5结果,作为资源锁定。
在锁定期间的后续重复请求自动返回无效。

技术

事件处理

概要

通过消息队列完成
采用订阅模式:生产者发布消息,消费者订阅并处理
消息必须设置成持久化

MQ方案

架构

业务-生产者:{Biz}Service,如TradeService。
业务-消费者:{Biz}Handler,如IncomeHandler。
框架-业务:EventDispatchService(事件分发,可不用消息队列),MsgDeliveryHandler(信息投递消费者)
框架-基础:MQService(消息队列服务)

调用方(生产者)工作

{Biz}Service通过EventDispatchService发布消息
{Biz}Service提供指定日期范围内的业务消息

业务方(消费者)工作

  1. {Biz}Handler注册事件处理到EventDispatchService,通过EventDispatchService接收消息,处理消息
  2. {Biz}Handler定期查询负责的业务的消息是否都已消费,没有消费则加入到MQ等待处理

消息结构

事件:如trade.finish
data(json格式):数据。
原则:如果db有值(如订单),则使用id或者code。无值(如日志)则保存完整数据。
实例:id和code二选一

  1. {
  2. event: 'trade.finish',
  3. ver: '1',
  4. time: 'yyyy-MM-dd HH:mm:ss'
  5. data: {
  6. id: 123456,
  7. code: '123456'
  8. }
  9. }

使用案例

信息发送:如短信,微信通知等

事件:msg.delivery
data结构:

  1. {
  2. [{
  3. type: 'SMS',
  4. receivers: ['13916870669'],
  5. tpl: {
  6. code: 'tpl_23423423',
  7. text: '',
  8. params: {userName: '旺旺', expireSecond:'60'}
  9. }
  10. },
  11. {
  12. type: 'WX',
  13. receivers: ['453rsfstslf3rwfsfs'],
  14. tpl: {
  15. code: 'tpl_wx322345364',
  16. text: 'hello {userName}',
  17. params: {userName: '旺旺', expireSecond:'60'}
  18. }
  19. }]
  20. }

params支持扩展参数,比如阿里短信的默认前缀编码是ali_sms_prefix_code
如使用阿里短信,还需要配置默认签名,langya.sms.alibaba.prefix.default=阿里不知道啊

参考

RabbitMQ
MQ资料
RabbitMQ三种Exchange模式

支付

流程

客户端支付同步通知

  1. 调用后端API(检查,更新订单),返回支付结果
  2. 将API返回的结果告知用户

支付宝

app支付流程

微信

app支付流程
统一下单API
prepay(2b设计)解决方案
1. 2小时有效,如果支付时间少于2小时,可以存在订单上
2. 再次付款的解决方案

监控

监控表结构:监控url,监控内容(没有内容则监控http200),监控状态(正常,异常,确定异常),异常次数,目标类型,目标编号

异常次数越多,查询间隔时间越长,超过次数则确定异常。
确定异常后,周期查询是否正常,正常则恢复正常。
恢复正常:重置异常次数
当监控确定异常或者恢复正常时,通知目标。

版本发布

不管前端或者后端,版本定了后要发布。
特别是前端,需要发布一个uri存放特定版本,如/{codeName}/V。

页面缓存处理

每个js库有自身的版本号(config.js),如有更新,版本号+1。
项目也有自身的版本号(index.html),如有更新,版本号+1。
项目有版本信息(index.html的VERSION),会和版本号一起更新。
注意:项目只有在正式环境下时,文件才有缓存。
文件替换注意事项:强制全覆盖。

微信缓存解决(微信访问的第一个页面会做强制缓存,加随机数是无效的)
index.html页面上有个加载js文件,在js文件中加随机数加载其他js配置文件(确保本文件每次都被重新加载),至于其他js文件,如果没有版本更新,无需加载新的。

文件签名

3种方案:
1. 路径签名,如图片网址
2. 特征取样签名,按照文件尺寸等间隔取16个点,合并点值,签名。参考
3. 全部内容签名,参考
最终文件签名:类型(p,t,a)/签名。如p/guid,a/guid。

文件存储规范

系统环境(测试和正式)各自有域名和存储空间。定好后不能改变。
1. 存储空间使用规范
正式存储空间:静态文件(如logo,加载中等),正式的动态数据(如cms,广告等)
测试存储空间:测试的动态数据
2. 所有db内的url必须是绝对地址

自动计算方案

业务

通过公式计算结果
常用于收入计算,清分等

分工

计算方:基于对象数据计算结果
业务方:提供对象数据,提供时间范围内符合要求的待计算业务对象列表

流程

常规

业务方发起计算请求,计算方计算结果

确认

确保所有的业务对象都会处理
计算方周期发起请求给业务方,获取时间范围内符合要求的待计算业务对象列表,计算方重新计算

功能

支持线性公式和阶梯式公式
公式有code,支持公式嵌套
不支持公式集
不支持实时公式更新
支持模拟计算:需提供所有的输入参数

系统设计

第三方库有mvel、SpEL、Aviator,现采用SpEL
公式调用要提供:公式编码和参数列表(类型,编码)

表结构

公式表:编码,名称,输入参数列表(json:编码code,名称name,数据类型type),公式

公式

公式定义采用占位符参数形式。比如:
incoming1=#personCount * 1
incoming2=#saleFee * #percent
incoming=#fnCalcDecimal('incoming1', #root)+#fnCalcDecimal('incoming2', #root)
bonus=#fnCalcDecimal('incoming', #root) * 10%
total=#fnCalcDecimal('incoming', #root) + #fnCalcDecimal('bonus', #root)

公式嵌套

  1. #fnCalcDecimal('调用公式的编码', #root)

获取点附近信息点位

距离计算方案(网格计算)

常规方法是直接计算点和点之间的距离。精准度高,但计算量大。
现按照10米一网格将网格中心和点的距离保存,如下一个信息处于本网格中,直接使用本距离。显示误差是10米。
如误差容忍度更高,可以将网格调整的更高。

  1. 遍历全部,傻算
  2. 距离
    建立距离表:点,信息,距离(米,存储距离是2千米以内的数据)
    在点或者信息有CUD时,更新距离表
    附近信息可以快速通过距离表获取
  3. 网格化
    地图按照10米一格,做成网络。信息要有对应的网格。一个网格里的每个信息和点的距离是一样的。
    基于点算出距离内所有的网格,获取所有符合条件网格的信息。搜索匹配。
    例子:起点121.234234,10米一格,100个网格。第一个网格是[121.234234,121.234235),网格名称是121.234234
    格子数量估算:按照以下估算计算量也不少,不建议使用本方案。
    半径是100米:格宽度是50米,则需要8个格子。格宽度是20米,则需要40个?格子。
    半径是1000米:格宽度是50米,则需要800个?格子。二次方。
  4. 选定区域后计算
    选择点是中心的正方形(边长是2个半径),选出处于正方形区域内的信息,再排除超过半径的信息。这样可以大量减少计算量。
    本方案还可优化,比如先选择边长是一个半径的正方形区域先计算。但如果没有动态实时需求,不建议优化,使用缓存即可有大提升。

参考

API安全机制

API接口的安全级别

级别 机制 接口实例 说明
获取商品明细
普通 白名单,设置域名或者IP 获取商品明细 黑客可以模拟攻击
高级 商户:商户授权 商户账单获取
高级 用户:商户授权+用户令牌 订单获取

商户授权:
1. 存储位置
a. 后端(客户端被攻击成功也是临时的,提供前端暂时使用的令牌)
b. 前端(客户端被攻击成功就是永久获取)
2. 安全级别
a. 普通(key,可以存储在前端,如百度地图)
b. 高级(key+secret,只能存储在后端,如淘宝)

渠道的安全机制

渠道 采用机制 说明
自有网站 普通安全级别
第三方后端直连 高级安全级别,后端存储
原生APP 高级安全级别,前端存储 大部分app采用的方案,前端要确保秘钥安全
混合型APP 高级安全级别,前端存储不安全啊??? 调用原生函数实现签名?

测试

长期测试功能

有些功能是长期测试功能,通过用户的测试级别定。
用户的测试级别有3级

级别 范围 说明
1 测试部门
2 公司内部
3 公司外部

AB测试/灰度发布

  • 用户有功能版本列表(功能+AB版本),不同功能间的版本是独立的。
  • 功能版本列表存放在本地,如cookie。
  • 没有功能版本则使用功能前自动分配。
  • 分流控制:等比例分配。如2个版本各是50%,5个版本各是20%。
  • 每个功能版本都有其页面版本包(页面和逻辑)。通过用户版本自动加载不同的页面和逻辑。
  • 测试完成后,可以全部使用选中的版本。

功能逐级发布(facebook的机制,gatekeeper)

方案

本质上是个功能的路由器,可重定向,可关闭。
每个功能都有发布级别,上下线
分流控制:可指定特定地域,用户级别等
每个发布控制点关联一个功能编码
功能的发布级别是可以管理配置的。支持手工调整和自动调整(如3天升一级)
用户选择随机性确保:功能编码做随机数种子。

发布级别

级别 范围 说明
1 测试人员
2 公司内部 内测
3 1% 外部小范围
4 5% 外部大范围
5 100% 发布

流程

前端一次性下载(周期性更新):用户级别,功能级别(发布中的功能,不是全部功能)。
用户级别足够,则显示功能,否则隐藏。

参考

AB测试和灰度发布平台架构设计和实践

通讯接口的数据结构调整后的兼容方案

老数据的处理方案:如无历史数据压力,推荐方案1
方案1:调整老结构成新结构
数据持久化的是新结构。
老接口做结构转换(新结构转换成老结构)
新接口按照新结构开发

方案2:新老接口同时兼容新老结构
新数据持久化的是新结构。
老接口兼容新老结构
新接口兼容新老结构

组合枚举

常规枚举:单选模式。
组合枚举:多选模式。值是2的倍数,可同时有多个枚举值。比如3就是选择了1和2。

  1. enum Styles {
  2. ShowBorder = 1, //是否显示边框
  3. ShowCaption = 2, //是否显示标题
  4. ShowToolbox = 4 //是否显示工具箱
  5. }

keyvalue持久化

keyValue持久化表(kv),有过期时间。
如有重复则重新生成key(可以不用锁,冲突概率忽略不计)。

  1. if (key exist)
  2. return false;
  3. else
  4. insert

短链接地址(ShortUrl)

短地址是keyvalue持久化的一种应用场景。

转换

可使用t.cn之类的服务

短信注意事项

因第三方短信服务商要检查链接的域名许可。所以只能使用自己的域名。

方案

  • 地址路径:域名/t/短地址编码
  • 短地址编码:6位的字母数字
    配置项:http://www.xyz.com/t/{0}

流程

  1. 生成短地址编码,和网址一起insert到表kv

数据导入导出

都在内存操作,不需要存储到文件系统。
导入:前端输入导入文件,后端直接在内存里用模板文件生成。模板文件在类启动时自动加载到内存。
导出:前端输入查询条件,后端获取数据,返回内存文件。

文件存储时的冗余文件处理

冗余说明:文件上传了,但最后没有用到,放弃了。
冗余实例:修改头像时,未保存的文章,草稿文章删除,文章删除。

七牛支持给每个文件附加数据
方案(未考虑删除):
1. 实时操作
1.1. 标识模式:文件先上传,文件标识成临时使用。在“保存”时标识成永久使用。
1.2. 数量模式:文件先上传,文件使用数是0。在“保存”时使用数+1。
缺陷:要精准使用数成本太高。
1.3. 临时目录模式:文件先上传到存储系统的临时目录,在“保存”时迁移到正式目录。
缺陷:迁移的同时需要修改文件url。用户需要重开编辑界面编辑(临时文件url已经无效)。
临时的打标签
2. 周期清理
列出所有使用到的存储文件,比对存储系统上的列表。清理差异。
缺陷:不适用于大型系统。

流程:TODO
1. 前端设置两个路径,前端/后端返回存储服务的路径清单。
2. 前端/后端比对路径差异,差异文件上传到存储服务。
3. 刷新存储服务的CDN。

缓存(性能)TODO

列表:
lastindex确保结果唯一
新增:系统周期的自动更新到lastindex
修改:对象动态自动更新
删除:不管,会自动去除

搜索结果:
累计次数,无效之?

分组和标签TODO

分组用于商品等,标签用于订单、活动等

实体通用属性的适用场景

属性 适用场景 例子 反例
CreateTime 关注时间的 订单 键值
Status 有状态的,支持逻辑删除的 商品 公式

图片上传

支持图片多选/单选,编辑

方案1:七牛组件
方案2:自建
app:前端调用原生选图编辑图。原生提交编辑后的图片本地路径到前端,前端上传到后端,后端处理后返回图片地网址给前端。
微信:用js组件

emoji

方案(移动端)

手机起始页面方案

ios的预置第一页就是欢迎页

类型 说明 内容 数量
引导页 安装后显示一次 功能介绍 2-3
欢迎页 每次显示 产品简介,文字类或者一个简单图片 1
广告页 广告需求才显示 0-1

页面行为和行为流程设计(重点是后退)

页面行为

产品页面:

类型 底栏按钮 后退按钮
根页面
其他页面

深层次的页面,需要能退出到首页吗?

第三方应用页面(页面或者iframe):

类型 设备 底部按钮 后退按钮 退出按钮(第三方应用)
根页面 Android
其他页面 Android 有(点击后回应用首页)
根页面 iOS
其他页面 iOS 有(点击后回应用首页)

页面配置

配置 类型 说明
根页面 bool
预置的后退页面 string
保留状态 bool 当前页面所处位置记忆

后退说明

后退发起的页面会自动从url列表清除
跳转到的页面,是需要不支持后退,底层支持情况如下:
1. app可控制后退(含内置浏览器),达到要求
2. 浏览器或者微信浏览器无法控制后退,不行

后退流程

  1. if (有预置的后退页面)
  2. 后退到预置的后退页面
  3. else
  4. 后退到上个页面

页面后退

浏览器的后退是无法控制,所以我们要能获悉它的后退。
获悉方法:通过自己创建的页面堆栈来辅助。如当前render页面是堆栈最后一个,就是后退。如果是ABA(B跳转到A),无法判断是后退还是跳转你。

页面状态记忆

需要记忆的页面:重点页面,列表页

模块(列表)容器记忆的方案(另外方案是记录可允许记忆的最大容器数量):
1. 每个模块板块都有容器列表
2. 容器输入参数:是否清除记忆
3. 容器定义参数:功能板块名称,容器名称,记忆方式(永久,临时,不记忆)
4. 明细页:记录变量“是否清除记忆”(如果来源页是其对应的列表页,则是true),变量作为参数传递到下个页面
5. 处理流程(render):

  1. 赋值当前功能板块
  2. if (是否清除记忆)
  3. 删除当前容器的记忆
  4. 遍历容器列表
  5. if (容器.记忆方式 == 临时)
  6. if (容器.功能板块名称 != 当前功能板块)
  7. 删除容器的记忆
  8. 继续 render
  9. if (容器.记忆方式 != 不记忆)
  10. 记忆当前容器

二维码扫描

扫描结果是网址(为了支持微信扫描,必须是网址),方案有2个,推荐使用方案1。
方案1:
静态,如http://www.xyz.com/item/123456http://www.xyz.com/event/123456
处理方式:
1. 微信处理:直接跳转
2. app处理:绝对匹配“http://www.xyz.com/”,前端处理参数,跳转到指定路由。
方案2:
动态,如http://www.xyz.com/scan?type=item&id=123456
处理方式:
1. 微信处理:前端处理参数
2. app处理:绝对匹配“http://www.xyz.com/scan”,前端处理参数。
http://www.xyz.com/scan?type=item&id=123456转换成
http://www.xyz.com/item/123456
http://www.xyz.com/event/123456

参考

利用简单的事件驱动组件简化系统

国家行政结构

  • 结构:省/直辖市 市 区/县 街道/镇 居委 社区
  • 实例:上海市 上海市 黄浦区 南京东路街道 南东一居委 人民一村
    浙江省 台州市 椒江区 路东街道 唐镇3居委 华唐新村
  • 省市区选择的联动逻辑:
    “省/直辖市”选择后,如果选的是直辖市,“市”直接选中且不能修改。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注