All My Projects Description

Made by Ziyang CHEN, Rocky, 陈子阳


CV Chain(Blockchain) Developer (HTML + CSS + JS + Python3)

- Made platform schools or companies can store and check applier or worker CVs on the CV Chain Platform
- Backend Technology using Blockchain & Web Development

Github: https://github.com/Team-Unhackable/CV-Chain

Website: https://cvchain.skyproton.org/

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# This Python script is a simple implementation of a blockchain using the Flask web framework. It also interacts with the LeanCloud platform, a backend as a service provider, to handle user data.
# 此 Python 脚本是使用 Flask Web 框架的区块链的简单实现。 它还与作为服务提供商的后端 LeanCloud 平台进行交互,以处理用户数据。

# Imports necessary modules: The script begins by importing various Python modules such as datetime, json, hashlib, os, and flask. It also imports the leancloud module to interact with LeanCloud.
# 导入必要的模块:该脚本首先导入各种 Python 模块,例如 datetime、json、hashlib、os 和flask。 它还导入leancloud模块来与LeanCloud交互。
import datetime
import json
import hashlib
import os
from flask import Flask, jsonify
import leancloud

# Initializes LeanCloud: The leancloud.init() function is called with two empty strings as arguments. Normally, you should replace the "" with your LeanCloud Application ID and Master Key.
# 初始化LeanCloud:使用两个空字符串作为参数调用leancloud.init()函数。 通常,这些将是您的 LeanCloud 应用程序 ID 和主密钥。
leancloud.init("", "")

# Blockchain class: A Blockchain class is defined which has various methods associated with it such as __init__, create_blockchain, get_previous_block, proof_of_work, hash, and is_chain_valid. These methods are used to create a new blockchain, add blocks to the chain, validate the blockchain, and perform other related functions.
# 区块链类:定义了一个区块链类,它具有与其关联的各种方法,例如 __init__、create_blockchain、get_previous_block、proof_of_work、hash 和 is_chain_valid。 这些方法用于创建新的区块链、向链添加区块、验证区块链以及执行其他相关功能。
class Blockchain:
def __init__(self):
self.chain = []
self.create_blockchain(proof=1, previous_hash='0')

def create_blockchain(self, proof, previous_hash):
block = {
'index': len(self.chain) + 1,
'timestamp': str(datetime.datetime.now()),
'proof': proof,
'previous_hash': previous_hash
}
self.chain.append(block)
return block

def get_previous_block(self):
last_block = self.chain[-1]
return last_block

def proof_of_work(self, previous_proof):
new_proof = 1
check_proof = False
while check_proof is False:
hash_operation = hashlib.sha256(
str(new_proof ** 2 - previous_proof ** 2).encode()).hexdigest()
if hash_operation[:4] == '0000':
check_proof = True
else:
new_proof += 1
return new_proof

def hash(self, block): # generate a hash of an entire block
encoded_block = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(encoded_block).hexdigest()

def is_chain_valid(self, chain): # check if the blockchain is valid
previous_block = chain[0]
block_index = 1
while block_index < len(chain):
block = chain[block_index]
if block["previous_hash"] != self.hash(previous_block):
return False
previous_proof = previous_block['proof']
current_proof = block['proof']
hash_operation = hashlib.sha256(
str(current_proof ** 2 - previous_proof ** 2).encode()).hexdigest()
if hash_operation[:4] != '0000':
return False
previous_block = block
block_index += 1
return True

# Flask application setup: A Flask application is instantiated and a new Blockchain object is created.
# Flask 应用程序设置:实例化 Flask 应用程序并创建新的区块链对象。
app = Flask(__name__)

blockchain = Blockchain()

# def cache_read():
# url = "https://skyproton.org/save/cache.txt"
# file = urllib.request.urlopen(url)
# name = file.readlines()[0].decode("utf-8")
# name = name.strip('\n')
# return name


# LeanCloud interaction functions: Several functions are defined to interact with LeanCloud, including getCacheUserNameList, updateUserNameIndex, hasIndex, and getUserName. These functions retrieve user data from LeanCloud, update user data, and perform other related operations.
# LeanCloud交互函数:定义了几个与LeanCloud交互的函数,包括getCacheUserNameList、updateUserNameIndex、hasIndex和getUserName。 这些函数从 LeanCloud 检索用户数据、更新用户数据并执行其他相关操作。
def getCacheUserNameList():
leancloud.init("", "")
list = []
query = leancloud.Query('UserData')
query.equal_to('blockIndex', "")
query.descending('createdAt')
user_list = query.find()
for user in user_list:
list.append(user.get('nameOnBlk'))
user.save()
# if len(user_list) > 0:
return list


def updateUserNameIndex(inUserName, inIndex):
leancloud.init("", "")
query = leancloud.Query('UserData')
query.equal_to('username', inUserName)
flag = False
if (query.count()>0):
user_list = query.find()
# for user in user_list:
user = user_list[0]
if (user.get('username') == inUserName and user.get('blockIndex') == ""):
user.set('blockIndex',str(inIndex))
user.save()
flag = True
return flag

def hasIndex(inUserName):
leancloud.init("", "")
query = leancloud.Query('UserData')
query.equal_to('username', inUserName)
flag = False
if (query.count()>0):
user_list = query.find()
user = user_list[0]
if (user.get('blockIndex') != ""):
flag = True
return flag

def getUserName(inRealName):
leancloud.init("", "")
outName = ''
flag = False
query = leancloud.Query('UserData')
query.equal_to('nameOnBlk', str(inRealName))
if (query.count()>0):
user_list = query.find()
for user in user_list:
if (user.get('nameOnBlk') == inRealName):
outName = user.get('username')
flag = True
if (flag):
return outName
else:
return flag


# def addUsersIntoBlock():
# userList = []
# # while (len(userList)==0):
# userList = getCacheUserNameList()
# if (len(userList)!=0):
# for user in userList:
# return block_data(user)

# Flask routes: Two Flask routes are defined, /block_data and /get_chain. The /block_data route is a GET route that retrieves a list of usernames from LeanCloud, adds them to the blockchain, and writes the blockchain data to JSON files. The /get_chain route is also a GET route that returns the entire blockchain and its length.
# Flask 路由:定义了两个 Flask 路由,/block_data 和 /get_chain。 /block_data 路由是一个 GET 路由,它从 LeanCloud 检索用户名列表,将它们添加到区块链,并将区块链数据写入 JSON 文件。 /get_chain 路由也是一个 GET 路由,它返回整个区块链及其长度。

@app.route('/block_data', methods=['GET'])
def block_data():
inNameList = getCacheUserNameList()
while (len(inNameList)>0):
inName = inNameList.pop()
previous_block = blockchain.get_previous_block()
previous_proof = previous_block['proof']
proof = blockchain.proof_of_work(previous_proof)
previous_hash = blockchain.hash(previous_block)
block = blockchain.create_blockchain(proof, previous_hash)
response = {'data': inName,
'index': block['index'],
'timestamp': block['timestamp'],
'proof': block['proof'],
'previous_hash': block['previous_hash']}

username = getUserName(inName)
if (not hasIndex(username)):
updateUserNameIndex(username, block['index'])

json_object = json.dumps(response, indent = 5)

with open("./data/main/main_block_" + str(block['index']) + ".json", "w") as outfile:
outfile.write(json_object)

os.mkdir("./data/" + str(block['index']))

count = {'num': len(blockchain.chain)
}
json_count = json.dumps(count, indent = 1)

with open("./count/main.json", "w") as outfile:
outfile.write(json_count)


#return jsonify(response), 200
return jsonify(response = {'data': None,
'index': None,
'timestamp': None,
'proof': None,
'previous_hash': None}), 200

@app.route('/get_chain', methods=['GET'])
def get_chain():
response = {'chain': blockchain.chain,
'length': len(blockchain.chain)}
return jsonify(response), 200

# Application run: Finally, the Flask application is run on host '0.0.0.0' and port 5001.
# 应用程序运行:最后,Flask 应用程序在主机“0.0.0.0”和端口 5001 上运行。
app.run(host='0.0.0.0', port=5001)

The script is part of a larger system. It is designed to create a blockchain, add blocks to the chain when new users are added to the LeanCloud service, and provide a way to retrieve the entire blockchain via a web API. It also creates a new directory for each block and two JSON /ˈdʒeɪsn/ files: one that contains the block data and another that contains the current length of the chain.
该脚本是更大系统的一部分。 它旨在创建一个区块链,当新用户添加到 LeanCloud 服务时将区块添加到链中,并提供一种通过 Web API 检索整个区块链的方法。 它还为每个块创建一个新目录和两个 JSON 文件:一个包含块数据,另一个包含链的当前长度。

This is a very basic implementation of a blockchain. It lacks elements like peer-to-peer network, consensus algorithm for resolving conflicts, transaction handling, etc. that you would find in a full-fledged blockchain system like Bitcoin or Ethereum.
这是区块链的一个非常基本的实现。它缺乏点对点网络、解决冲突的共识算法、交易处理等元素,而这些元素你会在比特币或以太坊等成熟的区块链系统中找到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import time
import leancloud

# 定义了一个名为getCacheUserNameListLen()的函数,该函数使用LeanCloud的SDK连接到云端数据库,并查询名为UserData的表格中,blockIndex字段为空的记录的数量,并返回该数量。
# A function named getCacheUserNameListLen() is defined that uses LeanCloud's SDK to connect to the cloud database and query the number of records in a table named UserData with the blockIndex field empty and return that number.
def getCacheUserNameListLen():
leancloud.init("", "")
list = []
query = leancloud.Query('UserData')
query.equal_to('blockIndex', "")
user_list = query.find()
return len(user_list)

# 使用一个while循环,不断地向URL http://127.0.0.1:5001/block_data 发送GET请求,每隔5秒钟进行一次请求。
while True:
URL = "http://127.0.0.1:5001/block_data"
if (getCacheUserNameListLen()>0): # 在每次循环中,如果getCacheUserNameListLen()函数返回的值大于0
r = requests.get(url = URL)
print("Get") # 则打印"Get",表示可以发送请求;
else:
print("Wait") # 否则打印"Wait",表示需要等待。
time.sleep(5)
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
import requests
import time
import leancloud
import json
import os

# 定义了一个名为getCacheBusinessListLen()的函数,该函数使用LeanCloud的SDK连接到云端数据库,并查询名为UserData的表格中,isAdded字段为"f"的记录的数量,并返回该数量。
def getCacheBusinessListLen():
leancloud.init("", "")
bList = []
query = leancloud.Query('UserData')
query.equal_to('isAdded', "f")
user_list = query.find()
return len(user_list)

