6a-spec-install 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,13 +48,13 @@ def extract_default_value(description: str) -> Optional[Any]:
48
48
  """从字段说明中提取默认值"""
49
49
  if not description:
50
50
  return None
51
-
51
+
52
52
  patterns = [
53
53
  r'默认[为值]?[::]\s*(\d+)',
54
54
  r'默认[为值]?(\d+)',
55
55
  r',默认[为值]?[::]?\s*(\d+)',
56
56
  ]
57
-
57
+
58
58
  for pattern in patterns:
59
59
  match = re.search(pattern, description)
60
60
  if match:
@@ -62,43 +62,44 @@ def extract_default_value(description: str) -> Optional[Any]:
62
62
  return int(match.group(1))
63
63
  except ValueError:
64
64
  return match.group(1)
65
-
65
+
66
66
  return None
67
67
 
68
68
 
69
69
  def parse_table_section(content: str) -> List[Dict[str, Any]]:
70
- """解析第三步:数据库物理设计部分的所有表结构"""
71
-
72
- step3_pattern = r'###\s*第三步[::]\s*数据库物理设计.*?\n'
73
- match = re.search(step3_pattern, content, re.IGNORECASE)
70
+ """解析数据库物理设计部分的所有表结构"""
71
+
72
+ # 只匹配「数据库物理设计」章节,不要求「第三步」等前缀(文档格式可能不同)
73
+ section_pattern = r'###\s*[^#\n]*数据库物理设计[^\n]*\n'
74
+ match = re.search(section_pattern, content)
74
75
  if not match:
75
- raise ValueError("未找到'第三步:数据库物理设计'章节")
76
-
76
+ raise ValueError("未找到'数据库物理设计'章节")
77
+
77
78
  start_pos = match.end()
78
79
  next_section = re.search(r'\n###\s+', content[start_pos:])
79
80
  if next_section:
80
81
  section_content = content[start_pos:start_pos + next_section.start()]
81
82
  else:
82
83
  section_content = content[start_pos:]
83
-
84
+
84
85
  tables = []
85
86
  table_pattern = r'#####\s+\d+\.\s+([^(]+)\s*\(([^)]+)\)'
86
87
  table_matches = list(re.finditer(table_pattern, section_content))
87
-
88
+
88
89
  for i, table_match in enumerate(table_matches):
89
90
  table_name_cn = table_match.group(1).strip()
90
91
  table_name = table_match.group(2).strip()
91
-
92
+
92
93
  if i + 1 < len(table_matches):
93
94
  table_end = table_matches[i + 1].start()
94
95
  else:
95
96
  table_end = len(section_content)
96
-
97
+
97
98
  table_section = section_content[table_match.end():table_end]
98
-
99
+
99
100
  table_info_pattern = r'\|\s*表中文名称\s*\|\s*([^|]+)\s*\|\s*\n\|\s*[::]\s*---\s*\|\s*[::]\s*---\s*\|\s*\n\|\s*表英文名称\s*\|\s*([^|]+)\s*\|\s*\n\|\s*说明\s*\|\s*([^|]+)\s*\|'
100
101
  table_info_match = re.search(table_info_pattern, table_section)
101
-
102
+
102
103
  if table_info_match:
103
104
  table_name_cn = table_info_match.group(1).strip()
104
105
  table_name = table_info_match.group(2).strip()
@@ -108,11 +109,11 @@ def parse_table_section(content: str) -> List[Dict[str, Any]]:
108
109
  desc_match = re.search(r'\|\s*说明\s*\|\s*([^|]+)\s*\|', table_section)
109
110
  if desc_match:
110
111
  description = desc_match.group(1).strip()
111
-
112
+
112
113
  fields = []
113
114
  field_table_pattern = r'\|\s*字段中文名称\s*\|\s*字段英文名称\s*\|\s*字段类型\s*\|\s*长度\s*\|\s*是否必填\s*\|\s*字段说明\s*\|'
114
115
  field_table_match = re.search(field_table_pattern, table_section)
115
-
116
+
116
117
  if field_table_match:
117
118
  table_start = field_table_match.end()
118
119
  table_end_match = re.search(r'\n\n|\n---|\n#####', table_section[table_start:])
