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 -- +goose StatementBegin
-- add column bind_user_id to tenants -- 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_id INT8 NOT NULL DEFAULT 0;
ALTER TABLE tenants ADD COLUMN bind_user_contact varchar(128) NOT NULL DEFAULT '';
-- +goose StatementEnd -- +goose StatementEnd
-- +goose Down -- +goose Down
-- +goose StatementBegin -- +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 -- +goose StatementEnd

View File

@@ -20,4 +20,5 @@ type Tenants struct {
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
BindUserID int64 `json:"bind_user_id"` BindUserID int64 `json:"bind_user_id"`
BindUserContact string `json:"bind_user_contact"`
} }

View File

@@ -25,6 +25,7 @@ type tenantsTable struct {
CreatedAt postgres.ColumnTimestamp CreatedAt postgres.ColumnTimestamp
UpdatedAt postgres.ColumnTimestamp UpdatedAt postgres.ColumnTimestamp
BindUserID postgres.ColumnInteger BindUserID postgres.ColumnInteger
BindUserContact postgres.ColumnString
AllColumns postgres.ColumnList AllColumns postgres.ColumnList
MutableColumns postgres.ColumnList MutableColumns postgres.ColumnList
@@ -73,8 +74,9 @@ func newTenantsTableImpl(schemaName, tableName, alias string) tenantsTable {
CreatedAtColumn = postgres.TimestampColumn("created_at") CreatedAtColumn = postgres.TimestampColumn("created_at")
UpdatedAtColumn = postgres.TimestampColumn("updated_at") UpdatedAtColumn = postgres.TimestampColumn("updated_at")
BindUserIDColumn = postgres.IntegerColumn("bind_user_id") BindUserIDColumn = postgres.IntegerColumn("bind_user_id")
allColumns = postgres.ColumnList{IDColumn, NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn} BindUserContactColumn = postgres.StringColumn("bind_user_contact")
mutableColumns = postgres.ColumnList{NameColumn, SlugColumn, DescriptionColumn, ExpireAtColumn, CreatedAtColumn, UpdatedAtColumn, BindUserIDColumn} 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{ return tenantsTable{
@@ -89,6 +91,7 @@ func newTenantsTableImpl(schemaName, tableName, alias string) tenantsTable {
CreatedAt: CreatedAtColumn, CreatedAt: CreatedAtColumn,
UpdatedAt: UpdatedAtColumn, UpdatedAt: UpdatedAtColumn,
BindUserID: BindUserIDColumn, BindUserID: BindUserIDColumn,
BindUserContact: BindUserContactColumn,
AllColumns: allColumns, AllColumns: allColumns,
MutableColumns: mutableColumns, 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) return errors.Wrapf(err, "get tenant: %d", claim.TenantID)
} }
info.IsAdmin = tenant.BindUserID == claim.UserID info.IsAdmin = tenant.BindUserID == claim.UserID
info.AdminContact = tenant.BindUserContact
return ctx.JSON(info) return ctx.JSON(info)
} }
@@ -92,3 +93,26 @@ func (c *Controller) GetChargeCodes(ctx fiber.Ctx) error {
return ctx.JSON(codes) 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 package users
import (
"time"
"backend/pkg/pg"
)
type UserInfo struct { type UserInfo struct {
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
AdminContact string `json:"admin_contact"`
Balance int64 `json:"balance"` 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.Get("info", r.controller.Info)
group.Patch("charge/:code", r.controller.Charge) group.Patch("charge/:code", r.controller.Charge)
group.Get("codes", r.controller.GetChargeCodes) 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 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( return atom.Command(
atom.Name("tenants"), atom.Name("tenants"),
atom.Short("租户相关操作"), atom.Short("租户相关操作"),
atom.Example("create Name --slug [slug] --contact [wechat_contact]"),
atom.Command( atom.Command(
atom.Name("create"), atom.Name("create"),
atom.Providers(defaultProviders().With( atom.Providers(defaultProviders().With(
@@ -40,6 +41,7 @@ func Command() atom.Option {
)), )),
atom.Arguments(func(cmd *cobra.Command) { atom.Arguments(func(cmd *cobra.Command) {
cmd.Flags().String("slug", "", "slug") cmd.Flags().String("slug", "", "slug")
cmd.Flags().String("contact", "", "contact")
}), }),
atom.RunE(func(cmd *cobra.Command, args []string) error { atom.RunE(func(cmd *cobra.Command, args []string) error {
return container.Container.Invoke(func(t *tenant.Create) 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> <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 作为容器 --> <!-- 可以使用 CellGroup 作为容器 -->
<van-cell-group> <van-cell-group>
<van-cell size="large" title="账户余额" :value="user.balance ?? '--'" /> <van-cell size="large" title="账户余额" :value="user.balance ?? '--'" />
@@ -11,28 +12,25 @@
</van-field> </van-field>
</van-cell-group> </van-cell-group>
<van-cell-group title="充值码" v-if="user.is_admin"> <charge-code v-if="user.is_admin === true" />
<van-cell v-for="c in codes" size="large" :title="getCodeAmountTitle(c)" :value="c.code" @click="copyCode(c)" />
</van-cell-group>
<balance-history />
</template> </template>
<script setup> <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"; import request from "@/utils/request";
const codes = ref({});
const user = ref({}); const user = ref({});
const chargeCode = ref("") const chargeCode = ref("")
const loadUserInfo = () => { const loadUserInfo = () => {
request.get("/users/info").then((res) => { request.get("/users/info").then((res) => {
user.value = res.data; user.value = res.data;
if (user.value.is_admin) {
loadChargeCodes();
}
}).catch((err) => { }).catch((err) => {
console.log(err); console.log(err);
}); });
@@ -42,17 +40,6 @@ onMounted(() => {
loadUserInfo(); 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 = () => { const confirmCharge = () => {
if (!chargeCode.value) { 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> </script>