OOP 迭代二

写在前面

同学们,我们用了两周时间完成了 TMS 的部分功能,包括注册、登录、打印信息等。在 TMS-2 中,我们将会实现上架商品、购买商品等一系列相关命令。

在开始编码之前,建议大家先仔细阅读需求说明和文末的 HINTS

编写好的代码需要经过 patpat 评测。通过所有的测试点后,需将整个项目文件打包上传到云平台,即可完成本次实验。希望大家能够认真完成,不作弊不抄袭

此外再次说明,对于各类导致 WA 和 RE 的逻辑漏洞,助教们在原则上不能为你提供太多帮助,更不能提供测试数据。另一方面,试想每个同学都发一个上百行上千行的项目文件,助教也无从看起。每个测试点都有很多命令,如果助教要面向测试点 debug,也只能一条一条复制粘贴,效率自然非常低下。建议同学们在编码时严谨地考虑各类情况,对于题目表述有不确定的,不妨问问其他同学或直接和助教交流,你的问题很可能被写进 QA 文档,这也为更多同学提供了帮助🤔。

对于每一种“合法情况”,同学们应当认真考虑其反面。例如,“可含前导零的五位整数”,其反面可以是 1234,可以是 123456,也可以是 12a34_+$%& 等非数字输入,这些都应该考虑到位,不然不仅可能误判,还可能导致参数分隔错误、参数数量不一致、库方法抛异常等更让人摸不着头脑的 bug。另,下文中的“字母”如果不加说明,大小写均可。

patpat 的下载和使用说明请参考:patpat 使用指南


实验说明

一、实验目标

  1. 熟练使用继承与多态。
  2. 了解方法的重载。

二、实验的重难点

  1. 对于相同命令的不同格式做好相应的处理。
  2. 对于复杂的功能,思考如何将其拆分为合理的、相对简单的小功能,以便于实现。
  3. 对于一个方法是定义在实体类中还是工具类中,一个功能直接通过一个方法实现还是将某些代码拆分出去之类的问题,可以从类的封装性以及代码的复用性两个方面进行考虑。合理的代码结构既可以减少 bug 的产生,也方便继续迭代修改。

三、关键技术

  1. 集合类的排序方法。

四、相关资料

  1. 常见问题解答 Q & A
  2. Java 8 - 17 新特性总结(其中的 StreamCollection 很有用)
  3. 对于商品类的设计,可以选择参考享元模式。感兴趣的同学可以看这个链接。如果觉得这个概念有些抽象也没有关系,我们在 HINTS 中也有讲解。

五、DDL及其他要求

  1. 实验持续 3 周,截止时间为 10 月 30 日 23:59,具体时间以云平台为准。
  2. 注意输出是否有空格,建议复制粘贴标准输出。

鉴于迭代二很多同学反馈难度较大,我们将截止时间延长至 10 月 30 日 23:59。希望大家能够抓紧时间完成,不要拖到最后一天。


实验内容

一、命令概览

本次迭代需要完成的命令如下

命令符 说明
registerShop 店铺名称 注册店铺
putCommodity 可选参数 上架商品
listShop 可选参数 查看店铺
listCommodity 可选参数 查看商品
searchCommodity 查找商品
buyCommodity 购买商品
removeCommodity 下架商品
cancelShop 注销店铺

二、功能描述

1. 注册店铺

店铺

  1. 身份为 Merchant 的用户(商家)可以注册店铺,每个商家可注册最多 5 家店铺。对于同一商家,店铺名称不可重复。
  2. 每个店铺都有一个编号,编号格式为 S-数字,例如 S-1S-2,数字无前导零。店铺编号是全局的:从 1 开始分配,若当前系统仅有 A、B 两个商家,A商家先注册了 4 家店铺,那么当B商家注册第一家店铺时,其店铺编号分配为 S-5。特别, S-0 是不合法的。
  3. 注销店铺的编号不会再重新分配。
  4. 店铺有两种状态:已注册(open),已注销(closed)。

1.1 格式说明

命令符 参数1
registerShop 店铺名称

店铺名称

  1. 由英文字母、连接符-、下划线_组成。
  2. 第一位必须为英文字母。
  3. 长度在 1~50 个字符之间。
  4. 同一商家名下的所有店铺名称不可重复,不同商家的店铺名称可以重复。