# 使用一个while循环,不断地向URL http://127.0.0.1:5002/block_data 发送GET请求,每隔5秒钟进行一次请求。
while True:
URL = "http://127.0.0.1:5002/block_data"
if (getCacheBusinessListLen()>0): # 在每次循环中,如果getCacheBusinessListLen()函数返回的值大于0
r = requests.get(url = URL)
print("Get") # 则打印"Get",表示可以发送请求

# 在请求成功后,程序会扫描指定目录下的文件,获取文件数量并将文件序号存储到JSON文件中。
# 具体来说,程序会遍历目录"/var/www/html/blockchain/data"下的所有文件夹。
# 对于每个文件夹,如果它包含文件,则程序会找到该文件夹中编号最大的文件,
# 并将该文件的序号减1写入一个名为"{文件夹编号}.json"的JSON文件中,存储在"./count"目录下。
path = "/var/www/html/blockchain/data"
list = os.listdir(path)
number_files = len(list)

for i in range(2, number_files+1):
path = "/var/www/html/blockchain/data/" + str(i)
subdata_file = os.listdir(path)
if len(subdata_file) > 0:
index = int(max(subdata_file)[0]) - 1
num = {'num': index}
json_object = json.dumps(num, indent = 1)
with open("./count/" + str(i) + ".json", "w") as outfile:
outfile.write(json_object)

else:
print("Wait") # 否则打印"Wait",表示需要等待。
time.sleep(5)
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import datetime
import json
import hashlib
import os
from flask import Flask, jsonify
import leancloud

# 区块链类:该类是区块链系统的核心。 它包含处理区块链操作的各种方法,例如创建新区块 (create_blockchain)、获取前一个区块 (get_previous_block)、执行工作证明 (proof_of_work)、为区块生成哈希 (hash) 以及验证区块链 (is_chain_valid)。
class Blockchain:
def __init__(self):
self.chain = []
self.create_blockchain(proof=1, previous_hash='0', UID='0', index='1')

def create_blockchain(self, proof, previous_hash, UID, index):
#index_1 = os.path.splitext("./data/" + str(UID) + "/" + str(index) + ".json")[0]
block = {
#'index': int(index_1[-1:]) + 1,
'index': int(index) + 1,
'timestamp': str(datetime.datetime.now()),
'proof': proof,
'previous_hash': previous_hash
}
self.chain.append(block)
return block

def get_previous_block(self):
last_block = self.chain[-1]
return last_block

def proof_of_work(self, previous_proof):
new_proof = 1
check_proof = False
while check_proof is False:
hash_operation = hashlib.sha256(
str(new_proof ** 2 - previous_proof ** 2).encode()).hexdigest()
if hash_operation[:4] == '0000':
check_proof = True
else:
new_proof += 1
return new_proof

def hash(self, block): # generate a hash of an entire block
encoded_block = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(encoded_block).hexdigest()

def is_chain_valid(self, chain): # check if the blockchain is valid
previous_block = chain[0]
block_index = 1
while block_index < len(chain):
block = chain[block_index]
if block["previous_hash"] != self.hash(previous_block):
return False
previous_proof = previous_block['proof']
current_proof = block['proof']
hash_operation = hashlib.sha256(
str(current_proof ** 2 - previous_proof ** 2).encode()).hexdigest()
if hash_operation[:4] != '0000':
return False
previous_block = block
block_index += 1
return True

# Flask 应用程序:Flask 应用程序提供与区块链系统交互的接口。 路由 /block_data 是处理区块链操作的端点。 它从 LeanCloud 获取业务列表,对于每个业务,它执行工作量证明,在区块链中创建一个新块,然后将此块数据作为 json 文件保存在特定目录中。
# Flask app: The Flask application provides an interface to interact with the blockchain system. The route /block_data is the endpoint where the blockchain operations are handled. It fetches the business list from LeanCloud, and for each business, it performs a proof of work, creates a new block in the blockchain and then saves this block data as a json file in a specific directory.
app = Flask(__name__)

blockchain = Blockchain()

# def cache_read(): 被注释掉,与缓存和更新用户数据等附加功能有关,但当前代码中并未使用它们。
# url = "https://skyproton.org/save/cache.txt"
# file = urllib.request.urlopen(url)
# name = file.readlines()[0].decode("utf-8")
# name = name.strip('\n')
# return name

# LeanCloud 操作:该代码包含多个与 LeanCloud 服务交互以存储和获取数据的函数。
def getCacheUserNameList():
leancloud.init("", "")
list = []
query = leancloud.Query('UserData')
query.equal_to('blockIndex', "")
query.descending('createdAt')
user_list = query.find()
for user in user_list:
list.append(user.get('nameOnBlk'))
user.save()
# if len(user_list) > 0:
return list


def updateUserNameIndex(inUserName, inIndex):
leancloud.init("", "")
query = leancloud.Query('UserData')
query.equal_to('username', inUserName)
flag = False
if (query.count()>0):
user_list = query.find()
# for user in user_list:
user = user_list[0]
if (user.get('username') == inUserName and user.get('blockIndex') == ""):
user.set('blockIndex',str(inIndex))
user.save()
flag = True
return flag

def hasIndex(inUserName):
leancloud.init("", "")
query = leancloud.Query('UserData')
query.equal_to('username', inUserName)
flag = False
if (query.count()>0):
user_list = query.find()
user = user_list[0]
if (user.get('blockIndex') != ""):
flag = True
return flag

def getUserName(inRealName):
leancloud.init("", "")
outName = ''
flag = False
query = leancloud.Query('UserData')
query.equal_to('nameOnBlk', str(inRealName))
if (query.count()>0):
user_list = query.find()
for user in user_list:
if (user.get('nameOnBlk') == inRealName):
outName = user.get('username')
flag = True
if (flag):
return outName
else:
return flag

# 例如,getCacheBusinessList 获取尚未添加到区块链的企业列表(其中“isAdded”等于“f”)。 然后,这些业务将被添加到区块链中并标记为已添加(将“isAdded”设置为“t”)。
def getCacheBusinessList():
leancloud.init("", "")
bList = list()
query = leancloud.Query('UserData')
query.equal_to('isAdded', "f")
query.descending('createdAt')
user_list = query.find()
for user in user_list:
inList = list()
inList.append(user.get('UID'))
inList.append(user.get('companyType'))
inList.append(user.get('companyName'))
inList.append(user.get('DegreeOrJob'))
inList.append(user.get('ClassOrYear'))
bList.append(inList)
user.set("isAdded","t")
user.save()
return bList

@app.route('/block_data', methods=['GET'])
def block_data():
inNameList = getCacheBusinessList()
while (len(inNameList)>0):
inName = inNameList.pop()
UID = inName[0]
inType = inName[1]
inBName = inName[2]
inData_1 = inName[3]
inData_2 = inName[4]
previous_block = blockchain.get_previous_block()
previous_proof = previous_block['proof']
proof = blockchain.proof_of_work(previous_proof)
previous_hash = blockchain.hash(previous_block)

path = "/var/www/html/blockchain/data/" + str(UID)
subdata_file = os.listdir(path)
if len(subdata_file) > 0:
index = max(subdata_file)[0]
else:
index = 1

block = blockchain.create_blockchain(proof, previous_hash, UID, index)
response = {'type': inType,
'data': {
'Name_of_Work_or_Study': inBName,
'data_1': inData_1,
'data_2': inData_2,
},
'index': block['index'],
'timestamp': block['timestamp'],
'proof': block['proof'],
'previous_hash': block['previous_hash']}

# username = getUserName(inName)
# if (not hasIndex(username)):
# updateUserNameIndex(username, block['index'])

json_object = json.dumps(response, indent = 5)

with open("./data/" + str(UID) + "/" + str(block['index']) + ".json", "w") as outfile:
outfile.write(json_object)

#return jsonify(response), 200
return jsonify(response = {'data': None,
'index': None,
'timestamp': None,
'proof': None,
'previous_hash': None}), 200

app.run(host='0.0.0.0', port=5002) # 应用程序运行:最后一行在主机“0.0.0.0”和端口 5002 上运行 Flask 应用程序。

A Blockchain Implementation (C++)

- This program is about Blockchain which is a cryptographic tool. The goal is to be used in Fintech to protect the chronological order of transactions. The user can use this program to store the information that he wants to save, the information can neither be deleted nor modified. And provide the function of displaying chain data to facilitate user queries. If there are malicious changes to transaction data, the program detects and reflects them.
- 这个是一个关于加密工具区块链的程序。目标是在金融科技中使用,以保护交易的时间顺序。用户可以使用这个程序来存储他想要保存的信息,这些信息既不能删除也不能修改。并提供链数据显示功能,方便用户查询。如果存在对事务数据的恶意更改,程序将检测并反映这些更改。

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// First of all, here is the library of the program. It includes 3 libraries and given files hpp. and a cpp.
#include <iostream>
#include <cstring>
#include "SHA1.hpp"
#include "SHA1.cpp"
#define MAX 1000
using namespace std;

// The program designs two structures, namely Block and Blockchain. Among them, the Block structure contains the data, number, hash value, and previous hash value of the Block. In order to facilitate the calculation, when we designed the Blockchain structure, we maintained an array of Blocks with a capacity of 1000 and recorded the number of Blocks in the current Blockchain.
struct Block { // here is the structure of block
string Data; // The data that the user wants to enter(store)
int Number; // the index of the blocks
string p_hash; // previous hash value
string hash; // hash value
};

struct Blockchain{ // here is the structure of blockchain which is including blocks
struct Block BlockArray[MAX]; // By default, 1000 blocks can be added
int m_Size;
};

// The project is built on the blockchain. This program allows the user to perform four operations: 1. Add a new block 2. Display the chain data 3. Check the integrity of the data 0. Exit the program If the user writes numbers other than these four, such as 4, 5, 6. The system redisplays the menu and asks the user to retype. When the user enters 0 to exit the program.
void showMenu(){ // display the menu
cout << "******************************************************************************************" << endl;
cout << "**************************************** Blockchain **************************************" << endl;
cout << "********* This is the blockchain menu, you can run related functions by number ***********" << endl;
cout << "* 1. Add a new block *" << endl;
cout << "* 2. Search block by given information *" << endl;
cout << "* 3. Check the integrity of the data *" << endl;
cout << "* 0. Exit the program *" << endl;
cout << "******************************************************************************************" << endl;
cout << endl;
}

