fix: issues

This commit is contained in:
Rogee
2024-12-13 10:38:54 +08:00
parent 5a6b7bcdae
commit 19f445144d
13 changed files with 262 additions and 67 deletions

View File

@@ -2,9 +2,11 @@
-- +goose StatementBegin
-- add column bind_user_id to tenants
ALTER TABLE tenants ADD COLUMN bind_user_id INT8 NOT NULL DEFAULT 0;
ALTER TABLE tenants ADD COLUMN bind_user_contact varchar(128) NOT NULL DEFAULT '';
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP COLUMN bind_user_id FROM tenants;
ALTER TABLE tenants DROP COLUMN bind_user_id ;
ALTER TABLE tenants DROP COLUMN bind_user_contact ;
-- +goose StatementEnd

View File

@@ -12,12 +12,13 @@ import (
)
type Tenants struct {
ID int64 `sql:"primary_key" json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description"`
ExpireAt time.Time `json:"expire_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
BindUserID int64 `json:"bind_user_id"`
ID int64 `sql:"primary_key" json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description"`
ExpireAt time.Time `json:"expire_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
BindUserID int64 `json:"bind_user_id"`
BindUserContact string `json:"bind_user_contact"`
}

View File

@@ -17,14 +17,15 @@ type tenantsTable struct {
postgres.Table
// Columns
ID postgres.ColumnInteger
Name postgres.ColumnString
Slug postgres.ColumnString
Description postgres.ColumnString
ExpireAt postgres.ColumnTimestamp
CreatedAt postgres.ColumnTimestamp
UpdatedAt postgres.ColumnTimestamp
BindUserID postgres.ColumnInteger
ID postgres.ColumnInteger
Name postgres.ColumnString
Slug postgres.ColumnString
Description postgres.ColumnString
ExpireAt postgres.ColumnTimestamp
CreatedAt postgres.ColumnTimestamp
UpdatedAt postgres.ColumnTimestamp
BindUserID postgres.ColumnInteger
BindUserContact postgres.ColumnString
AllColumns postgres.ColumnList
MutableColumns postgres.ColumnList
@@ -65,30 +66,32 @@ func newTenantsTable(schemaName, tableName, alias string) *TenantsTable {
func newTenantsTableImpl(schemaName, tableName, alias string) tenantsTable {
var (
IDColumn = postgres.IntegerColumn("id")
NameColumn = postgres.StringColumn("name")
SlugColumn = postgres.StringColumn("slug")
DescriptionColumn = postgres.StringColumn("description")
ExpireAtColumn = postgres.TimestampColumn("expire_at")
CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at")
BindUserIDColumn = postgres.IntegerColumn("bind_user_id")
allColumns = postgres.ColumnList{IDColumn, NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn}
mutableColumns = postgres.ColumnList{NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn}
IDColumn = postgres.IntegerColumn("id")
NameColumn = postgres.StringColumn("name")
SlugColumn = postgres.StringColumn("slug")
DescriptionColumn = postgres.StringColumn("description")
ExpireAtColumn = postgres.TimestampColumn("expire_at")
CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at")
BindUserIDColumn = postgres.IntegerColumn("bind_user_id")
BindUserContactColumn = postgres.StringColumn("bind_user_contact")
allColumns = postgres.ColumnList{IDColumn, NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn, BindUserContactColumn}
mutableColumns = postgres.ColumnList{NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn, BindUserContactColumn}
)
return tenantsTable{
Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns
ID: IDColumn,
Name: NameColumn,
Slug: SlugColumn,
Description: DescriptionColumn,
ExpireAt: ExpireAtColumn,
CreatedAt: CreatedAtColumn,
UpdatedAt: UpdatedAtColumn,
BindUserID: BindUserIDColumn,
ID: IDColumn,
Name: NameColumn,
Slug: SlugColumn,
Description: DescriptionColumn,
ExpireAt: ExpireAtColumn,
CreatedAt: CreatedAtColumn,
UpdatedAt: UpdatedAtColumn,
BindUserID: BindUserIDColumn,
BindUserContact: BindUserContactColumn,
AllColumns: allColumns,
MutableColumns: mutableColumns,

View File

@@ -54,6 +54,7 @@ func (c *Controller) Info(ctx fiber.Ctx) error {
return errors.Wrapf(err, "get tenant: %d", claim.TenantID)
}
info.IsAdmin = tenant.BindUserID == claim.UserID
info.AdminContact = tenant.BindUserContact
return ctx.JSON(info)
}
@@ -92,3 +93,26 @@ func (c *Controller) GetChargeCodes(ctx fiber.Ctx) error {
return ctx.JSON(codes)
}
// BalanceHistory
func (c *Controller) BalanceHistory(ctx fiber.Ctx) error {
claim := fiber.Locals[*jwt.Claims](ctx, consts.CtxKeyClaim)
log.Debug(claim)
histories, err := c.svc.GetBalanceHistory(ctx.Context(), claim.TenantID, claim.UserID)
if err != nil {
return errorx.Wrap(err)
}
items := []BalanceHistory{}
for _, h := range histories {
items = append(items, BalanceHistory{
Balance: h.Balance,
Type: h.Type,
Target: h.Target,
CreatedAt: h.CreatedAt,
})
}
return ctx.JSON(items)
}

View File

@@ -1,6 +1,20 @@
package users
import (
"time"
"backend/pkg/pg"
)
type UserInfo struct {
IsAdmin bool `json:"is_admin"`
Balance int64 `json:"balance"`
IsAdmin bool `json:"is_admin"`
AdminContact string `json:"admin_contact"`
Balance int64 `json:"balance"`
}
type BalanceHistory struct {
Type pg.BalanceType `json:"type"`
Target pg.BalanceTarget `json:"target"`
Balance int64 `json:"balance"`
CreatedAt time.Time `json:"created_at"`
}

View File

@@ -29,4 +29,5 @@ func (r *Router) Register(router fiber.Router) {
group.Get("info", r.controller.Info)
group.Patch("charge/:code", r.controller.Charge)
group.Get("codes", r.controller.GetChargeCodes)
group.Get("balance-histories", r.controller.BalanceHistory)
}

View File

@@ -413,3 +413,28 @@ func (svc *Service) SetTenantBindUserID(ctx context.Context, tenantID, adminUser
}
return nil
}
// GetBalanceHistory
func (svc *Service) GetBalanceHistory(ctx context.Context, tenantID, userID int64) ([]model.UserBalanceHistories, error) {
log := svc.log.WithField("method", "GetBalanceHistory")
tbl := table.UserBalanceHistories
stmt := tbl.
SELECT(
tbl.AllColumns,
).
WHERE(
tbl.TenantID.EQ(Int64(tenantID)).AND(tbl.UserID.EQ(Int64(userID))),
).
ORDER_BY(
tbl.CreatedAt.DESC(),
).
LIMIT(20)
log.Debug(stmt.DebugSql())
var items []model.UserBalanceHistories
if err := stmt.QueryContext(ctx, db.FromContext(ctx, svc.db), &items); err != nil {
return nil, errors.Wrap(err, "failed to query balance history")
}
return items, nil
}

View File

@@ -31,6 +31,7 @@ func Command() atom.Option {
return atom.Command(
atom.Name("tenants"),
atom.Short("租户相关操作"),
atom.Example("create Name --slug [slug] --contact [wechat_contact]"),
atom.Command(
atom.Name("create"),
atom.Providers(defaultProviders().With(
@@ -40,6 +41,7 @@ func Command() atom.Option {
)),
atom.Arguments(func(cmd *cobra.Command) {
cmd.Flags().String("slug", "", "slug")
cmd.Flags().String("contact", "", "contact")
}),
atom.RunE(func(cmd *cobra.Command, args []string) error {
return container.Container.Invoke(func(t *tenant.Create) error {

View File

@@ -0,0 +1,65 @@
<template>
<van-cell-group inset title="消费历史">
<van-cell v-for="h in histories" size="large" :title="processTitle(h)" :label="parseDate(h.created_at)"
:value="h.balance + '点'" />
</van-cell-group>
</template>
<script>
import request from "@/utils/request";
import { defineComponent, onMounted, ref } from 'vue';
export default defineComponent({
props: {
},
setup(props) {
const histories = ref([]);
const processTitle = (h) => {
if (h.type === "charge") {
return "充值";
} else if (h.type === "consume") {
return "消费";
} else if (h.type === "refund") {
return "退款";
}
}
const loadHistories = () => {
request.get("/users/balance-histories").then((res) => {
histories.value = res.data;
}).catch((err) => {
console.log(err);
});
};
const parseDate = (timeStr) => {
const date = new Date(timeStr);
// 获取年、月、日、时、分 using UTC
const year = date.getUTCFullYear();
const month = date.getUTCMonth() + 1; // 月份是从0开始计数的所以要加1
const day = date.getUTCDate();
const hour = date.getUTCHours();
const minute = date.getUTCMinutes();
// 按照要求格式输出
const formattedTime = `${year}${month}${day}${hour}${minute}`;
return formattedTime;
}
onMounted(() => {
loadHistories();
});
return {
histories,
processTitle,
loadHistories,
parseDate,
}
}
})
</script>

View File

@@ -0,0 +1,52 @@
<template>
<van-cell-group inset title="充值码">
<van-cell v-for="c in codes" size="large" :title="getCodeAmountTitle(c)" :value="c.code" @click="copyCode(c)" />
</van-cell-group>
</template>
<script>
import request from "@/utils/request";
import { defineComponent, onMounted } from 'vue';
export default defineComponent({
props: {
contact: String,
},
setup(props) {
const codes = ref([]);
const loadChargeCodes = () => {
request.get("/users/codes").then((res) => {
codes.value = res.data;
}).catch((err) => {
console.log(err);
});
};
const getCodeAmountTitle = (code) => {
return code.amount / 100 + " 元/ " + code.amount + " 点";
};
const copyCode = (code) => {
navigator.clipboard.writeText(code.code).then(() => {
showSuccessToast('充值码已复制');
}).catch((err) => {
showFailToast('复制失败');
});
};
onMounted(() => {
loadChargeCodes();
});
return {
codes,
getCodeAmountTitle,
copyCode,
loadChargeCodes,
}
}
})
</script>

View File

@@ -0,0 +1,29 @@
<template>
<van-notice-bar left-icon="volume-o" :text="'购买充值码请添加客服微信:' + contact + '(点击复制微信号)'" @click="copyCode(contact)" />
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
props: {
contact: String,
},
setup(props) {
const copyCode = (code) => {
navigator.clipboard.writeText(code).then(() => {
showSuccessToast('客服微信已复制');
}).catch((err) => {
showFailToast('复制失败');
});
};
return {
copyCode,
}
}
})
</script>

View File

@@ -1,6 +1,7 @@
<template>
<van-image width="100%" height="100" src="https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg" />
<div style="width:100%; height: 50px;background-color: #3700B3;"></div>
<charge-notice-bar v-if="user.admin_contact" :contact="user.admin_contact" />
<!-- 可以使用 CellGroup 作为容器 -->
<van-cell-group>
<van-cell size="large" title="账户余额" :value="user.balance ?? '--'" />
@@ -11,28 +12,25 @@
</van-field>
</van-cell-group>
<van-cell-group title="充值码" v-if="user.is_admin">
<van-cell v-for="c in codes" size="large" :title="getCodeAmountTitle(c)" :value="c.code" @click="copyCode(c)" />
</van-cell-group>
<charge-code v-if="user.is_admin === true" />
<balance-history />
</template>
<script setup>
import BalanceHistory from "@/components/BalanceHistory.vue";
import ChargeCode from "@/components/ChargeCode.vue";
import ChargeNoticeBar from "@/components/ChargeNoticeBar.vue";
import request from "@/utils/request";
const codes = ref({});
const user = ref({});
const chargeCode = ref("")
const loadUserInfo = () => {
request.get("/users/info").then((res) => {
user.value = res.data;
if (user.value.is_admin) {
loadChargeCodes();
}
}).catch((err) => {
console.log(err);
});
@@ -42,17 +40,6 @@ onMounted(() => {
loadUserInfo();
});
const loadChargeCodes = () => {
if (!user.value.is_admin) {
return;
}
request.get("/users/codes").then((res) => {
codes.value = res.data;
}).catch((err) => {
console.log(err);
});
};
const confirmCharge = () => {
if (!chargeCode.value) {
@@ -80,16 +67,6 @@ const confirmCharge = () => {
});
};
const getCodeAmountTitle = (code) => {
return code.amount / 100 + " 元/ " + code.amount + " 点";
};
const copyCode = (c) => {
// h5 copy c.code to clipboard
navigator.clipboard.writeText(c.code);
showSuccessToast(getCodeAmountTitle(c) + " 已复制");
};
</script>