| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- # -*- coding: utf-8 -*-
- """按数据库表结构生成 Java 实体 + Mapper 骨架。
- 生成规则与项目既有实体保持一致:
- - 表名为全名(MyBatis-Plus 的 table-prefix 对显式 @TableName 无效)
- - int/tinyint -> Integer,bigint -> Long,decimal -> BigDecimal(ToStringSerializer,PHP 输出字符串)
- - double -> Double(PhpFloatSerializer,对齐 PHP 1.0 -> 1)
- - create_time / update_time -> @JsonIgnore Integer + 输出格式化字符串的 getter
- - is_delete -> @TableLogic(value="0", delval="1")(本库统一 0/1)
- """
- import re
- import sys
- import pymysql
- from pathlib import Path
- from _root import ROOT
- YML = str(ROOT / "yoshop-java/src/main/resources/application.yml")
- ROOT = Path(str(ROOT / "yoshop-java/src/main/java/cn/yoshop"))
- pwd = re.findall(r"password:\s*(.+)", open(YML, encoding="utf-8").read())[0].strip().strip("\"'")
- # 表名 -> (类名, 说明, 需转义的列)
- TABLES = {
- "yoshop_category": ("Category", "商品分类"),
- "yoshop_goods_service": ("GoodsService", "商品服务(保障服务)"),
- "yoshop_upload_group": ("UploadGroup", "文件库分组"),
- "yoshop_region": ("Region", "地区(省/市/区)"),
- "yoshop_user": ("User", "商城会员"),
- "yoshop_user_grade": ("UserGrade", "会员等级"),
- "yoshop_page": ("PageEntity", "页面(首页/自定义页)"),
- "yoshop_article_category": ("ArticleCategory", "文章分类"),
- "yoshop_article": ("Article", "文章"),
- "yoshop_help": ("Help", "帮助"),
- "yoshop_coupon": ("Coupon", "优惠券"),
- "yoshop_delivery": ("Delivery", "配送模板"),
- "yoshop_express": ("Express", "物流公司"),
- "yoshop_printer": ("Printer", "打印机"),
- "yoshop_store_address": ("StoreAddress", "商城地址(发货/退货)"),
- "yoshop_store_menu": ("StoreMenu", "后台菜单"),
- "yoshop_store_role": ("StoreRole", "角色"),
- "yoshop_store_role_menu": ("StoreRoleMenu", "角色-菜单关联"),
- "yoshop_store_user_role": ("StoreUserRole", "管理员-角色关联"),
- "yoshop_store_setting": ("StoreSetting", "商城设置项"),
- "yoshop_comment": ("Comment", "商品评价"),
- "yoshop_order": ("Order", "订单"),
- "yoshop_order_goods": ("OrderGoods", "订单商品"),
- "yoshop_order_address": ("OrderAddress", "订单收货地址"),
- "yoshop_payment_trade": ("PaymentTrade", "第三方支付交易"),
- "yoshop_order_delivery": ("OrderDelivery", "订单发货单"),
- "yoshop_user_balance_log": ("UserBalanceLog", "余额变动记录"),
- "yoshop_recharge_plan": ("RechargePlan", "充值套餐"),
- "yoshop_order_refund": ("OrderRefund", "订单退款单"),
- "yoshop_delivery_rule": ("DeliveryRule", "配送模板规则"),
- }
- RESERVED = {"describe", "values", "key", "interval", "condition", "order", "group", "system"}
- INT_TYPES = ("int", "tinyint", "smallint", "mediumint")
- def camel(name, upper_first=False):
- parts = name.split("_")
- out = "".join(p[:1].upper() + p[1:] for p in parts)
- if not upper_first:
- out = out[:1].lower() + out[1:]
- return out
- def java_type(col_type):
- t = col_type.lower()
- # 形如 "int(10) unsigned" -> "int";"decimal(10,2) unsigned" -> "decimal"
- base = t.split("(")[0].split()[0]
- if base == "bigint":
- return "Long"
- if base in INT_TYPES:
- return "Integer"
- if base == "decimal":
- return "BigDecimal"
- if base in ("double", "float"):
- return "Double"
- return "String"
- def gen_entity(table, cls, comment, cols):
- pk = None
- body = []
- imports = {
- "import com.baomidou.mybatisplus.annotation.IdType;",
- "import com.baomidou.mybatisplus.annotation.TableId;",
- "import com.baomidou.mybatisplus.annotation.TableName;",
- "import lombok.Data;",
- }
- if any(c[1].lower().startswith("decimal") for c in cols):
- imports.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;")
- imports.add("import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;")
- imports.add("import java.math.BigDecimal;")
- if any(c[1].lower().startswith("double") for c in cols):
- imports.add("import com.fasterxml.jackson.databind.annotation.JsonSerialize;")
- imports.add("import cn.yoshop.common.util.PhpFloatSerializer;")
- has_delete = any(c[0] == "is_delete" for c in cols)
- has_time = any(c[0] in ("create_time", "update_time") for c in cols)
- if has_delete:
- imports.add("import com.baomidou.mybatisplus.annotation.TableLogic;")
- for name, ctype, extra, cmt in cols:
- jt = java_type(ctype)
- field = camel(name)
- base = ctype.lower().split("(")[0].split()[0]
- if name == "id" and table == "yoshop_region":
- pk = name
- body.append(" @TableId(value = \"id\", type = IdType.AUTO)")
- body.append(f" private {jt} {field};")
- body.append("")
- continue
- if name.endswith("_id") and pk is None and name == table.replace("yoshop_", "") + "_id":
- pk = name
- body.append(f" @TableId(value = \"{name}\", type = IdType.AUTO)")
- elif name.endswith("_id") and pk is None and name in (
- "category_id", "service_id", "group_id", "grade_id", "page_id",
- "article_id", "help_id", "coupon_id", "delivery_id", "express_id",
- "printer_id", "address_id", "menu_id", "role_id", "comment_id",
- "order_id", "order_goods_id", "trade_id", "log_id", "plan_id",
- "refund_id", "rule_id"):
- pk = name
- body.append(f" @TableId(value = \"{name}\", type = IdType.AUTO)")
- elif name in ("create_time", "update_time"):
- body.append(f" @JsonIgnore")
- body.append(f" private Integer {field};")
- body.append("")
- continue
- elif name == "is_delete":
- body.append(" @TableLogic(value = \"0\", delval = \"1\")")
- elif name in RESERVED:
- body.append(f" @TableField(\"`{name}`\")")
- imports.add("import com.baomidou.mybatisplus.annotation.TableField;")
- annotations = []
- if jt == "BigDecimal":
- annotations.append(" @JsonSerialize(using = ToStringSerializer.class)")
- elif jt == "Double":
- annotations.append(" @JsonSerialize(using = PhpFloatSerializer.class)")
- for a in annotations:
- body.append(a)
- body.append(f" private {jt} {field};" + (f" // {cmt}" if cmt else ""))
- body.append("")
- # 时间字段输出
- if has_time:
- imports.add("import com.fasterxml.jackson.annotation.JsonIgnore;")
- imports.add("import com.fasterxml.jackson.annotation.JsonProperty;")
- imports.add("import cn.yoshop.common.util.TimeUtil;")
- if any(c[0] == "create_time" for c in cols):
- body.append(" @JsonProperty(\"create_time\")")
- body.append(" public String getCreateTimeText() {")
- body.append(" return TimeUtil.format(createTime);")
- body.append(" }")
- body.append("")
- if any(c[0] == "update_time" for c in cols):
- body.append(" @JsonProperty(\"update_time\")")
- body.append(" public String getUpdateTimeText() {")
- body.append(" return TimeUtil.format(updateTime);")
- body.append(" }")
- body.append("")
- src = "package cn.yoshop.common.entity;\n\n"
- src += "\n".join(sorted(imports)) + "\n\n"
- src += f"/**\n * {comment}({table}),字段与库内结构一一对应。\n */\n"
- src += "@Data\n"
- src += f"@TableName(\"{table}\")\n"
- src += f"public class {cls} {{\n\n"
- src += "\n".join(body).rstrip() + "\n"
- src += "}\n"
- (ROOT / "common" / "entity" / f"{cls}.java").write_text(src, encoding="utf-8")
- mapper = (
- "package cn.yoshop.common.mapper;\n\n"
- "import cn.yoshop.common.entity." + cls + ";\n"
- "import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n"
- "import org.apache.ibatis.annotations.Mapper;\n\n"
- f"@Mapper\npublic interface {cls}Mapper extends BaseMapper<{cls}> {{\n}}\n"
- )
- (ROOT / "common" / "mapper" / f"{cls}Mapper.java").write_text(mapper, encoding="utf-8")
- def main():
- conn = pymysql.connect(host="192.168.1.177", port=3306, user="root", password=pwd, database="shop")
- cur = conn.cursor()
- for table, (cls, comment) in TABLES.items():
- cur.execute(
- "SELECT column_name,column_type,extra,column_comment FROM information_schema.columns "
- "WHERE table_schema='shop' AND table_name=%s ORDER BY ordinal_position", (table,))
- cols = cur.fetchall()
- if not cols:
- print("!! 表不存在:", table)
- continue
- gen_entity(table, cls, comment, cols)
- print("ok", cls, len(cols), "列")
- conn.close()
- if __name__ == "__main__":
- main()
|