// Regarding the first operation, the user can add blocks by himself, store the information (data) that the user wants to store into the blocks, and generate a hash value to ensure security. First, the program checks whether the blockchain's memory is full and if not, the following operations are performed.
// The system will ask the number of blocks the user wants to add. And then the user could enter the information(data) into it. If x blocks are added successfully, the system will display "You have added x block(s) successfully!!! ". After the user enters the information, the system will automatically calculate its hash value. And store the information of the block in the corresponding structure.
// After the first function completes, the system displays the menu again to the user, allowing the user to choose the next step.
void addBlocks(Blockchain * blo){ // This is the function to add blocks
if (blo -> m_Size == MAX) { // Blocks cannot be added if they reach 1000
cout << "Adding failure!" << endl;
return;
}
else{
cout << "Enter the number of blocks you want to add: ";
int addNum = 0; // Initialize the number of blocks that user want to add
cin >> addNum;
if (addNum > 0){
// Calculate the new space size, the adding number should not be negative
int newSize; // 0
newSize = blo->m_Size + addNum;
string data = ""; // Initialize the data of block
string p_hash = ""; // Initialize the previous hash of block

if (blo->m_Size != 0){
p_hash = blo->BlockArray[blo->m_Size - 1].hash; // in case previous hash become the initialize value
}

for (int i = blo->m_Size; i < newSize; i++) { // i = 0, i < 2
cout << endl;
cout<<"***** Please enter the information of block No."<<i+1<<" *****"<<endl;
cout<<"\tPlease enter the data: "; cin >> data;
cout << endl;

string input(data);
SHA1 checksum;
checksum.update(input);
char hash[41];
strcpy(hash, checksum.final().c_str());

blo -> BlockArray[i].Data = data;
blo -> BlockArray[i].Number = i+1;
blo -> BlockArray[i].p_hash = p_hash;
blo -> BlockArray[i].hash = hash;

p_hash = hash;
blo -> m_Size++; // the size of block could plus 1 after adding 1 block.
}
cout << "You have added " << addNum << " block(s) successfully!!!" << endl;

}
else{
cout << "Incorrect input!" << endl;
}
}
}

// First, the second operation checks if there are blocks in it. If the user directly selects this action without entering a new block, the system will display Empty, the system will redisplay the menu and let the user re-enter.
// If there is the block(s), the second operation can display the data of the chain in three ways: 1. the user can display the data of the whole chain by entering the block number. 2. the data of the whole chain. 3. the hash value of a specific block.
// If the user enters another number, the program will display: "Input wrong or empty information!". The And then program redisplays the three query methods.
void findBlock(Blockchain * blo){ // display the chain data in a number of ways
// by block number
// the data of the whole chain
// the hash values of a particular block

// Take case 1 as an example. When the "select" input by the user is 1, execute case 1. Check if the block is not empty, then compare the "Number" stored in the structure with the number that the user wants to find for traversal and comparison. If found, output the information of the entire chain. The program also designs a Boolean variable check, which is used if the data entered by the user matches, if not found, the program outputs "No corresponding block was found!". And asked to re-enter the numbers. If the data entered when querying the method is incorrect, the function will exit, and the menu will be redisplayed.
if (blo->m_Size == 0){ // if the size of block is zero, output Empty!
cout << "Empty!!" << endl;
}
else{
cout << "1.Search by block number" << endl;
cout << "2.Search by the data of the whole chain" << endl;
cout << "3.Search by the hash values of a particular block" << endl;
cout << endl;
cout << "Please enter the search method: ";
int select;
int S_number; // initialize the block number that user will input
string data;
string hash;
cin >> select;
if (select == 1) { // Search by block number
cout << endl;
cout << "Please enter the block number you are looking for: ";
cin >> S_number; // let user input the block number they want to search
if (blo->m_Size == 0){
cout << "Empty!!" << endl;
}
else{ // Display the informations of the block that the user wants to see according to the block number entered by the user
cout << "************************************************************************" << endl;
cout << "***************** Here is the information of block " << S_number << " *****************" << endl;
cout << "* *" << endl;
cout << "\tNumber: " << blo->BlockArray[S_number-1].Number << endl;
cout << "\tData: " << blo->BlockArray[S_number-1].Data << endl;
cout << "\tHash value: " << blo->BlockArray[S_number-1].hash << endl;
cout << "\tPrevious hash value: " << blo->BlockArray[S_number-1].p_hash << endl;
cout << "* *" << endl;
cout << "************************************************************************" << endl;
}
cout << endl;
}
else if (select == 2){ // Search by the data of the whole chain
cout << endl;
cout << "Please enter the data of the block you are looking for: ";
cin >> data;
if (blo->m_Size == 0){ // if the size of block is zero, output Empty!
cout << "Empty!!" << endl;
}
else {
// Display the informations of the block that the user wants to see according to the block data entered by the user
for (int i = 0; i < blo->m_Size; i++){
if (blo->BlockArray[i].Data == data) {
cout << "************************************************************************" << endl;
cout << "***************** Here is the information of block " << i+1 << " *****************" << endl;
cout << "* *" << endl;
cout << "\tNumber: " << blo->BlockArray[i].Number << endl;
cout << "\tData: " << blo->BlockArray[i].Data << endl;
cout << "\tHash value: " << blo->BlockArray[i].hash << endl;
cout << "\tPrevious hash value: " << blo->BlockArray[i].p_hash << endl;
cout << "* *" << endl;
cout << "************************************************************************" << endl;
}
}
}
}
else if (select == 3){ // Search by the hash values of a particular block
cout << endl;
cout << "Please enter the hash value of the block you are looking for: ";
cin >> hash;
if (blo->m_Size == 0){ // if the size of block is zero, output Empty!
cout << "Empty!!" << endl;
}
else {
// Display the informations of the block that the user wants to see according to the hash values of a particular block entered by the user
for (int i = 0; i < blo->m_Size; i++){
if (blo->BlockArray[i].hash == hash) {
cout << "************************************************************************" << endl;
cout << "***************** Here is the information of block " << i+1 << " *****************" << endl;
cout << "* *" << endl;
cout << "\tNumber: " << blo->BlockArray[i].Number << endl;
cout << "\tData: " << blo->BlockArray[i].Data << endl;
cout << "\tHash value: " << blo->BlockArray[i].hash << endl;
cout << "\tPrevious hash value: " << blo->BlockArray[i].p_hash << endl;
cout << "* *" << endl;
cout << "************************************************************************" << endl;
}
}
}

}
else{
cout << "Input wrong or empty information!" << endl;
}
}
}

// The third operation checks whether there are blocks in it. If not, the program will display "Empty" and the system will redisplay the menu and ask the user to retype.
// This function allows the user to check the integrity of the data stored in the chain. The integrity of the data in the chain is judged by comparing the hash value with the previous hash value. When the blockchain already has block information entered by the user. When the user enters Menu number 3, the program will compare the hash value of each block with the previous hash value. Traverse each block, if the previous hash value of the next one is equal to the previous hash value, output "The blocks are integral".
void Check_integrity(Blockchain * blo){ // Check the integrity of the data

if (blo->m_Size == 0){ // if the size of block is zero, output Empty!
cout << "Empty!!" << endl;
}
else {
bool check = true; // Initialize a Boolean value
for (int i = 0; i < blo->m_Size-1; i++) { // Check all hash values, compare with their previous hash values
if (blo->BlockArray[i+1].p_hash != blo->BlockArray[i].hash) {
check = false;
}
}
cout << endl;
if (check) { // output the integrity according to the Boolean value
cout << "\tThe blocks are integral" << endl;
}
else{
cout << "\tThe blocks are NOT integral!!!" << endl;
}
cout << endl;
cout << "Below is all hash value of the blockchain: " << endl; // display all the hash values and previous hash values to user for better understanding of the integrity of the blockchain
for (int i = 0; i < blo->m_Size; i++){
cout << "\tHash value: " << blo->BlockArray[i].hash << endl;
cout << "\tPrevious hash value: " << blo->BlockArray[i].p_hash << endl;
cout << endl;
}
}
}

// In the code, the program creates the Blockchain structure variable "blo" and initializes the current number of blocks in the Blockchain. Calling the menu guarantees that the menu is available to the user every time. select is the user's input to perform the corresponding operation.
int main(){
Blockchain blo; // naming structure
blo.m_Size = 0; // Initialize the size of blocks
int select = 0;
while (true){
showMenu();
cout << "Enter the number of the Menu number: "; cin >> select;
switch (select) {
case 1:
addBlocks(&blo); // This is the function to add blocks
break;
case 2:
findBlock(&blo); // display the chain data in a number of ways by block number, the data of the whole chain or the hash values of a particular block
break;
case 3:
Check_integrity(&blo); // check the integrity of the blocks
break;
case 0: // enter 0 to end the program
cout << "Thank you for using." << endl;
return 0;
break;

default:
cout << "Please enter correct a number!" << endl;
}
}
return 0;
}

Multi-thread Web Server (Python)

This is a Python-based web server that is capable of processing HTTP requests from browsers or other clients in a multi-threaded way.

You should first run the program “Server.py” and click on the link on the console. This will navigate to the server’s main menu where you can find links to image files, the “server.txt” log file, modifiable files, and 404 not found. You can click on any link and follow the instructions to access the desired file. If the server hasn’t responded for a while, it may have closed the connection. Please try refreshing and clicking again. After you run it, you can go to the log file to find the request record, including the client address, access time, requested file name and response type for each record.

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
89
90
91
92
93
94
95
96
97
98
99
100
101
# import socket, import threading, import os, import time, and from datetime import datetime are Python modules imported at the start of the code. Class Server is defined as the main server class that takes host, port, and html_file parameters in its constructor. It creates a server socket with socket.socket(), sets socket options with setsockopt(), and binds the socket to the host and port with bind(). It then listens for incoming connections with listen().
import socket
import threading
import os
import time
from datetime import datetime
from http import HTTPStatus

class Server:
# __init__: The constructor initializes the server with a specified host and port, and a default html_file to serve when the root directory ("/") is requested. It also sets up a server socket and binds it to the given address and port.
def __init__(self, host, port, html_file):
self.host = host
self.port = port
self.html_file = html_file
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen()

# serve_forever: This method starts the server loop, which continuously listens for incoming connections. For each client that connects, it spawns a new thread to handle the client's requests, allowing the server to handle multiple clients simultaneously.
def serve_forever(self):
print(f"Server listening on http://{self.host}:{self.port}")
while True:
client_socket, client_address = self.server_socket.accept()
print(f"Accepted connection from {client_address}")
client_thread = threading.Thread(target=self.handle_client, args=(client_socket, client_address))
client_thread.start()

# handle_client: This method handles a client's connection. It reads and parses the client's HTTP request, logs the request details, determines what file the client is requesting, and responds appropriately. If the client is requesting a file that exists, it sends the file's content with a 200 OK status. If the client is requesting a file that does not exist, it sends a 404 Not Found status. If the client is making a conditional GET request with an If-Modified-Since header, and the requested file has not been modified since the specified date, it sends a 304 Not Modified status.
def handle_client(self, client_socket, client_address):
with client_socket:
request = client_socket.recv(1024).decode()
print(f"Received request from {client_address}:")
print(request)