@@ -120,13 +121,13 @@ def parse_table_section(content: str) -> List[Dict[str, Any]]:
120
121
  table_content = table_section[table_start:table_start + table_end_match.start()]
121
122
  else:
122
123
  table_content = table_section[table_start:]
123
-
124
+
124
125
  field_lines = [line.strip() for line in table_content.split('\n') if line.strip() and line.strip().startswith('|')]
125
-
126
+
126
127
  for line in field_lines:
127
128
  if re.match(r'^\|\s*[::]\s*---', line):
128
129
  continue
129
-
130
+
130
131
  parts = [p.strip() for p in line.split('|') if p.strip()]
131
132
  if len(parts) >= 6:
132
133
  field_name_cn = parts[0]
@@ -135,10 +136,10 @@ def parse_table_section(content: str) -> List[Dict[str, Any]]:
135
136
  length = parse_length(parts[3])
136
137
  required = parse_required(parts[4])
137
138
  field_description = parts[5]
138
-
139
+
139
140
  is_pk = is_primary_key(field_description) or 'GUID' in field_name.upper()
140
141
  default_value = extract_default_value(field_description)
141
-
142
+
142
143
  field = {
143
144
  "fieldName": field_name,
144
145
  "fieldNameCN": field_name_cn,
@@ -148,21 +149,21 @@ def parse_table_section(content: str) -> List[Dict[str, Any]]:
148
149
  "description": field_description,
149
150
  "isPrimaryKey": is_pk
150
151
  }
151
-
152
+
152
153
  if default_value is not None:
153
154
  field["defaultValue"] = default_value
154
-
155
+
155
156
  fields.append(field)
156
-
157
+
157
158
  table = {
158
159
  "tableName": table_name,
159
160
  "tableNameCN": table_name_cn,
160
161
  "description": description,
161
162
  "fields": fields
162
163
  }
163
-
164
+
164
165
  tables.append(table)
165
-
166
+
166
167
  return tables
167
168
 
168
169
 
@@ -171,7 +172,7 @@ def parse_table_section(content: str) -> List[Dict[str, Any]]:
171
172
  def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[str, Any]:
172
173
  """将字段类型映射为数据库类型和字段类型"""
173
174
  field_type_lower = field_type.lower()
174
-
175
+
175
176
  if "唯一标识" in field_type or "guid" in field_type_lower:
176
177
  return {
177
178
  "dbType": "uniqueidentifier",
@@ -180,7 +181,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
180
181
  "length": None,
181
182
  "decimalPrecision": 0
182
183
  }
183
-
184
+
184
185
  if "单行文本" in field_type:
185
186
  db_length = length if length and length > 0 else 512
186
187
  return {
@@ -190,7 +191,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
190
191
  "length": db_length,
191
192
  "decimalPrecision": 0
192
193
  }
193
-
194
+
194
195
  if "多行文本" in field_type:
195
196
  db_length = length if length and length > 0 else -1
196
197
  if db_length > 0 and db_length <= 512:
@@ -209,7 +210,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
209
210
  "length": -1,
210
211
  "decimalPrecision": 0
211
212
  }
212
-
213
+
213
214
  if "日期时间" in field_type or "日期" in field_type or "时间" in field_type:
214
215
  return {
215
216
  "dbType": "datetime",
@@ -218,7 +219,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
218
219
  "length": None,
219
220
  "decimalPrecision": 0
220
221
  }
221
-
222
+
222
223
  if "整数" in field_type:
223
224
  return {
224
225
  "dbType": "int",
@@ -227,7 +228,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
227
228
  "length": None,
228
229
  "decimalPrecision": 0
229
230
  }
230
-
231
+
231
232
  if "图片" in field_type:
232
233
  return {
233
234
  "dbType": "nvarchar",
@@ -236,7 +237,7 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
236
237
  "length": -1,
237
238
  "decimalPrecision": 0
238
239
  }
239
-
240
+
240
241
  db_length = length if length and length > 0 else 512
