gen_entities.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. # -*- coding: utf-8 -*-
  2. """按数据库表结构生成 Java 实体 + Mapper 骨架。
  3. 生成规则与项目既有实体保持一致:
  4. - 表名为全名(MyBatis-Plus 的 table-prefix 对显式 @TableName 无效)
  5. - int/tinyint -> Integer,bigint -> Long,decimal -> BigDecimal(ToStringSerializer,PHP 输出字符串)
  6. - double -> Double(PhpFloatSerializer,对齐 PHP 1.0 -> 1)
  7. - create_time / update_time -> @JsonIgnore Integer + 输出格式化字符串的 getter
  8. - is_delete -> @TableLogic(value="0", delval="1")(本库统一 0/1)
  9. """
  10. import re
  11. import sys
  12. import pymysql
  13. from pathlib import Path
  14. YML = r"C:\Users\Administrator\WorkBuddy\2026-10-17-12-27-10\java-rewrite\yoshop-java\src\main\resources\application.yml"
  15. ROOT = Path(r"C:\Users\Administrator\WorkBuddy\2026-10-17-12-27-10\java-rewrite\yoshop-java\src\main\java\cn\yoshop")
  16. pwd = re.findall(r"password:\s*(.+)", open(YML, encoding="utf-8").read())[0].strip().strip("\"'")
  17. # 表名 -> (类名, 说明, 需转义的列)
  18. TABLES = {
  19. "yoshop_category": ("Category", "商品分类"),
  20. "yoshop_goods_service": ("GoodsService", "商品服务(保障服务)"),
  21. "yoshop_upload_group": ("UploadGroup", "文件库分组"),
  22. "yoshop_region": ("Region", "地区(省/市/区)"),
  23. "yoshop_user": ("User", "商城会员"),
  24. "yoshop_user_grade": ("UserGrade", "会员等级"),
  25. "yoshop_page": ("PageEntity", "页面(首页/自定义页)"),
  26. "yoshop_article_category": ("ArticleCategory", "文章分类"),
  27. "yoshop_article": ("Article", "文章"),
  28. "yoshop_help": ("Help", "帮助"),
  29. "yoshop_coupon": ("Coupon", "优惠券"),
  30. "yoshop_delivery": ("Delivery", "配送模板"),
  31. "yoshop_express": ("Express", "物流公司"),
  32. "yoshop_printer": ("Printer", "打印机"),
  33. "yoshop_store_address": ("StoreAddress", "商城地址(发货/退货)"),
  34. "yoshop_store_menu": ("StoreMenu", "后台菜单"),
  35. "yoshop_store_role": ("StoreRole", "角色"),
  36. "yoshop_store_role_menu": ("StoreRoleMenu", "角色-菜单关联"),
  37. "yoshop_store_user_role": ("StoreUserRole", "管理员-角色关联"),
  38. "yoshop_store_setting": ("StoreSetting", "商城设置项"),
  39. "yoshop_comment": ("Comment", "商品评价"),
  40. "yoshop_order": ("Order", "订单"),
  41. "yoshop_order_goods": ("OrderGoods", "订单商品"),
  42. "yoshop_order_address": ("OrderAddress", "订单收货地址"),
  43. "yoshop_payment_trade": ("PaymentTrade", "第三方支付交易"),
  44. "yoshop_order_delivery": ("OrderDelivery", "订单发货单"),
  45. "yoshop_user_balance_log": ("UserBalanceLog", "余额变动记录"),
  46. "yoshop_recharge_plan": ("RechargePlan", "充值套餐"),
  47. "yoshop_order_refund": ("OrderRefund", "订单退款单"),
  48. "yoshop_delivery_rule": ("DeliveryRule", "配送模板规则"),
  49. }
  50. RESERVED = {"describe", "values", "key", "interval", "condition", "order", "group", "system"}
  51. INT_TYPES = ("int", "tinyint", "smallint", "mediumint")
  52. def camel(name, upper_first=False):
  53. parts = name.split("_")
  54. out = "".join(p[:1].upper() + p[1:] for p in parts)
  55. if not upper_first:
  56. out = out[:1].lower() + out[1:]
  57. return out
  58. def java_type(col_type):
  59. t = col_type.lower()
  60. # 形如 "int(10) unsigned" -> "int";"decimal(10,2) unsigned" -> "decimal"
  61. base = t.split("(")[0].split()[0]
  62. if base == "bigint":
  63. return "Long"
  64. if base in INT_TYPES:
  65. return "Integer"
  66. if base == "decimal":
  67. return "BigDecimal"
  68. if base in ("double", "float"):
  69. return "Double"
  70. return "String"
  71. def gen_entity(table, cls, comment, cols):
  72. pk = None
  73. body = []
  74. imports = {
  75. "import com.baomidou.mybatisplus.annotation.IdType;",
  76. "import com.baomidou.mybatisplus.annotation.TableId;",
  77. "import com.baomidou.mybatisplus.annotation.TableName;",
  78. "import lombok.Data;",
  79. }
  80. if any(c[1].lower().startswith("decimal") for c in cols):
  81. imports.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;")
  82. imports.add("import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;")
  83. imports.add("import java.math.BigDecimal;")
  84. if any(c[1].lower().startswith("double") for c in cols):
  85. imports.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;")
  86. imports.add("import cn.yoshop.common.util.PhpFloatSerializer;")
  87. has_delete = any(c[0] == "is_delete" for c in cols)
  88. has_time = any(c[0] in ("create_time", "update_time") for c in cols)
  89. if has_delete:
  90. imports.add("import com.baomidou.mybatisplus.annotation.TableLogic;")
  91. for name, ctype, extra, cmt in cols:
  92. jt = java_type(ctype)
  93. field = camel(name)
  94. base = ctype.lower().split("(")[0].split()[0]
  95. if name == "id" and table == "yoshop_region":
  96. pk = name
  97. body.append(" @TableId(value = \"id\", type = IdType.AUTO)")
  98. body.append(f" private {jt} {field};")
  99. body.append("")
  100. continue
  101. if name.endswith("_id") and pk is None and name == table.replace("yoshop_", "") + "_id":
  102. pk = name
  103. body.append(f" @TableId(value = \"{name}\", type = IdType.AUTO)")
  104. elif name.endswith("_id") and pk is None and name in (
  105. "category_id", "service_id", "group_id", "grade_id", "page_id",
  106. "article_id", "help_id", "coupon_id", "delivery_id", "express_id",
  107. "printer_id", "address_id", "menu_id", "role_id", "comment_id",
  108. "order_id", "order_goods_id", "trade_id", "log_id", "plan_id",
  109. "refund_id", "rule_id"):
  110. pk = name
  111. body.append(f" @TableId(value = \"{name}\", type = IdType.AUTO)")
  112. elif name in ("create_time", "update_time"):
  113. body.append(f" @JsonIgnore")
  114. body.append(f" private Integer {field};")
  115. body.append("")
  116. continue
  117. elif name == "is_delete":
  118. body.append(" @TableLogic(value = \"0\", delval = \"1\")")
  119. elif name in RESERVED:
  120. body.append(f" @TableField(\"`{name}`\")")
  121. imports.add("import com.baomidou.mybatisplus.annotation.TableField;")
  122. annotations = []
  123. if jt == "BigDecimal":
  124. annotations.append(" @JsonSerialize(using = ToStringSerializer.class)")
  125. elif jt == "Double":
  126. annotations.append(" @JsonSerialize(using = PhpFloatSerializer.class)")
  127. for a in annotations:
  128. body.append(a)
  129. body.append(f" private {jt} {field};" + (f" // {cmt}" if cmt else ""))
  130. body.append("")
  131. # 时间字段输出
  132. if has_time:
  133. imports.add("import com.fasterxml.jackson.annotation.JsonIgnore;")
  134. imports.add("import com.fasterxml.jackson.annotation.JsonProperty;")
  135. imports.add("import cn.yoshop.common.util.TimeUtil;")
  136. if any(c[0] == "create_time" for c in cols):
  137. body.append(" @JsonProperty(\"create_time\")")
  138. body.append(" public String getCreateTimeText() {")
  139. body.append(" return TimeUtil.format(createTime);")
  140. body.append(" }")
  141. body.append("")
  142. if any(c[0] == "update_time" for c in cols):
  143. body.append(" @JsonProperty(\"update_time\")")
  144. body.append(" public String getUpdateTimeText() {")
  145. body.append(" return TimeUtil.format(updateTime);")
  146. body.append(" }")
  147. body.append("")
  148. src = "package cn.yoshop.common.entity;\n\n"
  149. src += "\n".join(sorted(imports)) + "\n\n"
  150. src += f"/**\n * {comment}({table}),字段与库内结构一一对应。\n */\n"
  151. src += "@Data\n"
  152. src += f"@TableName(\"{table}\")\n"
  153. src += f"public class {cls} {{\n\n"
  154. src += "\n".join(body).rstrip() + "\n"
  155. src += "}\n"
  156. (ROOT / "common" / "entity" / f"{cls}.java").write_text(src, encoding="utf-8")
  157. mapper = (
  158. "package cn.yoshop.common.mapper;\n\n"
  159. "import cn.yoshop.common.entity." + cls + ";\n"
  160. "import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n"
  161. "import org.apache.ibatis.annotations.Mapper;\n\n"
  162. f"@Mapper\npublic interface {cls}Mapper extends BaseMapper<{cls}> {{\n}}\n"
  163. )
  164. (ROOT / "common" / "mapper" / f"{cls}Mapper.java").write_text(mapper, encoding="utf-8")
  165. def main():
  166. conn = pymysql.connect(host="192.168.1.177", port=3306, user="root", password=pwd, database="shop")
  167. cur = conn.cursor()
  168. for table, (cls, comment) in TABLES.items():
  169. cur.execute(
  170. "SELECT column_name,column_type,extra,column_comment FROM information_schema.columns "
  171. "WHERE table_schema='shop' AND table_name=%s ORDER BY ordinal_position", (table,))
  172. cols = cur.fetchall()
  173. if not cols:
  174. print("!! 表不存在:", table)
  175. continue
  176. gen_entity(table, cls, comment, cols)
  177. print("ok", cls, len(cols), "列")
  178. conn.close()
  179. if __name__ == "__main__":
  180. main()