# Parse the request to determine the specific file being requested
headers = request.split('\n')
filename = headers[0].split()[1] # /
method = headers[0].split()[0] # Get, Head

# Add logging here
with open("server.txt", encoding="utf-8", mode="a") as log_file:
if filename == "/":
log_message = f"Client address: [{client_address[0]}]\tAccess time: [{datetime.now()}]\nFile name: [{filename}index.html]\tResponse type: [{method} HTTP/1.1 200 OK]\n\n"
else:
log_message = f"Client address: [{client_address[0]}]\tAccess time: [{datetime.now()}]\nFile name: [{filename}]\tResponse type: [{method} HTTP/1.1 200 OK]\n\n"
log_file.write(log_message)

if method == 'GET' or method == 'HEAD':
# If the method is GET or HEAD, it attempts to retrieve the requested file and sends a response back to the client with the file contents and appropriate headers. Since image files are binary and must be viewed in "rb" mode before being transferred individually, they cannot be read and delivered to the browser immediately. In this manner, the picture returned by the GET request may be seen normally in the browser. If the file has not been updated after being cached by the browser, it will return 304 Not Modified when we reload the website. This indicates that our software can appropriately handle image files and 304 answers. If the file does not exist, it sends a 404 Not Found error. If the file is a directory, it attempts to append index.html to the file path and retrieve that file.
if filename == '/':
path = self.html_file
else:
path = filename[1:]

filepath = os.path.join(os.getcwd(), path)
if not os.path.exists(filepath):
self.send_error(client_socket, HTTPStatus.NOT_FOUND)
return

if os.path.isdir(filepath):
filepath = os.path.join(filepath, self.html_file)

try:
with open(filepath, "rb") as f:
content = f.read()
content_length = len(content)
last_modified = datetime.utcfromtimestamp(os.path.getmtime(filepath)).strftime(
'%a, %d %b %Y %H:%M:%S GMT')
# Check for If-Modified-Since header
if 'If-Modified-Since' in request:
ims = headers[headers.index('If-Modified-Since: ') + 1].strip()
ims_time = datetime.strptime(ims, '%a, %d %b %Y %H:%M:%S GMT')
if ims_time >= datetime.utcfromtimestamp(os.path.getmtime(filepath)):
self.send_error(client_socket, HTTPStatus.NOT_MODIFIED) # 304 Not Modified
return
# Get Response Headers
response_headers = [
f"HTTP/1.1 {HTTPStatus.OK.value} {HTTPStatus.OK.phrase}",
f"Content-Length: {content_length}",
f"Last-Modified: {last_modified}",
"Connection: keep-alive",
]
response = '\r\n'.join(response_headers).encode() + b'\r\n\r\n'
if method == 'GET':
response += content
client_socket.sendall(response)
except:
self.send_error(client_socket, HTTPStatus.NOT_FOUND) # 404 File Not Found
return

def send_error(self, client_socket, status): # sends an error response to the client with the specified HTTP status.
response = f"HTTP/1.1 {status.value} {status.phrase}\r\nConnection: close\r\n\r\n"
client_socket.sendall(response.encode())

def main(): # main() creates a new instance of the Server class with a host of 127.0.0.1, port 8000, and the HTML file web.html. It then calls serve_forever() on this instance, which starts the server and listens for incoming connections.
server = Server(host='127.0.0.1', port=8000, html_file='web.html')
server.serve_forever()

if __name__ == '__main__':
main()
  1. The main function creates an instance of the Server class with the host 127.0.0.1 (localhost), the port 8000, and the default HTML file web.html, and starts the server.
  2. The if __name__ == '__main__' conditional checks whether the script is being executed directly (rather than being imported as a module), and if so, it calls the main function to start the server.
  3. The code creates a multithreaded HTTP server in Python with sockets and threading. It listens on a given IP address and port and handles requests from clients. It sends static files from the local file system for HTTP GET requests and returns suitable HTTP status codes. It writes client requests to a file called “server.txt” in UTF-8. The code has a class named Server with three methods: init(), serve_forever(), and handle_client(). The init() method sets up the server TCP socket and binds it to the IP address and port. The serve_forever() method waits for client connections and launches a new thread for each one. The handle_client() method gets and parses the client request, reads the file from the local file system, and sends a response to the client. The main() function makes a Server object and starts serving requests.

Command-line-based Personal Information Manager (Java)

View - Main method

Main 类中的 main 方法是应用程序的入口点。它首先尝试从一个文件中加载数据。如果数据成功加载,程序进入一个循环,它等待用户输入并处理命令。

每个命令都表示为一个字符串。程序使用 ifelse if 语句来确定用户输入了哪个命令。根据命令的不同,调用不同的方法来处理命令。例如,如果用户输入一个以 “note “ 开头的命令,将调用 NoteController.executeCommand(sc, command) 方法。

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
89
package info.pim;

import info.pim.constant.Message;
import info.pim.controller.*;
import info.pim.service.PimDataService;

import java.util.Scanner;

public class Main {

public static void main(String[] args) {
String imDataFilePath = "";
if (args.length > 0) {
imDataFilePath = args[0];
}
boolean loaded = PimDataService.loadPimData(imDataFilePath);
if (!loaded) {
return;
}

Scanner sc = new Scanner(System.in);
showCommand();
while(true) {
System.out.println();
System.out.println("----------------------------------------------------------------------");
printCommand("help", "指令清单");
printCommand("quit", "退出PIM");
System.out.println("请输入操作指令:");
String command = sc.nextLine();

if (command.equals("quit")) {
System.exit(0);
} else if (command.equals("help")) {
showCommand();
} else if (command.startsWith("note ")) {
NoteController.executeCommand(sc, command);
} else if (command.startsWith("task ")) {
TaskController.executeCommand(sc, command);
} else if (command.startsWith("event ")) {
EventController.executeCommand(sc, command);
} else if (command.startsWith("contact ")) {
ContactController.executeCommand(sc, command);
} else if (command.trim().startsWith("search")) {
SearchController.executeCommand(sc, command);
} else if (command.trim().startsWith("print")) {
PrintController.executeCommand(sc, command);
}
}
}

private static void showCommand() {
System.out.println();
System.out.println(Message.WELCOME);

printCommand("note", "list", "列表note");
printCommand("note", "add", "新增note");
printCommand("note", "edit [id]", "编辑note");
printCommand("note", "delete [id]", "删除note");

printCommand("task", "list", "列表task");
printCommand("task", "add", "新增task");
printCommand("task", "edit [id]", "编辑task");
printCommand("task", "delete [id]", "删除task");

printCommand("event", "list", "列表event");
printCommand("event", "add", "新增event");
printCommand("event", "edit [id]", "编辑event");
printCommand("event", "delete [id]", "删除event");

printCommand("contact", "list", "列表contact");
printCommand("contact", "add", "新增contact");
printCommand("contact", "edit [id]", "编辑contact");
printCommand("contact", "delete [id]", "删除contact");

printCommand("search", "[type|text|time]", "查询");
printCommand("print", "[id|all]", "输出");

}

private static void printCommand(String command, String subCommand, String desc) {
System.out.printf("%-15s %-30s %-25s", command, subCommand, desc);
System.out.println();
}

private static void printCommand(String command, String desc) {
System.out.printf("%-15s %-30s", command, desc);
System.out.println();
}
}

Service package

info.pim.service 包含了 PimDataService 类,该类提供了管理便签,任务,事件,和联系人的方法。它从文件加载和存储数据。

Class PimDataService

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// 这个Java类PimDataService充当个人信息管理器(PIM)应用程序的服务类。它处理从文件读取和写入数据 (reading and writing of data from/to a file),并提供管理笔记、任务、事件和联系人的方法 (methods for managing notes, tasks, events, and contacts).
package info.pim.service;

import info.pim.model.Contact;
import info.pim.model.Event;
import info.pim.model.Note;
import info.pim.model.Task;
import info.pim.util.SnowflakeIdWorker;
import info.pim.util.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;

/**
* PIM数据服务
*/
public class PimDataService {
/**
* pim数据存储的文件路径
*/
private static String imDataFilePath = "data" + File.separator + "data.pim";

public static List<Note> notes = new ArrayList<>();
public static List<Task> tasks = new ArrayList<>();
public static List<Event> events = new ArrayList<>();
public static List<Contact> contacts = new ArrayList<>();

public static SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);

/**
* 从文件中加载PIM数据
*
* @param path 如果为空,则从默认位置data/data.pim加载
*/
public static boolean loadPimData(String path) {
if (!StringUtils.isEmpty(path)) { // path 如果不为空,直接从文件中加载PIM数据
imDataFilePath = path;
}
try {
File pimFile = new File(imDataFilePath); // path 如果为空,则从默认位置data/data.pim加载

if (!pimFile.exists()) {
if (!pimFile.getParentFile().exists()) { // 这段代码首先检查父目录是否存在,如果不存在则创建。然后在该路径下创建新的文件。如果文件是新创建的(也就是空文件),那么调用 savePimData() 方法来初始化数据。
pimFile.getParentFile().mkdirs();
}
pimFile.createNewFile();
// 空文件时初始化数据结构
savePimData();
} else {
// 创建 FileInputStream 和 ObjectInputStream 来从文件中读取对象,然后数据被反序列化为包含不同类型的PIM数据(笔记,任务,事件,联系人)的HashMap。
FileInputStream fis = null;
ObjectInputStream ois = null;
fis = new FileInputStream(pimFile);
ois = new ObjectInputStream(fis);
HashMap<String, List> map = (HashMap<String, List>) ois.readObject();
// 然后,将读取的对象强制转换为 HashMap<String, List>

if (map != null) {
// 如果读取到的 map 不为 null,那么将 map 中对应的列表赋值给类变量 notes, tasks, events, contacts。
notes = map.getOrDefault("notes", new ArrayList());
tasks = map.getOrDefault("tasks", new ArrayList());
events = map.getOrDefault("events", new ArrayList());
contacts = map.getOrDefault("contacts", new ArrayList());
}
ois.close();
}
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("加载PIM数据出错!");
}
return false;
}

/**
* 保存PIM数据到文件中
*/
private static void savePimData() {
HashMap<String, List> map = new HashMap<>();
map.put("notes", notes);
map.put("tasks", tasks);
map.put("events", events);
map.put("contacts", contacts);

File pimFile = new File(imDataFilePath);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(pimFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(map);
oos.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println("保存PIM数据出错!");
}
}