1.2 命令反馈说明

1.2.1 注册店铺成功

1
Register shop success (shopId: S-X)

其中X为店铺编号的数字。

1.2.2 参数数量不合法

1
Illegal argument count

1.2.3 未登录

1
Please log in first

1.2.4 登录用户身份不是Merchant

1
Permission denied

1.2.5 拥有店铺已达最大数量

1
Shop count reached limit

1.2.6 店铺名称不合法

1
Illegal shop name

1.2.7 店铺名称已存在

1
Shop name already exists

2. 上架商品

(建议阅读 HINTS,理清商家、店铺、商品三者的关系)

商品

  1. 身份为 Merchant 的用户(商家)可以在店铺中上架商品。
  2. 每种上架的商品都有一个编号,编号格式为 C-数字,数字无前导零,例如 C-1C-2。该编号根据商品上架的顺序获得,从 1 开始。商品编号同样是全局的 。
  3. 下架商品的编号不会再重新分配。
  4. 商品有两种状态:已上架(available),已下架(unavailable)。
  5. 你需要特别注意:商品的编号隶属于商家,假设商家 A 已有商品 C-1,商家 B 不能上架 C-1,但是可以上架一个和 C-1 具有相同名称、单价的商品(上架新商品),这个新商品会有一个新编号,并隶属于商家 B。理解这一点对于后面的“下架商品”至关重要。

2.1 格式说明

该命令有两种格式:一种为上架新商品,一种为上架已有商品。

上架新商品

命令符 参数1 参数2 参数3 参数4
putCommodity 店铺编号 商品名称 商品单价 商品数量

该命令用于上架一款全新的商品(生成一个新的商品编号)。

店铺编号

  1. 该商家已注册的店铺编号。

商品名称

  1. 由英文字母、连接符 -、下划线 _ 组成。
  2. 第一位必须为英文字母。
  3. 长度在 1~50 个字符之间。

商品单价

  1. 商品单价必须大于 0,小于等于 99999999.99。
  2. 商品单价最高保留到小数点后两位(可以为整数、保留一位、保留二位,但是不能有更多位)。

商品数量

  1. 商品数量必须大于 0。

  2. 商品数量必须为整数,不能有前导零。

对于是数字的参数,测试用例保证参数为数字格式,转换时无需特殊考虑。

上架已有商品命令

命令符 参数1 参数2 参数3
putCommodity 店铺编号 商品编号 商品数量

需满足要求如下

  • 该店铺已上架此商品,相当于补充商品数量。
  • 该店铺未上架此商品,此商品在该商家其它店铺上架过。即商品状态仍为已上架。

店铺编号

  1. 该商家已注册的店铺编号。

商品编号

  1. 该商家已上架的商品编号。

商品数量

  1. 商品数量必须大于 0。
  2. 商品数量必须为整数,不能有前导零。

2.2 命令反馈说明

上架新商品

2.2.1 上架商品成功

1
Put commodity success (commodityId: C-X)

其中X为商品编号的数字。

2.2.2 参数数量不合法

1
Illegal argument count

2.2.3 未登录

1
Please log in first

2.2.4 登录用户身份不是 Merchant

1
Permission denied

2.2.5 店铺编号不合法

1
Illegal shop id

2.2.6 店铺未注册、店铺不属于该商家、店铺已注销

1
Shop id not exists

2.2.7 商品名称不合法

1
Illegal commodity name

2.2.8 商品单价不合法

1
Illegal commodity price

2.2.9 商品数量不合法

1
Illegal commodity quantity

上架已有商品

2.2.10 上架商品成功

1
Put commodity success (commodityId: C-X)

其中X为商品编号的数字。

2.2.11 参数数量不合法

1
Illegal argument count

2.2.12 未登录

1
Please log in first

2.2.13 登录用户身份不是 Merchant

1
Permission denied

2.2.14 店铺编号不合法

1
Illegal shop id

2.2.15 店铺未注册、店铺不属于该商家、店铺已注销

1
Shop id not exists

2.2.16 商品编号不合法

1
Illegal commodity id

2.2.17 商品编号未注册、商品编号不属于该商家、商品已下架

1
Commodity id not exists

