阅读更多

1顶
0踩

行业应用

原创新闻 饿了么推荐系统:从0到1

2017-01-20 10:41 by 副主编 jihong10102006 评论(0) 有5942人浏览
引用
本文由携程技术中心投递,ID:ctriptech。作者:饿了么数据运营部资深算法工程师陈一村,在携程个性化推荐与人工智能Meetup上的分享。陈一村2016年加入饿了么,现从事大数据挖掘和算法相关工作,包括推荐系统、用户画像等。

随着移动互联网的发展,用户使用习惯日趋碎片化,如何让用户在有限的访问时间里找到想要的产品,成为了搜索/推荐系统演进的重要职责。作为外卖领域的独角兽, 饿了么拥有百万级的日活跃用户,如何利用数据挖掘/机器学习的方法挖掘潜在用户、增加用户粘性,已成为迫切需要解决的问题。

个性化推荐系统通过研究用户的兴趣偏好,进行个性化计算,发现用户的兴趣点,从而引导用户发现自己的信息需求。一个好的推荐系统不仅能为用户提供个性化的服务,还能和用户之间建立密切关系,让用户对推荐产生依赖。

本次分享介绍饿 了么如何从0到1构建一个可快速迭代的推荐系统,从产品形态出发,包括推荐模型与特征工程、日志处理与效果评估,以及更深层次的场景选择和意图识别。

在携程个性化推荐与人工智能meetup上,已经就以上几部分做了整体上的说明,本文将就其中模型排序与特征计算的线上实现做具体说明,同时补充有关业务规则相关的洗牌逻辑说明,力图从细节上还原和展示饿了么美食推荐系统。

一、模型排序

1、设计流程
对于任何一个外部请求, 系统都会构建一个QueryInfo(查询请求), 同时从各种数据源提取UserInfo(用户信息)、ShopInfo(商户信息)、FoodInfo(食物信息)以及ABTest配置信息等, 然后调用Ranker排序。以下是排序的基本流程(如下图所示):
  • 调取RankerManager, 初始化排序器Ranker:
  • 1.根据ABTest配置信息, 构建排序器Ranker;
    2.调取ScorerManger, 指定所需打分器Scorer(可以多个); 同时, Scorer会从ModelManager获取对应Model, 并校验;
    3.调取FeatureManager, 指定及校验Scorer所需特征Features。
  • 调取InstanceBuilder, 汇总所有打分器Scorer的特征, 计算对应排序项EntityInfo(餐厅/食物)排序所需特征Features;
  • 对EntityInfo进行打分, 并按需对Records进行排序。

这里需要说明的是:任何一个模型Model都必须以打分器Scorer形式展示或者被调用。主要是基于以下几点考虑:
  • 型迭代:比如同一个Model,根据时间、地点、数据抽样等衍生出多个版本Version;
  • 模型参数:比如组合模式(见下一小节)时的权重与轮次设定,模型是否支持并行化等;
  • 特征参数:特征Feature计算参数,比如距离在不同城市具有不同的分段参数。

2、排序逻辑
对于机器学习或者学习排序而言, 多种模型的组合(Bagging, Voting或Boosting等)往往能够带来稳定、有效的预测结果。所以, 针对当前美食推荐项目, 框架结合ABTest系统, 支持single、linear及multi三种组合模式, 具体说明如下:
  • single:单一模式, 仅用一个Scorer进行排序打分;
  • linear:线性加权模式, 指定一系列Scorer以及对应的权重, 加权求和;
  • multi:多轮排序模式, 每轮指定Scorer, 仅对前一轮的top N进行排序。
具体说明如下:
单一模式:rankType=single
对于单一模式, 仅有一个Scorer, 且不存在混合情况, 所以只要简单对Scorer的打分进行排序即可, 故在此不做详细展开。ABTest配置格式如下表:

线性加权模式:rankType=linear

当LinearRanker初始化时, 会校验和初始化所有打分器Scorer。之后, 按照以下步骤对餐厅/食物列表进行排序, 详见下图(左):
  • 特征计算器InstanceBuilder调用ScorerList, 获取所有所需特征Feature并去重;
  • InstanceBuilder对所有餐厅/食物进行特征计算, 详见特征计算;
  • ScorerList中所有Scorer对所有餐厅/食物依次进行打分;
  • 对所有Scorer打分进行加权求和, 之后排序。

