《C++新经典设计模式》之第12章 状态模式

《C++新经典设计模式》之第12章 状态模式

        • 状态模式.cpp

状态模式.cpp
#include <iostream>
#include <memory>
using namespace std;// 用类表示状态, 一般用于有限状态机
// 状态机3部分组成:状态(State)、事件(Event)、动作(Action)
// 事件(转移条件, 触发状态变化的行为或动作)发生时, 状态改变, 可能执行动作// 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类
// 3种角色
// Context(环境/上下文类),该类对象拥有多种状态以便对这些状态进行维护
// State(抽象状态类),定义接口以封装与环境类的特征状态相关的行为,声明不同状态对应的方法
// ConcreteState(具体状态类),实现与环境类该状态相关的行为namespace ns1
{class Monster // 怪物类{public:enum MonsterState // 怪物状态枚举值{MonS_Fer,  // 凶悍MonS_Worr, // 不安MonS_Fear, // 恐惧MonS_Dead  // 死亡};private:int m_life;            // 血量(生命值)MonsterState m_status; // 初始状态public:Monster(int life, MonsterState status = MonS_Fer) : m_life(life), m_status(status) {}public:void Attacked(int power) // 怪物被攻击。参数power表示主角对怪物的攻击力(即怪物丢失的血量){m_life -= power; // 怪物剩余的血量switch (m_status){case MonS_Fer:if (m_life > 400){cout << "monster " << power << " hurt, attact!" << endl;// 处理其他动作逻辑比如反击}else if (m_life > 100){cout << "monster " << power << " hurt, attact, help!" << endl;m_status = MonS_Worr;// 处理其他动作逻辑比如反击和呼唤支援}else if (m_life > 0){cout << "monster " << power << " hurt, MonS_Fear, run!" << endl;m_status = MonS_Fear;// 处理其他动作逻辑比如逃跑}else{cout << "monster " << power << " hurt, dead!" << endl;m_status = MonS_Dead;// 处理怪物死亡后事宜比如怪物尸体定时消失等}break;case MonS_Worr: // 目前怪物已经处于不安状态, 这说明怪物的血量<=400并且>100if (m_life > 100){cout << "monster " << power << " hurt, attact, help!" << endl;// 处理其他动作逻辑比如反击和呼唤支援}else if (m_life > 0){cout << "monster " << power << " hurt, MonS_Fear, run!" << endl;m_status = MonS_Fear;// 处理其他动作逻辑比如逃跑}else{cout << "monster " << power << " hurt, dead!" << endl;m_status = MonS_Dead;// 处理怪物死亡后事宜比如怪物尸体定时消失等}break;case MonS_Fear:if (m_life > 0){cout << "monster " << power << " hurt, run!" << endl;// 处理其他动作逻辑比如逃跑}else{cout << "monster " << power << " hurt, dead!" << endl;m_status = MonS_Dead;// 处理怪物死亡后事宜比如怪物尸体定时消失等}break;default: // 怪物已经处于死亡状态cout << "monster dead, cannot be attacketed!" << endl;break;}}};
}namespace ns2
{class Monster;      // 类前向声明class MonsterStatus // 怪物状态类{public:virtual ~MonsterStatus() {}virtual void Attacked(int power, Monster *const mainobj) = 0;};class MonsterStatus_Feroc : public MonsterStatus // 凶悍状态类{public:// 传递进来的参数是否有必要使用, 开发者自行斟酌void Attacked(int power, Monster *const mainobj) override{cout << "MonsterStatus_Feroc, attackt!" << endl;// 处理其他动作逻辑比如反击}};class MonsterStatus_Worr : public MonsterStatus // 不安状态类{public:void Attacked(int power, Monster *const mainobj) override{cout << "MonsterStatus_Worr, attackt, help!" << endl;// 处理其他动作逻辑比如反击和不停的呼唤支援}};class MonsterStatus_Fear : public MonsterStatus // 恐惧状态类{public:void Attacked(int power, Monster *const mainobj) override{cout << "MonsterStatus_Fear, run!" << endl;// 处理其他动作逻辑比如逃跑}};class MonsterStatus_Dead : public MonsterStatus // 死亡状态类{public:void Attacked(int power, Monster *const mainobj) override{cout << "dead!" << endl;// 处理怪物死亡后事宜比如怪物尸体定时消失等}};class Monster // 怪物类{int m_life;                         // 血量(生命值)shared_ptr<MonsterStatus> m_pState; // 持有状态对象public:Monster(int life, const shared_ptr<MonsterStatus> &pState = make_shared<MonsterStatus_Feroc>()) : m_life(life), m_pState(pState) {}public:void Attacked(int power){int orglife = m_life; // 暂存原来的怪物血量值用于后续比较m_life -= power;      // 怪物剩余的血量if (orglife > 400)    // 怪物原来处于凶悍状态{if (m_life > 400) // 状态未变{// cout << "monster " << power << "点伤害并对主角进行疯狂的反击!" << endl;m_pState->Attacked(power, this); // 其他的逻辑代码被本Monster类委托给了具体状态类来处理}else if (m_life > 100) // 状态从凶悍改变到不安{// cout << "monster " << power << "点伤害并对主角进行反击, 怪物变得焦躁不安并开始呼唤支援!" << endl;m_pState.reset(new MonsterStatus_Worr()); // 怪物转到不安状态m_pState->Attacked(power, this);}else if (m_life > 0) // 状态从凶悍状态改变到恐惧状态, 主角的攻击太恐怖了{// cout << "monster " << power << "点伤害, 怪物变得恐惧并开始逃跑!" << endl;m_pState.reset(new MonsterStatus_Fear()); // 怪物转到恐惧状态m_pState->Attacked(power, this);}else // 状态从凶悍改变到死亡{// cout << "monster " << power << "点伤害, 已经死亡!" << endl;m_pState.reset(new MonsterStatus_Dead()); // 怪物转到死亡状态m_pState->Attacked(power, this);}}else if (orglife > 100) // 怪物原来处于不安状态{if (m_life > 100) // 状态未变{// cout << "monster " << power << "点伤害并对主角进行反击, 并继续急促的呼唤支援!" << endl;m_pState->Attacked(power, this);}else if (m_life > 0) // 状态从不安改变到恐惧{// cout << "monster " << power << "点伤害, 怪物变得恐惧并开始逃跑!" << endl;m_pState.reset(new MonsterStatus_Fear()); // 怪物转到恐惧状态m_pState->Attacked(power, this);}else // 状态从不安改变到死亡{// cout << "monster " << power << "点伤害, 已经死亡!" << endl;m_pState.reset(new MonsterStatus_Dead()); // 怪物转到死亡状态m_pState->Attacked(power, this);}}else if (orglife > 0) // 怪物原来处于恐惧状态{if (m_life > 0) // 状态未变{// cout << "monster " << power << "点伤害, 怪物继续逃跑!" << endl;m_pState->Attacked(power, this);}else // 状态从恐惧改变到死亡{// cout << "monster " << power << "点伤害, 已经死亡!" << endl;m_pState.reset(new MonsterStatus_Dead()); // 怪物转到死亡状态m_pState->Attacked(power, this);}}else // 怪物已经死亡{// 已经死亡的怪物, 状态不会继续发生改变//  cout << "怪物已死亡, 不能再被攻击!" << endl;m_pState->Attacked(power, this);}}};
}namespace ns3
{class Monster;      // 类前向声明class MonsterStatus // 怪物状态类{public:virtual ~MonsterStatus() {}virtual void Attacked(int power, Monster *const mainobj) = 0;};class MonsterStatus_Feroc : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;};class MonsterStatus_Worr : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;};class MonsterStatus_Fear : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;};class MonsterStatus_Dead : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;};class Monster // 怪物类定义{int m_life;                         // 血量(生命值)shared_ptr<MonsterStatus> m_pState; // 持有状态对象public:Monster(int life, const shared_ptr<MonsterStatus> &pState = make_shared<MonsterStatus_Feroc>()) : m_life(life), m_pState(pState) {}public:void Attacked(int power) { m_pState->Attacked(power, this); } // 怪物被攻击public:int GetLife() const { return m_life; }                                               // 获取怪物血量void SetLife(int life) { m_life = life; }                                            // 设置怪物血量shared_ptr<MonsterStatus> getCurrentState() const { return m_pState; }               // 获取怪物当前状态void setCurrentState(const shared_ptr<MonsterStatus> &pState) { m_pState = pState; } // 设置怪物当前状态};void MonsterStatus_Feroc::Attacked(int power, Monster *const mainobj) // 凶悍状态类{int orglife = mainobj->GetLife(); // 暂存原来的怪物血量值用于后续比较if ((orglife - power) > 400)      // 怪物原来处于凶悍状态, 现在依旧处于凶悍状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Feroc, attackt!" << endl;// 处理其他动作逻辑比如反击}else{// 不管下个状态是啥, 总之不会是凶悍状态, 只可能是不安、恐惧、死亡状态之一, 先无条件转到不安状态去(不安状态中会进行再次判断)mainobj->setCurrentState(make_shared<MonsterStatus_Worr>());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Worr::Attacked(int power, Monster *const mainobj) // 不安状态类{int orglife = mainobj->GetLife();if ((orglife - power) > 100) // 怪物原来处于不安状态, 现在依旧处于不安状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Worr, attackt, help!" << endl;// 处理其他动作逻辑比如反击和不停的呼唤支援}else{// 不管下个状态是啥, 总之不会是凶悍和不安状态, 只可能是恐惧、死亡状态之一, 先无条件转到恐惧状态去mainobj->setCurrentState(make_shared<MonsterStatus_Fear>());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Fear::Attacked(int power, Monster *const mainobj) // 恐惧状态类{int orglife = mainobj->GetLife();if ((orglife - power) > 0) // 怪物原来处于恐惧状态, 现在依旧处于恐惧状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Fear, run!" << endl;// 处理其他动作逻辑比如逃跑}else{// 不管下个状态是啥, 总之不会是凶悍、不安和恐惧状态, 只可能是死亡状态mainobj->setCurrentState(make_shared<MonsterStatus_Dead>());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Dead::Attacked(int power, Monster *const mainobj) // 死亡状态类{int orglife = mainobj->GetLife();if (orglife > 0){// 还要把怪物生命值减掉mainobj->SetLife(orglife - power); // 怪物剩余的血量// 处理怪物死亡后事宜比如怪物尸体定时消失等}cout << "dead!" << endl;}
}namespace ns4
{class Monster;      // 类前向声明class MonsterStatus // 怪物状态类{public:virtual ~MonsterStatus() {}virtual void Attacked(int power, Monster *const mainobj) = 0;};class MonsterStatus_Feroc : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;public:static shared_ptr<MonsterStatus> getInstance(){static shared_ptr<MonsterStatus> instance(new MonsterStatus_Feroc());return instance;}};class MonsterStatus_Worr : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;public:static shared_ptr<MonsterStatus> getInstance(){static shared_ptr<MonsterStatus> instance(new MonsterStatus_Worr());return instance;}};class MonsterStatus_Fear : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;public:static shared_ptr<MonsterStatus> getInstance(){static shared_ptr<MonsterStatus> instance(new MonsterStatus_Fear());return instance;}};class MonsterStatus_Dead : public MonsterStatus{public:void Attacked(int power, Monster *const mainobj) override;public:static shared_ptr<MonsterStatus> getInstance(){static shared_ptr<MonsterStatus> instance(new MonsterStatus_Dead());return instance;}};class Monster // 怪物类定义{int m_life;                         // 血量(生命值)shared_ptr<MonsterStatus> m_pState; // 持有状态对象public:Monster(int life, const shared_ptr<MonsterStatus> &pState = MonsterStatus_Feroc::getInstance()) : m_life(life), m_pState(pState) {}public:void Attacked(int power) { m_pState->Attacked(power, this); } // 怪物被攻击public:int GetLife() const { return m_life; }                                               // 获取怪物血量void SetLife(int life) { m_life = life; }                                            // 设置怪物血量shared_ptr<MonsterStatus> getCurrentState() const { return m_pState; }               // 获取怪物当前状态void setCurrentState(const shared_ptr<MonsterStatus> &pState) { m_pState = pState; } // 设置怪物当前状态};void MonsterStatus_Feroc::Attacked(int power, Monster *const mainobj) // 凶悍状态类{int orglife = mainobj->GetLife(); // 暂存原来的怪物血量值用于后续比较if ((orglife - power) > 400)      // 怪物原来处于凶悍状态, 现在依旧处于凶悍状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Feroc, attackt!" << endl;// 处理其他动作逻辑比如反击}else{// 不管下个状态是啥, 总之不会是凶悍状态, 只可能是不安、恐惧、死亡状态之一, 先无条件转到不安状态去(不安状态中会进行再次判断)mainobj->setCurrentState(MonsterStatus_Worr::getInstance());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Worr::Attacked(int power, Monster *const mainobj) // 不安状态类{int orglife = mainobj->GetLife();if ((orglife - power) > 100) // 怪物原来处于不安状态, 现在依旧处于不安状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Worr, attackt, help!" << endl;// 处理其他动作逻辑比如反击和不停的呼唤支援}else{// 不管下个状态是啥, 总之不会是凶悍和不安状态, 只可能是恐惧、死亡状态之一, 先无条件转到恐惧状态去mainobj->setCurrentState(MonsterStatus_Fear::getInstance());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Fear::Attacked(int power, Monster *const mainobj) // 恐惧状态类{int orglife = mainobj->GetLife();if ((orglife - power) > 0) // 怪物原来处于恐惧状态, 现在依旧处于恐惧状态{// 状态未变mainobj->SetLife(orglife - power); // 怪物剩余的血量cout << "MonsterStatus_Fear, run!" << endl;// 处理其他动作逻辑比如逃跑}else{// 不管下个状态是啥, 总之不会是凶悍、不安和恐惧状态, 只可能是死亡状态mainobj->setCurrentState(MonsterStatus_Dead::getInstance());// mainobj->getCurrentState()->Attacked(power, mainobj);mainobj->Attacked(power);}}void MonsterStatus_Dead::Attacked(int power, Monster *const mainobj) // 死亡状态类{int orglife = mainobj->GetLife();if (orglife > 0){// 还要把怪物生命值减掉mainobj->SetLife(orglife - power); // 怪物剩余的血量// 处理怪物死亡后事宜比如怪物尸体定时消失等}cout << "dead!" << endl;}
}int main()
{
#if 0using namespace ns1;Monster monster(500, Monster::MonS_Fer);cout << "Monster, MonS_Fer, 500 blood!" << endl;monster.Attacked(20);monster.Attacked(100);monster.Attacked(200);monster.Attacked(170);monster.Attacked(100);monster.Attacked(100);
#endif#if 0using namespace ns2;Monster monster(500);cout << "Monster, MonS_Fer, 500 blood!" << endl;monster.Attacked(20);monster.Attacked(100);monster.Attacked(200);monster.Attacked(170);monster.Attacked(100);monster.Attacked(100);
#endif#if 0using namespace ns3;Monster monster(500);cout << "Monster, MonS_Fer, 500 blood!" << endl;monster.Attacked(20);monster.Attacked(100);monster.Attacked(200);monster.Attacked(170);monster.Attacked(100);monster.Attacked(100);
#endif#if 1using namespace ns4;Monster monster(500);cout << "Monster, MonS_Fer, 500 blood!" << endl;monster.Attacked(20);monster.Attacked(100);monster.Attacked(200);monster.Attacked(170);monster.Attacked(100);monster.Attacked(100);
#endifcout << "Over!\n";return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/216374.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

temu上传产品的素材哪里找

在为Temu&#xff08;拼多多跨境电商平台&#xff09;上传产品时&#xff0c;您需要准备一些高质量的素材&#xff0c;包括图片和视频。这些素材对于吸引用户的注意力、展示产品的特点以及提高购买意愿非常重要。但是&#xff0c;很多卖家都不知道从哪里找到这些素材。本文将为…

【Deeplearning4j】小小的了解下深度学习

文章目录 1. 起因2. Deeplearning4j是什么3. 相关基本概念4. Maven依赖5. 跑起来了&#xff0c;小例子&#xff01;6. 鸢尾花分类代码 7. 波士顿房价 回归预测代码 8. 参考资料 1. 起因 其实一直对这些什么深度学习&#xff0c;神经网络很感兴趣&#xff0c;之前也尝试过可能因…

SQL Sever 方式做牛客SQL的题目--SQL254

----SQL254 统计salary的累计和running_total 按照salary的累计和running_total&#xff0c;其中running_total为前N个当前( to_date ‘9999-01-01’)员工的salary累计和&#xff0c;其他以此类推。 输出顺序&#xff1a;emp_no salary running_total Demo展示&#xff1a; e…

Unity-小工具-LookAt

Unity-小工具-LookAt &#x1f959;介绍 &#x1f959;介绍 &#x1f4a1;通过扩展方法调用 gameObject.LookAtTarget&#xff0c;让物体转向目标位置 &#x1f4a1;gameObject.StopLookat 停止更新 &#x1f4a1;可以在调用时传入自动停止标记&#xff0c;等转向目标位置后自…

基于Python的教学流程自动化的设计与实现

&#x1f680;&#x1f680; 基于Python的教学流程自动化的设计与实现 Design and Implementation of Teaching Process Automation Based on Python 目录 目录 2 摘要 3 关键词 4 第一章 引言 4 1.1 研究背景与意义 4 1.2 研究目的 6 1.3 相关工作 7 1.4 论文结构 8 第二章 Py…

.net 洋葱模型

洋葱架构 内层部分比外层更抽象(内层接口&#xff0c;外层实现)。外层的代码只能调用内层的代码&#xff0c;内层的代码可以通过依赖注入的形式来间接调用外层的代码 简单的例子&#xff0c;引用依赖图 demo 接口类库 EmailInfo using System; using System.Collections.…

Python安装包(模块)的八种方法,Python初学者必备知识点

文章目录 1. 使用 easy\_install2. 使用 pip install3. 使用 pipx4. 使用 setup.py5. 使用 yum6. 使用 pipenv7. 使用 poetry8. 使用 curl 管道关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Py…

轻量封装WebGPU渲染系统示例<44>- 材质组装流水线(MaterialPipeline)之灯光和阴影(源码)

目标: 数据化&#xff0c;模块化&#xff0c;自动化 备注: 从这个节点开始整体设计往系统规范的方向靠拢。之前的都算作是若干准备。所以会和之前的版本实现有些差异。 当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sa…

apt-get update失败

一、先验证是否有网络 rootlocalhost:~# ping www.baidu.com ping: www.baidu.com: Temporary failure in name resolution rootlocalhost:~# 说明没有网&#xff0c;参考&#xff1a;https://blog.csdn.net/qq_43445867/article/details/132384031 sudo vim /etc/resolv.con…

代码随想录二刷 | 二叉树 |404.左叶子之和

代码随想录二刷 &#xff5c; 二叉树 &#xff5c;404.左叶子之和 题目描述解题思路递归法迭代法 代码实现递归法迭代法 题目描述 404.左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出…

使用node实现链接数据库并对数据库进行增删改查的后端接口

环境 node npm 编辑器 vscode 项目配置 新建目录 用vscode打开 终端输入 npm init -y npm install mysql npm install express 代码 安装好之后的代码页面 新建 在根目录新建api.js文件 const express require(express); const db require(./db/index); const app…

el-tree包含下级回显半选状态一些问题

上一篇文章如何在el-tree懒加载并且包含下级的情况下进行数据回显-02 说了一下在包含下级的时候&#xff0c;数据的回显&#xff0c;通过nodesMap进行赋值&#xff0c;这次说一下在做这个需求的过程中遇到的一些问题&#xff1a; loadNode(node, resolve) {// 处理回显主要是通…

13、RockerMQ消息类型之广播与集群消息

RocketMq中提供两种消费模式&#xff1a;集群模式和广播模式。 集群模式 集群模式表示同一个消息会被同一个消费组中的消费者消费一次&#xff0c;消息被负载均衡分配到同一个消费者上的多个实例上。 还有另外一种平均的算法是AllocateMessageQueueAveragelyByCircle&#xff…

CSS 的背景属性(开发中常用)

目录 1 内容预览 背景颜色 背景图片 背景平铺 背景图片位置(常用) 背景图像固定 背景复合写法 背景色半透明 实现案例 1 内容预览 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 注意&#xff1a; 把表格中的五个属背下来&#xff0c…

约数性质以及辗转相除法

文章目录 AcWing 869. 试除法求约数题目链接思路CODE AcWing 870. 约数个数题目链接思路CODE AcWing 871. 约数之和题目链接思路CODE AcWing 872. 最大公约数题目链接思路CODE AcWing 869. 试除法求约数 题目链接 https://www.acwing.com/activity/content/problem/content/9…

go集成nacos

1,go集成nacos 注册实例与注销实例 package mainimport ("fmt""github.com/nacos-group/nacos-sdk-go/clients""github.com/nacos-group/nacos-sdk-go/clients/naming_client""github.com/nacos-group/nacos-sdk-go/common/constant"…

【LuatOS】简单案例网页点灯

材料 硬件&#xff1a;合宙ESP32C3简约版&#xff0c;BH1750光照度模块&#xff0c;0.96寸OLED(4P_IIC)&#xff0c;杜邦线若干 接线&#xff1a; ESP32C3.GND — OLED.GND — BH1750.GND ESP32C3.3.3V — OLED.VCC — BH1750.VCC ESP32C3.GPIO5 — OLED.SCL — BH1750.SCL E…

AOP跨模块捕获异常遭CGLIB拦截而继续向上抛出异常

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、BUG详情 1.1 报错信息 1.2 接口响应信息 1.3 全局异常处理器的定义 二、排查过程 三、解决方案 四、总结 前言 最近&…

数据科学工作的20个Pandas函数(备忘)

Pandas 是数据科学社区中使用最广泛的库之一&#xff0c;它是一个强大的工具&#xff0c;可以进行数据操作、清理和分析。 本文将提供最常用的 Pandas 函数以及如何实际使用它们的样例。我们将涵盖从基本数据操作到高级数据分析技术的所有内容&#xff0c;到本文结束时&#xf…

Ubuntu 20.04安装禅道开源版

## Ubuntu 20.04安装禅道开源版 ## jerry 20231208 # 下载解压 cd /opt wget https://dl.cnezsoft.com/zentao/18.9/ZenTaoPMS-18.9-zbox_amd64.tar.gz tar xvzf ZenTaoPMS-18.9-zbox_amd64.tar.gz # 配置&#xff08;没啥可配的&#xff09; # 启动 启动所…