diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..32c5033 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,78 @@ +# service.xpcool.com 自动部署:Gitea act_runner(push main/dev 触发) +# 注意点(与前端差异): +# 1. 后端不是拷贝静态文件,而是 Go 交叉编译 + Docker 镜像重建容器(runner 已挂载 docker.sock)。 +# 2. job 基础镜像 node:20-bullseye 无 Go,需下载 Go 1.23 工具链(npmmirror 源,GOPROXY 走 goproxy.cn)。 +# 3. 容器 env 通过 Gitea Actions secrets 注入(DB_DSN 必须带 mysql: 类型前缀,勿写死在仓库)。 +# 4. 数据库结构变更请手动执行 manifest/sql/ 下脚本(003→004→004b→007),workflow 不做自动 DDL。 +name: Build and Deploy (service.xpcool.com) + +on: + push: + branches: [main, dev] + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Install git & download Go toolchain + run: | + set -e + apt-get update -qq + apt-get install -y -qq git >/dev/null + curl -fsSL https://npmmirror.com/mirrors/golang/go1.23.12.linux-amd64.tar.gz -o /tmp/go.tar.gz + mkdir -p /usr/local && tar -C /usr/local -xzf /tmp/go.tar.gz + export PATH="/usr/local/go/bin:$PATH" + echo "/usr/local/go/bin" >> "$GITHUB_ENV" + go version + + - name: Checkout (local Gitea) + run: | + git clone --depth 1 --branch "${{ github.ref_name }}" https://oauth2:${{ github.token }}@git.xpcool.com/${{ github.repository }}.git . + git checkout ${{ github.sha }} + + - name: Cross compile (linux/amd64, CGO disabled) + env: + GOPROXY: https://goproxy.cn,direct + GOFLAGS: -mod=mod + run: | + export PATH="/usr/local/go/bin:$PATH" + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /tmp/deploy/main main.go + ls -lh /tmp/deploy/main + + - name: Assemble deploy dir (binary + config + resource + Dockerfile) + run: | + set -e + mkdir -p /tmp/deploy/manifest/config /tmp/deploy/resource + # 配置:基础 config.yaml + 环境化 config.prod.yaml(GF_GCFG_ENV=prod 时生效) + cp manifest/config/config.prod.yaml /tmp/deploy/manifest/config/config.prod.yaml + cp manifest/config/config.prod.yaml /tmp/deploy/manifest/config/config.yaml + # 静态资源(GoFrame public/template) + cp -r resource/public /tmp/deploy/resource/public + mkdir -p /tmp/deploy/resource/template + # 容器 Dockerfile(仓库内维护 deploy/Dockerfile,避免 workflow 内 heredoc 缩进问题) + cp deploy/Dockerfile /tmp/deploy/Dockerfile + echo "assemble done" + + - name: Build image & restart container + env: + DB_DSN: ${{ secrets.DB_DSN }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + run: | + set -e + cd /tmp/deploy + docker build -t service.xpcool.com:latest . + docker rm -f service.xpcool.com >/dev/null 2>&1 || true + docker run -d --name service.xpcool.com \ + --network xpcool-net \ + -p 127.0.0.1:10100:10100 \ + -e GF_GCFG_ENV=prod \ + -e "DB_DSN=$DB_DSN" \ + -e "JWT_SECRET=$JWT_SECRET" \ + --restart unless-stopped \ + service.xpcool.com:latest + # 冒烟:等待启动并验证 OpenAPI + sleep 5 + curl -fsS -m 10 http://127.0.0.1:10100/api.json | head -c 120 || { echo "SMOKE TEST FAILED"; exit 1; } + echo + echo "Deployed service.xpcool.com (container restarted, 127.0.0.1:10100)" diff --git a/.gitignore b/.gitignore index f0fab95..ee928aa 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ log/ # 本地环境变量(含数据库口令),禁止提交 .env* + +# GoLand 项目级运行配置(含数据库口令),禁止提交 +.run/ diff --git a/.workbuddy/memory/CHANGELOG.md b/.workbuddy/memory/CHANGELOG.md index a22b3cd..ce6eb18 100644 --- a/.workbuddy/memory/CHANGELOG.md +++ b/.workbuddy/memory/CHANGELOG.md @@ -1,6 +1,10 @@ # service.xpcool.com 变更记录 > 倒序:最新在上。格式:YYYY-MM-DD | 类型 | 摘要 +2026-08-26 | CFG | 新增 Gitea act_runner 自动部署:.gitea/workflows/deploy.yml(push main/dev → 下载 Go1.23 工具链 npmmirror + GOPROXY goproxy.cn → CGO=0 linux/amd64 交叉编译 → 组装 main+manifest/config(config.yaml+config.prod.yaml)+resource+deploy/Dockerfile → docker build 重建容器 127.0.0.1:10100(xpcool-net,GF_GCFG_ENV=prod,DB_DSN/JWT_SECRET 走 Gitea Actions secrets)→ curl /api.json 冒烟);deploy/Dockerfile 仓库内维护;DB 结构变更不自动 DDL,需手动导 manifest/sql +2026-08-26 | FIX | 本地登录 no rows 修复:根因=本地库 service_xpcool_com 的 admin_user 空表(本地/服务器库两套,此前仅改服务器);补 super_admin 角色 + 导入 009_admin_account_v2.sql(须 --default-character-set=utf8mb4 否则中文 Data too long)→ 本地 xxcool/xxCool@2026 登录成功(subject=3);同时服务器废弃 webhook 方案已删除(deploy-webhook.service/webhook.py/secrets.env/deploy.sh),自动部署改走 Gitea act_runner + .gitea/workflows(仓库内尚未配置,待补) +2026-08-26 | CHG | 看房 house 模块后端落地:010_house_tables.sql 建 9 表(community/building/listing/price_snapshot/transaction/facility/community_facility/school_district/preference)+ 011_house_menu.sql 菜单/权限种子(超管 role_id=1 绑定);gf gen dao 生成 dao/entity/do;实现 api/house/{community,listing,dashboard} + controller/house + service/house(管理 CRUD + 看板聚合,全 POST 动作式,前缀 /api/service/admin/house,含多平台软关联 match_group_id、笋盘/低可信标记);cmd.go 注册 housectl + 3 个 RegisterService;go build 通过。坑①gf CLI 为 com.lib.gf.v2 分支,gen dao 产物 import 需 sed 回 github.com/gogf/gf/v2(18 文件);②mysql 客户端须 --default-character-set=utf8mb4 否则中文 COMMENT/INSERT 乱码;③TINYINT 字段 comment 含 0/1 被 gf 映射 bool(status 三值改 INT);hack/config.yaml 的 link+tables 已指向本地库 +2026-08-26 | CHG | 生产超级管理员账号替换:删除 admin/admin123(用户/绑定/refresh 会话全清),新增 xxcool/xxCool@2026(bcrypt $2b$10$,绑定 super_admin);脚本 manifest/sql/009_admin_account_v2.sql(008 编号已被 008_menu_rbac_v4.sql 占用);004_seed.sql 种子账号同步改 xxcool;登录验证:xxcool 成功、admin 报 no rows(预期);⚠️ 线上前端仍是旧版(默认 admin/admin123),需部署新版 dist 后 xxcool 才可正常登录 2026-08-26 | CHG | RBAC 菜单调整:移除管理员管理(id=2 及按钮 21-25 软删+解绑),新增日志管理菜单(id=7, system:log, component=system/log/index),原按钮 63(system:log:list)挂到 id=7 下;超管绑定新菜单;幂等脚本 manifest/sql/008_menu_rbac_v4.sql 2026-08-26 | FIX | RBAC 安全漏洞修复:禁用角色(status=0)绑定的权限仍生效。根因为 Codes/HasPermission/Routes 三处联表查询未过滤 admin_role 启用状态,仅 roleCodes 过滤。修复:三处统一 LeftJoin admin_role 并加 r.status=1,opuser 联调验证 codes 空/routes 空/接口 403,admin 全链路回归通过 diff --git a/README.MD b/README.MD index 17bf26f..aa89557 100644 --- a/README.MD +++ b/README.MD @@ -20,3 +20,9 @@ The command is intentionally the only source of `internal/dao`, `internal/model/ - `/admin/v1/*`: admin API. Protected endpoints require both an admin access token and an `X-Permission` identifier. Use `GF_GCFG_FILE=config.dev.yaml` (or `config.test.yaml` / `config.prod.yaml`) and set `DB_DSN` plus a strong `JWT_SECRET` before startup. + +## Local startup + +- **Terminal**: `source .env.dev && go run main.go` (`.env.dev` is gitignored, holds local DSN + secret). +- **GoLand**: run configuration `.run/service-dev.run.xml` (project-level, auto-detected after reloading the project) already injects `GF_GCFG_FILE` / `DB_DSN` / `JWT_SECRET`; pick it in the run dropdown. The file is gitignored because it contains the DB password. + diff --git a/api/admin/admin/login/login.go b/api/admin/admin/login/login.go index 43003ee..5472fa9 100644 --- a/api/admin/admin/login/login.go +++ b/api/admin/admin/login/login.go @@ -1 +1,59 @@ -package login +// Package admin_admin_login 定义管理端认证接口(登录/资料/权限码/刷新/登出),路由前缀 /admin。 +package admin_admin_login + +import "github.com/gogf/gf/v2/frame/g" + +// LoginReq 管理员登录请求。 +type LoginReq struct { + g.Meta `path:"/system/auth/login" method:"post" tags:"Admin/System/Auth" summary:"管理员登录"` + Username string `json:"username" v:"required"` + Password string `json:"password" v:"required"` +} + +// LoginRes 管理员登录响应(令牌对 + 管理员 ID)。 +type LoginRes struct { + AccessToken string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int64 `json:"expiresIn"` + AdminID uint64 `json:"adminId"` +} + +// InfoReq 获取当前管理员资料(供 vben getUserInfo 使用)。 +type InfoReq struct { + g.Meta `path:"/system/auth/info" method:"post" tags:"Admin/System/Auth" summary:"当前管理员资料"` +} + +// InfoRes 是 InfoReq 的响应,Roles 携带角色码供 vben 权限使用。 +type InfoRes struct { + AdminID uint64 `json:"adminId"` + Username string `json:"username"` + Nickname string `json:"nickname"` + Roles []string `json:"roles"` +} + +// CodesReq 获取当前管理员的按钮级权限码(vben getAccessCodes,后端鉴权模式)。 +type CodesReq struct { + g.Meta `path:"/system/auth/codes" method:"post" tags:"Admin/System/Auth" summary:"当前管理员权限码"` +} + +// CodesRes 是 CodesReq 的响应。 +type CodesRes struct { + Codes []string `json:"codes"` +} + +// RefreshReq 使用刷新令牌轮换管理员令牌对。 +type RefreshReq struct { + g.Meta `path:"/system/auth/refresh" method:"post" tags:"Admin/System/Auth" summary:"轮换管理员令牌"` + RefreshToken string `json:"refreshToken" v:"required"` +} + +// RefreshRes 是 RefreshReq 的响应。 +type RefreshRes LoginRes + +// LogoutReq 结束管理员会话。 +type LogoutReq struct { + g.Meta `path:"/system/auth/logout" method:"post" tags:"Admin/System/Auth" summary:"管理员登出"` +} + +// LogoutRes 是 LogoutReq 的响应。 +type LogoutRes struct{} diff --git a/api/admin/system/login_log/login_log.go b/api/admin/system/login_log/login_log.go new file mode 100644 index 0000000..b0521b3 --- /dev/null +++ b/api/admin/system/login_log/login_log.go @@ -0,0 +1,30 @@ +// Package admin_system_login_log 定义管理员登录日志查询接口,路由前缀 /admin。 +package admin_system_login_log + +import "github.com/gogf/gf/v2/frame/g" + +// LoginLogItem 一条管理员登录日志记录。 +type LoginLogItem struct { + Id uint64 `json:"id"` + Username string `json:"username"` + IP string `json:"ip"` + UserAgent string `json:"userAgent"` + Status int `json:"status"` // 1 成功, 0 失败 + FailReason string `json:"failReason"` + CreatedAt string `json:"createdAt"` +} + +// LoginLogListReq 分页查询管理员登录日志。 +type LoginLogListReq struct { + g.Meta `path:"/system/login-log" method:"post" tags:"Admin/System/LoginLog" summary:"登录日志列表"` + Page int `json:"page" d:"1" v:"min:1"` + Size int `json:"size" d:"10" v:"min:1|max:100"` + Username string `json:"username"` // 按账号过滤 + Status *int `json:"status"` // 按结果过滤:1 成功 0 失败,不传为全部 +} + +// LoginLogListRes 是 LoginLogListReq 的响应。 +type LoginLogListRes struct { + List []*LoginLogItem `json:"list"` + Total int `json:"total"` +} diff --git a/api/admin/v1/admin/admin/admin.go b/api/admin/v1/admin/admin/admin.go deleted file mode 100644 index 3de38ea..0000000 --- a/api/admin/v1/admin/admin/admin.go +++ /dev/null @@ -1,74 +0,0 @@ -// Package admin 定义后台管理接口(管理员账号管理),路由前缀 /admin/v1/admin/admin。 -package admin - -import "github.com/gogf/gf/v2/frame/g" - -// AdminItem is one row of the admin list. -type AdminItem struct { - Id uint64 `json:"id"` - Username string `json:"username"` - Nickname string `json:"nickname"` - Status int `json:"status"` - RoleIds []uint64 `json:"roleIds"` - RoleNames []string `json:"roleNames"` - CreatedAt string `json:"createdAt"` -} - -// AdminListReq pages the administrators. -type AdminListReq struct { - g.Meta `path:"/admin" method:"get" tags:"Admin/Admin" summary:"Admin list"` - Page int `json:"page" d:"1" v:"min:1"` - Size int `json:"size" d:"10" v:"min:1|max:100"` - Keyword string `json:"keyword"` // matches username or nickname -} - -// AdminListRes is the response of AdminListReq. -type AdminListRes struct { - List []*AdminItem `json:"list"` - Total int `json:"total"` -} - -// AdminCreateReq creates an administrator. -type AdminCreateReq struct { - g.Meta `path:"/admin" method:"post" tags:"Admin/Admin" summary:"Create admin"` - Username string `json:"username" v:"required"` - Password string `json:"password" v:"required|min-length:6#password required|password too short"` - Nickname string `json:"nickname"` - RoleIds []uint64 `json:"roleIds"` -} - -// AdminCreateRes is the response of AdminCreateReq. -type AdminCreateRes struct { - Id uint64 `json:"id"` -} - -// AdminUpdateReq updates profile/status/roles of an administrator. -type AdminUpdateReq struct { - g.Meta `path:"/admin/{id}" method:"put" tags:"Admin/Admin" summary:"Update admin"` - Id uint64 `json:"id" in:"path" v:"required"` - Nickname string `json:"nickname"` - Status int `json:"status"` - RoleIds []uint64 `json:"roleIds"` -} - -// AdminUpdateRes is the response of AdminUpdateReq. -type AdminUpdateRes struct{} - -// AdminResetPwdReq resets an administrator's password. -type AdminResetPwdReq struct { - g.Meta `path:"/admin/{id}/password" method:"put" tags:"Admin/Admin" summary:"Reset admin password"` - Id uint64 `json:"id" in:"path" v:"required"` - Password string `json:"password" v:"required|min-length:6#password required|password too short"` -} - -// AdminResetPwdRes is the response of AdminResetPwdReq. -type AdminResetPwdRes struct{} - -// AdminDeleteReq deletes an administrator (soft delete). -type AdminDeleteReq struct { - g.Meta `path:"/admin/{id}" method:"delete" tags:"Admin/Admin" summary:"Delete admin"` - Id uint64 `json:"id" in:"path" v:"required"` -} - -// AdminDeleteRes is the response of AdminDeleteReq. -type AdminDeleteRes struct{} diff --git a/api/admin/v1/system/auth/auth.go b/api/admin/v1/system/auth/auth.go deleted file mode 100644 index 8607372..0000000 --- a/api/admin/v1/system/auth/auth.go +++ /dev/null @@ -1,53 +0,0 @@ -// Package auth 定义管理端认证接口(登录/资料/权限码/刷新/登出),路由前缀 /admin/v1/system/auth。 -package auth - -import "github.com/gogf/gf/v2/frame/g" - -type LoginReq struct { - g.Meta `path:"/system/auth/login" method:"post" tags:"Admin/System/Auth" summary:"Administrator login"` - Username string `json:"username" v:"required"` - Password string `json:"password" v:"required"` -} -type LoginRes struct { - AccessToken string `json:"accessToken"` - RefreshToken string `json:"refreshToken"` - ExpiresIn int64 `json:"expiresIn"` - AdminID uint64 `json:"adminId"` -} - -// InfoReq returns the current administrator profile (vben getUserInfo). -type InfoReq struct { - g.Meta `path:"/system/auth/info" method:"get" tags:"Admin/System/Auth" summary:"Current admin info"` -} - -// InfoRes is the response of InfoReq. Roles carries role codes for vben authority. -type InfoRes struct { - AdminID uint64 `json:"adminId"` - Username string `json:"username"` - Nickname string `json:"nickname"` - Roles []string `json:"roles"` -} - -// CodesReq returns the button-level permission codes of the current admin -// (vben getAccessCodes, backend access mode). -type CodesReq struct { - g.Meta `path:"/system/auth/codes" method:"get" tags:"Admin/System/Auth" summary:"Current admin access codes"` -} - -// CodesRes is the response of CodesReq. -type CodesRes struct { - Codes []string `json:"codes"` -} - -// RefreshReq rotates the administrator token pair using a refresh token. -type RefreshReq struct { - g.Meta `path:"/system/auth/refresh" method:"post" tags:"Admin/System/Auth" summary:"Rotate admin token"` - RefreshToken string `json:"refreshToken" v:"required"` -} -type RefreshRes LoginRes - -// LogoutReq ends the administrator session. -type LogoutReq struct { - g.Meta `path:"/system/auth/logout" method:"post" tags:"Admin/System/Auth" summary:"Admin logout"` -} -type LogoutRes struct{} diff --git a/api/admin/v1/system/menu/menu.go b/api/admin/v1/system/menu/menu.go deleted file mode 100644 index 1c1313e..0000000 --- a/api/admin/v1/system/menu/menu.go +++ /dev/null @@ -1,33 +0,0 @@ -package menu - -import "github.com/gogf/gf/v2/frame/g" - -// RouteMeta mirrors vben admin dynamic route metadata. -type RouteMeta struct { - Title string `json:"title"` - Icon string `json:"icon"` - Order int `json:"order"` - Authority []string `json:"authority,omitempty"` // role codes - HideInMenu bool `json:"hideInMenu,omitempty"` - KeepAlive bool `json:"keepAlive,omitempty"` -} - -// RouteItem is one node of the vben route tree. -type RouteItem struct { - Name string `json:"name"` - Path string `json:"path"` - Component string `json:"component,omitempty"` - Meta RouteMeta `json:"meta"` - Children []*RouteItem `json:"children,omitempty"` -} - -// MenuRoutesReq returns the current admin's menu tree as vben routes -// (backend access mode, login-only endpoint). -type MenuRoutesReq struct { - g.Meta `path:"/system/menu/routes" method:"get" tags:"Admin/System/Menu" summary:"Current admin menu routes"` -} - -// MenuRoutesRes is the response of MenuRoutesReq. -type MenuRoutesRes struct { - Routes []*RouteItem `json:"routes"` -} diff --git a/api/admin/v1/system/menu_manage/menu_manage.go b/api/admin/v1/system/menu_manage/menu_manage.go deleted file mode 100644 index 68721d1..0000000 --- a/api/admin/v1/system/menu_manage/menu_manage.go +++ /dev/null @@ -1,77 +0,0 @@ -package menu_manage - -import "github.com/gogf/gf/v2/frame/g" - -// MenuItem is one node of the full menu tree (type 1 menu / 2 button). -type MenuItem struct { - Id uint64 `json:"id"` - ParentId uint64 `json:"parentId"` - Name string `json:"name"` - Icon string `json:"icon"` - Type int `json:"type"` // 1 menu, 2 button/api - Path string `json:"path"` - Component string `json:"component"` - Permission string `json:"permission"` - Sort int `json:"sort"` - Status int `json:"status"` - Hidden bool `json:"hidden"` - Children []*MenuItem `json:"children,omitempty"` -} - -// MenuTreeReq returns the full menu tree for management. -type MenuTreeReq struct { - g.Meta `path:"/system/menu/tree" method:"get" tags:"Admin/System/Menu" summary:"Full menu tree"` -} - -// MenuTreeRes is the response of MenuTreeReq. -type MenuTreeRes struct { - Tree []*MenuItem `json:"tree"` -} - -// MenuCreateReq creates a menu or button node. -type MenuCreateReq struct { - g.Meta `path:"/system/menu" method:"post" tags:"Admin/System/Menu" summary:"Create menu"` - ParentId uint64 `json:"parentId"` - Name string `json:"name" v:"required"` - Icon string `json:"icon"` - Type int `json:"type" v:"in:1,2#type must be 1 or 2"` - Path string `json:"path"` - Component string `json:"component"` - Permission string `json:"permission" v:"required"` - Sort int `json:"sort"` - Status int `json:"status"` - Hidden bool `json:"hidden"` -} - -// MenuCreateRes is the response of MenuCreateReq. -type MenuCreateRes struct { - Id uint64 `json:"id"` -} - -// MenuUpdateReq updates a menu or button node. -type MenuUpdateReq struct { - g.Meta `path:"/system/menu/{id}" method:"put" tags:"Admin/System/Menu" summary:"Update menu"` - Id uint64 `json:"id" in:"path" v:"required"` - ParentId uint64 `json:"parentId"` - Name string `json:"name" v:"required"` - Icon string `json:"icon"` - Type int `json:"type" v:"in:1,2#type must be 1 or 2"` - Path string `json:"path"` - Component string `json:"component"` - Permission string `json:"permission" v:"required"` - Sort int `json:"sort"` - Status int `json:"status"` - Hidden bool `json:"hidden"` -} - -// MenuUpdateRes is the response of MenuUpdateReq. -type MenuUpdateRes struct{} - -// MenuDeleteReq deletes a menu node (soft delete). -type MenuDeleteReq struct { - g.Meta `path:"/system/menu/{id}" method:"delete" tags:"Admin/System/Menu" summary:"Delete menu"` - Id uint64 `json:"id" in:"path" v:"required"` -} - -// MenuDeleteRes is the response of MenuDeleteReq. -type MenuDeleteRes struct{} diff --git a/api/admin/v1/system/role/role.go b/api/admin/v1/system/role/role.go deleted file mode 100644 index 8dad972..0000000 --- a/api/admin/v1/system/role/role.go +++ /dev/null @@ -1,62 +0,0 @@ -package role - -import "github.com/gogf/gf/v2/frame/g" - -// RoleItem is one row of the role list. -type RoleItem struct { - Id uint64 `json:"id"` - Code string `json:"code"` - Name string `json:"name"` - Status int `json:"status"` - MenuIds []uint64 `json:"menuIds"` // bound menu ids (for editing) - CreatedAt string `json:"createdAt"` -} - -// RoleListReq pages the roles. -type RoleListReq struct { - g.Meta `path:"/system/role" method:"get" tags:"Admin/System/Role" summary:"Role list"` - Page int `json:"page" d:"1" v:"min:1"` - Size int `json:"size" d:"10" v:"min:1|max:100"` - Keyword string `json:"keyword"` // matches code or name -} - -// RoleListRes is the response of RoleListReq. -type RoleListRes struct { - List []*RoleItem `json:"list"` - Total int `json:"total"` -} - -// RoleCreateReq creates a role and binds menu ids. -type RoleCreateReq struct { - g.Meta `path:"/system/role" method:"post" tags:"Admin/System/Role" summary:"Create role"` - Code string `json:"code" v:"required"` - Name string `json:"name" v:"required"` - Status int `json:"status"` - MenuIds []uint64 `json:"menuIds"` -} - -// RoleCreateRes is the response of RoleCreateReq. -type RoleCreateRes struct { - Id uint64 `json:"id"` -} - -// RoleUpdateReq updates a role and rebinds menu ids. -type RoleUpdateReq struct { - g.Meta `path:"/system/role/{id}" method:"put" tags:"Admin/System/Role" summary:"Update role"` - Id uint64 `json:"id" in:"path" v:"required"` - Name string `json:"name" v:"required"` - Status int `json:"status"` - MenuIds []uint64 `json:"menuIds"` -} - -// RoleUpdateRes is the response of RoleUpdateReq. -type RoleUpdateRes struct{} - -// RoleDeleteReq deletes a role (soft delete). -type RoleDeleteReq struct { - g.Meta `path:"/system/role/{id}" method:"delete" tags:"Admin/System/Role" summary:"Delete role"` - Id uint64 `json:"id" in:"path" v:"required"` -} - -// RoleDeleteRes is the response of RoleDeleteReq. -type RoleDeleteRes struct{} diff --git a/api/hello/hello.go b/api/hello/hello.go deleted file mode 100644 index a9b48fa..0000000 --- a/api/hello/hello.go +++ /dev/null @@ -1,15 +0,0 @@ -// ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package hello - -import ( - "context" - - "service.xpcool.com/api/hello/v1" -) - -type IHelloV1 interface { - Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) -} diff --git a/api/hello/v1/hello.go b/api/hello/v1/hello.go deleted file mode 100644 index b4dd233..0000000 --- a/api/hello/v1/hello.go +++ /dev/null @@ -1,12 +0,0 @@ -package v1 - -import ( - "github.com/gogf/gf/v2/frame/g" -) - -type HelloReq struct { - g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"` -} -type HelloRes struct { - g.Meta `mime:"text/html" example:"string"` -} diff --git a/api/house/community/community.go b/api/house/community/community.go new file mode 100644 index 0000000..bd22c50 --- /dev/null +++ b/api/house/community/community.go @@ -0,0 +1,93 @@ +// Package house_community 定义楼盘/小区管理接口,路由前缀 /house。 +package house_community + +import "github.com/gogf/gf/v2/frame/g" + +// CommunityItem 小区列表中的一行。 +type CommunityItem struct { + Id uint64 `json:"id"` + Name string `json:"name"` + Region string `json:"region"` + BusinessDistrict string `json:"businessDistrict"` + Address string `json:"address"` + Lng float64 `json:"lng"` + Lat float64 `json:"lat"` + BuildYear int `json:"buildYear"` + Households int `json:"households"` + PlotRatio float64 `json:"plotRatio"` + GreenRate float64 `json:"greenRate"` + PropertyCompany string `json:"propertyCompany"` + PropertyFee float64 `json:"propertyFee"` + Developer string `json:"developer"` + Source string `json:"source"` +} + +// CommunityListReq 分页查询小区。 +type CommunityListReq struct { + g.Meta `path:"/house/community/list" method:"post" tags:"Admin/House/Community" summary:"小区列表"` + Page int `json:"page" d:"1" v:"min:1"` + Size int `json:"size" d:"10" v:"min:1|max:100"` + Keyword string `json:"keyword"` // 匹配名称/地址 + Region string `json:"region"` +} + +// CommunityListRes 是 CommunityListReq 的响应。 +type CommunityListRes struct { + List []*CommunityItem `json:"list"` + Total int `json:"total"` +} + +// CommunityCreateReq 新增小区。 +type CommunityCreateReq struct { + g.Meta `path:"/house/community/create" method:"post" tags:"Admin/House/Community" summary:"新增小区"` + Name string `json:"name" v:"required"` + Region string `json:"region"` + BusinessDistrict string `json:"businessDistrict"` + Address string `json:"address"` + Lng float64 `json:"lng"` + Lat float64 `json:"lat"` + BuildYear int `json:"buildYear"` + Households int `json:"households"` + PlotRatio float64 `json:"plotRatio"` + GreenRate float64 `json:"greenRate"` + PropertyCompany string `json:"propertyCompany"` + PropertyFee float64 `json:"propertyFee"` + Developer string `json:"developer"` + Source string `json:"source"` +} + +// CommunityCreateRes 是 CommunityCreateReq 的响应。 +type CommunityCreateRes struct { + Id uint64 `json:"id"` +} + +// CommunityUpdateReq 更新小区。 +type CommunityUpdateReq struct { + g.Meta `path:"/house/community/update/{id}" method:"post" tags:"Admin/House/Community" summary:"更新小区"` + Id uint64 `json:"id" in:"path" v:"required"` + Name string `json:"name" v:"required"` + Region string `json:"region"` + BusinessDistrict string `json:"businessDistrict"` + Address string `json:"address"` + Lng float64 `json:"lng"` + Lat float64 `json:"lat"` + BuildYear int `json:"buildYear"` + Households int `json:"households"` + PlotRatio float64 `json:"plotRatio"` + GreenRate float64 `json:"greenRate"` + PropertyCompany string `json:"propertyCompany"` + PropertyFee float64 `json:"propertyFee"` + Developer string `json:"developer"` +} + +// CommunityUpdateRes 是 CommunityUpdateReq 的响应。 +type CommunityUpdateRes struct{} + +// CommunityDeleteReq 删除小区(软删除)。 +type CommunityDeleteReq struct { + g.Meta `path:"/house/community/delete/{id}" method:"post" tags:"Admin/House/Community" summary:"删除小区"` + Id uint64 `json:"id" in:"path" v:"required"` +} + +// CommunityDeleteRes 是 CommunityDeleteReq 的响应。 +type CommunityDeleteRes struct{} diff --git a/api/house/dashboard/dashboard.go b/api/house/dashboard/dashboard.go new file mode 100644 index 0000000..ebf0ae3 --- /dev/null +++ b/api/house/dashboard/dashboard.go @@ -0,0 +1,85 @@ +// Package house_dashboard 定义看房数据看板聚合接口,路由前缀 /house。 +package house_dashboard + +import "github.com/gogf/gf/v2/frame/g" + +// OverviewRes 看板统计概览。 +type OverviewRes struct { + CommunityCount int `json:"communityCount"` // 小区数 + ListingCount int `json:"listingCount"` // 在售房源数 + BargainCount int `json:"bargainCount"` // 笋盘数 + LowConfidence int `json:"lowConfidence"` // 低可信房源数 + AvgUnitPrice float64 `json:"avgUnitPrice"` // 在售均价(元/平米) + AvgTotalPrice float64 `json:"avgTotalPrice"` // 在售平均总价(万元) + AvgListDays float64 `json:"avgListDays"` // 平均挂牌天数 +} + +// OverviewReq 看板统计概览请求。 +type OverviewReq struct { + g.Meta `path:"/house/dashboard/overview" method:"post" tags:"Admin/House/Dashboard" summary:"看板概览"` + Region string `json:"region"` +} + +// MapPoint 地图上的一个小区点。 +type MapPoint struct { + CommunityId uint64 `json:"communityId"` + Name string `json:"name"` + Region string `json:"region"` + Lng float64 `json:"lng"` + Lat float64 `json:"lat"` + AvgUnitPrice float64 `json:"avgUnitPrice"` + ListingCount int `json:"listingCount"` + BargainCount int `json:"bargainCount"` +} + +// MapPointsReq 地图点位查询(带筛选)。 +type MapPointsReq struct { + g.Meta `path:"/house/dashboard/map-points" method:"post" tags:"Admin/House/Dashboard" summary:"地图点位"` + Region string `json:"region"` + PriceMin float64 `json:"priceMin"` + PriceMax float64 `json:"priceMax"` + Status int `json:"status"` +} + +// MapPointsRes 是 MapPointsReq 的响应。 +type MapPointsRes struct { + Points []*MapPoint `json:"points"` +} + +// TrendPoint 一个日期的均价点。 +type TrendPoint struct { + Date string `json:"date"` + AvgListPrice float64 `json:"avgListPrice"` // 挂牌均价(万元) + AvgDealPrice float64 `json:"avgDealPrice"` // 成交均价(万元) +} + +// PriceTrendReq 价格趋势查询。 +type PriceTrendReq struct { + g.Meta `path:"/house/dashboard/price-trend" method:"post" tags:"Admin/House/Dashboard" summary:"价格趋势"` + CommunityId uint64 `json:"communityId"` + Region string `json:"region"` + Limit int `json:"limit" d:"30"` // 最近 N 天 +} + +// PriceTrendRes 是 PriceTrendReq 的响应。 +type PriceTrendRes struct { + Trend []*TrendPoint `json:"trend"` +} + +// RegionAgg 一个区域的聚合指标。 +type RegionAgg struct { + Region string `json:"region"` + AvgUnitPrice float64 `json:"avgUnitPrice"` + ListingCount int `json:"listingCount"` + BargainCount int `json:"bargainCount"` +} + +// AggregateRegionReq 区域聚合查询。 +type AggregateRegionReq struct { + g.Meta `path:"/house/dashboard/aggregate-region" method:"post" tags:"Admin/House/Dashboard" summary:"区域聚合"` +} + +// AggregateRegionRes 是 AggregateRegionReq 的响应。 +type AggregateRegionRes struct { + List []*RegionAgg `json:"list"` +} diff --git a/api/house/listing/listing.go b/api/house/listing/listing.go new file mode 100644 index 0000000..4950d4e --- /dev/null +++ b/api/house/listing/listing.go @@ -0,0 +1,139 @@ +// Package house_listing 定义房源/挂牌管理接口,路由前缀 /house。 +package house_listing + +import "github.com/gogf/gf/v2/frame/g" + +// ListingItem 房源列表中的一行。 +type ListingItem struct { + Id uint64 `json:"id"` + CommunityId uint64 `json:"communityId"` + CommunityName string `json:"communityName"` + BuildingId uint64 `json:"buildingId"` + HouseNo string `json:"houseNo"` + Layout string `json:"layout"` + Area float64 `json:"area"` + UsableArea float64 `json:"usableArea"` + Orientation string `json:"orientation"` + Floor int `json:"floor"` + TotalFloors int `json:"totalFloors"` + Decoration string `json:"decoration"` + TotalPrice float64 `json:"totalPrice"` + UnitPrice float64 `json:"unitPrice"` + ListPrice float64 `json:"listPrice"` + Source string `json:"source"` + SourceHouseId string `json:"sourceHouseId"` + SourceUrl string `json:"sourceUrl"` + MatchGroupId uint64 `json:"matchGroupId"` + OnMarketDays int `json:"onMarketDays"` + PriceChangeCount int `json:"priceChangeCount"` + Status int `json:"status"` + Confidence int `json:"confidence"` + IsBargain int `json:"isBargain"` + ListingTime string `json:"listingTime"` + CreatedAt string `json:"createdAt"` +} + +// ListingListReq 分页查询房源(统一筛选入参,供管理列表与看板共用)。 +type ListingListReq struct { + g.Meta `path:"/house/listing/list" method:"post" tags:"Admin/House/Listing" summary:"房源列表"` + Page int `json:"page" d:"1" v:"min:1"` + Size int `json:"size" d:"10" v:"min:1|max:100"` + CommunityId uint64 `json:"communityId"` + Keyword string `json:"keyword"` // 匹配房号/户型 + Layout string `json:"layout"` + Region string `json:"region"` + Source string `json:"source"` + PriceMin float64 `json:"priceMin"` + PriceMax float64 `json:"priceMax"` + AreaMin float64 `json:"areaMin"` + AreaMax float64 `json:"areaMax"` + Status int `json:"status"` + IsBargain int `json:"isBargain"` // 0 全部,1 仅笋盘 + Confidence int `json:"confidence"` // 0 全部,1 仅低可信 +} + +// ListingListRes 是 ListingListReq 的响应。 +type ListingListRes struct { + List []*ListingItem `json:"list"` + Total int `json:"total"` +} + +// ListingCreateReq 新增房源(采集器/手动录入)。 +type ListingCreateReq struct { + g.Meta `path:"/house/listing/create" method:"post" tags:"Admin/House/Listing" summary:"新增房源"` + CommunityId uint64 `json:"communityId" v:"required"` + BuildingId uint64 `json:"buildingId"` + HouseNo string `json:"houseNo"` + Layout string `json:"layout"` + Area float64 `json:"area"` + UsableArea float64 `json:"usableArea"` + Orientation string `json:"orientation"` + Floor int `json:"floor"` + TotalFloors int `json:"totalFloors"` + Decoration string `json:"decoration"` + TotalPrice float64 `json:"totalPrice"` + UnitPrice float64 `json:"unitPrice"` + ListPrice float64 `json:"listPrice"` + Source string `json:"source"` + SourceHouseId string `json:"sourceHouseId"` + SourceUrl string `json:"sourceUrl"` + MatchGroupId uint64 `json:"matchGroupId"` + OnMarketDays int `json:"onMarketDays"` + PriceChangeCount int `json:"priceChangeCount"` + Status int `json:"status" d:"1"` + Confidence int `json:"confidence"` + IsBargain int `json:"isBargain"` +} + +// ListingCreateRes 是 ListingCreateReq 的响应。 +type ListingCreateRes struct { + Id uint64 `json:"id"` +} + +// ListingUpdateReq 更新房源。 +type ListingUpdateReq struct { + g.Meta `path:"/house/listing/update/{id}" method:"post" tags:"Admin/House/Listing" summary:"更新房源"` + Id uint64 `json:"id" in:"path" v:"required"` + CommunityId uint64 `json:"communityId"` + BuildingId uint64 `json:"buildingId"` + HouseNo string `json:"houseNo"` + Layout string `json:"layout"` + Area float64 `json:"area"` + UsableArea float64 `json:"usableArea"` + Orientation string `json:"orientation"` + Floor int `json:"floor"` + TotalFloors int `json:"totalFloors"` + Decoration string `json:"decoration"` + TotalPrice float64 `json:"totalPrice"` + UnitPrice float64 `json:"unitPrice"` + ListPrice float64 `json:"listPrice"` + MatchGroupId uint64 `json:"matchGroupId"` + Status int `json:"status"` + Confidence int `json:"confidence"` + IsBargain int `json:"isBargain"` +} + +// ListingUpdateRes 是 ListingUpdateReq 的响应。 +type ListingUpdateRes struct{} + +// ListingDeleteReq 删除房源(软删除)。 +type ListingDeleteReq struct { + g.Meta `path:"/house/listing/delete/{id}" method:"post" tags:"Admin/House/Listing" summary:"删除房源"` + Id uint64 `json:"id" in:"path" v:"required"` +} + +// ListingDeleteRes 是 ListingDeleteReq 的响应。 +type ListingDeleteRes struct{} + +// ListingBatchMarkReq 批量标记房源(置信度/笋盘/状态)。 +type ListingBatchMarkReq struct { + g.Meta `path:"/house/listing/batch-mark" method:"post" tags:"Admin/House/Listing" summary:"批量标记房源"` + Ids []uint64 `json:"ids" v:"required"` + Field string `json:"field" v:"required"` // confidence/isBargain/status + Value int `json:"value"` +} + +// ListingBatchMarkRes 是 ListingBatchMarkReq 的响应。 +type ListingBatchMarkRes struct { + Affected int64 `json:"affected"` +} diff --git a/api/open/v1/tools/doc.go b/api/open/v1/tools/doc.go deleted file mode 100644 index b9ec226..0000000 --- a/api/open/v1/tools/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package tools hosts the open API tool endpoints (GET/POST /tools/*). -// -// Naming rule: every sub-feature gets its own directory under tools/, named -// after the feature (dir name = package name), and the Req/Res contracts live -// in a file named after the feature inside that directory, e.g. -// -// api/open/v1/tools/uuid/uuid.go - GET /tools/uuid -// api/open/v1/tools/md5/md5.go - POST /tools/md5 -// api/open/v1/tools/random/random.go - GET /tools/random -// api/open/v1/tools/time/time.go - GET /tools/time -// api/open/v1/tools/ip/ip.go - GET /tools/ip -// -// As a feature grows, add more files inside its directory (e.g. idcard.go, -// invoice.go under ocr/) instead of bloating the starting file. -// -// Add a new feature by creating tools//.go and a matching method -// in internal/controller/open/.go; the router binds it automatically. -package tools diff --git a/api/open/v1/tools/ip/ip.go b/api/open/v1/tools/ip/ip.go deleted file mode 100644 index 211977c..0000000 --- a/api/open/v1/tools/ip/ip.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package ip defines the GET /tools/ip endpoint contract. -package ip - -import "github.com/gogf/gf/v2/frame/g" - -// IPReq returns the caller's IP information. -type IPReq struct { - g.Meta `path:"/tools/ip" method:"get" tags:"Open/Tools" summary:"Client IP info"` -} - -// IPRes is the response of IPReq. -type IPRes struct { - IP string `json:"ip"` - Internal bool `json:"internal"` // whether the IP is a private/internal address -} diff --git a/api/open/v1/tools/md5/md5.go b/api/open/v1/tools/md5/md5.go deleted file mode 100644 index cc3e13f..0000000 --- a/api/open/v1/tools/md5/md5.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package md5 defines the POST /tools/md5 endpoint contract. -package md5 - -import "github.com/gogf/gf/v2/frame/g" - -// MD5Req computes an MD5 digest. -type MD5Req struct { - g.Meta `path:"/tools/md5" method:"post" tags:"Open/Tools" summary:"Compute MD5 digest"` - Text string `json:"text" v:"required#text required"` -} - -// MD5Res is the response of MD5Req. -type MD5Res struct { - MD5 string `json:"md5"` -} diff --git a/api/open/v1/tools/random/random.go b/api/open/v1/tools/random/random.go deleted file mode 100644 index 3bbc346..0000000 --- a/api/open/v1/tools/random/random.go +++ /dev/null @@ -1,16 +0,0 @@ -// Package random defines the GET /tools/random endpoint contract. -package random - -import "github.com/gogf/gf/v2/frame/g" - -// RandomReq generates a random string. -type RandomReq struct { - g.Meta `path:"/tools/random" method:"get" tags:"Open/Tools" summary:"Generate a random string"` - Length int `json:"length" d:"16" v:"min:1|max:128#length must be 1-128"` - Type string `json:"type" d:"alnum" v:"in:alnum,digits,letters#unsupported type"` -} - -// RandomRes is the response of RandomReq. -type RandomRes struct { - Value string `json:"value"` -} diff --git a/api/open/v1/tools/time/time.go b/api/open/v1/tools/time/time.go deleted file mode 100644 index c88e738..0000000 --- a/api/open/v1/tools/time/time.go +++ /dev/null @@ -1,16 +0,0 @@ -// Package time defines the GET /tools/time endpoint contract. -package time - -import "github.com/gogf/gf/v2/frame/g" - -// TimeReq returns the current server time. -type TimeReq struct { - g.Meta `path:"/tools/time" method:"get" tags:"Open/Tools" summary:"Current server time"` -} - -// TimeRes is the response of TimeReq. -type TimeRes struct { - Timestamp int64 `json:"timestamp"` // unix seconds - DateTime string `json:"dateTime"` // 2006-01-02 15:04:05 - Date string `json:"date"` // 2006-01-02 -} diff --git a/api/open/v1/tools/uuid/uuid.go b/api/open/v1/tools/uuid/uuid.go deleted file mode 100644 index 47244cc..0000000 --- a/api/open/v1/tools/uuid/uuid.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package uuid defines the GET /tools/uuid endpoint contract. -package uuid - -import "github.com/gogf/gf/v2/frame/g" - -// UUIDReq generates a unique ID. -type UUIDReq struct { - g.Meta `path:"/tools/uuid" method:"get" tags:"Open/Tools" summary:"Generate a unique ID"` - Short bool `json:"short"` // true: 8-char short code; false: 32-char ID -} - -// UUIDRes is the response of UUIDReq. -type UUIDRes struct { - UUID string `json:"uuid"` -} diff --git a/api/user/login/login.go b/api/user/login/login.go new file mode 100644 index 0000000..607adca --- /dev/null +++ b/api/user/login/login.go @@ -0,0 +1 @@ +package user_login diff --git a/api/user/v1/auth/auth.go b/api/user/v1/auth/auth.go deleted file mode 100644 index 4a1bbe8..0000000 --- a/api/user/v1/auth/auth.go +++ /dev/null @@ -1,25 +0,0 @@ -package auth - -import "github.com/gogf/gf/v2/frame/g" - -type LoginReq struct { - g.Meta `path:"/auth/login" method:"post" tags:"User/Auth" summary:"User login"` - LoginType string `json:"loginType" v:"required|in:wechat,mobile,password#login type required|unsupported login type"` - Code string `json:"code"` - Mobile string `json:"mobile"` - VerifyCode string `json:"verifyCode"` - Account string `json:"account"` - Password string `json:"password"` - Terminal string `json:"terminal" v:"required|in:mini,h5,app#terminal required|invalid terminal"` -} -type LoginRes struct { - AccessToken string `json:"accessToken"` - RefreshToken string `json:"refreshToken"` - ExpiresIn int64 `json:"expiresIn"` - UserID uint64 `json:"userId"` -} -type RefreshReq struct { - g.Meta `path:"/auth/refresh" method:"post" tags:"User/Auth" summary:"Rotate user token"` - RefreshToken string `json:"refreshToken" v:"required"` -} -type RefreshRes LoginRes diff --git a/common/doc.go b/common/doc.go index 1bfcbf5..9d82f3a 100644 --- a/common/doc.go +++ b/common/doc.go @@ -1,10 +1,10 @@ -// Package common is the public, reusable module of service.xpcool.com. +// Package common 是 service.xpcool.com 的公共可复用模块。 // -// It contains only code that is independent of internal/ — anything here can -// be shared across services inside the repository, or extracted into a -// standalone library later without touching business code. +// 仅包含与 internal/ 无关的代码——此处的内容可以 +// 在仓库内跨服务共享,或日后抽取为独立库, +// 均无需改动业务代码。 // -// Current layout: +// 当前结构: // // common/tools shared utility toolbox (md5, cryptox, uuid, ...) package common diff --git a/common/tools/convertx/convertx.go b/common/tools/convertx/convertx.go index 8b463c4..89c5e71 100644 --- a/common/tools/convertx/convertx.go +++ b/common/tools/convertx/convertx.go @@ -1,9 +1,9 @@ -// Package convertx provides type conversion helpers with default-value -// fallback, built on top of gconv. +// Package convertx 提供带默认值兜底的类型转换工具, +// 基于 gconv 构建。 // -// Note: gconv silently returns the zero value when conversion fails, so the -// helpers here only fall back to the default for nil / blank-string inputs. -// Pass pre-validated data when strict conversion is required. +// 注意:gconv 转换失败时静默返回零值, +// 因此本工具仅对 nil / 空字符串输入回退到默认值。 +// 需要严格转换时请传入已校验的数据。 package convertx import ( @@ -12,7 +12,7 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) -// ToInt converts v to int, returning def when v is nil or a blank string. +// ToInt 将 v 转为 int,v 为 nil 或空字符串时返回 def。 func ToInt(v any, def int) int { if isEmpty(v) { return def @@ -20,7 +20,7 @@ func ToInt(v any, def int) int { return gconv.Int(v) } -// ToInt64 converts v to int64, returning def when v is nil or a blank string. +// ToInt64 将 v 转为 int64,v 为 nil 或空字符串时返回 def。 func ToInt64(v any, def int64) int64 { if isEmpty(v) { return def @@ -28,7 +28,7 @@ func ToInt64(v any, def int64) int64 { return gconv.Int64(v) } -// ToFloat64 converts v to float64, returning def when v is nil or a blank string. +// ToFloat64 将 v 转为 float64,v 为 nil 或空字符串时返回 def。 func ToFloat64(v any, def float64) float64 { if isEmpty(v) { return def @@ -36,7 +36,7 @@ func ToFloat64(v any, def float64) float64 { return gconv.Float64(v) } -// ToString converts v to string, returning def when v is nil. +// ToString 将 v 转为 string,v 为 nil 时返回 def。 func ToString(v any, def string) string { if v == nil { return def @@ -44,7 +44,7 @@ func ToString(v any, def string) string { return gconv.String(v) } -// ToBool converts v to bool, returning def when v is nil or a blank string. +// ToBool 将 v 转为 bool,v 为 nil 或空字符串时返回 def。 func ToBool(v any, def bool) bool { if isEmpty(v) { return def @@ -52,7 +52,7 @@ func ToBool(v any, def bool) bool { return gconv.Bool(v) } -// isEmpty reports whether v is nil or a blank string. +// isEmpty 判断 v 是否为 nil 或空字符串。 func isEmpty(v any) bool { if v == nil { return true diff --git a/common/tools/cryptox/cryptox.go b/common/tools/cryptox/cryptox.go index 8045c1e..55efc63 100644 --- a/common/tools/cryptox/cryptox.go +++ b/common/tools/cryptox/cryptox.go @@ -1,8 +1,8 @@ -// Package cryptox provides AES/DES encryption helpers with base64 output, -// built on top of gaes and gdes. +// Package cryptox 提供 AES/DES 加解密工具(base64 输出), +// 基于 gaes 与 gdes 构建。 // -// Keys: any-length secrets are accepted — they are normalized to the exact -// key size internally, so callers do not need to manage key lengths. +// 密钥:接受任意长度密钥,内部会归一化为精确密钥尺寸, +// 调用方无需关心密钥长度。 package cryptox import ( @@ -19,7 +19,7 @@ const ( desKeySize = 8 // DES key size in bytes ) -// normalizeKey derives a fixed-size key from an arbitrary-length secret. +// normalizeKey 从任意长度密钥派生出固定尺寸密钥。 func normalizeKey(secret string, size int) []byte { key := []byte(secret) if len(key) == size { @@ -33,8 +33,8 @@ func normalizeKey(secret string, size int) []byte { return out } -// AesEncrypt encrypts plainText with AES-128-CBC using a key derived from -// secret, and returns the ciphertext encoded in base64. +// AesEncrypt 使用由 secret 派生的密钥对 plainText 做 AES-128-CBC 加密, +// 返回 base64 编码的密文。 func AesEncrypt(plainText, secret string) (string, error) { out, err := gaes.Encrypt([]byte(plainText), normalizeKey(secret, aesKeySize)) if err != nil { @@ -43,7 +43,7 @@ func AesEncrypt(plainText, secret string) (string, error) { return base64.StdEncoding.EncodeToString(out), nil } -// AesDecrypt decrypts the base64-encoded cipherText produced by AesEncrypt. +// AesDecrypt 解密 AesEncrypt 产生的 base64 密文。 func AesDecrypt(cipherText, secret string) (string, error) { data, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { @@ -56,8 +56,8 @@ func AesDecrypt(cipherText, secret string) (string, error) { return string(out), nil } -// DesEncrypt encrypts plainText with DES-ECB (PKCS5 padding) using a key -// derived from secret, and returns the ciphertext encoded in base64. +// DesEncrypt 使用由 secret 派生的密钥对 plainText 做 DES-ECB(PKCS5 填充)加密, +// 返回 base64 编码的密文。 func DesEncrypt(plainText, secret string) (string, error) { out, err := gdes.EncryptECB([]byte(plainText), normalizeKey(secret, desKeySize), gdes.PKCS5PADDING) if err != nil { @@ -66,7 +66,7 @@ func DesEncrypt(plainText, secret string) (string, error) { return base64.StdEncoding.EncodeToString(out), nil } -// DesDecrypt decrypts the base64-encoded cipherText produced by DesEncrypt. +// DesDecrypt 解密 DesEncrypt 产生的 base64 密文。 func DesDecrypt(cipherText, secret string) (string, error) { data, err := base64.StdEncoding.DecodeString(cipherText) if err != nil { diff --git a/common/tools/doc.go b/common/tools/doc.go index 9768911..5fc5ea0 100644 --- a/common/tools/doc.go +++ b/common/tools/doc.go @@ -1,10 +1,10 @@ -// Package tools is the shared utility toolbox of the project. +// Package tools 是项目的公共工具集。 // -// Every sub-package is a thin, documented wrapper around GoFrame built-in -// components (gmd5, gaes, gdes, guid, grand, gtime, gconv, gstr, gfile, ...), -// so it stays small and consistent with the framework. +// 每个子包都是对 GoFrame 内置组件的薄封装, +// (gmd5、gaes、gdes、guid、grand、gtime、gconv、gstr、gfile 等), +// 保持小巧且与框架风格一致。 // -// Layout: +// 结构: // // common/tools/md5 MD5 digest helpers // common/tools/cryptox AES/DES encryption with base64 output @@ -17,7 +17,7 @@ // common/tools/ip IP address helpers // common/tools/filex file system helpers // -// Rules: +// 规则: // - Never depend on internal/ — this module must stay self-contained. // - Prefer reusing GoFrame built-in components over re-implementing. // - Keep each helper small and add a Chinese doc comment. diff --git a/common/tools/filex/filex.go b/common/tools/filex/filex.go index 4853141..2fb817d 100644 --- a/common/tools/filex/filex.go +++ b/common/tools/filex/filex.go @@ -1,28 +1,28 @@ -// Package filex provides common file system helpers on top of gfile. +// Package filex 基于 gfile 提供常用文件系统工具。 package filex import ( "github.com/gogf/gf/v2/os/gfile" ) -// Exists reports whether the file or directory at path exists. +// Exists 判断 path 对应的文件或目录是否存在。 func Exists(path string) bool { return gfile.Exists(path) } -// IsDir reports whether path is a directory. +// IsDir 判断 path 是否为目录。 func IsDir(path string) bool { return gfile.IsDir(path) } -// ReadString returns the full content of the file at path as a string. -// Returns an empty string when the file does not exist. +// ReadString 以字符串形式返回 path 文件的完整内容, +// 文件不存在时返回空字符串。 func ReadString(path string) string { return gfile.GetContents(path) } -// WriteString writes content to the file at path, creating intermediate -// directories if needed. +// WriteString 将 content 写入 path 文件,必要时自动创建中间目录。 +// (续上一行) func WriteString(path, content string) error { return gfile.PutContents(path, content) } diff --git a/common/tools/ip/ip.go b/common/tools/ip/ip.go index bb8913f..abebf37 100644 --- a/common/tools/ip/ip.go +++ b/common/tools/ip/ip.go @@ -1,4 +1,4 @@ -// Package ip provides IP address helpers. +// Package ip 提供 IP 地址工具。 package ip import ( @@ -9,12 +9,12 @@ import ( "github.com/gogf/gf/v2/net/gipv4" ) -// IsValid reports whether s is a valid IPv4 address. +// IsValid 判断 s 是否为合法的 IPv4 地址。 func IsValid(s string) bool { return gipv4.Validate(s) } -// LocalIP returns the first non-loopback IPv4 address of this host. +// LocalIP 返回本机第一个非回环 IPv4 地址。 func LocalIP() (string, error) { addrs, err := net.InterfaceAddrs() if err != nil { @@ -30,7 +30,7 @@ func LocalIP() (string, error) { return "", nil } -// IsInternal reports whether s is a private/internal IPv4 address +// IsInternal 判断 s 是否为私有/内网 IPv4 地址 // (private ranges, loopback or link-local). func IsInternal(s string) bool { parsed := net.ParseIP(s) @@ -40,7 +40,7 @@ func IsInternal(s string) bool { return parsed.IsPrivate() || parsed.IsLoopback() || parsed.IsLinkLocalUnicast() } -// ToLong converts an IPv4 string to its uint32 representation +// ToLong 将 IPv4 字符串转为 uint32 数值表示 // (big-endian, same as inet_aton). func ToLong(s string) (uint32, error) { ipv4 := net.ParseIP(s).To4() @@ -50,7 +50,7 @@ func ToLong(s string) (uint32, error) { return uint32(ipv4[0])<<24 | uint32(ipv4[1])<<16 | uint32(ipv4[2])<<8 | uint32(ipv4[3]), nil } -// ToString converts a uint32 IPv4 value to its dotted-decimal string. +// ToString 将 uint32 的 IPv4 值转为点分十进制字符串。 func ToString(v uint32) string { return strconv.Itoa(int(v>>24)) + "." + strconv.Itoa(int(v>>16&0xFF)) + "." + diff --git a/common/tools/md5/md5.go b/common/tools/md5/md5.go index d7d0f93..4dc8cf3 100644 --- a/common/tools/md5/md5.go +++ b/common/tools/md5/md5.go @@ -1,23 +1,23 @@ -// Package md5 provides MD5 digest helpers. +// Package md5 提供 MD5 摘要工具。 package md5 import ( "github.com/gogf/gf/v2/crypto/gmd5" ) -// Md5Hex returns the MD5 digest of s as a lowercase hex string. -// The underlying error is ignored because it never fails for in-memory input. +// Md5Hex 返回 s 的 MD5 摘要(小写十六进制字符串)。 +// 底层错误被忽略,因为对内存输入永远不会失败。 func Md5Hex(s string) string { h, _ := gmd5.EncryptString(s) return h } -// Md5Bytes returns the MD5 digest of data as a lowercase hex string. +// Md5Bytes 返回 data 的 MD5 摘要(小写十六进制字符串)。 func Md5Bytes(data []byte) (string, error) { return gmd5.Encrypt(data) } -// Md5File returns the MD5 digest of the local file at path as a hex string. +// Md5File 返回 path 本地文件的 MD5 摘要(十六进制字符串)。 func Md5File(path string) (string, error) { return gmd5.EncryptFile(path) } diff --git a/common/tools/random/random.go b/common/tools/random/random.go index 330bff6..2f06b48 100644 --- a/common/tools/random/random.go +++ b/common/tools/random/random.go @@ -1,26 +1,26 @@ -// Package random provides random number and string generation helpers. +// Package random 提供随机数与随机字符串生成工具。 package random import ( "github.com/gogf/gf/v2/util/grand" ) -// Int returns a random integer in [min, max]. +// Int 返回 [min, max] 区间内的随机整数。 func Int(min, max int) int { return grand.N(min, max) } -// String returns a random alphanumeric string of length n. +// String 返回长度为 n 的随机字母数字字符串。 func String(n int) string { return grand.S(n) } -// Digits returns a random numeric-only string of length n, e.g. SMS codes. +// Digits 返回长度为 n 的纯数字随机字符串,如短信验证码。 func Digits(n int) string { return grand.Digits(n) } -// Letters returns a random letter-only string of length n. +// Letters 返回长度为 n 的纯字母随机字符串。 func Letters(n int) string { return grand.Letters(n) } diff --git a/common/tools/slicex/slicex.go b/common/tools/slicex/slicex.go index b71f312..497f092 100644 --- a/common/tools/slicex/slicex.go +++ b/common/tools/slicex/slicex.go @@ -1,17 +1,17 @@ -// Package slicex provides generic slice utilities built on the standard -// library (Go 1.23+). +// Package slicex 基于标准库提供通用切片工具(Go 1.23+)。 +// (续上一行) package slicex import ( "slices" ) -// Contains reports whether v is present in items. +// Contains 判断 items 中是否包含 v。 func Contains[T comparable](items []T, v T) bool { return slices.Contains(items, v) } -// Unique returns items with duplicates removed, preserving first-seen order. +// Unique 去除 items 中重复元素,保持首次出现顺序。 func Unique[T comparable](items []T) []T { seen := make(map[T]struct{}, len(items)) out := make([]T, 0, len(items)) @@ -25,8 +25,8 @@ func Unique[T comparable](items []T) []T { return out } -// Chunk splits items into sub-slices of at most size elements. -// Returns nil when size <= 0 or items is empty. +// Chunk 将 items 切分为最多 size 个元素的子切片, +// size <= 0 或 items 为空时返回 nil。 func Chunk[T any](items []T, size int) [][]T { if size <= 0 || len(items) == 0 { return nil @@ -43,7 +43,7 @@ func Chunk[T any](items []T, size int) [][]T { return out } -// Map applies fn to every element and returns the results. +// Map 对每个元素应用 fn 并返回结果。 func Map[T, R any](items []T, fn func(T) R) []R { out := make([]R, len(items)) for i, v := range items { @@ -52,7 +52,7 @@ func Map[T, R any](items []T, fn func(T) R) []R { return out } -// Filter returns the elements for which fn returns true, preserving order. +// Filter 返回 fn 为 true 的元素,保持原顺序。 func Filter[T any](items []T, fn func(T) bool) []T { out := make([]T, 0, len(items)) for _, v := range items { diff --git a/common/tools/strx/strx.go b/common/tools/strx/strx.go index 5c51868..472a60a 100644 --- a/common/tools/strx/strx.go +++ b/common/tools/strx/strx.go @@ -1,5 +1,5 @@ -// Package strx provides string helpers on top of gstr, including naming -// conversion and sensitive-data masking. +// Package strx 基于 gstr 提供字符串工具,含命名 +// 转换与敏感数据脱敏。 package strx import ( @@ -8,28 +8,28 @@ import ( "github.com/gogf/gf/v2/text/gstr" ) -// SnakeCase converts s to snake_case, e.g. "UserName" -> "user_name". +// SnakeCase 将 s 转为 snake_case,如 "UserName" -> "user_name"。 func SnakeCase(s string) string { return gstr.CaseSnake(s) } -// CamelCase converts s to CamelCase, e.g. "user_name" -> "UserName". +// CamelCase 将 s 转为 CamelCase,如 "user_name" -> "UserName"。 func CamelCase(s string) string { return gstr.CaseCamel(s) } -// LowerCamelCase converts s to lowerCamelCase, e.g. "user_name" -> "userName". +// LowerCamelCase 将 s 转为 lowerCamelCase,如 "user_name" -> "userName"。 func LowerCamelCase(s string) string { return gstr.CaseCamelLower(s) } -// IsEmpty reports whether s is empty or whitespace-only. +// IsEmpty 判断 s 是否为空或仅含空白字符。 func IsEmpty(s string) bool { return strings.TrimSpace(s) == "" } -// MaskPhone masks a phone number, keeping the first 3 and last 4 characters. -// e.g. "13812345678" -> "138****5678". +// MaskPhone 对手机号脱敏,保留前 3 位与后 4 位。 +// 例如 "13812345678" -> "138****5678"。 func MaskPhone(s string) string { if len(s) < 7 { return s @@ -37,8 +37,8 @@ func MaskPhone(s string) string { return s[:3] + "****" + s[len(s)-4:] } -// MaskIDCard masks a Chinese ID card number, keeping the first 6 and last 4 -// characters. e.g. "110101199003074512" -> "110101********4512". +// MaskIDCard 对身份证号脱敏,保留前 6 位与后 4 位, +// 例如 "110101199003074512" -> "110101********4512"。 func MaskIDCard(s string) string { if len(s) < 10 { return s @@ -46,7 +46,7 @@ func MaskIDCard(s string) string { return s[:6] + "********" + s[len(s)-4:] } -// MaskName masks a Chinese name, keeping only the first character. +// MaskName 对中文姓名脱敏,仅保留首字符。 // e.g. "张三丰" -> "张**". func MaskName(s string) string { r := []rune(s) diff --git a/common/tools/timex/timex.go b/common/tools/timex/timex.go index a6ce097..3447693 100644 --- a/common/tools/timex/timex.go +++ b/common/tools/timex/timex.go @@ -1,4 +1,4 @@ -// Package timex provides time formatting and computation helpers on top of gtime. +// Package timex 基于 gtime 提供时间格式化与计算工具。 package timex import ( @@ -6,22 +6,22 @@ import ( ) const ( - // LayoutDateTime is the conventional datetime layout: 2006-01-02 15:04:05. + // LayoutDateTime 是常用日期时间格式:2006-01-02 15:04:05。 LayoutDateTime = "2006-01-02 15:04:05" - // LayoutDate is the conventional date layout: 2006-01-02. + // LayoutDate 是常用日期格式:2006-01-02。 LayoutDate = "2006-01-02" ) -// Now returns the current time. +// Now 返回当前时间。 func Now() *gtime.Time { return gtime.Now() } -// Format returns t formatted with the given Go layout. -// When layout is empty, LayoutDateTime is used. +// Format 按给定 Go 布局格式化 t, +// layout 为空时使用 LayoutDateTime。 // -// Note: gtime v2.10.2's Format() takes PHP-style format ("Y-m-d H:i:s"), -// so this helper uses the Layout() method which accepts Go layouts. +// 注意:gtime v2.10.2 的 Format() 接收 PHP 风格格式("Y-m-d H:i:s"), +// 因此本工具改用接受 Go 布局的 Layout() 方法。 func Format(t *gtime.Time, layout ...string) string { ly := LayoutDateTime if len(layout) > 0 && layout[0] != "" { @@ -30,17 +30,17 @@ func Format(t *gtime.Time, layout ...string) string { return t.Layout(ly) } -// Timestamp returns the current Unix timestamp in seconds. +// Timestamp 返回当前 Unix 秒级时间戳。 func Timestamp() int64 { return gtime.Now().Timestamp() } -// StartOfDay returns the beginning (00:00:00) of the day containing t. +// StartOfDay 返回 t 所在日期的零点(00:00:00)。 func StartOfDay(t *gtime.Time) *gtime.Time { return gtime.NewFromStr(t.Layout(LayoutDate) + " 00:00:00") } -// EndOfDay returns the end (23:59:59) of the day containing t. +// EndOfDay 返回 t 所在日期的末尾(23:59:59)。 func EndOfDay(t *gtime.Time) *gtime.Time { return gtime.NewFromStr(t.Layout(LayoutDate) + " 23:59:59") } diff --git a/common/tools/uuid/uuid.go b/common/tools/uuid/uuid.go index 16e33d7..f5661c9 100644 --- a/common/tools/uuid/uuid.go +++ b/common/tools/uuid/uuid.go @@ -1,4 +1,4 @@ -// Package uuid provides unique ID generation helpers. +// Package uuid 提供唯一 ID 生成工具。 package uuid import ( @@ -6,13 +6,13 @@ import ( "github.com/gogf/gf/v2/util/guid" ) -// New returns a 32-character unique ID without dashes. +// New 返回不带连字符的 32 位唯一 ID。 func New() string { return guid.S() } -// Short returns a random alphanumeric ID of length n (defaults to 8 when n <= 0). -// Suitable for short invite codes / trace ids. +// Short 返回长度为 n 的随机字母数字 ID(n <= 0 时默认为 8 位), +// 适合短邀请码 / 追踪 ID 场景。 func Short(n int) string { if n <= 0 { n = 8 diff --git a/docs/house-system-design.md b/docs/house-system-design.md new file mode 100644 index 0000000..66fdb6f --- /dev/null +++ b/docs/house-system-design.md @@ -0,0 +1,223 @@ +# 看房系统(House)总设计文档 + +> 版本:v1.0 | 日期:2026-08-26 | 状态:开发中(阶段 0/1) +> 定位:贵阳购房决策辅助系统,从公开房源数据中筛选「最适合自己的房子」。 + +## 1. 项目概述 + +买房的核心矛盾是「信息高度不对称」。本系统通过 **采集 → 存储 → 管理 → 可视化 → 推送** 五段闭环,把贵阳公开房源数据沉淀成一份可管理、可分析、可对比的个人决策资产。 + +- **目标城市**:贵阳(二线/省会,数据源以贝壳/安居客/房天下 + 住建局网签为主) +- **房源类型**:新房 + 二手房都做(两套数据模型并存) +- **核心诉求**:房价波动分析、楼盘楼栋级地图、多平台报价对比、个性化打分推荐、Bark 推送 + +## 2. 总体架构 + +### 2.1 五层架构 + +``` +应用展示层 房价地图热力 · 楼盘对比看板 · 个性化榜单 +分析计算层 价格趋势分析 · 匹配打分排序 · 通勤配套评估 +数据治理层 清洗与标准化 · 房源去重对齐(改:软关联) · 小区实体映射 +数据存储层 MySQL(house_* 表) · 价格时序快照 +数据采集层 多平台采集器 · 调度与限频 · 代理与反反爬 +``` + +### 2.2 落地拓扑(贴合现有项目) + +``` +贵阳公开数据源(贝壳/安居客/房天下/住建局网签) + │ 抓取 +Python 采集分析服务(独立进程: 采集/清洗/软关联/打分/调度) + │ 写库 +MySQL 共享库(house_* 表) ←── 与 service 同库 + │ 读写 +service.xpcool.com(GoFrame v2 · house 模块 REST API) + ├── REST ──→ admin.xpcool.com(Vue3+Vben: 管理列表 + 可视化看板) + └── 推送 ──→ Bark(苹果) +``` + +**关键决策**:Python 只负责「把数据搞干净写进库 + 算分」,不对外提供业务 API;所有查询/推送由 Go 的 house 模块统一暴露,保持单一出口。 + +## 3. 技术选型 + +| 层 | 选型 | 理由 | +|----|------|------| +| 后端 | GoFrame v2.10(现有 service 栈) | 复用分层 + RBAC + gf gen dao | +| 数据库 | MySQL 8(现有同库) | 贵阳数据量级(房源几十万/快照百万)单机够用,不引 PostGIS/TimescaleDB | +| 前端 | Vben Admin 5(web-tdesign)+ echarts@6 + vxe-table@4 | 全部现成依赖,零新增 | +| 地图 | 腾讯地图 GL JS(合规)+ DataV GeoJSON | 底图合规、支持 MultiMarker/热力/多边形 | +| 采集分析 | Python(Scrapy/httpx + pandas) | 爬虫与数据分析生态最强 | +| 推送 | Bark(自建 Server 到腾讯云 / 官方免费版) | 苹果原生推送,一条 HTTP 即可 | + +## 4. 数据源规划(贵阳) + +| 类别 | 数据源 | 备注 | +|------|--------|------| +| 二手房挂牌 | 贝壳 gy.ke.com、安居客、房天下、58 | 贝壳最规整,MVP 首选 | +| 成交/网签 | 贵阳市住建局网签备案、贝壳成交频道 | **成交价是真实价,最大风险点** | +| 新房备案价 | 住建局预售许可 + 一房一价备案 | 新房「真价格」来源 | +| 配套/通勤 | 高德/百度地图 API | 地铁、学校、商圈 POI + 真实通勤时间 | +| 学区划片 | 贵阳市/各区教育局划片文件 | 年度版本,半自动采集 | + +## 5. 数据模型(9 张表) + +### 5.1 house_community 小区/楼盘 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT UNSIGNED PK | | +| name | VARCHAR | 小区名 | +| region | VARCHAR | 区县(云岩/南明/观山湖/花溪…) | +| business_district | VARCHAR | 板块 | +| address / lng / lat | VARCHAR / DECIMAL(10,6) | 定位(GCJ-02) | +| build_year / households | INT | 建成年份/户数 | +| plot_ratio / green_rate | DECIMAL | 容积率/绿化率 | +| property_company / property_fee | VARCHAR / DECIMAL | 物业/物业费 | +| developer | VARCHAR | 开发商 | + +### 5.2 house_building 楼栋 + +| 字段 | 说明 | +|------|------| +| community_id | 所属小区 | +| building_no | 栋号 | +| units / total_floors / elevator_count / ladder_ratio | 单元/总层/电梯/梯户比 | +| building_type | 板楼/塔楼 | +| lng / lat | 楼栋级坐标(三级落地:小区中心→楼栋图解析→重点盘人工校准) | + +### 5.3 house_listing 房源/挂牌(核心) + +| 字段 | 说明 | +|------|------| +| community_id / building_id / house_no | 归属 | +| layout / area / usable_area | 户型 / 建面 / 套内(**建面套内要标准化**) | +| orientation / floor / total_floors / decoration | 朝向/楼层/总层/装修 | +| total_price / unit_price / list_price | 总价/单价/挂牌价 | +| **source / source_house_id / source_url** | 来源平台(**跨平台不去重**) | +| **match_group_id** | 疑似同房源软关联(对比用) | +| on_market_days / price_change_count | 挂牌天数/调价次数 | +| status | 在售/下架/成交 | +| confidence / is_bargain | 可信度标记 / 笋盘标记 | + +> 多平台对比策略:同平台 `source+source_house_id` 唯一(防重复抓);跨平台各存一条,用「小区+楼栋+户型+面积±3%+楼层」算相似度打 `match_group_id`,用于「疑似同房源」对比视图(不合并)。 + +### 5.4 house_price_snapshot 价格快照(时序) + +`listing_id + snap_date` 唯一;`list_price` / `deal_price` 分列。**趋势分析命脉,长期保留 1–2 年**。 + +### 5.5 house_transaction 成交记录 + +`deal_price` / `deal_unit_price` / `list_days`(挂牌到成交天数)/ `deal_date`。 + +### 5.6 house_facility 配套 POI + +`name` / `type`(地铁/学校/医院/商圈) / `lng/lat` / `line`(地铁线路)。 + +### 5.7 house_community_facility 小区-配套关系 + +`community_id + facility_id`,`distance`(米) + `commute_minutes`(通勤分钟)。 + +### 5.8 house_school_district 学区划片 + +`school_name` / `community_id` / `district_polygon`(GeoJSON) / `district_year`(划片年度,版本化)。 + +### 5.9 house_preference 用户偏好画像 + +`budget_min/max` / `area_min/max` / `layouts`(JSON) / `subway_lines`(JSON) / `school_required` / `commute_target` / `commute_limit_min` / `weights`(权重 JSON)。 + +## 6. 后端模块设计(service.xpcool.com) + +### 6.1 目录落位 + +``` +api/house//.go # 契约(g.Meta path/method) +internal/controller/house/*.go # 适配层 +internal/service/house// # 领域服务(接口+实现+Register) +internal/model/dto/house.go # 服务边界 dto +internal/model/{entity,do} # gf gen dao 生成 +manifest/sql/010_house_tables.sql # 建表 +manifest/sql/011_house_menu.sql # 菜单+权限种子 +``` + +### 6.2 接口清单(全 POST 动作式,前缀 /api/service/admin/house) + +**管理 CRUD**: +``` +/house/community/{list|create|update|delete} +/house/listing/{list|create|update|delete|batch-mark} +/house/snapshot/{list} +/house/transaction/{list} +/house/facility/{list|create|update|delete} +/house/district/{list|create|update|delete} +/house/crawl-task/{list|trigger|log} +``` + +**看板聚合/筛选**(统一 `FilterDto` 入参): +``` +/house/dashboard/{overview|map-points|price-trend|aggregate-region} +/house/compare +/house/rank +``` + +### 6.3 权限 + +`admin_menu`:type=1 菜单(component 指向前端组件)+ type=2 API 权限(path=`POST /api/service/admin/house/...`);`admin_role_menu` 绑定超管(role_id=1)。 + +## 7. 前端模块设计(admin.xpcool.com) + +### 7.1 页面 + +| 分组 | 页面 | 组件路径 | +|------|------|---------| +| 数据管理 | 小区/楼盘管理 | house/community/index | +| 数据管理 | 房源管理(筛选/标记/批量) | house/listing/index | +| 数据管理 | 价格快照/成交/配套/学区 | house/data/index(后续拆分) | +| 可视化看板 | 看板(筛选器+图表联动) | house/dashboard/index | + +### 7.2 交互 + +- 管理列表:查询表单 + vxe-table + 分页 + 行内操作 + 批量,复用 RBAC +- 看板:全局筛选器(区域/价格/户型/面积/地铁/学区/通勤)驱动图表联动;筛选器→图表、图表交叉过滤、列表↔地图双向;状态放 Pinia + +## 8. Python 采集分析服务 + +``` +house-data/ +├── crawlers/ 各平台采集器 +├── pipeline/ 清洗→软关联→标准化→入库 +├── analysis/ pandas 趋势/议价空间/打分 +├── scheduler/ APScheduler 调度 + 限频 +└── notify/ 触发 Bark +``` + +只写库,不对外 API;独立 git 仓库(建议 E:\xxcool\project\house-data\)。 + +## 9. 可视化设计(地图图层) + +底图腾讯地图 GL JS,自下而上叠加:区县/板块边界(DataV GeoJSON) → 地铁线(1/2/3号线 Polyline) → 学区划片(polygon,年度版本) → 楼盘/楼栋点(MultiMarker) → 价格热力(可切换)。 + +## 10. 推送设计(Bark) + +| 触发 | level | group | 附 url | +|------|-------|-------|--------| +| 降价>3% | timeSensitive | 降价 | 跳房源对比页 | +| 新房上架 | active | 新房 | 跳详情 | +| 划片变更 | timeSensitive | 学区 | 跳小区 | +| 每日汇总 | passive | 汇总 | 跳看板 | + +## 11. 分阶段路线图 + +| 阶段 | 周期 | 交付 | +|------|------|------| +| 0 方案定稿 | 1–2 天 | 建表 + gf gen dao + Python 骨架 | +| 1 MVP | ~1 周 | 贝壳二手房挂牌 + 价格快照;房源列表/详情 API;列表 + 趋势图 | +| 2 分析 | ~1 周 | 成交/网签 + 软关联对比 + 地图热力 + 楼盘对比 | +| 3 决策 | ~1 周 | 新房备案价 + 学区/配套 + 偏好打分 | +| 4 自动化 | ~1 周 | Bark 推送 + 迁腾讯云 7×24 | + +## 12. 合规与风险 + +- 地图:仅腾讯/高德/百度/天地图;区县边界用 DataV 审图号数据;key 走代理不外泄;不采集他人个人位置 +- 爬虫:遵守 robots、低频、代理池、只存公开信息、个人自用 +- **最大风险**:贵阳网签/成交数据公开程度不如一线,MVP 用贝壳成交频道兜底 diff --git a/hack/config.yaml b/hack/config.yaml index 83c7dc6..76d7735 100644 --- a/hack/config.yaml +++ b/hack/config.yaml @@ -4,8 +4,9 @@ gfcli: gen: dao: - - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + - link: "mysql:root:root123@tcp(127.0.0.1:3306)/service_xpcool_com" descriptionTag: true + tables: "house_community,house_building,house_listing,house_price_snapshot,house_transaction,house_facility,house_community_facility,house_school_district,house_preference" docker: build: "-a amd64 -s linux -p temp -ew" diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index ab86234..b8b6947 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -10,19 +10,23 @@ import ( "github.com/gogf/gf/v2/os/genv" adminctl "service.xpcool.com/internal/controller/admin" - "service.xpcool.com/internal/controller/hello" + housectl "service.xpcool.com/internal/controller/house" openctl "service.xpcool.com/internal/controller/open" userctl "service.xpcool.com/internal/controller/user" "service.xpcool.com/internal/library/jwt" "service.xpcool.com/internal/middleware" - "service.xpcool.com/internal/service/admin/admin" - "service.xpcool.com/internal/service/admin/base/log" - adminaudit "service.xpcool.com/internal/service/admin/system/audit" - adminauth "service.xpcool.com/internal/service/admin/system/auth" + admin "service.xpcool.com/internal/service/admin/admin/admin" + adminauth "service.xpcool.com/internal/service/admin/admin/login" + log "service.xpcool.com/internal/service/admin/base/log" + adminaudit "service.xpcool.com/internal/service/admin/system/log" + adminloginlog "service.xpcool.com/internal/service/admin/system/login_log" adminmenu "service.xpcool.com/internal/service/admin/system/menu" - "service.xpcool.com/internal/service/admin/system/menu_manage" - "service.xpcool.com/internal/service/admin/system/role" + menu_manage "service.xpcool.com/internal/service/admin/system/menu_manage" + role "service.xpcool.com/internal/service/admin/system/role" userauth "service.xpcool.com/internal/service/user/auth" + housecommunity "service.xpcool.com/internal/service/house/community" + housedashboard "service.xpcool.com/internal/service/house/dashboard" + houselisting "service.xpcool.com/internal/service/house/listing" ) // injectEnv 手动把关键环境变量写入配置系统。 @@ -58,37 +62,35 @@ var ( menu_manage.RegisterMenuManage(menu_manage.NewMenuManage()) log.RegisterLogManage(log.NewLogManage()) adminaudit.RegisterAdminAudit(adminaudit.NewAdminAudit()) - s.Group("/", func(group *ghttp.RouterGroup) { - group.Middleware(middleware.Recover, middleware.CORS) - group.Middleware(ghttp.MiddlewareHandlerResponse) - group.Bind( - hello.NewV1(), - ) - }) - s.Group("/api/open/v1", func(group *ghttp.RouterGroup) { + adminloginlog.RegisterAdminLoginLog(adminloginlog.NewAdminLoginLog()) + housecommunity.RegisterCommunity(housecommunity.NewCommunity()) + houselisting.RegisterListing(houselisting.NewListing()) + housedashboard.RegisterDashboard(housedashboard.NewDashboard()) + s.Group("/api/service/open", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(openctl.New()) // Open tools API for frontends, no auth required. }) - s.Group("/api/v1", func(group *ghttp.RouterGroup) { + s.Group("/api/service/user", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(userctl.New()) // Login and refresh routes are public. group.Group("/", func(protected *ghttp.RouterGroup) { protected.Middleware(middleware.UserAuth(tokens)) }) }) - s.Group("/admin/v1", func(group *ghttp.RouterGroup) { + s.Group("/api/service/admin", func(group *ghttp.RouterGroup) { group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) group.Bind(adminctl.NewAuth()) // Public: login only. group.Group("/", func(profile *ghttp.RouterGroup) { - // Login-only endpoints: own profile / access codes / menu routes. + // 仅登录端点:个人资料 / 权限码 / 菜单路由。 profile.Middleware(middleware.AdminAuthOnly(tokens)) profile.Bind(adminctl.NewProfile()) }) group.Group("/", func(protected *ghttp.RouterGroup) { - // Permission-protected endpoints: RBAC management, logs, ... + // 受权限保护端点:RBAC 管理、日志等。 // 权限由后端按「方法+路径」自动匹配,无需前端传 X-Permission。 protected.Middleware(middleware.AdminAuth(tokens, adminauth.AdminAuth().PermissionForPath, adminauth.AdminAuth().HasPermission, func(ctx context.Context, id uint64, permission, method, path, ip, param string, duration, status int) { adminaudit.AdminAudit().Record(ctx, adminaudit.AuditEvent{AdminID: id, Permission: permission, Method: method, Path: path, IP: ip, Param: param, DurationMS: duration, StatusCode: status}) })) protected.Bind(adminctl.New()) + protected.Bind(housectl.New()) }) }) s.Run() diff --git a/internal/controller/admin/admin.go b/internal/controller/admin/admin.go index 3db16f4..fc5c762 100644 --- a/internal/controller/admin/admin.go +++ b/internal/controller/admin/admin.go @@ -3,12 +3,12 @@ package admin import ( "context" - adminv1 "service.xpcool.com/api/admin/v1/admin/admin" + adminv1 "service.xpcool.com/api/admin/admin/admin" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/admin/admin" + admin "service.xpcool.com/internal/service/admin/admin/admin" ) -// AdminList pages the administrators. +// AdminList 分页查询管理员。 func (c *Controller) AdminList(ctx context.Context, req *adminv1.AdminListReq) (res *adminv1.AdminListRes, err error) { items, total, err := admin.AdminManage().List(ctx, dto.PageQuery{Page: req.Page, Size: req.Size, Keyword: req.Keyword}) if err != nil { @@ -24,7 +24,7 @@ func (c *Controller) AdminList(ctx context.Context, req *adminv1.AdminListReq) ( return &adminv1.AdminListRes{List: list, Total: total}, nil } -// AdminCreate creates an administrator. +// AdminCreate 创建管理员。 func (c *Controller) AdminCreate(ctx context.Context, req *adminv1.AdminCreateReq) (res *adminv1.AdminCreateRes, err error) { id, err := admin.AdminManage().Create(ctx, dto.AdminCreateInput{ Username: req.Username, Password: req.Password, Nickname: req.Nickname, RoleIds: req.RoleIds, @@ -35,7 +35,7 @@ func (c *Controller) AdminCreate(ctx context.Context, req *adminv1.AdminCreateRe return &adminv1.AdminCreateRes{Id: id}, nil } -// AdminUpdate updates an administrator. +// AdminUpdate 更新管理员。 func (c *Controller) AdminUpdate(ctx context.Context, req *adminv1.AdminUpdateReq) (res *adminv1.AdminUpdateRes, err error) { if err = admin.AdminManage().Update(ctx, dto.AdminUpdateInput{Id: req.Id, Nickname: req.Nickname, Status: req.Status, RoleIds: req.RoleIds}); err != nil { return nil, err @@ -43,7 +43,7 @@ func (c *Controller) AdminUpdate(ctx context.Context, req *adminv1.AdminUpdateRe return &adminv1.AdminUpdateRes{}, nil } -// AdminResetPwd resets an administrator's password. +// AdminResetPwd 重置管理员密码。 func (c *Controller) AdminResetPwd(ctx context.Context, req *adminv1.AdminResetPwdReq) (res *adminv1.AdminResetPwdRes, err error) { if err = admin.AdminManage().ResetPassword(ctx, req.Id, req.Password); err != nil { return nil, err @@ -51,7 +51,7 @@ func (c *Controller) AdminResetPwd(ctx context.Context, req *adminv1.AdminResetP return &adminv1.AdminResetPwdRes{}, nil } -// AdminDelete deletes an administrator. +// AdminDelete 删除管理员。 func (c *Controller) AdminDelete(ctx context.Context, req *adminv1.AdminDeleteReq) (res *adminv1.AdminDeleteRes, err error) { if err = admin.AdminManage().Delete(ctx, req.Id); err != nil { return nil, err diff --git a/internal/controller/admin/auth.go b/internal/controller/admin/auth.go index 213a0a9..62d85c2 100644 --- a/internal/controller/admin/auth.go +++ b/internal/controller/admin/auth.go @@ -3,27 +3,41 @@ package admin import ( "context" - authv1 "service.xpcool.com/api/admin/v1/system/auth" + "github.com/gogf/gf/v2/net/ghttp" + + authv1 "service.xpcool.com/api/admin/admin/login" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/admin/system/auth" + auth "service.xpcool.com/internal/service/admin/admin/login" + loginlog "service.xpcool.com/internal/service/admin/system/login_log" ) -// AuthController exposes only the public login endpoint. +// AuthController 仅暴露公开的登录相关端点。 type AuthController struct{} -// NewAuth creates the public admin auth controller (login only). +// NewAuth 创建公开的管理端认证控制器(仅登录)。 func NewAuth() *AuthController { return &AuthController{} } -// Login authenticates an administrator and issues a token pair. +// Login 校验管理员身份并签发令牌对,成功与失败均写入登录日志。 func (c *AuthController) Login(ctx context.Context, req *authv1.LoginReq) (res *authv1.LoginRes, err error) { + // 从请求上下文提取来源信息用于登录审计 + r := ghttp.RequestFromCtx(ctx) + ip, ua := "", "" + if r != nil { + ip = r.GetClientIp() + ua = r.Header.Get("User-Agent") + } p, id, err := auth.AdminAuth().Login(ctx, dto.AdminLoginInput{Username: req.Username, Password: req.Password}) if err != nil { + // 登录失败也落库(含失败原因),便于排查异常登录;日志失败不回传 + _ = loginlog.AdminLoginLog().Record(ctx, loginlog.LoginEvent{Username: req.Username, IP: ip, UserAgent: ua, Status: 0, FailReason: err.Error()}) return nil, err } + // 登录成功落库;日志落库失败不影响登录结果 + _ = loginlog.AdminLoginLog().Record(ctx, loginlog.LoginEvent{Username: req.Username, IP: ip, UserAgent: ua, Status: 1}) return &authv1.LoginRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, AdminID: id}, nil } -// Refresh rotates the administrator token pair using a valid refresh token. +// Refresh 使用有效刷新令牌轮换管理员令牌对。 func (c *AuthController) Refresh(ctx context.Context, req *authv1.RefreshReq) (res *authv1.RefreshRes, err error) { p, id, err := auth.AdminAuth().Refresh(ctx, req.RefreshToken) if err != nil { @@ -32,8 +46,8 @@ func (c *AuthController) Refresh(ctx context.Context, req *authv1.RefreshReq) (r return &authv1.RefreshRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, AdminID: id}, nil } -// Logout ends the administrator session. Stateless JWT logout relies on the -// client discarding tokens; the endpoint always succeeds for an authenticated admin. +// Logout 结束管理员会话。无状态 JWT 登出依赖客户端 +// 丢弃令牌,该端点对任意已登录管理员始终成功。 func (c *AuthController) Logout(ctx context.Context, req *authv1.LogoutReq) (res *authv1.LogoutRes, err error) { return &authv1.LogoutRes{}, nil } diff --git a/internal/controller/admin/controller.go b/internal/controller/admin/controller.go index 011966a..39f817e 100644 --- a/internal/controller/admin/controller.go +++ b/internal/controller/admin/controller.go @@ -8,14 +8,14 @@ import ( "service.xpcool.com/internal/middleware" ) -// Controller implements the permission-protected /admin/v1 endpoints +// Controller 实现受权限保护的后台管理端点 // (RBAC management, logs, etc.). Bind behind AdminAuth (X-Permission). type Controller struct{} -// New creates the protected admin controller. +// New 创建受保护的后台管理控制器。 func New() *Controller { return &Controller{} } -// adminID returns the authenticated admin id stored by the auth middleware. +// adminID 返回认证中间件写入的管理员 id。 func adminID(ctx context.Context) uint64 { return g.RequestFromCtx(ctx).GetCtxVar(middleware.AdminIDKey).Uint64() } diff --git a/internal/controller/admin/log.go b/internal/controller/admin/log.go index 11a7835..467e520 100644 --- a/internal/controller/admin/log.go +++ b/internal/controller/admin/log.go @@ -3,11 +3,14 @@ package admin import ( "context" - logv1 "service.xpcool.com/api/admin/v1/base/log" - "service.xpcool.com/internal/service/admin/base/log" + logv1 "service.xpcool.com/api/admin/base/log" + systemlogv1 "service.xpcool.com/api/admin/system/log" + "service.xpcool.com/internal/model/dto" + log "service.xpcool.com/internal/service/admin/base/log" + adminlog "service.xpcool.com/internal/service/admin/system/log" ) -// LogFiles lists the server log files. +// LogFiles 列出服务器日志文件。 func (c *Controller) LogFiles(ctx context.Context, req *logv1.LogFilesReq) (res *logv1.LogFilesRes, err error) { dir, files, err := log.LogManage().Files(ctx) if err != nil { @@ -20,7 +23,7 @@ func (c *Controller) LogFiles(ctx context.Context, req *logv1.LogFilesReq) (res return &logv1.LogFilesRes{Dir: dir, Files: list}, nil } -// LogTail reads the tail of a log file with optional keyword filter. +// LogTail 读取日志文件尾部,支持关键字过滤。 func (c *Controller) LogTail(ctx context.Context, req *logv1.LogTailReq) (res *logv1.LogTailRes, err error) { lines, err := log.LogManage().Tail(ctx, req.File, req.Lines, req.Keyword) if err != nil { @@ -28,3 +31,20 @@ func (c *Controller) LogTail(ctx context.Context, req *logv1.LogTailReq) (res *l } return &logv1.LogTailRes{Lines: lines}, nil } + +// LogList 分页查询 admin 系统日志(操作审计记录)。 +func (c *Controller) LogList(ctx context.Context, req *systemlogv1.LogListReq) (res *systemlogv1.LogListRes, err error) { + items, total, err := adminlog.AdminAudit().List(ctx, dto.LogQuery{Page: req.Page, Size: req.Size, AdminID: req.AdminID, Permission: req.Keyword}) + if err != nil { + return nil, err + } + list := make([]*systemlogv1.LogItem, 0, len(items)) + for _, it := range items { + list = append(list, &systemlogv1.LogItem{ + Id: it.Id, AdminID: it.AdminID, Permission: it.Permission, Method: it.Method, + Path: it.Path, IP: it.IP, Param: it.Param, DurationMS: it.DurationMS, + StatusCode: it.StatusCode, CreatedAt: it.CreatedAt, + }) + } + return &systemlogv1.LogListRes{List: list, Total: total}, nil +} diff --git a/internal/controller/admin/login_log.go b/internal/controller/admin/login_log.go new file mode 100644 index 0000000..928a020 --- /dev/null +++ b/internal/controller/admin/login_log.go @@ -0,0 +1,27 @@ +package admin + +import ( + "context" + + loginlogv1 "service.xpcool.com/api/admin/system/login_log" + "service.xpcool.com/internal/model/dto" + loginlog "service.xpcool.com/internal/service/admin/system/login_log" +) + +// LoginLogList 分页查询管理员登录日志。 +func (c *Controller) LoginLogList(ctx context.Context, req *loginlogv1.LoginLogListReq) (res *loginlogv1.LoginLogListRes, err error) { + items, total, err := loginlog.AdminLoginLog().List(ctx, dto.LoginLogQuery{ + Page: req.Page, Size: req.Size, Username: req.Username, Status: req.Status, + }) + if err != nil { + return nil, err + } + list := make([]*loginlogv1.LoginLogItem, 0, len(items)) + for _, it := range items { + list = append(list, &loginlogv1.LoginLogItem{ + Id: it.Id, Username: it.Username, IP: it.IP, UserAgent: it.UserAgent, + Status: it.Status, FailReason: it.FailReason, CreatedAt: it.CreatedAt, + }) + } + return &loginlogv1.LoginLogListRes{List: list, Total: total}, nil +} diff --git a/internal/controller/admin/menu.go b/internal/controller/admin/menu.go index 6d97b0b..65c65fb 100644 --- a/internal/controller/admin/menu.go +++ b/internal/controller/admin/menu.go @@ -3,12 +3,12 @@ package admin import ( "context" - menuv1 "service.xpcool.com/api/admin/v1/system/menu_manage" + menuv1 "service.xpcool.com/api/admin/system/menu_manage" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/admin/system/menu_manage" + menu_manage "service.xpcool.com/internal/service/admin/system/menu_manage" ) -// MenuTree returns the full menu tree (menus + button permissions). +// MenuTree 返回完整菜单树(菜单 + 按钮权限)。 func (c *Controller) MenuTree(ctx context.Context, req *menuv1.MenuTreeReq) (res *menuv1.MenuTreeRes, err error) { tree, err := menu_manage.MenuManage().Tree(ctx) if err != nil { @@ -21,7 +21,7 @@ func (c *Controller) MenuTree(ctx context.Context, req *menuv1.MenuTreeReq) (res return &menuv1.MenuTreeRes{Tree: out}, nil } -// MenuCreate creates a menu or button node. +// MenuCreate 创建菜单或按钮节点。 func (c *Controller) MenuCreate(ctx context.Context, req *menuv1.MenuCreateReq) (res *menuv1.MenuCreateRes, err error) { id, err := menu_manage.MenuManage().Create(ctx, dto.MenuCreateInput{ ParentId: req.ParentId, Name: req.Name, Icon: req.Icon, Type: req.Type, Path: req.Path, @@ -33,7 +33,7 @@ func (c *Controller) MenuCreate(ctx context.Context, req *menuv1.MenuCreateReq) return &menuv1.MenuCreateRes{Id: id}, nil } -// MenuUpdate updates a menu or button node. +// MenuUpdate 更新菜单或按钮节点。 func (c *Controller) MenuUpdate(ctx context.Context, req *menuv1.MenuUpdateReq) (res *menuv1.MenuUpdateRes, err error) { if err = menu_manage.MenuManage().Update(ctx, dto.MenuUpdateInput{ Id: req.Id, ParentId: req.ParentId, Name: req.Name, Icon: req.Icon, Type: req.Type, Path: req.Path, @@ -44,7 +44,7 @@ func (c *Controller) MenuUpdate(ctx context.Context, req *menuv1.MenuUpdateReq) return &menuv1.MenuUpdateRes{}, nil } -// MenuDelete deletes a menu node. +// MenuDelete 删除菜单节点。 func (c *Controller) MenuDelete(ctx context.Context, req *menuv1.MenuDeleteReq) (res *menuv1.MenuDeleteRes, err error) { if err = menu_manage.MenuManage().Delete(ctx, req.Id); err != nil { return nil, err diff --git a/internal/controller/admin/profile.go b/internal/controller/admin/profile.go index e660dfa..410a01a 100644 --- a/internal/controller/admin/profile.go +++ b/internal/controller/admin/profile.go @@ -3,21 +3,21 @@ package admin import ( "context" - authv1 "service.xpcool.com/api/admin/v1/system/auth" - menuv1 "service.xpcool.com/api/admin/v1/system/menu" + authv1 "service.xpcool.com/api/admin/admin/login" + menuv1 "service.xpcool.com/api/admin/system/menu" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/admin/system/auth" - "service.xpcool.com/internal/service/admin/system/menu" + auth "service.xpcool.com/internal/service/admin/admin/login" + menu "service.xpcool.com/internal/service/admin/system/menu" ) -// ProfileController exposes the logged-in admin's own profile endpoints +// ProfileController 暴露当前登录管理员的资料端点 // (login-only, no X-Permission required). Bind behind AdminAuthOnly. type ProfileController struct{} -// NewProfile creates the profile controller. +// NewProfile 创建资料控制器。 func NewProfile() *ProfileController { return &ProfileController{} } -// Info returns the current administrator profile. +// Info 返回当前管理员资料。 func (c *ProfileController) Info(ctx context.Context, req *authv1.InfoReq) (res *authv1.InfoRes, err error) { info, err := auth.AdminAuth().Info(ctx, adminID(ctx)) if err != nil { @@ -26,7 +26,7 @@ func (c *ProfileController) Info(ctx context.Context, req *authv1.InfoReq) (res return &authv1.InfoRes{AdminID: info.AdminID, Username: info.Username, Nickname: info.Nickname, Roles: info.Roles}, nil } -// Codes returns the button-level permission codes of the current administrator. +// Codes 返回当前管理员的按钮级权限码。 func (c *ProfileController) Codes(ctx context.Context, req *authv1.CodesReq) (res *authv1.CodesRes, err error) { codes, err := auth.AdminAuth().Codes(ctx, adminID(ctx)) if err != nil { @@ -35,7 +35,7 @@ func (c *ProfileController) Codes(ctx context.Context, req *authv1.CodesReq) (re return &authv1.CodesRes{Codes: codes}, nil } -// Routes returns the current administrator's visible menu tree as vben routes. +// Routes 返回当前管理员的可见菜单树(vben 路由)。 func (c *ProfileController) Routes(ctx context.Context, req *menuv1.MenuRoutesReq) (res *menuv1.MenuRoutesRes, err error) { routes, err := menu.AdminMenu().Routes(ctx, adminID(ctx)) if err != nil { @@ -48,7 +48,7 @@ func (c *ProfileController) Routes(ctx context.Context, req *menuv1.MenuRoutesRe return &menuv1.MenuRoutesRes{Routes: out}, nil } -// toV1Route converts a dto route tree into the v1 API shape. +// toV1Route 将 dto 路由树转换为 API 层结构。 func toV1Route(r *dto.RouteItem) *menuv1.RouteItem { item := &menuv1.RouteItem{ Name: r.Name, diff --git a/internal/controller/admin/role.go b/internal/controller/admin/role.go index 4327970..6a78f6c 100644 --- a/internal/controller/admin/role.go +++ b/internal/controller/admin/role.go @@ -3,12 +3,12 @@ package admin import ( "context" - rolev1 "service.xpcool.com/api/admin/v1/system/role" + rolev1 "service.xpcool.com/api/admin/system/role" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/admin/system/role" + role "service.xpcool.com/internal/service/admin/system/role" ) -// RoleList pages the roles. +// RoleList 分页查询角色。 func (c *Controller) RoleList(ctx context.Context, req *rolev1.RoleListReq) (res *rolev1.RoleListRes, err error) { items, total, err := role.RoleManage().List(ctx, dto.PageQuery{Page: req.Page, Size: req.Size, Keyword: req.Keyword}) if err != nil { @@ -23,7 +23,7 @@ func (c *Controller) RoleList(ctx context.Context, req *rolev1.RoleListReq) (res return &rolev1.RoleListRes{List: list, Total: total}, nil } -// RoleCreate creates a role. +// RoleCreate 创建角色。 func (c *Controller) RoleCreate(ctx context.Context, req *rolev1.RoleCreateReq) (res *rolev1.RoleCreateRes, err error) { id, err := role.RoleManage().Create(ctx, dto.RoleCreateInput{Code: req.Code, Name: req.Name, Status: req.Status, MenuIds: req.MenuIds}) if err != nil { @@ -32,7 +32,7 @@ func (c *Controller) RoleCreate(ctx context.Context, req *rolev1.RoleCreateReq) return &rolev1.RoleCreateRes{Id: id}, nil } -// RoleUpdate updates a role. +// RoleUpdate 更新角色。 func (c *Controller) RoleUpdate(ctx context.Context, req *rolev1.RoleUpdateReq) (res *rolev1.RoleUpdateRes, err error) { if err = role.RoleManage().Update(ctx, dto.RoleUpdateInput{Id: req.Id, Name: req.Name, Status: req.Status, MenuIds: req.MenuIds}); err != nil { return nil, err @@ -40,7 +40,7 @@ func (c *Controller) RoleUpdate(ctx context.Context, req *rolev1.RoleUpdateReq) return &rolev1.RoleUpdateRes{}, nil } -// RoleDelete deletes a role. +// RoleDelete 删除角色。 func (c *Controller) RoleDelete(ctx context.Context, req *rolev1.RoleDeleteReq) (res *rolev1.RoleDeleteRes, err error) { if err = role.RoleManage().Delete(ctx, req.Id); err != nil { return nil, err diff --git a/internal/controller/hello/hello.go b/internal/controller/hello/hello.go deleted file mode 100644 index f72082f..0000000 --- a/internal/controller/hello/hello.go +++ /dev/null @@ -1,5 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package hello diff --git a/internal/controller/hello/hello_new.go b/internal/controller/hello/hello_new.go deleted file mode 100644 index b512e14..0000000 --- a/internal/controller/hello/hello_new.go +++ /dev/null @@ -1,15 +0,0 @@ -// ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package hello - -import ( - "service.xpcool.com/api/hello" -) - -type ControllerV1 struct{} - -func NewV1() hello.IHelloV1 { - return &ControllerV1{} -} diff --git a/internal/controller/hello/hello_v1_hello.go b/internal/controller/hello/hello_v1_hello.go deleted file mode 100644 index d8e045f..0000000 --- a/internal/controller/hello/hello_v1_hello.go +++ /dev/null @@ -1,13 +0,0 @@ -package hello - -import ( - "context" - "github.com/gogf/gf/v2/frame/g" - - "service.xpcool.com/api/hello/v1" -) - -func (c *ControllerV1) Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) { - g.RequestFromCtx(ctx).Response.Writeln("Hello World!") - return -} diff --git a/internal/controller/house/community.go b/internal/controller/house/community.go new file mode 100644 index 0000000..28ea867 --- /dev/null +++ b/internal/controller/house/community.go @@ -0,0 +1,64 @@ +package house + +import ( + "context" + + communityv1 "service.xpcool.com/api/house/community" + "service.xpcool.com/internal/model/dto" + community "service.xpcool.com/internal/service/house/community" +) + +// CommunityList 分页查询小区。 +func (c *Controller) CommunityList(ctx context.Context, req *communityv1.CommunityListReq) (res *communityv1.CommunityListRes, err error) { + list, total, err := community.Community().List(ctx, req.Page, req.Size, req.Keyword, req.Region) + if err != nil { + return nil, err + } + out := make([]*communityv1.CommunityItem, 0, len(list)) + for i := range list { + e := &list[i] + out = append(out, &communityv1.CommunityItem{ + Id: e.Id, Name: e.Name, Region: e.Region, BusinessDistrict: e.BusinessDistrict, + Address: e.Address, Lng: e.Lng, Lat: e.Lat, BuildYear: e.BuildYear, + Households: e.Households, PlotRatio: e.PlotRatio, GreenRate: e.GreenRate, + PropertyCompany: e.PropertyCompany, PropertyFee: e.PropertyFee, Developer: e.Developer, Source: e.Source, + }) + } + return &communityv1.CommunityListRes{List: out, Total: total}, nil +} + +// CommunityCreate 新增小区。 +func (c *Controller) CommunityCreate(ctx context.Context, req *communityv1.CommunityCreateReq) (res *communityv1.CommunityCreateRes, err error) { + id, err := community.Community().Create(ctx, dto.HouseCommunityInput{ + Name: req.Name, Region: req.Region, BusinessDistrict: req.BusinessDistrict, + Address: req.Address, Lng: req.Lng, Lat: req.Lat, BuildYear: req.BuildYear, + Households: req.Households, PlotRatio: req.PlotRatio, GreenRate: req.GreenRate, + PropertyCompany: req.PropertyCompany, PropertyFee: req.PropertyFee, Developer: req.Developer, Source: req.Source, + }) + if err != nil { + return nil, err + } + return &communityv1.CommunityCreateRes{Id: id}, nil +} + +// CommunityUpdate 更新小区。 +func (c *Controller) CommunityUpdate(ctx context.Context, req *communityv1.CommunityUpdateReq) (res *communityv1.CommunityUpdateRes, err error) { + err = community.Community().Update(ctx, dto.HouseCommunityInput{ + Id: req.Id, Name: req.Name, Region: req.Region, BusinessDistrict: req.BusinessDistrict, + Address: req.Address, Lng: req.Lng, Lat: req.Lat, BuildYear: req.BuildYear, + Households: req.Households, PlotRatio: req.PlotRatio, GreenRate: req.GreenRate, + PropertyCompany: req.PropertyCompany, PropertyFee: req.PropertyFee, Developer: req.Developer, + }) + if err != nil { + return nil, err + } + return &communityv1.CommunityUpdateRes{}, nil +} + +// CommunityDelete 删除小区。 +func (c *Controller) CommunityDelete(ctx context.Context, req *communityv1.CommunityDeleteReq) (res *communityv1.CommunityDeleteRes, err error) { + if err = community.Community().Delete(ctx, req.Id); err != nil { + return nil, err + } + return &communityv1.CommunityDeleteRes{}, nil +} diff --git a/internal/controller/house/controller.go b/internal/controller/house/controller.go new file mode 100644 index 0000000..68807cf --- /dev/null +++ b/internal/controller/house/controller.go @@ -0,0 +1,8 @@ +// Package house 实现看房模块的管理端点,绑定在 admin 受权限保护分组下。 +package house + +// Controller 实现看房模块的所有端点。 +type Controller struct{} + +// New 创建看房模块控制器。 +func New() *Controller { return &Controller{} } diff --git a/internal/controller/house/dashboard.go b/internal/controller/house/dashboard.go new file mode 100644 index 0000000..d4529dc --- /dev/null +++ b/internal/controller/house/dashboard.go @@ -0,0 +1,67 @@ +package house + +import ( + "context" + + dashboardv1 "service.xpcool.com/api/house/dashboard" + dashboard "service.xpcool.com/internal/service/house/dashboard" +) + +// DashboardOverview 看板统计概览。 +func (c *Controller) DashboardOverview(ctx context.Context, req *dashboardv1.OverviewReq) (res *dashboardv1.OverviewRes, err error) { + o, err := dashboard.Dashboard().Overview(ctx, req.Region) + if err != nil { + return nil, err + } + return &dashboardv1.OverviewRes{ + CommunityCount: o.CommunityCount, ListingCount: o.ListingCount, BargainCount: o.BargainCount, + LowConfidence: o.LowConfidence, AvgUnitPrice: o.AvgUnitPrice, AvgTotalPrice: o.AvgTotalPrice, AvgListDays: o.AvgListDays, + }, nil +} + +// DashboardMapPoints 地图点位聚合。 +func (c *Controller) DashboardMapPoints(ctx context.Context, req *dashboardv1.MapPointsReq) (res *dashboardv1.MapPointsRes, err error) { + pts, err := dashboard.Dashboard().MapPoints(ctx, req.Region, req.PriceMin, req.PriceMax, req.Status) + if err != nil { + return nil, err + } + out := make([]*dashboardv1.MapPoint, 0, len(pts)) + for i := range pts { + p := &pts[i] + out = append(out, &dashboardv1.MapPoint{ + CommunityId: p.CommunityId, Name: p.Name, Region: p.Region, Lng: p.Lng, Lat: p.Lat, + AvgUnitPrice: p.AvgUnitPrice, ListingCount: p.ListingCount, BargainCount: p.BargainCount, + }) + } + return &dashboardv1.MapPointsRes{Points: out}, nil +} + +// DashboardPriceTrend 价格趋势聚合。 +func (c *Controller) DashboardPriceTrend(ctx context.Context, req *dashboardv1.PriceTrendReq) (res *dashboardv1.PriceTrendRes, err error) { + trend, err := dashboard.Dashboard().PriceTrend(ctx, req.CommunityId, req.Region, req.Limit) + if err != nil { + return nil, err + } + out := make([]*dashboardv1.TrendPoint, 0, len(trend)) + for i := range trend { + t := &trend[i] + out = append(out, &dashboardv1.TrendPoint{Date: t.Date, AvgListPrice: t.AvgListPrice, AvgDealPrice: t.AvgDealPrice}) + } + return &dashboardv1.PriceTrendRes{Trend: out}, nil +} + +// DashboardAggregateRegion 区域聚合。 +func (c *Controller) DashboardAggregateRegion(ctx context.Context, req *dashboardv1.AggregateRegionReq) (res *dashboardv1.AggregateRegionRes, err error) { + list, err := dashboard.Dashboard().AggregateRegion(ctx) + if err != nil { + return nil, err + } + out := make([]*dashboardv1.RegionAgg, 0, len(list)) + for i := range list { + r := &list[i] + out = append(out, &dashboardv1.RegionAgg{ + Region: r.Region, AvgUnitPrice: r.AvgUnitPrice, ListingCount: r.ListingCount, BargainCount: r.BargainCount, + }) + } + return &dashboardv1.AggregateRegionRes{List: out}, nil +} diff --git a/internal/controller/house/listing.go b/internal/controller/house/listing.go new file mode 100644 index 0000000..3974256 --- /dev/null +++ b/internal/controller/house/listing.go @@ -0,0 +1,85 @@ +package house + +import ( + "context" + + listingv1 "service.xpcool.com/api/house/listing" + "service.xpcool.com/internal/model/dto" + listing "service.xpcool.com/internal/service/house/listing" +) + +// ListingList 分页查询房源(带多维筛选)。 +func (c *Controller) ListingList(ctx context.Context, req *listingv1.ListingListReq) (res *listingv1.ListingListRes, err error) { + list, total, err := listing.Listing().List(ctx, dto.HouseListingFilter{ + Page: req.Page, Size: req.Size, CommunityId: req.CommunityId, Keyword: req.Keyword, + Layout: req.Layout, Region: req.Region, Source: req.Source, + PriceMin: req.PriceMin, PriceMax: req.PriceMax, AreaMin: req.AreaMin, AreaMax: req.AreaMax, + Status: req.Status, IsBargain: req.IsBargain, Confidence: req.Confidence, + }) + if err != nil { + return nil, err + } + out := make([]*listingv1.ListingItem, 0, len(list)) + for i := range list { + v := &list[i] + out = append(out, &listingv1.ListingItem{ + Id: v.Id, CommunityId: v.CommunityId, CommunityName: v.CommunityName, BuildingId: v.BuildingId, + HouseNo: v.HouseNo, Layout: v.Layout, Area: v.Area, UsableArea: v.UsableArea, + Orientation: v.Orientation, Floor: v.Floor, TotalFloors: v.TotalFloors, Decoration: v.Decoration, + TotalPrice: v.TotalPrice, UnitPrice: v.UnitPrice, ListPrice: v.ListPrice, + Source: v.Source, SourceHouseId: v.SourceHouseId, SourceUrl: v.SourceUrl, + MatchGroupId: v.MatchGroupId, OnMarketDays: v.OnMarketDays, PriceChangeCount: v.PriceChangeCount, + Status: v.Status, Confidence: v.Confidence, IsBargain: v.IsBargain, + ListingTime: v.ListingTime, CreatedAt: v.CreatedAt, + }) + } + return &listingv1.ListingListRes{List: out, Total: total}, nil +} + +// ListingCreate 新增房源。 +func (c *Controller) ListingCreate(ctx context.Context, req *listingv1.ListingCreateReq) (res *listingv1.ListingCreateRes, err error) { + id, err := listing.Listing().Create(ctx, dto.HouseListingInput{ + CommunityId: req.CommunityId, BuildingId: req.BuildingId, HouseNo: req.HouseNo, Layout: req.Layout, + Area: req.Area, UsableArea: req.UsableArea, Orientation: req.Orientation, Floor: req.Floor, + TotalFloors: req.TotalFloors, Decoration: req.Decoration, TotalPrice: req.TotalPrice, UnitPrice: req.UnitPrice, + ListPrice: req.ListPrice, Source: req.Source, SourceHouseId: req.SourceHouseId, SourceUrl: req.SourceUrl, + MatchGroupId: req.MatchGroupId, OnMarketDays: req.OnMarketDays, PriceChangeCount: req.PriceChangeCount, + Status: req.Status, Confidence: req.Confidence, IsBargain: req.IsBargain, + }) + if err != nil { + return nil, err + } + return &listingv1.ListingCreateRes{Id: id}, nil +} + +// ListingUpdate 更新房源。 +func (c *Controller) ListingUpdate(ctx context.Context, req *listingv1.ListingUpdateReq) (res *listingv1.ListingUpdateRes, err error) { + err = listing.Listing().Update(ctx, dto.HouseListingInput{ + Id: req.Id, CommunityId: req.CommunityId, BuildingId: req.BuildingId, HouseNo: req.HouseNo, + Layout: req.Layout, Area: req.Area, UsableArea: req.UsableArea, Orientation: req.Orientation, + Floor: req.Floor, TotalFloors: req.TotalFloors, Decoration: req.Decoration, TotalPrice: req.TotalPrice, + UnitPrice: req.UnitPrice, ListPrice: req.ListPrice, MatchGroupId: req.MatchGroupId, + Status: req.Status, Confidence: req.Confidence, IsBargain: req.IsBargain, + }) + if err != nil { + return nil, err + } + return &listingv1.ListingUpdateRes{}, nil +} + +// ListingDelete 删除房源。 +func (c *Controller) ListingDelete(ctx context.Context, req *listingv1.ListingDeleteReq) (res *listingv1.ListingDeleteRes, err error) { + if err = listing.Listing().Delete(ctx, req.Id); err != nil { + return nil, err + } + return &listingv1.ListingDeleteRes{}, nil +} + +// ListingBatchMark 批量标记房源。 +func (c *Controller) ListingBatchMark(ctx context.Context, req *listingv1.ListingBatchMarkReq) (res *listingv1.ListingBatchMarkRes, err error) { + affected, err := listing.Listing().BatchMark(ctx, req.Ids, req.Field, req.Value) + if err != nil { + return nil, err + } + return &listingv1.ListingBatchMarkRes{Affected: affected}, nil +} diff --git a/internal/controller/open/controller.go b/internal/controller/open/controller.go index 2c69a0d..f63c522 100644 --- a/internal/controller/open/controller.go +++ b/internal/controller/open/controller.go @@ -1,11 +1,11 @@ -// Package open implements the public open API (/api/open/v1). -// These controllers are thin adapters over the common/tools Go packages and -// require no authentication. Each sub-feature lives in its own file here, -// mirroring api/open/v1/tools//index.go. +// Package open 实现公开的开放接口(/api/open)。 +// 这些控制器是 common/tools Go 包的薄适配层, +// 无需认证。每个子功能位于本目录下的独立文件, +// 与 api/open/tools//index.go 一一对应。 package open -// Controller implements the /api/open/v1 endpoints. +// Controller 实现 /api/open 端点。 type Controller struct{} -// New creates an open API controller. +// New 创建开放接口控制器。 func New() *Controller { return &Controller{} } diff --git a/internal/controller/open/ip.go b/internal/controller/open/ip.go index c335ed2..809671f 100644 --- a/internal/controller/open/ip.go +++ b/internal/controller/open/ip.go @@ -5,11 +5,11 @@ import ( "github.com/gogf/gf/v2/frame/g" - ipapi "service.xpcool.com/api/open/v1/tools/ip" + ipapi "service.xpcool.com/api/open/tools/ip" "service.xpcool.com/common/tools/ip" ) -// IP returns the caller's IP and whether it is an internal address. +// IP 返回调用方 IP 及是否为内网地址。 func (c *Controller) IP(ctx context.Context, req *ipapi.IPReq) (res *ipapi.IPRes, err error) { clientIP := g.RequestFromCtx(ctx).GetClientIp() return &ipapi.IPRes{IP: clientIP, Internal: ip.IsInternal(clientIP)}, nil diff --git a/internal/controller/open/md5.go b/internal/controller/open/md5.go index 9bb7169..e20ad80 100644 --- a/internal/controller/open/md5.go +++ b/internal/controller/open/md5.go @@ -3,11 +3,11 @@ package open import ( "context" - md5api "service.xpcool.com/api/open/v1/tools/md5" + md5api "service.xpcool.com/api/open/tools/md5" "service.xpcool.com/common/tools/md5" ) -// MD5 computes the MD5 digest of the given text. +// MD5 计算给定文本的 MD5 摘要。 func (c *Controller) MD5(ctx context.Context, req *md5api.MD5Req) (res *md5api.MD5Res, err error) { return &md5api.MD5Res{MD5: md5.Md5Hex(req.Text)}, nil } diff --git a/internal/controller/open/random.go b/internal/controller/open/random.go index bec7b8b..d537ce3 100644 --- a/internal/controller/open/random.go +++ b/internal/controller/open/random.go @@ -3,11 +3,11 @@ package open import ( "context" - randomapi "service.xpcool.com/api/open/v1/tools/random" + randomapi "service.xpcool.com/api/open/tools/random" "service.xpcool.com/common/tools/random" ) -// Random generates a random string of the requested type and length. +// Random 生成指定类型和长度的随机字符串。 func (c *Controller) Random(ctx context.Context, req *randomapi.RandomReq) (res *randomapi.RandomRes, err error) { var value string switch req.Type { diff --git a/internal/controller/open/time.go b/internal/controller/open/time.go index a2b24c0..eec7ab0 100644 --- a/internal/controller/open/time.go +++ b/internal/controller/open/time.go @@ -3,11 +3,11 @@ package open import ( "context" - timeapi "service.xpcool.com/api/open/v1/tools/time" + timeapi "service.xpcool.com/api/open/tools/time" "service.xpcool.com/common/tools/timex" ) -// Time returns the current server timestamp and formatted time. +// Time 返回当前服务器时间戳与格式化时间。 func (c *Controller) Time(ctx context.Context, req *timeapi.TimeReq) (res *timeapi.TimeRes, err error) { now := timex.Now() return &timeapi.TimeRes{ diff --git a/internal/controller/open/uuid.go b/internal/controller/open/uuid.go index 67d955e..c57f339 100644 --- a/internal/controller/open/uuid.go +++ b/internal/controller/open/uuid.go @@ -3,11 +3,11 @@ package open import ( "context" - uuidapi "service.xpcool.com/api/open/v1/tools/uuid" + uuidapi "service.xpcool.com/api/open/tools/uuid" "service.xpcool.com/common/tools/uuid" ) -// UUID generates a unique ID (32-char by default, 8-char when short=true). +// UUID 生成唯一 ID(默认 32 位,short=true 时为 8 位)。 func (c *Controller) UUID(ctx context.Context, req *uuidapi.UUIDReq) (res *uuidapi.UUIDRes, err error) { if req.Short { return &uuidapi.UUIDRes{UUID: uuid.Short(8)}, nil diff --git a/internal/controller/user/auth.go b/internal/controller/user/auth.go index 6e059fa..e867c47 100644 --- a/internal/controller/user/auth.go +++ b/internal/controller/user/auth.go @@ -2,9 +2,9 @@ package user import ( "context" - authv1 "service.xpcool.com/api/user/v1/auth" + authv1 "service.xpcool.com/api/user/auth" "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/service/user/auth" + auth "service.xpcool.com/internal/service/user/auth" ) type Controller struct{} diff --git a/internal/dao/admin_login_log.go b/internal/dao/admin_login_log.go new file mode 100644 index 0000000..a589b75 --- /dev/null +++ b/internal/dao/admin_login_log.go @@ -0,0 +1,22 @@ +// ================================================================================= +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminLoginLogDao 是表 admin_login_log 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 +type adminLoginLogDao struct { + *internal.AdminLoginLogDao +} + +var ( + // AdminLoginLog 是表 admin_login_log 的全局可访问操作对象。 + AdminLoginLog = adminLoginLogDao{internal.NewAdminLoginLogDao()} +) + +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_menu.go b/internal/dao/admin_menu.go index 425ba5c..6bce8e8 100644 --- a/internal/dao/admin_menu.go +++ b/internal/dao/admin_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminMenuDao is the data access object for the table admin_menu. -// You can define custom methods on it to extend its functionality as needed. +// adminMenuDao 是表 admin_menu 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminMenuDao struct { *internal.AdminMenuDao } var ( - // AdminMenu is a globally accessible object for table admin_menu operations. + // AdminMenu 是表 admin_menu 的全局可访问操作对象。 AdminMenu = adminMenuDao{internal.NewAdminMenuDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_operation_log.go b/internal/dao/admin_operation_log.go index 060adad..097e614 100644 --- a/internal/dao/admin_operation_log.go +++ b/internal/dao/admin_operation_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminOperationLogDao is the data access object for the table admin_operation_log. -// You can define custom methods on it to extend its functionality as needed. +// adminOperationLogDao 是表 admin_operation_log 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminOperationLogDao struct { *internal.AdminOperationLogDao } var ( - // AdminOperationLog is a globally accessible object for table admin_operation_log operations. + // AdminOperationLog 是表 admin_operation_log 的全局可访问操作对象。 AdminOperationLog = adminOperationLogDao{internal.NewAdminOperationLogDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_role.go b/internal/dao/admin_role.go index 440b6ff..7fb7989 100644 --- a/internal/dao/admin_role.go +++ b/internal/dao/admin_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminRoleDao is the data access object for the table admin_role. -// You can define custom methods on it to extend its functionality as needed. +// adminRoleDao 是表 admin_role 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminRoleDao struct { *internal.AdminRoleDao } var ( - // AdminRole is a globally accessible object for table admin_role operations. + // AdminRole 是表 admin_role 的全局可访问操作对象。 AdminRole = adminRoleDao{internal.NewAdminRoleDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_role_menu.go b/internal/dao/admin_role_menu.go index 0474296..80b5013 100644 --- a/internal/dao/admin_role_menu.go +++ b/internal/dao/admin_role_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminRoleMenuDao is the data access object for the table admin_role_menu. -// You can define custom methods on it to extend its functionality as needed. +// adminRoleMenuDao 是表 admin_role_menu 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminRoleMenuDao struct { *internal.AdminRoleMenuDao } var ( - // AdminRoleMenu is a globally accessible object for table admin_role_menu operations. + // AdminRoleMenu 是表 admin_role_menu 的全局可访问操作对象。 AdminRoleMenu = adminRoleMenuDao{internal.NewAdminRoleMenuDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_user.go b/internal/dao/admin_user.go index 7b12f23..64df558 100644 --- a/internal/dao/admin_user.go +++ b/internal/dao/admin_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminUserDao is the data access object for the table admin_user. -// You can define custom methods on it to extend its functionality as needed. +// adminUserDao 是表 admin_user 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminUserDao struct { *internal.AdminUserDao } var ( - // AdminUser is a globally accessible object for table admin_user operations. + // AdminUser 是表 admin_user 的全局可访问操作对象。 AdminUser = adminUserDao{internal.NewAdminUserDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/admin_user_role.go b/internal/dao/admin_user_role.go index 16c37f9..7fc8803 100644 --- a/internal/dao/admin_user_role.go +++ b/internal/dao/admin_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// adminUserRoleDao is the data access object for the table admin_user_role. -// You can define custom methods on it to extend its functionality as needed. +// adminUserRoleDao 是表 admin_user_role 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type adminUserRoleDao struct { *internal.AdminUserRoleDao } var ( - // AdminUserRole is a globally accessible object for table admin_user_role operations. + // AdminUserRole 是表 admin_user_role 的全局可访问操作对象。 AdminUserRole = adminUserRoleDao{internal.NewAdminUserRoleDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/auth_refresh_session.go b/internal/dao/auth_refresh_session.go index b895353..b6f57f8 100644 --- a/internal/dao/auth_refresh_session.go +++ b/internal/dao/auth_refresh_session.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// authRefreshSessionDao is the data access object for the table auth_refresh_session. -// You can define custom methods on it to extend its functionality as needed. +// authRefreshSessionDao 是表 auth_refresh_session 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type authRefreshSessionDao struct { *internal.AuthRefreshSessionDao } var ( - // AuthRefreshSession is a globally accessible object for table auth_refresh_session operations. + // AuthRefreshSession 是表 auth_refresh_session 的全局可访问操作对象。 AuthRefreshSession = authRefreshSessionDao{internal.NewAuthRefreshSessionDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/content.go b/internal/dao/content.go index 83d004b..e67bf4b 100644 --- a/internal/dao/content.go +++ b/internal/dao/content.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// contentDao is the data access object for the table content. -// You can define custom methods on it to extend its functionality as needed. +// contentDao 是表 content 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type contentDao struct { *internal.ContentDao } var ( - // Content is a globally accessible object for table content operations. + // Content 是表 content 的全局可访问操作对象。 Content = contentDao{internal.NewContentDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/house_building.go b/internal/dao/house_building.go new file mode 100644 index 0000000..7f66e1f --- /dev/null +++ b/internal/dao/house_building.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseBuildingDao is the data access object for the table house_building. +// You can define custom methods on it to extend its functionality as needed. +type houseBuildingDao struct { + *internal.HouseBuildingDao +} + +var ( + // HouseBuilding is a globally accessible object for table house_building operations. + HouseBuilding = houseBuildingDao{internal.NewHouseBuildingDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_community.go b/internal/dao/house_community.go new file mode 100644 index 0000000..6115d9b --- /dev/null +++ b/internal/dao/house_community.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseCommunityDao is the data access object for the table house_community. +// You can define custom methods on it to extend its functionality as needed. +type houseCommunityDao struct { + *internal.HouseCommunityDao +} + +var ( + // HouseCommunity is a globally accessible object for table house_community operations. + HouseCommunity = houseCommunityDao{internal.NewHouseCommunityDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_community_facility.go b/internal/dao/house_community_facility.go new file mode 100644 index 0000000..6f8114f --- /dev/null +++ b/internal/dao/house_community_facility.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseCommunityFacilityDao is the data access object for the table house_community_facility. +// You can define custom methods on it to extend its functionality as needed. +type houseCommunityFacilityDao struct { + *internal.HouseCommunityFacilityDao +} + +var ( + // HouseCommunityFacility is a globally accessible object for table house_community_facility operations. + HouseCommunityFacility = houseCommunityFacilityDao{internal.NewHouseCommunityFacilityDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_facility.go b/internal/dao/house_facility.go new file mode 100644 index 0000000..7eef491 --- /dev/null +++ b/internal/dao/house_facility.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseFacilityDao is the data access object for the table house_facility. +// You can define custom methods on it to extend its functionality as needed. +type houseFacilityDao struct { + *internal.HouseFacilityDao +} + +var ( + // HouseFacility is a globally accessible object for table house_facility operations. + HouseFacility = houseFacilityDao{internal.NewHouseFacilityDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_listing.go b/internal/dao/house_listing.go new file mode 100644 index 0000000..56ee51b --- /dev/null +++ b/internal/dao/house_listing.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseListingDao is the data access object for the table house_listing. +// You can define custom methods on it to extend its functionality as needed. +type houseListingDao struct { + *internal.HouseListingDao +} + +var ( + // HouseListing is a globally accessible object for table house_listing operations. + HouseListing = houseListingDao{internal.NewHouseListingDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_preference.go b/internal/dao/house_preference.go new file mode 100644 index 0000000..ea9fd60 --- /dev/null +++ b/internal/dao/house_preference.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// housePreferenceDao is the data access object for the table house_preference. +// You can define custom methods on it to extend its functionality as needed. +type housePreferenceDao struct { + *internal.HousePreferenceDao +} + +var ( + // HousePreference is a globally accessible object for table house_preference operations. + HousePreference = housePreferenceDao{internal.NewHousePreferenceDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_price_snapshot.go b/internal/dao/house_price_snapshot.go new file mode 100644 index 0000000..7c23b5e --- /dev/null +++ b/internal/dao/house_price_snapshot.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// housePriceSnapshotDao is the data access object for the table house_price_snapshot. +// You can define custom methods on it to extend its functionality as needed. +type housePriceSnapshotDao struct { + *internal.HousePriceSnapshotDao +} + +var ( + // HousePriceSnapshot is a globally accessible object for table house_price_snapshot operations. + HousePriceSnapshot = housePriceSnapshotDao{internal.NewHousePriceSnapshotDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_school_district.go b/internal/dao/house_school_district.go new file mode 100644 index 0000000..90da793 --- /dev/null +++ b/internal/dao/house_school_district.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseSchoolDistrictDao is the data access object for the table house_school_district. +// You can define custom methods on it to extend its functionality as needed. +type houseSchoolDistrictDao struct { + *internal.HouseSchoolDistrictDao +} + +var ( + // HouseSchoolDistrict is a globally accessible object for table house_school_district operations. + HouseSchoolDistrict = houseSchoolDistrictDao{internal.NewHouseSchoolDistrictDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/house_transaction.go b/internal/dao/house_transaction.go new file mode 100644 index 0000000..9c6d4b3 --- /dev/null +++ b/internal/dao/house_transaction.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// houseTransactionDao is the data access object for the table house_transaction. +// You can define custom methods on it to extend its functionality as needed. +type houseTransactionDao struct { + *internal.HouseTransactionDao +} + +var ( + // HouseTransaction is a globally accessible object for table house_transaction operations. + HouseTransaction = houseTransactionDao{internal.NewHouseTransactionDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/internal/admin_login_log.go b/internal/dao/internal/admin_login_log.go new file mode 100644 index 0000000..22d6fbd --- /dev/null +++ b/internal/dao/internal/admin_login_log.go @@ -0,0 +1,91 @@ +// ========================================================================== +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminLoginLogDao 是表 admin_login_log 的数据访问对象。 +type AdminLoginLogDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminLoginLogColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminLoginLogColumns 定义并存储表 admin_login_log 的列名。 +type AdminLoginLogColumns struct { + Id string // + Username string // + Ip string // + UserAgent string // + Status string // + FailReason string // + CreatedAt string // +} + +// adminLoginLogColumns 保存表 admin_login_log 的列信息。 +var adminLoginLogColumns = AdminLoginLogColumns{ + Id: "id", + Username: "username", + Ip: "ip", + UserAgent: "user_agent", + Status: "status", + FailReason: "fail_reason", + CreatedAt: "created_at", +} + +// NewAdminLoginLogDao 创建并返回一个新的表数据访问 DAO 对象。 +func NewAdminLoginLogDao(handlers ...gdb.ModelHandler) *AdminLoginLogDao { + return &AdminLoginLogDao{ + group: "default", + table: "admin_login_log", + columns: adminLoginLogColumns, + handlers: handlers, + } +} + +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 +func (dao *AdminLoginLogDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table 返回当前 DAO 的表名。 +func (dao *AdminLoginLogDao) Table() string { + return dao.table +} + +// Columns 返回当前 DAO 的全部列名。 +func (dao *AdminLoginLogDao) Columns() AdminLoginLogColumns { + return dao.columns +} + +// Group 返回当前 DAO 的数据库配置组名。 +func (dao *AdminLoginLogDao) Group() string { + return dao.group +} + +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 +func (dao *AdminLoginLogDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 +// +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 +func (dao *AdminLoginLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_menu.go b/internal/dao/internal/admin_menu.go index 2c3b0e0..27cea03 100644 --- a/internal/dao/internal/admin_menu.go +++ b/internal/dao/internal/admin_menu.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminMenuDao is the data access object for the table admin_menu. +// AdminMenuDao 是表 admin_menu 的数据访问对象。 type AdminMenuDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminMenuDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminMenuColumns defines and stores column names for the table admin_menu. +// AdminMenuColumns 定义并存储表 admin_menu 的列名。 type AdminMenuColumns struct { Id string // ParentId string // @@ -34,7 +34,7 @@ type AdminMenuColumns struct { DeletedAt string // } -// adminMenuColumns holds the columns for the table admin_menu. +// adminMenuColumns 保存表 admin_menu 的列信息。 var adminMenuColumns = AdminMenuColumns{ Id: "id", ParentId: "parent_id", @@ -49,7 +49,7 @@ var adminMenuColumns = AdminMenuColumns{ DeletedAt: "deleted_at", } -// NewAdminMenuDao creates and returns a new DAO object for table data access. +// NewAdminMenuDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminMenuDao(handlers ...gdb.ModelHandler) *AdminMenuDao { return &AdminMenuDao{ group: "default", @@ -59,27 +59,27 @@ func NewAdminMenuDao(handlers ...gdb.ModelHandler) *AdminMenuDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminMenuDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminMenuDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminMenuDao) Columns() AdminMenuColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminMenuDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminMenuDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -88,12 +88,12 @@ func (dao *AdminMenuDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/admin_operation_log.go b/internal/dao/internal/admin_operation_log.go index 77681a1..1506b6f 100644 --- a/internal/dao/internal/admin_operation_log.go +++ b/internal/dao/internal/admin_operation_log.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminOperationLogDao is the data access object for the table admin_operation_log. +// AdminOperationLogDao 是表 admin_operation_log 的数据访问对象。 type AdminOperationLogDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminOperationLogDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminOperationLogColumns defines and stores column names for the table admin_operation_log. +// AdminOperationLogColumns 定义并存储表 admin_operation_log 的列名。 type AdminOperationLogColumns struct { Id string // AdminUserId string // @@ -35,7 +35,7 @@ type AdminOperationLogColumns struct { DeletedAt string // } -// adminOperationLogColumns holds the columns for the table admin_operation_log. +// adminOperationLogColumns 保存表 admin_operation_log 的列信息。 var adminOperationLogColumns = AdminOperationLogColumns{ Id: "id", AdminUserId: "admin_user_id", @@ -51,7 +51,7 @@ var adminOperationLogColumns = AdminOperationLogColumns{ DeletedAt: "deleted_at", } -// NewAdminOperationLogDao creates and returns a new DAO object for table data access. +// NewAdminOperationLogDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminOperationLogDao(handlers ...gdb.ModelHandler) *AdminOperationLogDao { return &AdminOperationLogDao{ group: "default", @@ -61,27 +61,27 @@ func NewAdminOperationLogDao(handlers ...gdb.ModelHandler) *AdminOperationLogDao } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminOperationLogDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminOperationLogDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminOperationLogDao) Columns() AdminOperationLogColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminOperationLogDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminOperationLogDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -90,12 +90,12 @@ func (dao *AdminOperationLogDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminOperationLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/admin_role.go b/internal/dao/internal/admin_role.go index 9acb9b4..bd2de35 100644 --- a/internal/dao/internal/admin_role.go +++ b/internal/dao/internal/admin_role.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminRoleDao is the data access object for the table admin_role. +// AdminRoleDao 是表 admin_role 的数据访问对象。 type AdminRoleDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminRoleDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminRoleColumns defines and stores column names for the table admin_role. +// AdminRoleColumns 定义并存储表 admin_role 的列名。 type AdminRoleColumns struct { Id string // Code string // @@ -30,7 +30,7 @@ type AdminRoleColumns struct { DeletedAt string // } -// adminRoleColumns holds the columns for the table admin_role. +// adminRoleColumns 保存表 admin_role 的列信息。 var adminRoleColumns = AdminRoleColumns{ Id: "id", Code: "code", @@ -41,7 +41,7 @@ var adminRoleColumns = AdminRoleColumns{ DeletedAt: "deleted_at", } -// NewAdminRoleDao creates and returns a new DAO object for table data access. +// NewAdminRoleDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminRoleDao(handlers ...gdb.ModelHandler) *AdminRoleDao { return &AdminRoleDao{ group: "default", @@ -51,27 +51,27 @@ func NewAdminRoleDao(handlers ...gdb.ModelHandler) *AdminRoleDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminRoleDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminRoleDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminRoleDao) Columns() AdminRoleColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminRoleDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminRoleDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -80,12 +80,12 @@ func (dao *AdminRoleDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/admin_role_menu.go b/internal/dao/internal/admin_role_menu.go index 0a2e2ea..b8640fa 100644 --- a/internal/dao/internal/admin_role_menu.go +++ b/internal/dao/internal/admin_role_menu.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminRoleMenuDao is the data access object for the table admin_role_menu. +// AdminRoleMenuDao 是表 admin_role_menu 的数据访问对象。 type AdminRoleMenuDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminRoleMenuDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminRoleMenuColumns defines and stores column names for the table admin_role_menu. +// AdminRoleMenuColumns 定义并存储表 admin_role_menu 的列名。 type AdminRoleMenuColumns struct { Id string // RoleId string // @@ -29,7 +29,7 @@ type AdminRoleMenuColumns struct { DeletedAt string // } -// adminRoleMenuColumns holds the columns for the table admin_role_menu. +// adminRoleMenuColumns 保存表 admin_role_menu 的列信息。 var adminRoleMenuColumns = AdminRoleMenuColumns{ Id: "id", RoleId: "role_id", @@ -39,7 +39,7 @@ var adminRoleMenuColumns = AdminRoleMenuColumns{ DeletedAt: "deleted_at", } -// NewAdminRoleMenuDao creates and returns a new DAO object for table data access. +// NewAdminRoleMenuDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminRoleMenuDao(handlers ...gdb.ModelHandler) *AdminRoleMenuDao { return &AdminRoleMenuDao{ group: "default", @@ -49,27 +49,27 @@ func NewAdminRoleMenuDao(handlers ...gdb.ModelHandler) *AdminRoleMenuDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminRoleMenuDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminRoleMenuDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminRoleMenuDao) Columns() AdminRoleMenuColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminRoleMenuDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminRoleMenuDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -78,12 +78,12 @@ func (dao *AdminRoleMenuDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminRoleMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/admin_user.go b/internal/dao/internal/admin_user.go index fb8f0cc..8e8b622 100644 --- a/internal/dao/internal/admin_user.go +++ b/internal/dao/internal/admin_user.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminUserDao is the data access object for the table admin_user. +// AdminUserDao 是表 admin_user 的数据访问对象。 type AdminUserDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminUserDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminUserColumns defines and stores column names for the table admin_user. +// AdminUserColumns 定义并存储表 admin_user 的列名。 type AdminUserColumns struct { Id string // Username string // @@ -32,7 +32,7 @@ type AdminUserColumns struct { DeletedAt string // } -// adminUserColumns holds the columns for the table admin_user. +// adminUserColumns 保存表 admin_user 的列信息。 var adminUserColumns = AdminUserColumns{ Id: "id", Username: "username", @@ -45,7 +45,7 @@ var adminUserColumns = AdminUserColumns{ DeletedAt: "deleted_at", } -// NewAdminUserDao creates and returns a new DAO object for table data access. +// NewAdminUserDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminUserDao(handlers ...gdb.ModelHandler) *AdminUserDao { return &AdminUserDao{ group: "default", @@ -55,27 +55,27 @@ func NewAdminUserDao(handlers ...gdb.ModelHandler) *AdminUserDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminUserDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminUserDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminUserDao) Columns() AdminUserColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminUserDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminUserDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -84,12 +84,12 @@ func (dao *AdminUserDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/admin_user_role.go b/internal/dao/internal/admin_user_role.go index d86f632..dbea78a 100644 --- a/internal/dao/internal/admin_user_role.go +++ b/internal/dao/internal/admin_user_role.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AdminUserRoleDao is the data access object for the table admin_user_role. +// AdminUserRoleDao 是表 admin_user_role 的数据访问对象。 type AdminUserRoleDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AdminUserRoleDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AdminUserRoleColumns defines and stores column names for the table admin_user_role. +// AdminUserRoleColumns 定义并存储表 admin_user_role 的列名。 type AdminUserRoleColumns struct { Id string // AdminUserId string // @@ -29,7 +29,7 @@ type AdminUserRoleColumns struct { DeletedAt string // } -// adminUserRoleColumns holds the columns for the table admin_user_role. +// adminUserRoleColumns 保存表 admin_user_role 的列信息。 var adminUserRoleColumns = AdminUserRoleColumns{ Id: "id", AdminUserId: "admin_user_id", @@ -39,7 +39,7 @@ var adminUserRoleColumns = AdminUserRoleColumns{ DeletedAt: "deleted_at", } -// NewAdminUserRoleDao creates and returns a new DAO object for table data access. +// NewAdminUserRoleDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAdminUserRoleDao(handlers ...gdb.ModelHandler) *AdminUserRoleDao { return &AdminUserRoleDao{ group: "default", @@ -49,27 +49,27 @@ func NewAdminUserRoleDao(handlers ...gdb.ModelHandler) *AdminUserRoleDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AdminUserRoleDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AdminUserRoleDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AdminUserRoleDao) Columns() AdminUserRoleColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AdminUserRoleDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AdminUserRoleDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -78,12 +78,12 @@ func (dao *AdminUserRoleDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AdminUserRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/auth_refresh_session.go b/internal/dao/internal/auth_refresh_session.go index 5e89a38..0f8b880 100644 --- a/internal/dao/internal/auth_refresh_session.go +++ b/internal/dao/internal/auth_refresh_session.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// AuthRefreshSessionDao is the data access object for the table auth_refresh_session. +// AuthRefreshSessionDao 是表 auth_refresh_session 的数据访问对象。 type AuthRefreshSessionDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type AuthRefreshSessionDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// AuthRefreshSessionColumns defines and stores column names for the table auth_refresh_session. +// AuthRefreshSessionColumns 定义并存储表 auth_refresh_session 的列名。 type AuthRefreshSessionColumns struct { Id string // SubjectId string // ????????? ID @@ -33,7 +33,7 @@ type AuthRefreshSessionColumns struct { DeletedAt string // } -// authRefreshSessionColumns holds the columns for the table auth_refresh_session. +// authRefreshSessionColumns 保存表 auth_refresh_session 的列信息。 var authRefreshSessionColumns = AuthRefreshSessionColumns{ Id: "id", SubjectId: "subject_id", @@ -47,7 +47,7 @@ var authRefreshSessionColumns = AuthRefreshSessionColumns{ DeletedAt: "deleted_at", } -// NewAuthRefreshSessionDao creates and returns a new DAO object for table data access. +// NewAuthRefreshSessionDao 创建并返回一个新的表数据访问 DAO 对象。 func NewAuthRefreshSessionDao(handlers ...gdb.ModelHandler) *AuthRefreshSessionDao { return &AuthRefreshSessionDao{ group: "default", @@ -57,27 +57,27 @@ func NewAuthRefreshSessionDao(handlers ...gdb.ModelHandler) *AuthRefreshSessionD } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *AuthRefreshSessionDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *AuthRefreshSessionDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *AuthRefreshSessionDao) Columns() AuthRefreshSessionColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *AuthRefreshSessionDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *AuthRefreshSessionDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -86,12 +86,12 @@ func (dao *AuthRefreshSessionDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *AuthRefreshSessionDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/content.go b/internal/dao/internal/content.go index 9c803d9..17885e7 100644 --- a/internal/dao/internal/content.go +++ b/internal/dao/internal/content.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// ContentDao is the data access object for the table content. +// ContentDao 是表 content 的数据访问对象。 type ContentDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type ContentDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// ContentColumns defines and stores column names for the table content. +// ContentColumns 定义并存储表 content 的列名。 type ContentColumns struct { Id string // Title string // @@ -30,7 +30,7 @@ type ContentColumns struct { DeletedAt string // } -// contentColumns holds the columns for the table content. +// contentColumns 保存表 content 的列信息。 var contentColumns = ContentColumns{ Id: "id", Title: "title", @@ -41,7 +41,7 @@ var contentColumns = ContentColumns{ DeletedAt: "deleted_at", } -// NewContentDao creates and returns a new DAO object for table data access. +// NewContentDao 创建并返回一个新的表数据访问 DAO 对象。 func NewContentDao(handlers ...gdb.ModelHandler) *ContentDao { return &ContentDao{ group: "default", @@ -51,27 +51,27 @@ func NewContentDao(handlers ...gdb.ModelHandler) *ContentDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *ContentDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *ContentDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *ContentDao) Columns() ContentColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *ContentDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *ContentDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -80,12 +80,12 @@ func (dao *ContentDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *ContentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/house_building.go b/internal/dao/internal/house_building.go new file mode 100644 index 0000000..b7aadcb --- /dev/null +++ b/internal/dao/internal/house_building.go @@ -0,0 +1,103 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseBuildingDao is the data access object for the table house_building. +type HouseBuildingDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseBuildingColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseBuildingColumns defines and stores column names for the table house_building. +type HouseBuildingColumns struct { + Id string // + CommunityId string // 小区ID + BuildingNo string // 栋号 + Units string // 单元数 + TotalFloors string // 总楼层 + ElevatorCount string // 电梯数 + LadderRatio string // 梯户比 + BuildingType string // 板楼/塔楼 + Lng string // 楼栋经度 + Lat string // 楼栋纬度 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseBuildingColumns holds the columns for the table house_building. +var houseBuildingColumns = HouseBuildingColumns{ + Id: "id", + CommunityId: "community_id", + BuildingNo: "building_no", + Units: "units", + TotalFloors: "total_floors", + ElevatorCount: "elevator_count", + LadderRatio: "ladder_ratio", + BuildingType: "building_type", + Lng: "lng", + Lat: "lat", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseBuildingDao creates and returns a new DAO object for table data access. +func NewHouseBuildingDao(handlers ...gdb.ModelHandler) *HouseBuildingDao { + return &HouseBuildingDao{ + group: "default", + table: "house_building", + columns: houseBuildingColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseBuildingDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseBuildingDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseBuildingDao) Columns() HouseBuildingColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseBuildingDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseBuildingDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseBuildingDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_community.go b/internal/dao/internal/house_community.go new file mode 100644 index 0000000..89d6557 --- /dev/null +++ b/internal/dao/internal/house_community.go @@ -0,0 +1,113 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseCommunityDao is the data access object for the table house_community. +type HouseCommunityDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseCommunityColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseCommunityColumns defines and stores column names for the table house_community. +type HouseCommunityColumns struct { + Id string // + Name string // 小区/楼盘名 + Region string // 区县(云岩/南明/观山湖/花溪等) + BusinessDistrict string // 板块 + Address string // 地址 + Lng string // 经度(GCJ-02) + Lat string // 纬度(GCJ-02) + BuildYear string // 建成年份 + Households string // 总户数 + PlotRatio string // 容积率 + GreenRate string // 绿化率 + PropertyCompany string // 物业公司 + PropertyFee string // 物业费(元/月/平米) + Developer string // 开发商 + Source string // 数据来源 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseCommunityColumns holds the columns for the table house_community. +var houseCommunityColumns = HouseCommunityColumns{ + Id: "id", + Name: "name", + Region: "region", + BusinessDistrict: "business_district", + Address: "address", + Lng: "lng", + Lat: "lat", + BuildYear: "build_year", + Households: "households", + PlotRatio: "plot_ratio", + GreenRate: "green_rate", + PropertyCompany: "property_company", + PropertyFee: "property_fee", + Developer: "developer", + Source: "source", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseCommunityDao creates and returns a new DAO object for table data access. +func NewHouseCommunityDao(handlers ...gdb.ModelHandler) *HouseCommunityDao { + return &HouseCommunityDao{ + group: "default", + table: "house_community", + columns: houseCommunityColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseCommunityDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseCommunityDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseCommunityDao) Columns() HouseCommunityColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseCommunityDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseCommunityDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseCommunityDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_community_facility.go b/internal/dao/internal/house_community_facility.go new file mode 100644 index 0000000..abab5d0 --- /dev/null +++ b/internal/dao/internal/house_community_facility.go @@ -0,0 +1,93 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseCommunityFacilityDao is the data access object for the table house_community_facility. +type HouseCommunityFacilityDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseCommunityFacilityColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseCommunityFacilityColumns defines and stores column names for the table house_community_facility. +type HouseCommunityFacilityColumns struct { + Id string // + CommunityId string // 小区ID + FacilityId string // 配套ID + Distance string // 距离(米) + CommuteMinutes string // 通勤分钟 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseCommunityFacilityColumns holds the columns for the table house_community_facility. +var houseCommunityFacilityColumns = HouseCommunityFacilityColumns{ + Id: "id", + CommunityId: "community_id", + FacilityId: "facility_id", + Distance: "distance", + CommuteMinutes: "commute_minutes", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseCommunityFacilityDao creates and returns a new DAO object for table data access. +func NewHouseCommunityFacilityDao(handlers ...gdb.ModelHandler) *HouseCommunityFacilityDao { + return &HouseCommunityFacilityDao{ + group: "default", + table: "house_community_facility", + columns: houseCommunityFacilityColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseCommunityFacilityDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseCommunityFacilityDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseCommunityFacilityDao) Columns() HouseCommunityFacilityColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseCommunityFacilityDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseCommunityFacilityDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseCommunityFacilityDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_facility.go b/internal/dao/internal/house_facility.go new file mode 100644 index 0000000..b8737ba --- /dev/null +++ b/internal/dao/internal/house_facility.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseFacilityDao is the data access object for the table house_facility. +type HouseFacilityDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseFacilityColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseFacilityColumns defines and stores column names for the table house_facility. +type HouseFacilityColumns struct { + Id string // + Name string // 配套名称 + Type string // 地铁/学校/医院/商圈 + Lng string // 经度 + Lat string // 纬度 + Line string // 地铁线路 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseFacilityColumns holds the columns for the table house_facility. +var houseFacilityColumns = HouseFacilityColumns{ + Id: "id", + Name: "name", + Type: "type", + Lng: "lng", + Lat: "lat", + Line: "line", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseFacilityDao creates and returns a new DAO object for table data access. +func NewHouseFacilityDao(handlers ...gdb.ModelHandler) *HouseFacilityDao { + return &HouseFacilityDao{ + group: "default", + table: "house_facility", + columns: houseFacilityColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseFacilityDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseFacilityDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseFacilityDao) Columns() HouseFacilityColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseFacilityDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseFacilityDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseFacilityDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_listing.go b/internal/dao/internal/house_listing.go new file mode 100644 index 0000000..442ba06 --- /dev/null +++ b/internal/dao/internal/house_listing.go @@ -0,0 +1,131 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseListingDao is the data access object for the table house_listing. +type HouseListingDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseListingColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseListingColumns defines and stores column names for the table house_listing. +type HouseListingColumns struct { + Id string // + CommunityId string // 小区ID + BuildingId string // 楼栋ID + HouseNo string // 房号 + Layout string // 户型(如3室2厅) + Area string // 建筑面积(平米) + UsableArea string // 套内面积(平米) + Orientation string // 朝向 + Floor string // 所在楼层 + TotalFloors string // 总楼层 + Decoration string // 装修 + TotalPrice string // 总价(万元) + UnitPrice string // 单价(元/平米) + ListPrice string // 挂牌价(万元) + Source string // 来源平台 + SourceHouseId string // 平台侧房源ID + SourceUrl string // 房源链接 + MatchGroupId string // 疑似同房源分组(跨平台软关联) + OnMarketDays string // 挂牌天数 + PriceChangeCount string // 调价次数 + Status string // 1在售 2下架 3成交 + Confidence string // 可信度 0正常 1低可信 + IsBargain string // 笋盘标记 0否 1是 + ListingTime string // 挂牌时间 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseListingColumns holds the columns for the table house_listing. +var houseListingColumns = HouseListingColumns{ + Id: "id", + CommunityId: "community_id", + BuildingId: "building_id", + HouseNo: "house_no", + Layout: "layout", + Area: "area", + UsableArea: "usable_area", + Orientation: "orientation", + Floor: "floor", + TotalFloors: "total_floors", + Decoration: "decoration", + TotalPrice: "total_price", + UnitPrice: "unit_price", + ListPrice: "list_price", + Source: "source", + SourceHouseId: "source_house_id", + SourceUrl: "source_url", + MatchGroupId: "match_group_id", + OnMarketDays: "on_market_days", + PriceChangeCount: "price_change_count", + Status: "status", + Confidence: "confidence", + IsBargain: "is_bargain", + ListingTime: "listing_time", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseListingDao creates and returns a new DAO object for table data access. +func NewHouseListingDao(handlers ...gdb.ModelHandler) *HouseListingDao { + return &HouseListingDao{ + group: "default", + table: "house_listing", + columns: houseListingColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseListingDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseListingDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseListingDao) Columns() HouseListingColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseListingDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseListingDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseListingDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_preference.go b/internal/dao/internal/house_preference.go new file mode 100644 index 0000000..990a686 --- /dev/null +++ b/internal/dao/internal/house_preference.go @@ -0,0 +1,107 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HousePreferenceDao is the data access object for the table house_preference. +type HousePreferenceDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HousePreferenceColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HousePreferenceColumns defines and stores column names for the table house_preference. +type HousePreferenceColumns struct { + Id string // + BudgetMin string // 预算下限(万元) + BudgetMax string // 预算上限(万元) + AreaMin string // 面积下限(平米) + AreaMax string // 面积上限(平米) + Layouts string // 户型偏好JSON + SubwayLines string // 地铁线路JSON + SchoolRequired string // 是否要求学区 + CommuteTarget string // 通勤目标点 + CommuteLimitMin string // 通勤上限(分钟) + RegionPrefer string // 区域偏好JSON + Weights string // 权重JSON + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// housePreferenceColumns holds the columns for the table house_preference. +var housePreferenceColumns = HousePreferenceColumns{ + Id: "id", + BudgetMin: "budget_min", + BudgetMax: "budget_max", + AreaMin: "area_min", + AreaMax: "area_max", + Layouts: "layouts", + SubwayLines: "subway_lines", + SchoolRequired: "school_required", + CommuteTarget: "commute_target", + CommuteLimitMin: "commute_limit_min", + RegionPrefer: "region_prefer", + Weights: "weights", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHousePreferenceDao creates and returns a new DAO object for table data access. +func NewHousePreferenceDao(handlers ...gdb.ModelHandler) *HousePreferenceDao { + return &HousePreferenceDao{ + group: "default", + table: "house_preference", + columns: housePreferenceColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HousePreferenceDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HousePreferenceDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HousePreferenceDao) Columns() HousePreferenceColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HousePreferenceDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HousePreferenceDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HousePreferenceDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_price_snapshot.go b/internal/dao/internal/house_price_snapshot.go new file mode 100644 index 0000000..94ca3d3 --- /dev/null +++ b/internal/dao/internal/house_price_snapshot.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HousePriceSnapshotDao is the data access object for the table house_price_snapshot. +type HousePriceSnapshotDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HousePriceSnapshotColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HousePriceSnapshotColumns defines and stores column names for the table house_price_snapshot. +type HousePriceSnapshotColumns struct { + Id string // + ListingId string // 房源ID + CommunityId string // 小区ID + SnapDate string // 快照日期 + ListPrice string // 挂牌价(万元) + DealPrice string // 成交价(万元) + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// housePriceSnapshotColumns holds the columns for the table house_price_snapshot. +var housePriceSnapshotColumns = HousePriceSnapshotColumns{ + Id: "id", + ListingId: "listing_id", + CommunityId: "community_id", + SnapDate: "snap_date", + ListPrice: "list_price", + DealPrice: "deal_price", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHousePriceSnapshotDao creates and returns a new DAO object for table data access. +func NewHousePriceSnapshotDao(handlers ...gdb.ModelHandler) *HousePriceSnapshotDao { + return &HousePriceSnapshotDao{ + group: "default", + table: "house_price_snapshot", + columns: housePriceSnapshotColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HousePriceSnapshotDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HousePriceSnapshotDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HousePriceSnapshotDao) Columns() HousePriceSnapshotColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HousePriceSnapshotDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HousePriceSnapshotDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HousePriceSnapshotDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_school_district.go b/internal/dao/internal/house_school_district.go new file mode 100644 index 0000000..d15be18 --- /dev/null +++ b/internal/dao/internal/house_school_district.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseSchoolDistrictDao is the data access object for the table house_school_district. +type HouseSchoolDistrictDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseSchoolDistrictColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseSchoolDistrictColumns defines and stores column names for the table house_school_district. +type HouseSchoolDistrictColumns struct { + Id string // + SchoolName string // 学校名 + CommunityId string // 小区ID + DistrictPolygon string // 划片范围GeoJSON + DistrictYear string // 划片年度 + Note string // 备注 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseSchoolDistrictColumns holds the columns for the table house_school_district. +var houseSchoolDistrictColumns = HouseSchoolDistrictColumns{ + Id: "id", + SchoolName: "school_name", + CommunityId: "community_id", + DistrictPolygon: "district_polygon", + DistrictYear: "district_year", + Note: "note", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseSchoolDistrictDao creates and returns a new DAO object for table data access. +func NewHouseSchoolDistrictDao(handlers ...gdb.ModelHandler) *HouseSchoolDistrictDao { + return &HouseSchoolDistrictDao{ + group: "default", + table: "house_school_district", + columns: houseSchoolDistrictColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseSchoolDistrictDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseSchoolDistrictDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseSchoolDistrictDao) Columns() HouseSchoolDistrictColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseSchoolDistrictDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseSchoolDistrictDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseSchoolDistrictDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/house_transaction.go b/internal/dao/internal/house_transaction.go new file mode 100644 index 0000000..755b0cd --- /dev/null +++ b/internal/dao/internal/house_transaction.go @@ -0,0 +1,103 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// HouseTransactionDao is the data access object for the table house_transaction. +type HouseTransactionDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns HouseTransactionColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// HouseTransactionColumns defines and stores column names for the table house_transaction. +type HouseTransactionColumns struct { + Id string // + CommunityId string // 小区ID + BuildingId string // 楼栋ID + Layout string // 户型 + Area string // 面积(平米) + DealPrice string // 成交总价(万元) + DealUnitPrice string // 成交单价(元/平米) + ListDays string // 挂牌到成交天数 + DealDate string // 成交日期 + Source string // 来源 + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// houseTransactionColumns holds the columns for the table house_transaction. +var houseTransactionColumns = HouseTransactionColumns{ + Id: "id", + CommunityId: "community_id", + BuildingId: "building_id", + Layout: "layout", + Area: "area", + DealPrice: "deal_price", + DealUnitPrice: "deal_unit_price", + ListDays: "list_days", + DealDate: "deal_date", + Source: "source", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewHouseTransactionDao creates and returns a new DAO object for table data access. +func NewHouseTransactionDao(handlers ...gdb.ModelHandler) *HouseTransactionDao { + return &HouseTransactionDao{ + group: "default", + table: "house_transaction", + columns: houseTransactionColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *HouseTransactionDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *HouseTransactionDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *HouseTransactionDao) Columns() HouseTransactionColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *HouseTransactionDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *HouseTransactionDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *HouseTransactionDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/user.go b/internal/dao/internal/user.go index f0a9e95..6a9c812 100644 --- a/internal/dao/internal/user.go +++ b/internal/dao/internal/user.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// UserDao is the data access object for the table user. +// UserDao 是表 user 的数据访问对象。 type UserDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type UserDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// UserColumns defines and stores column names for the table user. +// UserColumns 定义并存储表 user 的列名。 type UserColumns struct { Id string // UnionId string // @@ -36,7 +36,7 @@ type UserColumns struct { DeletedAt string // } -// userColumns holds the columns for the table user. +// userColumns 保存表 user 的列信息。 var userColumns = UserColumns{ Id: "id", UnionId: "union_id", @@ -53,7 +53,7 @@ var userColumns = UserColumns{ DeletedAt: "deleted_at", } -// NewUserDao creates and returns a new DAO object for table data access. +// NewUserDao 创建并返回一个新的表数据访问 DAO 对象。 func NewUserDao(handlers ...gdb.ModelHandler) *UserDao { return &UserDao{ group: "default", @@ -63,27 +63,27 @@ func NewUserDao(handlers ...gdb.ModelHandler) *UserDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *UserDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *UserDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *UserDao) Columns() UserColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *UserDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *UserDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -92,12 +92,12 @@ func (dao *UserDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *UserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/user_favorite.go b/internal/dao/internal/user_favorite.go index 9b52a32..f8dba75 100644 --- a/internal/dao/internal/user_favorite.go +++ b/internal/dao/internal/user_favorite.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// UserFavoriteDao is the data access object for the table user_favorite. +// UserFavoriteDao 是表 user_favorite 的数据访问对象。 type UserFavoriteDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type UserFavoriteDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// UserFavoriteColumns defines and stores column names for the table user_favorite. +// UserFavoriteColumns 定义并存储表 user_favorite 的列名。 type UserFavoriteColumns struct { Id string // UserId string // @@ -29,7 +29,7 @@ type UserFavoriteColumns struct { DeletedAt string // } -// userFavoriteColumns holds the columns for the table user_favorite. +// userFavoriteColumns 保存表 user_favorite 的列信息。 var userFavoriteColumns = UserFavoriteColumns{ Id: "id", UserId: "user_id", @@ -39,7 +39,7 @@ var userFavoriteColumns = UserFavoriteColumns{ DeletedAt: "deleted_at", } -// NewUserFavoriteDao creates and returns a new DAO object for table data access. +// NewUserFavoriteDao 创建并返回一个新的表数据访问 DAO 对象。 func NewUserFavoriteDao(handlers ...gdb.ModelHandler) *UserFavoriteDao { return &UserFavoriteDao{ group: "default", @@ -49,27 +49,27 @@ func NewUserFavoriteDao(handlers ...gdb.ModelHandler) *UserFavoriteDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *UserFavoriteDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *UserFavoriteDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *UserFavoriteDao) Columns() UserFavoriteColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *UserFavoriteDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *UserFavoriteDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -78,12 +78,12 @@ func (dao *UserFavoriteDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *UserFavoriteDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/user_message.go b/internal/dao/internal/user_message.go index 9389c1a..a1b0284 100644 --- a/internal/dao/internal/user_message.go +++ b/internal/dao/internal/user_message.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ========================================================================== package internal @@ -11,7 +11,7 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// UserMessageDao is the data access object for the table user_message. +// UserMessageDao 是表 user_message 的数据访问对象。 type UserMessageDao struct { table string // table is the underlying table name of the DAO. group string // group is the database configuration group name of the current DAO. @@ -19,7 +19,7 @@ type UserMessageDao struct { handlers []gdb.ModelHandler // handlers for customized model modification. } -// UserMessageColumns defines and stores column names for the table user_message. +// UserMessageColumns 定义并存储表 user_message 的列名。 type UserMessageColumns struct { Id string // UserId string // @@ -31,7 +31,7 @@ type UserMessageColumns struct { DeletedAt string // } -// userMessageColumns holds the columns for the table user_message. +// userMessageColumns 保存表 user_message 的列信息。 var userMessageColumns = UserMessageColumns{ Id: "id", UserId: "user_id", @@ -43,7 +43,7 @@ var userMessageColumns = UserMessageColumns{ DeletedAt: "deleted_at", } -// NewUserMessageDao creates and returns a new DAO object for table data access. +// NewUserMessageDao 创建并返回一个新的表数据访问 DAO 对象。 func NewUserMessageDao(handlers ...gdb.ModelHandler) *UserMessageDao { return &UserMessageDao{ group: "default", @@ -53,27 +53,27 @@ func NewUserMessageDao(handlers ...gdb.ModelHandler) *UserMessageDao { } } -// DB retrieves and returns the underlying raw database management object of the current DAO. +// DB 获取并返回当前 DAO 的底层原始数据库管理对象。 func (dao *UserMessageDao) DB() gdb.DB { return g.DB(dao.group) } -// Table returns the table name of the current DAO. +// Table 返回当前 DAO 的表名。 func (dao *UserMessageDao) Table() string { return dao.table } -// Columns returns all column names of the current DAO. +// Columns 返回当前 DAO 的全部列名。 func (dao *UserMessageDao) Columns() UserMessageColumns { return dao.columns } -// Group returns the database configuration group name of the current DAO. +// Group 返回当前 DAO 的数据库配置组名。 func (dao *UserMessageDao) Group() string { return dao.group } -// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +// Ctx 为当前 DAO 创建并返回一个 Model,自动设置本次操作的上下文。 func (dao *UserMessageDao) Ctx(ctx context.Context) *gdb.Model { model := dao.DB().Model(dao.table) for _, handler := range dao.handlers { @@ -82,12 +82,12 @@ func (dao *UserMessageDao) Ctx(ctx context.Context) *gdb.Model { return model.Safe().Ctx(ctx) } -// Transaction wraps the transaction logic using function f. -// It rolls back the transaction and returns the error if function f returns a non-nil error. -// It commits the transaction and returns nil if function f returns nil. +// Transaction 使用函数 f 包裹事务逻辑。 +// 若 f 返回非 nil 错误,则回滚事务并返回该错误。 +// 若 f 返回 nil,则提交事务并返回 nil。 // -// Note: Do not commit or roll back the transaction in function f, -// as it is automatically handled by this function. +// 注意:请勿在函数 f 内提交或回滚事务, +// 该函数会自动处理。 func (dao *UserMessageDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/user.go b/internal/dao/user.go index acb129f..b47e8c6 100644 --- a/internal/dao/user.go +++ b/internal/dao/user.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// userDao is the data access object for the table user. -// You can define custom methods on it to extend its functionality as needed. +// userDao 是表 user 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type userDao struct { *internal.UserDao } var ( - // User is a globally accessible object for table user operations. + // User 是表 user 的全局可访问操作对象。 User = userDao{internal.NewUserDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/user_favorite.go b/internal/dao/user_favorite.go index b595e82..581fe03 100644 --- a/internal/dao/user_favorite.go +++ b/internal/dao/user_favorite.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// userFavoriteDao is the data access object for the table user_favorite. -// You can define custom methods on it to extend its functionality as needed. +// userFavoriteDao 是表 user_favorite 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type userFavoriteDao struct { *internal.UserFavoriteDao } var ( - // UserFavorite is a globally accessible object for table user_favorite operations. + // UserFavorite 是表 user_favorite 的全局可访问操作对象。 UserFavorite = userFavoriteDao{internal.NewUserFavoriteDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/dao/user_message.go b/internal/dao/user_message.go index 00c348d..19c40f5 100644 --- a/internal/dao/user_message.go +++ b/internal/dao/user_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package dao @@ -8,15 +8,15 @@ import ( "service.xpcool.com/internal/dao/internal" ) -// userMessageDao is the data access object for the table user_message. -// You can define custom methods on it to extend its functionality as needed. +// userMessageDao 是表 user_message 的数据访问对象。 +// 可在其上定义自定义方法以扩展其功能。 type userMessageDao struct { *internal.UserMessageDao } var ( - // UserMessage is a globally accessible object for table user_message operations. + // UserMessage 是表 user_message 的全局可访问操作对象。 UserMessage = userMessageDao{internal.NewUserMessageDao()} ) -// Add your custom methods and functionality below. +// 在下方添加你的自定义方法。 diff --git a/internal/library/jwt/jwt.go b/internal/library/jwt/jwt.go index 8ec42b8..c931ef7 100644 --- a/internal/library/jwt/jwt.go +++ b/internal/library/jwt/jwt.go @@ -1,4 +1,4 @@ -// Package jwt implements HS256 JWT with Go standard crypto primitives. +// Package jwt 使用 Go 标准加密库实现 HS256 JWT。 package jwt import ( diff --git a/internal/library/response/error.go b/internal/library/response/error.go index 9af0141..954f422 100644 --- a/internal/library/response/error.go +++ b/internal/library/response/error.go @@ -5,7 +5,7 @@ import ( "github.com/gogf/gf/v2/errors/gerror" ) -// Error creates an error carrying a stable application code for global response handling. +// Error 创建携带稳定业务码的错误,供全局响应处理使用。 func Error(code int, message string) error { return gerror.NewCode(gcode.New(code, message, nil), message) } diff --git a/internal/library/response/response.go b/internal/library/response/response.go index 8394214..dd16132 100644 --- a/internal/library/response/response.go +++ b/internal/library/response/response.go @@ -2,7 +2,7 @@ package response import "github.com/gogf/gf/v2/net/ghttp" -// Body is the only JSON envelope exposed by both API surfaces. +// Body 是两套 API 共用的唯一 JSON 响应信封。 type Body struct { Code int `json:"code"` Message string `json:"message"` diff --git a/internal/middleware/common.go b/internal/middleware/common.go index 2e71c74..ca7960f 100644 --- a/internal/middleware/common.go +++ b/internal/middleware/common.go @@ -6,7 +6,7 @@ import ( "service.xpcool.com/internal/library/response" ) -// CORS is deliberately mounted only on public HTTP route groups. +// CORS 仅挂载在公开的 HTTP 路由组上。 func CORS(r *ghttp.Request) { r.Response.CORSDefault() if r.Method == "OPTIONS" { @@ -15,7 +15,7 @@ func CORS(r *ghttp.Request) { r.Middleware.Next() } -// Recover converts panics to the common envelope; server logs retain stack traces. +// Recover 将 panic 转为统一响应信封;服务端日志保留堆栈信息。 func Recover(r *ghttp.Request) { defer func() { if recover() != nil { diff --git a/internal/model/do/admin_login_log.go b/internal/model/do/admin_login_log.go new file mode 100644 index 0000000..6d71a04 --- /dev/null +++ b/internal/model/do/admin_login_log.go @@ -0,0 +1,22 @@ +// ================================================================================= +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminLoginLog 是表 admin_login_log 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 +type AdminLoginLog struct { + g.Meta `orm:"table:admin_login_log, do:true"` + Id any // + Username any // + Ip any // + UserAgent any // + Status any // + FailReason any // + CreatedAt *gtime.Time // +} diff --git a/internal/model/do/admin_menu.go b/internal/model/do/admin_menu.go index 4f71771..917f9d3 100644 --- a/internal/model/do/admin_menu.go +++ b/internal/model/do/admin_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminMenu is the golang structure of table admin_menu for DAO operations like Where/Data. +// AdminMenu 是表 admin_menu 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminMenu struct { g.Meta `orm:"table:admin_menu, do:true"` Id any // diff --git a/internal/model/do/admin_operation_log.go b/internal/model/do/admin_operation_log.go index 1d45785..f699865 100644 --- a/internal/model/do/admin_operation_log.go +++ b/internal/model/do/admin_operation_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminOperationLog is the golang structure of table admin_operation_log for DAO operations like Where/Data. +// AdminOperationLog 是表 admin_operation_log 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminOperationLog struct { g.Meta `orm:"table:admin_operation_log, do:true"` Id any // diff --git a/internal/model/do/admin_role.go b/internal/model/do/admin_role.go index 849fe41..56e17c8 100644 --- a/internal/model/do/admin_role.go +++ b/internal/model/do/admin_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminRole is the golang structure of table admin_role for DAO operations like Where/Data. +// AdminRole 是表 admin_role 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminRole struct { g.Meta `orm:"table:admin_role, do:true"` Id any // diff --git a/internal/model/do/admin_role_menu.go b/internal/model/do/admin_role_menu.go index f78004e..e249251 100644 --- a/internal/model/do/admin_role_menu.go +++ b/internal/model/do/admin_role_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminRoleMenu is the golang structure of table admin_role_menu for DAO operations like Where/Data. +// AdminRoleMenu 是表 admin_role_menu 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminRoleMenu struct { g.Meta `orm:"table:admin_role_menu, do:true"` Id any // diff --git a/internal/model/do/admin_user.go b/internal/model/do/admin_user.go index 5072be2..dded414 100644 --- a/internal/model/do/admin_user.go +++ b/internal/model/do/admin_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminUser is the golang structure of table admin_user for DAO operations like Where/Data. +// AdminUser 是表 admin_user 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminUser struct { g.Meta `orm:"table:admin_user, do:true"` Id any // diff --git a/internal/model/do/admin_user_role.go b/internal/model/do/admin_user_role.go index 2fbd1a2..0570a27 100644 --- a/internal/model/do/admin_user_role.go +++ b/internal/model/do/admin_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminUserRole is the golang structure of table admin_user_role for DAO operations like Where/Data. +// AdminUserRole 是表 admin_user_role 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AdminUserRole struct { g.Meta `orm:"table:admin_user_role, do:true"` Id any // diff --git a/internal/model/do/auth_refresh_session.go b/internal/model/do/auth_refresh_session.go index c68929a..86ed908 100644 --- a/internal/model/do/auth_refresh_session.go +++ b/internal/model/do/auth_refresh_session.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AuthRefreshSession is the golang structure of table auth_refresh_session for DAO operations like Where/Data. +// AuthRefreshSession 是表 auth_refresh_session 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type AuthRefreshSession struct { g.Meta `orm:"table:auth_refresh_session, do:true"` Id any // diff --git a/internal/model/do/content.go b/internal/model/do/content.go index a5ff59f..f1c1d22 100644 --- a/internal/model/do/content.go +++ b/internal/model/do/content.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// Content is the golang structure of table content for DAO operations like Where/Data. +// Content 是表 content 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type Content struct { g.Meta `orm:"table:content, do:true"` Id any // diff --git a/internal/model/do/house_building.go b/internal/model/do/house_building.go new file mode 100644 index 0000000..3cf17e8 --- /dev/null +++ b/internal/model/do/house_building.go @@ -0,0 +1,27 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseBuilding is the golang structure of table house_building for DAO operations like Where/Data. +type HouseBuilding struct { + g.Meta `orm:"table:house_building, do:true"` + Id any // + CommunityId any // 小区ID + BuildingNo any // 栋号 + Units any // 单元数 + TotalFloors any // 总楼层 + ElevatorCount any // 电梯数 + LadderRatio any // 梯户比 + BuildingType any // 板楼/塔楼 + Lng any // 楼栋经度 + Lat any // 楼栋纬度 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_community.go b/internal/model/do/house_community.go new file mode 100644 index 0000000..77f9281 --- /dev/null +++ b/internal/model/do/house_community.go @@ -0,0 +1,32 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseCommunity is the golang structure of table house_community for DAO operations like Where/Data. +type HouseCommunity struct { + g.Meta `orm:"table:house_community, do:true"` + Id any // + Name any // 小区/楼盘名 + Region any // 区县(云岩/南明/观山湖/花溪等) + BusinessDistrict any // 板块 + Address any // 地址 + Lng any // 经度(GCJ-02) + Lat any // 纬度(GCJ-02) + BuildYear any // 建成年份 + Households any // 总户数 + PlotRatio any // 容积率 + GreenRate any // 绿化率 + PropertyCompany any // 物业公司 + PropertyFee any // 物业费(元/月/平米) + Developer any // 开发商 + Source any // 数据来源 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_community_facility.go b/internal/model/do/house_community_facility.go new file mode 100644 index 0000000..6153104 --- /dev/null +++ b/internal/model/do/house_community_facility.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseCommunityFacility is the golang structure of table house_community_facility for DAO operations like Where/Data. +type HouseCommunityFacility struct { + g.Meta `orm:"table:house_community_facility, do:true"` + Id any // + CommunityId any // 小区ID + FacilityId any // 配套ID + Distance any // 距离(米) + CommuteMinutes any // 通勤分钟 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_facility.go b/internal/model/do/house_facility.go new file mode 100644 index 0000000..b59240a --- /dev/null +++ b/internal/model/do/house_facility.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseFacility is the golang structure of table house_facility for DAO operations like Where/Data. +type HouseFacility struct { + g.Meta `orm:"table:house_facility, do:true"` + Id any // + Name any // 配套名称 + Type any // 地铁/学校/医院/商圈 + Lng any // 经度 + Lat any // 纬度 + Line any // 地铁线路 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_listing.go b/internal/model/do/house_listing.go new file mode 100644 index 0000000..123cd97 --- /dev/null +++ b/internal/model/do/house_listing.go @@ -0,0 +1,41 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseListing is the golang structure of table house_listing for DAO operations like Where/Data. +type HouseListing struct { + g.Meta `orm:"table:house_listing, do:true"` + Id any // + CommunityId any // 小区ID + BuildingId any // 楼栋ID + HouseNo any // 房号 + Layout any // 户型(如3室2厅) + Area any // 建筑面积(平米) + UsableArea any // 套内面积(平米) + Orientation any // 朝向 + Floor any // 所在楼层 + TotalFloors any // 总楼层 + Decoration any // 装修 + TotalPrice any // 总价(万元) + UnitPrice any // 单价(元/平米) + ListPrice any // 挂牌价(万元) + Source any // 来源平台 + SourceHouseId any // 平台侧房源ID + SourceUrl any // 房源链接 + MatchGroupId any // 疑似同房源分组(跨平台软关联) + OnMarketDays any // 挂牌天数 + PriceChangeCount any // 调价次数 + Status any // 1在售 2下架 3成交 + Confidence any // 可信度 0正常 1低可信 + IsBargain any // 笋盘标记 0否 1是 + ListingTime any // 挂牌时间 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_preference.go b/internal/model/do/house_preference.go new file mode 100644 index 0000000..fe82d10 --- /dev/null +++ b/internal/model/do/house_preference.go @@ -0,0 +1,29 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HousePreference is the golang structure of table house_preference for DAO operations like Where/Data. +type HousePreference struct { + g.Meta `orm:"table:house_preference, do:true"` + Id any // + BudgetMin any // 预算下限(万元) + BudgetMax any // 预算上限(万元) + AreaMin any // 面积下限(平米) + AreaMax any // 面积上限(平米) + Layouts any // 户型偏好JSON + SubwayLines any // 地铁线路JSON + SchoolRequired any // 是否要求学区 + CommuteTarget any // 通勤目标点 + CommuteLimitMin any // 通勤上限(分钟) + RegionPrefer any // 区域偏好JSON + Weights any // 权重JSON + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_price_snapshot.go b/internal/model/do/house_price_snapshot.go new file mode 100644 index 0000000..2139e9a --- /dev/null +++ b/internal/model/do/house_price_snapshot.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HousePriceSnapshot is the golang structure of table house_price_snapshot for DAO operations like Where/Data. +type HousePriceSnapshot struct { + g.Meta `orm:"table:house_price_snapshot, do:true"` + Id any // + ListingId any // 房源ID + CommunityId any // 小区ID + SnapDate any // 快照日期 + ListPrice any // 挂牌价(万元) + DealPrice any // 成交价(万元) + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_school_district.go b/internal/model/do/house_school_district.go new file mode 100644 index 0000000..3d671b7 --- /dev/null +++ b/internal/model/do/house_school_district.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseSchoolDistrict is the golang structure of table house_school_district for DAO operations like Where/Data. +type HouseSchoolDistrict struct { + g.Meta `orm:"table:house_school_district, do:true"` + Id any // + SchoolName any // 学校名 + CommunityId any // 小区ID + DistrictPolygon any // 划片范围GeoJSON + DistrictYear any // 划片年度 + Note any // 备注 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/house_transaction.go b/internal/model/do/house_transaction.go new file mode 100644 index 0000000..06c29d3 --- /dev/null +++ b/internal/model/do/house_transaction.go @@ -0,0 +1,27 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// HouseTransaction is the golang structure of table house_transaction for DAO operations like Where/Data. +type HouseTransaction struct { + g.Meta `orm:"table:house_transaction, do:true"` + Id any // + CommunityId any // 小区ID + BuildingId any // 楼栋ID + Layout any // 户型 + Area any // 面积(平米) + DealPrice any // 成交总价(万元) + DealUnitPrice any // 成交单价(元/平米) + ListDays any // 挂牌到成交天数 + DealDate any // 成交日期 + Source any // 来源 + CreatedAt any // + UpdatedAt any // + DeletedAt any // +} diff --git a/internal/model/do/user.go b/internal/model/do/user.go index aa8cfd4..5d83373 100644 --- a/internal/model/do/user.go +++ b/internal/model/do/user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// User is the golang structure of table user for DAO operations like Where/Data. +// User 是表 user 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type User struct { g.Meta `orm:"table:user, do:true"` Id any // diff --git a/internal/model/do/user_favorite.go b/internal/model/do/user_favorite.go index 6c2398a..75ac836 100644 --- a/internal/model/do/user_favorite.go +++ b/internal/model/do/user_favorite.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// UserFavorite is the golang structure of table user_favorite for DAO operations like Where/Data. +// UserFavorite 是表 user_favorite 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type UserFavorite struct { g.Meta `orm:"table:user_favorite, do:true"` Id any // diff --git a/internal/model/do/user_message.go b/internal/model/do/user_message.go index 26e4e28..aa91b3b 100644 --- a/internal/model/do/user_message.go +++ b/internal/model/do/user_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package do @@ -9,7 +9,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// UserMessage is the golang structure of table user_message for DAO operations like Where/Data. +// UserMessage 是表 user_message 的 Go 结构体,供 DAO 的 Where/Data 等操作使用。 type UserMessage struct { g.Meta `orm:"table:user_message, do:true"` Id any // diff --git a/internal/model/dto/admin_menu.go b/internal/model/dto/admin_menu.go index 358f8b5..acc6add 100644 --- a/internal/model/dto/admin_menu.go +++ b/internal/model/dto/admin_menu.go @@ -1,7 +1,7 @@ -// Package dto defines service boundary types for the admin RBAC domain. +// Package dto 定义 admin RBAC 领域的服务边界类型。 package dto -// RouteItem mirrors the vben admin dynamic-route shape (backend access mode). +// RouteItem 镜像 vben 后台动态路由结构(后端路由模式)。 type RouteItem struct { Name string Path string @@ -10,7 +10,7 @@ type RouteItem struct { Children []*RouteItem } -// RouteMeta is the route metadata consumed by vben. +// RouteMeta 是 vben 消费的路由元数据。 type RouteMeta struct { Title string Icon string diff --git a/internal/model/dto/admin_rbac.go b/internal/model/dto/admin_rbac.go index dd41de7..c196cd8 100644 --- a/internal/model/dto/admin_rbac.go +++ b/internal/model/dto/admin_rbac.go @@ -1,4 +1,4 @@ -// Package dto — RBAC management inputs/outputs. +// Package dto — RBAC 管理的输入输出类型。 package dto type PageQuery struct { @@ -54,7 +54,7 @@ type RoleUpdateInput struct { MenuIds []uint64 } -// MenuNode is the full menu tree node used by menu management. +// MenuNode 是菜单管理使用的完整菜单树节点。 type MenuNode struct { Id uint64 ParentId uint64 diff --git a/internal/model/dto/house.go b/internal/model/dto/house.go new file mode 100644 index 0000000..62ff091 --- /dev/null +++ b/internal/model/dto/house.go @@ -0,0 +1,112 @@ +// Package dto 定义看房模块服务边界的输入/输出结构。 +package dto + +import "service.xpcool.com/internal/model/entity" + +// HouseCommunityInput 小区创建/更新输入(Id=0 表示创建)。 +type HouseCommunityInput struct { + Id uint64 + Name string + Region string + BusinessDistrict string + Address string + Source string + Lng float64 + Lat float64 + PlotRatio float64 + GreenRate float64 + PropertyFee float64 + BuildYear int + Households int + PropertyCompany string + Developer string +} + +// HouseListingInput 房源创建/更新输入(Id=0 表示创建)。 +type HouseListingInput struct { + Id uint64 + CommunityId uint64 + BuildingId uint64 + MatchGroupId uint64 + HouseNo string + Layout string + Orientation string + Decoration string + Source string + SourceHouseId string + SourceUrl string + Area float64 + UsableArea float64 + TotalPrice float64 + UnitPrice float64 + ListPrice float64 + Floor int + TotalFloors int + OnMarketDays int + PriceChangeCount int + Status int + Confidence int + IsBargain int +} + +// HouseListingFilter 房源列表统一筛选条件(管理列表与看板共用)。 +type HouseListingFilter struct { + Page int + Size int + CommunityId uint64 + Keyword string + Layout string + Region string + Source string + PriceMin float64 + PriceMax float64 + AreaMin float64 + AreaMax float64 + Status int + IsBargain int + Confidence int +} + +// HouseListingVO 房源查询结果(内嵌实体 + 关联小区名)。 +type HouseListingVO struct { + entity.HouseListing + CommunityName string `json:"communityName" orm:"community_name"` +} + +// DashboardOverview 看板统计概览。 +type DashboardOverview struct { + CommunityCount int + ListingCount int + BargainCount int + LowConfidence int + AvgUnitPrice float64 + AvgTotalPrice float64 + AvgListDays float64 +} + +// DashboardMapPoint 地图点位。 +type DashboardMapPoint struct { + CommunityId uint64 + Name string + Region string + Lng float64 + Lat float64 + AvgUnitPrice float64 + ListingCount int + BargainCount int +} + +// DashboardTrendPoint 价格趋势点。 +type DashboardTrendPoint struct { + Date string + AvgListPrice float64 + AvgDealPrice float64 +} + +// DashboardRegionAgg 区域聚合。 +type DashboardRegionAgg struct { + Region string + AvgUnitPrice float64 + ListingCount int + BargainCount int +} diff --git a/internal/model/dto/log.go b/internal/model/dto/log.go index 18f6cc6..2f63f16 100644 --- a/internal/model/dto/log.go +++ b/internal/model/dto/log.go @@ -1,10 +1,51 @@ -// Package dto — log monitoring types. +// Package dto — 日志监控相关类型。 package dto -// LogFile describes one server log file. +// LogFile 描述一个服务器日志文件。 type LogFile struct { Name string Path string Size int64 ModTime string } + +// LogQuery 管理员操作日志(admin 系统日志)分页查询参数。 +type LogQuery struct { + Page int + Size int + AdminID uint64 // 按管理员过滤 + Permission string // 按权限码/路径关键字过滤 +} + +// LogItem 一条管理员操作日志记录。 +type LogItem struct { + Id uint64 + AdminID uint64 + Permission string + Method string + Path string + IP string + Param string + DurationMS uint + StatusCode int + CreatedAt string +} + +// LoginLogQuery 管理员登录日志分页查询参数。 +type LoginLogQuery struct { + Page int + Size int + Username string // 按账号过滤 + Status *int // 按结果过滤:1 成功 0 失败,nil 为全部 +} + +// LoginLogItem 一条管理员登录日志记录。 +type LoginLogItem struct { + Id uint64 + Username string + IP string + UserAgent string + Status int + FailReason string + CreatedAt string +} diff --git a/internal/model/entity/admin_login_log.go b/internal/model/entity/admin_login_log.go new file mode 100644 index 0000000..052f0a0 --- /dev/null +++ b/internal/model/entity/admin_login_log.go @@ -0,0 +1,20 @@ +// ================================================================================= +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminLoginLog 是表 admin_login_log 的 Go 结构体。 +type AdminLoginLog struct { + Id uint64 `json:"id" orm:"id" description:""` // 登录账号 + Username string `json:"username" orm:"username" description:""` // 登录账号 + Ip string `json:"ip" orm:"ip" description:""` // 来源 IP + UserAgent string `json:"userAgent" orm:"user_agent" description:""` // 浏览器 UA + Status int `json:"status" orm:"status" description:""` // 1 成功, 0 失败 + FailReason string `json:"failReason" orm:"fail_reason" description:""` // 失败原因 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // +} diff --git a/internal/model/entity/admin_menu.go b/internal/model/entity/admin_menu.go index 42b6f3f..a1fd277 100644 --- a/internal/model/entity/admin_menu.go +++ b/internal/model/entity/admin_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,20 +8,20 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminMenu is the golang structure for table admin_menu. +// AdminMenu 是表 admin_menu 的 Go 结构体。 type AdminMenu struct { - Id uint64 `json:"id" orm:"id" description:""` // - ParentId uint64 `json:"parentId" orm:"parent_id" description:""` // - Name string `json:"name" orm:"name" description:""` // - Icon string `json:"icon" orm:"icon" description:"menu icon (iconify name)"` // menu icon (iconify name) - Type int `json:"type" orm:"type" description:"1 menu,2 api"` // 1 menu,2 api - Path string `json:"path" orm:"path" description:""` // - Component string `json:"component" orm:"component" description:"vue component path, empty for top-level dir"` // vue component path, empty for top-level dir - Permission string `json:"permission" orm:"permission" description:""` // - Sort int `json:"sort" orm:"sort" description:""` // - Status int `json:"status" orm:"status" description:""` // - Hidden bool `json:"hidden" orm:"hidden" description:"0 show,1 hide in menu"` // 0 show,1 hide in menu - CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // - UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // - DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // + Id uint64 `json:"id" orm:"id" description:""` // + ParentId uint64 `json:"parentId" orm:"parent_id" description:""` // + Name string `json:"name" orm:"name" description:""` // + Icon string `json:"icon" orm:"icon" description:"menu icon (iconify name)"` // menu icon (iconify name) + Type int `json:"type" orm:"type" description:"1 menu,2 api"` // 1 menu,2 api + Path string `json:"path" orm:"path" description:""` // + Component string `json:"component" orm:"component" description:"vue component path, empty for top-level dir"` // vue component path, empty for top-level dir + Permission string `json:"permission" orm:"permission" description:""` // + Sort int `json:"sort" orm:"sort" description:""` // + Status int `json:"status" orm:"status" description:""` // + Hidden bool `json:"hidden" orm:"hidden" description:"0 show,1 hide in menu"` // 0 show,1 hide in menu + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // } diff --git a/internal/model/entity/admin_operation_log.go b/internal/model/entity/admin_operation_log.go index 48c9036..4f0aa76 100644 --- a/internal/model/entity/admin_operation_log.go +++ b/internal/model/entity/admin_operation_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminOperationLog is the golang structure for table admin_operation_log. +// AdminOperationLog 是表 admin_operation_log 的 Go 结构体。 type AdminOperationLog struct { Id uint64 `json:"id" orm:"id" description:""` // AdminUserId uint64 `json:"adminUserId" orm:"admin_user_id" description:""` // diff --git a/internal/model/entity/admin_role.go b/internal/model/entity/admin_role.go index eda5d0c..17e0dba 100644 --- a/internal/model/entity/admin_role.go +++ b/internal/model/entity/admin_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminRole is the golang structure for table admin_role. +// AdminRole 是表 admin_role 的 Go 结构体。 type AdminRole struct { Id uint64 `json:"id" orm:"id" description:""` // Code string `json:"code" orm:"code" description:""` // diff --git a/internal/model/entity/admin_role_menu.go b/internal/model/entity/admin_role_menu.go index 4e0796d..50b105f 100644 --- a/internal/model/entity/admin_role_menu.go +++ b/internal/model/entity/admin_role_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminRoleMenu is the golang structure for table admin_role_menu. +// AdminRoleMenu 是表 admin_role_menu 的 Go 结构体。 type AdminRoleMenu struct { Id uint64 `json:"id" orm:"id" description:""` // RoleId uint64 `json:"roleId" orm:"role_id" description:""` // diff --git a/internal/model/entity/admin_user.go b/internal/model/entity/admin_user.go index 2823d66..eaba73f 100644 --- a/internal/model/entity/admin_user.go +++ b/internal/model/entity/admin_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminUser is the golang structure for table admin_user. +// AdminUser 是表 admin_user 的 Go 结构体。 type AdminUser struct { Id uint64 `json:"id" orm:"id" description:""` // Username string `json:"username" orm:"username" description:""` // diff --git a/internal/model/entity/admin_user_role.go b/internal/model/entity/admin_user_role.go index 002cdf0..e157fde 100644 --- a/internal/model/entity/admin_user_role.go +++ b/internal/model/entity/admin_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AdminUserRole is the golang structure for table admin_user_role. +// AdminUserRole 是表 admin_user_role 的 Go 结构体。 type AdminUserRole struct { Id uint64 `json:"id" orm:"id" description:""` // AdminUserId uint64 `json:"adminUserId" orm:"admin_user_id" description:""` // diff --git a/internal/model/entity/auth_refresh_session.go b/internal/model/entity/auth_refresh_session.go index 3c3c686..f0b574b 100644 --- a/internal/model/entity/auth_refresh_session.go +++ b/internal/model/entity/auth_refresh_session.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// AuthRefreshSession is the golang structure for table auth_refresh_session. +// AuthRefreshSession 是表 auth_refresh_session 的 Go 结构体。 type AuthRefreshSession struct { Id uint64 `json:"id" orm:"id" description:""` // SubjectId uint64 `json:"subjectId" orm:"subject_id" description:"????????? ID"` // ????????? ID diff --git a/internal/model/entity/content.go b/internal/model/entity/content.go index 5491afd..80f7a4d 100644 --- a/internal/model/entity/content.go +++ b/internal/model/entity/content.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// Content is the golang structure for table content. +// Content 是表 content 的 Go 结构体。 type Content struct { Id uint64 `json:"id" orm:"id" description:""` // Title string `json:"title" orm:"title" description:""` // diff --git a/internal/model/entity/house_building.go b/internal/model/entity/house_building.go new file mode 100644 index 0000000..bf853a1 --- /dev/null +++ b/internal/model/entity/house_building.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseBuilding is the golang structure for table house_building. +type HouseBuilding struct { + Id uint64 `json:"id" orm:"id" description:""` // + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + BuildingNo string `json:"buildingNo" orm:"building_no" description:"栋号"` // 栋号 + Units int `json:"units" orm:"units" description:"单元数"` // 单元数 + TotalFloors int `json:"totalFloors" orm:"total_floors" description:"总楼层"` // 总楼层 + ElevatorCount int `json:"elevatorCount" orm:"elevator_count" description:"电梯数"` // 电梯数 + LadderRatio string `json:"ladderRatio" orm:"ladder_ratio" description:"梯户比"` // 梯户比 + BuildingType string `json:"buildingType" orm:"building_type" description:"板楼/塔楼"` // 板楼/塔楼 + Lng float64 `json:"lng" orm:"lng" description:"楼栋经度"` // 楼栋经度 + Lat float64 `json:"lat" orm:"lat" description:"楼栋纬度"` // 楼栋纬度 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_community.go b/internal/model/entity/house_community.go new file mode 100644 index 0000000..5aafbcc --- /dev/null +++ b/internal/model/entity/house_community.go @@ -0,0 +1,27 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseCommunity is the golang structure for table house_community. +type HouseCommunity struct { + Id uint64 `json:"id" orm:"id" description:""` // + Name string `json:"name" orm:"name" description:"小区/楼盘名"` // 小区/楼盘名 + Region string `json:"region" orm:"region" description:"区县(云岩/南明/观山湖/花溪等)"` // 区县(云岩/南明/观山湖/花溪等) + BusinessDistrict string `json:"businessDistrict" orm:"business_district" description:"板块"` // 板块 + Address string `json:"address" orm:"address" description:"地址"` // 地址 + Lng float64 `json:"lng" orm:"lng" description:"经度(GCJ-02)"` // 经度(GCJ-02) + Lat float64 `json:"lat" orm:"lat" description:"纬度(GCJ-02)"` // 纬度(GCJ-02) + BuildYear int `json:"buildYear" orm:"build_year" description:"建成年份"` // 建成年份 + Households int `json:"households" orm:"households" description:"总户数"` // 总户数 + PlotRatio float64 `json:"plotRatio" orm:"plot_ratio" description:"容积率"` // 容积率 + GreenRate float64 `json:"greenRate" orm:"green_rate" description:"绿化率"` // 绿化率 + PropertyCompany string `json:"propertyCompany" orm:"property_company" description:"物业公司"` // 物业公司 + PropertyFee float64 `json:"propertyFee" orm:"property_fee" description:"物业费(元/月/平米)"` // 物业费(元/月/平米) + Developer string `json:"developer" orm:"developer" description:"开发商"` // 开发商 + Source string `json:"source" orm:"source" description:"数据来源"` // 数据来源 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_community_facility.go b/internal/model/entity/house_community_facility.go new file mode 100644 index 0000000..e20317b --- /dev/null +++ b/internal/model/entity/house_community_facility.go @@ -0,0 +1,17 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseCommunityFacility is the golang structure for table house_community_facility. +type HouseCommunityFacility struct { + Id uint64 `json:"id" orm:"id" description:""` // + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + FacilityId uint64 `json:"facilityId" orm:"facility_id" description:"配套ID"` // 配套ID + Distance int `json:"distance" orm:"distance" description:"距离(米)"` // 距离(米) + CommuteMinutes int `json:"commuteMinutes" orm:"commute_minutes" description:"通勤分钟"` // 通勤分钟 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_facility.go b/internal/model/entity/house_facility.go new file mode 100644 index 0000000..3561a5e --- /dev/null +++ b/internal/model/entity/house_facility.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseFacility is the golang structure for table house_facility. +type HouseFacility struct { + Id uint64 `json:"id" orm:"id" description:""` // + Name string `json:"name" orm:"name" description:"配套名称"` // 配套名称 + Type string `json:"type" orm:"type" description:"地铁/学校/医院/商圈"` // 地铁/学校/医院/商圈 + Lng float64 `json:"lng" orm:"lng" description:"经度"` // 经度 + Lat float64 `json:"lat" orm:"lat" description:"纬度"` // 纬度 + Line string `json:"line" orm:"line" description:"地铁线路"` // 地铁线路 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_listing.go b/internal/model/entity/house_listing.go new file mode 100644 index 0000000..c85ec0d --- /dev/null +++ b/internal/model/entity/house_listing.go @@ -0,0 +1,36 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseListing is the golang structure for table house_listing. +type HouseListing struct { + Id uint64 `json:"id" orm:"id" description:""` // + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + BuildingId uint64 `json:"buildingId" orm:"building_id" description:"楼栋ID"` // 楼栋ID + HouseNo string `json:"houseNo" orm:"house_no" description:"房号"` // 房号 + Layout string `json:"layout" orm:"layout" description:"户型(如3室2厅)"` // 户型(如3室2厅) + Area float64 `json:"area" orm:"area" description:"建筑面积(平米)"` // 建筑面积(平米) + UsableArea float64 `json:"usableArea" orm:"usable_area" description:"套内面积(平米)"` // 套内面积(平米) + Orientation string `json:"orientation" orm:"orientation" description:"朝向"` // 朝向 + Floor int `json:"floor" orm:"floor" description:"所在楼层"` // 所在楼层 + TotalFloors int `json:"totalFloors" orm:"total_floors" description:"总楼层"` // 总楼层 + Decoration string `json:"decoration" orm:"decoration" description:"装修"` // 装修 + TotalPrice float64 `json:"totalPrice" orm:"total_price" description:"总价(万元)"` // 总价(万元) + UnitPrice float64 `json:"unitPrice" orm:"unit_price" description:"单价(元/平米)"` // 单价(元/平米) + ListPrice float64 `json:"listPrice" orm:"list_price" description:"挂牌价(万元)"` // 挂牌价(万元) + Source string `json:"source" orm:"source" description:"来源平台"` // 来源平台 + SourceHouseId string `json:"sourceHouseId" orm:"source_house_id" description:"平台侧房源ID"` // 平台侧房源ID + SourceUrl string `json:"sourceUrl" orm:"source_url" description:"房源链接"` // 房源链接 + MatchGroupId uint64 `json:"matchGroupId" orm:"match_group_id" description:"疑似同房源分组(跨平台软关联)"` // 疑似同房源分组(跨平台软关联) + OnMarketDays int `json:"onMarketDays" orm:"on_market_days" description:"挂牌天数"` // 挂牌天数 + PriceChangeCount int `json:"priceChangeCount" orm:"price_change_count" description:"调价次数"` // 调价次数 + Status int `json:"status" orm:"status" description:"1在售 2下架 3成交"` // 1在售 2下架 3成交 + Confidence int `json:"confidence" orm:"confidence" description:"可信度 0正常 1低可信"` // 可信度 0正常 1低可信 + IsBargain int `json:"isBargain" orm:"is_bargain" description:"笋盘标记 0否 1是"` // 笋盘标记 0否 1是 + ListingTime string `json:"listingTime" orm:"listing_time" description:"挂牌时间"` // 挂牌时间 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_preference.go b/internal/model/entity/house_preference.go new file mode 100644 index 0000000..27c1b81 --- /dev/null +++ b/internal/model/entity/house_preference.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HousePreference is the golang structure for table house_preference. +type HousePreference struct { + Id uint64 `json:"id" orm:"id" description:""` // + BudgetMin float64 `json:"budgetMin" orm:"budget_min" description:"预算下限(万元)"` // 预算下限(万元) + BudgetMax float64 `json:"budgetMax" orm:"budget_max" description:"预算上限(万元)"` // 预算上限(万元) + AreaMin float64 `json:"areaMin" orm:"area_min" description:"面积下限(平米)"` // 面积下限(平米) + AreaMax float64 `json:"areaMax" orm:"area_max" description:"面积上限(平米)"` // 面积上限(平米) + Layouts string `json:"layouts" orm:"layouts" description:"户型偏好JSON"` // 户型偏好JSON + SubwayLines string `json:"subwayLines" orm:"subway_lines" description:"地铁线路JSON"` // 地铁线路JSON + SchoolRequired int `json:"schoolRequired" orm:"school_required" description:"是否要求学区"` // 是否要求学区 + CommuteTarget string `json:"commuteTarget" orm:"commute_target" description:"通勤目标点"` // 通勤目标点 + CommuteLimitMin int `json:"commuteLimitMin" orm:"commute_limit_min" description:"通勤上限(分钟)"` // 通勤上限(分钟) + RegionPrefer string `json:"regionPrefer" orm:"region_prefer" description:"区域偏好JSON"` // 区域偏好JSON + Weights string `json:"weights" orm:"weights" description:"权重JSON"` // 权重JSON + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_price_snapshot.go b/internal/model/entity/house_price_snapshot.go new file mode 100644 index 0000000..bdc05db --- /dev/null +++ b/internal/model/entity/house_price_snapshot.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HousePriceSnapshot is the golang structure for table house_price_snapshot. +type HousePriceSnapshot struct { + Id uint64 `json:"id" orm:"id" description:""` // + ListingId uint64 `json:"listingId" orm:"listing_id" description:"房源ID"` // 房源ID + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + SnapDate string `json:"snapDate" orm:"snap_date" description:"快照日期"` // 快照日期 + ListPrice float64 `json:"listPrice" orm:"list_price" description:"挂牌价(万元)"` // 挂牌价(万元) + DealPrice float64 `json:"dealPrice" orm:"deal_price" description:"成交价(万元)"` // 成交价(万元) + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_school_district.go b/internal/model/entity/house_school_district.go new file mode 100644 index 0000000..1d33eee --- /dev/null +++ b/internal/model/entity/house_school_district.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseSchoolDistrict is the golang structure for table house_school_district. +type HouseSchoolDistrict struct { + Id uint64 `json:"id" orm:"id" description:""` // + SchoolName string `json:"schoolName" orm:"school_name" description:"学校名"` // 学校名 + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + DistrictPolygon string `json:"districtPolygon" orm:"district_polygon" description:"划片范围GeoJSON"` // 划片范围GeoJSON + DistrictYear int `json:"districtYear" orm:"district_year" description:"划片年度"` // 划片年度 + Note string `json:"note" orm:"note" description:"备注"` // 备注 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/house_transaction.go b/internal/model/entity/house_transaction.go new file mode 100644 index 0000000..b0ed9a0 --- /dev/null +++ b/internal/model/entity/house_transaction.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// HouseTransaction is the golang structure for table house_transaction. +type HouseTransaction struct { + Id uint64 `json:"id" orm:"id" description:""` // + CommunityId uint64 `json:"communityId" orm:"community_id" description:"小区ID"` // 小区ID + BuildingId uint64 `json:"buildingId" orm:"building_id" description:"楼栋ID"` // 楼栋ID + Layout string `json:"layout" orm:"layout" description:"户型"` // 户型 + Area float64 `json:"area" orm:"area" description:"面积(平米)"` // 面积(平米) + DealPrice float64 `json:"dealPrice" orm:"deal_price" description:"成交总价(万元)"` // 成交总价(万元) + DealUnitPrice float64 `json:"dealUnitPrice" orm:"deal_unit_price" description:"成交单价(元/平米)"` // 成交单价(元/平米) + ListDays int `json:"listDays" orm:"list_days" description:"挂牌到成交天数"` // 挂牌到成交天数 + DealDate string `json:"dealDate" orm:"deal_date" description:"成交日期"` // 成交日期 + Source string `json:"source" orm:"source" description:"来源"` // 来源 + CreatedAt string `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt string `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt string `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/user.go b/internal/model/entity/user.go index f66dc8e..308b859 100644 --- a/internal/model/entity/user.go +++ b/internal/model/entity/user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// User is the golang structure for table user. +// User 是表 user 的 Go 结构体。 type User struct { Id uint64 `json:"id" orm:"id" description:""` // UnionId string `json:"unionId" orm:"union_id" description:""` // diff --git a/internal/model/entity/user_favorite.go b/internal/model/entity/user_favorite.go index ba4cd7b..648b5b7 100644 --- a/internal/model/entity/user_favorite.go +++ b/internal/model/entity/user_favorite.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// UserFavorite is the golang structure for table user_favorite. +// UserFavorite 是表 user_favorite 的 Go 结构体。 type UserFavorite struct { Id uint64 `json:"id" orm:"id" description:""` // UserId uint64 `json:"userId" orm:"user_id" description:""` // diff --git a/internal/model/entity/user_message.go b/internal/model/entity/user_message.go index 0f314ce..34f611a 100644 --- a/internal/model/entity/user_message.go +++ b/internal/model/entity/user_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// 本文件由 GoFrame CLI 工具生成并维护,请勿编辑。 // ================================================================================= package entity @@ -8,7 +8,7 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// UserMessage is the golang structure for table user_message. +// UserMessage 是表 user_message 的 Go 结构体。 type UserMessage struct { Id uint64 `json:"id" orm:"id" description:""` // UserId uint64 `json:"userId" orm:"user_id" description:""` // diff --git a/internal/service/admin/admin/admin.go b/internal/service/admin/admin/admin.go deleted file mode 100644 index 2ce5c8e..0000000 --- a/internal/service/admin/admin/admin.go +++ /dev/null @@ -1,158 +0,0 @@ -package admin - -import ( - "context" - - "github.com/gogf/gf/v2/errors/gerror" - "golang.org/x/crypto/bcrypt" - - "service.xpcool.com/internal/consts" - "service.xpcool.com/internal/dao" - "service.xpcool.com/internal/library/response" - "service.xpcool.com/internal/model/do" - "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/model/entity" -) - -// IAdminManage 管理后台管理员账号。 -type IAdminManage interface { - List(context.Context, dto.PageQuery) ([]*dto.AdminItem, int, error) - Create(context.Context, dto.AdminCreateInput) (uint64, error) - Update(context.Context, dto.AdminUpdateInput) error - ResetPassword(context.Context, uint64, string) error - Delete(context.Context, uint64) error -} - -type adminManage struct{} - -var localAdminManage IAdminManage - -func NewAdminManage() IAdminManage { return &adminManage{} } - -func AdminManage() IAdminManage { - if localAdminManage == nil { - panic("AdminManage implementation not registered") - } - return localAdminManage -} -func RegisterAdminManage(i IAdminManage) { localAdminManage = i } - -// ---------------------- Admin manage ---------------------- - -func (s *adminManage) List(ctx context.Context, q dto.PageQuery) ([]*dto.AdminItem, int, error) { - total, err := dao.AdminUser.Ctx(ctx).Count() - if err != nil { - return nil, 0, gerror.Wrap(err, "count admins") - } - if total == 0 { - return nil, 0, nil - } - var list []entity.AdminUser - if err = dao.AdminUser.Ctx(ctx).Page(q.Page, q.Size).OrderDesc("id").Scan(&list); err != nil { - return nil, 0, gerror.Wrap(err, "query admins") - } - items := make([]*dto.AdminItem, 0, len(list)) - for i := range list { - a := &list[i] - roleIds, roleNames, err := s.roles(ctx, a.Id) - if err != nil { - return nil, 0, err - } - items = append(items, &dto.AdminItem{ - Id: a.Id, Username: a.Username, Nickname: a.Nickname, Status: a.Status, - RoleIds: roleIds, RoleNames: roleNames, - CreatedAt: a.CreatedAt.Layout("2006-01-02 15:04:05"), - }) - } - return items, total, nil -} - -func (s *adminManage) roles(ctx context.Context, adminID uint64) ([]uint64, []string, error) { - var rels []entity.AdminUserRole - if err := dao.AdminUserRole.Ctx(ctx).Where(do.AdminUserRole{AdminUserId: adminID}).Scan(&rels); err != nil { - return nil, nil, gerror.Wrap(err, "query admin roles") - } - roleIds := make([]uint64, 0, len(rels)) - for _, r := range rels { - roleIds = append(roleIds, r.RoleId) - } - if len(roleIds) == 0 { - return roleIds, nil, nil - } - var roles []entity.AdminRole - if err := dao.AdminRole.Ctx(ctx).WhereIn("id", roleIds).Scan(&roles); err != nil { - return nil, nil, gerror.Wrap(err, "query roles") - } - names := make([]string, 0, len(roles)) - for _, r := range roles { - names = append(names, r.Name) - } - return roleIds, names, nil -} - -func (s *adminManage) Create(ctx context.Context, in dto.AdminCreateInput) (uint64, error) { - count, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Username: in.Username}).Count() - if err != nil { - return 0, gerror.Wrap(err, "check username") - } - if count > 0 { - return 0, response.Error(consts.CodeInvalidParam, "username already exists") - } - hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost) - if err != nil { - return 0, gerror.Wrap(err, "hash password") - } - id, err := dao.AdminUser.Ctx(ctx).Data(do.AdminUser{ - Username: in.Username, PasswordHash: string(hash), Nickname: in.Nickname, Status: 1, - }).InsertAndGetId() - if err != nil { - return 0, gerror.Wrap(err, "insert admin") - } - if err = bindAdminRoles(ctx, uint64(id), in.RoleIds); err != nil { - return 0, err - } - return uint64(id), nil -} - -func (s *adminManage) Update(ctx context.Context, in dto.AdminUpdateInput) error { - data := do.AdminUser{Nickname: in.Nickname, Status: in.Status} - if _, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: in.Id}).Data(data).Update(); err != nil { - return gerror.Wrap(err, "update admin") - } - return bindAdminRoles(ctx, in.Id, in.RoleIds) -} - -func (s *adminManage) ResetPassword(ctx context.Context, id uint64, password string) error { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - return gerror.Wrap(err, "hash password") - } - if _, err = dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: id}).Data(do.AdminUser{PasswordHash: string(hash)}).Update(); err != nil { - return gerror.Wrap(err, "reset password") - } - return nil -} - -func (s *adminManage) Delete(ctx context.Context, id uint64) error { - if _, err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: id}).Delete(); err != nil { - return gerror.Wrap(err, "delete admin") - } - // 关联关系物理删除,避免唯一键残留。 - if _, err := dao.AdminUserRole.Ctx(ctx).Unscoped().Where(do.AdminUserRole{AdminUserId: id}).Delete(); err != nil { - return gerror.Wrap(err, "delete admin role bindings") - } - return nil -} - -// bindAdminRoles 全量重绑管理员-角色关系。 -func bindAdminRoles(ctx context.Context, adminID uint64, roleIds []uint64) error { - if _, err := dao.AdminUserRole.Ctx(ctx).Unscoped().Where(do.AdminUserRole{AdminUserId: adminID}).Delete(); err != nil { - return gerror.Wrap(err, "clear admin roles") - } - for _, rid := range roleIds { - if _, err := dao.AdminUserRole.Ctx(ctx).Data(do.AdminUserRole{AdminUserId: adminID, RoleId: rid}).Insert(); err != nil { - return gerror.Wrap(err, "bind admin role") - } - } - return nil -} diff --git a/internal/service/admin/system/audit/audit.go b/internal/service/admin/system/audit/audit.go deleted file mode 100644 index 6c20b20..0000000 --- a/internal/service/admin/system/audit/audit.go +++ /dev/null @@ -1,42 +0,0 @@ -package audit - -import ( - "context" - "github.com/gogf/gf/v2/os/glog" - "service.xpcool.com/internal/dao" - "service.xpcool.com/internal/model/do" -) - -// AuditEvent 管理端操作审计事件。 -type AuditEvent struct { - AdminID uint64 - Permission, Method, Path, IP, Param string - DurationMS, StatusCode int -} - -// IAdminAudit 管理端操作审计服务接口。 -type IAdminAudit interface { - Record(context.Context, AuditEvent) -} - -var localAdminAudit IAdminAudit - -func AdminAudit() IAdminAudit { - if localAdminAudit == nil { - panic("AdminAudit implementation not registered") - } - return localAdminAudit -} -func RegisterAdminAudit(i IAdminAudit) { localAdminAudit = i } - -type adminAudit struct{} - -func NewAdminAudit() IAdminAudit { return &adminAudit{} } - -// Record 采用尽力而为策略:审计落库失败会记录系统日志,但不影响原业务请求结果。 -func (a *adminAudit) Record(ctx context.Context, e AuditEvent) { - _, err := dao.AdminOperationLog.Ctx(ctx).Data(do.AdminOperationLog{AdminUserId: e.AdminID, Permission: e.Permission, Method: e.Method, Path: e.Path, Ip: e.IP, RequestParam: e.Param, DurationMs: e.DurationMS, StatusCode: e.StatusCode}).Insert() - if err != nil { - glog.Error(ctx, err) - } -} diff --git a/internal/service/admin/system/auth/auth.go b/internal/service/admin/system/auth/auth.go deleted file mode 100644 index 07c21d0..0000000 --- a/internal/service/admin/system/auth/auth.go +++ /dev/null @@ -1,182 +0,0 @@ -package auth - -import ( - "context" - "regexp" - "strings" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/os/gtime" - "golang.org/x/crypto/bcrypt" - "service.xpcool.com/internal/consts" - "service.xpcool.com/internal/dao" - "service.xpcool.com/internal/library/jwt" - "service.xpcool.com/internal/library/response" - "service.xpcool.com/internal/model/do" - "service.xpcool.com/internal/model/dto" - "service.xpcool.com/internal/model/entity" -) - -// IAdminAuth 管理端认证服务接口。 -type IAdminAuth interface { - Login(context.Context, dto.AdminLoginInput) (*dto.TokenPair, uint64, error) - Refresh(context.Context, string) (*dto.TokenPair, uint64, error) - HasPermission(context.Context, uint64, string) (bool, error) - Info(context.Context, uint64) (*dto.AdminInfo, error) - Codes(context.Context, uint64) ([]string, error) - // PermissionForPath resolves the permission code required by an endpoint - // from admin_menu (type=2 rows) by matching " ". - PermissionForPath(context.Context, string, string) (string, error) -} - -var localAdminAuth IAdminAuth - -func AdminAuth() IAdminAuth { - if localAdminAuth == nil { - panic("AdminAuth implementation not registered") - } - return localAdminAuth -} -func RegisterAdminAuth(i IAdminAuth) { localAdminAuth = i } - -type adminAuth struct{ tokens *jwt.Service } - -func NewAdminAuth(tokens *jwt.Service) IAdminAuth { return &adminAuth{tokens} } -func (s *adminAuth) Login(ctx context.Context, in dto.AdminLoginInput) (*dto.TokenPair, uint64, error) { - // 管理端只允许账号密码登录,状态异常或密码错误均返回统一错误,避免枚举账号。 - var a entity.AdminUser - if err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Username: in.Username}).Scan(&a); err != nil { - return nil, 0, gerror.Wrap(err, "query administrator") - } - if a.Id == 0 { - return nil, 0, response.Error(consts.CodeAdminNotFound, "administrator not found") - } - if a.Status != 1 || bcrypt.CompareHashAndPassword([]byte(a.PasswordHash), []byte(in.Password)) != nil { - return nil, 0, response.Error(consts.CodeAdminPasswordWrong, "username or password incorrect") - } - // 通过 issue 签发令牌对并把刷新令牌 JTI 落库,保证后续可刷新、可撤销。 - return s.issue(ctx, a.Id, "") -} - -// Refresh 用有效的刷新令牌轮换管理员令牌对:校验 → 撤销旧会话 → 签发新对并落库。 -func (s *adminAuth) Refresh(ctx context.Context, refresh string) (*dto.TokenPair, uint64, error) { - c, err := s.tokens.Parse(refresh, "refresh", "admin") - if err != nil { - return nil, 0, response.Error(consts.CodeUnauthorized, "invalid refresh token") - } - // 单次使用:撤销旧刷新会话,防止令牌被重复使用。 - if _, err = dao.AuthRefreshSession.Ctx(ctx).Where(do.AuthRefreshSession{Jti: c.JTI}).WhereNull("revoked_at").Data(do.AuthRefreshSession{RevokedAt: gtime.Now()}).Update(); err != nil { - return nil, 0, gerror.Wrap(err, "撤销旧刷新令牌失败") - } - return s.issue(ctx, c.Subject, c.Terminal) -} - -// issue 签发令牌对并把刷新令牌的 JTI 落库,支持撤销与设备会话追踪。 -func (s *adminAuth) issue(ctx context.Context, id uint64, terminal string) (*dto.TokenPair, uint64, error) { - access, refresh, exp, err := s.tokens.Issue(id, "admin", terminal) - if err != nil { - return nil, 0, gerror.Wrap(err, "issue token") - } - claims, err := s.tokens.Parse(refresh, "refresh", "admin") - if err != nil { - return nil, 0, gerror.Wrap(err, "解析新刷新令牌失败") - } - if _, err = dao.AuthRefreshSession.Ctx(ctx).Data(do.AuthRefreshSession{ - SubjectId: id, Scope: "admin", Terminal: terminal, - Jti: claims.JTI, ExpiredAt: gtime.NewFromTimeStamp(claims.ExpireAt), - }).Insert(); err != nil { - return nil, 0, gerror.Wrap(err, "保存刷新令牌会话失败") - } - return &dto.TokenPair{AccessToken: access, RefreshToken: refresh, ExpiresIn: exp}, id, nil -} - -func (s *adminAuth) HasPermission(ctx context.Context, adminID uint64, permission string) (bool, error) { - // 多角色权限通过管理员-角色-菜单三表关联查询,菜单中的 permission 即接口权限标识。 - count, err := dao.AdminUserRole.Ctx(ctx).As("ur").LeftJoin("admin_role_menu rm", "ur.role_id=rm.role_id").LeftJoin("admin_menu m", "rm.menu_id=m.id").Where("ur.admin_user_id", adminID).Where("m.permission", permission).Where("m.status", 1).Count() - if err != nil { - return false, gerror.Wrap(err, "check permission") - } - return count > 0, nil -} -func (s *adminAuth) Info(ctx context.Context, adminID uint64) (*dto.AdminInfo, error) { - var a entity.AdminUser - if err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Id: adminID}).Scan(&a); err != nil { - return nil, gerror.Wrap(err, "query admin info") - } - if a.Id == 0 { - return nil, response.Error(consts.CodeAdminNotFound, "administrator not found") - } - roles, err := s.roleCodes(ctx, adminID) - if err != nil { - return nil, err - } - return &dto.AdminInfo{AdminID: a.Id, Username: a.Username, Nickname: a.Nickname, Roles: roles}, nil -} -func (s *adminAuth) Codes(ctx context.Context, adminID uint64) ([]string, error) { - // 权限码 = 该管理员所有启用角色绑定的菜单 permission(含菜单与按钮级), - // 同时用作 vben 前端按钮权限码与后端 X-Permission 校验标识。 - list, err := dao.AdminUserRole.Ctx(ctx).As("ur").LeftJoin("admin_role_menu rm", "ur.role_id=rm.role_id").LeftJoin("admin_menu m", "rm.menu_id=m.id").Where("ur.admin_user_id", adminID).Where("m.status", 1).WhereGT("m.permission", "").Fields("DISTINCT m.permission").Array() - if err != nil { - return nil, gerror.Wrap(err, "query access codes") - } - codes := make([]string, 0, len(list)) - for _, v := range list { - codes = append(codes, v.String()) - } - return codes, nil -} -func (s *adminAuth) roleCodes(ctx context.Context, adminID uint64) ([]string, error) { - list, err := dao.AdminUserRole.Ctx(ctx).As("ur").LeftJoin("admin_role r", "ur.role_id=r.id").Where("ur.admin_user_id", adminID).Where("r.status", 1).Fields("DISTINCT r.code").Array() - if err != nil { - return nil, gerror.Wrap(err, "query admin roles") - } - codes := make([]string, 0, len(list)) - for _, v := range list { - codes = append(codes, v.String()) - } - return codes, nil -} -func (s *adminAuth) PermissionForPath(ctx context.Context, method, path string) (string, error) { - // 从菜单表 type=2(按钮/API)行反查当前请求所需的权限码。 - // 未配置映射的接口一律拒绝访问(返回空则中间件拦截)。 - var list []entity.AdminMenu - if err := dao.AdminMenu.Ctx(ctx).Where(do.AdminMenu{Type: 2, Status: 1}).Scan(&list); err != nil { - return "", gerror.Wrap(err, "query permission mappings") - } - req := method + " " + path - for _, m := range list { - if matchRoute(m.Path, req) { - return m.Permission, nil - } - } - return "", nil -} - -// matchRoute 匹配 "METHOD /path" 模式,{id} 视为动态段。 -func matchRoute(pattern, req string) bool { - if pattern == "" { - return false - } - var b strings.Builder - b.WriteByte('^') - for i := 0; i < len(pattern); i++ { - c := pattern[i] - switch { - case c == '{': - if j := strings.IndexByte(pattern[i:], '}'); j > 0 { - b.WriteString("[^/]+") - i += j - } else { - b.WriteString(regexp.QuoteMeta(string(c))) - } - case c == ' ' || c == '/' || c == '-' || c == '_' || c == '.' || - (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'): - b.WriteByte(c) - default: - b.WriteString(regexp.QuoteMeta(string(c))) - } - } - b.WriteByte('$') - ok, err := regexp.MatchString(b.String(), req) - return err == nil && ok -} diff --git a/internal/service/admin/system/login_log/login_log.go b/internal/service/admin/system/login_log/login_log.go new file mode 100644 index 0000000..22448aa --- /dev/null +++ b/internal/service/admin/system/login_log/login_log.go @@ -0,0 +1,83 @@ +// Package admin_system_login_log 提供管理员登录日志服务:写(Record,登录成功/失败落库)与读(List,分页查询)。 +package admin_system_login_log + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/model/do" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/model/entity" +) + +// LoginEvent 管理员登录日志事件。 +type LoginEvent struct { + Username string + IP string + UserAgent string + Status int // 1 成功, 0 失败 + FailReason string // 失败原因(成功时为空) +} + +// IAdminLoginLog 登录日志服务接口:审计写入 + 分页查询。 +type IAdminLoginLog interface { + Record(context.Context, LoginEvent) error + List(context.Context, dto.LoginLogQuery) ([]*dto.LoginLogItem, int, error) +} + +var localAdminLoginLog IAdminLoginLog + +type adminLoginLog struct{} + +func AdminLoginLog() IAdminLoginLog { + if localAdminLoginLog == nil { + panic("AdminLoginLog implementation not registered") + } + return localAdminLoginLog +} +func RegisterAdminLoginLog(i IAdminLoginLog) { localAdminLoginLog = i } + +func NewAdminLoginLog() IAdminLoginLog { return &adminLoginLog{} } + +// Record 写入一条登录日志(成功或失败)。 +func (s *adminLoginLog) Record(ctx context.Context, e LoginEvent) error { + _, err := dao.AdminLoginLog.Ctx(ctx).Data(do.AdminLoginLog{ + Username: e.Username, Ip: e.IP, UserAgent: e.UserAgent, + Status: e.Status, FailReason: e.FailReason, + }).Insert() + return gerror.Wrap(err, "insert login log") +} + +// List 分页查询管理员登录日志,支持账号与结果过滤(Status 为 nil 时不过滤)。 +func (s *adminLoginLog) List(ctx context.Context, q dto.LoginLogQuery) ([]*dto.LoginLogItem, int, error) { + m := dao.AdminLoginLog.Ctx(ctx) + if q.Username != "" { + m = m.WhereLike("username", "%"+q.Username+"%") + } + if q.Status != nil { + m = m.Where(do.AdminLoginLog{Status: *q.Status}) + } + total, err := m.Count() + if err != nil { + return nil, 0, gerror.Wrap(err, "count login logs") + } + if total == 0 { + return nil, 0, nil + } + var list []entity.AdminLoginLog + if err = m.Page(q.Page, q.Size).OrderDesc("id").Scan(&list); err != nil { + return nil, 0, gerror.Wrap(err, "query login logs") + } + items := make([]*dto.LoginLogItem, 0, len(list)) + for i := range list { + l := &list[i] + items = append(items, &dto.LoginLogItem{ + Id: l.Id, Username: l.Username, IP: l.Ip, UserAgent: l.UserAgent, + Status: l.Status, FailReason: l.FailReason, + CreatedAt: l.CreatedAt.Layout("2006-01-02 15:04:05"), + }) + } + return items, total, nil +} diff --git a/internal/service/admin/system/menu_manage/menu_manage.go b/internal/service/admin/system/menu_manage/menu_manage.go index b3302ad..8e30b6e 100644 --- a/internal/service/admin/system/menu_manage/menu_manage.go +++ b/internal/service/admin/system/menu_manage/menu_manage.go @@ -1,4 +1,4 @@ -package menu_manage +package admin_system_menu_manage import ( "context" diff --git a/internal/service/admin/system/role/role.go b/internal/service/admin/system/role/role.go index 5cb7b97..4dc4307 100644 --- a/internal/service/admin/system/role/role.go +++ b/internal/service/admin/system/role/role.go @@ -1,4 +1,4 @@ -package role +package admin_system_role import ( "context" diff --git a/internal/service/house/community/community.go b/internal/service/house/community/community.go new file mode 100644 index 0000000..cfea671 --- /dev/null +++ b/internal/service/house/community/community.go @@ -0,0 +1,113 @@ +// Package house_community 提供楼盘/小区领域服务。 +package house_community + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/model/do" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/model/entity" +) + +// ICommunity 小区/楼盘领域服务接口。 +type ICommunity interface { + List(context.Context, int, int, string, string) ([]entity.HouseCommunity, int, error) + Create(context.Context, dto.HouseCommunityInput) (uint64, error) + Update(context.Context, dto.HouseCommunityInput) error + Delete(context.Context, uint64) error +} + +type community struct{} + +var localCommunity ICommunity + +func NewCommunity() ICommunity { return &community{} } + +// Community 返回已注册的小区服务实现。 +func Community() ICommunity { + if localCommunity == nil { + panic("Community implementation not registered") + } + return localCommunity +} + +// RegisterCommunity 注册小区服务实现。 +func RegisterCommunity(i ICommunity) { localCommunity = i } + +// List 分页查询小区,支持关键字(名称)与区域过滤。 +func (s *community) List(ctx context.Context, page, size int, keyword, region string) ([]entity.HouseCommunity, int, error) { + m := dao.HouseCommunity.Ctx(ctx) + if keyword != "" { + m = m.WhereLike("name", "%"+keyword+"%") + } + if region != "" { + m = m.Where("region", region) + } + total, err := m.Clone().Count() + if err != nil { + return nil, 0, gerror.Wrap(err, "count community") + } + var list []entity.HouseCommunity + if err = m.Clone().Page(page, size).OrderDesc("id").Scan(&list); err != nil { + return nil, 0, gerror.Wrap(err, "query community list") + } + return list, total, nil +} + +// Create 新增小区。 +func (s *community) Create(ctx context.Context, in dto.HouseCommunityInput) (uint64, error) { + id, err := dao.HouseCommunity.Ctx(ctx).Data(do.HouseCommunity{ + Name: in.Name, + Region: in.Region, + BusinessDistrict: in.BusinessDistrict, + Address: in.Address, + Lng: in.Lng, + Lat: in.Lat, + BuildYear: in.BuildYear, + Households: in.Households, + PlotRatio: in.PlotRatio, + GreenRate: in.GreenRate, + PropertyCompany: in.PropertyCompany, + PropertyFee: in.PropertyFee, + Developer: in.Developer, + Source: in.Source, + }).InsertAndGetId() + if err != nil { + return 0, gerror.Wrap(err, "insert community") + } + return uint64(id), nil +} + +// Update 更新小区。 +func (s *community) Update(ctx context.Context, in dto.HouseCommunityInput) error { + _, err := dao.HouseCommunity.Ctx(ctx).Where(do.HouseCommunity{Id: in.Id}).Data(do.HouseCommunity{ + Name: in.Name, + Region: in.Region, + BusinessDistrict: in.BusinessDistrict, + Address: in.Address, + Lng: in.Lng, + Lat: in.Lat, + BuildYear: in.BuildYear, + Households: in.Households, + PlotRatio: in.PlotRatio, + GreenRate: in.GreenRate, + PropertyCompany: in.PropertyCompany, + PropertyFee: in.PropertyFee, + Developer: in.Developer, + }).Update() + if err != nil { + return gerror.Wrap(err, "update community") + } + return nil +} + +// Delete 软删除小区。 +func (s *community) Delete(ctx context.Context, id uint64) error { + if _, err := dao.HouseCommunity.Ctx(ctx).Where(do.HouseCommunity{Id: id}).Delete(); err != nil { + return gerror.Wrap(err, "delete community") + } + return nil +} diff --git a/internal/service/house/dashboard/dashboard.go b/internal/service/house/dashboard/dashboard.go new file mode 100644 index 0000000..8a1ffbb --- /dev/null +++ b/internal/service/house/dashboard/dashboard.go @@ -0,0 +1,141 @@ +// Package house_dashboard 提供看房数据看板聚合服务。 +package house_dashboard + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/model/dto" +) + +// IDashboard 看板聚合领域服务接口。 +type IDashboard interface { + Overview(context.Context, string) (dto.DashboardOverview, error) + MapPoints(context.Context, string, float64, float64, int) ([]dto.DashboardMapPoint, error) + PriceTrend(context.Context, uint64, string, int) ([]dto.DashboardTrendPoint, error) + AggregateRegion(context.Context) ([]dto.DashboardRegionAgg, error) +} + +type dashboard struct{} + +var localDashboard IDashboard + +func NewDashboard() IDashboard { return &dashboard{} } + +// Dashboard 返回已注册的看板服务实现。 +func Dashboard() IDashboard { + if localDashboard == nil { + panic("Dashboard implementation not registered") + } + return localDashboard +} + +// RegisterDashboard 注册看板服务实现。 +func RegisterDashboard(i IDashboard) { localDashboard = i } + +// Overview 统计概览:小区数、在售房源、笋盘、低可信、均价、平均挂牌天数。 +func (s *dashboard) Overview(ctx context.Context, region string) (dto.DashboardOverview, error) { + var out dto.DashboardOverview + cm := dao.HouseCommunity.Ctx(ctx) + if region != "" { + cm = cm.Where("region", region) + } + cnt, err := cm.Count() + if err != nil { + return out, gerror.Wrap(err, "count community") + } + out.CommunityCount = cnt + + // 房源相关指标统一经小区联表,支持按区域过滤。 + lm := dao.HouseListing.Ctx(ctx).As("l").LeftJoin("house_community c", "l.community_id=c.id") + if region != "" { + lm = lm.Where("c.region", region) + } + out.ListingCount, _ = lm.Clone().Where("l.status", 1).Count() + out.BargainCount, _ = lm.Clone().Where("l.is_bargain", 1).Count() + out.LowConfidence, _ = lm.Clone().Where("l.confidence", 1).Count() + + var agg struct { + AvgUnitPrice float64 `orm:"avg_unit_price"` + AvgTotalPrice float64 `orm:"avg_total_price"` + AvgListDays float64 `orm:"avg_list_days"` + } + if err := lm.Clone().Where("l.status", 1).Fields( + "COALESCE(AVG(l.unit_price),0) AS avg_unit_price, COALESCE(AVG(l.total_price),0) AS avg_total_price, COALESCE(AVG(l.on_market_days),0) AS avg_list_days", + ).Scan(&agg); err != nil { + return out, gerror.Wrap(err, "aggregate overview") + } + out.AvgUnitPrice = agg.AvgUnitPrice + out.AvgTotalPrice = agg.AvgTotalPrice + out.AvgListDays = agg.AvgListDays + return out, nil +} + +// MapPoints 地图点位:按小区聚合均价与在售/笋盘数量。 +func (s *dashboard) MapPoints(ctx context.Context, region string, priceMin, priceMax float64, status int) ([]dto.DashboardMapPoint, error) { + m := dao.HouseListing.Ctx(ctx).As("l"). + LeftJoin("house_community c", "l.community_id=c.id"). + Fields("l.community_id AS community_id, c.name AS name, c.region AS region, c.lng AS lng, c.lat AS lat, COALESCE(AVG(l.unit_price),0) AS avg_unit_price, COUNT(*) AS listing_count, COALESCE(SUM(l.is_bargain),0) AS bargain_count"). + Group("l.community_id, c.name, c.region, c.lng, c.lat") + if region != "" { + m = m.Where("c.region", region) + } + if priceMin > 0 { + m = m.WhereGTE("l.total_price", priceMin) + } + if priceMax > 0 { + m = m.WhereLTE("l.total_price", priceMax) + } + if status > 0 { + m = m.Where("l.status", status) + } + var list []dto.DashboardMapPoint + if err := m.Scan(&list); err != nil { + return nil, gerror.Wrap(err, "query map points") + } + return list, nil +} + +// PriceTrend 价格趋势:按快照日期聚合挂牌/成交均价,返回时间升序。 +func (s *dashboard) PriceTrend(ctx context.Context, communityId uint64, region string, limit int) ([]dto.DashboardTrendPoint, error) { + m := dao.HousePriceSnapshot.Ctx(ctx).As("p") + if communityId > 0 { + m = m.Where("p.community_id", communityId) + } + if region != "" { + m = m.LeftJoin("house_community c", "p.community_id=c.id").Where("c.region", region) + } + m = m.Fields("p.snap_date AS date, COALESCE(AVG(p.list_price),0) AS avg_list_price, COALESCE(AVG(p.deal_price),0) AS avg_deal_price"). + Group("p.snap_date"). + OrderDesc("p.snap_date") + if limit > 0 { + m = m.Limit(limit) + } + var list []dto.DashboardTrendPoint + if err := m.Scan(&list); err != nil { + return nil, gerror.Wrap(err, "query price trend") + } + // 反转为时间升序,便于前端画趋势线。 + for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { + list[i], list[j] = list[j], list[i] + } + return list, nil +} + +// AggregateRegion 区域聚合:按区县统计在售均价与数量。 +func (s *dashboard) AggregateRegion(ctx context.Context) ([]dto.DashboardRegionAgg, error) { + m := dao.HouseListing.Ctx(ctx).As("l"). + LeftJoin("house_community c", "l.community_id=c.id"). + Fields("c.region AS region, COALESCE(AVG(l.unit_price),0) AS avg_unit_price, COUNT(*) AS listing_count, COALESCE(SUM(l.is_bargain),0) AS bargain_count"). + Where("l.status", 1). + WhereGT("c.region", ""). + Group("c.region"). + OrderAsc("c.region") + var list []dto.DashboardRegionAgg + if err := m.Scan(&list); err != nil { + return nil, gerror.Wrap(err, "query region aggregate") + } + return list, nil +} diff --git a/internal/service/house/listing/listing.go b/internal/service/house/listing/listing.go new file mode 100644 index 0000000..265a5b9 --- /dev/null +++ b/internal/service/house/listing/listing.go @@ -0,0 +1,179 @@ +// Package house_listing 提供房源/挂牌领域服务。 +package house_listing + +import ( + "context" + + "github.com/gogf/gf/v2/errors/gerror" + + "service.xpcool.com/internal/consts" + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/library/response" + "service.xpcool.com/internal/model/do" + "service.xpcool.com/internal/model/dto" +) + +// IListing 房源/挂牌领域服务接口。 +type IListing interface { + List(context.Context, dto.HouseListingFilter) ([]dto.HouseListingVO, int, error) + Create(context.Context, dto.HouseListingInput) (uint64, error) + Update(context.Context, dto.HouseListingInput) error + Delete(context.Context, uint64) error + BatchMark(context.Context, []uint64, string, int) (int64, error) +} + +type listing struct{} + +var localListing IListing + +func NewListing() IListing { return &listing{} } + +// Listing 返回已注册的房源服务实现。 +func Listing() IListing { + if localListing == nil { + panic("Listing implementation not registered") + } + return localListing +} + +// RegisterListing 注册房源服务实现。 +func RegisterListing(i IListing) { localListing = i } + +// List 分页查询房源,关联小区名,支持多维筛选(管理列表与看板共用)。 +func (s *listing) List(ctx context.Context, f dto.HouseListingFilter) ([]dto.HouseListingVO, int, error) { + m := dao.HouseListing.Ctx(ctx).As("l").LeftJoin("house_community c", "l.community_id=c.id") + m = m.Fields("l.*, c.name AS community_name") + if f.CommunityId > 0 { + m = m.Where("l.community_id", f.CommunityId) + } + if f.Keyword != "" { + m = m.Where("l.house_no LIKE ? OR l.layout LIKE ?", "%"+f.Keyword+"%", "%"+f.Keyword+"%") + } + if f.Layout != "" { + m = m.Where("l.layout", f.Layout) + } + if f.Source != "" { + m = m.Where("l.source", f.Source) + } + if f.Region != "" { + m = m.Where("c.region", f.Region) + } + if f.PriceMin > 0 { + m = m.WhereGTE("l.total_price", f.PriceMin) + } + if f.PriceMax > 0 { + m = m.WhereLTE("l.total_price", f.PriceMax) + } + if f.AreaMin > 0 { + m = m.WhereGTE("l.area", f.AreaMin) + } + if f.AreaMax > 0 { + m = m.WhereLTE("l.area", f.AreaMax) + } + if f.Status > 0 { + m = m.Where("l.status", f.Status) + } + if f.IsBargain > 0 { + m = m.Where("l.is_bargain", 1) + } + if f.Confidence > 0 { + m = m.Where("l.confidence", 1) + } + total, err := m.Clone().Count() + if err != nil { + return nil, 0, gerror.Wrap(err, "count listing") + } + var list []dto.HouseListingVO + if err = m.Clone().Page(f.Page, f.Size).OrderDesc("l.id").Scan(&list); err != nil { + return nil, 0, gerror.Wrap(err, "query listing list") + } + return list, total, nil +} + +// Create 新增房源。 +func (s *listing) Create(ctx context.Context, in dto.HouseListingInput) (uint64, error) { + id, err := dao.HouseListing.Ctx(ctx).Data(do.HouseListing{ + CommunityId: in.CommunityId, + BuildingId: in.BuildingId, + HouseNo: in.HouseNo, + Layout: in.Layout, + Area: in.Area, + UsableArea: in.UsableArea, + Orientation: in.Orientation, + Floor: in.Floor, + TotalFloors: in.TotalFloors, + Decoration: in.Decoration, + TotalPrice: in.TotalPrice, + UnitPrice: in.UnitPrice, + ListPrice: in.ListPrice, + Source: in.Source, + SourceHouseId: in.SourceHouseId, + SourceUrl: in.SourceUrl, + MatchGroupId: in.MatchGroupId, + OnMarketDays: in.OnMarketDays, + PriceChangeCount: in.PriceChangeCount, + Status: in.Status, + Confidence: in.Confidence, + IsBargain: in.IsBargain, + }).InsertAndGetId() + if err != nil { + return 0, gerror.Wrap(err, "insert listing") + } + return uint64(id), nil +} + +// Update 更新房源。 +func (s *listing) Update(ctx context.Context, in dto.HouseListingInput) error { + _, err := dao.HouseListing.Ctx(ctx).Where(do.HouseListing{Id: in.Id}).Data(do.HouseListing{ + CommunityId: in.CommunityId, + BuildingId: in.BuildingId, + HouseNo: in.HouseNo, + Layout: in.Layout, + Area: in.Area, + UsableArea: in.UsableArea, + Orientation: in.Orientation, + Floor: in.Floor, + TotalFloors: in.TotalFloors, + Decoration: in.Decoration, + TotalPrice: in.TotalPrice, + UnitPrice: in.UnitPrice, + ListPrice: in.ListPrice, + MatchGroupId: in.MatchGroupId, + Status: in.Status, + Confidence: in.Confidence, + IsBargain: in.IsBargain, + }).Update() + if err != nil { + return gerror.Wrap(err, "update listing") + } + return nil +} + +// Delete 软删除房源。 +func (s *listing) Delete(ctx context.Context, id uint64) error { + if _, err := dao.HouseListing.Ctx(ctx).Where(do.HouseListing{Id: id}).Delete(); err != nil { + return gerror.Wrap(err, "delete listing") + } + return nil +} + +// BatchMark 批量标记房源(置信度/笋盘/状态),字段白名单防注入。 +func (s *listing) BatchMark(ctx context.Context, ids []uint64, field string, value int) (int64, error) { + var data do.HouseListing + switch field { + case "confidence": + data = do.HouseListing{Confidence: value} + case "isBargain": + data = do.HouseListing{IsBargain: value} + case "status": + data = do.HouseListing{Status: value} + default: + return 0, response.Error(consts.CodeInvalidParam, "unsupported mark field") + } + res, err := dao.HouseListing.Ctx(ctx).WhereIn("id", ids).Data(data).Update() + if err != nil { + return 0, gerror.Wrap(err, "batch mark listing") + } + affected, _ := res.RowsAffected() + return affected, nil +} diff --git a/internal/service/user/auth/auth.go b/internal/service/user/auth/auth.go index ab6dfef..fe420dc 100644 --- a/internal/service/user/auth/auth.go +++ b/internal/service/user/auth/auth.go @@ -1,4 +1,4 @@ -package auth +package user_auth import ( "context" diff --git a/internal/table/admin_login_log.go b/internal/table/admin_login_log.go new file mode 100644 index 0000000..d217560 --- /dev/null +++ b/internal/table/admin_login_log.go @@ -0,0 +1,93 @@ +// ================================================================================= +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminLoginLog 定义表 "admin_login_log" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 +var AdminLoginLog = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "username": { + Index: 1, + Name: "username", + Type: "varchar(64)", + Null: false, + Key: "MUL", + Default: "", + Extra: "", + Comment: "登录账号", + }, + "ip": { + Index: 2, + Name: "ip", + Type: "varchar(64)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "来源 IP", + }, + "user_agent": { + Index: 3, + Name: "user_agent", + Type: "varchar(255)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "浏览器 UA", + }, + "status": { + Index: 4, + Name: "status", + Type: "tinyint", + Null: false, + Key: "MUL", + Default: "1", + Extra: "", + Comment: "1 成功, 0 失败", + }, + "fail_reason": { + Index: 5, + Name: "fail_reason", + Type: "varchar(255)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "失败原因", + }, + "created_at": { + Index: 6, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// AdminLoginLogTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 +func SetAdminLoginLogTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_login_log", AdminLoginLog, schema...) +} diff --git a/internal/table/admin_menu.go b/internal/table/admin_menu.go index 62b4be0..cf622e0 100644 --- a/internal/table/admin_menu.go +++ b/internal/table/admin_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminMenu defines the fields of table "admin_menu" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminMenu 定义表 "admin_menu" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminMenu = map[string]*gdb.TableField{ "id": { Index: 0, @@ -155,9 +155,9 @@ var AdminMenu = map[string]*gdb.TableField{ }, } -// SetAdminMenuTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminMenuTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminMenuTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_menu", AdminMenu, schema...) } diff --git a/internal/table/admin_operation_log.go b/internal/table/admin_operation_log.go index 6fba817..7209b77 100644 --- a/internal/table/admin_operation_log.go +++ b/internal/table/admin_operation_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminOperationLog defines the fields of table "admin_operation_log" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminOperationLog 定义表 "admin_operation_log" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminOperationLog = map[string]*gdb.TableField{ "id": { Index: 0, @@ -135,9 +135,9 @@ var AdminOperationLog = map[string]*gdb.TableField{ }, } -// SetAdminOperationLogTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminOperationLogTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminOperationLogTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_operation_log", AdminOperationLog, schema...) } diff --git a/internal/table/admin_role.go b/internal/table/admin_role.go index b957b00..4add71a 100644 --- a/internal/table/admin_role.go +++ b/internal/table/admin_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminRole defines the fields of table "admin_role" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminRole 定义表 "admin_role" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminRole = map[string]*gdb.TableField{ "id": { Index: 0, @@ -85,9 +85,9 @@ var AdminRole = map[string]*gdb.TableField{ }, } -// SetAdminRoleTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminRoleTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminRoleTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_role", AdminRole, schema...) } diff --git a/internal/table/admin_role_menu.go b/internal/table/admin_role_menu.go index 17eafa1..b1e7ba7 100644 --- a/internal/table/admin_role_menu.go +++ b/internal/table/admin_role_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminRoleMenu defines the fields of table "admin_role_menu" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminRoleMenu 定义表 "admin_role_menu" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminRoleMenu = map[string]*gdb.TableField{ "id": { Index: 0, @@ -75,9 +75,9 @@ var AdminRoleMenu = map[string]*gdb.TableField{ }, } -// SetAdminRoleMenuTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminRoleMenuTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminRoleMenuTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_role_menu", AdminRoleMenu, schema...) } diff --git a/internal/table/admin_user.go b/internal/table/admin_user.go index 49b4064..977553d 100644 --- a/internal/table/admin_user.go +++ b/internal/table/admin_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminUser defines the fields of table "admin_user" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminUser 定义表 "admin_user" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminUser = map[string]*gdb.TableField{ "id": { Index: 0, @@ -105,9 +105,9 @@ var AdminUser = map[string]*gdb.TableField{ }, } -// SetAdminUserTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminUserTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminUserTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_user", AdminUser, schema...) } diff --git a/internal/table/admin_user_role.go b/internal/table/admin_user_role.go index c3231dc..dc0de4b 100644 --- a/internal/table/admin_user_role.go +++ b/internal/table/admin_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AdminUserRole defines the fields of table "admin_user_role" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AdminUserRole 定义表 "admin_user_role" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AdminUserRole = map[string]*gdb.TableField{ "id": { Index: 0, @@ -75,9 +75,9 @@ var AdminUserRole = map[string]*gdb.TableField{ }, } -// SetAdminUserRoleTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AdminUserRoleTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAdminUserRoleTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "admin_user_role", AdminUserRole, schema...) } diff --git a/internal/table/auth_refresh_session.go b/internal/table/auth_refresh_session.go index 60ce418..995ad48 100644 --- a/internal/table/auth_refresh_session.go +++ b/internal/table/auth_refresh_session.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// AuthRefreshSession defines the fields of table "auth_refresh_session" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// AuthRefreshSession 定义表 "auth_refresh_session" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var AuthRefreshSession = map[string]*gdb.TableField{ "id": { Index: 0, @@ -115,9 +115,9 @@ var AuthRefreshSession = map[string]*gdb.TableField{ }, } -// SetAuthRefreshSessionTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// AuthRefreshSessionTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetAuthRefreshSessionTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "auth_refresh_session", AuthRefreshSession, schema...) } diff --git a/internal/table/content.go b/internal/table/content.go index d068387..3caa5bc 100644 --- a/internal/table/content.go +++ b/internal/table/content.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// Content defines the fields of table "content" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// Content 定义表 "content" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var Content = map[string]*gdb.TableField{ "id": { Index: 0, @@ -85,9 +85,9 @@ var Content = map[string]*gdb.TableField{ }, } -// SetContentTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// ContentTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetContentTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "content", Content, schema...) } diff --git a/internal/table/user.go b/internal/table/user.go index 86f9f42..8538407 100644 --- a/internal/table/user.go +++ b/internal/table/user.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// User defines the fields of table "user" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// User 定义表 "user" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var User = map[string]*gdb.TableField{ "id": { Index: 0, @@ -145,9 +145,9 @@ var User = map[string]*gdb.TableField{ }, } -// SetUserTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// UserTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetUserTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "user", User, schema...) } diff --git a/internal/table/user_favorite.go b/internal/table/user_favorite.go index 67c6146..fe1d9eb 100644 --- a/internal/table/user_favorite.go +++ b/internal/table/user_favorite.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// UserFavorite defines the fields of table "user_favorite" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// UserFavorite 定义表 "user_favorite" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var UserFavorite = map[string]*gdb.TableField{ "id": { Index: 0, @@ -75,9 +75,9 @@ var UserFavorite = map[string]*gdb.TableField{ }, } -// SetUserFavoriteTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// UserFavoriteTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetUserFavoriteTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "user_favorite", UserFavorite, schema...) } diff --git a/internal/table/user_message.go b/internal/table/user_message.go index d0376ed..89da784 100644 --- a/internal/table/user_message.go +++ b/internal/table/user_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// 本文件由 GoFrame CLI 工具自动生成,可按需修改。 // ================================================================================= package table @@ -10,8 +10,8 @@ import ( "github.com/gogf/gf/v2/database/gdb" ) -// UserMessage defines the fields of table "user_message" with their properties. -// This map is used internally by GoFrame ORM to understand table structure. +// UserMessage 定义表 "user_message" 的字段及其属性。 +// 该映射由 GoFrame ORM 内部使用,用于理解表结构。 var UserMessage = map[string]*gdb.TableField{ "id": { Index: 0, @@ -95,9 +95,9 @@ var UserMessage = map[string]*gdb.TableField{ }, } -// SetUserMessageTableFields registers the table fields definition to the database instance. -// db: database instance that implements gdb.DB interface. -// schema: optional schema/namespace name, especially for databases that support schemas. +// UserMessageTableFields 将表字段定义注册到数据库实例。 +// db:实现 gdb.DB 接口的数据库实例。 +// schema:可选的 schema/命名空间名,尤其适用于支持 schema 的数据库。 func SetUserMessageTableFields(ctx context.Context, db gdb.DB, schema ...string) error { return db.GetCore().SetTableFields(ctx, "user_message", UserMessage, schema...) } diff --git a/manifest/config/config.dev.yaml b/manifest/config/config.dev.yaml index 1d8dba2..d7da735 100644 --- a/manifest/config/config.dev.yaml +++ b/manifest/config/config.dev.yaml @@ -1,5 +1,5 @@ server: - address: ":8000" + address: ":10100" openapiPath: "/api.json" swaggerPath: "/swagger" logger: @@ -11,7 +11,7 @@ database: default: link: "${DB_DSN}" jwt: - # Must be overridden by JWT_SECRET in every deployed environment. + # 生产环境必须通过 JWT_SECRET 覆盖该值。 secret: "${JWT_SECRET}" accessExpire: "2h" refreshExpire: "720h" diff --git a/manifest/config/config.prod.yaml b/manifest/config/config.prod.yaml index 6a2bbdc..5b8ce21 100644 --- a/manifest/config/config.prod.yaml +++ b/manifest/config/config.prod.yaml @@ -1,4 +1,4 @@ -server: { address: ":8000", openapiPath: "/api.json", swaggerPath: "/swagger" } +server: { address: ":10100", openapiPath: "/api.json", swaggerPath: "/swagger" } logger: { level: "warning", stdout: true } database: { default: { link: "${DB_DSN}" } } jwt: { secret: "${JWT_SECRET}", accessExpire: "2h", refreshExpire: "720h" } diff --git a/manifest/config/config.test.yaml b/manifest/config/config.test.yaml index 6a9181d..d49d310 100644 --- a/manifest/config/config.test.yaml +++ b/manifest/config/config.test.yaml @@ -1,3 +1,3 @@ -server: { address: ":8001" } +server: { address: ":10100" } database: { default: { link: "${TEST_DB_DSN}" } } jwt: { secret: "${JWT_SECRET}", accessExpire: "15m", refreshExpire: "1h" } diff --git a/manifest/sql/004_seed.sql b/manifest/sql/004_seed.sql index a86353d..3b4c13e 100644 --- a/manifest/sql/004_seed.sql +++ b/manifest/sql/004_seed.sql @@ -1,10 +1,10 @@ -- 004_seed.sql -- Initial seed: super admin, super_admin role, system menu tree (with button -- permission codes) and role/menu bindings. Run after 003_schema_ext.sql. --- Default admin password: admin123 +-- Default super admin password: xxCool@2026 INSERT INTO admin_user (id, username, password_hash, nickname, status, created_at, updated_at) VALUES - (1, 'admin', '$2a$10$TaPfjTcy7nY1kyEcwRJBwOmrvjzoRZ48orMIO5pAdUN8qj.AAbUpG', '超级管理员', 1, NOW(), NOW()); + (1, 'xxcool', '$2b$10$9saH0goudfeZ4pdIUELOFe5zJCgTMGB/cvPE/uPA343gHC2.xdfly', '超级管理员', 1, NOW(), NOW()); INSERT INTO admin_role (id, code, name, status, created_at, updated_at) VALUES (1, 'super_admin', '超级管理员', 1, NOW(), NOW()); diff --git a/manifest/sql/009_admin_account_v2.sql b/manifest/sql/009_admin_account_v2.sql new file mode 100644 index 0000000..c6fe9bb --- /dev/null +++ b/manifest/sql/009_admin_account_v2.sql @@ -0,0 +1,36 @@ +-- 008_admin_account_v2.sql +-- 生产环境超级管理员账号替换(一次性/可重跑幂等): +-- 1) 彻底删除 admin/admin123 相关数据(用户、角色绑定、refresh 会话、审计记录) +-- 2) 新增超级管理员 xxcool(密码 xxCool@2026,bcrypt 哈希),绑定 super_admin 角色 +-- 说明:xxcool 哈希为 bcrypt cost=10($2b$10$...,Go bcrypt.CompareHashAndPassword 兼容 2a/2b/2y)。 + +-- ============ 1) 删除 admin 账号及其全部数据痕迹 ============ +-- refresh 会话(scope=admin 且 subject 指向 admin) +DELETE FROM auth_refresh_session +WHERE scope = 'admin' AND subject_id = (SELECT id FROM admin_user WHERE username = 'admin'); + +-- 审计操作日志 +DELETE FROM admin_operation_log +WHERE admin_user_id = (SELECT id FROM admin_user WHERE username = 'admin'); + +-- 用户-角色绑定 +DELETE FROM admin_user_role +WHERE admin_user_id = (SELECT id FROM admin_user WHERE username = 'admin'); + +-- 用户本身 +DELETE FROM admin_user WHERE username = 'admin'; + +-- ============ 2) 新增 xxcool 超级管理员 ============ +-- 防重跑:先清掉可能存在的 xxcool +DELETE FROM admin_user_role +WHERE admin_user_id = (SELECT id FROM admin_user WHERE username = 'xxcool'); +DELETE FROM auth_refresh_session +WHERE scope = 'admin' AND subject_id = (SELECT id FROM admin_user WHERE username = 'xxcool'); +DELETE FROM admin_user WHERE username = 'xxcool'; + +INSERT INTO admin_user (username, password_hash, nickname, status, created_at, updated_at) +VALUES ('xxcool', '$2b$10$9saH0goudfeZ4pdIUELOFe5zJCgTMGB/cvPE/uPA343gHC2.xdfly', '超级管理员', 1, NOW(), NOW()); + +-- 绑定 super_admin 角色(role_id=1) +INSERT INTO admin_user_role (admin_user_id, role_id, created_at, updated_at) +VALUES (LAST_INSERT_ID(), 1, NOW(), NOW()); diff --git a/manifest/sql/009_menu_rbac_v5.sql b/manifest/sql/009_menu_rbac_v5.sql new file mode 100644 index 0000000..0361658 --- /dev/null +++ b/manifest/sql/009_menu_rbac_v5.sql @@ -0,0 +1,44 @@ +-- 009_menu_rbac_v5.sql +-- 2026-08-26 RBAC 菜单结构调整(二期): +-- 1) 系统管理下新增"人员管理"(id=8,组件 system/admin/index,复用管理员接口),按钮 81-85(system:personnel:*,映射原 /admin/* 接口); +-- 2) 日志管理(id=7)改为目录,下拆"操作日志"(id=71,组件 system/log/index)与"登录日志"(id=72,组件 system/login-log/index); +-- 原按钮 63(system:log:list)改挂到 71 下;新增 721(system:login-log:list,POST /api/service/admin/system/login-log); +-- 注意:admin_menu.permission 有唯一索引,菜单行与按钮行权限码必须不同 +-- (操作日志菜单=system:operation-log,查询按钮=system:log:list;登录日志菜单=system:login-log,查询按钮=system:login-log:list)。 +-- 3) 菜单管理(id=4)图标由 mdi:menu-outline 改为 mdi:file-tree-outline(原图标渲染异常); +-- 4) 超管角色绑定新菜单。 +-- 依赖:先执行 004_seed.sql + 007_menu_permissions_v3.sql + 008_menu_rbac_v4.sql。 +-- 幂等性:INSERT ... ON DUPLICATE KEY UPDATE + UPDATE,可重复执行。 + +-- ============ 1) 人员管理菜单 + 按钮 ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (8, 1, '人员管理', 'mdi:account-outline', 1, 'personnel', 'system/admin/index', 'system:personnel', 4, 1, 0, NOW(), NOW()), + (81, 8, '查询', '', 2, 'POST /api/service/admin/admin/list', '', 'system:personnel:list', 1, 1, 0, NOW(), NOW()), + (82, 8, '新增', '', 2, 'POST /api/service/admin/admin/create', '', 'system:personnel:create', 2, 1, 0, NOW(), NOW()), + (83, 8, '编辑', '', 2, 'POST /api/service/admin/admin/update/{id}', '', 'system:personnel:update', 3, 1, 0, NOW(), NOW()), + (84, 8, '删除', '', 2, 'POST /api/service/admin/admin/delete/{id}', '', 'system:personnel:delete', 4, 1, 0, NOW(), NOW()), + (85, 8, '重置密码', '', 2, 'POST /api/service/admin/admin/resetPwd/{id}', '', 'system:personnel:resetPwd', 5, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 2) 日志管理改目录 + 操作日志/登录日志 ============ +UPDATE admin_menu SET path='/log', component='', sort=5, deleted_at=NULL WHERE id=7; + +-- 操作日志菜单(permission=system:operation-log,与按钮 63 的 system:log:list 区分,避免唯一索引冲突) +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (71, 7, '操作日志', 'mdi:clipboard-text-outline', 1, 'operation', 'system/log/index', 'system:operation-log', 1, 1, 0, NOW(), NOW()), + (72, 7, '登录日志', 'mdi:login', 1, 'login', 'system/login-log/index', 'system:login-log', 2, 1, 0, NOW(), NOW()), + (721, 72, '查询', '', 2, 'POST /api/service/admin/system/login-log', '', 'system:login-log:list', 1, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- 原按钮 63(system:log:list)恢复为按钮行并挂到操作日志菜单(71)下 +UPDATE admin_menu SET parent_id=71, name='查询', type=2, icon='', path='POST /api/service/admin/system/log', + component='', permission='system:log:list', sort=1, status=1, hidden=0, deleted_at=NULL WHERE id=63; + +-- ============ 3) 菜单管理图标修正 ============ +UPDATE admin_menu SET icon='mdi:file-tree-outline' WHERE id=4; + +-- ============ 4) 超管绑定新菜单 ============ +INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at) +SELECT 1, id, NOW(), NOW() FROM admin_menu WHERE id IN (8,81,82,83,84,85,71,72,721); diff --git a/manifest/sql/010_house_tables.sql b/manifest/sql/010_house_tables.sql new file mode 100644 index 0000000..8ce9d29 --- /dev/null +++ b/manifest/sql/010_house_tables.sql @@ -0,0 +1,198 @@ +-- 010_house_tables.sql +-- 看房系统(House)核心数据模型:9 张表。 +-- 约定:BIGINT UNSIGNED 自增主键;统一 created_at/updated_at/deleted_at(GoFrame 软删除); +-- 经纬度 GCJ-02;价格单位(总价万元 / 单价元每平米)。 +-- 幂等:CREATE TABLE IF NOT EXISTS,可重复执行。 + +-- 1) 楼盘/小区主档 +CREATE TABLE IF NOT EXISTS house_community ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(128) NOT NULL COMMENT '小区/楼盘名', + region VARCHAR(64) NOT NULL DEFAULT '' COMMENT '区县(云岩/南明/观山湖/花溪等)', + business_district VARCHAR(64) NOT NULL DEFAULT '' COMMENT '板块', + address VARCHAR(255) NOT NULL DEFAULT '' COMMENT '地址', + lng DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '经度(GCJ-02)', + lat DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '纬度(GCJ-02)', + build_year INT NOT NULL DEFAULT 0 COMMENT '建成年份', + households INT NOT NULL DEFAULT 0 COMMENT '总户数', + plot_ratio DECIMAL(6,3) NOT NULL DEFAULT 0 COMMENT '容积率', + green_rate DECIMAL(6,3) NOT NULL DEFAULT 0 COMMENT '绿化率', + property_company VARCHAR(128) NOT NULL DEFAULT '' COMMENT '物业公司', + property_fee DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '物业费(元/月/平米)', + developer VARCHAR(128) NOT NULL DEFAULT '' COMMENT '开发商', + source VARCHAR(32) NOT NULL DEFAULT '' COMMENT '数据来源', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_community_name (name), + KEY idx_community_region (region, business_district), + KEY idx_community_lnglat (lng, lat), + KEY idx_community_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='楼盘/小区'; + +-- 2) 楼栋(具体到栋) +CREATE TABLE IF NOT EXISTS house_building ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + community_id BIGINT UNSIGNED NOT NULL COMMENT '小区ID', + building_no VARCHAR(32) NOT NULL DEFAULT '' COMMENT '栋号', + units INT NOT NULL DEFAULT 0 COMMENT '单元数', + total_floors INT NOT NULL DEFAULT 0 COMMENT '总楼层', + elevator_count INT NOT NULL DEFAULT 0 COMMENT '电梯数', + ladder_ratio VARCHAR(32) NOT NULL DEFAULT '' COMMENT '梯户比', + building_type VARCHAR(16) NOT NULL DEFAULT '' COMMENT '板楼/塔楼', + lng DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '楼栋经度', + lat DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '楼栋纬度', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_building_community (community_id), + KEY idx_building_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='楼栋'; + +-- 3) 房源/挂牌(核心,多平台不去重) +CREATE TABLE IF NOT EXISTS house_listing ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + community_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小区ID', + building_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '楼栋ID', + house_no VARCHAR(64) NOT NULL DEFAULT '' COMMENT '房号', + layout VARCHAR(32) NOT NULL DEFAULT '' COMMENT '户型(如3室2厅)', + area DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '建筑面积(平米)', + usable_area DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '套内面积(平米)', + orientation VARCHAR(32) NOT NULL DEFAULT '' COMMENT '朝向', + floor INT NOT NULL DEFAULT 0 COMMENT '所在楼层', + total_floors INT NOT NULL DEFAULT 0 COMMENT '总楼层', + decoration VARCHAR(32) NOT NULL DEFAULT '' COMMENT '装修', + total_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '总价(万元)', + unit_price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '单价(元/平米)', + list_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '挂牌价(万元)', + source VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源平台', + source_house_id VARCHAR(64) NOT NULL DEFAULT '' COMMENT '平台侧房源ID', + source_url VARCHAR(512) NOT NULL DEFAULT '' COMMENT '房源链接', + match_group_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '疑似同房源分组(跨平台软关联)', + on_market_days INT NOT NULL DEFAULT 0 COMMENT '挂牌天数', + price_change_count INT NOT NULL DEFAULT 0 COMMENT '调价次数', + status INT NOT NULL DEFAULT 1 COMMENT '1在售 2下架 3成交', + confidence INT NOT NULL DEFAULT 0 COMMENT '可信度 0正常 1低可信', + is_bargain INT NOT NULL DEFAULT 0 COMMENT '笋盘标记 0否 1是', + listing_time DATETIME NULL COMMENT '挂牌时间', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + UNIQUE KEY uk_listing_source_house (source, source_house_id), + KEY idx_listing_community (community_id), + KEY idx_listing_match_group (match_group_id), + KEY idx_listing_unit_price (unit_price), + KEY idx_listing_status (status), + KEY idx_listing_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源/挂牌'; + +-- 4) 价格快照(时序,趋势分析命脉) +CREATE TABLE IF NOT EXISTS house_price_snapshot ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + listing_id BIGINT UNSIGNED NOT NULL COMMENT '房源ID', + community_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小区ID', + snap_date DATE NOT NULL COMMENT '快照日期', + list_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '挂牌价(万元)', + deal_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '成交价(万元)', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + UNIQUE KEY uk_snapshot_listing_date (listing_id, snap_date), + KEY idx_snapshot_community_date (community_id, snap_date), + KEY idx_snapshot_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='价格快照'; + +-- 5) 成交记录 +CREATE TABLE IF NOT EXISTS house_transaction ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + community_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小区ID', + building_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '楼栋ID', + layout VARCHAR(32) NOT NULL DEFAULT '' COMMENT '户型', + area DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '面积(平米)', + deal_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '成交总价(万元)', + deal_unit_price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '成交单价(元/平米)', + list_days INT NOT NULL DEFAULT 0 COMMENT '挂牌到成交天数', + deal_date DATE NULL COMMENT '成交日期', + source VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_transaction_community (community_id), + KEY idx_transaction_deal_date (deal_date), + KEY idx_transaction_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成交记录'; + +-- 6) 配套 POI(地铁站/学校/医院/商圈) +CREATE TABLE IF NOT EXISTS house_facility ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(128) NOT NULL COMMENT '配套名称', + type VARCHAR(32) NOT NULL DEFAULT '' COMMENT '地铁/学校/医院/商圈', + lng DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '经度', + lat DECIMAL(10,6) NOT NULL DEFAULT 0 COMMENT '纬度', + line VARCHAR(32) NOT NULL DEFAULT '' COMMENT '地铁线路', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_facility_type (type), + KEY idx_facility_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配套POI'; + +-- 7) 小区-配套关系 +CREATE TABLE IF NOT EXISTS house_community_facility ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + community_id BIGINT UNSIGNED NOT NULL COMMENT '小区ID', + facility_id BIGINT UNSIGNED NOT NULL COMMENT '配套ID', + distance INT NOT NULL DEFAULT 0 COMMENT '距离(米)', + commute_minutes INT NOT NULL DEFAULT 0 COMMENT '通勤分钟', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + UNIQUE KEY uk_cf_community_facility (community_id, facility_id), + KEY idx_cf_facility (facility_id), + KEY idx_cf_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小区-配套关系'; + +-- 8) 学区划片(版本化) +CREATE TABLE IF NOT EXISTS house_school_district ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + school_name VARCHAR(128) NOT NULL COMMENT '学校名', + community_id BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '小区ID', + district_polygon TEXT NULL COMMENT '划片范围GeoJSON', + district_year INT NOT NULL DEFAULT 0 COMMENT '划片年度', + note VARCHAR(255) NOT NULL DEFAULT '' COMMENT '备注', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_sd_community (community_id), + KEY idx_sd_year (district_year), + KEY idx_sd_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学区划片'; + +-- 9) 用户偏好画像 +CREATE TABLE IF NOT EXISTS house_preference ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + budget_min DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '预算下限(万元)', + budget_max DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '预算上限(万元)', + area_min DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '面积下限(平米)', + area_max DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '面积上限(平米)', + layouts VARCHAR(255) NOT NULL DEFAULT '' COMMENT '户型偏好JSON', + subway_lines VARCHAR(255) NOT NULL DEFAULT '' COMMENT '地铁线路JSON', + school_required INT NOT NULL DEFAULT 0 COMMENT '是否要求学区', + commute_target VARCHAR(255) NOT NULL DEFAULT '' COMMENT '通勤目标点', + commute_limit_min INT NOT NULL DEFAULT 0 COMMENT '通勤上限(分钟)', + region_prefer VARCHAR(255) NOT NULL DEFAULT '' COMMENT '区域偏好JSON', + weights VARCHAR(512) NOT NULL DEFAULT '' COMMENT '权重JSON', + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + KEY idx_pref_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户偏好画像'; diff --git a/manifest/sql/011_house_menu.sql b/manifest/sql/011_house_menu.sql new file mode 100644 index 0000000..7133303 --- /dev/null +++ b/manifest/sql/011_house_menu.sql @@ -0,0 +1,58 @@ +-- 011_house_menu.sql +-- 看房系统(House)菜单 + API 权限种子。 +-- 约定:type=1 菜单(component 指向 views/house/xxx/index.vue);type=2 API 权限(path 为完整 "METHOD /api/service/admin/...")。 +-- 依赖:先执行 010_house_tables.sql(建表)+ 已存在 admin_menu/admin_role_menu 结构。 +-- 幂等:INSERT ... ON DUPLICATE KEY UPDATE + INSERT IGNORE,可重复执行。 + +-- ============ 1) 看房中心菜单(type=1) ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (90, 0, '看房中心', 'mdi:home-city', 1, '/house', '', 'house:center', 6, 1, 0, NOW(), NOW()), + (91, 90, '小区管理', 'mdi:domain', 1, 'community', 'house/community/index', 'house:community', 1, 1, 0, NOW(), NOW()), + (92, 90, '房源管理', 'mdi:home-search', 1, 'listing', 'house/listing/index', 'house:listing', 2, 1, 0, NOW(), NOW()), + (93, 90, '数据看板', 'mdi:chart-box', 1, 'dashboard', 'house/dashboard/index', 'house:dashboard', 3, 1, 0, NOW(), NOW()), + (94, 90, '数据明细', 'mdi:database', 1, 'data', 'house/data/index', 'house:data', 4, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), + status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 2) 小区管理按钮(type=2) ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (910, 91, '查询', '', 2, 'POST /api/service/admin/house/community/list', '', 'house:community:list', 1, 1, 0, NOW(), NOW()), + (911, 91, '新增', '', 2, 'POST /api/service/admin/house/community/create', '', 'house:community:create', 2, 1, 0, NOW(), NOW()), + (912, 91, '编辑', '', 2, 'POST /api/service/admin/house/community/update/{id}', '', 'house:community:update', 3, 1, 0, NOW(), NOW()), + (913, 91, '删除', '', 2, 'POST /api/service/admin/house/community/delete/{id}', '', 'house:community:delete', 4, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), + status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 3) 房源管理按钮(type=2) ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (920, 92, '查询', '', 2, 'POST /api/service/admin/house/listing/list', '', 'house:listing:list', 1, 1, 0, NOW(), NOW()), + (921, 92, '新增', '', 2, 'POST /api/service/admin/house/listing/create', '', 'house:listing:create', 2, 1, 0, NOW(), NOW()), + (922, 92, '编辑', '', 2, 'POST /api/service/admin/house/listing/update/{id}', '', 'house:listing:update', 3, 1, 0, NOW(), NOW()), + (923, 92, '删除', '', 2, 'POST /api/service/admin/house/listing/delete/{id}', '', 'house:listing:delete', 4, 1, 0, NOW(), NOW()), + (924, 92, '标记', '', 2, 'POST /api/service/admin/house/listing/batch-mark', '', 'house:listing:batchMark', 5, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), + status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 4) 数据看板按钮(type=2) ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (930, 93, '概览', '', 2, 'POST /api/service/admin/house/dashboard/overview', '', 'house:dashboard:overview', 1, 1, 0, NOW(), NOW()), + (931, 93, '地图点', '', 2, 'POST /api/service/admin/house/dashboard/map-points', '', 'house:dashboard:mapPoints', 2, 1, 0, NOW(), NOW()), + (932, 93, '价格趋势', '', 2, 'POST /api/service/admin/house/dashboard/price-trend', '', 'house:dashboard:priceTrend', 3, 1, 0, NOW(), NOW()), + (933, 93, '区域聚合', '', 2, 'POST /api/service/admin/house/dashboard/aggregate-region', '', 'house:dashboard:aggregateRegion', 4, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), + status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 5) 数据明细按钮(type=2,快照/成交/配套/学区共用 list) ============ +INSERT INTO admin_menu (id, parent_id, name, icon, type, path, component, permission, sort, status, hidden, created_at, updated_at) VALUES + (940, 94, '查询', '', 2, 'POST /api/service/admin/house/snapshot/list', '', 'house:data:list', 1, 1, 0, NOW(), NOW()) +ON DUPLICATE KEY UPDATE parent_id=VALUES(parent_id), name=VALUES(name), icon=VALUES(icon), type=VALUES(type), + path=VALUES(path), component=VALUES(component), permission=VALUES(permission), sort=VALUES(sort), + status=VALUES(status), hidden=VALUES(hidden), deleted_at=NULL; + +-- ============ 6) 超管角色绑定新菜单(role_id=1 超管) ============ +INSERT IGNORE INTO admin_role_menu (role_id, menu_id, created_at, updated_at) +SELECT 1, id, NOW(), NOW() FROM admin_menu WHERE id IN (90,91,92,93,94,910,911,912,913,920,921,922,923,924,930,931,932,933,940);