migrate_camel_java.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. # -*- coding: utf-8 -*-
  2. """Java 侧驼峰迁移:
  3. 1. application.yml 移除 jackson.property-naming-strategy: SNAKE_CASE
  4. 2. @JsonProperty("snake_case") -> @JsonProperty("camelCase")
  5. 3. Map 出入参 key(.put/.get/.remove/.has/.containsKey)snake -> camel
  6. 排除 Redis 登录态内部存储(LoginInfo / RedisTokenStore),那是自造 key,与 API 无关
  7. 4. @Select SQL 里 `as snake_alias` 输出别名 -> camel
  8. 注意:绝不动 MyBatis 条件列名(eq/orderBy 等),那才是数据库字段。
  9. """
  10. import re
  11. from pathlib import Path
  12. from _root import ROOT
  13. SRC = ROOT / "yoshop-java" / "src" / "main" / "java"
  14. YML = ROOT / "yoshop-java" / "src" / "main" / "resources" / "application.yml"
  15. # Redis 登录态内部存储 key 不转(自造 JSON,与 API 出入参无关)
  16. EXCLUDE_FILES = {
  17. SRC / "cn" / "yoshop" / "common" / "auth" / "LoginInfo.java",
  18. SRC / "cn" / "yoshop" / "common" / "auth" / "RedisTokenStore.java",
  19. }
  20. SNAKE = re.compile(r'^[a-z]+(_[a-z0-9]+)+$')
  21. def to_camel(s: str) -> str:
  22. return re.sub(r'_([a-z0-9])', lambda m: m.group(1).upper(), s)
  23. # 方法名 -> 是否处理。只处理 Map/JsonNode 的 key 读写,不碰数据库列名。
  24. KEY_METHODS = ("put", "putIfAbsent", "get", "remove", "has", "containsKey", "getOrDefault")
  25. CALL = re.compile(r'\.(' + "|".join(KEY_METHODS) + r')\("([a-z0-9_]+)"(?=[,)])')
  26. JP = re.compile(r'@JsonProperty\("([a-z0-9_]+)"\)')
  27. SQL_AS = re.compile(r'\bas ([a-z]+(_[a-z0-9]+)+)\b')
  28. changed_files = []
  29. total = {"jp": 0, "call": 0, "sql": 0}
  30. for f in sorted(SRC.rglob("*.java")):
  31. text = f.read_text(encoding="utf-8")
  32. orig = text
  33. def jp_repl(m):
  34. total["jp"] += 1
  35. return '@JsonProperty("%s")' % to_camel(m.group(1))
  36. def call_repl(m):
  37. key = m.group(2)
  38. if not SNAKE.match(key):
  39. return m.group(0)
  40. total["call"] += 1
  41. return '.%s("%s")' % (m.group(1), to_camel(key))
  42. def sql_repl(m):
  43. total["sql"] += 1
  44. return 'as %s' % to_camel(m.group(1))
  45. text = JP.sub(jp_repl, text)
  46. if f not in EXCLUDE_FILES:
  47. text = CALL.sub(call_repl, text)
  48. text = SQL_AS.sub(sql_repl, text)
  49. if text != orig:
  50. f.write_text(text, encoding="utf-8")
  51. changed_files.append(str(f.relative_to(SRC)))
  52. # application.yml:移除全局 SNAKE_CASE
  53. yml_text = YML.read_text(encoding="utf-8")
  54. new_yml = re.sub(r'\n[ \t]*property-naming-strategy:.*', '', yml_text)
  55. if new_yml != yml_text:
  56. YML.write_text(new_yml, encoding="utf-8")
  57. print("application.yml: 已移除 property-naming-strategy: SNAKE_CASE")
  58. print("改动文件 %d 个;@JsonProperty %d 处、Map key %d 处、SQL 别名 %d 处"
  59. % (len(changed_files), total["jp"], total["call"], total["sql"]))
  60. for n in changed_files:
  61. print(" ", n)