多轮排序模式:rankType=multi
对于多轮排序模式, 每轮设定一个Scorer, 对前一轮top=Num个餐厅/食物进行排序, 故在ABTest中需要设定每个Scorer的轮次(round)和排序数(num), 格式如下表。

MultiRanker初始化与特征计算与LinearRanker类似, 具体步骤详见上图(右):
  • 特征计算器InstanceBuilder调用ScorerList, 获取所有所需特征Feature并去重;
  • InstanceBuilder对所有餐厅/食物进行特征计算, 详见特征计算;
  • Scorer按轮次(round)对top=Num餐厅/食物进行打分;
  • 对top=Num餐厅/食物按当前Scorer的打分进行排序。
重复步骤3、4, 直到走完所有轮次。

在初始化阶段, Ranker根据ABTest配置信息指定算法版本(algoVersion)、排序类型(rankType)、排序层级(rankLevel)及相关打分器(ScorerList)。

3、模型定义
对于线上任何Model,ModelManager 都会通过以下流程获取相应实例和功能(如下图所示):
  • 模型实例化时的构造函数BaseModel()和校验函数validate();
  • 通过FeatureManager获取对应Model的特征Feature:abstract getFieldNames()/getFeatures();
  • 传入Model的特征, 获取预测分数:abstract predict(Map
对于Model的迭代和更新、以及之后的Online Learning等, 通过ModelManager对接相应服务来实现。

如上图所示, 对于任何一个可被Scorer直接调用Model, 都需要实现以下接口:
  • 可供ModelManager进行Model实例化的BaseModel() 和初始化的init()

  • 可供Scorer/InstanceBuilder获取特征项的 getFieldNames()/getFeatures();
  • 可供Scorer调用进行打分的 predict(Map<K, V> values) 和 predict(List<Map<K, V> values)&#8232。
二、特征计算

1、设计流程
不同于离线模型训练,线上特征计算要求低延迟、高复用、强扩展,具体如下:
  • 低延迟:针对不同请求Query,能够快速计算当前特征值,包括从各种DB、Redis、ES等数据源实时地提取相关数据进行计算;
  • 高复用:对于同类或者相同操作的特征,应该具有高复用性,避免重复开发,比如特征交叉操作、从USER/SHOP提取基本字段等;
  • 强扩展:能够快速、简单地实现特征,低耦合,减少开发成本。
根据以上系统设计要求, 下图给出了特征计算的设计流程和特征基类说明。

具体说明如下:
  • FeatureManager:特征管理器, 用于特征管理, 主要功能如下:
  • a.特征管理:包括自定义特征、基础特征、实时特征、复合特征等;
    b.特征导入:自定义特征静态代码注册,其他特征数据库导入;
    c.特征构建:CompsiteFeature类型特征构建。
  • InstanceBuilder:特征构建器, 用于计算餐厅/食物特征, 具体步骤如下:

  • a.从每个Scorer获取Feature列表, 去重, 依赖计算, 最后初始化;
    b.层级、并行计算每个EntityInfo的特征值(之后会考虑接入ETL, 用于Online Learning)。

2、特征定义
上图给出了特征基类说明, 以下是具体的字段和方法说明:
  • type: 特征类型, 现有query、shop、food, 表示Feature的特征维度
(粒度)
  • operate: CompositeFeature专属, 特征操作, 指定当前特征行为, 比如ADD、MAPGET等
  • name: 特征名称

  • weight: 权重, 简单线性模型参数
  • retType: 特征返回字段类型
  • defValue: 特征默认返回值

  • level: CompositeFeature专属, 当前特征层次, 用于特征层次计算
  • operands: CompositeFeature专属, 特征操作数, 前置特征直接依赖
  • dependencies: CompositeFeature专属, 特征依赖
  • *init(): 特征初始化函数
  • *initOther(QueryInfo): InstanceBuilder调用时实时初始化, 即传入当前特征参数

  • *evaluate(QueryInfo, EntityInfo, StringBuilder): 用于餐厅/食物维度的特征计算

根据上两小节设计流程和基类定义的说明, 我们能够非常快速、简便地实现一个自定义特征, 具体流程如下(score为例, 对应类名XXXFeature):
特征类实现:
  • 建立XXXFeature, 并继承BaseFeature/CompositeFeature;
  • 实现init(), 设置type\name(defValue\weight可选)等;
  • 实现initOther(), 设置特征参数, 包括infoMap;
  • 实现evaluate(), 具体包括特征计算的详细逻辑, 对于返回数值的特征。
特征注册:
  • 在FeatureManager中注册, 或者在后台特征管理系统中注册;
  • 考虑到代码中不允许出现明文常量, 故需在FeatureConsts中添加常量定义。

3、特征分类
1. 基础特征:
基础特征为线上可以通过配置特征名直接从SHOP/USER获取特征值的特征, 比如:shop_meta_、user_meta_、food_meta_等, 详细说明如下表,其从本质上来讲等同于特征操作符(复合特征)。

2. 实时特征:
实时特征来源于Kafka与Storm的日志实时计算,存于Redis,比如:用户食物搜索与点击信息,实例如下表。

3. 自定义特征:
线上除CompositeFeature特征外, 所有XXXFeature均为自定义特征, 在此不再累述。
4. 复合特征(CompositeFeature):
用户特征组合的复杂操作, 比如下表所示(部分)


三、洗牌逻辑
1、洗牌类型
很多时候, 基于算法模型的结果能够给出数据层面的最佳结果, 但是不能保证推荐结果符合人的认知, 比如基于CTR预估的逻辑, 在结果推荐上会倾向于用户已点过或已购买过的商户/食物, 这样就使得推荐缺少足够的兴趣面。所以, 为了保证推荐结果与用户的相关性, 我们会保留算法模型的结果; 同时, 为了保证结果符合认知, 我们会人为地添加规则来对结果进行洗牌; 最后, 为了扩展用户兴趣点、引导用户选择, 将会人工地引入非相关商户/食物, 该部分将是我们后续优化点之一。下面将详细介绍“猜你喜欢”模块线上生效的部分洗牌逻辑,其他洗牌规则类似。

餐厅类目洗牌:
考虑到餐厅排序时, 为避免同类目餐厅扎堆问题, 我们设定了餐厅类目洗牌, 基本规则如下:
引用
针对 top = SHOP_CATE_TOPNUM 餐厅, 不允许同类目餐厅连续超过 MAX_SHOP_SHOPCNT。


餐厅推荐食物数洗牌:
在餐厅列表排序时, 总是希望排在前面的商户具有更好的展示效果、更高的质量。针对 1*餐厅+3*食物 模式, 如果前排餐厅食物缺失(少于3个)时, 页面的整体效果就会大打折扣, 所以我们制定了食物数洗牌, 具体规则如下:
引用
所有1个食物的餐厅沉底;
针对top=SHOP_FOODCNT_TOPNUM餐厅, 食物数 < SHOP_FOODCNT_FOODCNT(3) 的餐厅降权

餐厅名称洗牌:
正常时候, 推荐需要扩展和引导用户的兴趣点, 避免同类扎堆, 比如盖浇饭类目餐厅等。同样的, 我们也不希望相同或相似名称的餐厅扎堆, 比如连锁店、振鼎鸡等。针对此问题, 考虑到餐厅名称的不规则性, 我们通过分词和统计, 把所有餐厅名称做了结构化归类(distinct_flag), 比如所有“XXX黄焖鸡”都归为“黄焖鸡”、“星巴克 XX店”归为“星巴克”等。之后类似于餐厅类目洗牌, 做重排, 具体规则如下:
引用
对top=SHOP_FLAG_TOPNUM 餐厅进行标签(flag)洗牌, 使得同一标签的餐厅排序位置差不得小于 SHOP_FLAG_SPAN

2、线上逻辑
从上一节中可知, 各个洗牌之间存在相互制约, 即洗牌不能并行、只能串行, 谁前谁后就会导致不同的排序结果, 所以, 这里需要考虑各个洗牌对排序的影响度和优先级:
  • 影响度:即对原列表的重排力度, 比如对于连锁店少的区域, 名称洗牌的影响度就会小, 反之, 比如公司周边有25家振鼎鸡, 影响度就会变大;
  • 优先级:即洗牌的重要性, 比如前排餐厅如果食物少于规定数量, 其实质是浪费了页面曝光机会, 所以食物数洗牌很有必要。
考虑到洗牌的串行逻辑, 越靠后的洗牌具有更高优先级。为了能够灵活变更线上的洗牌规则, 系统结合Huskar System(线上配置修改系统), 能够快速、便捷地更改洗牌逻辑,下面给出了一个配置实例。
[
{"name": "recfoods", "topnum": 15, "foodcnt": 3}, 
{"name": "category", "topnum": 15, "shopcnt": 2}, 
{"name": "shopflag", "topnum": 20, "span": 3, "exclude": "XXX"}},
{"name": "recfoods", "topnum": 15, "foodcnt": 3},
{"name": "dinner","topnum":5,"interval": ["10:30~12:30","16:30~18:30"]},
{"name": "mixture","topnum":12, "include": "XXX"}
]