241
242
  return {
242
243
  "dbType": "nvarchar",
@@ -247,10 +248,10 @@ def map_field_type_to_db_type(field_type: str, length: Optional[int]) -> Dict[st
247
248
  }
248
249
 
249
250
 
250
- def create_control_config(field_type: str, db_type: str, field_type_name: str,
251
- display_name: str, is_required: bool, length: Optional[int]) -> Dict[str, Any]:
251
+ def create_control_config(field_type: str, db_type: str, field_type_name: str,
252
+ display_name: str, is_required: bool, length: Optional[int]) -> Dict[str, Any]:
252
253
  """创建控件配置对象"""
253
-
254
+
254
255
  if field_type_name == "guid":
255
256
  return {
256
257
  "$type": "Mysoft.Map6.Metadata.Models.Entity.GuidField, Mysoft.Map6.Metadata.Models",
@@ -303,7 +304,7 @@ def create_control_config(field_type: str, db_type: str, field_type_name: str,
303
304
  "limitType": "*.*",
304
305
  "fillExample": ""
305
306
  }
306
-
307
+
307
308
  return {
308
309
  "$type": "Mysoft.Map6.Metadata.Models.Entity.SingleLineText, Mysoft.Map6.Metadata.Models",
309
310
  "isRequired": is_required,
@@ -315,7 +316,7 @@ def create_control_config(field_type: str, db_type: str, field_type_name: str,
315
316
 
316
317
  def convert_field_to_attribute(field: Dict[str, Any], is_primary: bool) -> Dict[str, Any]:
317
318
  """将字段转换为属性格式"""
318
-
319
+
319
320
  field_type = field.get("fieldType", "")
320
321
  length = field.get("length")
321
322
  field_name = field.get("fieldName", "")
@@ -323,25 +324,25 @@ def convert_field_to_attribute(field: Dict[str, Any], is_primary: bool) -> Dict[
323
324
  description = field.get("description", "")
324
325
  required = field.get("required", False)
325
326
  default_value = field.get("defaultValue")
326
-
327
+
327
328
  type_info = map_field_type_to_db_type(field_type, length)
328
-
329
+
329
330
  if is_primary and type_info["fieldType"] == "guid":
330
331
  attr_default_value = "newid()"
331
332
  elif default_value is not None:
332
333
  attr_default_value = str(default_value)
333
334
  else:
334
335
  attr_default_value = ""
335
-
336
+
336
337
  control = create_control_config(
337
- field_type,
338
- type_info["dbType"],
338
+ field_type,
339
+ type_info["dbType"],
339
340
  type_info["fieldType"],
340
341
  field_name_cn,
341
342
  required,
342
343
  type_info["length"]
343
344
  )
344
-
345
+
345
346
  attribute = {
346
347
  "attributeType": type_info["attributeType"],
347
348
  "defaultValue": attr_default_value,
@@ -357,21 +358,21 @@ def convert_field_to_attribute(field: Dict[str, Any], is_primary: bool) -> Dict[
357
358
  "curEntityState": "modify",
358
359
  "entityState": "Created"
359
360
  }
360
-
361
+
361
362
  if type_info["length"] is not None:
362
363
  attribute["length"] = type_info["length"]
363
-
364
+
364
365
  return attribute
365
366
 
366
367
 
367
368
  def convert_table_to_entity(table: Dict[str, Any], application: str, function_guid: str) -> Dict[str, Any]:
368
369
  """将表转换为实体格式"""
369
-
370
+
370
371
  table_name = table.get("tableName", "")
371
372
  table_name_cn = table.get("tableNameCN", "")
372
373
  description = table.get("description", "")
373
374
  fields = table.get("fields", [])
374
-
375
+
375
376
  primary_key_field = None
376
377
  primary_key_name = None
377
378
  for field in fields:
@@ -379,7 +380,7 @@ def convert_table_to_entity(table: Dict[str, Any], application: str, function_gu
379
380
  primary_key_field = field
380
381
  primary_key_name = field.get("fieldName", "")
381
382
  break
382
-
383
+
383
384
  if not primary_key_name:
384
385
  for field in fields:
385
386
  if "GUID" in field.get("fieldName", "").upper() or "唯一标识" in field.get("fieldType", ""):
@@ -389,7 +390,7 @@ def convert_table_to_entity(table: Dict[str, Any], application: str, function_gu
389
390
  if not primary_key_name and fields:
390
391
  primary_key_name = fields[0].get("fieldName", "")
391
392
  primary_key_field = fields[0]
392
-
393
+
393
394
  attributes = []
394
395
  for field in fields:
395
396
  field_name = field.get("fieldName", "")
@@ -398,11 +399,11 @@ def convert_table_to_entity(table: Dict[str, Any], application: str, function_gu
398
399
  is_primary = False
399
400
  attribute = convert_field_to_attribute(field, is_primary)
400
401
  attributes.append(attribute)
401
-
402
+
402
403
  primary_key_display_name = ""
403
404
  if primary_key_field:
404
405
  primary_key_display_name = primary_key_field.get("fieldNameCN", "")
405
-
406
+
406
407
  entity = {
407
408
  "entity": {
408
409
  "application": application,
@@ -420,7 +421,7 @@ def convert_table_to_entity(table: Dict[str, Any], application: str, function_gu
420
421
  "primaryKeyDisplayName": primary_key_display_name + " ",
421
422
  "primaryKeyRemark": ""
422
423
  }
423
-
424
+
424
425
  return entity
425
426
 
426
427
 
@@ -435,7 +436,7 @@ def load_config_from_file() -> Dict[str, str]:
435
436
  """从用户目录下的.6aspec/modeling_env文件读取配置"""
436
437
  config = {}
437
438
  config_file = Path.home() / '.6aspec' / 'modeling_env'
438
-
439
+
439
440
  if config_file.exists():
440
441
  try:
441
442
  with open(config_file, 'r', encoding='utf-8') as f:
@@ -453,7 +454,7 @@ def load_config_from_file() -> Dict[str, str]:
453
454
  except Exception:
454
455
  # 读取配置文件失败,忽略错误,继续使用环境变量
455
456
  pass
456
-
457
+
457
458
  return config
458
459
 
459
460
 
@@ -463,13 +464,13 @@ def get_host() -> str:
463
464
  host = os.environ.get('MODELING_HOST')
464
465
  if host:
465
466
  return host
466
-
467
+
467
468
  # 如果环境变量没有,尝试从配置文件读取
468
469
  config = load_config_from_file()
469
470
  host = config.get('MODELING_HOST')
470
471
  if host:
471
472
  return host
472
-
473
+
473
474
  # 都没有则返回默认值
474
475
  return 'localhost'
475
476
 
@@ -480,13 +481,13 @@ def get_cookie() -> str:
480
481
  cookie = os.environ.get('MODELING_COOKIE')
481
482
  if cookie:
482
483
  return cookie
483
-
484
+
484
485
  # 如果环境变量没有,尝试从配置文件读取
485
486
  config = load_config_from_file()
486
487
  cookie = config.get('MODELING_COOKIE')
487
488
  if cookie:
488
489
  return cookie
489
-
490
+
490
491
  # 都没有则抛出异常
491
492
  raise ValueError(
492
493
  "错误:未配置Cookie\n"
@@ -499,20 +500,20 @@ def get_cookie() -> str:
499
500
 
500
501
 
501
502
  def send_create_entity_request(
502
- design_data: str,
503
- host: Optional[str] = None,
504
- port: int = 9300,
505
- cookie: Optional[str] = None
503
+ design_data: str,
504
+ host: Optional[str] = None,
505
+ port: int = 9300,
506
+ cookie: Optional[str] = None
506
507
  ) -> requests.Response:
507
508
  """发送创建实体请求"""
508
509
  if host is None:
509
510
  host = get_host()
510
-
511
+
511
512
  if cookie is None:
512
513
  cookie = get_cookie()
513
-
514
+
514
515
  url = f"http://{host}:{port}/ajax/Mysoft.Map6.Modeling.Handlers.EntityHandler/CreateEntity"
515
-
516
+
516
517
  headers = {
517
518
  'Accept': 'application/json, text/plain, */*',
518
519
  'Accept-Language': 'zh-CN,zh;q=0.9',
@@ -527,11 +528,11 @@ def send_create_entity_request(
527
528
  'work-mode': 'design',
528
529
  'Cookie': cookie
529
530
  }
530
-
531
+
531
532
  data = {
532
533
  'designData': design_data
533
534
  }
534
-
535
+
535
536
  response = requests.post(url, headers=headers, data=data)
536
537
  return response
537
538
 
@@ -547,26 +548,26 @@ def main():
547
548
  parser.add_argument('-p', '--port', type=int, default=9300, help='服务器端口(默认:9300)')
548
549
  parser.add_argument('-c', '--cookie', type=str, help='Cookie值(默认从环境变量MODELING_COOKIE获取)')
549
550
  parser.add_argument('-v', '--verbose', action='store_true', help='显示详细响应信息')
550
-
551
+
551
552
  args = parser.parse_args()
552
-
553
+
553
554
  # 步骤1:读取并解析Markdown文档
554
555
  print("=" * 60)
555
556
  print("步骤1:解析Markdown文档...")
556
557
  print("=" * 60)
557
-
558
+
558
559
  md_path = Path(args.markdown_file)
559
560
  if not md_path.exists():
560
561
  print(f"错误:文件不存在: {args.markdown_file}")
561
562
  return 1
562
-
563
+
563
564
  try:
564
565
  with open(md_path, 'r', encoding='utf-8') as f:
565
566
  content = f.read()
566
567
  except Exception as e:
567
568
  print(f"错误:读取文件失败: {e}")
568
569
  return 1
569
-
570
+
570
571
  try:
571
572
  tables = parse_table_section(content)
572
573
  print(f"✓ 成功解析 {len(tables)} 个表")
@@ -575,12 +576,12 @@ def main():
575
576
  except Exception as e:
576
577
  print(f"错误:解析失败: {e}")
577
578
  return 1
578
-
579
+
579
580
  # 步骤2:转换为实体JSON
580
581
  print("\n" + "=" * 60)
581
582
  print("步骤2:转换为实体JSON格式...")
582
583
  print("=" * 60)
583
-
584
+
584
585
  try:
585
586
  entities = convert_tables_to_entities(tables, args.application, args.function_guid)
586
587
  print(f"✓ 成功转换 {len(entities)} 个实体")
@@ -589,32 +590,32 @@ def main():
589
590
  import traceback
590
591
  traceback.print_exc()
591
592
  return 1
592
-
593
+
593
594
  # 步骤3:批量发送HTTP请求创建表
594
595
  print("\n" + "=" * 60)
595
596
  print("步骤3:批量创建实体表...")
596
597
  print("=" * 60)
597
-
598
+
598
599
  total = len(entities)
599
600
  success_count = 0
600
601
  fail_count = 0
601
-
602
+
602
603
  print(f"检测到 {total} 个实体,开始批量发送...\n")
603
-
604
+
604
605
  for index, entity in enumerate(entities, 1):
605
606
  try:
606
607
  entity_name = entity.get('entity', {}).get('name', f'实体#{index}')
607
608
  print(f"[{index}/{total}] 处理实体: {entity_name}")
608
-
609
+
609
610
  design_data = json.dumps(entity, ensure_ascii=False)
610
-
611
+
611
612
  response = send_create_entity_request(
612
613
  design_data=design_data,
613
614
  host=args.host,
614
615
  port=args.port,
615
616
  cookie=args.cookie
616
617
  )
617
-
618
+
618
619
  if response.status_code == 200:
619
620
  try:
620
621
  response_json = response.json()
@@ -656,7 +657,7 @@ def main():
656
657
  print(f" 响应: {json.dumps(response_json, ensure_ascii=False, indent=2)}")
657
658
  except json.JSONDecodeError:
658
659
  print(f" 响应: {response.text[:200]}")
659
-
660
+
660
661
  except ValueError as e:
661
662
  print(f" ✗ 错误: {str(e)}")
662
663
  return 1
@@ -669,9 +670,9 @@ def main():
669
670
  if args.verbose:
670
671
  import traceback
671
672
  traceback.print_exc()
672
-
673
+
673
674
  print()
674
-
675
+
675
676
  # 输出最终结果
676
677
  print("=" * 60)
677
678
  print("批量处理完成:")
@@ -679,10 +680,9 @@ def main():
679
680
  print(f" 成功: {success_count}")
680
681
  print(f" 失败: {fail_count}")
681
682
  print("=" * 60)
682
-
683
+
683
684
  return 0 if fail_count == 0 else 1
684
685
 
685
686
 
686
687
  if __name__ == '__main__':
687
- exit(main())
688
-
688
+ exit(main())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "6a-spec-install",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "6A-spec 驱动开发提示词安装工具,支持 Cursor 和 Claude",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {