# -*- 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()