/**
* 根据id获取Note
*/
public static Note getNote(String id) {
Optional<Note> optionalNote = notes.stream().filter(f -> f.getId().equals(id)).findFirst();
return optionalNote.orElse(null);
}

/**
* 保存Note
*/
public static void saveNote(Note note) {
Optional<Note> optionalNote = notes.stream().filter(f -> f.getId().equals(note.getId())).findFirst();
if (optionalNote.isPresent()) {
Note noteUpdate = optionalNote.get();
// 更新数据
noteUpdate.copyData(note);
} else {
// 新增数据
notes.add(note);
}
savePimData();
}

/**
* 删除Note
*
* @param id 待删除的 id
*/
public static void deleteNote(String id) {
for (int i = 0; i < notes.size(); i++) {
Note note = notes.get(i);
if (note.getId().equals(id)) {
notes.remove(i);
savePimData();
break;
}
}
}

/**
* 根据id获取Task
*/
public static Task getTask(String id) {
Optional<Task> optionalTask = tasks.stream().filter(f -> f.getId().equals(id)).findFirst();
return optionalTask.orElse(null);
}

/**
* 保存Task
*/
public static void saveTask(Task task) {
Optional<Task> optionalTask = tasks.stream().filter(f -> f.getId().equals(task.getId())).findFirst();
if (optionalTask.isPresent()) {
Task taskUpdate = optionalTask.get();
// 更新数据
taskUpdate.copyData(task);
} else {
// 新增数据
tasks.add(task);
}
savePimData();
}

/**
* 删除Task
*
* @param id 待删除的 id
*/
public static void deleteTask(String id) {
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
if (task.getId().equals(id)) {
tasks.remove(i);
savePimData();
break;
}
}
}

/**
* 根据id获取Event
*/
public static Event getEvent(String id) {
Optional<Event> optionalEvent = events.stream().filter(f -> f.getId().equals(id)).findFirst();
return optionalEvent.orElse(null);
}

/**
* 保存Event
*/
public static void saveEvent(Event event) {
Optional<Event> optionalEvent = events.stream().filter(f -> f.getId().equals(event.getId())).findFirst();
if (optionalEvent.isPresent()) {
Event eventUpdate = optionalEvent.get();
// 更新数据
eventUpdate.copyData(event);
} else {
// 新增数据
events.add(event);
}
savePimData();
}

/**
* 删除Event
*
* @param id 待删除的 id
*/
public static void deleteEvent(String id) {
for (int i = 0; i < events.size(); i++) {
Event event = events.get(i);
if (event.getId().equals(id)) {
events.remove(i);
savePimData();
break;
}
}
}

/**
* 根据id获取Contact
*/
public static Contact getContact(String id) {
Optional<Contact> optionalContact = contacts.stream().filter(f -> f.getId().equals(id)).findFirst();
return optionalContact.orElse(null);
}

/**
* 保存Contact
*/
public static void saveContact(Contact contact) {
Optional<Contact> optionalContact = contacts.stream().filter(f -> f.getId().equals(contact.getId())).findFirst();
if (optionalContact.isPresent()) {
Contact contactUpdate = optionalContact.get();
// 更新数据
contactUpdate.copyData(contact);
} else {
// 新增数据
contacts.add(contact);
}
savePimData();
}

/**
* 删除Contact
*
* @param id 待删除的 id
*/
public static void deleteContact(String id) {
for (int i = 0; i < contacts.size(); i++) {
Contact contact = contacts.get(i);
if (contact.getId().equals(id)) {
contacts.remove(i);
savePimData();
break;
}
}
}

/**
* 获取所有数据
*/
public static List<Object> getAllPir() {
List<Object> result = new ArrayList<>();
result.addAll(notes);
result.addAll(tasks);
result.addAll(events);
result.addAll(contacts);
return result;
}

/**
* 根据id获取
*/
public static Object getPirById(String id) {
Note note = getNote(id);
if (note != null) {
return note;
}
Task task = getTask(id);
if (task != null) {
return task;
}
Event event = getEvent(id);
if (event != null) {
return event;
}
Contact contact = getContact(id);
if (contact != null) {
return contact;
}
return null;
}
}

Model

info.pim.model 包含四个类:ContactEventNote,和 Task。每个类代表了 PIM 可以管理的不同类型的数据。这些类实现了 Serializable 接口,这允许它们被写入文件和从文件中读取。

Class Contact

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
package info.pim.model;

import java.io.Serializable;

/**
* 联系人模型
*/
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 唯一标识
*/
private String id;

/**
* 姓名
*/
private String name;

/**
* 地址
*/
private String address;

/**
* 手机
*/
private String mobile;

public void copyData(Contact source) {
this.name = source.name;
this.address = source.address;
this.mobile = source.mobile;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getMobile() {
return mobile;
}

public void setMobile(String mobile) {
this.mobile = mobile;
}

@Override
public String toString() {
return "Contact{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", address='" + address + '\'' +
", mobile='" + mobile + '\'' +
'}';
}
}

Class Event

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
package info.pim.model;

import java.io.Serializable;

/**
* 事件模型
*/
public class Event implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 唯一标识
*/
private String id;

/**
* 开始日期
*/
private String startTime;

/**
* 警告信息
*/
private String alarm;

/**
* 描述
*/
private String description;

