Post

使用AI工具DeepSeek

最近项目接口从proto格式转换为json,之前proto时模型对象是自动生成,很方便,现在变成json后,接口文档的字段要手动一个个转换成属性并赋值,过程很麻烦还容易出错,于是研究了下各个工具都大同小异,基本都是基于json内容来反向定义,这样内容的注释、类型适配的不是很好,于是准备自己写一套工具,但是整个逻辑还是比较复杂,再加上最近各种AI工具很发达,一开始想使用openAI,后面想着试用下最近很火的DeepSeek,使用后感觉特别棒,基本一行代码不写,直接把需求发出来,内容不对的及时纠正,用一个多小时就自动处理好了,感受到了AI的强大,这个已经超过绝大部分开发人员的理解力、逻辑力了,既震撼、期待,也有些对未来的焦虑 效果: image image2 生成的工具:

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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Swift Model Generator</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            height: 100vh;
        }
        .left, .right {
            flex: 1;
            padding: 20px;
            box-sizing: border-box;
        }
        .left {
            background-color: #f9f9f9;
            border-right: 1px solid #ddd;
        }
        .right {
            background-color: #fff;
            position: relative;
        }
        textarea {
            width: 100%;
            height: calc(100% - 260px);
            font-family: monospace;
            font-size: 14px;
            padding: 10px;
            box-sizing: border-box;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        pre {
            margin: 0;
            padding: 10px;
            background-color: #f4f4f4;
            border: 1px solid #ccc;
            border-radius: 4px;
            height: calc(100% - 120px); /* 调整高度以留出按钮空间 */
            overflow-y: auto;
            font-family: monospace;
            font-size: 14px;
        }
        h2 {
            margin-top: 0;
        }
        .controls {
            margin-bottom: 20px;
        }
        .controls label {
            margin-right: 10px;
        }
        .controls input, .controls select {
            padding: 5px;
            font-size: 14px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .optional-toggle {
            margin-top: 10px;
        }
        .optional-toggle label {
            margin-right: 10px;
        }
        .run-button {
            margin-top: 20px;
            text-align: center;
        }
        .run-button button {
            padding: 10px 20px;
            font-size: 16px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .run-button button:hover {
            background-color: #0056b3;
        }
        .copy-button {
            position: absolute;
            bottom: 20px;
            left: 20px;
            margin-top: 10px; /* 增加与生成内容框的间距 */
        }
        .copy-button button {
            padding: 5px 10px;
            font-size: 14px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .copy-button button:hover {
            background-color: #218838;
        }
        .toast {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: #333;
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            font-size: 14px;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.3s ease-in-out;
        }
        .toast.show {
            opacity: 1;
        }
    </style>
</head>
<body>

<div class="left">
    <h2>输入内容</h2>
    <div class="controls">
        <label for="modelName">模型名称:</label>
        <input type="text" id="modelName" value="MyModel" oninput="generateSwiftModel()">
        <label for="modelType">模型类型:</label>
        <select id="modelType" onchange="generateSwiftModel()">
            <option value="struct">struct</option>
            <option value="class">class</option>
        </select>
    </div>
    <div class="optional-toggle">
        <label for="isOptional">属性是否为可选值:</label>
        <input type="checkbox" id="isOptional" onchange="generateSwiftModel()">
    </div>
    <div class="optional-toggle">
        <label for="implementMapJsonProtocol">实现 MapJsonProtocol:</label>
        <input type="checkbox" id="implementMapJsonProtocol" checked onchange="generateSwiftModel()">
        <pre>
protocol MapJsonProtocol {
    // 字典转json
    static func map2Model&lt;T&gt;(dict: [String: Any]) -> T where T: MapJsonProtocol
}
        </pre>
    </div>
    <div class="optional-toggle">
        <label for="implementArchiveJsonProtocol">实现 ArchiveJsonProtocol:</label>
        <input type="checkbox" id="implementArchiveJsonProtocol" onchange="generateSwiftModel()">
        <pre>
protocol ArchiveJsonProtocol {
    // json反序列化
    static func data2Model&lt;T&gt;(_ data: Data) -> T? where T: ArchiveJsonProtocol
    // json序列化
    func model2Data() -> Data?
}
        </pre>
    </div>
    <textarea id="inputData" placeholder="Enter your data here..." oninput="generateSwiftModel()"></textarea>
    <div class="run-button">
        <button onclick="generateSwiftModel()">生成 Swift 模型</button>
    </div>
</div>

<div class="right">
    <h2>生成的 Swift 模型</h2>
    <pre id="outputModel"></pre>
    <div class="copy-button">
        <button onclick="copyToClipboard()">复制代码</button>
    </div>
</div>

<!-- Toast 提示 -->
<div id="toast" class="toast">代码已复制!</div>

<script>
    function generateSwiftModel() {
        const inputData = document.getElementById('inputData').value;
        const modelName = document.getElementById('modelName').value || 'MyModel';
        const modelType = document.getElementById('modelType').value;
        const isOptional = document.getElementById('isOptional').checked;
        const implementMapJsonProtocol = document.getElementById('implementMapJsonProtocol').checked;
        const implementArchiveJsonProtocol = document.getElementById('implementArchiveJsonProtocol').checked;

        const lines = inputData.split('\n');
        let swiftModel = `${modelType} ${modelName}: Codable`;

        if (implementMapJsonProtocol) {
            swiftModel += `, MapJsonProtocol`;
        }

        if (implementArchiveJsonProtocol) {
            swiftModel += `, ArchiveJsonProtocol`;
        }

        swiftModel += ` {\n`;

        const properties = [];

        lines.forEach(line => {
            const trimmedLine = line.trim();
            if (trimmedLine) {
                const parts = trimmedLine.split(/\s+/);
                if (parts.length >= 3) {
                    const propertyName = parts[0];
                    const lastPart = parts[parts.length - 1];
                    let type = lastPart;
                    let comment = '';

                    const arrayIndex = parts.indexOf('array');
                    if (arrayIndex !== -1) {
                        type = parts.slice(arrayIndex).join(' ');
                        comment = parts.slice(1, arrayIndex).join(' ');
                    } else {
                        comment = parts.slice(1, parts.length - 1).join(' ');
                    }
                    comment = comment.replace("\tinteger(int32)", "")
                    comment = comment.replace(" integer(int32)", "")
                    comment = comment.replace("\tinteger(int64)", "")
                    comment = comment.replace(" integer(int64)", "")
                    comment = comment.replace("\tstring(date-time)", "")
                    comment = comment.replace("string(date-time)", "")
                    comment = comment.replace("\tstring", "")
                    comment = comment.replace(" string", "")
                    
                    comment = comment.trim()

                    let swiftType;
                    if (type.startsWith('array')) {
                        const arrayType = type.split(' ')[1];
                        swiftType = `[${mapType(arrayType)}]`;
                    } else {
                        swiftType = mapType(type);
                    }

                    let propertyDeclaration;
                    if (isOptional) {
                        propertyDeclaration = `var ${propertyName}: ${swiftType}?`;
                    } else {
                        const defaultValue = getDefaultValue(swiftType);
                        propertyDeclaration = `var ${propertyName}: ${swiftType} = ${defaultValue}`;
                    }

                    swiftModel += `    // ${comment}\n`;
                    swiftModel += `    ${propertyDeclaration}\n\n`;

                    properties.push({ name: propertyName, type: swiftType, isOptional, isArray: type.startsWith('array'), arrayType: type.startsWith('array') ? mapType(type.split(' ')[1]) : null });
                }
            }
        });

        swiftModel += `    static func dict2Model(dict: [String: Any]) -> ${modelName} {\n`;
        if (modelType == "class") {
            swiftModel += `        let model = ${modelName}()\n`;
        } else {
            swiftModel += `        var model = ${modelName}()\n`;
        }
        
        properties.forEach(prop => {
            if (prop.isArray) {
                if (isBasicType(prop.arrayType)) {
                    if (prop.isOptional) {
                        swiftModel += `        model.${prop.name} = dict.get${prop.type.slice(1, -1)}List("${prop.name}")\n`;
                    } else {
                        swiftModel += `        model.${prop.name} = dict.get${prop.type.slice(1, -1)}List("${prop.name}") ?? []\n`;
                    }
                } else {
                    if (prop.isOptional) {
                        swiftModel += `        model.${prop.name} = (dict["${prop.name}"] as? [[String: Any]])?.map { ${prop.arrayType}.dict2Model(dict: $0) }\n`;
                    } else {
                        swiftModel += `        model.${prop.name} = (dict["${prop.name}"] as? [[String: Any]])?.map { ${prop.arrayType}.dict2Model(dict: $0) } ?? []\n`;
                    }
                }
            } else if (isBasicType(prop.type)) {
                if (prop.isOptional) {
                    // swiftModel += `        model.${prop.name} = dict["${prop.name}"] as? ${prop.type}\n`;
                    swiftModel += `        model.${prop.name} = dict.get${prop.type}("${prop.name}")\n`;
                } else {
                    const defaultValue = getDefaultValue(prop.type);
                    swiftModel += `        model.${prop.name} = dict.get${prop.type}("${prop.name}") ?? ${defaultValue}\n`;
                }
            } else {
                if (prop.isOptional) {
                    swiftModel += `        model.${prop.name} = (dict["${prop.name}"] as? [String: Any]).map { ${prop.type}.dict2Model(dict: $0) }\n`;
                } else {
                    swiftModel += `        model.${prop.name} = ${prop.type}.dict2Model(dict: dict["${prop.name}"] as? [String: Any] ?? [: ])\n`;
                }
            }
        });
        swiftModel += `        return model\n`;
        swiftModel += `    }\n`;

        if (implementMapJsonProtocol) {
            swiftModel += `\n    // MARK: - MapJsonProtocol\n`;
            swiftModel += `    static func map2Model<T>(dict: [String: Any]) -> T where T: MapJsonProtocol {\n`;
            swiftModel += `        return ${modelName}.dict2Model(dict: dict) as! T\n`;
            swiftModel += `    }\n`;
        }

        if (implementArchiveJsonProtocol) {
            swiftModel += `\n    // MARK: - ArchiveJsonProtocol\n`;
            swiftModel += `    static func data2Model<T>(_ data: Data) -> T? where T: ArchiveJsonProtocol {\n`;
            swiftModel += `        return try? JSONDecoder().decode(${modelName}.self, from: data) as? T\n`;
            swiftModel += `    }\n\n`;
            swiftModel += `    // MARK: json序列化\n`;
            swiftModel += `    func model2Data() -> Data? {\n`;
            swiftModel += `        return try? JSONEncoder().encode(self)\n`;
            swiftModel += `    }\n`;
        }

        swiftModel += '}';
        document.getElementById('outputModel').textContent = swiftModel;
    }

    function copyToClipboard() {
        const outputModel = document.getElementById('outputModel');
        const text = outputModel.textContent;

        if (navigator.clipboard) {
            navigator.clipboard.writeText(text)
                .then(() => {
                    showToast('代码已复制!');
                })
                .catch(() => {
                    fallbackCopyToClipboard(text);
                });
        } else {
            fallbackCopyToClipboard(text);
        }
    }

    function fallbackCopyToClipboard(text) {
        const textarea = document.createElement('textarea');
        textarea.value = text;
        document.body.appendChild(textarea);
        textarea.select();
        try {
            document.execCommand('copy');
            showToast('代码已复制!');
        } catch (err) {
            showToast('复制失败,请手动复制。');
        }
        document.body.removeChild(textarea);
    }

    function showToast(message) {
        const toast = document.getElementById('toast');
        toast.textContent = message;
        toast.classList.add('show');
        setTimeout(() => {
            toast.classList.remove('show');
        }, 2000);
    }

    const basicTypes = {
        'string': 'String',
        'String': 'String',
        'integer(int32)': 'Int32',
        'int32': 'Int32',
        'Int32': 'Int32',
        'integer(int64)': 'Int64',
        'int64': 'Int64',
        'Int64': 'Int64',
        'integer': 'Int',
        'int': 'Int',
        'Int': 'Int',
        'number': 'String',
        'Double': 'Double',
        'string(date-time)': 'Int64',
        'boolean': 'Bool',
        'Bool': 'Bool'
    };

    function isBasicType(type) {
        return basicTypes[type] !== undefined;
    }

    function mapType(type) {
        return basicTypes[type] || type;
    }

    function getDefaultValue(type) {
        if (type.endsWith('?')) {
            type = type.slice(0, -1);
        }
        if (type.startsWith('[') && type.endsWith(']')) {
            return '[]';
        } else if (isBasicType(type)) {
            switch (type) {
                case 'String':
                    return '""';
                case 'Int':
                case 'Int32':
                case 'Int64':
                    return '0';
                case 'Double':
                    return '0.0';
                case 'Bool':
                    return 'false';
                default:
                    return '""';
            }
        } else {
            return `${type}()`;
        }
    }

    // 初始化时生成一次
    generateSwiftModel();
</script>

</body>
</html>

配合从dict里获取key的方法

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
extension Dictionary where Key == String, Value == Any {
    func getString(_ key: String) -> String? {
        guard let val = self[key] else {
            return nil
        }
        if let strVal = val as? String {
            return strVal
        }
        if let intVal = val as? Int64 {
            return intVal.strValue
        }
        if let intVal = val as? Int {
            return intVal.strValue
        }
        if let intVal = val as? Int32 {
            return intVal.strValue
        }
        if let dVal = val as? Double {
            //return dVal.toString()
            let strV = "\(dVal)"
            return strV
        }
        return nil
    }
    func getInt64(_ key: String) -> Int64? {
        guard let val = self[key] else {
            return nil
        }
        if let intVal = val as? Int64 {
            return intVal
        }
        if let strVal = val as? String {
            return strVal.toInt64()
        }
        if let intVal = val as? Int {
            return Int64(intVal)
        }
        if let intVal = val as? Int32 {
            return Int64(intVal)
        }
        if let dVal = val as? Double {
            return Int64(dVal)
        }
        return nil
    }
    func getInt(_ key: String) -> Int? {
        guard let val = self[key] else {
            return nil
        }
        if let intVal = val as? Int {
            return intVal
        }
        if let strVal = val as? String {
            return strVal.toInt()
        }
        if let intVal = val as? Int64 {
            return Int(intVal)
        }
        if let intVal = val as? Int32 {
            return Int(intVal)
        }
        if let dVal = val as? Double {
            return Int(dVal)
        }
        return nil
    }
    func getInt32(_ key: String) -> Int32? {
        guard let val = self[key] else {
            return nil
        }
        if let intVal = val as? Int32 {
            return intVal
        }
        if let strVal = val as? String {
            return strVal.toInt32()
        }
        if let intVal = val as? Int64 {
            return Int32(intVal)
        }
        if let intVal = val as? Int {
            return Int32(intVal)
        }
        if let dVal = val as? Double {
            return Int32(dVal)
        }
        return nil
    }
    func getDouble(_ key: String) -> Double? {
        guard let val = self[key] else {
            return nil
        }
        if let intVal = val as? Double {
            return intVal
        }
        if let strVal = val as? String {
            return strVal.toDouble()
        }
        if let intVal = val as? Int64 {
            return Double(intVal)
        }
        if let intVal = val as? Int {
            return Double(intVal)
        }
        if let dVal = val as? Int32 {
            return Double(dVal)
        }
        return nil
    }
    func getBool(_ key: String) -> Bool? {
        guard let val = self[key] else {
            return nil
        }
        if let intVal = val as? Bool {
            return intVal
        }
        if let strVal = val as? String {
            let lowStr = strVal.lowercased()
            if  lowStr == "true" || lowStr == "1" {
                return true
            }
            if  lowStr == "false" || lowStr == "0" {
                return false
            }
            return false
        }
        if let intVal = val as? Int64 {
            return intVal == 1
        }
        if let intVal = val as? Int {
            return intVal == 1
        }
        if let dVal = val as? Int32 {
            return dVal == 1
        }
        return false
    }
    func getInt32List(_ key: String) -> [Int32]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getInt32("key") }
        return list
    }
    func getIntList(_ key: String) -> [Int]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getInt("key") }
        return list
    }
    func getInt64List(_ key: String) -> [Int64]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getInt64("key") }
        return list
    }
    func getDoubleList(_ key: String) -> [Double]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getDouble("key") }
        return list
    }
    func getBoolList(_ key: String) -> [Bool]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getBool("key") }
        return list
    }
    func getStringList(_ key: String) -> [String]? {
        guard let val = self[key] else {
            return nil
        }
        guard let listVal = val as? [Any] else {
            return []
        }
        let list = listVal.compactMap{ ["key": $0].getString("key") }
        return list
    }
}
This post is licensed under CC BY 4.0 by the author.