2.2.18 商品数量不合法

1
Illegal commodity quantity

3. 查看店铺

3.1 格式说明

命令符 可选参数
listShop [Kakafee 卡号]
  • 无参数:用户查看所有店铺,商家查看自己的所有店铺,管理员查看所有店铺。
  • 参数为 Kakafee 卡号:管理员查看 Kakafee 卡号对应商家的所有店铺。

3.2 命令反馈说明

3.2.1 成功打印信息

每家店铺占一行,按店铺编号由小到大输出。

顾客与商家打印格式为 店铺id 店铺名称

1
2
3
# 举例
S-1 Buaa
S-3 Al_1

管理员打印格式为 Kakafee卡号 店铺id 店铺名称

1
2
3
# 举例
000117871002 S-1 Buaa
000117871002 S-2 QLBL

无参数

3.2.2 参数数量不合法

1
Illegal argument count

3.2.3 未登录

1
Please log in first

3.2.3 店铺不存在

对顾客和管理员而言是当前系统无店铺,对商家而言是名下无店铺。

1
Shop not exists

参数为 Kakafee 卡号

3.2.4 参数数量不合法

1
Illegal argument count

3.2.5 未登录

1
Please log in first

3.2.6 非管理员用户执行有参数的命令

1
Permission denied

3.2.7 Kakafee 卡号不合法

1
Illegal Kakafee number

3.2.8 Kakafee 卡号对应的用户未注册

1
Kakafee number not exists

3.2.9 Kakafee 卡号对应的用户身份不是 Merchant

1
Kakafee number does not belong to a Merchant

3.2.10 Kakafee 卡号对应的商家名下无店铺

1
Shop not exists

4. 查看商品

4.1 格式说明

命令符 可选参数
listCommodity [店铺编号]
  • 无参数:顾客和管理员查看所有商品,商家查看自己的所有商品。
  • 参数为店铺编号:顾客和管理员查看任意某店铺的商品,商家查看自己某一店铺的商品

4.2 命令反馈说明

4.2.1 成功打印信息

每件商品占一行,按照 shopId 从小到大输出;对于同一商店的商品,按照 commodityId 从小到大输出。格式为 店铺id: 商品id 商品名称 商品价格 商品数量,其中,商品价格保留两位小数,单位为 yuan。

提示

库存为 0 的商品也需要打印,不打印该店铺被下架的商品。(但是查找商品时库存为 0 的商品不打印。)

1
2
3
4
5
6
7
# 举例
# (按照 shopId 由小到大的顺序,先打印编号为 1 店铺的商品信息)
S-1: C-2 book 10.00yuan 999
S-1: C-4 pencil 2.00yuan 20
# ... (按照 commodityId 的顺序打印该商店其他商品信息)
S-2: C-1 potato 3.50yuan 50
S-2: C-3 tomato 1.20yuan 40

无参数

4.2.2 参数数量不合法

1
Illegal argument count

4.2.3 未登录

1
Please log in first

4.2.4 当前系统无商品、商家名下店铺无商品

1
Commodity not exists

参数为店铺编号

4.2.5 参数数量不合法

1
Illegal argument count

4.2.6 未登录

1
Please log in first

4.2.7 店铺编号不合法

1
Illegal shop id

4.2.8 店铺不存在

对管理员和顾客而言是店铺未注册或已注销,对商家而言是店铺不属于该商家店、店铺未注册或已注销。

1
Shop id not exists

4.2.9 当前店铺无商品

1
Commodity not exists

5. 查找商品

5.1 格式说明

命令符 参数1
searchCommodity 商品名称
  • 顾客、管理员可以搜索所有商品。
  • 商家只能搜索自己商店的商品。
  • 剩余数量为 0 的商品不打印。

5.2 命令反馈说明

5.2.1 查找成功打印信息

每件匹配的商品占一行,按照 shopId 从小到大输出;对于同一商店的商品,按照 commodityId 从小到大输出。格式为店铺id: 商品id 商品名称 商品价格 商品数量,其中,商品价格保留两位小数,单位为 yuan。

1
2
3
4
5
# 举例
S-1: C-2 bottle 20.00yuan 20
S-1: C-5 bottle 30.00yuan 10
S-3: C-2 bottle 1.00yuan 100
S-4: C-1 bottle 1000.00yuan 1