四、总结
对于一个处于业务快速增长期的互联网企业,如何能够在最短时间内构建一个可快速迭代的推荐系统,是摆在眼前的现实问题。此次分享从饿了么自身业务出发,结合推荐系统的常见问题和解决方案,给出了从产品形态出发, 包括推荐模型与特征工程、日志处理与效果评估, 以及更深层次的场景选择和意图识别等在内多方面的线上实践,力图从整体及细节上还原和展示推荐系统的本质,以期能够为大家今后的工作提供帮助。
  • 大小: 87 KB
  • 大小: 4 KB
  • 大小: 4.5 KB
  • 大小: 110.2 KB
  • 大小: 4.7 KB
  • 大小: 103.5 KB
  • 大小: 97.8 KB
  • 大小: 15.8 KB
  • 大小: 9.2 KB
  • 大小: 37.8 KB
1
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 饿了么餐厅订单管理系统分析.pdf

    饿了么餐厅订单管理系统分析.pdf

  • 饿了么推荐系统的从0到1

    随着移动互联网的发展,用户使用习惯日趋碎片化,如何让用户在有限的访问时间里找到想要的产品,成为了搜索/推荐系统演进的重要职责。作为外卖领域的独角兽, 饿了么拥有百万级的日活跃用户,如何利用数据挖掘/机器...

  • 饿了么监控体系:从架构的减法中演进而来

    我差不多是2015年中加入饿了么,主要是负责饿了么整个监控平台的搭建,从0开始搭建这套监控系统。今天主要从以下四块给大家讲一下,整个过程我们遇到了哪些问题,怎么来解决这些问题,以及用怎么样的设计来支撑起这...

  • 从0到1:饿了么风控计数服务是如何炼成的

    引言2017年4月份从饿了么正式进入多活领域开始,也预示着饿了么业务开始迈入下半场,此时风控团队面临着严峻的挑战,风控需要在事前、事中、事后进行全方位的防御.而计数器的业务几乎贯穿了整个风控的需求,规则根据...

  • 饿了么外卖管理系统.zip

    饿了么外卖系统,c++语言编写,包含界面,分为用户端和商户端,支持注册登录可以实现菜单的增加删除,相关功能如下: 1)客户注册:客户通过添加必要的信息到数据库来完成注册。 ​ 2)客户信息修改:客户根据自己...

  • 推荐系统:数据与特征工程

    具体来说,我们会从推荐算法建模的一般流程、推荐系统依赖的数据源介绍、数据处理与特征工程简介、常用推荐算法之数据与特征工程、推荐系统数据与特征工程未来趋势等 5 个部分来介绍相关知识点,期望本章的讲解能够...

  • 饿了么api接口 php,饿了么接口:php

    首先要拿到两个参数consumer_key是对接饿了么开放平台的基本凭证,它代表的是一个品牌或个人实体。该品牌或个人的所有餐厅会关联在此key之下。consumer_secreteleme分配给APP的consumer_key2.制...

  • 饿了么交易系统 5 年演化史

    作者介绍:杨凡,花名挽晴,饿了么高级架构师,2014 年加入饿了么,2018 年随饿了么被阿里巴巴收购一同加入阿里巴巴,4 年团队管理经验,4 年主要从事饿了么交易系统建设,也曾负责过饿了么账号、评价、IM、履约交付...

  • 【SDCC讲师专访】饿了么毕洪宇:小析饿了么大数据平台从无到有到优的架构探索三味境...

    【SDCC讲师专访】阅文集团帅翔:从0到1落地分布式存储系统架构 【SDCC讲师专访】专访架构师薛珂:弹性调度平台Saturn的架构设计 【SDCC讲师专访】饿了么毕洪宇:小析饿了么大数据平台从无到有到优的架构探索三味...

  • 饿了么架构师黄晓路:从单体架构到亿级微服务架构演进实践

    点击“技术领导力”关注∆每天早上8:30推送作为一个互联网创业公司,饿了么从初创到壮大,在移动互联网时代,业务量和技术团队的体量经历了10倍增长,这其中的经历,是互联网领域许多创业公...

  • 2万字雄文:饿了么核心交易系统 5 年演化史!

    回到正题,在 2014 年 12 月~ 2014 年 4 月这几个月的时间里,我配合完成了一个更老的 BD 系统后端迁移到 Walis ,并且在我的导师转岗到 CI 团队后,自己完成了 Walis 从单应用迁移到分布式应用。 订单组的成立 对...

  • 堪比巨著:饿了么交易系统5年演化血泪史

    点击上方“朱小厮的博客”,选择“设为星标”后台回复&#34;书&#34;,获取后台回复“k8s”,可领取k8s资料来源:阿里巴巴中间件作者介绍杨凡,花名挽晴,饿了么高级架构师,2014 年...

  • 基于java的-28-“智慧食堂”设计与实现--LW-源码.zip

    提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

  • C#,回文分割问题(Palindrome Partitioning Problem)算法与源代码

    C#,回文分割问题(Palindrome Partitioning Problem)算法与源代码 1 回文串 “回文串”是一个正读和反读都一样的字符串,初始化标志flag=true,比如“level”或者“noon”等等就是回文串。 2 回文分割问题 给定一个字符串,如果该字符串的每个子字符串都是回文的,那么该字符串的分区就是回文分区。 例如,“aba | b | bbabb | a | b | aba”是“abababababa”的回文分区。 确定给定字符串的回文分区所需的最少切割。 例如,“ababababababa”至少需要3次切割。 这三个分段是“a | babbab | b | ababa”。 如果字符串是回文,则至少需要0个分段。 如果一个长度为n的字符串包含所有不同的字符,则至少需要n-1个分段。

  • node-v9.2.1.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 贪心算法解决活动选择问题,Java版源码

    贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法策略。贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是局部最优解可以决定全局最优解。 附件中一个使用贪心算法解决活动选择问题(也称为会议时间安排问题)的 Java 示例代码。这个问题的目标是选择最大的活动数量,使得活动之间互不重叠。 在示例中,我们定义了一个 Activity 类来表示每个活动的开始和结束时间。然后,我们创建了一个活动数组,并使用 Arrays.sort() 方法按照活动的结束时间对它们进行排序。 接下来,我们遍历排序后的数组,使用贪心算法的策略选择活动。选择的标准是当前活动的开始时间是否晚于或等于之前选择的最后一个活动的结束时间。如果是,我们就选择这个活动,并更新 lastActivityEnd 为当前活动的结束时间。最后,我们打印出可以执行的最大活动数量。

  • 小程序-2-“最多跑一次”微信小程序--LW-源码.zip

    提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

  • 基于java的-198-ssm智能新冠疫苗接种助手--LW-源码.zip

    提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

  • 酒店管理系统的设计与实现.zip

    本项目基于jsp框架,数据库采用mysql,采用tomcat服务部署,适合学生论文、课设参考及学习。 功能介绍: 1)基本操作 2)客户管理 3)客房管理 4)菜品管理 5)餐桌预定 6)餐饮消费管理

Global site tag (gtag.js) - Google Analytics