public void copyData(Event source) {
this.startTime = source.startTime;
this.alarm = source.alarm;
this.description = source.description;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getStartTime() {
return startTime;
}

public void setStartTime(String startTime) {
this.startTime = startTime;
}

public String getAlarm() {
return alarm;
}

public void setAlarm(String alarm) {
this.alarm = alarm;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

@Override
public String toString() {
return "Event{" +
"id='" + id + '\'' +
", startTime='" + startTime + '\'' +
", alarm='" + alarm + '\'' +
", description='" + description + '\'' +
'}';
}
}

Class Note

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
package info.pim.model;

import java.io.Serializable;

/**
* 笔记模型
*/
public class Note implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 唯一标识
*/
private String id;

/**
* 笔记内容
*/
private String content;

public void copyData(Note source) {
this.content = source.content;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

@Override
public String toString() {
return "Note{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
'}';
}
}

Class Task

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
package info.pim.model;

import java.io.Serializable;

/**
* 任务模型
*/
public class Task implements Serializable {
private static final long serialVersionUID = 1L;

/**
* 唯一标识
*/
private String id;

/**
* 截止日期
*/
private String deadline;

/**
* 描述
*/
private String description;

public void copyData(Task source) {
this.deadline = source.deadline;
this.description = source.description;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getDeadline() {
return deadline;
}

public void setDeadline(String deadline) {
this.deadline = deadline;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

@Override
public String toString() {
return "Task{" +
"id='" + id + '\'' +
", deadline='" + deadline + '\'' +
", description='" + description + '\'' +
'}';
}
}

Controller

info.pim.controller 包含各种控制器类,包括 ContactControllerEventControllerNoteControllerPrintControllerSearchController,和 TaskController。这些类包含处理与管理便签,任务,事件,和联系人相关的特定命令的方法。

Class ContactController

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
89
90
91
92
93
package info.pim.controller;

import info.pim.model.Contact;
import info.pim.model.Task;
import info.pim.service.PimDataService;

import java.util.List;
import java.util.Scanner;

/**
* Contact Controller
*/
public class ContactController {
public static void executeCommand(Scanner sc, String command) {
String[] commands = command.split(" ");
if (commands.length >= 2) {
if (commands[1].equals("list")) {
list();
} else if (commands[1].equals("add")) {
add(sc);
} else if (commands[1].equals("edit")) {
edit(sc, commands);
} else if (commands[1].equals("delete")) {
delete(commands);
}
}
}

/**
* 列表查询
*/
private static void list() {
List<Contact> contacts = PimDataService.contacts;
System.out.println("contact list:");
for (Contact contact : contacts) {
System.out.println(contact);
}
}

/**
* 添加
*/
private static void add(Scanner sc) {
Contact contact = new Contact();
contact.setId(PimDataService.idWorker.nextId());

updateData(sc, contact);
}

/**
* 编辑
*/
private static void edit(Scanner sc, String[] commands) {
if (commands.length < 3) {
System.out.println("执行edit指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
Contact contact = PimDataService.getContact(id);
if (contact == null) {
System.out.println("指定的contact不存在!");
return;
}

updateData(sc, contact);
}

private static void updateData(Scanner sc, Contact contact) {
System.out.println("请输入name:");
String name = sc.nextLine();
contact.setName(name);
System.out.println("请输入address:");
String address = sc.nextLine();
contact.setAddress(address);
System.out.println("请输入mobile:");
String mobile = sc.nextLine();
contact.setMobile(mobile);
PimDataService.saveContact(contact);
System.out.println("保存成功!");
}

/**
* 删除
*/
private static void delete(String[] commands) {
if (commands.length < 3) {
System.out.println("执行delete指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
PimDataService.deleteContact(id);
}
}

Class EventController

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
89
90
91
92
93
94
95
96
97
98
99
package info.pim.controller;

import info.pim.model.Event;
import info.pim.service.PimDataService;

import java.util.List;
import java.util.Scanner;

/**
* Event Controller
*/
public class EventController {
public static void executeCommand(Scanner sc, String command) {
String[] commands = command.split(" ");
if (commands.length >= 2) {
if (commands[1].equals("list")) {
list();
} else if (commands[1].equals("add")) {
add(sc);
} else if (commands[1].equals("edit")) {
edit(sc, commands);
} else if (commands[1].equals("delete")) {
delete(commands);
}
}
}

/**
* 列表查询
*/
private static void list() {
List<Event> events = PimDataService.events;
System.out.println("event list:");
for (Event event : events) {
System.out.println(event);
}
}

/**
* 添加
*/
private static void add(Scanner sc) {
Event event = new Event();
event.setId(PimDataService.idWorker.nextId());

updateData(sc, event);
}

/**
* 编辑
*/
private static void edit(Scanner sc, String[] commands) {
if (commands.length < 3) {
System.out.println("执行edit指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
Event event = PimDataService.getEvent(id);
if (event == null) {
System.out.println("指定的event不存在!");
return;
}

updateData(sc, event);
}

private static void updateData(Scanner sc, Event event) {
System.out.println("请输入startTime(格式为yyyy-MM-dd, 例如 2023-10-27):");
String startTime = sc.nextLine();
event.setStartTime(startTime);
System.out.println("请输入alarm(格式为yyyy-MM-dd, 例如 2023-10-27):");
String alarm = sc.nextLine();
event.setAlarm(alarm);
System.out.println("请输入description(当一行输入为[:quit]退出输入操作):");
StringBuffer sbDescription = new StringBuffer();
while (true) {
String description = sc.nextLine();
if (description.equals(":quit")) {
break;
}
sbDescription.append(description);
}
event.setDescription(sbDescription.toString());
PimDataService.saveEvent(event);
System.out.println("保存成功!");
}

/**
* 删除
*/
private static void delete(String[] commands) {
if (commands.length < 3) {
System.out.println("执行delete指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
PimDataService.deleteEvent(id);
}
}

Class NoteController

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
89
90
91
92
93
94
package info.pim.controller;

import info.pim.model.Note;
import info.pim.model.Task;
import info.pim.service.PimDataService;

import java.util.List;
import java.util.Scanner;

/**
* Note Controller
*/
public class NoteController {
public static void executeCommand(Scanner sc, String command) {
String[] commands = command.split(" ");
if (commands.length >= 2) {
if (commands[1].equals("list")) {
list();
} else if (commands[1].equals("add")) {
add(sc);
} else if (commands[1].equals("edit")) {
edit(sc, commands);
} else if (commands[1].equals("delete")) {
delete(commands);
}
}
}

/**
* 列表查询
*/
private static void list() {
List<Note> notes = PimDataService.notes;
System.out.println("note list:");
for (Note note : notes) {
System.out.println(note);
}
}

/**
* 添加
*/
private static void add(Scanner sc) {
Note note = new Note();
note.setId(PimDataService.idWorker.nextId());

updateData(sc, note);
}

/**
* 编辑
*/
private static void edit(Scanner sc, String[] commands) {
if (commands.length < 3) {
System.out.println("执行edit指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
Note note = PimDataService.getNote(id);
if (note == null) {
System.out.println("指定的note不存在!");
return;
}

updateData(sc, note);
}

private static void updateData(Scanner sc, Note note) {
System.out.println("请输入content(当一行输入为[:quit]退出输入操作):");
StringBuffer sbContent = new StringBuffer();
while (true) {
String content = sc.nextLine();
if (content.equals(":quit")) {
break;
}
sbContent.append(content);
}
note.setContent(sbContent.toString());
PimDataService.saveNote(note);
System.out.println("保存成功!");
}

/**
* 删除
*/
private static void delete(String[] commands) {
if (commands.length < 3) {
System.out.println("执行delete指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
PimDataService.deleteNote(id);
}
}

Class PrintController

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
package info.pim.controller;

import info.pim.service.PimDataService;

import java.util.List;
import java.util.Scanner;

/**
* Print Controller
*/
public class PrintController {
public static void executeCommand(Scanner sc, String command) {
String[] commands = command.split(" ");
if (commands.length >= 2) {
// 第二个指令作为查询的id
String id = commands[1].trim();
if (id.equals("all")) {
List<Object> result = PimDataService.getAllPir();
for (Object pir : result) {
System.out.println(pir);
}
} else {
Object pir = PimDataService.getPirById(id);
if (pir != null) {
System.out.println(pir);
}
}
}
}
}

Class SearchController

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
package info.pim.controller;

import info.pim.model.Contact;
import info.pim.model.Event;
import info.pim.model.Note;
import info.pim.model.Task;
import info.pim.service.PimDataService;
import info.pim.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

/**
* Search Controller
* 查询条件的逻辑处理
* 查询命令格式 search + 空格 + 可选的条件
* 可选的条件 type | text | time,并且组合的条件必须按照此顺序
* 条件的值前可加!,代表取反操作
* 多条件之间可使用 && 或者 ||
*/
public class SearchController {
public static void executeCommand(Scanner sc, String command) {
List<Object> result = new ArrayList<>();
String[] commands = command.split(" ");
if (commands.length >= 2) {
if (!commands[1].startsWith("type=") && !commands[1].startsWith("text=") && !commands[1].startsWith("time")) {
return;
}
// 当前搜索的数据类型条件
String typeValue = "";
// 标记type是否为取反查询
boolean typeNegation = false;
// 当前搜索的文本条件
String textValue = "";
// 标记text是否为取反查询
boolean textNegation = false;
// 标记text条件和前序条件(如果有为type)是否为and关系
boolean textAnd = false;
// 标记text条件和前序条件(如果有为type)是否为or关系
boolean textOr = false;
// 当前搜索的时间条件
String timeValue = "";
// 标记time是否为取反查询
boolean timeNegation = false;
// 标记time条件和前序条件(如果有为type,text)是否为and关系
boolean timeAnd = false;
// 标记time条件和前序条件(如果有为type,text)是否为or关系
boolean timeOr = false;
for (int i = 1; i < commands.length; i++) {
String condition = commands[i];
boolean negation = false;
if (condition.startsWith("type=")) {
condition = condition.replace("type=", "").trim();
if (condition.startsWith("!")) {
negation = true;
condition = condition.substring(1);
}
if (StringUtils.isEmpty(condition)) {
System.out.println("输入的type查询条件不符合要求,请重新输入!");
return;
}
typeValue = condition;
typeNegation = negation;
} else if (condition.startsWith("text=")) {
condition = condition.replace("text=", "").trim();
if (condition.startsWith("!")) {
negation = true;
condition = condition.substring(1);
}
if (StringUtils.isEmpty(condition)) {
System.out.println("输入的text查询条件不符合要求,请重新输入!");
return;
}
textValue = condition;
textNegation = negation;
} else if (condition.startsWith("time")) {
condition = condition.replace("time", "").trim();
if (condition.startsWith("!")) {
negation = true;
condition = condition.substring(1);
}
if (StringUtils.isEmpty(condition)) {
System.out.println("输入的time查询条件不符合要求,请重新输入!");
return;
}
timeValue = condition;
timeNegation = negation;
} else if (condition.equals("&&")) {
int nextIndex = i + 1;
if (nextIndex < commands.length) {
String nextCondition = commands[nextIndex];
if (nextCondition.startsWith("text=")) {
textAnd = true;
} else if (nextCondition.startsWith("time")) {
timeAnd = true;
}
}
} else if (condition.equals("||")) {
int nextIndex = i + 1;
if (nextIndex < commands.length) {
String nextCondition = commands[nextIndex];
if (nextCondition.startsWith("text=")) {
textOr = true;
} else if (nextCondition.startsWith("time")) {
timeOr = true;
}
}
}
}
List<Note> noteList = searchNote(typeValue, typeNegation,
textValue, textNegation, textAnd, textOr);
result.addAll(noteList);
List<Task> taskList = searchTask(typeValue, typeNegation,
textValue, textNegation, textAnd, textOr,
timeValue, timeNegation, timeAnd, timeOr);
result.addAll(taskList);
List<Event> eventList = searchEvent(typeValue, typeNegation,
textValue, textNegation, textAnd, textOr,
timeValue, timeNegation, timeAnd, timeOr);
result.addAll(eventList);
List<Contact> contactList = searchContact(typeValue, typeNegation,
textValue, textNegation, textAnd, textOr);
result.addAll(contactList);
} else {
result = PimDataService.getAllPir();
}
for (Object pir : result) {
System.out.println(pir);
}
}

private static boolean checkType(String typeValue, boolean typeNegation, String typeKey) {
if (StringUtils.isEmpty(typeValue)) {
// 如果没有输入条件type,返回true
return true;
}
// 取反操作
if (typeNegation) {
return !typeValue.equals(typeKey);
} else {
return typeValue.equals(typeKey);
}
}

private static List<Note> searchNote(String typeValue, boolean typeNegation,
String textValue, boolean textNegation,
boolean textAnd, boolean textOr) {
List<Note> result = new ArrayList<>();
if (checkType(typeValue, typeNegation, "note")) {
List<Note> noteList = searchNoteDetail(textValue, textNegation);
result.addAll(noteList);
} else {
if (textOr) {
List<Note> noteList = searchNoteDetail(textValue, textNegation);
result.addAll(noteList);
}
}

return result;
}

private static List<Note> searchNoteDetail(String textValue, boolean textNegation) {
List<Note> matchList = new ArrayList<>();
if (StringUtils.isEmpty(textValue)) {
return matchList; // PimDataService.notes;
}
if (textNegation) {
matchList = PimDataService.notes.stream().filter(f -> !f.getContent().contains(textValue)).collect(Collectors.toList());
} else {
matchList = PimDataService.notes.stream().filter(f -> f.getContent().contains(textValue)).collect(Collectors.toList());
}
return matchList;
}

private static List<Task> searchTask(String typeValue, boolean typeNegation,
String textValue, boolean textNegation,
boolean textAnd, boolean textOr,
String timeValue, boolean timeNegation,
boolean timeAnd, boolean timeOr) {
List<Task> result = new ArrayList<>();
if (checkType(typeValue, typeNegation, "task")) {
List<Task> taskList = searchTaskDetail(textValue, textNegation, timeValue, timeNegation, timeAnd, timeOr);
result.addAll(taskList);
} else {
if (textOr) {
List<Task> taskList = searchTaskDetail(textValue, textNegation, timeValue, timeNegation, timeAnd, timeOr);
result.addAll(taskList);
}
}

return result;
}

private static List<Task> searchTaskDetail(String textValue, boolean textNegation,
String timeValue, boolean timeNegation,
boolean timeAnd, boolean timeOr) {
List<Task> matchList = new ArrayList<>();

for (Task task : PimDataService.tasks) {
boolean isMatch = false;
if (StringUtils.isEmpty(textValue)) {
if (StringUtils.isEmpty(timeValue)) {
isMatch = true;
} else {
if (timeNegation) {
if (timeValue.startsWith(">")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) <= 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) != 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) >= 0) {
isMatch = true;
}
}
} else {
if (timeValue.startsWith(">")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) > 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) == 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (task.getDeadline().compareTo(timeValue.substring(1)) < 0) {
isMatch = true;
}
}
}
}
} else {
if (StringUtils.isEmpty(timeValue)) {
if (textNegation) {
if (!task.getDescription().contains(textValue)) {
isMatch = true;
}
} else {
if (task.getDescription().contains(textValue)) {
isMatch = true;
}
}
} else {
if (textNegation) {
boolean partMath = !task.getDescription().contains(textValue);
if (timeNegation) {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) <= 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) != 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) >= 0) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) <= 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) != 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) >= 0) {
isMatch = true;
}
}
}

} else {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) > 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) == 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) < 0) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) > 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) == 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) < 0) {
isMatch = true;
}
}
}
}
} else {
boolean partMath = task.getDescription().contains(textValue);
if (timeNegation) {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) <= 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) != 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) >= 0) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) <= 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) != 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) >= 0) {
isMatch = true;
}
}
}

} else {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) > 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) == 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && task.getDeadline().compareTo(timeValue.substring(1)) < 0) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) > 0) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) == 0) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || task.getDeadline().compareTo(timeValue.substring(1)) < 0) {
isMatch = true;
}
}
}
}
}
}
}