5.2.2 参数数量不合法

1
Illegal argument count

5.2.3 未登录

1
Please log in first

5.2.4 商品名称不合法

1
Illegal commodity name

5.2.5 商品不存在或商品数量均为零

1
Commodity not exists

6. 购买商品

6.1 格式说明

命令符 参数1 参数2 参数3
buyCommodity 店铺编号 商品编号 购买数量

只有顾客可购买商品,购买后相应店铺的相应商品数量应减少。

购买数量

  1. 购买数量必须大于 0。
  2. 购买数量必须为整数,不能有前导零。

6.2 命令反馈说明

6.2.1 购买成功

1
Buy commodity success

6.2.2 参数数量不合法

1
Illegal argument count

6.2.3 未登录

1
Please log in first

6.2.4 登录用户身份不是 Customer

1
Permission denied

6.2.5 店铺编号不合法

1
Illegal shop id

6.2.6 店铺编号不存在

1
Shop id not exists

6.2.6 商品编号不合法

1
Illegal commodity id

6.2.7 商品编号未注册、该商品不是该店铺的产品

1
Commodity id not exists

6.2.8 购买数量不合法、购买数量大于商品数量

1
Illegal buy quantity

7. 下架商品

7.1 格式说明

命令符 参数1 可选参数
removeCommodity 商品编号 [店铺编号]
  • 参数仅有商品编号:在所有店铺中下架该商品。商家仅可以下架自己所有店铺的商品,管理员可以下架任意商家的商品。对于下架的商品 C,不仅要清空 C 在所有店铺中的库存,同时未来商家也不能再重新上架 C。
  • 参数为商品编号和店铺编号:在商家的指定店铺下架该商品。商家仅可以下架自己某店铺的某商品,管理员可下架任意店铺的任意商品。对于店铺 S 中商品 C 的下架,指的是清空 C 在 S 中的库存,并在 S 中删除 C 的记录,商家仍可在他的所有店铺中上架该商品。
  • 以上两种情况均需要清空库存,当库存为 0 时也不报错。

提示:

商品最终归属于商家。对于两条下架商品指令,都需要清空库存,它们的区别如下:

  • 当商品在某个店铺下架时,它是店铺的下架商品,仍是商家的上架商品,商家可以在自己的所有店铺上架。

    再次在该店铺下架:输出商品不存在。

    查看商店商品:不打印该商品(因为该商品已下架)。

  • 当商品在全局下架时,它是店铺的下架商品,同时是商家的下架商品,商家不能再次上架。

7.2 命令反馈说明

无可选参数

7.2.1 下架商品成功

1
Remove commodity success

7.2.2 参数数量不合法

1
Illegal argument count

7.2.3 未登录

1
Please log in first

7.2.4 登录用户身份不是 MerchantAdministrator

1
Permission denied

7.2.5 商品编号不合法

1
Illegal commodity id

7.2.6 商品编号不存在、商品不属于该商家

1
Commodity id not exists

可选参数为店铺编号

7.2.7 下架商品成功

1
Remove commodity success

7.2.8 参数数量不合法

1
Illegal argument count

7.2.9 未登录

1
Please log in first

7.2.10 登录用户身份不是 MerchantAdministrator

1
Permission denied

7.2.11 商品编号不合法

1
Illegal commodity id

7.2.12 店铺编号不合法

1
Illegal shop id

7.2.13 店铺编号不存在、店铺不属于该商家

1
Shop id not exists

7.2.14 商品编号不存在、商品不属于该商家、商品不属于该店铺

1
Commodity id not exists

8. 注销店铺

8.1 格式说明

命令符 参数1
cancelShop 店铺编号
  • 商家可以注销自己的店铺。
  • 管理员可以注销任意店铺。
  • 注销店铺后,需将店铺中所有商品下架。

8.2 命令反馈说明

8.2.1 注销店铺成功

1
Cancel shop success

8.2.2 参数数量不合法

1
Illegal argument count

8.2.3 未登录

1
Please log in first

8.2.4 登录用户身份不是 MerchantAdministrator

1
Permission denied

8.2.5 店铺编号不合法

1
Illegal shop id

8.2.6 店铺编号不存在