if (isMatch) {
matchList.add(task);
}
}

return matchList;
}


private static List<Event> searchEvent(String typeValue, boolean typeNegation,
String textValue, boolean textNegation,
boolean textAnd, boolean textOr,
String timeValue, boolean timeNegation,
boolean timeAnd, boolean timeOr) {
List<Event> result = new ArrayList<>();
if (checkType(typeValue, typeNegation, "event")) {
List<Event> eventList = searchEventDetail(textValue, textNegation, timeValue, timeNegation, timeAnd, timeOr);
result.addAll(eventList);
} else {
if (textOr) {
List<Event> eventList = searchEventDetail(textValue, textNegation, timeValue, timeNegation, timeAnd, timeOr);
result.addAll(eventList);
}
}

return result;
}

private static List<Event> searchEventDetail(String textValue, boolean textNegation,
String timeValue, boolean timeNegation,
boolean timeAnd, boolean timeOr) {
List<Event> matchList = new ArrayList<>();

for (Event event : PimDataService.events) {
boolean isMatch = false;
if (StringUtils.isEmpty(textValue)) {
if (StringUtils.isEmpty(timeValue)) {
isMatch = true;
} else {
if (timeNegation) {
if (timeValue.startsWith(">")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) <= 0 || event.getAlarm().compareTo(timeValue.substring(1)) <= 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) != 0 || event.getAlarm().compareTo(timeValue.substring(1)) != 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) >= 0 || event.getAlarm().compareTo(timeValue.substring(1)) >= 0)) {
isMatch = true;
}
}
} else {
if (timeValue.startsWith(">")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) > 0 || event.getAlarm().compareTo(timeValue.substring(1)) > 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) == 0 || event.getAlarm().compareTo(timeValue.substring(1)) == 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if ((event.getStartTime().compareTo(timeValue.substring(1)) < 0 || event.getAlarm().compareTo(timeValue.substring(1)) < 0)) {
isMatch = true;
}
}
}
}
} else {
if (StringUtils.isEmpty(timeValue)) {
if (textNegation) {
if (!event.getDescription().contains(textValue)) {
isMatch = true;
}
} else {
if (event.getDescription().contains(textValue)) {
isMatch = true;
}
}
} else {
if (textNegation) {
boolean partMath = !event.getDescription().contains(textValue);
if (timeNegation) {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) <= 0 || event.getAlarm().compareTo(timeValue.substring(1)) <= 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) != 0 || event.getAlarm().compareTo(timeValue.substring(1)) != 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) >= 0 || event.getAlarm().compareTo(timeValue.substring(1)) >= 0)) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) <= 0 || event.getAlarm().compareTo(timeValue.substring(1)) <= 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) != 0 || event.getAlarm().compareTo(timeValue.substring(1)) != 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) >= 0 || event.getAlarm().compareTo(timeValue.substring(1)) >= 0)) {
isMatch = true;
}
}
}

} else {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) > 0 || event.getAlarm().compareTo(timeValue.substring(1)) > 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) == 0 || event.getAlarm().compareTo(timeValue.substring(1)) == 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) < 0 || event.getAlarm().compareTo(timeValue.substring(1)) < 0)) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) > 0 || event.getAlarm().compareTo(timeValue.substring(1)) > 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) == 0 || event.getAlarm().compareTo(timeValue.substring(1)) == 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) < 0 || event.getAlarm().compareTo(timeValue.substring(1)) < 0)) {
isMatch = true;
}
}
}
}
} else {
boolean partMath = event.getDescription().contains(textValue);
if (timeNegation) {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) <= 0 || event.getAlarm().compareTo(timeValue.substring(1)) <= 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) != 0 || event.getAlarm().compareTo(timeValue.substring(1)) != 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) >= 0 || event.getAlarm().compareTo(timeValue.substring(1)) >= 0)) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) <= 0 || event.getAlarm().compareTo(timeValue.substring(1)) <= 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) != 0 || event.getAlarm().compareTo(timeValue.substring(1)) != 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) >= 0 || event.getAlarm().compareTo(timeValue.substring(1)) >= 0)) {
isMatch = true;
}
}
}

} else {
if (timeAnd) {
if (timeValue.startsWith(">")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) > 0 || event.getAlarm().compareTo(timeValue.substring(1)) > 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) == 0 || event.getAlarm().compareTo(timeValue.substring(1)) == 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath && (event.getStartTime().compareTo(timeValue.substring(1)) < 0 || event.getAlarm().compareTo(timeValue.substring(1)) < 0)) {
isMatch = true;
}
}
} else if (timeOr) {
if (timeValue.startsWith(">")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) > 0 || event.getAlarm().compareTo(timeValue.substring(1)) > 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("=")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) == 0 || event.getAlarm().compareTo(timeValue.substring(1)) == 0)) {
isMatch = true;
}
} else if (timeValue.startsWith("<")) {
if (partMath || (event.getStartTime().compareTo(timeValue.substring(1)) < 0 || event.getAlarm().compareTo(timeValue.substring(1)) < 0)) {
isMatch = true;
}
}
}
}
}
}
}

if (isMatch) {
matchList.add(event);
}
}

return matchList;
}

private static List<Contact> searchContact(String typeValue, boolean typeNegation,
String textValue, boolean textNegation,
boolean textAnd, boolean textOr) {
List<Contact> result = new ArrayList<>();
if (checkType(typeValue, typeNegation, "contact")) {
List<Contact> contactList = searchContactDetail(textValue, textNegation);
result.addAll(contactList);
} else {
if (textOr) {
List<Contact> contactList = searchContactDetail(textValue, textNegation);
result.addAll(contactList);
}
}

return result;
}

private static List<Contact> searchContactDetail(String textValue, boolean textNegation) {
List<Contact> matchList = new ArrayList<>();
if (StringUtils.isEmpty(textValue)) {
return matchList; // PimDataService.contacts;
}
if (textNegation) {
matchList = PimDataService.contacts.stream().filter(f -> (!f.getName().contains(textValue) || !f.getMobile().contains(textValue) || !f.getAddress().contains(textValue))).collect(Collectors.toList());
} else {
matchList = PimDataService.contacts.stream().filter(f -> (f.getName().contains(textValue) || f.getMobile().contains(textValue) || f.getAddress().contains(textValue))).collect(Collectors.toList());
}
return matchList;
}
}

Class TaskController

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
89
90
91
92
93
94
95
96
package info.pim.controller;

import info.pim.model.Task;
import info.pim.service.PimDataService;

import java.util.List;
import java.util.Scanner;