对管理员而言,店铺编号未注册或已注销;对商家而言,店铺编号未注册或已注销或者不属于自己。

1
Shop id not exists

三、HINTS

3.1 相关概念说明

3.1.1 商家、店铺、商品

商家拥有店铺、店铺中有商品。上架的商品最终归属在商家,上架新商品会给商家添加一种商品,商家可在自己的所有店铺里添加该商品,但商家不可添加其他商家的商品。

同一商家名下的店铺名称不可相同,不同商家的店铺名称可以相同。

3.1.2 下架商品、注销店铺

所有打印商品、店铺信息的命令,如无特殊说明,均不打印下架商品、注销店铺的信息。

关于两种下架商品命令的区别

  • 下架某编号所有商品:将所有店铺中的该商品删除,并且商品状态标记为已下架。
  • 下架某店铺中某编号商品:只是将该店铺中该商品删除,并减少商家该商品数量。该商品仍可在该商家的其他商店上架,状态仍为已上架

注销店铺后,需要下架店铺中的商品。 此处的下架仅下架该店铺中的所有商品,即不改变商品的上架状态。

3.1.3 同一命令的不同格式

对于同一命令不同格式,在命令反馈说明中将不同情况均分开进行了说明。实际编写过程中会有一些判断是共通的,大家可以视情况简化重复的代码。

3.2 实现思路

3.2.1 商品类的设计

相信大家在阅读完商品相关命令以及提示后,会发现这样一个事情:商品有“数量”这一属性,但是这一属性在具体到某一商店时,与其在某一商家、全局时的值是不同的。也就是商品在全局时的数量等于对应商家商品的数量,而这个数量等于商品在该商家店铺中数量之和。我们可以用下面这种思路来解决。

既然这一属性在不同地方的值不同,那我们干脆让全局与商家处存放的是同一个实例;在店铺中存放另一个实例,只要维护好他们之间的数量关系就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Commodity 类

// 返回一个新的 Commodity 实例,其拥有与原对象除 count 属性以外相同的属性值
public Commodity getNewCommodity(int count) {
Commodity newCommodity = new Commodity(name, price, count);
newCommodity.setId(id);
return newCommodity;
}

// 返回一个新的 Commodity 实例,其拥有与原对象相同的属性值
public Commodity getNewCommodity() {
// 这个 count 是该对象的 count 属性,也就是 this.count
return getNewCommodity(count);
}

通过以上代码,我们可以获取一个全新的商品类的实例。
但其实本系统中并没有要求对于商品数量进行一个全局的统计,那我们不如在全局就干脆不要数量这个属性,只对店铺中的商品保留这个属性,也省去了维护数量关系的麻烦。

相关实体类:

1
2
3
4
5
6
7
8
9
10
public class CommodityUnit {
private String id;
private String name;
private double price;
}

public class Commodity {
private CommodityUnit commodityUnit;
private int count;
}

相关静态类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static HashMap<String, CommodityUnit> 
commodityUnits = new HashMap<>();

private static int currentCommodityId = 0;

private static String getCommodityId() {
currentCommodityId++;
return "C-" + currentCommodityId;
}

// 生成一个新的 CommodityUnit 实例(对应上架一种新商品)
public static Commodity getCommodity(String commodityName,
double commodityPrice,
int commodityCount)
{
CommodityUnit commodityUnit =
new CommodityUnit(getCommodityId(), commodityName, commodityPrice);

commodityUnits.put(commodityUnit.getId(), commodityUnit);
return new Commodity(commodityUnit, commodityCount);
}

// 获取一种已有商品的实例(对应上架一种已有商品)
public static Commodity getCommodity(String CommodityId,
int commodityCount)
{
CommodityUnit commodityUnit = commodityUnits.get(CommodityId);
return new Commodity(commodityUnit, commodityCount);
}

// 上架商品时如果该店铺已有该商品,视作补充数量,获取到对应商品对象添加数量即可
// 与享元模式无关,在此不做赘述

这里只是提供一个简单的思路以便参考。实际编写中,商品可能还需要记录所在商店的信息,大家可以视具体情况而定。
通过如上代码的实现,我们可以发现:尽管不同商店均存储着不同的商品实例,但是对于同一种商品,他们的CommodityUnit 属性均为同一个实例。