/**
* Task Controller
*/
public class TaskController {
public static void executeCommand(Scanner sc, String command) {
String[] commands = command.split(" ");
if (commands.length >= 2) {
if (commands[1].equals("list")) {
list();
} else if (commands[1].equals("add")) {
add(sc);
} else if (commands[1].equals("edit")) {
edit(sc, commands);
} else if (commands[1].equals("delete")) {
delete(commands);
}
}
}

/**
* 列表查询
*/
private static void list() {
List<Task> tasks = PimDataService.tasks;
System.out.println("task list:");
for (Task task : tasks) {
System.out.println(task);
}
}

/**
* 添加
*/
private static void add(Scanner sc) {
Task task = new Task();
task.setId(PimDataService.idWorker.nextId());

updateData(sc, task);
}

/**
* 编辑
*/
private static void edit(Scanner sc, String[] commands) {
if (commands.length < 3) {
System.out.println("执行edit指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
Task task = PimDataService.getTask(id);
if (task == null) {
System.out.println("指定的event不存在!");
return;
}

updateData(sc, task);
}

private static void updateData(Scanner sc, Task task) {
System.out.println("请输入deadline(格式为yyyy-MM-dd, 例如 2023-10-27):");
String deadline = sc.nextLine();
task.setDeadline(deadline);
System.out.println("请输入description(当一行输入为[:quit]退出输入操作):");
StringBuffer sbDescription = new StringBuffer();
while (true) {
String description = sc.nextLine();
if (description.equals(":quit")) {
break;
}
sbDescription.append(description);
}
task.setDescription(sbDescription.toString());
PimDataService.saveTask(task);
System.out.println("保存成功!");
}

/**
* 删除
*/
private static void delete(String[] commands) {
if (commands.length < 3) {
System.out.println("执行delete指令时,请在指令后附带唯一标识id");
return;
}
String id = commands[2];
PimDataService.deleteTask(id);
}
}

Content

info.pim.constant 包含了 Message 接口,该接口定义了程序显示给用户的各种消息的常量。

1
2
3
4
5
6
7
8
package info.pim.constant;

/**
* 消息提示信息
*/
public interface Message {
String WELCOME = "=============================欢迎使用PIM系统============================";
}

Test

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package test;

import info.pim.model.Contact;
import info.pim.model.Event;
import info.pim.model.Note;
import info.pim.model.Task;
import info.pim.service.PimDataService;
import info.pim.util.DateUtil;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import java.io.File;
import java.util.List;

import static org.junit.Assert.*;

/**
* 单元测试
*/
public class MainUnitTest {
@BeforeClass
public static void initTestData() { // 这是一个设置方法,它在所有测试之前运行一次以初始化测试数据。它删除任何现有的测试数据文件并加载一个新的。
String imDataFilePath = "data/unit_test_data.pim";
File pimTestFile = new File(imDataFilePath);
if (pimTestFile.exists()) {
pimTestFile.delete();
}
PimDataService.loadPimData(imDataFilePath);
}

@Test
public void testNoteAdd() { // 这个测试验证了可以正确添加 Note。
Note note = new Note(); // 创建一个新的 Note
String id = PimDataService.idWorker.nextId(); // 使用 PimDataService 保存它
note.setId(id);
note.setContent("content add");
// add
PimDataService.saveNote(note);

// get add data
Note noteSaved = PimDataService.getNote(id);
assertSame(note, noteSaved);

// test print data
String dataStr = "Note{" +
"id='" + id + '\'' +
", content='" + noteSaved.getContent() + '\'' +
'}';
assertEquals(note.toString(), dataStr); // 然后检索它并检查检索到的 Note 是否与原始的匹配。
}

@Test
public void testNoteEdit() { // 这个测试验证了可以正确编辑 Note。
Note note = PimDataService.notes.get(0); // 它从列表中获取一个 Note
String id = note.getId();
note.setContent("content edit");
// edit
PimDataService.saveNote(note); // 修改它,保存更改

// get edit data
Note noteSaved = PimDataService.getNote(id);
assertSame(note, noteSaved); // 然后再次检索它并检查检索到的 Note 是否与修改后的匹配。
}

@Test
public void testTaskAdd() { // 这个测试类似于 testNoteAdd(),但它测试了添加 Task。
Task task = new Task();
String id = PimDataService.idWorker.nextId();
task.setId(id);
task.setDeadline(DateUtil.date());
task.setDescription("description add");
// add
PimDataService.saveTask(task);

// get add data
Task taskSaved = PimDataService.getTask(id);
assertSame(task, taskSaved);

// test print data
String dataStr = "Task{" +
"id='" + id + '\'' +
", deadline='" + taskSaved.getDeadline() + '\'' +
", description='" + taskSaved.getDescription() + '\'' +
'}';
assertEquals(task.toString(), dataStr);
}

@Test
public void testTaskEdit() {
Task task = PimDataService.tasks.get(0);
String id = task.getId();
task.setDeadline(DateUtil.date());
task.setDescription("description edit");
// edit
PimDataService.saveTask(task);

// get edit data
Task taskSaved = PimDataService.getTask(id);
assertSame(task, taskSaved);
}

@Test
public void testEventAdd() {
Event event = new Event();
String id = PimDataService.idWorker.nextId();
event.setId(id);
event.setStartTime(DateUtil.date());
event.setAlarm(DateUtil.date());
event.setDescription("description add");
// add
PimDataService.saveEvent(event);

// get add data
Event eventSaved = PimDataService.getEvent(id);
assertSame(event, eventSaved);

// test print data
String dataStr = "Event{" +
"id='" + id + '\'' +
", startTime='" + eventSaved.getStartTime() + '\'' +
", alarm='" + eventSaved.getAlarm() + '\'' +
", description='" + eventSaved.getDescription() + '\'' +
'}';
assertEquals(event.toString(), dataStr);
}

@Test
public void testEventEdit() {
Event event = PimDataService.events.get(0);
String id = event.getId();
event.setStartTime(DateUtil.date());
event.setAlarm(DateUtil.date());
event.setDescription("description edit");
// edit
PimDataService.saveEvent(event);

// get edit data
Event eventSaved = PimDataService.getEvent(id);
assertSame(event, eventSaved);
}

@Test
public void testContactAdd() {
Contact contact = new Contact();
String id = PimDataService.idWorker.nextId();
contact.setId(id);
contact.setName("name add");
contact.setMobile("mobile add");
contact.setAddress("address add");
// add
PimDataService.saveContact(contact);

// get add data
Contact contactSaved = PimDataService.getContact(id);
assertSame(contact, contactSaved);

// test print data
String dataStr = "Contact{" +
"id='" + id + '\'' +
", name='" + contactSaved.getName() + '\'' +
", address='" + contactSaved.getAddress() + '\'' +
", mobile='" + contactSaved.getMobile() + '\'' +
'}';
assertEquals(contact.toString(), dataStr);
}

@Test
public void testContactEdit() {
Contact contact = PimDataService.contacts.get(0);
String id = contact.getId();
contact.setName("name edit");
contact.setMobile("mobile edit");
contact.setAddress("address edit");
// edit
PimDataService.saveContact(contact);

// get edit data
Contact contactSaved = PimDataService.getContact(id);
assertSame(contact, contactSaved);
}

@Test
public void testAllPirData() { // 这个测试检查 PimDataService 中的 getAllPir() 方法是否返回了正确大小的列表。
List<Object> allPir = PimDataService.getAllPir();
assertEquals(4, allPir.size());
}

@Test
public void testGetPirById() { // 这个测试验证了 PimDataService 中的 getPirById() 方法是否能够通过它们的 ID 正确检索 Note,Task,Event,或 Contact。
String noteId = PimDataService.notes.get(0).getId();
Note note = (Note) PimDataService.getPirById(noteId);
assertTrue(note != null && note.getId().equals(noteId));

String taskId = PimDataService.tasks.get(0).getId();
Task task = (Task) PimDataService.getPirById(taskId);
assertTrue(task != null && task.getId().equals(taskId));

String eventId = PimDataService.events.get(0).getId();
Event event = (Event) PimDataService.getPirById(eventId);
assertTrue(event != null && event.getId().equals(eventId));

String contactId = PimDataService.contacts.get(0).getId();
Contact contact = (Contact) PimDataService.getPirById(contactId);
assertTrue(contact != null && contact.getId().equals(contactId));
}

@Test
public void testNoteDelete() { // 这个测试验证了可以正确删除 Note。它删除一个 Note 并检查之后是否无法检索到它。
Note note = PimDataService.notes.get(0);
String id = note.getId();
// delete
PimDataService.deleteNote(id);

// check delete data
Note noteDeleted = PimDataService.getNote(id);
assertSame(null, noteDeleted);
}

@Test
public void testTaskDelete() {
Task task = PimDataService.tasks.get(0);
String id = task.getId();
// delete
PimDataService.deleteTask(id);

// check delete data
Task taskDeleted = PimDataService.getTask(id);
assertSame(null, taskDeleted);
}

@Test
public void testEventDelete() {
Event event = PimDataService.events.get(0);
String id = event.getId();
// delete
PimDataService.deleteEvent(id);

// check delete data
Event eventDeleted = PimDataService.getEvent(id);
assertSame(null, eventDeleted);
}

@Test
public void testContactDelete() {
Contact contact = PimDataService.contacts.get(0);
String id = contact.getId();
// delete
PimDataService.deleteContact(id);

// check delete data
Contact contactDeleted = PimDataService.getContact(id);
assertSame(null, contactDeleted);
}

@Test
public void testEmptyPirData() { // 这个测试检查在调用 PimDataService 中的 loadPimData() 方法后,PimDataService 中的所有列表是否为空。
String imDataFilePath = "data/unit_test_data.pim";
PimDataService.loadPimData(imDataFilePath);

assertEquals(0, PimDataService.notes.size());
assertEquals(0, PimDataService.tasks.size());
assertEquals(0, PimDataService.events.size());
assertEquals(0, PimDataService.contacts.size());
}
}

Exception

在这个 PIM 项目中,主要涉及到的异常有:

  1. FileNotFoundException:当尝试打开的文件不存在时,将抛出此异常。在 PimDataService 类中,当尝试打开 .pim 文件以读取或写入数据时,可能会遇到这种异常。

  2. IOException:当发生 I/O 错误时,将抛出此异常。在 PimDataService 类中,当读取或写入 .pim 文件时,可能会遇到这种异常。

  3. ClassNotFoundException:当应用程序试图加载的类找不到时,将抛出此异常。在 PimDataService 类中,当尝试从 .pim 文件中反序列化对象时,可能会遇到这种异常。

  4. NumberFormatException:当应用程序试图将字符串转换为数字,但该字符串的格式不适合数字时,将抛出此异常。在各种控制器类中,当解析用户输入的命令时,可能会遇到这种异常。

  5. IllegalArgumentException:当向方法传递非法或不适当的参数时,将抛出此异常。在各种控制器类中,当解析用户输入的命令时,如果命令格式不正确,可能会遇到这种异常。

这些异常都需要在代码中进行适当的处理,以确保程序的健壮性。处理异常的策略可以是捕获并处理异常,或者让异常向上层代码传递。对于 IOExceptionFileNotFoundExceptionClassNotFoundException 这些必须处理的异常,一般会在方法签名中使用 throws 关键字声明,以让调用者知道需要处理这些异常。对于 NumberFormatExceptionIllegalArgumentException 这些运行时异常,则可以选择在适当的地方使用 try-catch 语句进行捕获和处理。

Data stored

该项目的数据存储在一个文件中。 默认文件路径 (default file path) 在PimDataService类中设置为“data/data.pim”。 如果该文件不存在,则创建该文件,用于存储笔记、任务、事件和联系人的序列化数据。

“PimDataService”类中的“loadPimData”方法负责将文件中的数据加载到相应的列表(“notes”、“tasks”、“events”、“contacts”)中。 savePimData 方法用于将这些列表中的数据保存回文件。

您可以在运行程序时通过传递命令行参数来指定不同的文件路径。 例如,如果您使用参数“mydata.pim”运行程序,则数据将从文件“mydata.pim”加载并保存到文件“mydata.pim”,而不是默认的“data/data.pim”。

.pim数据文件采用Java语言中序列化和反序列化的机制进行存储和加载,并转换为Model中的对象列表。
存储结构采用hashmap,约定每个list的key为PIR模型的类型:
 初始化创建.pim文件时,保存每种key为空list的hashmap对象;
 当系统进行数据的创建、更新、删除操作时,同步保存所有类型的list数据到hashmap,并持久化到.pim文件中。

The .pim data file uses the serialization and deserialization mechanism in the Java language to store and load, and is converted into a list of objects in the Model.
The storage structure uses hashmap, and it is agreed that the key of each list is the type of PIR model:
 When initializing the .pim file, save the hashmap object with an empty list for each key;
 When the system creates, updates, and deletes data, it synchronously saves all types of list data to hashmap and persists it in the .pim file.

MVC architecture

提供的项目确实包含了模型-视图-控制器 (MVC) 架构的元素,尽管它不是严格的实现。 虽然服务包存在于项目中,但它并不直接与传统的 MVC 模式保持一致。

在MVC架构中,职责通常划分如下:

  • 模型:代表应用程序的数据和业务逻辑。 它封装了数据模型及其相关操作。 在这个项目中,模型包包含ContactEventNoteTask类,它们代表数据实体。

  • 视图:处理应用程序的表示逻辑和用户界面。 在此项目中,视图主要由“Main”类中提供的命令行界面(CLI)表示。 它显示菜单、提示用户输入并显示结果。

  • 控制器:充当模型和视图之间的中介。 它从视图接收用户输入,与模型交互以对数据执行操作,并根据模型的状态更新视图。 在这个项目中,控制器包包含ContactControllerEventControllerNoteControllerTaskControllerSearchControllerPrintController等类。 这些类处理用户命令,调用“PimDataService”类(可以被视为模型的一部分)中的相应方法,并向视图提供必要的数据。

本项目中的服务包,特别是“PimDataService”类,充当数据服务层。它封装了加载、保存和管理存储在文件中的 PIM 数据的操作。 在存储和检索数据方面发挥作用,它可以被视为控制器用来与持久数据交互的支持组件 (It encapsulates operations for loading, saving, and managing PIM data stored in files. Playing a role in storing and retrieving data, it can be viewed as a supporting component used by controllers to interact with persistent data)。

综上所述,该项目虽然没有严格遵循传统的MVC架构,但在一定程度上确实融入了模型、视图和控制器之间的关注点分离。 该服务包提供数据管理功能,供应用程序中的控制器使用。


All My Projects Description
http://example.com/2023/06/26/ProjectsDescription/
Author
Rocky CHEN
Posted on
June 26, 2023
Licensed under