这也正是享元模式的目的:减少重复对象或提取不同对象的重复属性,以节省空间开销。

在相关资料中给出的链接中的样例,其重点在于减少重复对象,而上述商品的设计重点在于提取不同对象的重复属性。

当然,以本系统的设计而言,商品的享元模式设计并不能实现节省大量空间开销的作用(一个商家最多有 5 间店铺)。但还是希望能够让大家亲自动手,体会一下程序设计模式的作用。

3.2.2 技术说明

集合排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// commodities 是一个 HashMap<String, Commodity>,其键为商品 id
// getNumberCommodityId() 返回字符串 id 的数字部分
List<Map.Entry<String, Commodity>> list
= new ArrayList<>(commodities.entrySet());

Collections.sort(list, new Comparator<Map.Entry<String, Commodity>>() {
@Override
public int compare(Map.Entry<String, Commodity> o1,
Map.Entry<String, Commodity> o2)
{
// 顺序排序,如果想要逆序,o2 - o1 即可
return getNumberCommodityId(o1.getKey()) -
getNumberCommodityId(o2.getKey());
}
});

// 也可以用 lambda 表达式替换匿名类这种写法
// comparingInt 的参数是一个函数
// 这个函数接受一个泛型(这里是 Map.Entry<String, Commodity>),返回一个 int
// 该排序就根据 List 中每个元素返回的 int 值进行,默认为顺序
// 如果想要逆序,在返回值前添加一个负号即可

list.sort(Comparator.comparingInt(o -> getNumberCommodityId(o.getKey())));

四、测试样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
register 000117861001 aaaa adddh7hhh@ adddh7hhh@ Administrator
Register success
register 046418112296 zXOYXO Y0$_5ujbis Y0$_5ujbis Merchant
Register success
register 217918521598 hKPACtyQySyJ Y0@kig8@4 Y0@kig8@4 Customer
Register success
login 000117861001 adddh7hhh@
Welcome to TMS
registerShop aubb
Permission denied
logout
Bye~
login 046418112296 Y0$_5ujbis
Welcome to TMS
registerShop bauu
Register shop success (shopId: S-1)
registerShop abuu
Register shop success (shopId: S-2)
putCommodity S-1 latiao 0.5 100
Put commodity success (commodityId: C-1)
putCommodity S-2 weilonglatiao 1 200
Put commodity success (commodityId: C-2)
putCommodity S-1 C-1 346
Put commodity success (commodityId: C-1)
putCommodity S-2 C-1 333
Put commodity success (commodityId: C-1)
listShop
S-1 bauu
S-2 abuu
logout
Bye~
login 217918521598 Y0@kig8@4
Welcome to TMS
listShop
S-1 bauu
S-2 abuu
listCommodity
S-1: C-1 latiao 0.50yuan 446
S-2: C-1 latiao 0.50yuan 333
S-2: C-2 weilonglatiao 1.00yuan 200
searchCommodity latiao
S-1: C-1 latiao 0.50yuan 446
S-2: C-1 latiao 0.50yuan 333
buyCommodity S-2 C-1 20
Buy commodity success
buyCommodity S-2 C-2 10
Buy commodity success
buyCommodity S-1 C-2 30
Commodity id not exists
buyCommodity S-2 C-1 0
Illegal buy quantity
buyCommodity S-1 C-2 30
Commodity id not exists
removeCommodity C-1
Permission denied
logout
Bye~
login 046418112296 Y0$_5ujbis
Welcome to TMS
removeCommodity C-1 S-1
Remove commodity success
removeCommodity C-2 S-1
Commodity id not exists
listCommodity
S-2: C-1 latiao 0.50yuan 313
S-2: C-2 weilonglatiao 1.00yuan 190
cancelShop S-1
Cancel shop success
logout
Bye~
login 000117861001 adddh7hhh@
Welcome to TMS
buyCommodity S-2 C-1 20
Permission denied
putCommodity S-1 latiao 0.5 100
Permission denied
removeCommodity C-2 S-1
Shop id not exists
removeCommodity C-1 S-1
Shop id not exists
cancelShop S-2
Cancel shop success
listCommodity
Commodity not exists
listShop
Shop not exists
quit
----- Good Bye! -----